From d19d969cb8e4a29f0c988f5e4ec48bf117f2ea4c Mon Sep 17 00:00:00 2001 From: Ivan Pan Date: Fri, 14 Jul 2017 15:34:28 -0700 Subject: [PATCH] Torrent Suite Software 5.4.0 Release --- Analysis/AnalysisOrg/ClonalFilter.cpp | 2 +- .../AnalysisOrg/IO/BeadfindControlOpts.cpp | 155 +- Analysis/AnalysisOrg/IO/BeadfindControlOpts.h | 3 + Analysis/AnalysisOrg/IO/BkgControlOpts.cpp | 46 +- Analysis/AnalysisOrg/IO/BkgControlOpts.h | 4 +- Analysis/AnalysisOrg/IO/CommandLineOpts.cpp | 41 +- Analysis/AnalysisOrg/IO/FlowContext.cpp | 10 +- Analysis/AnalysisOrg/IO/ProgramState.cpp | 1 + Analysis/AnalysisOrg/ImageLoaderQueue.cpp | 5 +- Analysis/AnalysisOrg/MaskFunctions.cpp | 246 +- Analysis/AnalysisOrg/MaskFunctions.h | 3 +- Analysis/AnalysisOrg/ProcessImageToWell.cpp | 4 +- Analysis/AnalysisOrg/SpatialContext.cpp | 2 + Analysis/AnalysisOrg/SpatialContext.h | 1 + Analysis/AnalysisOrg/cudaWrapper.cpp | 5 +- .../justBeadFind/SeparatorInterface.cpp | 6 +- Analysis/BaseCaller/BarcodeClassifier.cpp | 121 +- Analysis/BaseCaller/BarcodeClassifier.h | 5 +- Analysis/BaseCaller/BaseCaller.cpp | 130 +- Analysis/BaseCaller/BaseCallerFilters.cpp | 116 +- Analysis/BaseCaller/BaseCallerFilters.h | 14 +- Analysis/BaseCaller/BaseCallerParameters.cpp | 6 + Analysis/BaseCaller/BaseCallerParameters.h | 11 +- Analysis/BaseCaller/DPTreephaser.cpp | 144 +- Analysis/BaseCaller/DPTreephaser.h | 13 +- Analysis/BaseCaller/MolecularTagTrimmer.cpp | 40 +- Analysis/BaseCaller/MolecularTagTrimmer.h | 3 + Analysis/BaseCaller/OrderedDatasetWriter.cpp | 11 +- Analysis/BaseCaller/OrderedDatasetWriter.h | 3 +- Analysis/BaseCaller/PerBaseQual.cpp | 151 +- Analysis/BaseCaller/PerBaseQual.h | 23 +- Analysis/BaseCaller/TreephaserSSE.cpp | 303 +- Analysis/BaseCaller/TreephaserSSE.h | 3 + Analysis/BaseCaller/TreephaserVEC.cpp | 190 +- Analysis/BaseCaller/TreephaserVEC.h | 3 + Analysis/BkgModel/Bookkeeping/BeadTracker.cpp | 2 +- .../BkgModel/Bookkeeping/EmphasisVector.cpp | 121 +- .../BkgModel/Bookkeeping/EmphasisVector.h | 11 +- .../BkgModel/Bookkeeping/RegionParamDefault.h | 2 +- .../BkgModel/Bookkeeping/TimeCompression.h | 2 +- Analysis/BkgModel/Bookkeeping/TimeControl.cpp | 38 +- Analysis/BkgModel/Bookkeeping/TimeControl.h | 72 +- .../BkgModel/CUDA/GpuMultiFlowFitControl.cpp | 4 +- .../CUDA/GpuMultiFlowFitMatrixConfig.cpp | 26 +- .../CUDA/GpuMultiFlowFitMatrixConfig.h | 6 +- .../HostDataWrapper/ClonalFilterWrapper.cpp | 2 +- .../HostDataWrapper/ClonalFilterWrapper.h | 2 +- .../BkgModel/Fitters/Complex/BkgFitMatDat.h | 2 +- .../Fitters/Complex/BkgFitMatrixPacker.cpp | 4 +- .../Fitters/Complex/BkgFitMatrixPacker.h | 24 +- .../BkgModel/Fitters/Complex/BkgFitOptim.cpp | 341 +- .../BkgModel/Fitters/Complex/BkgFitOptim.h | 86 +- .../Fitters/Complex/BkgFitStructures.cpp | 120 +- .../Fitters/Complex/BkgFitStructures.h | 32 +- .../BkgModel/Fitters/Complex/FitControl.cpp | 110 +- .../BkgModel/Fitters/Complex/FitControl.h | 42 +- .../BkgModel/Fitters/Complex/MultiLevMar.cpp | 22 +- .../BkgModel/Fitters/Complex/MultiLevMar.h | 4 +- .../{ => BkgModel/Fitters}/LevMarFitterV2.cpp | 0 .../{ => BkgModel/Fitters}/LevMarFitterV2.h | 0 .../BkgModel/Fitters/SpatialCorrelator.cpp | 2 +- .../BkgModel/GlobalDefaultsForBkgModel.cpp | 222 +- Analysis/BkgModel/GlobalDefaultsForBkgModel.h | 10 +- Analysis/BkgModel/LocalTrace/BkgTrace.cpp | 266 +- Analysis/BkgModel/LocalTrace/BkgTrace.h | 4 +- Analysis/BkgModel/MathModel/MathOptim.cpp | 4 +- Analysis/BkgModel/MathModel/MathOptim.h | 8 +- Analysis/BkgModel/RegionalizedData.cpp | 113 +- .../BkgModel/SignalProcessingMasterFitter.cpp | 55 +- .../BkgModel/SignalProcessingMasterFitter.h | 1 + Analysis/BkgModel/Writers/DebugWriter.cpp | 123 + Analysis/BkgModel/Writers/DebugWriter.h | 19 +- Analysis/CMakeLists.txt | 135 +- Analysis/Calibration/Calibration.cpp | 2 +- Analysis/{ => ClonalFilter}/mixed.cpp | 34 +- Analysis/{ => ClonalFilter}/mixed.h | 6 +- Analysis/ClonalFilter/polyclonal_filter.cpp | 107 + Analysis/ClonalFilter/polyclonal_filter.h | 56 + Analysis/DataViewer/AlignmentSpatial.cpp | 370 + Analysis/DataViewer/AlignmentSpatial.h | 73 + Analysis/DataViewer/AlignmentTab.cpp | 32 + Analysis/DataViewer/AlignmentTab.h | 27 + Analysis/DataViewer/BfMaskSpatial.cpp | 125 + Analysis/DataViewer/BfMaskSpatial.h | 32 + Analysis/DataViewer/BfMaskTab.cpp | 49 + Analysis/DataViewer/BfMaskTab.h | 27 + Analysis/DataViewer/DataViewer.pro | 64 + Analysis/DataViewer/DataViewer.pro.user | 271 + Analysis/DataViewer/GainSpatial.cpp | 102 + Analysis/DataViewer/GainSpatial.h | 25 + Analysis/DataViewer/GainTab.cpp | 27 + Analysis/DataViewer/GainTab.h | 25 + Analysis/DataViewer/MainForm.ui.qml | 29 + Analysis/DataViewer/NoiseSpatial.cpp | 100 + Analysis/DataViewer/NoiseSpatial.h | 26 + Analysis/DataViewer/NoiseTab.cpp | 24 + Analysis/DataViewer/NoiseTab.h | 25 + Analysis/DataViewer/README | 4 + Analysis/DataViewer/RawSpatial.cpp | 391 + Analysis/DataViewer/RawSpatial.h | 59 + Analysis/DataViewer/RawTab.cpp | 43 + Analysis/DataViewer/RawTab.h | 26 + Analysis/DataViewer/SpatialPlot.cpp | 1002 + Analysis/DataViewer/SpatialPlot.h | 241 + Analysis/DataViewer/WellsSpatial.cpp | 97 + Analysis/DataViewer/WellsSpatial.h | 25 + Analysis/DataViewer/WellsTab.cpp | 30 + Analysis/DataViewer/WellsTab.h | 28 + Analysis/DataViewer/dialog.cpp | 505 + Analysis/DataViewer/dialog.h | 97 + Analysis/DataViewer/main.cpp | 22 + Analysis/DataViewer/main.qml | 16 + Analysis/DataViewer/modeltab.cpp | 123 + Analysis/DataViewer/modeltab.h | 52 + Analysis/DataViewer/qcustomplot.cpp | 23553 ++++++++++++++++ Analysis/DataViewer/qcustomplot.h | 3768 +++ Analysis/DataViewer/qml.qrc | 6 + Analysis/Image/AdvCompr.cpp | 1059 +- Analysis/Image/AdvCompr.h | 35 +- Analysis/Image/ChipIdDecoder.cpp | 5 +- Analysis/Image/ChipIdDecoder.h | 3 +- Analysis/Image/CorrNoiseCorrector.cpp | 18 +- Analysis/Image/CorrNoiseCorrector.h | 5 +- Analysis/Image/Image.cpp | 52 +- Analysis/Image/LSRowImageProcessor.cpp | 2 +- Analysis/Image/PCACompression.cpp | 26 +- Analysis/Image/PCACompression.h | 1 + Analysis/Image/Vecs.h | 106 +- Analysis/Mask/Mask.cpp | 69 +- Analysis/Mask/Mask.h | 7 +- Analysis/Mask/PinnedInFlow.cpp | 131 +- Analysis/Separator/BeadfindStats.cpp | 15 +- Analysis/Separator/DifferentialSeparator.cpp | 30 +- Analysis/Separator/DifferentialSeparator.h | 2 + Analysis/Separator/T0Model.h | 11 +- Analysis/T0Calc.h | 40 +- Analysis/TMAP/CMakeLists.txt | 12 +- Analysis/TMAP/src/index/tmap_refseq.c | 10 +- Analysis/TMAP/src/io/tmap_file.c | 87 +- Analysis/TMAP/src/io/tmap_file.h | 4 + Analysis/TMAP/src/map/tmap_map_driver.c | 2059 +- Analysis/TMAP/src/map/tmap_map_driver.h | 1 - Analysis/TMAP/src/map/util/tmap_map_opt.c | 64 +- Analysis/TMAP/src/map/util/tmap_map_opt.h | 23 +- Analysis/TMAP/src/map/util/tmap_map_stats.c | 166 +- Analysis/TMAP/src/map/util/tmap_map_stats.h | 48 +- Analysis/TMAP/src/map/util/tmap_map_util.c | 5312 ++-- Analysis/TMAP/src/map/util/tmap_map_util.h | 206 +- Analysis/TMAP/src/realign/Realign.cpp | 4 +- Analysis/TMAP/src/realign/cigar_op.h | 17 +- Analysis/TMAP/src/realign/cigar_utils.cpp | 11 +- Analysis/TMAP/src/realign/cigar_utils.h | 2 +- Analysis/TMAP/src/realign/contalign.cpp | 15 +- Analysis/TMAP/src/realign/contalign.h | 1 + Analysis/TMAP/src/realign/realign_proxy.h | 5 +- Analysis/TMAP/src/realign/realign_wrapper.cpp | 7 +- Analysis/TMAP/src/realign/realign_wrapper.h | 5 +- .../realign/realign_wrapper_context_imp.cpp | 15 +- .../src/realign/realign_wrapper_context_imp.h | 5 +- .../TMAP/src/realign/realign_wrapper_imp.cpp | 122 +- .../TMAP/src/realign/realign_wrapper_imp.h | 15 +- Analysis/TMAP/src/sw/tmap_fsw.c | 2 +- Analysis/TMAP/src/sw/tmap_sw.h | 3 +- Analysis/TMAP/src/util/tmap_definitions.c | 4 +- Analysis/TMAP/src/util/tmap_error.c | 49 + Analysis/TMAP/src/util/tmap_error.h | 20 +- Analysis/TsInputUtil/perChipJsonEditor.cpp | 1098 + Analysis/TsInputUtil/tsInputUtil.cpp | 66 +- Analysis/Util/NumericalComparison.h | 5 +- Analysis/Util/Utils.cpp | 125 + Analysis/Util/Utils.h | 9 +- Analysis/VariantCaller/BAMWalkerEngine.cpp | 235 +- Analysis/VariantCaller/BAMWalkerEngine.h | 90 +- .../Bookkeeping/ExtendParameters.cpp | 1072 +- .../Bookkeeping/ExtendParameters.h | 203 +- .../Bookkeeping/InputStructures.cpp | 7 +- .../Bookkeeping/InputStructures.h | 32 +- .../VariantCaller/Bookkeeping/VcfFormat.cpp | 474 +- .../VariantCaller/Bookkeeping/VcfFormat.h | 7 +- .../VariantCaller/Consensus/Consensus.cpp | 568 + Analysis/VariantCaller/Consensus/Consensus.h | 13 + .../Consensus/ConsensusParameters.cpp | 213 + .../Consensus/ConsensusParameters.h | 32 + .../Consensus/FlowSpaceConsensus.cpp | 1165 + .../Consensus/FlowSpaceConsensus.h | 138 + .../EnsembleEval/BiasGenerator.cpp | 17 +- .../EnsembleEval/BiasGenerator.h | 5 + .../EnsembleEval/CrossHypotheses.cpp | 657 +- .../EnsembleEval/CrossHypotheses.h | 65 +- .../EnsembleEval/DiagnosticJSON.cpp | 32 +- .../EnsembleEval/PosteriorInference.cpp | 400 +- .../EnsembleEval/PosteriorInference.h | 42 +- .../VariantCaller/EnsembleEval/ShortStack.cpp | 297 +- .../VariantCaller/EnsembleEval/ShortStack.h | 59 +- .../EnsembleEval/SigmaGenerator.cpp | 28 +- .../EnsembleEval/SigmaGenerator.h | 10 +- .../EnsembleEval/SkewGenerator.cpp | 2 +- .../EnsembleEval/StackEngine.cpp | 2028 +- .../VariantCaller/EnsembleEval/StackEngine.h | 132 +- .../VariantCaller/Filter/DecisionTreeData.cpp | 729 +- .../VariantCaller/Filter/DecisionTreeData.h | 52 +- Analysis/VariantCaller/Filter/VariantAssist.h | 2 + Analysis/VariantCaller/HandleVariant.cpp | 883 +- Analysis/VariantCaller/HandleVariant.h | 52 +- Analysis/VariantCaller/HotspotReader.cpp | 57 +- Analysis/VariantCaller/HotspotReader.h | 21 +- .../IndelAssembly/IndelAssembly.cpp | 12 +- .../IndelAssembly/IndelAssembly.h | 2 +- .../IndelAssembly/IndelAssemblyMain.cpp | 4 +- Analysis/VariantCaller/MolecularTag.cpp | 1398 +- Analysis/VariantCaller/MolecularTag.h | 204 +- Analysis/VariantCaller/OrderedVCFWriter.h | 4 +- .../VariantCaller/Reads/ExtendedReadInfo.cpp | 264 +- .../VariantCaller/Reads/ExtendedReadInfo.h | 7 +- Analysis/VariantCaller/SampleManager.cpp | 66 +- Analysis/VariantCaller/SampleManager.h | 11 +- .../VariantCaller/Splice/ClassifyVariant.cpp | 461 +- .../VariantCaller/Splice/ClassifyVariant.h | 138 +- .../VariantCaller/Splice/LocalContext.cpp | 73 +- Analysis/VariantCaller/Splice/LocalContext.h | 26 +- .../Splice/SpliceVariantHypotheses.cpp | 10 +- .../Splice/SpliceVariantHypotheses.h | 2 +- Analysis/VariantCaller/TargetsManager.cpp | 307 +- Analysis/VariantCaller/TargetsManager.h | 40 +- Analysis/VariantCaller/VariantCaller.cpp | 307 +- Analysis/VariantCaller/tmol/tmol.cpp | 25 +- .../tvcutils/prepare_hotspots.cpp | 271 +- Analysis/VariantCaller/tvcutils/split_vcf.cpp | 10 +- Analysis/VariantCaller/tvcutils/unify_vcf.cpp | 972 +- Analysis/VariantCaller/tvcutils/unify_vcf.h | 98 +- .../VariantCaller/tvcutils/validate_bed.cpp | 53 +- Analysis/VariantCaller/vcfcomp/vcfcomp.cpp | 1638 ++ .../bbctools/src/AmpliconRegionStatistics.cpp | 1 + Analysis/bbctools/src/BbcCoarse.cpp | 6 +- Analysis/bbctools/src/BbcCoarse.h | 4 +- Analysis/bbctools/src/BbcMain.cpp | 48 +- Analysis/bbctools/src/BbcUsage.h | 11 + Analysis/bbctools/src/BbcView.cpp | 1 + Analysis/bbctools/src/RegionCoverage.cpp | 60 +- Analysis/bbctools/src/RegionCoverage.h | 46 +- Analysis/bbctools/src/TrackReads.cpp | 39 + Analysis/bbctools/src/TrackReads.h | 31 + Analysis/config/args_314_beadfind.json | 5 +- Analysis/config/args_316_beadfind.json | 5 +- Analysis/config/args_316v2_beadfind.json | 5 +- Analysis/config/args_318D_beadfind.json | 5 +- Analysis/config/args_318_beadfind.json | 5 +- Analysis/config/args_318select_beadfind.json | 2 + Analysis/config/args_520_beadfind.json | 5 +- Analysis/config/args_521_beadfind.json | 5 +- Analysis/config/args_522_beadfind.json | 5 +- Analysis/config/args_530_beadfind.json | 5 +- Analysis/config/args_540_beadfind.json | 5 +- Analysis/config/args_541_beadfind.json | 5 +- Analysis/config/args_550_analysis.json | 2 +- Analysis/config/args_550_beadfind.json | 5 +- Analysis/config/args_560_beadfind.json | 5 +- Analysis/config/args_900_beadfind.json | 5 +- Analysis/config/args_P1.0.19_beadfind.json | 5 +- Analysis/config/args_P1.0.20_beadfind.json | 5 +- Analysis/config/args_P1.1.17_beadfind.json | 5 +- Analysis/config/args_P1.1.541_beadfind.json | 2 + Analysis/config/args_P1.2.18_beadfind.json | 2 + Analysis/config/args_P2.0.1_beadfind.json | 2 + Analysis/config/args_P2.1.1_beadfind.json | 2 + Analysis/config/args_P2.2.1_beadfind.json | 2 + Analysis/config/args_P2.2.2_beadfind.json | 5 +- Analysis/config/args_P2.3.1_analysis.json | 7 +- Analysis/config/args_P2.3.1_beadfind.json | 7 +- Analysis/config/exclusionMask_510.txt | 1729 ++ Analysis/config/exclusionMask_510.txt.png | Bin 0 -> 21093 bytes Analysis/config/exclusionMask_520.txt | 1729 ++ Analysis/config/exclusionMask_520.txt.png | Bin 0 -> 21093 bytes Analysis/config/exclusionMask_521.txt.png | Bin 0 -> 25508 bytes Analysis/config/exclusionMask_522.txt.png | Bin 0 -> 25508 bytes Analysis/config/exclusionMask_530.txt | 10626 +++---- Analysis/config/exclusionMask_530.txt.png | Bin 0 -> 64577 bytes Analysis/config/exclusionMask_540.txt | 21312 +++++++------- Analysis/config/exclusionMask_540.txt.png | Bin 0 -> 213354 bytes Analysis/config/exclusionMask_541.txt | 21312 +++++++------- Analysis/config/exclusionMask_541.txt.png | Bin 0 -> 213354 bytes Analysis/config/exclusionMask_550.txt | 14041 +++++++++ Analysis/config/exclusionMask_550.txt.png | Bin 0 -> 354139 bytes Analysis/config/exclusionMask_PQ.txt | 21297 ++++++++++++++ Analysis/config/exclusionMask_p1.0.19.txt.png | Bin 0 -> 65738 bytes Analysis/config/exclusionMask_p1.0.20.txt | 10626 +++---- Analysis/config/exclusionMask_p1.0.20.txt.png | Bin 0 -> 64577 bytes Analysis/config/exclusionMask_p1.1.17.txt | 21312 +++++++------- Analysis/config/exclusionMask_p1.1.17.txt.png | Bin 0 -> 213354 bytes Analysis/config/gopt_318D.param.json | 64 + Analysis/config/phredTable.318D.Recal.h5 | Bin 0 -> 418024 bytes Analysis/config/phredTable.318D.h5 | Bin 0 -> 418024 bytes Analysis/config/phredTable.521_ExT | 6997 +++-- Analysis/config/phredTable.521_ExT.Recal.h5 | Bin 1600008 -> 14978533 bytes Analysis/config/phredTable.521_ExT.h5 | Bin 1600008 -> 14978533 bytes Analysis/config/xtalk.550.well.settings.json | 1 + .../config/xtalk.p2.3.1.well.settings.json | 1 + Analysis/crop/ChkDat.cpp | 2 +- Analysis/crop/Crop.cpp | 3 + Analysis/polyclonal_filter.h | 21 - Analysis/realignment/Realigner.cpp | 40 +- Analysis/realignment/Realigner.h | 3 + Analysis/version | 4 +- Analysis/xtalk_sim/DiffEqModel.h | 2 +- Analysis/xtalk_sim/WorkerInfoQueue.cpp | 279 - Analysis/xtalk_sim/WorkerInfoQueue.h | 339 - RSM/version | 4 +- TSVersion | 4 +- buildTools/BUILD.txt | 1 - buildTools/LICENSE.txt.in | 1271 +- buildTools/build.sh | 9 + buildTools/cmake/CMakeLists.compiler.txt | 8 +- buildTools/cmake/CMakeLists.dependencies.txt | 21 +- buildTools/terms-of-use.txt | 1232 +- dbReports/.pylintrc | 4 +- dbReports/CMakeLists.txt | 32 +- .../torrent-server-config.conf.in | 11 +- dbReports/celery/config/celerybeat.in | 18 - dbReports/celery/config/celeryd.in | 48 - dbReports/celery/init.d/celerybeat | 318 - dbReports/celery/init.d/celeryd | 397 - dbReports/celery/init.d/celeryd.new | 188 - dbReports/celery/init/DjangoFTP.conf | 15 + dbReports/celery/init/celerybeat.conf | 39 + dbReports/celery/init/celeryd.conf | 68 + dbReports/debian/postinst.in | 62 +- dbReports/debian/postrm.in | 4 +- dbReports/debian/preinst.in | 51 +- dbReports/debian/prerm.in | 3 + dbReports/iondb/anaserve/serve.py | 41 +- .../iondb/bin/IonMeshDiscoveryManager.py | 36 +- dbReports/iondb/bin/add_barcodes.py | 54 +- .../bin/add_or_update_systemPlanTemplates.py | 917 +- dbReports/iondb/bin/check_system_services.py | 149 + dbReports/iondb/bin/crawler.py | 4 +- dbReports/iondb/bin/dj_config.py | 21 + dbReports/iondb/bin/from_wells_analysis.py | 107 +- dbReports/iondb/bin/grooming.py | 36 + dbReports/iondb/bin/install_dmfilesets.py | 17 +- dbReports/iondb/bin/install_new_tsvm.py | 44 + dbReports/iondb/bin/install_script.py | 112 +- dbReports/iondb/bin/ionPlugin.py | 103 +- dbReports/iondb/bin/ion_add_nfs_mount.py | 65 + dbReports/iondb/bin/ion_check_for_new_tsvm.py | 34 + dbReports/iondb/bin/ion_remove_nfs_mount.py | 48 + dbReports/iondb/bin/lock_ion_apt_sources.py | 59 + dbReports/iondb/bin/migrate_schema22.py | 70 - dbReports/iondb/bin/startup_housekeeping.py | 2 +- dbReports/iondb/celery.py | 2 +- dbReports/iondb/ftpserver/__init__.py | 7 + dbReports/iondb/ftpserver/_unix.py | 25 + dbReports/iondb/ftpserver/authorizers.py | 63 + .../iondb/ftpserver/management}/__init__.py | 0 .../ftpserver/management/commands/__init__.py | 0 .../management/commands/ftpserver.py | 122 + dbReports/iondb/ftpserver/utils.py | 59 + dbReports/iondb/media/Makefile | 3 +- .../media/jquery/js/plupload/jquery-ui.min.js | 1012 - .../media/jquery/js/plupload/jquery.form.js | 660 - .../jquery/js/plupload/jquery.metadata.js | 122 - .../media/jquery/js/plupload/jquery.min.js | 154 - .../jquery/js/plupload/jquery.validate.js | 1146 - .../jquery/js/plupload/jquery.validate.min.js | 16 - .../jquery/js/plupload/plupload.flash.js | 1 - .../jquery/js/plupload/plupload.flash.swf | Bin 18752 -> 0 bytes .../media/jquery/js/plupload/plupload.full.js | 2 - .../media/jquery/js/plupload/plupload.js | 2 - .../js/plupload/plupload.silverlight.js | 1 - .../js/plupload/plupload.silverlight.xap | Bin 43675 -> 0 bytes dbReports/iondb/media/js/data_models.js | 14 +- dbReports/iondb/media/js/data_tab.js | 313 +- dbReports/iondb/media/js/iframe.js | 18 + dbReports/iondb/media/js/monitor_tab.js | 8 +- dbReports/iondb/media/plupload/Moxie.swf | Bin 29349 -> 0 bytes dbReports/iondb/media/plupload/Moxie.xap | Bin 62643 -> 0 bytes .../iondb/media/plupload/plupload.full.min.js | 28 - .../bootstrap-select/bootstrap-select.css | 345 + .../bootstrap-select/bootstrap-select.js | 2024 ++ .../resources/img/appl_ampliSeqExome.png | Bin 1655 -> 987 bytes .../img/appl_category_16s_profile.png | Bin 0 -> 4359 bytes .../img/appl_category_inherited_disease.png | Bin 0 -> 1224 bytes .../resources/img/appl_category_onco_heme.png | Bin 0 -> 1433 bytes .../img/appl_category_onco_immune.png | Bin 0 -> 1667 bytes .../img/appl_category_onco_solid_tumor.png | Bin 0 -> 1821 bytes .../resources/img/appl_category_reproSeq.png | Bin 0 -> 907 bytes .../resources/img/appl_immuneRepertoire.png | Bin 0 -> 1667 bytes .../resources/img/appl_tagSequencing.png | Bin 21260 -> 1274 bytes .../iondb/media/resources/img/s5-icon2.png | Bin 2572 -> 818 bytes .../jquery/jquery-iframe-auto-height.min.js | 5 + .../jquery.iframe-auto-height.plugin.1.7.1.js | 258 - ...ery.iframe-auto-height.plugin.1.7.1.min.js | 1 - .../iondb/media/resources/less/tb-layout.less | 23 +- .../iondb/media/resources/less/tb-styles.less | 153 +- .../media/resources/plupload/changelog.txt | 218 - .../docs/api/class_plupload.File.html | 194 - .../api/class_plupload.QueueProgress.html | 204 - .../docs/api/class_plupload.Runtime.html | 92 - .../docs/api/class_plupload.Uploader.html | 913 - .../plupload/docs/api/class_plupload.html | 538 - .../class_plupload.runtimes.BrowserPlus.html | 95 - .../api/class_plupload.runtimes.Flash.html | 95 - .../api/class_plupload.runtimes.Gears.html | 95 - .../api/class_plupload.runtimes.Html4.html | 95 - .../api/class_plupload.runtimes.Html5.html | 95 - .../class_plupload.runtimes.Silverlight.html | 95 - .../plupload/docs/api/css/general.css | 235 - .../resources/plupload/docs/api/css/grids.css | 467 - .../plupload/docs/api/css/jquery.treeview.css | 75 - .../resources/plupload/docs/api/css/reset.css | 142 - .../plupload/docs/api/css/shCore.css | 342 - .../plupload/docs/api/css/shThemeMoxieDoc.css | 191 - .../resources/plupload/docs/api/img/class.gif | Bin 658 -> 0 bytes .../resources/plupload/docs/api/img/event.gif | Bin 179 -> 0 bytes .../resources/plupload/docs/api/img/help.png | Bin 786 -> 0 bytes .../plupload/docs/api/img/inherit-arrow.gif | Bin 66 -> 0 bytes .../plupload/docs/api/img/inherited.gif | Bin 144 -> 0 bytes .../plupload/docs/api/img/loader.gif | Bin 5330 -> 0 bytes .../plupload/docs/api/img/magnifier.png | Bin 615 -> 0 bytes .../plupload/docs/api/img/method.gif | Bin 191 -> 0 bytes .../plupload/docs/api/img/namespace.gif | Bin 75 -> 0 bytes .../plupload/docs/api/img/page_white_code.png | Bin 603 -> 0 bytes .../plupload/docs/api/img/page_white_copy.png | Bin 309 -> 0 bytes .../plupload/docs/api/img/printer.png | Bin 731 -> 0 bytes .../plupload/docs/api/img/property.gif | Bin 119 -> 0 bytes .../resources/plupload/docs/api/img/root.gif | Bin 580 -> 0 bytes .../plupload/docs/api/img/static.gif | Bin 662 -> 0 bytes .../docs/api/img/treeview-famfamfam.gif | Bin 671 -> 0 bytes .../plupload/docs/api/img/wrapping.png | Bin 631 -> 0 bytes .../resources/plupload/docs/api/index.html | 115 - .../plupload/docs/api/js/clipboard.swf | Bin 1361 -> 0 bytes .../resources/plupload/docs/api/js/general.js | 96 - .../docs/api/js/jquery.treeview.min.js | 15 - .../plupload/docs/api/js/shBrushJScript.js | 52 - .../resources/plupload/docs/api/js/shCore.js | 30 - .../resources/plupload/docs/api/model.xml | 690 - .../plupload/docs/api/plupload.vsdoc.js | 246 - .../media/resources/plupload/examples/bg.jpg | Bin 5753 -> 0 bytes .../resources/plupload/examples/custom.html | 88 - .../resources/plupload/examples/dump.php | 35 - .../plupload/examples/jquery/events.html | 196 - .../examples/jquery/jquery_ui_widget.html | 102 - .../examples/jquery/queue_widget.html | 174 - .../resources/plupload/examples/jquery/s3.php | 158 - .../resources/plupload/examples/upload.php | 132 - .../media/resources/plupload/js/Moxie.swf | Bin 0 -> 29910 bytes .../media/resources/plupload/js/Moxie.xap | Bin 0 -> 63118 bytes .../media/resources/plupload/js/i18n/cs.js | 16 +- .../media/resources/plupload/js/i18n/da.js | 14 +- .../media/resources/plupload/js/i18n/de.js | 26 +- .../media/resources/plupload/js/i18n/el.js | 16 +- .../media/resources/plupload/js/i18n/es.js | 27 +- .../media/resources/plupload/js/i18n/et.js | 35 +- .../media/resources/plupload/js/i18n/fa.js | 39 +- .../media/resources/plupload/js/i18n/fi.js | 35 +- .../media/resources/plupload/js/i18n/fr-ca.js | 35 - .../media/resources/plupload/js/i18n/fr.js | 27 +- .../media/resources/plupload/js/i18n/hr.js | 27 +- .../media/resources/plupload/js/i18n/hu.js | 35 +- .../media/resources/plupload/js/i18n/it.js | 26 +- .../media/resources/plupload/js/i18n/ja.js | 39 +- .../media/resources/plupload/js/i18n/ko.js | 38 +- .../media/resources/plupload/js/i18n/lv.js | 35 +- .../media/resources/plupload/js/i18n/nl.js | 23 +- .../media/resources/plupload/js/i18n/pl.js | 26 +- .../media/resources/plupload/js/i18n/pt-br.js | 35 - .../media/resources/plupload/js/i18n/ro.js | 26 +- .../media/resources/plupload/js/i18n/ru.js | 23 +- .../media/resources/plupload/js/i18n/sr.js | 16 +- .../media/resources/plupload/js/i18n/sv.js | 14 +- .../css/jquery.plupload.queue.css | 10 +- .../jquery.plupload.queue.js | 429 +- .../css/jquery.ui.plupload.css | 300 +- .../js/jquery.ui.plupload/img/loading.gif | Bin 0 -> 4023 bytes .../js/jquery.ui.plupload/img/plupload-bw.png | Bin 2105 -> 0 bytes .../js/jquery.ui.plupload/img/plupload.png | Bin 3641 -> 6597 bytes .../jquery.ui.plupload/jquery.ui.plupload.js | 1344 +- .../plupload/js/plupload.browserplus.js | 1 - .../resources/plupload/js/plupload.flash.js | 1 - .../resources/plupload/js/plupload.flash.swf | Bin 18873 -> 0 bytes .../resources/plupload/js/plupload.full.js | 2 - .../plupload/js/plupload.full.min.js | 29 + .../resources/plupload/js/plupload.gears.js | 1 - .../resources/plupload/js/plupload.html4.js | 1 - .../resources/plupload/js/plupload.html5.js | 1 - .../media/resources/plupload/js/plupload.js | 2 - .../plupload/js/plupload.silverlight.js | 1 - .../plupload/js/plupload.silverlight.xap | Bin 44013 -> 0 bytes .../iondb/media/resources/plupload/readme.md | 165 +- .../common/plan_template_zip_bundle_upload.js | 61 + .../scripts/plan/iru_get_user_input.js | 123 +- .../resources/scripts/plan/iru_validation.js | 10 +- .../scripts/plan/modal_batch_planning.js | 2 +- .../plan/modal_batch_planning_upload.js | 169 +- .../scripts/plan/page_plan_sample_table.js | 43 +- .../scripts/plan/plan_templates.html.js | 105 +- .../resources/scripts/plan/planned.html.js | 25 +- .../resources/scripts/planplugins_common.js | 20 +- .../media/resources/scripts/report.min.js | 2 - .../resources/scripts/{ => reports}/report.js | 125 +- dbReports/iondb/media/resources/scripts/tb.js | 14 +- .../iondb/media/resources/scripts/tb.min.js | 27 +- .../iondb/media/resources/styles/report.css | 10 + .../media/resources/styles/tb-layout.css | 22 +- .../media/resources/styles/tb-layout.min.css | 2 +- .../media/resources/styles/tb-styles.css | 162 +- .../media/resources/styles/tb-styles.min.css | 2 +- dbReports/iondb/plugins/launch_utils.py | 31 +- dbReports/iondb/plugins/manager.py | 15 +- .../iondb/plugins/plugin_barcodes_table.py | 133 + dbReports/iondb/plugins/plugin_json.py | 47 +- dbReports/iondb/plugins/runner.py | 3 - dbReports/iondb/plugins/tasks.py | 83 +- dbReports/iondb/rundb/admin.py | 130 +- dbReports/iondb/rundb/api.py | 988 +- dbReports/iondb/rundb/authn.py | 63 +- dbReports/iondb/rundb/barcodedata.py | 8 + dbReports/iondb/rundb/browserstacktest.py | 102 - .../rundb/configure/ampliseq_design_parser.py | 120 + .../iondb/rundb/configure/cluster_info.py | 13 +- dbReports/iondb/rundb/configure/genomes.py | 1 + .../iondb/rundb/configure/updateProducts.py | 116 +- dbReports/iondb/rundb/configure/urls.py | 2 + dbReports/iondb/rundb/configure/views.py | 356 +- dbReports/iondb/rundb/data/data_import.py | 1 - dbReports/iondb/rundb/data/data_management.py | 6 +- dbReports/iondb/rundb/data/dmactions.py | 41 +- .../iondb/rundb/data/project_msg_banner.py | 5 +- dbReports/iondb/rundb/data/views.py | 118 +- .../fixtures/barcodes/SingleSeq_1-24.csv | 1 + .../fixtures/barcodes/SingleSeq_1-96.csv | 97 + .../rundb/fixtures/barcodes/TagSequencing.csv | 1 + .../iondb/rundb/fixtures/ionmesh_group.json | 16 + .../iondb/rundb/fixtures/ionusers_group.json | 45 - ...tagseq_cfdna_lowstringency_parameters.json | 38 - .../tagseq_ffpe_lowstringency_parameters.json | 38 - .../tagseq_liquidbiopsy_parameters.json | 110 + .../tagseq_tumor_parameters.json | 110 + .../iondb/rundb/fixtures/template_init.json | 11 + dbReports/iondb/rundb/fixtures/ts_dbData.json | 11403 ++------ .../fixtures/ts_dbData_analysisargs.json | 2929 ++ .../rundb/fixtures/ts_dbData_chips_kits.json | 4994 ++++ dbReports/iondb/rundb/forms.py | 113 +- dbReports/iondb/rundb/json_field.py | 24 + dbReports/iondb/rundb/mesh_api.py | 388 +- .../migrations/0309_auto__add_ionmeshnode.py | 1174 + ...ference_path__del_field_globalconfig_fa.py | 1193 + ...cies__del_librarykit__del_sequencingkit.py | 1168 + ...aultPairedEndAdapterKit__del_field_appl.py | 1182 + .../0313_auto__add_plugin_result_job.py | 1141 + .../migrations/0314_plugin_result_job.py | 1166 + .../0315_auto__remove_plugin_result_fields.py | 1161 + .../0316_correct_pluginResult_modelDef.py | 1135 + ...317_auto__add_field_common_cv_isVisible.py | 1141 + .../migrations/0318_auto__add_plansession.py | 1154 + ...sInOrder__chg_field_floworder_flowOrder.py | 1163 + ...d_experimentanalysissettings_sseBedFile.py | 1150 + ..._chg_field_newspost_summary__chg_field_.py | 1166 + ...te_plannedexperiment_samplePrepProtocol.py | 1181 + dbReports/iondb/rundb/models.py | 841 +- dbReports/iondb/rundb/plan/ampliseq.py | 627 +- .../plan/ampliseq_to_TS_plan_convertor.py | 342 + .../iondb/rundb/plan/ampliseq_validator.py | 98 + .../plan/page_plan/abstract_step_data.py | 4 + .../page_plan/barcode_by_sample_step_data.py | 8 +- .../rundb/plan/page_plan/kits_step_data.py | 29 +- .../plan/page_plan/reference_step_data.py | 12 +- .../plan/page_plan/save_plan_step_data.py | 11 +- .../iondb/rundb/plan/page_plan/step_helper.py | 13 +- .../plan/page_plan/step_helper_db_loader.py | 70 +- .../plan/page_plan/step_helper_db_saver.py | 110 +- .../rundb/plan/plan_csv_iru_validator.py | 293 + .../iondb/rundb/plan/plan_csv_validator.py | 215 +- dbReports/iondb/rundb/plan/plan_csv_writer.py | 158 +- dbReports/iondb/rundb/plan/plan_validator.py | 252 +- dbReports/iondb/rundb/plan/urls.py | 5 +- dbReports/iondb/rundb/plan/views.py | 1306 +- dbReports/iondb/rundb/plan/views_helper.py | 115 +- dbReports/iondb/rundb/publishers.py | 107 +- dbReports/iondb/rundb/report/analyze.py | 12 + dbReports/iondb/rundb/report/urls.py | 2 + dbReports/iondb/rundb/report/views.py | 186 +- .../iondb/rundb/session_cleanup/tasks.py | 6 +- dbReports/iondb/rundb/tasks.py | 493 +- dbReports/iondb/rundb/test.py | 167 - dbReports/iondb/rundb/tsvm.py | 260 +- dbReports/iondb/rundb/urls.py | 4 + dbReports/iondb/security/__init__.py | 0 dbReports/iondb/security/admin.py | 23 + dbReports/iondb/security/api.py | 53 + .../iondb/security/migrations/0001_initial.py | 36 + .../iondb/security/migrations/__init__.py | 0 dbReports/iondb/security/models.py | 68 + .../tests/SecureStringResourceTest.py | 76 + .../iondb/security/tests/SecureStringTest.py | 53 + dbReports/iondb/security/tests/__init__.py | 0 dbReports/iondb/security/urls.py | 9 + dbReports/iondb/settings.py | 29 +- dbReports/iondb/templates/404.html | 3 - dbReports/iondb/templates/500.html | 5 +- dbReports/iondb/templates/501.html | 4 +- dbReports/iondb/templates/admin/index.html | 4 - dbReports/iondb/templates/admin/network.html | 6 +- .../iondb/templates/admin/tsvm_control.html | 33 +- dbReports/iondb/templates/admin/update.html | 84 +- dbReports/iondb/templates/maintenance.html | 5 +- .../common/application_kendo_template.html | 51 + .../iondb/templates/rundb/common/base.html | 52 +- .../plan_template_zip_bundle_upload.html | 26 + .../common/samples_popover_template.html | 43 +- .../templates/rundb/configure/about.html | 3 +- .../templates/rundb/configure/ampliseq.html | 105 +- .../iondb/templates/rundb/configure/base.html | 3 + .../templates/rundb/configure/configure.html | 28 +- .../rundb/configure/configure_mesh.html | 453 + .../rundb/configure/edit_reference.html | 12 +- .../templates/rundb/configure/ion_chips.html | 2 +- .../rundb/configure/ionreporter.html | 2 +- ...al_configure_plugins_plugin_configure.html | 1 + .../modal_confirm_plugin_uninstall.html | 4 +- .../modal_plugin_or_publisher_install.html | 11 +- .../configure/modal_references_edit_TF.html | 2 +- .../modal_references_new_genome.html | 67 +- .../rundb/configure/offcycleUpdates.html | 70 +- .../templates/rundb/configure/plugins.html | 19 +- .../plugins/plugin_configure_js.html | 13 + .../rundb/configure/plugins/plugin_usage.html | 12 +- .../rundb/configure/reference_download.html | 2 +- .../templates/rundb/configure/references.html | 5 +- .../configure/references_barcodeset.html | 9 +- .../templates/rundb/configure/services.html | 2 +- .../iondb/templates/rundb/data/base.html | 2 +- .../templates/rundb/data/completed_table.html | 2 +- .../iondb/templates/rundb/data/data.html | 357 +- .../templates/rundb/data/data_export.html | 2 +- .../templates/rundb/data/data_management.html | 4 +- .../rundb/data/dm_configuration.html | 2 +- .../templates/rundb/data/dm_history.html | 2 +- .../rundb/data/modal_combine_results.html | 2 +- .../iondb/templates/rundb/extra/news.html | 4 +- .../templates/rundb/ion_base_template.html | 2 +- .../rundb/ion_nightly_base_template.html | 2 +- .../rundb/ion_publisher_content_add.html | 15 +- .../iondb/templates/rundb/login/index.html | 2 +- .../templates/rundb/monitor/monitor.html | 2 +- .../rundb/plan/modal_batch_planning.html | 22 +- .../plan/modal_batch_planning_upload.html | 44 +- .../plan/modal_plannedexperiment_detail.html | 18 +- .../plan/modal_upload_and_install_files.html | 100 + .../templates/rundb/plan/page_plan/base.html | 11 + .../plan/page_plan/page_plan_application.html | 4 +- .../page_plan_by_sample_barcode.html | 1 + .../plan/page_plan/page_plan_ionreporter.html | 2 +- .../rundb/plan/page_plan/page_plan_kits.html | 223 +- .../plan/page_plan/page_plan_plugins.html | 19 +- .../page_plan/page_plan_sample_table.html | 119 +- .../summary/application_summary.html | 4 + .../page_plan/summary/reference_summary.html | 10 +- .../templates/rundb/plan/plan_templates.html | 31 +- .../iondb/templates/rundb/plan/planned.html | 55 +- .../reports/22_legacy_default_report.html | 2 +- .../templates/rundb/reports/analyze.html | 5 +- .../iondb/templates/rundb/reports/base.html | 2 +- .../rundb/reports/blocks/barcodes.html | 9 +- .../rundb/reports/blocks/basecaller.html | 12 + .../reports/blocks/plugin_barcodes_table.html | 148 + .../templates/rundb/reports/blocks/tabs.html | 139 +- .../rundb/reports/blocks/testfragments.html | 14 +- .../templates/rundb/reports/printreport.tex | 89 +- .../iondb/templates/rundb/reports/report.html | 25 +- .../rundb/sample/sampleattributes.html | 2 +- .../templates/rundb/sample/samplesets.html | 10 +- dbReports/iondb/urls.py | 11 +- dbReports/iondb/utils/hostip.py | 5 +- dbReports/iondb/utils/makePDF.py | 10 +- dbReports/iondb/utils/nexenta_nms.py | 120 +- dbReports/iondb/utils/utils.py | 55 +- dbReports/sudo/torrent-server-sudoers | 19 +- dbReports/version | 4 +- external/freebayes/src/AlleleParser.cpp | 733 +- external/freebayes/src/AlleleParser.h | 172 +- external/vcflib/Variant.cpp | 3 + external/vcflib/Variant.h | 3 + gpu/CMakeLists.txt | 21 +- gpu/bandwidthTest.cu | 1034 + gpu/common/inc/GL/freeglut.h | 22 + gpu/common/inc/GL/freeglut_ext.h | 115 + gpu/common/inc/GL/freeglut_std.h | 547 + gpu/common/inc/GL/glew.h | 14457 ++++++++++ gpu/common/inc/GL/glext.h | 7125 +++++ gpu/common/inc/GL/glut.h | 597 + gpu/common/inc/GL/glxew.h | 1121 + gpu/common/inc/GL/glxext.h | 805 + gpu/common/inc/GL/wglew.h | 958 + gpu/common/inc/cuda_drvapi_dynlink.c | 551 + gpu/common/inc/drvapi_error_string.h | 347 + gpu/common/inc/dynlink/cuda_drvapi_dynlink.h | 25 + .../inc/dynlink/cuda_drvapi_dynlink_cuda.h | 1702 ++ .../inc/dynlink/cuda_drvapi_dynlink_d3d.h | 110 + .../inc/dynlink/cuda_drvapi_dynlink_gl.h | 58 + gpu/common/inc/dynlink_d3d10.h | 303 + gpu/common/inc/dynlink_d3d11.h | 174 + gpu/common/inc/exception.h | 151 + gpu/common/inc/helper_cuda.h | 1283 + gpu/common/inc/helper_cuda_drvapi.h | 521 + gpu/common/inc/helper_cuda_gl.h | 183 + gpu/common/inc/helper_cusolver.h | 172 + gpu/common/inc/helper_functions.h | 42 + gpu/common/inc/helper_gl.h | 191 + gpu/common/inc/helper_image.h | 1110 + gpu/common/inc/helper_math.h | 1453 + gpu/common/inc/helper_string.h | 527 + gpu/common/inc/helper_timer.h | 499 + gpu/common/inc/multithreading.h | 60 + gpu/common/inc/nvMath.h | 95 + gpu/common/inc/nvMatrix.h | 524 + gpu/common/inc/nvQuaternion.h | 514 + gpu/common/inc/nvShaderUtils.h | 255 + gpu/common/inc/nvVector.h | 1058 + gpu/common/inc/nvrtc_helper.h | 99 + gpu/common/inc/param.h | 310 + gpu/common/inc/paramgl.h | 308 + gpu/common/inc/rendercheck_d3d10.h | 37 + gpu/common/inc/rendercheck_d3d11.h | 37 + gpu/common/inc/rendercheck_d3d9.h | 36 + gpu/common/inc/rendercheck_gl.h | 1473 + gpu/common/inc/rendercheck_gles.h | 1419 + gpu/common/inc/timer.h | 64 + gpu/debian/postinst | 11 +- gpu/debian/preinst | 26 +- gpu/deviceQuery.cpp | 288 + gpu/version | 2 +- pipeline/CMakeLists.txt | 6 +- pipeline/bin/BlockTLScript.py | 26 +- pipeline/bin/MergeTLScript.py | 17 +- pipeline/bin/ion-plugin-status | 3 +- pipeline/bin/ion_netinfo | 6 +- pipeline/bin/ion_timeout.sh | 1 + pipeline/oia/install2_OIA.sh | 82 + pipeline/oia/oia | 2 +- pipeline/oia/oia.config | 62 +- pipeline/oia/oiaTimingPlot.py | 111 + pipeline/oia/oiad.py | 221 +- pipeline/oia/pipeline_test.sh | 119 - pipeline/oia/pkg_deb.txt | 2 + pipeline/oia/pkg_oia.txt | 5 + pipeline/oia/pkg_proton.txt | 3 + pipeline/python/ion/plugin/__init__.py | 1 + .../python/ion/plugin/barcodetable_columns.py | 172 + pipeline/python/ion/plugin/base.py | 14 +- pipeline/python/ion/plugin/bedParser.py | 396 + pipeline/python/ion/plugin/commands.py | 10 +- pipeline/python/ion/plugin/remote.py | 42 - .../ion/plugin/templates/rundb/data/base.html | 2 +- pipeline/python/ion/reports/TLScript.py | 52 +- .../python/ion/reports/wells_beadogram.py | 128 +- pipeline/python/ion/utils/alignment.py | 278 +- pipeline/python/ion/utils/basecaller.py | 24 +- pipeline/python/ion/utils/explogparser.py | 79 +- pipeline/python/ion/utils/ionstats_plots.py | 3 +- pipeline/python/ion/utils/makeCSA.py | 522 +- pipeline/python/ion/utils/makeSSA.py | 77 + pipeline/python/ion/utils/pci_devices.py | 85 + pipeline/python/ion/utils/sigproc.py | 8 +- pipeline/tests/Makefile | 9 - .../Plugin_01_Minimal/Plugin_01_Minimal.py | 11 - .../Plugin_02_HelloWorld.py | 52 - .../plugins/Plugin_04_LaunchCompat/launch.sh | 5 - pipeline/tests/test_pipeline.py | 40 - pipeline/tests/test_plugin_loader.py | 71 - pipeline/tests/test_plugins.py | 106 - pipeline/version | 4 +- plugin/AssemblerSPAdes/bin/RunAssembler.py | 8 +- plugin/AssemblerSPAdes/launch.sh | 2 +- plugin/AssemblerSPAdes/lib/ReportGenerator.py | 2 +- plugin/CMakeLists.txt | 2 +- plugin/DataExport/DataExport.py | 4 +- plugin/DataExport/about.html | 2 +- plugin/ERCC_Analysis/ERCC_Analysis.py | 2 +- .../templates/barcode_summary.html | 2 +- .../ERCC_Analysis/templates/incomplete.html | 2 +- plugin/FieldSupport/CMakeLists.txt | 29 + plugin/FieldSupport/FieldSupport.py | 226 + plugin/FieldSupport/README.md | 15 + plugin/FieldSupport/fabfile.py | 19 + .../rndplugins/bubblePlots/bubble3.plots.R | 269 + .../rndplugins/bubblePlots/bubblePlots.pl | 248 + .../rndplugins/bubblePlots/launch.sh | 21 + .../FieldSupport/templates/status_block.html | 48 + plugin/FileExporter/FileExporter.py | 2 +- plugin/FileExporter/config.html | 2 +- plugin/FileExporter/instance.html | 2 +- plugin/FilterDuplicates/FilterDuplicates.py | 2 +- plugin/PGxAnalysis/PGxAnalysis.py | 2 +- .../templates/barcode_summary.html | 2 +- plugin/PGxAnalysis/templates/base.html | 66 +- plugin/RNASeqAnalysis/RNASeqAnalysis.py | 2 +- plugin/RNASeqAnalysis/lifechart/tabbar.css | 30 +- .../templates/barcode_summary.html | 2 +- .../RNASeqAnalysis/templates/incomplete.html | 2 +- plugin/RNASeqAnalysis/templates/report.html | 2 +- plugin/RunTransfer/CMakeLists.txt | 35 +- plugin/RunTransfer/RunTransfer.py | 469 +- plugin/RunTransfer/config.html | 370 +- plugin/RunTransfer/debian/postinst.in | 23 + plugin/RunTransfer/etc/RunTransfer.pdf | Bin 358500 -> 0 bytes plugin/RunTransfer/etc/icon.png | Bin 1357 -> 0 bytes plugin/RunTransfer/etc/screenshot.png | Bin 173628 -> 0 bytes plugin/RunTransfer/instance.html | 482 +- plugin/RunTransfer/package.json | 1 - plugin/RunTransfer/scripts/pscp_connection.sh | 13 - .../RunTransfer/scripts/rsync_connection.sh | 27 - plugin/RunTransfer/scripts/ssh_connection.sh | 12 - plugin/ampliSeqRNA/ampliSeqRNA.py | 2 +- plugin/ampliSeqRNA/lifechart/tabbar.css | 30 +- .../templates/barcode_summary.html | 2 +- plugin/ampliSeqRNA/templates/incomplete.html | 2 +- plugin/ampliSeqRNA/templates/report.html | 2 +- plugin/coverageAnalysis/coverageAnalysis.py | 2 +- .../coverageAnalysis_plugin.py | 73 +- plugin/coverageAnalysis/instance.html | 43 +- .../lifechart/ReferenceCoverageChart.js | 62 +- .../coverageAnalysis/lifechart/TCA.head.html | 1 + .../lifechart/pseudo-bootstrap.css | 24 + plugin/coverageAnalysis/plan.html | 125 +- .../coverageAnalysis/run_coverage_analysis.sh | 30 +- plugin/coverageAnalysis/scripts/gcbias.R | 60 + plugin/coverageAnalysis/scripts/plot_gc.R | 7 +- .../templates/barcode_block.html | 1 + .../templates/barcode_summary.html | 3 +- .../coverageAnalysis/templates/help_dict.json | 2 + .../templates/incomplete.html | 2 +- plugin/coverageAnalysis/templates/report.html | 14 +- .../templates/report_pdf.html | 14 +- plugin/debian/postinst.in | 3 + plugin/immuneResponseRNA/immuneResponseRNA.py | 2 +- .../templates/barcode_summary.html | 2 +- .../templates/incomplete.html | 2 +- .../immuneResponseRNA/templates/report.html | 2 +- plugin/sampleID/sampleID.py | 2 +- .../sampleID/templates/barcode_summary.html | 2 +- plugin/sampleID/templates/incomplete.html | 2 +- plugin/sampleID/templates/report.html | 2 +- plugin/variantCaller/CMakeLists.txt | 8 +- plugin/variantCaller/bin/TvcVcfFile.py | 607 + plugin/variantCaller/bin/swap_duplicates.py | 353 + plugin/variantCaller/bin/tmol_pipeline.py | 174 - .../bin/variant_caller_pipeline.py | 531 +- .../copytoreport/lifegrid/allelesTable.js | 612 +- .../lifegrid/allelesTableTagSeq.js | 1148 - plugin/variantCaller/extend.py | 323 +- plugin/variantCaller/instance.html | 894 +- plugin/variantCaller/launch.sh | 2 +- plugin/variantCaller/lib/libbamtools.so | Bin 711889 -> 0 bytes plugin/variantCaller/lib/libbamtools.so.2.3.0 | Bin 711889 -> 0 bytes plugin/variantCaller/plan.html | 894 +- .../pluginMedia/configs/description.json | 191 +- .../germline_low_stringency_proton.json | 2 +- ...ine_lowstringency_530_Chef_parameters.json | 0 ...germline_lowstringency_530_parameters.json | 90 + ...germline_lowstringency_540_parameters.json | 91 + ..._germline_lowstringency_p1_parameters.json | 91 + ...germline_lowstringency_pgm_parameters.json | 90 + ...tic_lowstringency_530_Chef_parameters.json | 0 ..._somatic_lowstringency_530_parameters.json | 91 + ...matic_lowstringency_p1-540_parameters.json | 91 + ..._somatic_lowstringency_pgm_parameters.json | 91 + ...germline_lowstringency_540_parameters.json | 0 ..._germline_lowstringency_p1_parameters.json | 0 .../5_2/builtin_parameter_sets.json | 135 + ..._somatic_lowstringency_540_parameters.json | 91 + ..._somatic_lowstringency_pgm_parameters.json | 0 .../parameter_sets/5_2/parameter_sets.json | 856 + ...germline_lowstringency_pgm_parameters.json | 0 .../5_2/tagseq_liquidbiopsy_parameters.json | 31 + .../5_2/tagseq_tumor_parameters.json | 31 + ..._germline_lowstringency_p1_parameters.json | 90 + ...germline_lowstringency_530_parameters.json | 16 +- ...germline_lowstringency_540_parameters.json | 20 +- ..._germline_lowstringency_p1_parameters.json | 20 +- ...germline_lowstringency_pgm_parameters.json | 20 +- ..._somatic_lowstringency_530_parameters.json | 22 +- ...matic_lowstringency_p1-540_parameters.json | 20 +- ..._somatic_lowstringency_pgm_parameters.json | 20 +- .../builtin_parameter_sets.json | 34 +- .../parameter_sets/parameter_sets.json | 364 +- .../tagseq_liquidbiopsy_parameters.json | 105 +- .../tagseq_tumor_parameters.json | 109 +- ..._germline_lowstringency_p1_parameters.json | 21 +- .../pluginMedia/parameter_sets/test_format.py | 122 +- .../scripts/filterVCFbyTarget.py | 104 + .../scripts/generate_variant_tables.py | 378 +- plugin/variantCaller/templates/base.html | 63 +- .../templates/block_barcodes.html | 263 +- .../templates/block_details.html | 4 +- .../templates/processing_status.html | 150 + .../templates/report_barcodes.html | 384 +- .../templates/report_details.html | 30 +- .../templates/report_incomplete_block.html | 42 + plugin/variantCaller/variantCaller.py | 38 +- plugin/variantCaller/variant_caller_plugin.py | 2536 +- plugin/version | 4 +- publishers/BED/pre_process.py | 75 - publishers/BED/validate.py | 376 +- publishers/version | 4 +- torrentPy/version | 4 +- torrentR/CMakeLists.txt | 60 +- torrentR/NAMESPACE | 3 +- torrentR/R/BkgModelHiddenFunctions.R | 10 +- torrentR/R/findAdapter.R | 27 + torrentR/configure | 16 +- torrentR/debian/postinst.in | 59 +- torrentR/debian/prerm.in | 44 +- torrentR/src/BamHelper.cpp | 21 - torrentR/src/CafieSolver.cpp | 4 +- torrentR/src/FlowErr.cpp | 42 +- torrentR/src/PhaseSolve.cpp | 15 +- torrentR/src/SimulateCAFIE.cpp | 4 +- torrentR/src/findAdapter.cpp | 299 + torrentR/src/readIonBam.cpp | 165 +- torrentR/src/treePhaser.cpp | 55 +- torrentR/version | 4 +- tsconfig/CMakeLists.txt | 10 +- tsconfig/ansible/deconfigure_compute.yml | 4 +- tsconfig/ansible/globals.yml | 13 +- tsconfig/ansible/rename_headnode.yml | 8 +- tsconfig/ansible/roles/common/tasks/main.yml | 42 +- tsconfig/ansible/roles/db/tasks/main.yml | 2 +- .../ansible/roles/fileserver/tasks/main.yml | 42 +- .../roles/fileserver/templates/hosts.j2 | 30 - .../roles/fileserver/templates/iptables.j2 | 4 + .../gridengine_clients/defaults/main.yml | 4 +- .../roles/gridengine_clients/tasks/main.yml | 24 +- .../roles/gridengine_master/defaults/main.yml | 4 +- .../roles/gridengine_master/tasks/main.yml | 40 +- .../roles/iontorrent_computes/tasks/main.yml | 8 +- .../iontorrent_master/files/s5_rig_default.py | 8 +- .../roles/iontorrent_master/handlers/main.yml | 4 +- .../roles/iontorrent_master/tasks/main.yml | 42 +- .../ansible/roles/nfs_client/tasks/main.yml | 24 +- tsconfig/ansible/software_update.yml | 40 +- .../{files => templates}/sentry_settings.j2 | 0 tsconfig/ansible/templates/tsconf.conf.j2 | 2 +- tsconfig/ansible/test.yml | 10 + tsconfig/apt-mirror/link-mirror.sh | 5 + tsconfig/apt-mirror/link-test-mirror.sh | 12 + tsconfig/apt-mirror/mirror.list | 29 + tsconfig/apt-mirror/test-mirror.list | 54 + tsconfig/bin/TSconfig | 447 +- tsconfig/bin/TSconfig-ansible | 449 - tsconfig/bin/TSconfig-legacy | 558 - tsconfig/bin/TScreateLocalRepository | 42 - tsconfig/bin/TSinit | 133 +- tsconfig/bin/TSstaticip | 277 +- tsconfig/bin/TSstaticip-ansible | 279 - tsconfig/bin/TSstaticip-legacy | 355 - tsconfig/bin/grp_mount_test | 4 +- tsconfig/ion_tsconfig/TSconfig.py | 875 +- tsconfig/rnd_playbooks/README.md | 71 + tsconfig/rnd_playbooks/open_iptables.j2 | 3 + tsconfig/rnd_playbooks/open_iptables.yml | 17 + .../{ansible => rnd_playbooks}/sentry.yml | 21 +- tsconfig/rnd_playbooks/sshd_keyonly.yml | 32 + tsconfig/rnd_playbooks/torrentpy_database.yml | 63 + tsconfig/ts_functions | 178 +- tsconfig/version | 4 +- tsvm/CMakeLists.txt | 13 +- tsvm/Vagrantfile | 16 +- tsvm/debian/postinst.in | 2 +- tsvm/tsvm-enable-nfs-rule | 13 + tsvm/tsvm-include | 7 +- tsvm/tsvm-setup | 46 +- tsvm/tsvm-versions | 4 +- tsvm/vagrant_sudoers | 4 +- tsvm/version | 4 +- 974 files changed, 242486 insertions(+), 90806 deletions(-) rename Analysis/{ => BkgModel/Fitters}/LevMarFitterV2.cpp (100%) rename Analysis/{ => BkgModel/Fitters}/LevMarFitterV2.h (100%) rename Analysis/{ => ClonalFilter}/mixed.cpp (97%) rename Analysis/{ => ClonalFilter}/mixed.h (93%) create mode 100644 Analysis/ClonalFilter/polyclonal_filter.cpp create mode 100644 Analysis/ClonalFilter/polyclonal_filter.h create mode 100644 Analysis/DataViewer/AlignmentSpatial.cpp create mode 100644 Analysis/DataViewer/AlignmentSpatial.h create mode 100644 Analysis/DataViewer/AlignmentTab.cpp create mode 100644 Analysis/DataViewer/AlignmentTab.h create mode 100644 Analysis/DataViewer/BfMaskSpatial.cpp create mode 100644 Analysis/DataViewer/BfMaskSpatial.h create mode 100644 Analysis/DataViewer/BfMaskTab.cpp create mode 100644 Analysis/DataViewer/BfMaskTab.h create mode 100644 Analysis/DataViewer/DataViewer.pro create mode 100644 Analysis/DataViewer/DataViewer.pro.user create mode 100644 Analysis/DataViewer/GainSpatial.cpp create mode 100644 Analysis/DataViewer/GainSpatial.h create mode 100644 Analysis/DataViewer/GainTab.cpp create mode 100644 Analysis/DataViewer/GainTab.h create mode 100644 Analysis/DataViewer/MainForm.ui.qml create mode 100644 Analysis/DataViewer/NoiseSpatial.cpp create mode 100644 Analysis/DataViewer/NoiseSpatial.h create mode 100644 Analysis/DataViewer/NoiseTab.cpp create mode 100644 Analysis/DataViewer/NoiseTab.h create mode 100644 Analysis/DataViewer/README create mode 100644 Analysis/DataViewer/RawSpatial.cpp create mode 100644 Analysis/DataViewer/RawSpatial.h create mode 100644 Analysis/DataViewer/RawTab.cpp create mode 100644 Analysis/DataViewer/RawTab.h create mode 100644 Analysis/DataViewer/SpatialPlot.cpp create mode 100644 Analysis/DataViewer/SpatialPlot.h create mode 100644 Analysis/DataViewer/WellsSpatial.cpp create mode 100644 Analysis/DataViewer/WellsSpatial.h create mode 100644 Analysis/DataViewer/WellsTab.cpp create mode 100644 Analysis/DataViewer/WellsTab.h create mode 100644 Analysis/DataViewer/dialog.cpp create mode 100644 Analysis/DataViewer/dialog.h create mode 100644 Analysis/DataViewer/main.cpp create mode 100644 Analysis/DataViewer/main.qml create mode 100644 Analysis/DataViewer/modeltab.cpp create mode 100644 Analysis/DataViewer/modeltab.h create mode 100644 Analysis/DataViewer/qcustomplot.cpp create mode 100644 Analysis/DataViewer/qcustomplot.h create mode 100644 Analysis/DataViewer/qml.qrc create mode 100644 Analysis/TsInputUtil/perChipJsonEditor.cpp create mode 100644 Analysis/VariantCaller/Consensus/Consensus.cpp create mode 100644 Analysis/VariantCaller/Consensus/Consensus.h create mode 100644 Analysis/VariantCaller/Consensus/ConsensusParameters.cpp create mode 100644 Analysis/VariantCaller/Consensus/ConsensusParameters.h create mode 100644 Analysis/VariantCaller/Consensus/FlowSpaceConsensus.cpp create mode 100644 Analysis/VariantCaller/Consensus/FlowSpaceConsensus.h create mode 100644 Analysis/VariantCaller/vcfcomp/vcfcomp.cpp create mode 100644 Analysis/bbctools/src/TrackReads.cpp create mode 100644 Analysis/bbctools/src/TrackReads.h create mode 100644 Analysis/config/exclusionMask_510.txt create mode 100644 Analysis/config/exclusionMask_510.txt.png create mode 100644 Analysis/config/exclusionMask_520.txt create mode 100644 Analysis/config/exclusionMask_520.txt.png create mode 100644 Analysis/config/exclusionMask_521.txt.png create mode 100644 Analysis/config/exclusionMask_522.txt.png create mode 100644 Analysis/config/exclusionMask_530.txt.png create mode 100644 Analysis/config/exclusionMask_540.txt.png create mode 100644 Analysis/config/exclusionMask_541.txt.png create mode 100644 Analysis/config/exclusionMask_550.txt create mode 100644 Analysis/config/exclusionMask_550.txt.png create mode 100644 Analysis/config/exclusionMask_PQ.txt create mode 100644 Analysis/config/exclusionMask_p1.0.19.txt.png create mode 100644 Analysis/config/exclusionMask_p1.0.20.txt.png create mode 100644 Analysis/config/exclusionMask_p1.1.17.txt.png create mode 100644 Analysis/config/gopt_318D.param.json create mode 100644 Analysis/config/phredTable.318D.Recal.h5 create mode 100644 Analysis/config/phredTable.318D.h5 create mode 100644 Analysis/config/xtalk.550.well.settings.json create mode 100644 Analysis/config/xtalk.p2.3.1.well.settings.json delete mode 100644 Analysis/polyclonal_filter.h delete mode 100644 Analysis/xtalk_sim/WorkerInfoQueue.cpp delete mode 100644 Analysis/xtalk_sim/WorkerInfoQueue.h delete mode 100644 dbReports/celery/config/celerybeat.in delete mode 100644 dbReports/celery/config/celeryd.in delete mode 100644 dbReports/celery/init.d/celerybeat delete mode 100644 dbReports/celery/init.d/celeryd delete mode 100644 dbReports/celery/init.d/celeryd.new create mode 100644 dbReports/celery/init/DjangoFTP.conf create mode 100644 dbReports/celery/init/celerybeat.conf create mode 100644 dbReports/celery/init/celeryd.conf mode change 100644 => 100755 dbReports/debian/preinst.in mode change 100755 => 100644 dbReports/iondb/bin/add_or_update_systemPlanTemplates.py create mode 100644 dbReports/iondb/bin/check_system_services.py create mode 100755 dbReports/iondb/bin/grooming.py create mode 100755 dbReports/iondb/bin/install_new_tsvm.py create mode 100644 dbReports/iondb/bin/ion_add_nfs_mount.py create mode 100644 dbReports/iondb/bin/ion_check_for_new_tsvm.py create mode 100644 dbReports/iondb/bin/ion_remove_nfs_mount.py create mode 100644 dbReports/iondb/bin/lock_ion_apt_sources.py create mode 100644 dbReports/iondb/ftpserver/__init__.py create mode 100644 dbReports/iondb/ftpserver/_unix.py create mode 100644 dbReports/iondb/ftpserver/authorizers.py rename {pipeline/tests => dbReports/iondb/ftpserver/management}/__init__.py (100%) create mode 100644 dbReports/iondb/ftpserver/management/commands/__init__.py create mode 100644 dbReports/iondb/ftpserver/management/commands/ftpserver.py create mode 100644 dbReports/iondb/ftpserver/utils.py delete mode 100644 dbReports/iondb/media/jquery/js/plupload/jquery-ui.min.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/jquery.form.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/jquery.metadata.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/jquery.min.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/jquery.validate.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/jquery.validate.min.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/plupload.flash.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/plupload.flash.swf delete mode 100644 dbReports/iondb/media/jquery/js/plupload/plupload.full.js delete mode 100644 dbReports/iondb/media/jquery/js/plupload/plupload.js delete mode 100755 dbReports/iondb/media/jquery/js/plupload/plupload.silverlight.js delete mode 100755 dbReports/iondb/media/jquery/js/plupload/plupload.silverlight.xap create mode 100644 dbReports/iondb/media/js/iframe.js delete mode 100755 dbReports/iondb/media/plupload/Moxie.swf delete mode 100755 dbReports/iondb/media/plupload/Moxie.xap delete mode 100755 dbReports/iondb/media/plupload/plupload.full.min.js create mode 100755 dbReports/iondb/media/resources/bootstrap-select/bootstrap-select.css create mode 100755 dbReports/iondb/media/resources/bootstrap-select/bootstrap-select.js mode change 100755 => 100644 dbReports/iondb/media/resources/img/appl_ampliSeqExome.png create mode 100755 dbReports/iondb/media/resources/img/appl_category_16s_profile.png create mode 100644 dbReports/iondb/media/resources/img/appl_category_inherited_disease.png create mode 100644 dbReports/iondb/media/resources/img/appl_category_onco_heme.png create mode 100644 dbReports/iondb/media/resources/img/appl_category_onco_immune.png create mode 100644 dbReports/iondb/media/resources/img/appl_category_onco_solid_tumor.png create mode 100644 dbReports/iondb/media/resources/img/appl_category_reproSeq.png create mode 100644 dbReports/iondb/media/resources/img/appl_immuneRepertoire.png create mode 100644 dbReports/iondb/media/resources/jquery/jquery-iframe-auto-height.min.js delete mode 100644 dbReports/iondb/media/resources/jquery/jquery.iframe-auto-height.plugin.1.7.1.js delete mode 100644 dbReports/iondb/media/resources/jquery/jquery.iframe-auto-height.plugin.1.7.1.min.js delete mode 100644 dbReports/iondb/media/resources/plupload/changelog.txt delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.File.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.QueueProgress.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.Runtime.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.Uploader.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.runtimes.BrowserPlus.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.runtimes.Flash.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.runtimes.Gears.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.runtimes.Html4.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.runtimes.Html5.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/class_plupload.runtimes.Silverlight.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/css/general.css delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/css/grids.css delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/css/jquery.treeview.css delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/css/reset.css delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/css/shCore.css delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/css/shThemeMoxieDoc.css delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/class.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/event.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/help.png delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/inherit-arrow.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/inherited.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/loader.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/magnifier.png delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/method.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/namespace.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/page_white_code.png delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/page_white_copy.png delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/printer.png delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/property.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/root.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/static.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/treeview-famfamfam.gif delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/img/wrapping.png delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/index.html delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/js/clipboard.swf delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/js/general.js delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/js/jquery.treeview.min.js delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/js/shBrushJScript.js delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/js/shCore.js delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/model.xml delete mode 100644 dbReports/iondb/media/resources/plupload/docs/api/plupload.vsdoc.js delete mode 100644 dbReports/iondb/media/resources/plupload/examples/bg.jpg delete mode 100644 dbReports/iondb/media/resources/plupload/examples/custom.html delete mode 100644 dbReports/iondb/media/resources/plupload/examples/dump.php delete mode 100644 dbReports/iondb/media/resources/plupload/examples/jquery/events.html delete mode 100644 dbReports/iondb/media/resources/plupload/examples/jquery/jquery_ui_widget.html delete mode 100644 dbReports/iondb/media/resources/plupload/examples/jquery/queue_widget.html delete mode 100644 dbReports/iondb/media/resources/plupload/examples/jquery/s3.php delete mode 100644 dbReports/iondb/media/resources/plupload/examples/upload.php create mode 100644 dbReports/iondb/media/resources/plupload/js/Moxie.swf create mode 100644 dbReports/iondb/media/resources/plupload/js/Moxie.xap delete mode 100644 dbReports/iondb/media/resources/plupload/js/i18n/fr-ca.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/i18n/pt-br.js create mode 100644 dbReports/iondb/media/resources/plupload/js/jquery.ui.plupload/img/loading.gif delete mode 100644 dbReports/iondb/media/resources/plupload/js/jquery.ui.plupload/img/plupload-bw.png delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.browserplus.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.flash.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.flash.swf delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.full.js create mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.full.min.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.gears.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.html4.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.html5.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.silverlight.js delete mode 100644 dbReports/iondb/media/resources/plupload/js/plupload.silverlight.xap create mode 100644 dbReports/iondb/media/resources/scripts/common/plan_template_zip_bundle_upload.js delete mode 100644 dbReports/iondb/media/resources/scripts/report.min.js rename dbReports/iondb/media/resources/scripts/{ => reports}/report.js (87%) create mode 100644 dbReports/iondb/plugins/plugin_barcodes_table.py delete mode 100644 dbReports/iondb/rundb/browserstacktest.py create mode 100644 dbReports/iondb/rundb/configure/ampliseq_design_parser.py create mode 100644 dbReports/iondb/rundb/fixtures/barcodes/SingleSeq_1-24.csv create mode 100644 dbReports/iondb/rundb/fixtures/barcodes/SingleSeq_1-96.csv create mode 100644 dbReports/iondb/rundb/fixtures/barcodes/TagSequencing.csv create mode 100644 dbReports/iondb/rundb/fixtures/ionmesh_group.json delete mode 100644 dbReports/iondb/rundb/fixtures/systemtemplateparams/tagseq_cfdna_lowstringency_parameters.json delete mode 100644 dbReports/iondb/rundb/fixtures/systemtemplateparams/tagseq_ffpe_lowstringency_parameters.json create mode 100755 dbReports/iondb/rundb/fixtures/systemtemplateparams/tagseq_liquidbiopsy_parameters.json create mode 100755 dbReports/iondb/rundb/fixtures/systemtemplateparams/tagseq_tumor_parameters.json create mode 100644 dbReports/iondb/rundb/fixtures/ts_dbData_analysisargs.json create mode 100644 dbReports/iondb/rundb/fixtures/ts_dbData_chips_kits.json create mode 100644 dbReports/iondb/rundb/migrations/0309_auto__add_ionmeshnode.py create mode 100644 dbReports/iondb/rundb/migrations/0310_auto__del_field_globalconfig_reference_path__del_field_globalconfig_fa.py create mode 100644 dbReports/iondb/rundb/migrations/0311_auto__del_variantfrequencies__del_librarykit__del_sequencingkit.py create mode 100644 dbReports/iondb/rundb/migrations/0312_auto__del_field_applproduct_defaultPairedEndAdapterKit__del_field_appl.py create mode 100644 dbReports/iondb/rundb/migrations/0313_auto__add_plugin_result_job.py create mode 100644 dbReports/iondb/rundb/migrations/0314_plugin_result_job.py create mode 100644 dbReports/iondb/rundb/migrations/0315_auto__remove_plugin_result_fields.py create mode 100644 dbReports/iondb/rundb/migrations/0316_correct_pluginResult_modelDef.py create mode 100644 dbReports/iondb/rundb/migrations/0317_auto__add_field_common_cv_isVisible.py create mode 100644 dbReports/iondb/rundb/migrations/0318_auto__add_plansession.py create mode 100644 dbReports/iondb/rundb/migrations/0319_auto__chg_field_experiment_flowsInOrder__chg_field_floworder_flowOrder.py create mode 100644 dbReports/iondb/rundb/migrations/0320_auto__add_field_experimentanalysissettings_sseBedFile.py create mode 100644 dbReports/iondb/rundb/migrations/0321_auto__chg_field_newspost_title__chg_field_newspost_summary__chg_field_.py create mode 100644 dbReports/iondb/rundb/migrations/0322_data_update_plannedexperiment_samplePrepProtocol.py create mode 100644 dbReports/iondb/rundb/plan/ampliseq_to_TS_plan_convertor.py create mode 100644 dbReports/iondb/rundb/plan/ampliseq_validator.py create mode 100644 dbReports/iondb/rundb/plan/plan_csv_iru_validator.py delete mode 100755 dbReports/iondb/rundb/test.py create mode 100644 dbReports/iondb/security/__init__.py create mode 100644 dbReports/iondb/security/admin.py create mode 100644 dbReports/iondb/security/api.py create mode 100644 dbReports/iondb/security/migrations/0001_initial.py create mode 100644 dbReports/iondb/security/migrations/__init__.py create mode 100644 dbReports/iondb/security/models.py create mode 100644 dbReports/iondb/security/tests/SecureStringResourceTest.py create mode 100644 dbReports/iondb/security/tests/SecureStringTest.py create mode 100644 dbReports/iondb/security/tests/__init__.py create mode 100644 dbReports/iondb/security/urls.py create mode 100644 dbReports/iondb/templates/rundb/common/application_kendo_template.html create mode 100644 dbReports/iondb/templates/rundb/common/plan_template_zip_bundle_upload.html create mode 100644 dbReports/iondb/templates/rundb/configure/configure_mesh.html create mode 100644 dbReports/iondb/templates/rundb/configure/plugins/plugin_configure_js.html create mode 100644 dbReports/iondb/templates/rundb/plan/modal_upload_and_install_files.html create mode 100644 dbReports/iondb/templates/rundb/reports/blocks/plugin_barcodes_table.html create mode 100644 gpu/bandwidthTest.cu create mode 100644 gpu/common/inc/GL/freeglut.h create mode 100644 gpu/common/inc/GL/freeglut_ext.h create mode 100644 gpu/common/inc/GL/freeglut_std.h create mode 100644 gpu/common/inc/GL/glew.h create mode 100644 gpu/common/inc/GL/glext.h create mode 100644 gpu/common/inc/GL/glut.h create mode 100644 gpu/common/inc/GL/glxew.h create mode 100644 gpu/common/inc/GL/glxext.h create mode 100644 gpu/common/inc/GL/wglew.h create mode 100644 gpu/common/inc/cuda_drvapi_dynlink.c create mode 100644 gpu/common/inc/drvapi_error_string.h create mode 100644 gpu/common/inc/dynlink/cuda_drvapi_dynlink.h create mode 100644 gpu/common/inc/dynlink/cuda_drvapi_dynlink_cuda.h create mode 100644 gpu/common/inc/dynlink/cuda_drvapi_dynlink_d3d.h create mode 100644 gpu/common/inc/dynlink/cuda_drvapi_dynlink_gl.h create mode 100644 gpu/common/inc/dynlink_d3d10.h create mode 100644 gpu/common/inc/dynlink_d3d11.h create mode 100644 gpu/common/inc/exception.h create mode 100644 gpu/common/inc/helper_cuda.h create mode 100644 gpu/common/inc/helper_cuda_drvapi.h create mode 100644 gpu/common/inc/helper_cuda_gl.h create mode 100644 gpu/common/inc/helper_cusolver.h create mode 100644 gpu/common/inc/helper_functions.h create mode 100644 gpu/common/inc/helper_gl.h create mode 100644 gpu/common/inc/helper_image.h create mode 100644 gpu/common/inc/helper_math.h create mode 100644 gpu/common/inc/helper_string.h create mode 100644 gpu/common/inc/helper_timer.h create mode 100644 gpu/common/inc/multithreading.h create mode 100644 gpu/common/inc/nvMath.h create mode 100644 gpu/common/inc/nvMatrix.h create mode 100644 gpu/common/inc/nvQuaternion.h create mode 100644 gpu/common/inc/nvShaderUtils.h create mode 100644 gpu/common/inc/nvVector.h create mode 100644 gpu/common/inc/nvrtc_helper.h create mode 100644 gpu/common/inc/param.h create mode 100644 gpu/common/inc/paramgl.h create mode 100644 gpu/common/inc/rendercheck_d3d10.h create mode 100644 gpu/common/inc/rendercheck_d3d11.h create mode 100644 gpu/common/inc/rendercheck_d3d9.h create mode 100644 gpu/common/inc/rendercheck_gl.h create mode 100644 gpu/common/inc/rendercheck_gles.h create mode 100644 gpu/common/inc/timer.h create mode 100644 gpu/deviceQuery.cpp create mode 100755 pipeline/oia/install2_OIA.sh create mode 100644 pipeline/oia/oiaTimingPlot.py delete mode 100755 pipeline/oia/pipeline_test.sh create mode 100644 pipeline/oia/pkg_deb.txt create mode 100644 pipeline/oia/pkg_oia.txt create mode 100644 pipeline/oia/pkg_proton.txt create mode 100644 pipeline/python/ion/plugin/barcodetable_columns.py create mode 100644 pipeline/python/ion/plugin/bedParser.py create mode 100644 pipeline/python/ion/utils/makeSSA.py create mode 100644 pipeline/python/ion/utils/pci_devices.py delete mode 100644 pipeline/tests/Makefile delete mode 100755 pipeline/tests/plugins/Plugin_01_Minimal/Plugin_01_Minimal.py delete mode 100755 pipeline/tests/plugins/Plugin_02_HelloWorld/Plugin_02_HelloWorld.py delete mode 100755 pipeline/tests/plugins/Plugin_04_LaunchCompat/launch.sh delete mode 100644 pipeline/tests/test_pipeline.py delete mode 100644 pipeline/tests/test_plugin_loader.py delete mode 100644 pipeline/tests/test_plugins.py mode change 100644 => 100755 plugin/DataExport/DataExport.py create mode 100644 plugin/FieldSupport/CMakeLists.txt create mode 100644 plugin/FieldSupport/FieldSupport.py create mode 100644 plugin/FieldSupport/README.md create mode 100644 plugin/FieldSupport/fabfile.py create mode 100644 plugin/FieldSupport/rndplugins/bubblePlots/bubble3.plots.R create mode 100755 plugin/FieldSupport/rndplugins/bubblePlots/bubblePlots.pl create mode 100755 plugin/FieldSupport/rndplugins/bubblePlots/launch.sh create mode 100644 plugin/FieldSupport/templates/status_block.html create mode 100755 plugin/RunTransfer/debian/postinst.in delete mode 100644 plugin/RunTransfer/etc/RunTransfer.pdf delete mode 100644 plugin/RunTransfer/etc/icon.png delete mode 100644 plugin/RunTransfer/etc/screenshot.png delete mode 100644 plugin/RunTransfer/package.json delete mode 100644 plugin/RunTransfer/scripts/pscp_connection.sh delete mode 100755 plugin/RunTransfer/scripts/rsync_connection.sh delete mode 100644 plugin/RunTransfer/scripts/ssh_connection.sh mode change 100644 => 100755 plugin/coverageAnalysis/coverageAnalysis.py create mode 100755 plugin/coverageAnalysis/scripts/gcbias.R mode change 100644 => 100755 plugin/sampleID/sampleID.py create mode 100644 plugin/variantCaller/bin/TvcVcfFile.py create mode 100644 plugin/variantCaller/bin/swap_duplicates.py delete mode 100755 plugin/variantCaller/bin/tmol_pipeline.py delete mode 100644 plugin/variantCaller/copytoreport/lifegrid/allelesTableTagSeq.js delete mode 100755 plugin/variantCaller/lib/libbamtools.so delete mode 100755 plugin/variantCaller/lib/libbamtools.so.2.3.0 rename plugin/variantCaller/pluginMedia/parameter_sets/{ => 5_2}/ampliseq_germline_lowstringency_530_Chef_parameters.json (100%) create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_germline_lowstringency_530_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_germline_lowstringency_540_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_germline_lowstringency_p1_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_germline_lowstringency_pgm_parameters.json rename plugin/variantCaller/pluginMedia/parameter_sets/{ => 5_2}/ampliseq_somatic_lowstringency_530_Chef_parameters.json (100%) create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_somatic_lowstringency_530_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_somatic_lowstringency_p1-540_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ampliseq_somatic_lowstringency_pgm_parameters.json rename plugin/variantCaller/pluginMedia/parameter_sets/{ => 5_2}/ampliseqexome_germline_lowstringency_540_parameters.json (100%) rename plugin/variantCaller/pluginMedia/parameter_sets/{ => 5_2}/ampliseqexome_germline_lowstringency_p1_parameters.json (100%) create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/builtin_parameter_sets.json create mode 100755 plugin/variantCaller/pluginMedia/parameter_sets/5_2/ocp_somatic_lowstringency_540_parameters.json rename plugin/variantCaller/pluginMedia/parameter_sets/{ => 5_2}/ocp_somatic_lowstringency_pgm_parameters.json (100%) create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/parameter_sets.json rename plugin/variantCaller/pluginMedia/parameter_sets/{ => 5_2}/pgx4_germline_lowstringency_pgm_parameters.json (100%) create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/tagseq_liquidbiopsy_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/tagseq_tumor_parameters.json create mode 100644 plugin/variantCaller/pluginMedia/parameter_sets/5_2/targetseq_germline_lowstringency_p1_parameters.json create mode 100644 plugin/variantCaller/scripts/filterVCFbyTarget.py create mode 100644 plugin/variantCaller/templates/processing_status.html create mode 100644 plugin/variantCaller/templates/report_incomplete_block.html create mode 100644 torrentR/R/findAdapter.R create mode 100644 torrentR/src/findAdapter.cpp delete mode 100644 tsconfig/ansible/roles/fileserver/templates/hosts.j2 rename tsconfig/ansible/{files => templates}/sentry_settings.j2 (100%) create mode 100644 tsconfig/ansible/test.yml create mode 100755 tsconfig/apt-mirror/link-mirror.sh create mode 100755 tsconfig/apt-mirror/link-test-mirror.sh create mode 100644 tsconfig/apt-mirror/mirror.list create mode 100644 tsconfig/apt-mirror/test-mirror.list delete mode 100755 tsconfig/bin/TSconfig-ansible delete mode 100755 tsconfig/bin/TSconfig-legacy delete mode 100755 tsconfig/bin/TScreateLocalRepository delete mode 100755 tsconfig/bin/TSstaticip-ansible delete mode 100755 tsconfig/bin/TSstaticip-legacy mode change 100644 => 100755 tsconfig/ion_tsconfig/TSconfig.py create mode 100644 tsconfig/rnd_playbooks/README.md create mode 100644 tsconfig/rnd_playbooks/open_iptables.j2 create mode 100644 tsconfig/rnd_playbooks/open_iptables.yml rename tsconfig/{ansible => rnd_playbooks}/sentry.yml (91%) create mode 100644 tsconfig/rnd_playbooks/sshd_keyonly.yml create mode 100644 tsconfig/rnd_playbooks/torrentpy_database.yml create mode 100644 tsvm/tsvm-enable-nfs-rule mode change 100644 => 100755 tsvm/tsvm-include diff --git a/Analysis/AnalysisOrg/ClonalFilter.cpp b/Analysis/AnalysisOrg/ClonalFilter.cpp index c8bedfc9..cc679d77 100644 --- a/Analysis/AnalysisOrg/ClonalFilter.cpp +++ b/Analysis/AnalysisOrg/ClonalFilter.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 2012 Ion Torrent Systems, Inc. All Rights Reserved */ #include "ClonalFilter.h" -#include "mixed.h" +#include "ClonalFilter/mixed.h" #include "hdf5.h" using namespace std; diff --git a/Analysis/AnalysisOrg/IO/BeadfindControlOpts.cpp b/Analysis/AnalysisOrg/IO/BeadfindControlOpts.cpp index 617bb9cd..b9e438a0 100644 --- a/Analysis/AnalysisOrg/IO/BeadfindControlOpts.cpp +++ b/Analysis/AnalysisOrg/IO/BeadfindControlOpts.cpp @@ -88,6 +88,9 @@ void BeadfindControlOpts::PrintHelp() printf (" --beadfind-zero-flows STRING beadfind double tap flows []\n"); printf (" --beadfind-num-threads INT beadfind number of threads [-1]\n"); printf (" --beadfind-mesh-step VECTOR_INT beadfind mesh steps for differntial separator [100,100]\n"); + printf (" --beadfind-acq-threshold VECTOR_INT beadfind threshold for hinge model for acquisition flow [-10,12,5,500]\n"); + printf (" --beadfind-bf-threshold VECTOR_INT beadfind threshold for hinge model for beadfind flow [-5,300,-20000,-10]\n"); + printf (" --exclusion-mask FILE exclusion mask file name []\n"); printf (" --bfold BOOL BF_ADVANCED [true]\n"); printf (" --noduds BOOL noduds [false]\n"); printf ("\n"); @@ -95,79 +98,93 @@ void BeadfindControlOpts::PrintHelp() void BeadfindControlOpts::SetOpts(OptArgs &opts, Json::Value& json_params) { - beadfindType = RetrieveParameterString(opts, json_params, '-', "beadfind-type", "differential"); - if ( beadfindType != "differential" ) - { - fprintf ( stderr, "*Error* - Illegal option to --beadfind-type: %s, valid options are 'differential'\n", - beadfindType.c_str() ); - exit ( EXIT_FAILURE ); - } - string s1 = RetrieveParameterString(opts, json_params, '-', "use-beadmask", ""); - if(s1.length() > 0) - { - sprintf(beadMaskFile, "%s", s1.c_str()); - } - maskFileCategorized = RetrieveParameterBool(opts, json_params, '-', "beadmask-categorized", false); - string s2 = RetrieveParameterString(opts, json_params, '-', "beadfind-basis", "naoh"); - if(s2.length() > 0) - { - bfType = s2; - if ( bfType != "naoh" && bfType != "positive" && bfType != "nobuffer") - { - fprintf ( stderr, "*Error* - Illegal option to --beadfind-basis: %s, valid options are 'naoh','nobuffer' or 'positive'\n", bfType.c_str() ); - exit ( EXIT_FAILURE ); - } - } - bfDat = RetrieveParameterString(opts, json_params, '-', "beadfind-dat", "beadfind_pre_0003.dat"); - bfBgDat = RetrieveParameterString(opts, json_params, '-', "beadfind-bgdat", ""); - sdAsBf = RetrieveParameterBool(opts, json_params, '-', "beadfind-sdasbf", true); - bfMult = RetrieveParameterFloat(opts, json_params, '-', "beadfind-bfmult", 1.0); - bfMinLiveRatio = RetrieveParameterDouble(opts, json_params, '-', "beadfind-minlive", 0.0001); - bfMinLiveLibSnr = RetrieveParameterDouble(opts, json_params, '-', "beadfind-minlivesnr", 4.0); + beadfindType = RetrieveParameterString(opts, json_params, '-', "beadfind-type", "differential"); + if ( beadfindType != "differential" ) + { + fprintf ( stderr, "*Error* - Illegal option to --beadfind-type: %s, valid options are 'differential'\n", + beadfindType.c_str() ); + exit ( EXIT_FAILURE ); + } + string s1 = RetrieveParameterString(opts, json_params, '-', "use-beadmask", ""); + if(s1.length() > 0) + { + sprintf(beadMaskFile, "%s", s1.c_str()); + } + maskFileCategorized = RetrieveParameterBool(opts, json_params, '-', "beadmask-categorized", false); + + exclusionMaskFile = RetrieveParameterString(opts, json_params, '-', "exclusion-mask", ""); + + string s2 = RetrieveParameterString(opts, json_params, '-', "beadfind-basis", "naoh"); + if(s2.length() > 0) + { + bfType = s2; + if ( bfType != "naoh" && bfType != "positive" && bfType != "nobuffer") + { + fprintf ( stderr, "*Error* - Illegal option to --beadfind-basis: %s, valid options are 'naoh','nobuffer' or 'positive'\n", bfType.c_str() ); + exit ( EXIT_FAILURE ); + } + } + bfDat = RetrieveParameterString(opts, json_params, '-', "beadfind-dat", "beadfind_pre_0003.dat"); + bfBgDat = RetrieveParameterString(opts, json_params, '-', "beadfind-bgdat", ""); + sdAsBf = RetrieveParameterBool(opts, json_params, '-', "beadfind-sdasbf", true); + bfMult = RetrieveParameterFloat(opts, json_params, '-', "beadfind-bfmult", 1.0); + bfMinLiveRatio = RetrieveParameterDouble(opts, json_params, '-', "beadfind-minlive", 0.0001); + bfMinLiveLibSnr = RetrieveParameterDouble(opts, json_params, '-', "beadfind-minlivesnr", 4.0); bfMinLiveTfSnr = RetrieveParameterDouble(opts, json_params, '-', "beadfind-min-tf-snr", 7); - minTfPeakMax = RetrieveParameterFloat(opts, json_params, '-', "beadfind-tf-min-peak", 40.0); - minLibPeakMax = RetrieveParameterFloat(opts, json_params, '-', "beadfind-lib-min-peak", 10.0); - bfLibFilterQuantile = RetrieveParameterDouble(opts, json_params, '-', "beadfind-lib-filt", 1.0); - bfTfFilterQuantile = RetrieveParameterDouble(opts, json_params, '-', "beadfind-tf-filt", 1.0); - skipBeadfindSdRecover = RetrieveParameterInt(opts, json_params, '-', "beadfind-skip-sd-recover", 1); + minTfPeakMax = RetrieveParameterFloat(opts, json_params, '-', "beadfind-tf-min-peak", 40.0); + minLibPeakMax = RetrieveParameterFloat(opts, json_params, '-', "beadfind-lib-min-peak", 10.0); + bfLibFilterQuantile = RetrieveParameterDouble(opts, json_params, '-', "beadfind-lib-filt", 1.0); + bfTfFilterQuantile = RetrieveParameterDouble(opts, json_params, '-', "beadfind-tf-filt", 1.0); + skipBeadfindSdRecover = RetrieveParameterInt(opts, json_params, '-', "beadfind-skip-sd-recover", 1); filterNoisyCols = RetrieveParameterString(opts, json_params, '-', "beadfind-filt-noisy-col", "none"); - beadfindUseSepRef = RetrieveParameterBool(opts, json_params, '-', "beadfind-sep-ref", false); - beadfindSmoothTrace = RetrieveParameterBool(opts, json_params, '-', "beadfind-smooth-trace", false); + beadfindUseSepRef = RetrieveParameterBool(opts, json_params, '-', "beadfind-sep-ref", false); + beadfindSmoothTrace = RetrieveParameterBool(opts, json_params, '-', "beadfind-smooth-trace", false); bfOutputDebug = RetrieveParameterInt(opts, json_params, '-', "beadfind-diagnostics", 2); - bool b1 = RetrieveParameterBool(opts, json_params, '-', "bead-washout", false); - SINGLEBF = !b1; - useBeadfindGainCorrection = RetrieveParameterBool(opts, json_params, '-', "beadfind-gain-correction", true); - useDatacollectGainCorrectionFile = RetrieveParameterBool(opts, json_params, '-', "datacollect-gain-correction", false); - blobFilter = RetrieveParameterBool(opts, json_params, '-', "beadfind-blob-filter", false); - predictFlowStart = RetrieveParameterInt(opts, json_params, '-', "beadfind-predict-start", -1); - predictFlowEnd = RetrieveParameterInt(opts, json_params, '-', "beadfind-predict-end", -1); - useSignalReference = RetrieveParameterInt(opts, json_params, '-', "beadfind-sig-ref-type", -1); - if(useSignalReference == -1) - { - useSignalReference = 4; - } - else - { - useSignalReferenceSet = true; - } - doubleTapFlows = RetrieveParameterString(opts, json_params, '-', "beadfind-zero-flows", ""); - numThreads = RetrieveParameterInt(opts, json_params, '-', "beadfind-num-threads", -1); + bool b1 = RetrieveParameterBool(opts, json_params, '-', "bead-washout", false); + SINGLEBF = !b1; + useBeadfindGainCorrection = RetrieveParameterBool(opts, json_params, '-', "beadfind-gain-correction", true); + useDatacollectGainCorrectionFile = RetrieveParameterBool(opts, json_params, '-', "datacollect-gain-correction", false); + blobFilter = RetrieveParameterBool(opts, json_params, '-', "beadfind-blob-filter", false); + predictFlowStart = RetrieveParameterInt(opts, json_params, '-', "beadfind-predict-start", -1); + predictFlowEnd = RetrieveParameterInt(opts, json_params, '-', "beadfind-predict-end", -1); + useSignalReference = RetrieveParameterInt(opts, json_params, '-', "beadfind-sig-ref-type", -1); + if(useSignalReference == -1) + { + useSignalReference = 4; + } + else + { + useSignalReferenceSet = true; + } + doubleTapFlows = RetrieveParameterString(opts, json_params, '-', "beadfind-zero-flows", ""); + numThreads = RetrieveParameterInt(opts, json_params, '-', "beadfind-num-threads", -1); - //jz the following comes from CommandLineOpts::GetOpts - BF_ADVANCED = RetrieveParameterBool(opts, json_params, '-', "bfold", true); - noduds = RetrieveParameterBool(opts, json_params, '-', "noduds", false); - string sb = RetrieveParameterString(opts, json_params, 'b', "beadfindfile", ""); - if(sb.length() > 0) - { - snprintf ( preRunbfFileBase, 256, "%s", sb.c_str() ); + //jz the following comes from CommandLineOpts::GetOpts + BF_ADVANCED = RetrieveParameterBool(opts, json_params, '-', "bfold", true); + noduds = RetrieveParameterBool(opts, json_params, '-', "noduds", false); + string sb = RetrieveParameterString(opts, json_params, 'b', "beadfindfile", ""); + if(sb.length() > 0) + { + snprintf ( preRunbfFileBase, 256, "%s", sb.c_str() ); bfFileBase[0] = '\0'; SINGLEBF = true; - } - vector vec; - RetrieveParameterVectorInt(opts, json_params, '-', "beadfind-mesh-step", "", vec); - if(vec.size() == 2) - { - meshStepX = vec[0]; - meshStepY = vec[1]; - } + } + vector vec; + RetrieveParameterVectorInt(opts, json_params, '-', "beadfind-mesh-step", "", vec); + if(vec.size() == 2) + { + meshStepX = vec[0]; + meshStepY = vec[1]; + } + + RetrieveParameterVectorFloat(opts, json_params, '-', "beadfind-acq-threshold","-10,3,5,500", beadfindAcqThreshold); + if( (beadfindAcqThreshold.size() != 4) || (beadfindAcqThreshold[0] > beadfindAcqThreshold[1]) || (beadfindAcqThreshold[2] > beadfindAcqThreshold[3]) ){ + fprintf ( stderr, "*Error* - Illegal option value --beadfind-acq-threshold. Provide 4 values minFirstSlope,maxFirstSlope,minSecondSlope,maxSecondSlope. \n"); + exit ( EXIT_FAILURE ); + } + RetrieveParameterVectorFloat(opts, json_params, '-', "beadfind-bf-threshold","-5,300,-2000,-10", beadfindBfThreshold); + if( (beadfindBfThreshold.size() != 4) || (beadfindBfThreshold[0] > beadfindBfThreshold[1]) || (beadfindBfThreshold[2] > beadfindBfThreshold[3]) ){ + fprintf ( stderr, "*Error* - Illegal option value --beadfind-bf-threshold. Provide 4 values minFirstSlope,maxFirstSlope,minSecondSlope,maxSecondSlope. \n"); + exit ( EXIT_FAILURE ); + } } diff --git a/Analysis/AnalysisOrg/IO/BeadfindControlOpts.h b/Analysis/AnalysisOrg/IO/BeadfindControlOpts.h index 7bedce7a..4b787bf5 100644 --- a/Analysis/AnalysisOrg/IO/BeadfindControlOpts.h +++ b/Analysis/AnalysisOrg/IO/BeadfindControlOpts.h @@ -22,6 +22,7 @@ class BeadfindControlOpts{ bool beadfindSmoothTrace; std::string filterNoisyCols; char *beadMaskFile; + std::string exclusionMaskFile; bool maskFileCategorized; char bfFileBase[MAX_PATH_LENGTH]; char preRunbfFileBase[MAX_PATH_LENGTH]; @@ -49,6 +50,8 @@ class BeadfindControlOpts{ int predictFlowEnd; int meshStepX; int meshStepY; + std::vector beadfindAcqThreshold; + std::vector beadfindBfThreshold; void DefaultBeadfindControl(); void PrintHelp(); void SetOpts(OptArgs &opts, Json::Value& json_params); diff --git a/Analysis/AnalysisOrg/IO/BkgControlOpts.cpp b/Analysis/AnalysisOrg/IO/BkgControlOpts.cpp index 9da9281d..8af74de4 100644 --- a/Analysis/AnalysisOrg/IO/BkgControlOpts.cpp +++ b/Analysis/AnalysisOrg/IO/BkgControlOpts.cpp @@ -156,76 +156,40 @@ void TraceControl::SetOpts(OptArgs &opts, Json::Value& json_params) void BkgModelControlOpts::PrintHelp() { printf (" BkgModelControlOpts\n"); - printf (" --mixed-first-flow INT mixed first flow of polyclonal filter [12]\n"); - printf (" --mixed-last-flow INT mixed last flow of polyclonal filter [72]\n"); - printf (" --max-iterations INT max iterations of polyclonal filter [30]\n"); - printf (" --mixed-model-option INT mixed model option of polyclonal filter [0]\n"); - printf (" --mixed-stringency DOUBLE mixed stringency of polyclonal filter [0.5]\n"); printf (" --nokey BOOL nokey [false]\n"); printf (" --xtalk-correction BOOL enable trace xtalk correction [false for Proton; true for P-zero and PGM]\n"); printf (" --n-unfiltered-lib INT number of unfiltered library random samples [100000]\n"); printf (" --bkg-dont-emphasize-by-compression INT emphasize by compression [1]\n"); - printf (" --clonal-filter-bkgmodel BOOL enable polyclonal filter [false for Proton; true for PGM]\n"); - printf (" --clonal-filter-debug BOOL enable polyclonal filter debug output \n"); - printf (" --clonal-filter-use-last-iter-params BOOL use last EM iteration cluster parameters if no convergence \n"); - printf (" --filter-extreme-ppf-only BOOL Skip polyclonal filter training and filter for extreme ppf only \n"); printf (" --sigproc-regional-smoothing-alpha FLOAT sigproc regional smoothing alpha [1.0]\n"); printf (" --sigproc-regional-smoothing-gamma FLOAT sigproc regional smoothing gamma [1.0]\n"); printf (" --restart-reg-params-file STRING json file to input/output regional parameters\n"); printf ("\n"); + polyclonal_filter.PrintHelp(true); signal_chunks.PrintHelp(); trace_control.PrintHelp(); pest_control.PrintHelp(); gpuControl.PrintHelp(); } -void BkgModelControlOpts::SetOpts(OptArgs &opts, Json::Value& json_params) +void BkgModelControlOpts::SetOpts(OptArgs &opts, Json::Value& json_params, int num_flows) { gpuControl.SetOpts(opts, json_params); signal_chunks.SetOpts(opts, json_params); pest_control.SetOpts(opts, json_params); trace_control.SetOpts(opts, json_params); + polyclonal_filter.SetOpts(true, opts,json_params, num_flows); + //jz the following comes from CommandLineOpts::GetOpts unfiltered_library_random_sample = RetrieveParameterInt(opts, json_params, '-', "n-unfiltered-lib", 100000); enable_trace_xtalk_correction = RetrieveParameterBool(opts, json_params, '-', "xtalk-correction", true); emphasize_by_compression = RetrieveParameterInt(opts, json_params, '-', "bkg-dont-emphasize-by-compression", 1); nokey = RetrieveParameterBool(opts, json_params, '-', "nokey", false); + washout_threshold = RetrieveParameterFloat(opts, json_params, '-', "bkg-washout-threshold", WASHOUT_THRESHOLD); washout_flow_detection = RetrieveParameterInt(opts, json_params, '-', "bkg-washout-flow-detection", WASHOUT_FLOW_DETECTION); - polyclonal_filter.enable = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-bkgmodel", true); - polyclonal_filter.mixed_first_flow = RetrieveParameterInt(opts, json_params, '-', "mixed-first-flow", 12); - polyclonal_filter.mixed_last_flow = RetrieveParameterInt(opts, json_params, '-', "mixed-last-flow", 72); - polyclonal_filter.max_iterations = RetrieveParameterInt(opts, json_params, '-', "max-iterations", 30); - polyclonal_filter.mixed_model_option = RetrieveParameterInt(opts, json_params, '-', "mixed-model-option", 0); - polyclonal_filter.verbose = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-debug", false); - polyclonal_filter.use_last_iter_params = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-use-last-iter-params", true); - polyclonal_filter.filter_extreme_ppf_only = RetrieveParameterBool(opts, json_params, '-', "filter-extreme-ppf-only", false); - double stringency = RetrieveParameterDouble(opts, json_params, '-', "mixed-stringency", 0.5); - if(stringency < 0) - { - stringency = 0; - } - if(stringency > 1) - { - stringency = 1; - } - //transform it to log scale - if(stringency > 0.5) - { - polyclonal_filter.mixed_stringency = 0.5 * std::log10((stringency - 0.5)*18 + 1) + 0.5; - } - else if (stringency< 0.5) - { - polyclonal_filter.mixed_stringency = 0.5 - 0.5 * std::log10(( 0.5 - stringency)*18 + 1); - } - else - { - polyclonal_filter.mixed_stringency = 0.5; - } - regional_smoothing.alpha = RetrieveParameterFloat(opts, json_params, '-', "sigproc-regional-smoothing-alpha", 1.0f); regional_smoothing.gamma = RetrieveParameterFloat(opts, json_params, '-', "sigproc-regional-smoothing-gamma", 1.0f); diff --git a/Analysis/AnalysisOrg/IO/BkgControlOpts.h b/Analysis/AnalysisOrg/IO/BkgControlOpts.h index 992583c0..a74292b5 100644 --- a/Analysis/AnalysisOrg/IO/BkgControlOpts.h +++ b/Analysis/AnalysisOrg/IO/BkgControlOpts.h @@ -20,7 +20,7 @@ #include "SeqList.h" #include "GpuControlOpts.h" #include "DebugMe.h" -#include "polyclonal_filter.h" +#include "ClonalFilter/polyclonal_filter.h" #include "FlowSequence.h" #include "OptBase.h" @@ -94,7 +94,7 @@ class BkgModelControlOpts{ void DefaultBkgModelControl(void); void PrintHelp(); - void SetOpts(OptArgs &opts, Json::Value& json_params); + void SetOpts(OptArgs &opts, Json::Value& json_params, int num_flows); }; #endif // BKGCONTROLOPTS_H diff --git a/Analysis/AnalysisOrg/IO/CommandLineOpts.cpp b/Analysis/AnalysisOrg/IO/CommandLineOpts.cpp index d97e43b2..292e6dd2 100644 --- a/Analysis/AnalysisOrg/IO/CommandLineOpts.cpp +++ b/Analysis/AnalysisOrg/IO/CommandLineOpts.cpp @@ -13,7 +13,7 @@ #include "CommandLineOpts.h" #include "ChipIdDecoder.h" #include "IonErr.h" -#include "mixed.h" +#include "ClonalFilter/mixed.h" using namespace std; @@ -93,22 +93,23 @@ void CommandLineOpts::PrintHelp() void CommandLineOpts::SetOpts(OptArgs &opts, Json::Value& json_params) { - bkg_control.DefaultBkgModelControl(); - bkg_control.SetOpts(opts, json_params); - bfd_control.DefaultBeadfindControl(); - bfd_control.SetOpts(opts, json_params); - img_control.DefaultImageOpts(); - img_control.SetOpts(opts, json_params); - mod_control.SetOpts(opts, json_params); - loc_context.DefaultSpatialContext(); - loc_context.SetOpts(opts, json_params); - flow_context.DefaultFlowFormula(); - flow_context.SetOpts(opts, json_params); - key_context.DefaultKeys(); - key_context.SetOpts(opts, json_params); - no_control.SetOpts(opts, json_params); - sys_context.DefaultSystemContext(); - sys_context.SetOpts(opts, json_params); + flow_context.DefaultFlowFormula(); + flow_context.SetOpts(opts, json_params); + bkg_control.DefaultBkgModelControl(); + // Apparently flow_context does not have a member that gives me the total number of flows in a run, so I'm hard coding this baby! + bkg_control.SetOpts(opts, json_params, 100000); + bfd_control.DefaultBeadfindControl(); + bfd_control.SetOpts(opts, json_params); + img_control.DefaultImageOpts(); + img_control.SetOpts(opts, json_params); + mod_control.SetOpts(opts, json_params); + loc_context.DefaultSpatialContext(); + loc_context.SetOpts(opts, json_params); + key_context.DefaultKeys(); + key_context.SetOpts(opts, json_params); + no_control.SetOpts(opts, json_params); + sys_context.DefaultSystemContext(); + sys_context.SetOpts(opts, json_params); // We can only do save and restore on an even flow block boundary. // Now that all the parameters have been set, we can check their sanity. @@ -226,6 +227,7 @@ ValidateOpts::ValidateOpts() // BeadfindControlOpts m_opts["beadfind-type"] = VT_STRING; m_opts["use-beadmask"] = VT_STRING; + m_opts["exclusion-mask"] = VT_STRING; m_opts["beadmask-categorized"] = VT_BOOL; m_opts["beadfind-basis"] = VT_STRING; m_opts["beadfind-dat"] = VT_STRING; @@ -437,6 +439,11 @@ void ValidateOpts::Validate(const int argc, char *argv[]) { if((!isdigit(argv[i][1])) && (argv[i][1] != '.')) { + if(s.length() > 2 && argv[i][1] != '-' && argv[i][2] != ' ' && argv[i][2] != '=') // handle mis-typing long option to short option + { + fprintf ( stdout, "WARNING: %s may miss a leading - . Please check if it is a long option.\n", s.c_str()); + } + s = s.substr(1, s.length() - 1); if(argv[i][1] == '-') // long option { diff --git a/Analysis/AnalysisOrg/IO/FlowContext.cpp b/Analysis/AnalysisOrg/IO/FlowContext.cpp index ea4dc0b9..99b968ff 100644 --- a/Analysis/AnalysisOrg/IO/FlowContext.cpp +++ b/Analysis/AnalysisOrg/IO/FlowContext.cpp @@ -36,9 +36,11 @@ void FlowContext::DetectFlowFormula ( char *explog_path ) // @TODO: obviously needs to be refactored into flow routine // expand flow formula = flowOrder into appropriate number of flows //Determine total number of flows in experiment or previous analysis - if ( true ) { - numTotalFlows = GetTotalFlows (explog_path ); - assert ( numTotalFlows > 0 ); + numTotalFlows = GetTotalFlows (explog_path ); + //assert ( numTotalFlows > 0 ); //TS-14040: changed assert to error log and exit + if ( numTotalFlows <= 0 ) { + fprintf ( stderr, "DetectFlowFormula Error: numTotalFlows=%d<=0\n",numTotalFlows); + exit ( EXIT_FAILURE ); } //If flow order was not specified on command line, @@ -47,12 +49,10 @@ void FlowContext::DetectFlowFormula ( char *explog_path ) if ( flowOrder ) free ( flowOrder ); // Get flow order from the explog.txt file - if ( true ) { flowOrder = GetPGMFlowOrder ( explog_path ); assert ( flowOrder != NULL ); numFlowsPerCycle = strlen ( flowOrder ); assert ( numFlowsPerCycle > 0 ); - } } // Adjust number of flows according to any command line options which may have been used diff --git a/Analysis/AnalysisOrg/IO/ProgramState.cpp b/Analysis/AnalysisOrg/IO/ProgramState.cpp index 99b55182..c026d8c4 100644 --- a/Analysis/AnalysisOrg/IO/ProgramState.cpp +++ b/Analysis/AnalysisOrg/IO/ProgramState.cpp @@ -337,6 +337,7 @@ void ProgramState::SetLocContext(Json::Value &json, SpatialContext &loc_context) loc_context.cropRegions[r].w = json["cropRegions"]["w"][r].asInt(); loc_context.cropRegions[r].h = json["cropRegions"]["h"][r].asInt(); loc_context.cropRegions[r].index = json["cropRegions"]["index"][r].asInt(); + loc_context.isCropped = true; } loc_context.cropped_region_x_offset = json["cropped_region_x_offset"].asInt(); diff --git a/Analysis/AnalysisOrg/ImageLoaderQueue.cpp b/Analysis/AnalysisOrg/ImageLoaderQueue.cpp index ca58713f..b4e77f7c 100644 --- a/Analysis/AnalysisOrg/ImageLoaderQueue.cpp +++ b/Analysis/AnalysisOrg/ImageLoaderQueue.cpp @@ -194,7 +194,10 @@ void *FileLoadWorker ( void *arg ) int buffer_ix = one_img_loader->cur_buffer; // setting the mean of frames to zero will be done by the bkgmodel as soon as its loaded. - img->SetMeanOfFramesToZero ( one_img_loader->normStart, one_img_loader->normEnd,0 ); + +// the traces will be zero'd by the bknd model loader anyway, no need to do them here. + // img->SetMeanOfFramesToZero ( one_img_loader->normStart, one_img_loader->normEnd,0 ); + // calculate the smooth pH step amplitude in empty wells across the whole image if ( one_img_loader->doEmptyWellNormalization ) diff --git a/Analysis/AnalysisOrg/MaskFunctions.cpp b/Analysis/AnalysisOrg/MaskFunctions.cpp index 98d493d4..2dda89bb 100644 --- a/Analysis/AnalysisOrg/MaskFunctions.cpp +++ b/Analysis/AnalysisOrg/MaskFunctions.cpp @@ -1,190 +1,90 @@ /* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ #include "MaskFunctions.h" +void SetExcludeMask(SpatialContext& loc_context, Mask* maskPtr, char* chipType, + int rows, int cols, std::string exclusionMaskFile, + bool isThumbnail) +{ + bool applyExclusionMask = (isThumbnail) ? false : true; + + /* + * If we get a cropped region definition from the command line, we want the + * whole chip to be MaskExclude + * except for the defined crop region(s) which are marked MaskEmpty. If no + * cropRegion defined on command line, + * then we proceed with marking the entire chip MaskEmpty + * + */ + + if ( loc_context.isCropped ) { + maskPtr->Init(cols, rows, MaskExclude); + } else { + maskPtr->Init(cols, rows, MaskEmpty); + } + if (applyExclusionMask && !exclusionMaskFile.empty()) { -//@TODO: Please do not use inline #ifdefs to rewrite code -// this should only be done >at the function level< to maintain readability. -// if you believe the function is too long to do this, >then rewrite the function to be shorter or more than one piece<. + // apply one or more cropRegions, mark them MaskEmpty + for (int q = 0; q < loc_context.numCropRegions; q++) { + maskPtr->MarkRegion(loc_context.cropRegions[q], MaskEmpty); + } - //Uncomment next line to revert to old exclusion mask file usage. Remove once it is passed. -#define OLDWAY + /* + * Apply exclude mask from file + */ + loc_context.exclusionMaskSet = false; -#ifdef OLDWAY + char* exclusionMaskFileName = GetIonConfigFile(exclusionMaskFile.c_str()); -void SetExcludeMask (SpatialContext &loc_context, Mask *maskPtr, char *chipType, int rows, int cols) -{ + fprintf(stderr, "Exclusion Mask File = '%s'\n", exclusionMaskFileName); - bool applyExclusionMask = true; - - /* - * Determine if this is a cropped dataset - * 3 types: - * wholechip image dataset - rows,cols should be == to chip_len_x,chip_len_y - * cropped image dataset - above test is false AND chip_offset_x == -1 - * blocked image dataset - above test is false AND chip_offset_x != -1 - */ - if ( (rows == loc_context.chip_len_y) && (cols == loc_context.chip_len_x)) - { - //This is a wholechip dataset - applyExclusionMask = true; - fprintf (stderr, "This is a wholechip dataset so the exclusion mask will be applied Chip %s\n",chipType); - } - else - { - if (loc_context.chip_offset_x == -1) - { - applyExclusionMask = false; - fprintf (stderr, "This is a cropped dataset so the exclusion mask will not be applied\n"); - fprintf (stderr, "rows=%d, chip_rows=%d,cols=%d, chip_cols=%d for chip %s\n",rows, loc_context.chip_len_y,cols, loc_context.chip_len_x, chipType); - } - else - { - applyExclusionMask = false; - fprintf (stderr, "This is a block dataset so the exclusion mask will not be applied\n"); + if (exclusionMaskFileName) { + loc_context.exclusionMaskSet = true; - } - } - /* - * If we get a cropped region definition from the command line, we want the whole chip to be MaskExclude - * except for the defined crop region(s) which are marked MaskEmpty. If no cropRegion defined on command line, - * then we proceed with marking the entire chip MaskEmpty - */ - if (loc_context.numCropRegions == 0) - { - maskPtr->Init (cols, rows, MaskEmpty); - } - else - { - maskPtr->Init (cols, rows, MaskExclude); - - // apply one or more cropRegions, mark them MaskEmpty - for (int q = 0; q < loc_context.numCropRegions; q++) - { - maskPtr->MarkRegion (loc_context.cropRegions[q], MaskEmpty); - } - } - - /* - * Apply exclude mask from file - */ - loc_context.exclusionMaskSet = false; - if (chipType && applyExclusionMask) - { - char *exclusionMaskFileName = NULL; - char filename[64] = { 0 }; - - sprintf (filename, "exclusionMask_%s.bin", chipType); - - exclusionMaskFileName = GetIonConfigFile (filename); - fprintf (stderr, "Exclusion Mask File = '%s'\n", exclusionMaskFileName); - if (exclusionMaskFileName) - { - loc_context.exclusionMaskSet = true; - - Mask excludeMask (1, 1); - excludeMask.SetMask (exclusionMaskFileName); - free (exclusionMaskFileName); - //--- Mark beadfind masks with MaskExclude bits from exclusionMaskFile - maskPtr->SetThese (&excludeMask, MaskExclude); + Mask excludeMask(loc_context.cols, loc_context.rows); - } - else - { - fprintf (stderr, "WARNING: Exclusion Mask %s not applied\n", filename); - } - } -} + if (exclusionMaskFile.compare(exclusionMaskFile.size() - 3, 3, "txt") == 0) { // this is text mask (e.g. for proton/S5) + std::cerr << "Using text exclusion mask\n"; -#else + // SpatialContext sets offsets to -1 if it can't parse them from the working directory + // We, in principle, want to enable handling of a PGM .txt mask + excludeMask.SetMaskFullChipText( exclusionMaskFileName, + max(0,loc_context.chip_offset_x), max(0,loc_context.chip_offset_y), + loc_context.cols, loc_context.rows ); -void SetExcludeMask (SpatialContext &loc_context, Mask *maskPtr, char *chipType, int rows, int cols) -{ + //--- Mark beadfind masks with MaskExclude bits from exclusionMaskFile + maskPtr->SetThese(&excludeMask, MaskExclude); + } else { // this is binary mask + std::cerr << "Using binary exclusion mask\n"; + excludeMask.SetMask(exclusionMaskFileName); - bool applyExclusionMask = true; - - /* - * Determine if this is a cropped dataset - * 3 types: - * wholechip image dataset - rows,cols should be == to chip_len_x,chip_len_y - * cropped image dataset - above test is false AND chip_offset_x == -1 - * blocked image dataset - above test is false AND chip_offset_x != -1 - */ - if ( (rows == loc_context.chip_len_y) && (cols == loc_context.chip_len_x)) - { - //This is a wholechip dataset - applyExclusionMask = true; - fprintf (stderr, "This is a wholechip dataset so the exclusion mask will be applied\n"); - } - else - { - if (loc_context.chip_offset_x == -1) - { - applyExclusionMask = false; - fprintf (stderr, "This is a cropped dataset so the exclusion mask will not be applied\n"); - } - else - { - applyExclusionMask = true; - fprintf (stderr, "This is a block dataset so the exclusion mask will be applied\n"); - } - } - /* - * If we get a cropped region definition from the command line, we want the whole chip to be MaskExclude - * except for the defined crop region(s) which are marked MaskEmpty. If no cropRegion defined on command line, - * then we proceed with marking the entire chip MaskEmpty - */ - if (loc_context.numCropRegions == 0) - { - maskPtr->Init (cols, rows, MaskEmpty); - } - else - { - maskPtr->Init (cols, rows, MaskExclude); - - // apply one or more cropRegions, mark them MaskEmpty - for (int q = 0; q < loc_context.numCropRegions; q++) - { - maskPtr->MarkRegion (loc_context.cropRegions[q], MaskEmpty); - } - } - - /* - * Apply exclude mask from file - */ - loc_context.exclusionMaskSet = false; - if (chipType && applyExclusionMask) - { - char *exclusionMaskFileName = NULL; - char filename[64] = { 0 }; - - sprintf (filename, "excludeMask_%s", chipType); - - exclusionMaskFileName = GetIonConfigFile (filename); - fprintf (stderr, "Exclusion Mask File = '%s'\n", exclusionMaskFileName); - if (exclusionMaskFileName) - { - loc_context.exclusionMaskSet = true; - - FILE *excludeFile = NULL; - excludeFile = fopen (exclusionMaskFileName,"rb"); - assert (excludeFile != NULL); - uint16_t x = 0; - uint16_t y = 0; - while (1) - { - if (fread (&x, sizeof (x), 1, excludeFile) != 1) break; - if (fread (&y, sizeof (y), 1, excludeFile) != 1) break; - //fprintf (stderr, "Excluding %d %d (%d %d)\n",x,y,(int) x - chip_offset_x,(int) y - chip_offset_y); - maskPtr->Set ( (int) x - loc_context.chip_offset_x, (int) y - loc_context.chip_offset_y,MaskExclude); - } + //--- Mark beadfind masks with MaskExclude bits from exclusionMaskFile + maskPtr->SetThese(&excludeMask, MaskExclude); + } + } else if (chipType) { + std::cerr << "Using binary exclusion mask based on chip type: " + << chipType << "\n"; + char filename[64] = { 0 }; + sprintf(filename, "exclusionMask_%s.bin", chipType); + + exclusionMaskFileName = GetIonConfigFile(filename); + fprintf(stderr, "Exclusion Mask File = '%s'\n", exclusionMaskFileName); + if (exclusionMaskFileName) { + loc_context.exclusionMaskSet = true; + + Mask excludeMask(1, 1); + excludeMask.SetMask(exclusionMaskFileName); + + //--- Mark beadfind masks with MaskExclude bits from exclusionMaskFile + maskPtr->SetThese(&excludeMask, MaskExclude); + + } else { + fprintf(stderr, "WARNING: Exclusion Mask %s not applied\n", filename); + } + } + free(exclusionMaskFileName); + } else { + std::cerr << "Exclusion mask is not applied"; } - else - { - fprintf (stderr, "WARNING: Exclusion Mask %s not applied\n", filename); - } - } } - -#endif diff --git a/Analysis/AnalysisOrg/MaskFunctions.h b/Analysis/AnalysisOrg/MaskFunctions.h index 96de6f71..da50f2c1 100644 --- a/Analysis/AnalysisOrg/MaskFunctions.h +++ b/Analysis/AnalysisOrg/MaskFunctions.h @@ -5,7 +5,8 @@ #include "Mask.h" #include "CommandLineOpts.h" -void SetExcludeMask(SpatialContext &loc_context, Mask *maskPtr, char *chipType, int rows, int cols); +void SetExcludeMask(SpatialContext &loc_context, Mask *maskPtr, char *chipType, int rows, int cols, + std::string exclusionMaskFileName, bool beadfindThumbnail); #endif // MASKFUNCTIONS_H diff --git a/Analysis/AnalysisOrg/ProcessImageToWell.cpp b/Analysis/AnalysisOrg/ProcessImageToWell.cpp index 1dc9e40f..49f34b06 100644 --- a/Analysis/AnalysisOrg/ProcessImageToWell.cpp +++ b/Analysis/AnalysisOrg/ProcessImageToWell.cpp @@ -529,8 +529,8 @@ void ImageToWells::MoveWellsFileAndFinalize() inception_state.loc_context.chip_offset_y, inception_state.loc_context.chip_offset_y + inception_state.loc_context.rows - 1, inception_state.loc_context.chip_len_y) ){ - printf("Masking DataCollect exclude regions as Bad Key\n"); - FromBeadfindMask.my_mask->SetAll((MaskType)(MaskFilteredBadKey)); // MaskIgnore + printf("Masking DataCollect exclude regions as Ignore\n"); + FromBeadfindMask.my_mask->SetAll((MaskType)(MaskIgnore)); } } diff --git a/Analysis/AnalysisOrg/SpatialContext.cpp b/Analysis/AnalysisOrg/SpatialContext.cpp index 98da13ff..281550ce 100644 --- a/Analysis/AnalysisOrg/SpatialContext.cpp +++ b/Analysis/AnalysisOrg/SpatialContext.cpp @@ -20,6 +20,7 @@ void SpatialContext::DefaultSpatialContext() regionYSize = 0; cropRegions = NULL; numCropRegions = 0; + isCropped = false; // some raw image processing (like cross talk correction in the Image class) needs the absolute coordinates of the // pixels in the image. This is easy for a standard data set, but for a cropped data set the origin of the data is // unknown. These allow the user to specify the location of the cropped region so that these parts of analysis @@ -137,6 +138,7 @@ void SpatialContext::SetOpts(OptArgs &opts, Json::Value& json_params) cropRegions[numCropRegions-1].row = vec2[1]; cropRegions[numCropRegions-1].w = vec2[2]; cropRegions[numCropRegions-1].h = vec2[3]; + isCropped = true; } else { diff --git a/Analysis/AnalysisOrg/SpatialContext.h b/Analysis/AnalysisOrg/SpatialContext.h index 648df368..fc8b42a3 100644 --- a/Analysis/AnalysisOrg/SpatialContext.h +++ b/Analysis/AnalysisOrg/SpatialContext.h @@ -25,6 +25,7 @@ class SpatialContext{ int regionsY; Region *cropRegions; int numCropRegions; + bool isCropped; int cropped_region_x_offset; int cropped_region_y_offset; int chip_offset_x; diff --git a/Analysis/AnalysisOrg/cudaWrapper.cpp b/Analysis/AnalysisOrg/cudaWrapper.cpp index 8435dbeb..6d86629d 100644 --- a/Analysis/AnalysisOrg/cudaWrapper.cpp +++ b/Analysis/AnalysisOrg/cudaWrapper.cpp @@ -124,8 +124,9 @@ void gpuDeviceConfig::initDeviceContexts(){ it++; } catch(cudaException &e) { + //throw cudaExecutionException(e.getCudaError(),__FILE__,__LINE__); cout << e.what() << endl; - cout << "CUDA "<< *it << ": Context could not be created. Can't use this GPU." << *it << endl; + cout << "CUDA "<< *it << ": gpuDeviceConfig::initDeviceContexts: Context could not be created. removing device with id: "<< *it << " from valid device list" << endl; it = validDevices.erase (it); } } @@ -196,7 +197,7 @@ bool gpuDeviceConfig::setValidDevices(std::vector &CmdlineDeviceList, bool initDeviceContexts(); - if(! validDevices.size() > 0 ) + if(! (validDevices.size() > 0) ) cout << "CUDA: gpuDeviceConfig: no context could be created, defaulting to CPU only execution" << endl; else cudaSetValidDevices( &validDevices[0], int( validDevices.size())); diff --git a/Analysis/AnalysisOrg/justBeadFind/SeparatorInterface.cpp b/Analysis/AnalysisOrg/justBeadFind/SeparatorInterface.cpp index 79dacece..dbd2b4fd 100644 --- a/Analysis/AnalysisOrg/justBeadFind/SeparatorInterface.cpp +++ b/Analysis/AnalysisOrg/justBeadFind/SeparatorInterface.cpp @@ -78,6 +78,8 @@ void DoDiffSeparatorFromCLO (DifferentialSeparator *diffSeparator, CommandLineOp opts.useMeshNeighbors = 0; opts.regionXSize = inception_state.loc_context.regionXSize; opts.regionYSize = inception_state.loc_context.regionYSize; + opts.beadfindAcqThreshold = inception_state.bfd_control.beadfindAcqThreshold; + opts.beadfindBfThreshold = inception_state.bfd_control.beadfindBfThreshold; // For saving dcOffset and NucStep opts.nucStepDir = string ( opts.analysisDir + string ( "/NucStepFromBeadfind" )); @@ -209,7 +211,7 @@ void IsolatedBeadFind ( char *results_folder, string &analysisLocation, SeqListClass &my_keys, TrackProgress &my_progress, - string& chipTpye) + string& chipType) { /********************************************************************* // Beadfind Section @@ -217,7 +219,7 @@ void IsolatedBeadFind ( Mask bfmask ( 1, 1 ); Mask *maskPtr = &bfmask; // beadfind has responsibility for defining the exclusion mask - SetExcludeMask ( inception_state.loc_context,maskPtr,(char*)chipTpye.c_str(),my_image_spec.rows,my_image_spec.cols ); + SetExcludeMask ( inception_state.loc_context,maskPtr,(char*)chipType.c_str(),my_image_spec.rows,my_image_spec.cols, inception_state.bfd_control.exclusionMaskFile, inception_state.bfd_control.beadfindThumbnail ); bool beadfind_done = false; if ( inception_state.mod_control.reusePriorBeadfind ) { diff --git a/Analysis/BaseCaller/BarcodeClassifier.cpp b/Analysis/BaseCaller/BarcodeClassifier.cpp index 5420292f..7c39ec2f 100644 --- a/Analysis/BaseCaller/BarcodeClassifier.cpp +++ b/Analysis/BaseCaller/BarcodeClassifier.cpp @@ -55,9 +55,10 @@ BarcodeClassifier::BarcodeClassifier(OptArgs& opts, BarcodeDatasets& datasets, c hamming_dmin_ = -1; barcode_min_start_flow_ = -1; no_barcode_read_group_ = -1; + have_ambiguity_codes_ = false; - dataset_in_use_ = datasets.DatasetInUse(); - is_control_dataset_ = datasets.IsControlDataset(); + dataset_in_use_ = datasets.DatasetInUse(); + is_control_dataset_ = datasets.IsControlDataset(); // --- Prepare directory structure and output files @@ -122,7 +123,7 @@ BarcodeClassifier::BarcodeClassifier(OptArgs& opts, BarcodeDatasets& datasets, c LoadBarcodesFromDataset(datasets, keys); // For now only option to get the properties of the barcode set - if (compute_dmin or score_auto_config_) + if ((compute_dmin or score_auto_config_) and not have_ambiguity_codes_) ComputeHammingDistance(); // Set options with limits that potentially depend on barcode set Hamming distance @@ -193,6 +194,12 @@ void BarcodeClassifier::LoadBarcodesFromDataset(BarcodeDatasets& datasets, const barcode_.back().full_barcode += read_group.get("barcode_adapter","").asString(); int key_barcode_adapter_length = barcode_.back().full_barcode.length(); + // Check for non-ACGT characters + if (std::string::npos != barcode_.back().full_barcode.find_first_not_of("ACGTN")){ + have_ambiguity_codes_ = true; + continue; + } + int flow = 0; int curBase = 0; int end_length = -1; @@ -239,17 +246,23 @@ void BarcodeClassifier::LoadBarcodesFromDataset(BarcodeDatasets& datasets, const if (dataset_in_use_ and no_barcode_read_group_ < 0) cout << " INFO: Dataset " << barcode_id << " does not have a non-barcoded read group." << endl; - // And loop through barcodes again to determine maximum amount of flows after start flow has been determined - int barcode_flows, barcode_min_flows = -1; - for (unsigned int bc=0; bc= 0 and barcode_min_flows != barcode_max_flows_) + cout << " WARNING: Barcode set is not flow space synchronized. Barcodes range from " + << barcode_min_flows << " to " << barcode_max_flows_ << " flows." << endl; } - if (dataset_in_use_ and barcode_min_flows >= 0 and barcode_min_flows != barcode_max_flows_) - cout << " WARNING: Barcode set is not flow space synchronized. Barcodes range from " - << barcode_min_flows << " to " << barcode_max_flows_ << " flows." << endl; + /*cout << " Barcode dataset info: min_start_flow=" << barcode_min_start_flow_ << endl << + " barcode_max_flows=" << barcode_max_flows_ << endl << + " num_flows=" << barcode_.back().num_flows << endl; + */ // Export barcode_max_flows_ to datasets structure datasets.SetBCmaxFlows(barcode_max_flows_); @@ -264,7 +277,12 @@ void BarcodeClassifier::SetClassificationParams(int mode, double cutoff, double score_cutoff_ = cutoff; score_separation_ = separation; - CheckParameterLowerUpperBound("barcode-mode", score_mode_, 1,1, 5,1, 2); + CheckParameterLowerUpperBound("barcode-mode", score_mode_, 0,1, 5,1, 2); + if (have_ambiguity_codes_ and score_mode_ != 0){ + cout << " WARNING: Non-ACGT characters detected. Setting score-mode=0." << endl; + score_mode_ =0; + return; + } // Do we have minimum distance information available? if (hamming_dmin_ > 0) { @@ -365,9 +383,29 @@ void BarcodeClassifier::BuildPredictedSignals(float cf, float ie, float dr) } +// ------------------------------------------------------------------------ +// trivially simple base space matching + +int BarcodeClassifier::SimpleBaseSpaceClassification(const BasecallerRead& basecaller_read) +{ + int bc_len; + for (int bc = 0; bc < num_barcodes_; ++bc) { + bc_len = barcode_[bc].full_barcode.length(); + if (basecaller_read.sequence.size() < barcode_[bc].full_barcode.length()) + continue; + + int i=0; + while (i& base_to_flow, int& best_errors) +int BarcodeClassifier::FlowAlignClassification(const ProcessedRead &processed_read, const vector& base_to_flow, int& best_errors) { int best_barcode = -1; best_errors = 1 + (int)score_cutoff_; // allows match with this many errors minimum when in bc_score_mode 1 @@ -599,25 +637,27 @@ void BarcodeClassifier::ClassifyAndTrimBarcode(int read_index, ProcessedRead &pr return; int best_barcode = -1; - // looks at flow-space absolute error counts, not ratios - if (score_mode_ == 1) { - best_barcode = BaseSpaceClassification(processed_read, base_to_flow, processed_read.barcode_n_errors); - } - // Minimize distance to barcode predicted signal - else if (score_mode_ == 2 or score_mode_ == 3) { - // Minimize L2 distance for score_mode_ == 2 - // Minimize L1 distance for score_mode_ == 3 - best_barcode = SignalSpaceClassification(basecaller_read, processed_read.barcode_distance, processed_read.barcode_n_errors, - processed_read.barcode_bias, processed_read.barcode_filt_zero_error); - } else if (score_mode_ ==4 or score_mode_ ==5){ - //L2 for score mode 4 - //L1 for score mode 5 + switch (score_mode_){ + case 1: // looks at flow-space absolute error counts, not ratios + best_barcode = FlowAlignClassification(processed_read, base_to_flow, processed_read.barcode_n_errors); + break; + case 2: // Minimize L2 distance for score_mode_ == 2 + case 3: // Minimize L1 distance for score_mode_ == 3 + best_barcode = SignalSpaceClassification(basecaller_read, processed_read.barcode_distance, processed_read.barcode_n_errors, + processed_read.barcode_bias, processed_read.barcode_filt_zero_error); + break; + case 4: //L2 for score mode 4 + case 5: //L1 for score mode 5 best_barcode = ProportionalSignalClassification(basecaller_read, processed_read.barcode_distance, processed_read.barcode_n_errors, - processed_read.barcode_bias, processed_read.barcode_filt_zero_error); + processed_read.barcode_bias, processed_read.barcode_filt_zero_error); + break; + default: // Trivial base space comparison + best_barcode = SimpleBaseSpaceClassification(basecaller_read); } // Optionally verify barcode adapter - AdapterValidation(basecaller_read, best_barcode, processed_read.barcode_adapter_filtered); + if (score_mode_ > 0) + AdapterValidation(basecaller_read, best_barcode, processed_read.barcode_adapter_filtered); // -------- Classification done, now accounting ---------- @@ -645,14 +685,19 @@ void BarcodeClassifier::ClassifyAndTrimBarcode(int read_index, ProcessedRead &pr return; // Account for barcode + barcode adapter bases - processed_read.filter.n_bases_barcode = 0; - while (processed_read.filter.n_bases_barcode < processed_read.filter.n_bases and base_to_flow[processed_read.filter.n_bases_barcode] < bce.num_flows-1) - processed_read.filter.n_bases_barcode++; - - int last_homopolymer = bce.last_homopolymer; - while (processed_read.filter.n_bases_barcode < processed_read.filter.n_bases and base_to_flow[processed_read.filter.n_bases_barcode] < bce.num_flows and last_homopolymer > 0) { - processed_read.filter.n_bases_barcode++; - last_homopolymer--; + if (score_mode_ == 0){ + processed_read.filter.n_bases_barcode = min(processed_read.filter.n_bases, (int)barcode_[best_barcode].full_barcode.length()); + } + else { + processed_read.filter.n_bases_barcode = 0; + while (processed_read.filter.n_bases_barcode < processed_read.filter.n_bases and base_to_flow[processed_read.filter.n_bases_barcode] < bce.num_flows-1) + processed_read.filter.n_bases_barcode++; + + int last_homopolymer = bce.last_homopolymer; + while (processed_read.filter.n_bases_barcode < processed_read.filter.n_bases and base_to_flow[processed_read.filter.n_bases_barcode] < bce.num_flows and last_homopolymer > 0) { + processed_read.filter.n_bases_barcode++; + last_homopolymer--; + } } // Propagate current 5' trimming point diff --git a/Analysis/BaseCaller/BarcodeClassifier.h b/Analysis/BaseCaller/BarcodeClassifier.h index 48a0c31c..bbc7d944 100644 --- a/Analysis/BaseCaller/BarcodeClassifier.h +++ b/Analysis/BaseCaller/BarcodeClassifier.h @@ -55,7 +55,9 @@ class BarcodeClassifier { void SetClassificationParams(int mode, double cutoff, double separation); - int BaseSpaceClassification(const ProcessedRead &processed_read, const vector& base_to_flow, int& best_errors); + int SimpleBaseSpaceClassification(const BasecallerRead& basecaller_read); + + int FlowAlignClassification(const ProcessedRead &processed_read, const vector& base_to_flow, int& best_errors); int SignalSpaceClassification(const BasecallerRead& basecaller_read, float& best_distance, int& best_errors, vector& best_bias, int& filtered_zero_errors); @@ -147,6 +149,7 @@ class BarcodeClassifier { bool barcode_filter_named_; // Exclude barcodes with an associated sample name from filtering bool barcode_ignore_flows_; // Switch telling the classifier to exclude certain flows from classification bool trim_barcodes_; // Switch to trim or leave barcodes alone + bool have_ambiguity_codes_; // Do we have non-ACGT characters in key, barcode? vector classifier_ignore_flows_; // Specifying an interval of flows to exclude from classification int hamming_dmin_; diff --git a/Analysis/BaseCaller/BaseCaller.cpp b/Analysis/BaseCaller/BaseCaller.cpp index 6bfc1611..068eaae1 100644 --- a/Analysis/BaseCaller/BaseCaller.cpp +++ b/Analysis/BaseCaller/BaseCaller.cpp @@ -217,9 +217,10 @@ void ClassifyAndSampleWells(BaseCallerContext & bc, const BCwellSampling & Sampl void scaleup_flowgram(const vector& sigIn, vector& sigOut, int max_flow) { - if (sigOut.size() != (size_t)max_flow) - sigOut.resize(max_flow); - for (int flow = 0; flow < max_flow; ++flow){ + int safe_max = min(max_flow, (int)sigIn.size()); + if (sigOut.size() != (size_t)safe_max) + sigOut.resize(safe_max); + for (int flow = 0; flow < safe_max; ++flow){ int v = 128*sigIn[flow]; // handle overflow if (v < -16383 or v > 16383) { @@ -230,6 +231,19 @@ void scaleup_flowgram(const vector& sigIn, vector& sigOut, int m } +void make_base_to_flow(const vector& sequence,ion::FlowOrder& flow_order, vector& base_to_flow,vector& flow_to_base,int num_flows) +{ + int nBases = sequence.size(); + base_to_flow.resize(nBases); + flow_to_base.assign(num_flows,-1); + + for (int base = 0, flow = 0; base < nBases; ++base) { + while (flow < num_flows and sequence.at(base) != flow_order[flow]) + flow++; + base_to_flow.at(base) = flow; + flow_to_base.at(flow) = base; + } +} // -------------------------------------------------------------------------- //! @brief Main function for BaseCaller executable Mark: XXX @@ -389,7 +403,7 @@ int main (int argc, const char *argv[]) ClassifyAndSampleWells(bc, bc_params.GetSamplingOpts()); // Find distribution of clonal reads for use in read filtering: - filters.TrainClonalFilter(bc_params.GetFiles().output_directory, wells, mask, bc.polyclonal_filter); + filters.TrainClonalFilter(bc_params.GetFiles().output_directory, wells, mask); MemUsage("ClonalPopulation"); ReportState(analysis_start_time,"Polyclonal Filter Training Complete"); @@ -425,29 +439,31 @@ int main (int argc, const char *argv[]) // Library data set writer - always bc.lib_writer.Open(bc_params.GetFiles().output_directory, datasets, 0, bc.chip_subset.NumRegions(), bc.flow_order, bc.keys[0].bases(), filters.GetLibBeadAdapters(), bc_params.NumBamWriterThreads(), - basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes()); + basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes(), bc_params.CompressOutputBam()); // Calibration reads data set writer - if applicable if (bc.have_calibration_panel) bc.calib_writer.Open(bc_params.GetFiles().output_directory, datasets_calibration, 0, bc.chip_subset.NumRegions(), bc.flow_order, bc.keys[0].bases(), filters.GetLibBeadAdapters(), bc_params.NumBamWriterThreads(), - basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes()); + basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes(), bc_params.CompressOutputBam()); // Test fragments data set writer - if applicable if (bc.process_tfs) bc.tf_writer.Open(bc_params.GetFiles().output_directory, datasets_tf, 1, bc.chip_subset.NumRegions(), bc.flow_order, bc.keys[1].bases(), filters.GetTFBeadAdapters(), bc_params.NumBamWriterThreads(), - basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes()); + basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes(), bc_params.CompressOutputBam()); // Unfiltered / unfiltered untrimmed data set writers - if applicable if (!bc.unfiltered_set.empty()) { bc.unfiltered_writer.Open(bc_params.GetFiles().unfiltered_untrimmed_directory, datasets_unfiltered_untrimmed, -1, bc.chip_subset.NumRegions(), bc.flow_order, bc.keys[0].bases(), filters.GetLibBeadAdapters(), - bc_params.NumBamWriterThreads(), basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes()); + bc_params.NumBamWriterThreads(), basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes(), + bc_params.CompressOutputBam()); bc.unfiltered_trimmed_writer.Open(bc_params.GetFiles().unfiltered_trimmed_directory, datasets_unfiltered_trimmed, -1, bc.chip_subset.NumRegions(), bc.flow_order, bc.keys[0].bases(), filters.GetLibBeadAdapters(), - bc_params.NumBamWriterThreads(), basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes()); + bc_params.NumBamWriterThreads(), basecaller_json, bam_comments, tag_trimmer, barcodes.TrimBarcodes(), + bc_params.CompressOutputBam()); } // @@ -576,6 +592,7 @@ void * BasecallerWorker(void *input) vector filtering_details(13,0); vector quality(3*num_flows); vector base_to_flow (3*num_flows); //!< Flow of in-phase incorporation of each base. + vector flow_to_base (num_flows,-1); //!< base pos of each flow, -1 if not available DPTreephaser treephaser(bc.flow_order, bc.windowSize); treephaser.SetStateProgression(bc.diagonal_state_prog); @@ -731,7 +748,7 @@ void * BasecallerWorker(void *input) } // Get rid of outliers quickly - bc.filters->FilterHighPPFAndPolyclonal (read_index, read_class, processed_read.filter, read.raw_measurements, bc.polyclonal_filter); + bc.filters->FilterHighPPFAndPolyclonal (read_index, read_class, processed_read.filter, read.raw_measurements); if (not key_pass) bc.filters->FilterFailedKeypass (read_index, read_class, processed_read.filter, read.sequence); if (!is_random_unfiltered and !bc.filters->IsValid(read_index)) // No reason to waste more time @@ -788,7 +805,10 @@ void * BasecallerWorker(void *input) } // Compute QV metrics on pot. calibrated sequence - treephaser_sse.ComputeQVmetrics(read); + // Generate base_to_flow before ComputeQVmetrics. But not too early, otherwise the sequence is not ready + make_base_to_flow(read.sequence,bc.flow_order,base_to_flow,flow_to_base,num_flows); + + treephaser_sse.ComputeQVmetrics_flow(read,flow_to_base,bc.flow_predictors_); compute_base_calls = false; } #endif @@ -817,7 +837,11 @@ void * BasecallerWorker(void *input) bc.histogram_calibration->PolishRead(bc.flow_order, x+bc.chip_subset.GetColOffset(), y+bc.chip_subset.GetRowOffset(), read, bc.linear_cal_model); } - treephaser.ComputeQVmetrics(read); + // Compute QV metrics on pot. calibrated sequence + // Generate base_to_flow before ComputeQVmetrics. But not too early, otherwise the sequence is not ready + make_base_to_flow(read.sequence,bc.flow_order,base_to_flow,flow_to_base,num_flows); + + treephaser.ComputeQVmetrics_flow(read,flow_to_base,bc.flow_predictors_); } // Misc data management: Generate residual, scaled_residual @@ -829,17 +853,6 @@ void * BasecallerWorker(void *input) // Misc data management: Put base calls in proper string form processed_read.filter.CalledRead(read.sequence.size()); - // Misc data management: Generate base_to_flow - - base_to_flow.clear(); - base_to_flow.reserve(processed_read.filter.n_bases); - for (int base = 0, flow = 0; base < processed_read.filter.n_bases; ++base) { - while (flow < num_flows and read.sequence[base] != bc.flow_order[flow]) - flow++; - base_to_flow.push_back(flow); - } - - // Misc data management: Populate some trivial read properties char read_name[256]; @@ -862,23 +875,58 @@ void * BasecallerWorker(void *input) // Predictor 5 - Treephaser: Penalty indicating deletion after the called base // Predictor 6 - Neighborhood noise - mean of 'noise' +-5 BASES around a base. Noise is mean{abs(val - round(val))} - int num_predictor_bases = min(num_flows, processed_read.filter.n_bases); - - PerBaseQual::PredictorLocalNoise(local_noise, num_predictor_bases, base_to_flow, read.normalized_measurements, read.prediction); - PerBaseQual::PredictorNeighborhoodNoise(neighborhood_noise, num_predictor_bases, base_to_flow, read.normalized_measurements, read.prediction); - //PerBaseQual::PredictorNoiseOverlap(minus_noise_overlap, num_predictor_bases, read.normalized_measurements, read.prediction); - PerBaseQual::PredictorBeverlyEvents(minus_noise_overlap, num_predictor_bases, base_to_flow, scaled_residual); - PerBaseQual::PredictorHomopolymerRank(homopolymer_rank, num_predictor_bases, read.sequence); + //char short_name[20]; // x:y only + //sprintf(short_name, "%05d:%05d", bc.chip_subset.GetRowOffset() + y, bc.chip_subset.GetColOffset() + x); + //bc.quality_generator.GenerateBaseQualities(short_name, processed_read.filter.n_bases, num_flows, + vector homopolymer_rank_flow(num_flows, 0); + bool use_flow_predictors = bc.flow_predictors_; + if (bc.quality_generator.toSavePredictors()) { + int num_predictor_bases = min(num_flows, processed_read.filter.n_bases); + PerBaseQual::PredictorLocalNoise(local_noise, num_predictor_bases, base_to_flow, read.normalized_measurements, read.prediction,use_flow_predictors); + PerBaseQual::PredictorNeighborhoodNoise(neighborhood_noise, num_predictor_bases, base_to_flow, read.normalized_measurements, read.prediction,use_flow_predictors); + //PerBaseQual::PredictorNoiseOverlap(minus_noise_overlap, num_predictor_bases, read.normalized_measurements, read.prediction,use_flow_predictors); + PerBaseQual::PredictorBeverlyEvents(minus_noise_overlap, num_predictor_bases, base_to_flow, scaled_residual,use_flow_predictors); + PerBaseQual::PredictorHomopolymerRank(homopolymer_rank, num_predictor_bases, read.sequence, homopolymer_rank_flow, flow_to_base, use_flow_predictors); + + bc.quality_generator.DumpPredictors(read_name, processed_read.filter.n_bases, num_flows, + read.penalty_residual, local_noise, minus_noise_overlap, // <- predictors 1,2,3 + homopolymer_rank, read.penalty_mismatch, neighborhood_noise, // <- predictors 4,5,6 + base_to_flow, quality, + read.additive_correction, + read.multiplicative_correction, + read.state_inphase, + read.penalty_residual_flow, + read.penalty_mismatch_flow, + homopolymer_rank_flow, + flow_to_base, + use_flow_predictors); + } - quality.clear(); - bc.quality_generator.GenerateBaseQualities(processed_read.bam.Name, processed_read.filter.n_bases, num_flows, + // 8/18/2016, ew: still use base-predictors to predict quality scores for now + use_flow_predictors = false; + bool redo_genPred = ((! bc.quality_generator.toSavePredictors()) || bc.flow_predictors_) ? true:false; + //redo_genPred = true; + if (redo_genPred) { + int num_predictor_bases = min(num_flows, processed_read.filter.n_bases); + PerBaseQual::PredictorLocalNoise(local_noise, num_predictor_bases, base_to_flow, read.normalized_measurements, read.prediction,use_flow_predictors); + PerBaseQual::PredictorNeighborhoodNoise(neighborhood_noise, num_predictor_bases, base_to_flow, read.normalized_measurements, read.prediction,use_flow_predictors); + //PerBaseQual::PredictorNoiseOverlap(minus_noise_overlap, num_predictor_bases, read.normalized_measurements, read.prediction,use_flow_predictors); + PerBaseQual::PredictorBeverlyEvents(minus_noise_overlap, num_predictor_bases, base_to_flow, scaled_residual,use_flow_predictors); + PerBaseQual::PredictorHomopolymerRank(homopolymer_rank, num_predictor_bases, read.sequence, homopolymer_rank_flow, flow_to_base, use_flow_predictors); + } + //bc.quality_generator.GenerateBaseQualities(processed_read.bam.Name, processed_read.filter.n_bases, num_flows, + bc.quality_generator.GenerateBaseQualities(read_name, processed_read.filter.n_bases, num_flows, read.penalty_residual, local_noise, minus_noise_overlap, // <- predictors 1,2,3 homopolymer_rank, read.penalty_mismatch, neighborhood_noise, // <- predictors 4,5,6 base_to_flow, quality, read.additive_correction, read.multiplicative_correction, - read.state_inphase); - + read.state_inphase, + read.penalty_residual_flow, + read.penalty_mismatch_flow, + homopolymer_rank_flow, + flow_to_base, + use_flow_predictors); // // Step 4a. Barcode classification of library reads @@ -947,9 +995,13 @@ void * BasecallerWorker(void *input) // Step 4b. Add flow signal information to ZM tag in BAM record. // - int max_flow = min(num_flows,16); - if (processed_read.filter.n_bases_filtered > 0) + int max_flow = num_flows; + if (bc.trim_zm){ + if (processed_read.filter.n_bases_filtered > 0) max_flow = min(num_flows, base_to_flow[processed_read.filter.n_bases_filtered-1] + 16); + else + max_flow = min(num_flows,16); + } scaleup_flowgram(read.normalized_measurements,flowgram2,max_flow); processed_read.bam.AddTag("ZM", flowgram2); @@ -960,8 +1012,10 @@ void * BasecallerWorker(void *input) processed_read.bam.AddTag("Yb", flowgram2); scaleup_flowgram(read.raw_measurements,flowgram2,max_flow); processed_read.bam.AddTag("Yw", flowgram2); - scaleup_flowgram(read.not_calibrated_measurements,flowgram2,max_flow); - processed_read.bam.AddTag("Yx", flowgram2); + if (bc.histogram_calibration->is_enabled()) { + scaleup_flowgram(read.not_calibrated_measurements,flowgram2,max_flow); + processed_read.bam.AddTag("Yx", flowgram2); + } } // diff --git a/Analysis/BaseCaller/BaseCallerFilters.cpp b/Analysis/BaseCaller/BaseCallerFilters.cpp index d772501f..1c178961 100644 --- a/Analysis/BaseCaller/BaseCallerFilters.cpp +++ b/Analysis/BaseCaller/BaseCallerFilters.cpp @@ -18,6 +18,7 @@ #include "IonErr.h" #include "RawWells.h" #include "Mask.h" +//#include "Utils.h" #include "BaseCallerUtils.h" #include "DPTreephaser.h" #include "OrderedDatasetWriter.h" @@ -490,7 +491,7 @@ void ReadFilteringStats::SaveToBasecallerJson(Json::Value &json, const string& c json["Filtering"]["ReadDetails"][class_name]["quality_trim"] = (Json::Int64)num_reads_removed_quality_trim_; json["Filtering"]["ReadDetails"][class_name]["quality_filter"] = (Json::Int64)num_reads_removed_quality_filt_; json["Filtering"]["ReadDetails"][class_name]["tag_trim"] = (Json::Int64)num_reads_removed_tag_trim_; - json["Filtering"]["ReadDetails"][class_name]["extra_trim"] = (Json::Int64)num_reads_removed_extra_trim_ + num_reads_removed_extra_trim_; + json["Filtering"]["ReadDetails"][class_name]["extra_trim"] = (Json::Int64)num_reads_removed_extra_trim_; json["Filtering"]["ReadDetails"][class_name]["valid"] = (Json::Int64)num_reads_final_; // BeadSummary - obsolete me! @@ -498,7 +499,7 @@ void ReadFilteringStats::SaveToBasecallerJson(Json::Value &json, const string& c json["BeadSummary"][class_name]["highPPF"] = (Json::Int64)(num_reads_removed_bkgmodel_high_ppf_ + num_reads_removed_high_ppf_); json["BeadSummary"][class_name]["zero"] = 0; json["BeadSummary"][class_name]["short"] = (Json::Int64)(num_reads_removed_short_ + num_reads_removed_adapter_trim_ - + num_reads_removed_quality_trim_ + num_reads_removed_tag_trim_); + + num_reads_removed_quality_trim_ + num_reads_removed_tag_trim_ +num_reads_removed_extra_trim_); json["BeadSummary"][class_name]["badKey"] = (Json::Int64)(num_reads_removed_bkgmodel_keypass_ + num_reads_removed_keypass_); json["BeadSummary"][class_name]["highRes"] = (Json::Int64)(num_reads_removed_residual_ + num_reads_removed_quality_filt_); json["BeadSummary"][class_name]["valid"] = (Json::Int64)num_reads_final_; @@ -530,7 +531,7 @@ void ReadFilteringStats::SaveToBasecallerJson(Json::Value &json, const string& c num_reads_removed_quality_trim_ + num_reads_removed_quality_filt_ + num_reads_removed_tag_trim_ + - num_bases_removed_extra_trim_); + num_reads_removed_extra_trim_); json["Filtering"]["LibraryReport"]["final_library_reads"] = (Json::Int64)num_reads_final_; // (3') Bead Adapters @@ -561,45 +562,43 @@ void ReadFilteringStats::SaveToBasecallerJson(Json::Value &json, const string& c void BaseCallerFilters::PrintHelp() { printf ("Read filtering and trimming options:\n"); - printf (" -d,--disable-all-filters on/off disable all filtering and trimming, overrides other args [off]\n"); + printf (" -d,--disable-all-filters on/off disable all filtering and trimming, overrides other args [off]\n"); printf (" Key-pass filter: \n"); - printf (" -k,--keypass-filter on/off apply keypass filter [on]\n"); - printf (" Polyclonal filter in BaseCaller:\n"); - printf (" --clonal-filter-solve on/off apply polyclonal filter [off]\n"); - printf (" --clonal-filter-tf on/off apply polyclonal filter to TFs [off]\n"); - printf (" --clonal-filter-maxreads INT maximum number of library reads used for polyclonal filter training [100000]\n"); - printf (" --min-read-length INT apply minimum read length filter [25]\n"); - printf (" --cr-filter on/off apply cafie residual filter [off]\n"); - printf (" --cr-filter-tf on/off apply cafie residual filter to TFs [off]\n"); - printf (" --cr-filter-max-value FLOAT cafie residual filter threshold [0.08]\n"); - printf (" --beverly-filter FLOAT,FLOAT filter_ratio,trim_ratio / off\n"); - printf (" apply Beverly filter/trimmer [off]\n"); - printf (" --qual-filter on/off apply quality filter based on expected number of errors [off]\n"); - printf (" --qual-filter-offset FLOAT error offset for expected errors quality filter [0.7]\n"); - printf (" --qual-filter-slope FLOAT expected errors allowed per base for expected errors quality filter [0.02]\n"); + printf (" -k,--keypass-filter on/off apply keypass filter [on]\n"); + printf (" Read filter settings:\n"); + printf (" --min-read-length INT apply minimum read length filter [25]\n"); + printf (" --cr-filter on/off apply cafie residual filter [off]\n"); + printf (" --cr-filter-tf on/off apply cafie residual filter to TFs [off]\n"); + printf (" --cr-filter-max-value FLOAT cafie residual filter threshold [0.08]\n"); + printf (" --beverly-filter FLOAT,FLOAT filter_ratio,trim_ratio / off\n"); + printf (" apply Beverly filter/trimmer [off]\n"); + printf (" --qual-filter on/off apply quality filter based on expected number of errors [off]\n"); + printf (" --qual-filter-offset FLOAT error offset for expected errors quality filter [0.7]\n"); + printf (" --qual-filter-slope FLOAT expected errors allowed per base for expected errors quality filter [0.02]\n"); printf ("\n"); + PolyclonalFilterOpts::PrintHelp(false); printf ("Read trimming options:\n"); - printf (" --trim-min-read-len INT reads trimmed shorter than this are omitted from output [min-read-length]\n"); - printf (" --trim-barcodes BOOL trim barcodes and barcode adapters [on]\n"); - printf (" --extra-trim-left INT Number of additional bases to remove from read start after prefix (key/barcode/tag) [0]\n"); - printf (" --extra-trim-right INT Number of additional bases to remove from read end before suffix (tag/adapter) [0]\n"); - printf (" --save-extra-trim BOOL Save extra trimmed bases to BAM tags (left ZE, right YE) [false]\n"); + printf (" --trim-min-read-len INT reads trimmed shorter than this are omitted from output [min-read-length]\n"); + printf (" --trim-barcodes BOOL trim barcodes and barcode adapters [on]\n"); + printf (" --extra-trim-left INT Number of additional bases to remove from read start after prefix (key/barcode/tag) [0]\n"); + printf (" --extra-trim-right INT Number of additional bases to remove from read end before suffix (tag/adapter) [0]\n"); + printf (" --save-extra-trim BOOL Save extra trimmed bases to BAM tags (left ZE, right YE) [false]\n"); printf (" Adapter trimming (turn off by supplying cutoff 0):\n"); - printf (" --trim-adapter-mode INT 0=use simplified metric, 1=use standard metric [1]\n"); - printf (" --trim-adapter STRING reverse complement of adapter sequence [ATCACCGACTGCCCATAGAGAGGCTGAGAC]\n"); - printf (" --trim-adapter-tf STRING/off adapter sequence for test fragments [off]\n"); - printf (" --trim-adapter-cutoff FLOAT cutoff for adapter trimming, 0=off [16]\n"); - printf (" --trim-adapter-min-match INT minimum adapter bases in the read required for trimming [6]\n"); + printf (" --trim-adapter-mode INT 0=use simplified metric, 1=use standard metric [1]\n"); + printf (" --trim-adapter STRING reverse complement of adapter sequence [ATCACCGACTGCCCATAGAGAGGCTGAGAC]\n"); + printf (" --trim-adapter-tf STRING/off adapter sequence for test fragments [off]\n"); + printf (" --trim-adapter-cutoff FLOAT cutoff for adapter trimming, 0=off [16]\n"); + printf (" --trim-adapter-min-match INT minimum adapter bases in the read required for trimming [6]\n"); printf (" Quality trimming (turn off by supplying mode 'off'):\n"); - printf (" --trim-qual-mode STRING select the method of quality trimming [\"sliding-window\"]\n"); - printf (" mode \"off\" : quality trimming disabled\n"); - printf (" mode \"sliding-window\" : sliding window quality trimming\n"); - printf (" mode \"expected-errors\" : quality trimming based on expected number of errors in read\n"); - printf (" mode \"all\" : shortest trimming point of all methods\n"); - printf (" --trim-qual-window-size INT window size for windowed quality trimming [30]\n"); - printf (" --trim-qual-cutoff FLOAT cutoff for windowed quality trimming, 100=off [15]\n"); - printf (" --trim-qual-offset FLOAT error threshold offset for expected error quality trimming [0.7]\n"); - printf (" --trim-qual-slope FLOAT increase of expected errors allowed for expected error quality trimming [0.005]\n"); + printf (" --trim-qual-mode STRING select the method of quality trimming [\"sliding-window\"]\n"); + printf (" mode \"off\" : quality trimming disabled\n"); + printf (" mode \"sliding-window\" : sliding window quality trimming\n"); + printf (" mode \"expected-errors\" : quality trimming based on expected number of errors in read\n"); + printf (" mode \"all\" : shortest trimming point of all methods\n"); + printf (" --trim-qual-window-size INT window size for windowed quality trimming [30]\n"); + printf (" --trim-qual-cutoff FLOAT cutoff for windowed quality trimming, 100=off [15]\n"); + printf (" --trim-qual-offset FLOAT error threshold offset for expected error quality trimming [0.7]\n"); + printf (" --trim-qual-slope FLOAT increase of expected errors allowed for expected error quality trimming [0.005]\n"); printf ("\n"); } @@ -613,6 +612,10 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, num_classes_ = keys_.size(); assert(num_classes_ == 2); filter_mask_.assign(mask.H()*mask.W(), kUninitialized); + Json::Value null_json; + + cout << "Polyclonal filter settings:" << endl; + clonal_opts_.SetOpts(false, opts, null_json, flow_order_.num_flows()); // *** Retrieve filter command line options @@ -620,10 +623,6 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, filter_min_read_length_ = max(opts.GetFirstInt ('-', "min-read-length", 25),1); trim_min_read_len_ = max(opts.GetFirstInt ('-', "trim-min-read-len", filter_min_read_length_),1); - filter_clonal_enabled_tfs_ = opts.GetFirstBoolean('-', "clonal-filter-tf", false); - filter_clonal_enabled_ = opts.GetFirstBoolean('-', "clonal-filter-solve", false); - filter_clonal_maxreads_ = opts.GetFirstInt ('-', "clonal-filter-maxreads", 100000); - filter_residual_enabled_ = opts.GetFirstBoolean('-', "cr-filter", false); filter_residual_enabled_tfs_ = opts.GetFirstBoolean('-', "cr-filter-tf", false); filter_residual_max_value_ = opts.GetFirstDouble ('-', "cr-filter-max-value", 0.08); @@ -633,7 +632,6 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, filter_quality_slope_ = opts.GetFirstDouble ('-', "qual-filter-slope",0.02); filter_quality_quadr_ = opts.GetFirstDouble ('-', "qual-filter-quadr",0.00); - // Adapter trimming options trim_adapter_ = opts.GetFirstStringVector ('-', "trim-adapter", "ATCACCGACTGCCCATAGAGAGGCTGAGAC"); trim_adapter_cutoff_ = opts.GetFirstDouble ('-', "trim-adapter-cutoff", 16.0); @@ -682,8 +680,7 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, // If this flag is set all filters & trimmers will be disabled if (disable_all_filters) { filter_keypass_enabled_ = false; - filter_clonal_enabled_tfs_ = false; - filter_clonal_enabled_ = false; + clonal_opts_.Disable(); filter_residual_enabled_ = false; filter_residual_enabled_tfs_ = false; filter_quality_enabled_ = false; @@ -697,8 +694,6 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, printf(" --disable-all-filters %s\n", disable_all_filters ? "on (overrides other options)" : "off"); printf(" --keypass-filter %s\n", filter_keypass_enabled_ ? "on" : "off"); printf(" --min-read-length %d\n", filter_min_read_length_); - printf(" --clonal-filter-solve %s\n", filter_clonal_enabled_ ? "on" : "off"); - printf(" --clonal-filter-tf %s\n", filter_clonal_enabled_tfs_ ? "on" : "off"); printf(" --cr-filter %s\n", filter_residual_enabled_ ? "on" : "off"); printf(" --cr-filter-tf %s\n", filter_residual_enabled_tfs_ ? "on" : "off"); printf(" --cr-filter-max-value %1.3f\n", filter_residual_max_value_); @@ -706,6 +701,7 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, printf(" --qual-filter %s\n", filter_quality_enabled_ ? "on" : "off"); printf(" --qual-filter-offset %1.4f\n", filter_quality_offset_); printf(" --qual-filter-slope %1.4f\n", filter_quality_slope_ ); + printf("\n"); printf("Adapter trimming settings\n"); printf(" --trim-adapter "); @@ -764,17 +760,21 @@ BaseCallerFilters::BaseCallerFilters(OptArgs& opts, Json::Value &comments_json, // ---------------------------------------------------------------------------- -void BaseCallerFilters::TrainClonalFilter(const string& output_directory, RawWells& wells, Mask& mask, const PolyclonalFilterOpts & opts) +void BaseCallerFilters::TrainClonalFilter(const string& output_directory, RawWells& wells, Mask& mask) { - if (!filter_clonal_enabled_ and !filter_clonal_enabled_tfs_) + + if (!(clonal_opts_.enable)){ + cout << "Polyclonal filter training disabled." << endl; return; + } + cout << "Polyclonal filter training." << endl; wells.OpenForIncrementalRead(); vector key_ionogram(keys_[0].flows(), keys_[0].flows()+keys_[0].flows_length()); filter_counts counts; int nlib = mask.GetCount(static_cast (MaskLib)); - counts._nsamp = min(nlib, filter_clonal_maxreads_); - make_filter(clonal_population_, counts, mask, wells, key_ionogram, opts); + counts._nsamp = min(nlib, clonal_opts_.filter_clonal_maxreads); + make_filter(clonal_population_, counts, mask, wells, key_ionogram, clonal_opts_); cout << counts << endl; wells.Close(); } @@ -888,20 +888,19 @@ void BaseCallerFilters::SetBkgmodelFailedKeypass(int read_index, ReadFilteringHi // ---------------------------------------------------------------------------- -void BaseCallerFilters::FilterHighPPFAndPolyclonal (int read_index, int read_class, ReadFilteringHistory& filter_history, - const vector& measurements, - const PolyclonalFilterOpts & opts) +void BaseCallerFilters::FilterHighPPFAndPolyclonal (int read_index, int read_class, + ReadFilteringHistory& filter_history, const vector& measurements) { if (filter_history.is_filtered) return; - if (read_class == 0 and !filter_clonal_enabled_) // Filter disabled for library? + if (read_class == 0 and !(clonal_opts_.filter_clonal_enabled_lib)) // Filter disabled for library? return; - if (read_class != 0 and !filter_clonal_enabled_tfs_) // Filter disabled for TFs? + if (read_class != 0 and !(clonal_opts_.filter_clonal_enabled_tfs)) // Filter disabled for TFs? return; - vector::const_iterator first = measurements.begin() + opts.mixed_first_flow; - vector::const_iterator last = measurements.begin() + opts.mixed_last_flow; + vector::const_iterator first = measurements.begin() + clonal_opts_.mixed_first_flow; + vector::const_iterator last = measurements.begin() + clonal_opts_.mixed_last_flow; float ppf = percent_positive(first, last); float ssq = sum_fractional_part(first, last); @@ -911,7 +910,7 @@ void BaseCallerFilters::FilterHighPPFAndPolyclonal (int read_index, int read_cla filter_history.n_bases_after_high_ppf = 0; filter_history.is_filtered = true; } - else if(!clonal_population_.is_clonal(ppf, ssq)) { + else if(!(clonal_opts_.filter_extreme_ppf_only) and !(clonal_population_.is_clonal(ppf, ssq, clonal_opts_.mixed_stringency))) { filter_mask_[read_index] = kFilterPolyclonal; filter_history.n_bases_filtered = 0; filter_history.n_bases_after_polyclonal = 0; @@ -1058,6 +1057,7 @@ void BaseCallerFilters::FilterFailedKeypass(int read_index, int read_class, Read if ((int)sequence.size() >= keys_[read_class].bases_length()) { int base=0; + //while (base < keys_[read_class].bases_length() and isBaseMatch(sequence[base], keys_[read_class].bases()[base])) while (base < keys_[read_class].bases_length() and sequence[base] == keys_[read_class].bases()[base]) ++base; diff --git a/Analysis/BaseCaller/BaseCallerFilters.h b/Analysis/BaseCaller/BaseCallerFilters.h index 9675559f..4473b1cf 100644 --- a/Analysis/BaseCaller/BaseCallerFilters.h +++ b/Analysis/BaseCaller/BaseCallerFilters.h @@ -13,7 +13,8 @@ #include "BaseCallerUtils.h" #include "Mask.h" -#include "mixed.h" +#include "ClonalFilter/polyclonal_filter.h" +#include "ClonalFilter/mixed.h" #include "OptArgs.h" class DPTreephaser; @@ -60,8 +61,7 @@ class BaseCallerFilters { //! @param wells Wells file reader object, source of filter training reads //! @param max_sample_size Max number of reads to sample for training //! @param mask Mask for determining which reads are eligible for training set - //! @param opts User options for polyclonal filter and flows - void TrainClonalFilter(const string& output_directory, RawWells& wells, Mask& mask, const PolyclonalFilterOpts & opts); + void TrainClonalFilter(const string& output_directory, RawWells& wells, Mask& mask); //! @brief Once filtering is complete, transfer filtering outcomes to Mask object. //! @@ -109,8 +109,7 @@ class BaseCallerFilters { //! @param read_index Read index //! @param read_class Read class, 0=library, 1=TFs //! @param measurements Key-normalized flow signal from wells - //! @param opts User options for how filters work - void FilterHighPPFAndPolyclonal (int read_index, int read_class, ReadFilteringHistory& filter_history, const vector& measurements, const PolyclonalFilterOpts & opts); + void FilterHighPPFAndPolyclonal (int read_index, int read_class, ReadFilteringHistory& filter_history, const vector& measurements); //! @brief Trim key sequence and initialize filtered read length //! @param key_length Length of key sequence @@ -243,9 +242,8 @@ class BaseCallerFilters { bool filter_residual_enabled_; //!< Is residual filter enabled for library reads? bool filter_residual_enabled_tfs_; //!< Is residual filter enabled for TFs? double filter_residual_max_value_; //!< Residual filter threshold - bool filter_clonal_enabled_; //!< Is polyclonal filter enabled for library reads? - bool filter_clonal_enabled_tfs_; //!< Is polyclonal filter enabled for TFs? - int filter_clonal_maxreads_; //!< Number of reads to be used for clonal filter training + + PolyclonalFilterOpts clonal_opts_; //! Class to store the clonal filter options clonal_filter clonal_population_; //!< Object implementing clonal filter // Beverly filter diff --git a/Analysis/BaseCaller/BaseCallerParameters.cpp b/Analysis/BaseCaller/BaseCallerParameters.cpp index 280bf66a..9a02ac2e 100644 --- a/Analysis/BaseCaller/BaseCallerParameters.cpp +++ b/Analysis/BaseCaller/BaseCallerParameters.cpp @@ -116,6 +116,7 @@ void BaseCallerParameters::PrintHelp() printf (" --flow-order STRING flow order [retrieved from wells file]\n"); printf (" --run-id STRING read name prefix [hashed input dir name]\n"); printf (" -n,--num-threads INT number of worker threads [2*numcores]\n"); + printf (" --compress-bam BOOL Output compressed / uncompressed BAM [true]\n"); printf (" -f,--flowlimit INT basecall only first n flows [all flows]\n"); printf (" --keynormalizer STRING key normalization algorithm [gain]\n"); printf (" --wells-normalization STRING normalize wells signal and correct for signal bias [off]/on/keyOnly/signalBiasOnly/pinZero\n"); @@ -227,9 +228,11 @@ bool BaseCallerParameters::InitContextVarsFromOptArgs(OptArgs& opts){ context_vars.run_id = opts.GetFirstString ('-', "run-id", default_run_id); num_threads_ = opts.GetFirstInt ('n', "num-threads", max(2*numCores(), 4)); num_bamwriter_threads_ = opts.GetFirstInt ('-', "num-threads-bamwriter", 0); + compress_output_bam_ = opts.GetFirstBoolean('-', "compress-bam", true); context_vars.flow_signals_type = opts.GetFirstString ('-', "flow-signals-type", "none"); context_vars.only_process_unfiltered_set = opts.GetFirstBoolean('-', "only-process-unfiltered-set", false); + context_vars.flow_predictors_ = opts.GetFirstBoolean('-', "flow-predictors", false); // Treephaser options #if defined( __SSE3__ ) @@ -245,6 +248,7 @@ bool BaseCallerParameters::InitContextVarsFromOptArgs(OptArgs& opts){ context_vars.wells_norm_method = opts.GetFirstString ('-', "wells-normalization", "off"); context_vars.just_phase_estimation = opts.GetFirstBoolean('-', "just-phase-estimation", false); context_vars.calibrate_TFs = opts.GetFirstBoolean('-', "calibrate-tfs", false); + context_vars.trim_zm = opts.GetFirstBoolean('-', "trim-zm", true); // debug options context_vars.debug_normalization_bam = opts.GetFirstBoolean ('-', "debug-normalization-bam", false); @@ -343,7 +347,9 @@ bool BaseCallerParameters::SetBaseCallerContextVars(BaseCallerContext & bc) bc.skip_droop = context_vars.skip_droop; bc.skip_recal_during_norm = context_vars.skip_recal_during_norm; bc.calibrate_TFs = context_vars.calibrate_TFs; + bc.trim_zm = context_vars.trim_zm; + bc.flow_predictors_ = context_vars.flow_predictors_; // debug options bc.debug_normalization_bam = context_vars.debug_normalization_bam; return true; diff --git a/Analysis/BaseCaller/BaseCallerParameters.h b/Analysis/BaseCaller/BaseCallerParameters.h index baea25af..4c8d7f22 100644 --- a/Analysis/BaseCaller/BaseCallerParameters.h +++ b/Analysis/BaseCaller/BaseCallerParameters.h @@ -105,6 +105,7 @@ struct BCcontextVars { string run_id; //!< Run ID string, prepended to each read name bool process_tfs; //!< If set to false, TF-related BAM will not be generated bool only_process_unfiltered_set; + bool flow_predictors_; //!< If set to true, use flow-based predictor for Quality Score prediction string flow_signals_type; //!< The flow signal type: "default" - Normalized and phased, // "wells" - Raw values (unnormalized and not dephased), // "key-normalized" - Key normalized and not dephased, @@ -122,6 +123,7 @@ struct BCcontextVars { bool debug_normalization_bam;//!< Switch to output debug data to the bam file bool just_phase_estimation; //!< BaseCaller will only do phase estimation and nothing else bool calibrate_TFs; //!< Switch to apply calibration to TFs + bool trim_zm; //!< Trim the ZM tag when writing it to the bam file bool options_set; //!< Flag whether options have been read to ensure order }; @@ -143,6 +145,7 @@ struct BaseCallerContext { string flow_signals_type; //!< The flow signal type: "default" - Normalized and phased, "wells" - Raw values (unnormalized and not dephased), "key-normalized" - Key normalized and not dephased, "adaptive-normalized" - Adaptive normalized and not dephased, and "unclipped" - Normalized and phased but unclipped. string output_directory; //!< Root directory for all output files string wells_norm_method; //!< Normalization method for wells file before any processing + bool flow_predictors_; //!< If set to false, TF-related BAM will not be generated bool process_tfs; //!< If set to false, TF-related BAM will not be generated int windowSize; //!< Normalization window size bool have_calibration_panel; //!< Signales the presence of a recalibration panel @@ -153,6 +156,7 @@ struct BaseCallerContext { bool skip_recal_during_norm; //!< Switch to exclude recalibration from the normalization stage bool debug_normalization_bam;//!< Switch to output debug info to the bam file bool calibrate_TFs; //!< Switch to apply calibration to TFs + bool trim_zm; //!< Trim the ZM tag when writing it to the bam file // Important outside entities accessed by BaseCaller ion::ChipSubset chip_subset; //!< Chip coordinate & region handling for Basecaller @@ -166,7 +170,6 @@ struct BaseCallerContext { BarcodeClassifier *calibration_barcodes; //!< Barcode detection for calibration set HistogramCalibration *histogram_calibration; //!< Posterior base call and signal adjustment algorithm LinearCalibrationModel *linear_cal_model; //!< Model estimation of simulated predictions and observed measurements - PolyclonalFilterOpts polyclonal_filter; //!< User options for polyclonal filtering MolecularTagTrimmer *tag_trimmer; //!< Class for tag accounting within read groups // Threaded processing @@ -193,6 +196,7 @@ class BaseCallerParameters { BaseCallerParameters() { num_threads_ = 1; num_bamwriter_threads_ = 1; + compress_output_bam_ = true; bc_files.options_set = false; sampling_opts.options_set = false; context_vars.options_set = false; @@ -212,6 +216,8 @@ class BaseCallerParameters { bool JustPhaseEstimation() { return context_vars.just_phase_estimation; }; + bool CompressOutputBam() { return compress_output_bam_; }; + const BaseCallerFiles & GetFiles() const { if (not bc_files.options_set){ @@ -234,8 +240,11 @@ class BaseCallerParameters { int NumBamWriterThreads() const { return num_bamwriter_threads_; }; private: + int num_threads_; //!< Number of worker threads to do base calling int num_bamwriter_threads_; //!< Number of threads one bam writer object uses + bool compress_output_bam_; //!< Switch to output compressed / uncompressed BAM + BaseCallerFiles bc_files; BCcontextVars context_vars; BCwellSampling sampling_opts; diff --git a/Analysis/BaseCaller/DPTreephaser.cpp b/Analysis/BaseCaller/DPTreephaser.cpp index 96277f62..33284c61 100644 --- a/Analysis/BaseCaller/DPTreephaser.cpp +++ b/Analysis/BaseCaller/DPTreephaser.cpp @@ -859,16 +859,38 @@ void DPTreephaser::AdvanceStateInPlace(TreephaserPath *state, char nuc, int max_ //------------------------------------------------------------------------- void DPTreephaser::Simulate(BasecallerRead& data, int max_flows,bool state_inphase) +{ + SimulateStateMatrix(data, max_flows, state_inphase, NULL, 0.0f); +} + +//------------------------------------------------------------------------- + +void DPTreephaser::SimulateStateMatrix(BasecallerRead& data, int max_flows,bool state_inphase, vector > >* row_linked_state_matrix, float amp_cut_off) { InitializeState(&path_[0]); int recent_flow = 0; float recent_state_inphase = 1.0; + bool is_output_state_matrix = (row_linked_state_matrix != NULL); + if (is_output_state_matrix){ + row_linked_state_matrix->reserve(data.sequence.size()); + } for (vector::iterator nuc = data.sequence.begin(); nuc != data.sequence.end() and path_[0].flow < max_flows; ++nuc) { AdvanceStateInPlace(&path_[0], *nuc, flow_order_.num_flows()); path_[0].sequence.push_back(*nuc); // Needed to simulate diagonal states correctly + if (is_output_state_matrix){ + vector > sparse_matrix_row; + sparse_matrix_row.reserve(path_[0].window_end - path_[0].window_start); + for (int flow = path_[0].window_start; flow < path_[0].window_end; ++flow){ + if (path_[0].state[flow] > amp_cut_off){ + sparse_matrix_row.push_back(pair(flow, path_[0].state[flow])); + } + } + row_linked_state_matrix->push_back(sparse_matrix_row); + } + if (state_inphase and path_[0].flow < max_flows) { for (int iFlow=recent_flow+1; iFlowflow >= flow_order_.num_flows()) { + penalty[nuc] = 25; // Mark for deletion + continue; + } + + if (child->last_hp > kMaxHP) { + penalty[nuc] = 25; // Mark for deletion + continue; + } + + if ((int)parent->sequence.size() >= (2 * flow_order_.num_flows() - 10)) { + penalty[nuc] = 25; // Mark for deletion + continue; + } + + for (int flow = parent->window_start; flow < child->window_end; ++flow) { + float residual = read.normalized_measurements[flow] - child->prediction[flow]; + if (residual <= 0 or flow < child->flow) + penalty[nuc] += residual*residual; + } + } //looping over nucs + + + // find current incorporating base + assert(children[called_nuc]->flow == solution_flow); + + recent_state_inphase = children[called_nuc]->state[solution_flow]; + recent_state_total = 0; + for (int flow = children[called_nuc]->window_start; flow < children[called_nuc]->window_end; ++flow) + recent_state_total += children[called_nuc]->state[flow]; + + // Get delta penalty to next best solution + read.penalty_mismatch[base] = -1; // min delta penalty to earlier base hypothesis + read.penalty_residual[base] = 0; + + if (solution_flow - parent->window_start > 0) + read.penalty_residual[base] = penalty[called_nuc] / (solution_flow - parent->window_start); + + for (int nuc = 0; nuc < 4; ++nuc) { + if (nuc == called_nuc) + continue; + float penalty_mismatch = penalty[called_nuc] - penalty[nuc]; + read.penalty_mismatch[base] = max(read.penalty_mismatch[base], penalty_mismatch); + } + + // Fill out the remaining portion of the prediction + for (int flow = 0; flow < parent->window_start; ++flow) + children[called_nuc]->prediction[flow] = parent->prediction[flow]; + + for (int flow = children[called_nuc]->window_end; flow < flow_order_.num_flows(); ++flow) + children[called_nuc]->prediction[flow] = 0; + + // Called state is the starting point for next base + TreephaserPath *swap = parent; + parent = children[called_nuc]; + children[called_nuc] = swap; + + } + + read.state_inphase[solution_flow] = max(recent_state_inphase, 0.01f); + read.state_total[solution_flow] = max(recent_state_total, 0.01f); + } + + read.prediction.swap(parent->prediction); + +} + + +void DPTreephaser::ComputeQVmetrics_flow(BasecallerRead& read, vector& flow_to_base, const bool flow_predictors_) +{ + //static const char nuc_int_to_char[5] = "ACGT"; + //read.state_total.assign(flow_order_.num_flows(), 1); + + if (read.sequence.empty()) + return; + int sz = (int)read.sequence.size(); + if (flow_predictors_) + sz = flow_order_.num_flows(); + + read.penalty_mismatch.assign(sz, 0); + read.penalty_residual.assign(sz, 0); TreephaserPath *parent = &path_[0]; TreephaserPath *children[4] = { &path_[1], &path_[2], &path_[3], &path_[4] }; @@ -1335,7 +1468,11 @@ void DPTreephaser::ComputeQVmetrics(BasecallerRead& read) // main loop for base calling for (int solution_flow = 0, base = 0; solution_flow < flow_order_.num_flows(); ++solution_flow) { - for (; base < (int)read.sequence.size() and read.sequence[base] == flow_order_[solution_flow]; ++base) { + for (; ; ++base) { + if (flow_predictors_) + break; + else if (not (base < sz and read.sequence[base] == flow_order_[solution_flow])) + break; float penalty[4] = { 0, 0, 0, 0 }; @@ -1419,6 +1556,7 @@ void DPTreephaser::ComputeQVmetrics(BasecallerRead& read) } + // ---------------------------------------------------------------------------- void DPTreephaser::TreephaserPath::Initialize() diff --git a/Analysis/BaseCaller/DPTreephaser.h b/Analysis/BaseCaller/DPTreephaser.h index f047893d..9f1b3598 100644 --- a/Analysis/BaseCaller/DPTreephaser.h +++ b/Analysis/BaseCaller/DPTreephaser.h @@ -43,6 +43,8 @@ struct BasecallerRead { vector state_total; //!< Fraction of live polymerase vector penalty_residual; //!< Absolute score of the called nuc hypothesis vector penalty_mismatch; //!< Score difference to second-best nuc hypothesis + vector penalty_residual_flow; //!< Absolute score of the called nuc hypothesis + vector penalty_mismatch_flow; //!< Score difference to second-best nuc hypothesis // Nuc gain data static constexpr float kZeromerMin = -0.20f; //!< Key flow corrected non-key flow zeromer 3-sigma minimum @@ -130,6 +132,14 @@ class DPTreephaser { //! @param[in] restart_flows Number of flows to simulate, rather than solve void Solve(BasecallerRead& read, int max_flows, int restart_flows = 0); + //! @brief Generate predicted signal and the row-linked sparse state matrix from base sequence + //! @param[in] read.sequence Base sequence + //! @param[out] read.prediction Predicted signal + //! @param[in] max_flows Number of flows to process + //! @param[out] row_linked_state_matrix (*row_linked_state_matrix)[nuc][j].second = the amplitude that read.sequence[nuc] contributes to read.prediction[flow] where flow = *(row_linked_state_matrix)[nuc][j].first + //! @param[in] amp_cut_off Neglect the entry in *row_linked_state_matrix if less than this value. + void SimulateStateMatrix(BasecallerRead& read, int max_flows, bool state_inphase, vector > >* row_linked_state_matrix, float amp_cut_off = 0.0f); + //! @brief Generate predicted signal from base sequence //! @param[in] read.sequence Base sequence //! @param[out] read.prediction Predicted signal @@ -160,7 +170,8 @@ class DPTreephaser { //! @param[out] read.onemer_height Expected 1-mer signal, used for scaling residuals //! @param[out] read.penalty_residual Absolute score of the called nuc hypothesis //! @param[out] read.penalty_mismatch Score difference to second-best nuc hypothesis - void ComputeQVmetrics(BasecallerRead& read); // Computes "oneMerHeight" and "deltaPenalty" + void ComputeQVmetrics(BasecallerRead& read); // Computes "oneMerHeight" and "deltaPenalty" + void ComputeQVmetrics_flow(BasecallerRead& read, vector& flow_to_base, const bool flow_predictors_=false); // Computes "oneMerHeight" and "deltaPenalty" //! @brief Correct for uniform multiplicative scaling //! @param[in] read.prediction Model-predicted signal diff --git a/Analysis/BaseCaller/MolecularTagTrimmer.cpp b/Analysis/BaseCaller/MolecularTagTrimmer.cpp index c5b9cf7e..21191dc8 100644 --- a/Analysis/BaseCaller/MolecularTagTrimmer.cpp +++ b/Analysis/BaseCaller/MolecularTagTrimmer.cpp @@ -33,22 +33,24 @@ MolecularTagTrimmer::MolecularTagTrimmer() void MolecularTagTrimmer::PrintHelp(bool tvc_call) { + string space1_for_tvc = tvc_call? " " : ""; + string space2_for_tvc = tvc_call? " " : ""; + cout << "Molecular tagging options:" << endl; + // Both + cout << " --suppress-mol-tags " << space1_for_tvc << "BOOL" << space2_for_tvc << " Ignore tag information [false]" << endl; + cout << " --tag-trim-method " << space1_for_tvc << "STRING" << space2_for_tvc << " Method to trim tags. Options: {strict-trim, sloppy-trim} [sloppy-trim]" << endl; // TVC only options if (tvc_call) - cout << " --min-tag-fam-size INT Minimum required size of molecular tag family [3]" << endl; - + cout << " --min-tag-fam-size " << space1_for_tvc << "INT" << space2_for_tvc << " Minimum required size of molecular tag family [3]" << endl; // BaseCaller only options else{ cout << " --prefix-mol-tag STRING Structure of prefix molecular tag {ACGTN bases}" << endl; cout << " --suffix-mol-tag STRING Structure of suffix molecular tag {ACGTN bases}" << endl; - cout << " --tag-trim-method STRING Method to trim tags. Options: {strict-trim, sloppy-trim} [sloppy-trim]" << endl; - cout << " --heal-tag-hp-indel Bool Heal hp indel on tags [true]" << endl; + cout << " --heal-tag-hp-indel BOOL Heal hp indel on tags [true]" << endl; + cout << " --tag-filter-method STRING Filter reads based on tags. Options: {need-prefix, need-suffix, need-all} [need-all]" << endl; + cout << endl; } - // Both - cout << " --suppress-mol-tags BOOL Ignore tag information [false]" << endl; - cout << " --tag-filter-method STRING Filter reads based on tags. Options: {need-prefix, need-suffix, need-all} [need-all]" << endl; - cout << endl; } @@ -256,12 +258,12 @@ void MolecularTagTrimmer::PrintOptionValues(bool tvc_call) // Verbose output XXX do not say anything if there are no tags if (num_read_groups_with_tags_ > 0) { cout << "MolecularTagTrimmer settings:" << endl; - cout << " found " << num_read_groups_with_tags_ << " read groups with tags." << endl; - cout << " suppress-mol-tags : " << (suppress_mol_tags_ ? "on" : "off") << endl; + cout << " found " << num_read_groups_with_tags_ << " read groups with tags." << endl; + cout << " suppress-mol-tags : " << (suppress_mol_tags_ ? "on" : "off") << endl; if (not tvc_call) { cout << " heal-tag-hp-indel : " << (heal_tag_hp_indel_? "on": "off") << endl; - cout << " tag-trim-method : "; + cout << " tag-trim-method : "; switch (tag_trim_method_){ case kSloppyTrim : cout << "sloppy-trim" << endl; break; case kStrictTrim : cout << "strict-trim" << endl; break; @@ -571,6 +573,14 @@ string MolecularTagTrimmer::GetPrefixTag (string read_group_name) const // ------------------------------------------------------------------------- +string MolecularTagTrimmer::GetPrefixTag (int read_group_idx) const +{ + string tag = tag_structure_.at(read_group_idx).prefix_mol_tag; + return tag; +} + +// ------------------------------------------------------------------------- + string MolecularTagTrimmer::GetSuffixTag (string read_group_name) const { string tag; @@ -583,6 +593,14 @@ string MolecularTagTrimmer::GetSuffixTag (string read_group_name) const // ------------------------------------------------------------------------- +string MolecularTagTrimmer::GetSuffixTag (int read_group_idx) const +{ + string tag = tag_structure_.at(read_group_idx).suffix_mol_tag; + return tag; +} + +// ------------------------------------------------------------------------- + bool MolecularTagTrimmer::HasTags(string read_group_name) const { bool has_tags = false; diff --git a/Analysis/BaseCaller/MolecularTagTrimmer.h b/Analysis/BaseCaller/MolecularTagTrimmer.h index 8716a744..329ccb50 100644 --- a/Analysis/BaseCaller/MolecularTagTrimmer.h +++ b/Analysis/BaseCaller/MolecularTagTrimmer.h @@ -164,12 +164,15 @@ class MolecularTagTrimmer MolTag GetReadGroupTags (string read_group_name) const; string GetPrefixTag (string read_group_name) const; + string GetPrefixTag (int read_group_idx) const; string GetSuffixTag (string read_group_name) const; + string GetSuffixTag (int read_group_idx) const; bool HasTags(int read_group_idx) const { return read_group_has_tags_.at(read_group_idx); }; bool HasTags(string read_group_name) const; bool HaveTags() const { return (num_read_groups_with_tags_>0); }; + int GetTagTrimMethod() const {return tag_trim_method_; }; }; diff --git a/Analysis/BaseCaller/OrderedDatasetWriter.cpp b/Analysis/BaseCaller/OrderedDatasetWriter.cpp index e04c2f57..26fbb8f9 100644 --- a/Analysis/BaseCaller/OrderedDatasetWriter.cpp +++ b/Analysis/BaseCaller/OrderedDatasetWriter.cpp @@ -28,6 +28,7 @@ OrderedDatasetWriter::OrderedDatasetWriter() num_read_groups_ = 0; num_datasets_ = 0; save_filtered_reads_ = false; + compress_bam_ = true; num_bamwriter_threads_ = 0; pthread_mutex_init(&dropbox_mutex_, NULL); pthread_mutex_init(&write_mutex_, NULL); @@ -46,7 +47,7 @@ OrderedDatasetWriter::~OrderedDatasetWriter() void OrderedDatasetWriter::Open(const string& base_directory, BarcodeDatasets& datasets, int read_class_idx, int num_regions, const ion::FlowOrder& flow_order, const string& key, const vector & bead_adapters, int num_bamwriter_threads, const Json::Value & basecaller_json, vector& comments, - MolecularTagTrimmer& tag_trimmer, bool trim_barcodes) + MolecularTagTrimmer& tag_trimmer, bool trim_barcodes, bool compress_bam) { num_regions_ = num_regions; num_regions_written_ = 0; @@ -61,6 +62,7 @@ void OrderedDatasetWriter::Open(const string& base_directory, BarcodeDatasets& d num_read_groups_ = datasets.num_read_groups(); num_reads_.resize(num_datasets_,0); bam_filename_.resize(num_datasets_); + compress_bam_ = compress_bam; // A negative read group index indicates untrimmed/unfiltered bam files (w. library key) and we save all reads if (read_class_idx < 0) { @@ -314,9 +316,12 @@ void OrderedDatasetWriter::PhysicalWriteRegion(int region) // Open Bam for writing RefVector empty_reference_vector; bam_writer_[target_file_idx] = new BamWriter(); - bam_writer_[target_file_idx]->SetCompressionMode(BamWriter::Compressed); + if (compress_bam_) + bam_writer_[target_file_idx]->SetCompressionMode(BamWriter::Compressed); + else + bam_writer_[target_file_idx]->SetCompressionMode(BamWriter::Uncompressed); bam_writer_[target_file_idx]->SetNumThreads(num_bamwriter_threads_); - //bam_writer_[ds]->SetCompressionMode(BamWriter::Uncompressed); + if (not bam_writer_[target_file_idx]->Open(bam_filename_[target_file_idx], sam_header_[target_file_idx], empty_reference_vector)) { cerr << "BaseCaller IO error: Failed to create bam file " << bam_filename_[target_file_idx] << endl; exit(EXIT_FAILURE); diff --git a/Analysis/BaseCaller/OrderedDatasetWriter.h b/Analysis/BaseCaller/OrderedDatasetWriter.h index 2ece7f7d..7277fb83 100644 --- a/Analysis/BaseCaller/OrderedDatasetWriter.h +++ b/Analysis/BaseCaller/OrderedDatasetWriter.h @@ -182,7 +182,7 @@ class OrderedDatasetWriter { void Open(const string& base_directory, BarcodeDatasets& datasets, int read_class_idx, int num_regions, const ion::FlowOrder& flow_order, const string& key, const vector & bead_adapters, int num_bamwriter_threads, const Json::Value & basecaller_json, vector& comments, - MolecularTagTrimmer& tag_trimmer, bool trim_barcodes); + MolecularTagTrimmer& tag_trimmer, bool trim_barcodes, bool compress_bam); //! @brief Drop off a region-worth of reads for writing. Write opportunistically. //! @param region Index of the region being dropped off. @@ -230,6 +230,7 @@ class OrderedDatasetWriter { vector bam_filename_; bool save_filtered_reads_; + bool compress_bam_; int num_bamwriter_threads_; vector read_group_num_Q20_bases_; //!< Number of >=Q20 bases written per read group diff --git a/Analysis/BaseCaller/PerBaseQual.cpp b/Analysis/BaseCaller/PerBaseQual.cpp index cb83d2c4..499dada7 100644 --- a/Analysis/BaseCaller/PerBaseQual.cpp +++ b/Analysis/BaseCaller/PerBaseQual.cpp @@ -424,15 +424,17 @@ uint8_t PerBaseQual::CalculatePerBaseScore(float* pred) const // Predictor 2 - Local noise/flowalign - Maximum residual within +-1 BASE void PerBaseQual::PredictorLocalNoise(vector& local_noise, int max_base, const vector& base_to_flow, - const vector& normalized_measurements, const vector& prediction) + const vector& normalized_measurements, const vector& prediction, const bool flow_predictors_) { int num_bases = base_to_flow.size(); for (int base = 0; base < max_base; ++base) { int val1 = max(base - 1, 0); - int val2 = min(base + 1, num_bases - 1); + //int val2 = min(base + 1, num_bases - 1); + int val2 = flow_predictors_ ? min(base+1, max_base-1) : min(base+1, num_bases-1); float noise = 0; for (int j = val1; j <= val2; ++j) { - noise = max(noise, fabsf(normalized_measurements[base_to_flow[j]] - prediction[base_to_flow[j]])); + int jj = flow_predictors_ ? j : base_to_flow[j]; + noise = max(noise, fabsf(normalized_measurements[jj] - prediction[jj])); } local_noise[base] = noise; } @@ -443,7 +445,7 @@ void PerBaseQual::PredictorLocalNoise(vector& local_noise, int max_base, // -(m_1 - m_0 - s_1 - s_0)/m_1 void PerBaseQual::PredictorNoiseOverlap(vector& minus_noise_overlap, int max_base, - const vector& normalized_measurements, const vector& prediction) + const vector& normalized_measurements, const vector& prediction, const bool flow_predictors_) { // 0-mer and 1-mer overlap // define 0-mer and 1-mer interval @@ -513,37 +515,43 @@ void PerBaseQual::PredictorNoiseOverlap(vector& minus_noise_overlap, int // Predictor 4 - Transformed homopolymer length -void PerBaseQual::PredictorHomopolymerRank(vector& homopolymer_rank, int max_base, const vector& sequence) +void PerBaseQual::PredictorHomopolymerRank(vector& homopolymer_rank, int max_base, const vector& sequence, vector& homopolymer_rank_flow, const vector& flow_to_base, int flow_predictors_) { - int hp_length = 0; - for (int base = 0; base < max_base; ++base) { + int hp_length = 0; + for (int base = 0; base < max_base; ++base) { hp_length++; // HP 1114 homopolymer_rank[base] = 1; if (sequence[base] != sequence[base+1] or (base+2) == max_base) { homopolymer_rank[base] = hp_length; hp_length = 0; + } + } + int nFlows = flow_to_base.size(); + for (int flow=0; flow=0 && base& neighborhood_noise, int max_base, const vector& base_to_flow, - const vector& normalized_measurements, const vector& prediction) + const vector& normalized_measurements, const vector& prediction, const bool flow_predictors_) { int num_bases = base_to_flow.size(); for (int base = 0; base < max_base; ++base) { int radius = 5; // protect at start/end of read int val1 = max(base-radius, 0); - int val2 = min(base+radius, num_bases-1); + int val2 = flow_predictors_ ? min(base+radius, max_base-1) : min(base+radius, num_bases-1); float noise = 0; int count = 0; for (int j = val1; j <= val2; j++) { - noise += fabsf(normalized_measurements[base_to_flow[j]] - prediction[base_to_flow[j]]); + int jj = flow_predictors_ ? j : base_to_flow[j]; + noise += fabsf(normalized_measurements[jj] - prediction[jj]); count++; } if (count) @@ -557,7 +565,7 @@ void PerBaseQual::PredictorNeighborhoodNoise(vector& neighborhood_noise, // Candidate predictor based on Beverly filter void PerBaseQual::PredictorBeverlyEvents(vector& beverly_events, int max_base, const vector& base_to_flow, - const vector& scaled_residual) + const vector& scaled_residual, const bool flow_predictors_) { const static int flow_window_radius = 10; @@ -568,9 +576,7 @@ void PerBaseQual::PredictorBeverlyEvents(vector& beverly_events, int max_ int num_beverly_events = 0; for (int base = 0; base < max_base; ++base) { - - int window_center_flow = base_to_flow[base]; - + int window_center_flow = flow_predictors_ ? base : base_to_flow[base]; // Advance window start while (window_start_flow < window_center_flow+flow_window_radius) { int hp_length = 0; @@ -610,60 +616,78 @@ void PerBaseQual::GenerateBaseQualities(const string& read_name, int num_bases, const vector &predictor1, const vector &predictor2, const vector &predictor3, const vector &predictor4, const vector &predictor5, const vector &predictor6, const vector& base_to_flow, vector &quality, - const vector &candidate1, const vector &candidate2, const vector &candidate3) + const vector &candidate1, const vector &candidate2, const vector &candidate3, + const vector &predictor1_flow, const vector &predictor5_flow, const vector &predictor4_flow, + const vector& flow_to_base, const bool flow_predictors_) { if (num_bases == 0) return; //! \todo This is a temporary fix for very long sequences that are sometimes generated by the basecaller - int max_eligible_base = min(num_bases, (int)(0.75*num_flows) + 1); - quality.clear(); + int last_base_to_flow = base_to_flow.back(); + int max_eligible_flow = (int)(0.75*num_flows) + 1; + max_eligible_flow = min(max_eligible_flow,last_base_to_flow); + //save_predictors_ = false; // debugging only + int max_eligible_base = flow_predictors_ ? max_eligible_flow : min(num_bases, max_eligible_flow); + //int max_eligible_base = min(num_bases, max_eligible_flow); // avoid out of range in debugging + quality.clear(); stringstream predictor_dump_block; for (int base = 0; base < max_eligible_base; base++) { // first 4 bases are the keys TCAG - float pred[kNumPredictors]; - pred[1] = predictor2[base]; - pred[2] = predictor3[base]; - pred[3] = predictor4[base]; - + pred[1] = predictor2[base]; // P2: local noise + pred[2] = predictor3[base]; // P3: high-residual events + int base_or_flow = flow_predictors_ ? base : base_to_flow[base]; + /* if (save_predictors_) { // the following lines are only for predictor_dump_block // they are not the same in new QvTables - pred[0] = predictor1[base]; - pred[4] = predictor5[base]; - pred[5] = predictor6[base]; + pred[0] = flow_predictors_ ? predictor1_flow[base] : predictor1[base]; // P1: penalty residual + pred[3] = flow_predictors_ ? predictor4_flow[base] : predictor4[base]; // P4: hp + pred[4] = flow_predictors_ ? predictor5_flow[base] : predictor5[base]; // P5: penalty mismatch + pred[5] = predictor6[base]; // P6: neighborhood noise predictor_dump_block << read_name << " " << base << " "; for (int k = 0; k < kNumPredictors; ++k) predictor_dump_block << pred[k] << " "; - predictor_dump_block << candidate1[base_to_flow[base]] << " "; - predictor_dump_block << candidate2[base_to_flow[base]] << " "; - predictor_dump_block << candidate3[base_to_flow[base]] << " "; - predictor_dump_block << base_to_flow[base] << endl; + predictor_dump_block << candidate1[base_or_flow] << " "; + predictor_dump_block << candidate2[base_or_flow] << " "; + predictor_dump_block << candidate3[base_or_flow] << " "; + if (flow_predictors_) { + int always_base = flow_predictors_ ? flow_to_base[base] : base; + predictor_dump_block << always_base << endl; // could be -1 + } else { + int always_flow = flow_predictors_ ? base : base_to_flow[base]; + predictor_dump_block << always_flow << endl; // cannot get flow if base=-1 + } + //predictor_dump_block << base_to_flow[base] << endl; } + */ // v3.4: p1,2,3,4,6,9 // the real predictors used in the QvTable pred[0] = transform_P1(predictor1[base]); - //pred[1] = transform_P2(predictor2[base]); // no transformation might help only if no Recalibration + pred[3] = predictor4[base]; // P4: hp + //pred[3] = flow_predictors_ ? predictor4_flow[base] : predictor4[base]; // P4: hp pred[4] = transform_P6(predictor6[base]); + //pred[1] = transform_P2(predictor2[base]); // no transformation might help only if no Recalibration //pred[5] = transform_P8(candidate2[base_to_flow[base]]); - pred[5] = transform_P9(candidate3[base_to_flow[base]]); + pred[5] = candidate3[base_or_flow]; + pred[5] = transform_P9(pred[5]); // v3.0: p1,2,3,4,5,6 //pred[0] = predictor1[base]; //pred[0] = transform_P1(predictor1[base]); //pred[4] = predictor5[base]; //pred[5] = predictor6[base]; - quality.push_back(CalculatePerBaseScore(pred)); } for (int base = max_eligible_base; base < num_bases; base++) quality.push_back(kMinQuality); + /* if (save_predictors_) { predictor_dump_block.flush(); pthread_mutex_lock(&predictor_mutex_); @@ -671,9 +695,70 @@ void PerBaseQual::GenerateBaseQualities(const string& read_name, int num_bases, predictor_dump_.flush(); pthread_mutex_unlock(&predictor_mutex_); } + */ } +void PerBaseQual::DumpPredictors(const string& read_name, int num_bases, int num_flows, + const vector &predictor1, const vector &predictor2, const vector &predictor3, + const vector &predictor4, const vector &predictor5, const vector &predictor6, + const vector& base_to_flow, vector &quality, + const vector &candidate1, const vector &candidate2, const vector &candidate3, + const vector &predictor1_flow, const vector &predictor5_flow, const vector &predictor4_flow, + const vector& flow_to_base, const bool flow_predictors_) +{ + + if (num_bases == 0) + return; + + //! \todo This is a temporary fix for very long sequences that are sometimes generated by the basecaller + int last_base_to_flow = base_to_flow.back(); + int max_eligible_flow = (int)(0.75*num_flows) + 1; + max_eligible_flow = min(max_eligible_flow,last_base_to_flow); + //save_predictors_ = false; // debugging only + int max_eligible_base = flow_predictors_ ? max_eligible_flow : min(num_bases, max_eligible_flow); + //int max_eligible_base = min(num_bases, max_eligible_flow); // avoid out of range in debugging + + stringstream predictor_dump_block; + + for (int base = 0; base < max_eligible_base; base++) { // first 4 bases are the keys TCAG + float pred[kNumPredictors]; + pred[1] = predictor2[base]; // P2: local noise + pred[2] = predictor3[base]; // P3: high-residual events + int always_flow = flow_predictors_ ? base : base_to_flow[base]; + //if (save_predictors_) { + // the following lines are only for predictor_dump_block + // they are not the same in new QvTables + pred[0] = flow_predictors_ ? predictor1_flow[base] : predictor1[base]; // P1: penalty residual + pred[3] = flow_predictors_ ? predictor4_flow[base] : predictor4[base]; // P4: hp + pred[4] = flow_predictors_ ? predictor5_flow[base] : predictor5[base]; // P5: penalty mismatch + pred[5] = predictor6[base]; // P6: neighborhood noise + + predictor_dump_block << read_name << " " << base << " "; + for (int k = 0; k < kNumPredictors; ++k) + predictor_dump_block << pred[k] << " "; + predictor_dump_block << candidate1[always_flow] << " "; + predictor_dump_block << candidate2[always_flow] << " "; + predictor_dump_block << candidate3[always_flow] << " "; + if (flow_predictors_) { + int always_base = flow_predictors_ ? flow_to_base[base] : base; + predictor_dump_block << always_base << endl; // could be -1 + } else { + predictor_dump_block << always_flow << endl; // cannot get flow if base=-1 + } + //predictor_dump_block << base_to_flow[base] << endl; + //} + } + + //if (save_predictors_) { + predictor_dump_block.flush(); + pthread_mutex_lock(&predictor_mutex_); + predictor_dump_ << predictor_dump_block.str(); + predictor_dump_.flush(); + pthread_mutex_unlock(&predictor_mutex_); + //} +} + float PerBaseQual::transform_P1(float p) { diff --git a/Analysis/BaseCaller/PerBaseQual.h b/Analysis/BaseCaller/PerBaseQual.h index 25f1176f..c10c020c 100644 --- a/Analysis/BaseCaller/PerBaseQual.h +++ b/Analysis/BaseCaller/PerBaseQual.h @@ -60,7 +60,17 @@ class PerBaseQual { const vector &predictor1, const vector &predictor2, const vector &predictor3, const vector &predictor4, const vector &predictor5, const vector &predictor6, const vector& base_to_flow, vector &quality, - const vector &candidate1, const vector &candidate2, const vector &candidate3); + const vector &candidate1, const vector &candidate2, const vector &candidate3, + const vector &predictor1_flow, const vector &predictor5_flow, const vector &predictor4_flow, + const vector& flow_to_base, const bool flow_predictors_=false); + + void DumpPredictors(const string& read_name, int num_bases, int num_flows, + const vector &predictor1, const vector &predictor2, const vector &predictor3, + const vector &predictor4, const vector &predictor5, const vector &predictor6, + const vector& base_to_flow, vector &quality, + const vector &candidate1, const vector &candidate2, const vector &candidate3, + const vector &predictor1_flow, const vector &predictor5_flow, const vector &predictor4_flow, + const vector& flow_to_base, const bool flow_predictors_=false); //! @brief Calculate Local Noise predictor for all bases in a read //! @param[out] local_noise Local Noise predictor @@ -69,7 +79,7 @@ class PerBaseQual { //! @param[in] normalized_measurements Normalized flow signal from wells file //! @param[in] prediction Model-predicted flow signal static void PredictorLocalNoise(vector& local_noise, int max_base, const vector& base_to_flow, - const vector& normalized_measurements, const vector& prediction); + const vector& normalized_measurements, const vector& prediction, const bool flow_predictors_); //! @brief Calculate Noise Overlap predictor shared by all bases in a read //! @param[out] minus_noise_overlap Noise Overlap predictor @@ -78,13 +88,13 @@ class PerBaseQual { //! @param[in] prediction Model-predicted flow signal //! @return Noise Overlap predictor static void PredictorNoiseOverlap(vector& minus_noise_overlap, int max_base, - const vector& normalized_measurements, const vector& prediction); + const vector& normalized_measurements, const vector& prediction, const bool flow_predictors_); //! @brief Calculate Homopolymer Rank predictor for all bases in a read //! @param[out] homopolymer_rank Homopolymer Rank predictor //! @param[in] max_base Number of bases for which predictor should be calculated //! @param[in] sequence Called bases - static void PredictorHomopolymerRank(vector& homopolymer_rank, int max_base, const vector& sequence); + static void PredictorHomopolymerRank(vector& homopolymer_rank, int max_base, const vector& sequence, vector& homopolymer_rank_flow, const vector& flow_to_base, int flow_predictors_=false); //! @brief Calculate Neighborhood Noise predictor for all bases in a read //! @param[out] neighborhood_noise Neighborhood Noise predictor @@ -93,12 +103,13 @@ class PerBaseQual { //! @param[in] normalized_measurements Normalized flow signal from wells file //! @param[in] prediction Model-predicted flow signal static void PredictorNeighborhoodNoise(vector& neighborhood_noise, int max_base, const vector& base_to_flow, - const vector& normalized_measurements, const vector& prediction); + const vector& normalized_measurements, const vector& prediction, const bool flow_predictors_); static void PredictorBeverlyEvents(vector& beverly_events, int max_base, const vector& base_to_flow, - const vector& scaled_residual); + const vector& scaled_residual, const bool flow_predictors_); + bool toSavePredictors() {return (save_predictors_ ? true:false);} protected: diff --git a/Analysis/BaseCaller/TreephaserSSE.cpp b/Analysis/BaseCaller/TreephaserSSE.cpp index 80721831..a774f795 100644 --- a/Analysis/BaseCaller/TreephaserSSE.cpp +++ b/Analysis/BaseCaller/TreephaserSSE.cpp @@ -1484,15 +1484,15 @@ void TreephaserSSE::WindowedNormalize(BasecallerRead& read, int num_steps) void TreephaserSSE::ComputeQVmetrics(BasecallerRead& read) { static const char nuc_int_to_char[5] = "ACGT"; - - read.state_inphase.assign(flow_order_.num_flows(), 1); - read.state_total.assign(flow_order_.num_flows(), 1); + int num_flows = flow_order_.num_flows(); + read.state_inphase.assign(num_flows, 1); + read.state_total.assign(num_flows, 1); if (read.sequence.empty()) return; - - read.penalty_mismatch.assign(read.sequence.size(), 0); - read.penalty_residual.assign(read.sequence.size(), 0); + int num_bases = read.sequence.size(); + read.penalty_mismatch.assign(num_bases, 0); + read.penalty_residual.assign(num_bases, 0); PathRec RESTRICT_PTR parent = sv_PathPtr[0]; PathRec RESTRICT_PTR children[4] = {sv_PathPtr[1], sv_PathPtr[2], sv_PathPtr[3], sv_PathPtr[4]}; @@ -1512,98 +1512,228 @@ void TreephaserSSE::ComputeQVmetrics(BasecallerRead& read) float recent_state_total = 1; // main loop for base calling - for (int solution_flow = 0, base = 0; solution_flow < flow_order_.num_flows(); ++solution_flow) { - for (; base < (int)read.sequence.size() and read.sequence[base] == flow_order_[solution_flow]; ++base) { - - float penalty[4] = { 0, 0, 0, 0 }; - - int called_nuc = -1; - - if(recalibrate_predictions_) { - parent->calib_A[parent->flow] = (*As_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); - parent->calib_B[parent->flow] = (*Bs_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); - } - - // compute child path flow states, predicted signal,negative and positive penalties - advanceState4(parent, flow_order_.num_flows()); - - for(int nuc = 0; nuc < 4; ++nuc) { - PathRec RESTRICT_PTR child = children[nuc]; - - if (nuc_int_to_char[nuc] == flow_order_[solution_flow]) - called_nuc = nuc; - - child->flow = min(ad_Idx[nuc], flow_order_.num_flows()); - child->window_end = min(ad_End[nuc], flow_order_.num_flows()); - child->window_start = min(ad_Beg[nuc], child->window_end); + for (int solution_flow = 0, base = 0; solution_flow < num_flows; ++solution_flow) { + for (; basecalib_A[parent->flow] = (*As_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + parent->calib_B[parent->flow] = (*Bs_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + } + // compute child path flow states, predicted signal,negative and positive penalties + advanceState4(parent, num_flows); + + float penalty[4] = { 0, 0, 0, 0 }; + int called_nuc = -1; + for(int nuc = 0; nuc < 4; ++nuc) { + PathRec RESTRICT_PTR child = children[nuc]; + if (nuc_int_to_char[nuc] == flow_order_[solution_flow]) + called_nuc = nuc; + child->flow = min(ad_Idx[nuc], num_flows); + child->window_end = min(ad_End[nuc], num_flows); + child->window_start = min(ad_Beg[nuc], child->window_end); + // Apply easy termination rules + if (child->flow >= num_flows || parent->last_hp >= MAX_HPXLEN ) { + penalty[nuc] = 25; // Mark for deletion + continue; + } + // pointer in the ad_Buf buffer pointing at the running sum of positive residuals at start of parent window + char RESTRICT_PTR pn = ad_Buf+nuc*4+(AD_NRES_OFS-16)-parent->window_start*16; + // sum of squared residuals for positive residuals for flows < child->flow + float penPar = *((float*)(pn+child->flow*16+(AD_PRES_OFS-AD_NRES_OFS))); + // sum of squared residuals for negative residuals for flows < child->window_end + float penNeg = *((float*)(pn+child->window_end*16)); + penalty[nuc] = penPar + penNeg; + } + // find current incorporating base + int test_nuc = nuc_char_to_int(flow_order_[solution_flow]); + assert(called_nuc==test_nuc); + assert(called_nuc > -1); + assert(children[called_nuc]->flow == solution_flow); + PathRec RESTRICT_PTR childToKeep = children[called_nuc]; + //copy + char RESTRICT_PTR p = ad_Buf+ called_nuc*4 + AD_STATE_OFS; + recent_state_total = 0; + for(int i = parent->window_start, j = 0, e = childToKeep->window_end; i < e; ++i, j += 16) { + childToKeep->state[i] = *((float*)(p+j)); + childToKeep->pred[i] = *((float*)(p+j+(AD_PRED_OFS-AD_STATE_OFS))); + recent_state_total += childToKeep->state[i]; + } + //sse implementation with aligned memory; no gain as the number of elements to be summed up is small + //recent_state_total = vecSumSSE(state_Buf, countStates); + + copySSE(childToKeep->pred, parent->pred, parent->window_start << 2); + if (childToKeep->flow == parent->flow) + childToKeep->last_hp = parent->last_hp = min(parent->last_hp+1, MAX_HPXLEN); + else + childToKeep->last_hp = 1; + + recent_state_inphase = childToKeep->state[solution_flow]; + // Get delta penalty to next best solution + read.penalty_mismatch[base] = -1; // min delta penalty to earlier base hypothesis + read.penalty_residual[base] = 0; + if (solution_flow - parent->window_start > 0) + read.penalty_residual[base] = penalty[called_nuc] / (solution_flow - parent->window_start); + + for (int nuc = 0; nuc < 4; ++nuc) { + if (nuc == called_nuc) + continue; + float penalty_mismatch = penalty[called_nuc] - penalty[nuc]; + read.penalty_mismatch[base] = max(read.penalty_mismatch[base], penalty_mismatch); + } - // Apply easy termination rules - if (child->flow >= flow_order_.num_flows() || parent->last_hp >= MAX_HPXLEN ) { - penalty[nuc] = 25; // Mark for deletion - continue; + // Called state is the starting point for next base + PathRec RESTRICT_PTR swap = parent; + parent = children[called_nuc]; + children[called_nuc] = swap; } + read.state_inphase[solution_flow] = max(recent_state_inphase, 0.01f); + read.state_total[solution_flow] = max(recent_state_total, 0.01f); + } - // pointer in the ad_Buf buffer pointing at the running sum of positive residuals at start of parent window - char RESTRICT_PTR pn = ad_Buf+nuc*4+(AD_NRES_OFS-16)-parent->window_start*16; - - // sum of squared residuals for positive residuals for flows < child->flow - float penPar = *((float*)(pn+child->flow*16+(AD_PRES_OFS-AD_NRES_OFS))); - - // sum of squared residuals for negative residuals for flows < child->window_end - float penNeg = *((float*)(pn+child->window_end*16)); - - penalty[nuc] = penPar + penNeg; - } - - // find current incorporating base - assert(called_nuc > -1); - assert(children[called_nuc]->flow == solution_flow); - - PathRec RESTRICT_PTR childToKeep = children[called_nuc]; - //copy - char RESTRICT_PTR p = ad_Buf+ called_nuc*4 + AD_STATE_OFS; - - recent_state_total = 0; - for(int i = parent->window_start, j = 0, e = childToKeep->window_end; i < e; ++i, j += 16) { - childToKeep->state[i] = *((float*)(p+j)); - childToKeep->pred[i] = *((float*)(p+j+(AD_PRED_OFS-AD_STATE_OFS))); - recent_state_total += childToKeep->state[i]; - } - //sse implementation with aligned memory; no gain as the number of elements to be summed up is small -// recent_state_total = vecSumSSE(state_Buf, countStates); - - copySSE(childToKeep->pred, parent->pred, parent->window_start << 2); + if(recalibrate_predictions_) { + RecalibratePredictions(parent); + ResetRecalibrationStructures(num_flows_); + } + setZeroSSE(&read.prediction[0], num_flows_*sizeof(float)); + copySSE(&read.prediction[0], parent->pred, parent->window_end*sizeof(float)); +} - if (childToKeep->flow == parent->flow) - childToKeep->last_hp = parent->last_hp = min(parent->last_hp+1, MAX_HPXLEN); - else - childToKeep->last_hp = 1; - recent_state_inphase = childToKeep->state[solution_flow]; +void TreephaserSSE::ComputeQVmetrics_flow(BasecallerRead& read, vector& flow_to_base, const bool flow_predictors_) +{ + static const char nuc_int_to_char[5] = "ACGT"; + int num_flows = flow_order_.num_flows(); + read.state_inphase.assign(num_flows, 1); + read.state_total.assign(num_flows, 1); - // Get delta penalty to next best solution - read.penalty_mismatch[base] = -1; // min delta penalty to earlier base hypothesis - read.penalty_residual[base] = 0; + if (read.sequence.empty()) + return; + int num_bases = read.sequence.size(); + read.penalty_mismatch.assign(num_bases, 0); + read.penalty_residual.assign(num_bases, 0); - if (solution_flow - parent->window_start > 0) - read.penalty_residual[base] = penalty[called_nuc] / (solution_flow - parent->window_start); + PathRec RESTRICT_PTR parent = sv_PathPtr[0]; + PathRec RESTRICT_PTR children[4] = {sv_PathPtr[1], sv_PathPtr[2], sv_PathPtr[3], sv_PathPtr[4]}; + parent->flow = 0; + parent->window_start = 0; + parent->window_end = 1; + parent->res = 0.0f; + parent->metr = 0.0f; + parent->flowMetr = 0.0f; + parent->dotCnt = 0; + parent->state[0] = 1.0f; + parent->sequence_length = 0; + parent->last_hp = 0; + parent->pred[0] = 0.0f; - for (int nuc = 0; nuc < 4; ++nuc) { - if (nuc == called_nuc) - continue; - float penalty_mismatch = penalty[called_nuc] - penalty[nuc]; - read.penalty_mismatch[base] = max(read.penalty_mismatch[base], penalty_mismatch); - } + float recent_state_inphase = 1; + float recent_state_total = 1; - // Called state is the starting point for next base - PathRec RESTRICT_PTR swap = parent; - parent = children[called_nuc]; - children[called_nuc] = swap; - } + // main loop for base calling + for (int solution_flow = 0, base = 0; solution_flow < num_flows; ++solution_flow) { + for (; basecalib_A[parent->flow] = (*As_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + parent->calib_B[parent->flow] = (*Bs_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + } + // compute child path flow states, predicted signal,negative and positive penalties + advanceState4(parent, num_flows); + + float penalty[4] = { 0, 0, 0, 0 }; + int called_nuc = -1; + for(int nuc = 0; nuc < 4; ++nuc) { + PathRec RESTRICT_PTR child = children[nuc]; + if (nuc_int_to_char[nuc] == flow_order_[solution_flow]) + called_nuc = nuc; + child->flow = min(ad_Idx[nuc], num_flows); + child->window_end = min(ad_End[nuc], num_flows); + child->window_start = min(ad_Beg[nuc], child->window_end); + // Apply easy termination rules + if (child->flow >= num_flows || parent->last_hp >= MAX_HPXLEN ) { + penalty[nuc] = 25; // Mark for deletion + continue; + } + // pointer in the ad_Buf buffer pointing at the running sum of positive residuals at start of parent window + char RESTRICT_PTR pn = ad_Buf+nuc*4+(AD_NRES_OFS-16)-parent->window_start*16; + // sum of squared residuals for positive residuals for flows < child->flow + float penPar = *((float*)(pn+child->flow*16+(AD_PRES_OFS-AD_NRES_OFS))); + // sum of squared residuals for negative residuals for flows < child->window_end + float penNeg = *((float*)(pn+child->window_end*16)); + penalty[nuc] = penPar + penNeg; + } + // find current incorporating base + int test_nuc = nuc_char_to_int(flow_order_[solution_flow]); + assert(called_nuc==test_nuc); + assert(called_nuc > -1); + assert(children[called_nuc]->flow == solution_flow); + PathRec RESTRICT_PTR childToKeep = children[called_nuc]; + //copy + char RESTRICT_PTR p = ad_Buf+ called_nuc*4 + AD_STATE_OFS; + recent_state_total = 0; + for(int i = parent->window_start, j = 0, e = childToKeep->window_end; i < e; ++i, j += 16) { + childToKeep->state[i] = *((float*)(p+j)); + childToKeep->pred[i] = *((float*)(p+j+(AD_PRED_OFS-AD_STATE_OFS))); + recent_state_total += childToKeep->state[i]; + } + //sse implementation with aligned memory; no gain as the number of elements to be summed up is small + //recent_state_total = vecSumSSE(state_Buf, countStates); + + copySSE(childToKeep->pred, parent->pred, parent->window_start << 2); + if (childToKeep->flow == parent->flow) + childToKeep->last_hp = parent->last_hp = min(parent->last_hp+1, MAX_HPXLEN); + else + childToKeep->last_hp = 1; + + recent_state_inphase = childToKeep->state[solution_flow]; + // Get delta penalty to next best solution + read.penalty_mismatch[base] = -1; // min delta penalty to earlier base hypothesis + read.penalty_residual[base] = 0; + if (solution_flow - parent->window_start > 0) + read.penalty_residual[base] = penalty[called_nuc] / (solution_flow - parent->window_start); + + for (int nuc = 0; nuc < 4; ++nuc) { + if (nuc == called_nuc) + continue; + float penalty_mismatch = penalty[called_nuc] - penalty[nuc]; + read.penalty_mismatch[base] = max(read.penalty_mismatch[base], penalty_mismatch); + } + // Called state is the starting point for next base + PathRec RESTRICT_PTR swap = parent; + parent = children[called_nuc]; + children[called_nuc] = swap; + } read.state_inphase[solution_flow] = max(recent_state_inphase, 0.01f); read.state_total[solution_flow] = max(recent_state_total, 0.01f); - } + } + + if (flow_predictors_) { //if (flow_predictors_) + read.penalty_mismatch_flow.assign(num_flows, 0); + read.penalty_residual_flow.assign(num_flows, 0); + //vector flows_to_proc; + for (int solution_flow = 0; solution_flow < num_flows; ++solution_flow) { + int curr_base = flow_to_base[solution_flow]; + if (curr_base >= 0) { + assert (curr_base0) { + for (int i=0; i& flow_to_base, const bool flow_predictors_=false); + + int nuc_char_to_int(char nuc) {if (nuc=='A') return 0; if (nuc=='C') return 1; if (nuc=='G') return 2; if (nuc=='T') return 3; return -1;} protected: diff --git a/Analysis/BaseCaller/TreephaserVEC.cpp b/Analysis/BaseCaller/TreephaserVEC.cpp index c8f84c4f..a56e6154 100644 --- a/Analysis/BaseCaller/TreephaserVEC.cpp +++ b/Analysis/BaseCaller/TreephaserVEC.cpp @@ -1304,15 +1304,15 @@ void TreephaserSSE::WindowedNormalize(BasecallerRead& read, int num_steps) void TreephaserSSE::ComputeQVmetrics(BasecallerRead& read) { static const char nuc_int_to_char[5] = "ACGT"; - - read.state_inphase.assign(flow_order_.num_flows(), 1); - read.state_total.assign(flow_order_.num_flows(), 1); + int num_flows = flow_order_.num_flows(); + read.state_inphase.assign(num_flows, 1); + read.state_total.assign(num_flows, 1); if (read.sequence.empty()) return; - - read.penalty_mismatch.assign(read.sequence.size(), 0); - read.penalty_residual.assign(read.sequence.size(), 0); + int num_bases = read.sequence.size(); + read.penalty_mismatch.assign(num_bases, 0); + read.penalty_residual.assign(num_bases, 0); PathRec RESTRICT_PTR parent = sv_PathPtr[0]; PathRec RESTRICT_PTR children[4] = {sv_PathPtr[1], sv_PathPtr[2], sv_PathPtr[3], sv_PathPtr[4]}; @@ -1332,33 +1332,157 @@ void TreephaserSSE::ComputeQVmetrics(BasecallerRead& read) float recent_state_total = 1; // main loop for base calling - for (int solution_flow = 0, base = 0; solution_flow < flow_order_.num_flows(); ++solution_flow) { - for (; base < (int)read.sequence.size() and read.sequence[base] == flow_order_[solution_flow]; ++base) { + for (int solution_flow = 0, base = 0; solution_flow < num_flows; ++solution_flow) { + for (; basecalib_A[parent->flow] = (*As_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + parent->calib_B[parent->flow] = (*Bs_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + } + // compute child path flow states, predicted signal,negative and positive penalties + advanceState4(parent, num_flows); - float penalty[4] = { 0, 0, 0, 0 }; + float penalty[4] = { 0, 0, 0, 0 }; + int called_nuc = -1; + for(int nuc = 0; nuc < 4; ++nuc) { + PathRec RESTRICT_PTR child = children[nuc]; + if (nuc_int_to_char[nuc] == flow_order_[solution_flow]) + called_nuc = nuc; + child->flow = min(ad_Idx.A[nuc], flow_order_.num_flows()); + child->window_end = min(ad_End.A[nuc], flow_order_.num_flows()); + child->window_start = min(ad_Beg.A[nuc], child->window_end); - int called_nuc = -1; + // Apply easy termination rules + if (child->flow >= num_flows || parent->last_hp >= MAX_HPXLEN ) { + penalty[nuc] = 25; // Mark for deletion + continue; + } - if(recalibrate_predictions_) { - parent->calib_A[parent->flow] = (*As_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); - parent->calib_B[parent->flow] = (*Bs_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + // pointer in the ad_Buf buffer pointing at the running sum of positive residuals at start of parent window +// char RESTRICT_PTR pn = ad_Buf+nuc*4+(AD_NRES_OFS-16)-parent->window_start*16; + + // sum of squared residuals for positive residuals for flows < child->flow + float penPar = pres_Buf[child->flow-parent->window_start].A[nuc];// *((float RESTRICT_PTR)(ad_Buf+nuc*4+(AD_PRES_OFS)+(child->flow-parent->window_start-1)*16)); + + // sum of squared residuals for negative residuals for flows < child->window_end + float penNeg = nres_Buf[child->window_end-parent->window_start].A[nuc];// *((float RESTRICT_PTR)(ad_Buf+nuc*4+AD_NRES_OFS+(child->window_end-parent->window_start-1)*16)); + + penalty[nuc] = penPar + penNeg; } - // compute child path flow states, predicted signal,negative and positive penalties - advanceState4(parent, flow_order_.num_flows()); + // find current incorporating base + assert(called_nuc > -1); + assert(children[called_nuc]->flow == solution_flow); + PathRec RESTRICT_PTR childToKeep = children[called_nuc]; + //copy +// char RESTRICT_PTR p = ad_Buf+ called_nuc*4 + AD_STATE_OFS; + + recent_state_total = 0; + for(int i = parent->window_start, j = 1, e = childToKeep->window_end; i < e; ++i, j ++) { + childToKeep->state[i] = state_Buf[j].A[called_nuc];// *((float*)(p+j*16)); + childToKeep->pred[i] = pred_Buf[j].A[called_nuc];// *((float*)(p+j*16+(AD_PRED_OFS-AD_STATE_OFS))); + recent_state_total += childToKeep->state[i]; + } + //sse implementation with aligned memory; no gain as the number of elements to be summed up is small +// recent_state_total = vecSumSSE(state_Buf, countStates); + + copySSE(childToKeep->pred, parent->pred, parent->window_start << 2); + + if (childToKeep->flow == parent->flow) + childToKeep->last_hp = parent->last_hp + 1; + else + childToKeep->last_hp = 1; + + recent_state_inphase = childToKeep->state[solution_flow]; + + // Get delta penalty to next best solution + read.penalty_mismatch[base] = -1; // min delta penalty to earlier base hypothesis + read.penalty_residual[base] = 0; + + if (solution_flow - parent->window_start > 0) + read.penalty_residual[base] = penalty[called_nuc] / (solution_flow - parent->window_start); + + for (int nuc = 0; nuc < 4; ++nuc) { + if (nuc == called_nuc) + continue; + float penalty_mismatch = penalty[called_nuc] - penalty[nuc]; + read.penalty_mismatch[base] = max(read.penalty_mismatch[base], penalty_mismatch); + } + + // Called state is the starting point for next base + PathRec RESTRICT_PTR swap = parent; + parent = children[called_nuc]; + children[called_nuc] = swap; + } + read.state_inphase[solution_flow] = max(recent_state_inphase, 0.01f); + read.state_total[solution_flow] = max(recent_state_total, 0.01f); + } + + if(recalibrate_predictions_) { + RecalibratePredictions(parent); + ResetRecalibrationStructures(num_flows_); + } + setZeroSSE(&read.prediction[0], num_flows_*sizeof(float)); + copySSE(&read.prediction[0], parent->pred, parent->window_end*sizeof(float)); +} + + +void TreephaserSSE::ComputeQVmetrics_flow(BasecallerRead& read, vector& flow_to_base, const bool flow_predictors_) +{ + static const char nuc_int_to_char[5] = "ACGT"; + int num_flows = flow_order_.num_flows(); + read.state_inphase.assign(num_flows, 1); + read.state_total.assign(num_flows, 1); + + if (read.sequence.empty()) + return; + int num_bases = read.sequence.size(); + read.penalty_mismatch.assign(num_bases, 0); + read.penalty_residual.assign(num_bases, 0); + if (flow_predictors_) { + read.penalty_mismatch_flow.assign(num_flows, 0); + read.penalty_residual_flow.assign(num_flows, 0); + } + + PathRec RESTRICT_PTR parent = sv_PathPtr[0]; + PathRec RESTRICT_PTR children[4] = {sv_PathPtr[1], sv_PathPtr[2], sv_PathPtr[3], sv_PathPtr[4]}; + parent->flow = 0; + parent->window_start = 0; + parent->window_end = 1; + parent->res = 0.0f; + parent->metr = 0.0f; + parent->flowMetr = 0.0f; + parent->dotCnt = 0; + parent->state[0] = 1.0f; + parent->sequence_length = 0; + parent->last_hp = 0; + parent->pred[0] = 0.0f; + + float recent_state_inphase = 1; + float recent_state_total = 1; + + // main loop for base calling + for (int solution_flow = 0, base = 0; solution_flow < num_flows; ++solution_flow) { + for (; basecalib_A[parent->flow] = (*As_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + parent->calib_B[parent->flow] = (*Bs_).at(parent->flow).at(flow_order_.int_at(parent->flow)).at(parent->last_hp); + } + // compute child path flow states, predicted signal,negative and positive penalties + advanceState4(parent, num_flows); + + float penalty[4] = { 0, 0, 0, 0 }; + int called_nuc = -1; for(int nuc = 0; nuc < 4; ++nuc) { PathRec RESTRICT_PTR child = children[nuc]; - if (nuc_int_to_char[nuc] == flow_order_[solution_flow]) called_nuc = nuc; - child->flow = min(ad_Idx.A[nuc], flow_order_.num_flows()); child->window_end = min(ad_End.A[nuc], flow_order_.num_flows()); child->window_start = min(ad_Beg.A[nuc], child->window_end); // Apply easy termination rules - if (child->flow >= flow_order_.num_flows() || parent->last_hp >= MAX_HPXLEN ) { + if (child->flow >= num_flows || parent->last_hp >= MAX_HPXLEN ) { penalty[nuc] = 25; // Mark for deletion continue; } @@ -1420,10 +1544,35 @@ void TreephaserSSE::ComputeQVmetrics(BasecallerRead& read) parent = children[called_nuc]; children[called_nuc] = swap; } - read.state_inphase[solution_flow] = max(recent_state_inphase, 0.01f); read.state_total[solution_flow] = max(recent_state_total, 0.01f); - } + } + + if (flow_predictors_) { //if (flow_predictors_) + //vector flows_to_proc; + for (int solution_flow = 0; solution_flow < num_flows; ++solution_flow) { + int curr_base = flow_to_base[solution_flow]; + if (curr_base >= 0) { + // copy from what's stored in read.penalty_mismatch[base] + read.penalty_mismatch_flow[solution_flow] = read.penalty_mismatch[curr_base]; + read.penalty_residual_flow[solution_flow] = read.penalty_residual[curr_base]; + /* + int nFlows = flows_to_proc.size(); + if (nFlows>0) { + for (int i=0; i& flow_to_base, const bool flow_predictors_=false); + + int nuc_char_to_int(char nuc) {if (nuc=='A') return 0; if (nuc=='C') return 1; if (nuc=='G') return 2; if (nuc=='T') return 3; return -1;} protected: diff --git a/Analysis/BkgModel/Bookkeeping/BeadTracker.cpp b/Analysis/BkgModel/Bookkeeping/BeadTracker.cpp index c9db7d60..448094fb 100644 --- a/Analysis/BkgModel/Bookkeeping/BeadTracker.cpp +++ b/Analysis/BkgModel/Bookkeeping/BeadTracker.cpp @@ -1,7 +1,7 @@ /* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ #include #include "BeadTracker.h" -#include "mixed.h" +#include "ClonalFilter/mixed.h" #include "ImageLoader.h" #include "MathUtil.h" #include "LevMarState.h" diff --git a/Analysis/BkgModel/Bookkeeping/EmphasisVector.cpp b/Analysis/BkgModel/Bookkeeping/EmphasisVector.cpp index aded1b2a..b9f242fc 100644 --- a/Analysis/BkgModel/Bookkeeping/EmphasisVector.cpp +++ b/Analysis/BkgModel/Bookkeeping/EmphasisVector.cpp @@ -81,7 +81,8 @@ int RescaleVector ( float *vect, int npts ) } // just a gaussian -int GenerateBlankEmphasis ( float *vect, float *emp, int tsize, float t_center, const vector& frames_per_point, const vector& frameNumber ) +int GenerateBlankEmphasis ( float *vect, float *emp, int tsize, float t_center, + const vector& frames_per_point, const vector& frameNumber ) { int npts = tsize; @@ -97,8 +98,27 @@ int GenerateBlankEmphasis ( float *vect, float *emp, int tsize, float t_center, return ( npts ); } +// t-distribution heavier tailed but has natural scale span +int BlankDataWeight(float *vect, int tsize, + float t_center, const vector& frames_per_point, const vector& frameNumber, + float blank_span){ + int npts = tsize; + for (int i=0; i& frames_per_point, const vector& frameNumber, float width, float ampl ) +int GenerateStratifiedEmphasis ( float *vect, int vn, float *emp, int tsize, + float t_center, const vector& frames_per_point, const vector& frameNumber, + float width, float ampl ) { int npts = tsize; float na = emp[0]+vn*emp[1]; @@ -124,6 +144,37 @@ int GenerateStratifiedEmphasis ( float *vect, int vn, float *emp, int tsize, flo return ( npts ); } +// fix from offsets +int GenerateNmerDataWeights(float *vect, int tsize, int vn, + float t_center, const vector& frames_per_point, const vector& frameNumber, + DataWeightDefaults &data_weights + ){ + // we fall to zero here + float nmer_zero = t_center+data_weights.zero_span+vn*data_weights.nmer_span_increase; + // we fall to zero at this rate + float zero_slope= (data_weights.zero_span*(1.0f-data_weights.zero_fade_start)); + if (zero_slope<1.0f ) zero_slope =1.0f; // 1 frame drop minimum + zero_slope = -1.0f/zero_slope; // no divide by zero, no weird negatives + + float offset= -1.0f * zero_slope*nmer_zero; + int npts = tsize; + for (int i=0; i1.0f) tmp_weight = 1.0f; + + // now apply constraint for prefix + if (t_center-frameNumber[i]>=data_weights.prefix_start) tmp_weight=data_weights.prefix_value; + vect[i] = frames_per_point[i]; + vect[i] *= tmp_weight; + + } + RescaleVector(vect, npts); + return(npts); +} + // prepare to export the default math as a separate routine // so that Rcpp can wrap it and we see what we expect to see @@ -155,23 +206,39 @@ void EmphasisClass::BuildCurrentEmphasisTable ( float t_center, float amult ) local_frames_per_point = my_frames_per_point; else{ local_frames_per_point.assign(my_frames_per_point.size(),1); // no weighting by compression to get rid of edge artifacts - int cur_lowest = 1000; - // annoyingly, it looks like I need to keep the emphasis at the >start< for the clonal penalty - //@TODO: fix the clonal penalty in multiflow lev mar to be sensible instead of senseless - for (unsigned int i=0; i0.0f){ + EmphasisScale[vn]=GenerateNmerDataWeights(vect, npts, vn, + t_center, local_frames_per_point, my_frameNumber, + data_weights); + + } else { + EmphasisScale[vn] = BlankDataWeight(vect, npts, + t_center, local_frames_per_point, my_frameNumber, + data_weights.blank_span); + } +/* + int vm=vn+1; + if (vm>=numEv) vm=vn; + float *tect = EmphasisVectorByHomopolymer[vm] = &emphasis_vector_storage[npts*(vm)]; + GenerateIndividualEmphasis ( tect, vn, emp, npts, + t_center, local_frames_per_point, my_frameNumber, + amult,emphasis_width, emphasis_ampl ); // actual data + for(int vp=0; vp emphasis_vector_storage; // storage for emphasis vectors float** EmphasisVectorByHomopolymer; // array of pointers to different vectors std::vector EmphasisScale; // scaling factor for each vector - float emp[NUMEMPHASISPARAMETERS]; // parameters for emphasis vector generation std::vector nonZeroEmphasisFrames; // number of non zero frame values for each emphasis vector + float emp[NUMEMPHASISPARAMETERS]; // parameters for emphasis vector generation bool point_emphasis_by_compression; // avoid emphasis artifacts due to highly compressed points // keep timing parameters as well float emphasis_width; // parameters scaling the emphasis vector float emphasis_ampl; // parameters scaling the emphasis vector + + DataWeightDefaults data_weights; // real control + // timing parameters - warning, if time-compression changes these need to be updated std::vector my_frames_per_point; std::vector my_frameNumber; @@ -41,6 +46,8 @@ class EmphasisClass EmphasisClass(); ~EmphasisClass(); + void SetUpEmphasis(TimeAndEmphasisDefaults &data_control, TimeCompression &time_c); + private: void DetermineNonZeroEmphasisFrames(int hp); void AllocateScratch(); @@ -65,6 +72,7 @@ class EmphasisClass & my_frames_per_point & my_frameNumber & npts + & data_weights & point_emphasis_by_compression; if ( npts > 0 ) @@ -88,6 +96,7 @@ class EmphasisClass & my_frames_per_point & my_frameNumber & npts + & data_weights & point_emphasis_by_compression; // fprintf(stdout, "done EmphasisVector\n"); diff --git a/Analysis/BkgModel/Bookkeeping/RegionParamDefault.h b/Analysis/BkgModel/Bookkeeping/RegionParamDefault.h index 96603412..04c090f2 100644 --- a/Analysis/BkgModel/Bookkeeping/RegionParamDefault.h +++ b/Analysis/BkgModel/Bookkeeping/RegionParamDefault.h @@ -23,8 +23,8 @@ struct RegionParamDefault{ float tau_R_o_default; float tau_E_default; float min_tauB_default; - float max_tauB_default; + float tauB_smooth_range_default; float tshift_default; float krate_default[NUMNUC]; diff --git a/Analysis/BkgModel/Bookkeeping/TimeCompression.h b/Analysis/BkgModel/Bookkeeping/TimeCompression.h index 82520e8f..d73cd012 100644 --- a/Analysis/BkgModel/Bookkeeping/TimeCompression.h +++ b/Analysis/BkgModel/Bookkeeping/TimeCompression.h @@ -12,7 +12,7 @@ #include "Serialization.h" #include "IonErr.h" #include "VectorMacros.h" -#include "Image.h" +//#include "Image.h" #if defined( __SSE__ ) && !defined( __CUDACC__ ) #include diff --git a/Analysis/BkgModel/Bookkeeping/TimeControl.cpp b/Analysis/BkgModel/Bookkeeping/TimeControl.cpp index 59463021..b35b560c 100644 --- a/Analysis/BkgModel/Bookkeeping/TimeControl.cpp +++ b/Analysis/BkgModel/Bookkeeping/TimeControl.cpp @@ -9,21 +9,50 @@ TimeAndEmphasisDefaults::TimeAndEmphasisDefaults() time_left_avg = 5; time_start_detail = -5; time_stop_detail = 16; + +} + +EmphasisDefaults::EmphasisDefaults(){ point_emphasis_by_compression = true; memcpy(emp,_emp,sizeof(float[NUMEMPHASISPARAMETERS])); emphasis_ampl_default = 7.25; emphasis_width_default = 2.89; + +} + +DataWeightDefaults::DataWeightDefaults(){ + blank_span = 17.0f; + zero_span = 17.0f; + nmer_span_increase = 1.0f; + zero_fade_start = 0.5f; + prefix_start = 3; + prefix_value = 0.15f; + use_data_weight = false; + } -void TimeAndEmphasisDefaults::DumpPoorlyStructuredText(){ +void EmphasisDefaults::DumpPoorlyStructuredText(){ printf ( "emphasis: %f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",emp[0],emp[1],emp[2],emp[3],emp[4],emp[5],emp[6],emp[7] ); printf ( "emp_amplitude: \t%f\n",emphasis_ampl_default ); printf ( "emp_width: \t%f\n",emphasis_width_default ); } -void TimeAndEmphasisDefaults::FromJson(Json::Value &gopt_params){ +void DataWeightDefaults::FromJson(Json::Value &gopt_params){ + if (!gopt_params["DataWeight"].isNull()){ + use_data_weight=true; + const Json::Value data_weight = gopt_params["DataWeight"]; + blank_span = data_weight["blank_span"].asFloat(); + zero_span = data_weight["zero_span"].asFloat(); + nmer_span_increase = data_weight["nmer_span_increase"].asFloat(); + zero_fade_start = data_weight["zero_fade_start"].asFloat(); + prefix_start = data_weight["prefix_start"].asFloat(); + prefix_value = data_weight["prefix_value"].asFloat(); + } +} + +void EmphasisDefaults::FromJson(Json::Value &gopt_params){ const Json::Value emphasis = gopt_params["emphasis"]; for ( int index = 0; index < (int) emphasis.size(); ++index ) emp[index] = emphasis[index].asFloat(); @@ -31,3 +60,8 @@ void TimeAndEmphasisDefaults::FromJson(Json::Value &gopt_params){ emphasis_ampl_default = gopt_params["emp_amplitude"].asFloat(); emphasis_width_default = gopt_params["emp_width"].asFloat(); } + +void TimeAndEmphasisDefaults::FromJson(Json::Value &gopt_params){ + emphasis_params.FromJson(gopt_params); + data_weights.FromJson(gopt_params); +} diff --git a/Analysis/BkgModel/Bookkeeping/TimeControl.h b/Analysis/BkgModel/Bookkeeping/TimeControl.h index d69b00ff..c8f8a9e2 100644 --- a/Analysis/BkgModel/Bookkeeping/TimeControl.h +++ b/Analysis/BkgModel/Bookkeeping/TimeControl.h @@ -12,13 +12,38 @@ #include "BkgMagicDefines.h" #include "Serialization.h" +struct DataWeightDefaults{ -struct TimeAndEmphasisDefaults{ - float nuc_flow_frame_width; - int time_left_avg; - int time_start_detail; - int time_stop_detail; + // revised emphasis parameterization for clarity + // linear approximation to weighting + bool use_data_weight; + float blank_span; // generic emphasis span (17 frames) + float zero_span; // span for a zero-mer emphasis (13 frames) + float nmer_span_increase; // frames per additiona n-mer over a zeromer (1.0 frames) + float zero_fade_start; // fraction of zeromer span to fade-to-zero weight over (0.5) + float prefix_start; // offset for prefix = 3 (-3 frames) + float prefix_value; // fraction of maximum value = 0.15 + + DataWeightDefaults(); + void FromJson(Json::Value &gopt_params); +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) + { + ar + & blank_span + & zero_span + & nmer_span_increase + & zero_fade_start + & prefix_start + & prefix_value + & use_data_weight; + } + +}; +struct EmphasisDefaults{ //@TODO: why are these not simply part of "EmphasisVector.h"? float emphasis_ampl_default; float emphasis_width_default; @@ -26,26 +51,51 @@ struct TimeAndEmphasisDefaults{ float emp[NUMEMPHASISPARAMETERS]; // parameters for emphasis vector generation bool point_emphasis_by_compression; - TimeAndEmphasisDefaults(); + EmphasisDefaults(); void DumpPoorlyStructuredText(); void FromJson(Json::Value &gopt_params); +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) + { + ar + & emphasis_ampl_default + & emphasis_width_default + & numEv + & emp; + } +}; + + +struct TimeAndEmphasisDefaults{ + float nuc_flow_frame_width; + int time_left_avg; + int time_start_detail; + int time_stop_detail; + + + DataWeightDefaults data_weights; + EmphasisDefaults emphasis_params; + + TimeAndEmphasisDefaults(); + + void FromJson(Json::Value &gopt_params); private: friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int version) { ar + & data_weights + & nuc_flow_frame_width & time_left_avg & time_start_detail & time_stop_detail - & emphasis_ampl_default - & emphasis_width_default - & numEv - & point_emphasis_by_compression - & emp; + &emphasis_params; } }; diff --git a/Analysis/BkgModel/CUDA/GpuMultiFlowFitControl.cpp b/Analysis/BkgModel/CUDA/GpuMultiFlowFitControl.cpp index debc15cb..2b14eac1 100644 --- a/Analysis/BkgModel/CUDA/GpuMultiFlowFitControl.cpp +++ b/Analysis/BkgModel/CUDA/GpuMultiFlowFitControl.cpp @@ -41,10 +41,9 @@ GpuMultiFlowFitMatrixConfig* GpuMultiFlowFitControl::createConfig( { // Build the configuration. GpuMultiFlowFitMatrixConfig* config = - new GpuMultiFlowFitMatrixConfig(levMarSparseMatrices->GetFitDescriptorByName(fitName.c_str()), + new GpuMultiFlowFitMatrixConfig(const_cast(levMarSparseMatrices)->GetFitDescriptorByName(fitName.c_str()), BkgFitStructures::Steps, BkgFitStructures::NumSteps, _activeFlowKey, _activeFlowMax); - // Accumulate maximum values. DetermineMaxSteps(config->GetNumSteps()); DetermineMaxParams(config->GetNumParamsToFit()); @@ -54,7 +53,6 @@ GpuMultiFlowFitMatrixConfig* GpuMultiFlowFitControl::createConfig( return config; } - void GpuMultiFlowFitControl::DetermineMaxSteps(int steps) { if (_maxSteps < steps) diff --git a/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.cpp b/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.cpp index 368724a8..63104627 100644 --- a/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.cpp +++ b/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.cpp @@ -3,27 +3,27 @@ #include "CudaDefines.h" #include "GpuMultiFlowFitMatrixConfig.h" -GpuMultiFlowFitMatrixConfig::GpuMultiFlowFitMatrixConfig(fit_descriptor* fd, CpuStep* Steps, int maxSteps, int flow_key, int flow_block_size) +GpuMultiFlowFitMatrixConfig::GpuMultiFlowFitMatrixConfig(const std::vector& fds, CpuStep* Steps, int maxSteps, int flow_key, int flow_block_size) { // num of partial derivative steps to compute for this fit descriptor - _numSteps = BkgFitStructures::GetNumParDerivStepsForFitDescriptor(fd); + _numSteps = BkgFitStructures::GetNumParDerivStepsForFitDescriptor(fds); // number of actual partial derivative steps to compute _numSteps = _numSteps + 2; // Need to calculate FVAL and YERR always // calculate num of params to fit based on param sensitivity classification - _numParamsToFit = BkgFitStructures::GetNumParamsToFitForDescriptor(fd, flow_key, flow_block_size); + _numParamsToFit = BkgFitStructures::GetNumParamsToFitForDescriptor(fds, flow_key, flow_block_size); // collect partial derivative steps from Steps structure in // BkgFitStructures.cpp for this fit - CreatePartialDerivStepsVector(fd, Steps, maxSteps); + CreatePartialDerivStepsVector(fds, Steps, maxSteps); _paramIdxMap = new unsigned int[_numParamsToFit]; _affectedFlowsForParamsBitMap = new unsigned int[_numParamsToFit]; _paramToStepMap = new unsigned int[_numParamsToFit]; _jtjMatrixBitMap = new unsigned int[_numParamsToFit*_numParamsToFit]; - CreateAffectedFlowsVector(fd, flow_key, flow_block_size); + CreateAffectedFlowsVector(fds, flow_key, flow_block_size); CreateBitMapForJTJMatrixComputation(); } @@ -36,7 +36,7 @@ GpuMultiFlowFitMatrixConfig::~GpuMultiFlowFitMatrixConfig() delete [] _paramToStepMap; } -void GpuMultiFlowFitMatrixConfig::CreatePartialDerivStepsVector(fit_descriptor* fd, CpuStep* Steps, int maxSteps) +void GpuMultiFlowFitMatrixConfig::CreatePartialDerivStepsVector(const std::vector& fds, CpuStep* Steps, int maxSteps) { _partialDerivSteps = new CpuStep[_numSteps]; @@ -46,11 +46,11 @@ void GpuMultiFlowFitMatrixConfig::CreatePartialDerivStepsVector(fit_descriptor* if (Steps[maxSteps - 1].PartialDerivMask == YERR) _partialDerivSteps[_numSteps - 1] = Steps[maxSteps - 1]; - for (int i=1; fd[i-1].comp != TBL_END; ++i) + for (int i=1; fds[i-1].comp != TBL_END; ++i) { for (int j=0; j& fds, int flow_key, int flow_block_size ) @@ -70,13 +70,13 @@ void GpuMultiFlowFitMatrixConfig::CreateAffectedFlowsVector( unsigned int paramIdx = 0; unsigned int actualStep = 1; - for (int i=0; fd[i].comp != TBL_END; ++i) + for (int i=0; fds[i].comp != TBL_END; ++i) { // Calculate a base index for reaching into the BeadParams / reg_params structure. - unsigned int baseIndex = fd[i].bead_params_func ? ( dummyBead.*( fd[i].bead_params_func ))() - reinterpret_cast< float * >( & dummyBead ) - : ( dummyReg .*( fd[i].reg_params_func ))() - reinterpret_cast< float * >( & dummyReg ); + unsigned int baseIndex = fds[i].bead_params_func ? ( dummyBead.*( fds[i].bead_params_func ))() - reinterpret_cast< float * >( & dummyBead ) + : ( dummyReg .*( fds[i].reg_params_func ))() - reinterpret_cast< float * >( & dummyReg ); - switch(fd[i].ptype) + switch(fds[i].ptype) { case ParamTypeAllFlow: _paramIdxMap[paramIdx] = baseIndex; diff --git a/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.h b/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.h index 4272eaf5..571f15c6 100644 --- a/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.h +++ b/Analysis/BkgModel/CUDA/GpuMultiFlowFitMatrixConfig.h @@ -8,7 +8,7 @@ class GpuMultiFlowFitMatrixConfig { public: - GpuMultiFlowFitMatrixConfig(fit_descriptor*, CpuStep*, int maxSteps, int flow_key, int flow_block_size); + GpuMultiFlowFitMatrixConfig(const std::vector& fds, CpuStep*, int maxSteps, int flow_key, int flow_block_size); ~GpuMultiFlowFitMatrixConfig(); int GetNumSteps() { return _numSteps; } @@ -18,8 +18,8 @@ class GpuMultiFlowFitMatrixConfig CpuStep* GetPartialDerivSteps() { return _partialDerivSteps; } private: - void CreatePartialDerivStepsVector(fit_descriptor*, CpuStep*, int); - void CreateAffectedFlowsVector(fit_descriptor*, int flow_key, int flow_block_size); + void CreatePartialDerivStepsVector(const std::vector& fds, CpuStep*, int); + void CreateAffectedFlowsVector(const std::vector& fds, int flow_key, int flow_block_size); void CreateBitMapForJTJMatrixComputation(); private: diff --git a/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.cpp b/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.cpp index e77496af..47da144d 100644 --- a/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.cpp +++ b/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.cpp @@ -7,7 +7,7 @@ */ #include "ClonalFilterWrapper.h" -#include "mixed.h" +#include "ClonalFilter/mixed.h" #include "hdf5.h" #define MIXED_PPF_CUTOFF 0.84f diff --git a/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.h b/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.h index 61855766..3f9e056b 100644 --- a/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.h +++ b/Analysis/BkgModel/CUDA/HostDataWrapper/ClonalFilterWrapper.h @@ -11,7 +11,7 @@ #include #include -#include "polyclonal_filter.h" +#include "ClonalFilter/polyclonal_filter.h" #include "LayoutTranslator.h" diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitMatDat.h b/Analysis/BkgModel/Fitters/Complex/BkgFitMatDat.h index 8dbdd807..0eb00a72 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitMatDat.h +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitMatDat.h @@ -4,7 +4,7 @@ #include -class BkgFitMatDat { +class BkgFitMatDat { public: arma::Mat *jtj; arma::Col *rhs; diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.cpp b/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.cpp index 5d7a3fc6..48761cbe 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.cpp +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.cpp @@ -49,6 +49,8 @@ BkgFitMatrixPacker:: BkgFitMatrixPacker (int imgLen,fit_instructions &fi,Partial nOutputs = fi.output_len; outputList = fi.output; + for (int i=0; idelta->at (i))) + if (std::isnan (data->delta->at (i))) { delta_ok = false; break; diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.h b/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.h index d54bf92b..d4349569 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.h +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitMatrixPacker.h @@ -9,6 +9,7 @@ #include "BkgMagicDefines.h" #include "BeadParams.h" #include "RegionParams.h" +#include class BkgFitMatDat; @@ -81,7 +82,7 @@ class mat_assy_input_line { mat_row = mat_col = 0; matId = JTJ_MAT; } - ~mat_assy_input_line() { delete [] affected_flows; } + ~mat_assy_input_line() { if (affected_flows) delete [] affected_flows; } void realloc( int size ) { delete [] affected_flows; affected_flows = new int[size]; } PartialDerivComponent comp1; @@ -93,6 +94,7 @@ class mat_assy_input_line { AssyMatID matId; int mat_row; int mat_col; + std::string derivName; }; @@ -115,10 +117,12 @@ struct mat_assembly_instruction { // structure that holds the output mapping of the solution matrix (the delta vector) // to the params structure struct delta_mat_output_line { + delta_mat_output_line() : delta_ndx(0), bead_params_func(NULL), reg_params_func(NULL), array_index(0) {} int delta_ndx; float * ( BeadParams::* bead_params_func )(); float * ( reg_params::* reg_params_func )(); int array_index; + std::string name; }; // collection of all input and output instruction information for a single fit configuration @@ -127,6 +131,13 @@ struct fit_instructions { int input_len; struct delta_mat_output_line *output; int output_len; + + fit_instructions() { + input = NULL; + input_len = 0; + output = NULL; + output_len = 0; + } }; typedef enum { @@ -152,6 +163,7 @@ class BkgFitMatrixPacker double *GetDestMatrixPtr(AssyMatID mat_id,int row,int col); + std::vector compNames; private: @@ -175,12 +187,12 @@ class BkgFitMatrixPacker public: fit_instructions& my_fit_instructions; - delta_mat_output_line* getOuputList() { return outputList; } - mat_assembly_instruction* getInstList() { return instList; } + delta_mat_output_line* getOuputList() const { return outputList; } + mat_assembly_instruction* getInstList() const { return instList; } void BuildMatrix(bool accum); - int getNumInstr() { return nInstr; } - int getNumOutputs() { return nOutputs; } - int getNumException() { return numException; } + int getNumInstr() const { return nInstr; } + int getNumOutputs() const { return nOutputs; } + int getNumException() const { return numException; } void resetNumException() { numException = 0; } void SetDataRhs(float, int); void SetDataJtj(float, int, int); diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.cpp b/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.cpp index a3513ddb..52506801 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.cpp +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.cpp @@ -3,12 +3,35 @@ #include "BkgFitOptim.h" #include "BkgFitStructures.h" +using namespace std; +#define DVAL(X) {X,#X} +std::map PartialDerivNames = { + DVAL(TBL_END),DVAL(TBL_END),DVAL(DFDP),DVAL(DFDR), + DVAL(DFDPDM),DVAL(DFDGAIN),DVAL(DFDA),DVAL(DFDDKR), + DVAL(DFDSIGMA),DVAL(DFDTSH),DVAL(DFDERR),DVAL(DFDT0), + DVAL(DFDTAUMR),DVAL(DFDTAUOR),DVAL(DFDRDR),DVAL(DFDKRATE), + DVAL(DFDSENS),DVAL(DFDD),DVAL(DFDPDR),DVAL(DFDMR),DVAL(DFDKMAX), + DVAL(DFDT0DLY),DVAL(DFDSMULT),DVAL(DFDTAUE),DVAL(YERR),DVAL(FVAL) + }; -//@TODO BAD CODE STYLE: function in header -void InitializeLevMarFitter(mat_table_build_instr *btbl,fit_instructions *instr, int flow_block_size) +std::string ComponentName( PartialDerivComponent comp ) +{ + std::string readable_name; + try { + readable_name = PartialDerivNames[comp]; + if(readable_name.substr(0,3)=="DFD") + readable_name=readable_name.substr(3); //cleanup the readable name by removing redundant part + } catch (const std::out_of_range&) { + readable_name = "UNKNOWN"; + } + return readable_name; +} + + +void InitializeLevMarFitter(const mat_table_build_instr *btbl,fit_instructions *instr, int flow_block_size) { int np; @@ -48,9 +71,6 @@ void InitializeLevMarFitter(mat_table_build_instr *btbl,fit_instructions *instr, // the output matrix gets one row for each indepdendent parameter struct delta_mat_output_line *ols = new struct delta_mat_output_line[np]; - // it's a good idea to clear these at the start - memset(ols,0,sizeof(struct delta_mat_output_line[np])); - // now build the input and output lines input_cnt = 0; for (int i=0;ioutput_len = np; } + + + void DumpBuildInstructionTable(mat_table_build_instr *tbl, int flow_block_size) { for (int i=0;true;i++) { std::string pcomp; - switch (tbl[i].comp) - { - case TBL_END: - pcomp="TBL_END "; - break; - case DFDR: - pcomp="DFDR "; - break; - case DFDA: - pcomp="DFDA "; - break; - case DFDT0: - pcomp="DFDT0 "; - break; - case DFDP: - pcomp="DFDP "; - break; - case DFDTSH: - pcomp="DFDTSH "; - break; - case DFDSIGMA: - pcomp="DFDSIGMA "; - break; - case DFDKRATE: - pcomp="DFDKRATE "; - break; - case DFDD: - pcomp="DFDD "; - break; - case DFDRDR: - pcomp="DFDRDR "; - break; - case DFDGAIN: - pcomp="DFDGAIN "; - break; - case YERR: - pcomp="YERR "; - break; - default: - pcomp = "UNKNOWN"; - } + pcomp = ComponentName( tbl[i].comp ); printf("%s % 4d [",pcomp.c_str(), tbl[i].array_index ); for (int j=0;j < flow_block_size-1;j++) @@ -170,14 +154,14 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ { // if there is a high-level fit descriptor, create a set of build instructions // from the high-level descriptor - if (fd != NULL) + if (fds.size()>0) { int row_cnt = 0; // first figure out how many entries the build instruction table will need - for (int i=0;fd[i].comp != TBL_END;i++) + for (int i=0;fds[i].comp != TBL_END;i++) { - switch (fd[i].ptype) + switch (fds[i].ptype) { case ParamTypeAFlows: case ParamTypeCFlows: @@ -222,28 +206,30 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ row_cnt = 0; // now create the rows - for (int i=0;fd[i].comp != TBL_END;i++) + for (int i=0;fds[i].comp != TBL_END;i++) { - switch (fd[i].ptype) + switch (fds[i].ptype) { case ParamTypeAllFlow: - mb[row_cnt].comp = fd[i].comp; - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].comp = fds[i].comp; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; for (int j=0;j < flow_block_size;j++) mb[row_cnt].SetAffectedFlow(j, 1); + mb[row_cnt].name = ComponentName(fds[i].comp); row_cnt++; break; case ParamTypeNotKey: // create an independent paramter per flow except for key flows for (int row=flow_key;row < flow_block_size;row++) { - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // individual parameters for each flow are assumed to be consecutive in the // BeadParams structure - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; mb[row_cnt].array_index = row - flow_key; + mb[row_cnt].name = ComponentName(fds[i].comp)+"_f"+std::to_string(row); // indicate which flow this specific parameter affects mb[row_cnt].SetAffectedFlow(row, 1); @@ -254,13 +240,14 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ // create an independent paramter per flow except for the first flow for (int row=1;row < flow_block_size;row++) { - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // individual parameters for each flow are assumed to be consecutive in the // BeadParams structure - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; mb[row_cnt].array_index = row - 1; + mb[row_cnt].name = ComponentName(fds[i].comp)+"_f"+std::to_string(row); // indicate which flow this specific parameter affects mb[row_cnt].SetAffectedFlow(row, 1); @@ -271,13 +258,14 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ // create an independent paramter per flow for (int row=0;row < flow_block_size;row++) { - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // individual parameters for each flow are assumed to be consecutive in the // bead_params structure - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; mb[row_cnt].array_index = row; + mb[row_cnt].name = ComponentName(fds[i].comp)+"_f"+std::to_string(row); // indicate which flow this specific parameter affects mb[row_cnt].SetAffectedFlow(row, 1); @@ -288,13 +276,14 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ // create an independent parameter per nucleotide for (int nuc=0;nuc < NUMNUC;nuc++) { - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // individual parameters for each nucleotide are assumed to be consecutive in the // BeadParams structure - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; mb[row_cnt].array_index = nuc; + mb[row_cnt].name = ComponentName(fds[i].comp)+"_n"+std::to_string(nuc); // indicate which flows this specific parameter affects for (int j=0;j < flow_block_size;j++) @@ -310,11 +299,12 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ break; case ParamTypeAFlows: // create one parameter for all flows of a single nucleotide (A) - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // the table entry should point to the specific parameter for this nucleotide - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; + mb[row_cnt].name = ComponentName(fds[i].comp); // indicate which flows this specific parameter affects for (int j=0;j < flow_block_size;j++) @@ -329,11 +319,12 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ break; case ParamTypeCFlows: // create one parameter for all flows of a single nucleotide (C) - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // the table entry should point to the specific parameter for this nucleotide - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; + mb[row_cnt].name = ComponentName(fds[i].comp); // indicate which flows this specific parameter affects for (int j=0;j < flow_block_size;j++) @@ -348,11 +339,12 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ break; case ParamTypeGFlows: // create one parameter for all flows of a single nucleotide (G) - mb[row_cnt].comp = fd[i].comp; + mb[row_cnt].comp = fds[i].comp; // the table entry should point to the specific parameter for this nucleotide - mb[row_cnt].bead_params_func = fd[i].bead_params_func; - mb[row_cnt].reg_params_func = fd[i].reg_params_func; + mb[row_cnt].bead_params_func = fds[i].bead_params_func; + mb[row_cnt].reg_params_func = fds[i].reg_params_func; + mb[row_cnt].name = ComponentName(fds[i].comp)+"_G"; // indicate which flows this specific parameter affects for (int j=0;j < flow_block_size;j++) @@ -379,60 +371,114 @@ void master_fit_type_entry::CreateBuildInstructions(const int *my_nuc, int flow_ InitializeLevMarFitter(mb,& (fi), flow_block_size); } - -master_fit_type_entry master_fit_type_table::base_bkg_model_fit_type[] = +void master_fit_type_table::set_base_bkg_model_fit_type() { -//{"name", &fit_descriptor, NULL, {NULL,0,NULL,0}}, - // individual well fits - {"FitWellAmpl", BkgFitStructures::fit_well_ampl_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitWellAmplBuffering", BkgFitStructures::fit_well_ampl_buffering_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitWellPostKey", BkgFitStructures::fit_well_post_key_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitWellAll", BkgFitStructures::fit_well_all_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitWellPostKeyNoDmult", BkgFitStructures::fit_well_post_key_descriptor_nodmult, NULL, {NULL,0,NULL,0}}, - - // region-wide fits - {"FitRegionTmidnucPlus", BkgFitStructures::fit_region_tmidnuc_plus_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionInit2", BkgFitStructures::fit_region_init2_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionInit2TauE", BkgFitStructures::fit_region_init2_taue_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionInit2TauENoRDR", BkgFitStructures::fit_region_init2_taue_NoRDR_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionFull", BkgFitStructures::fit_region_full_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionFullTauE", BkgFitStructures::fit_region_full_taue_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionFullTauENoRDR", BkgFitStructures::fit_region_full_taue_NoRDR_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionInit2NoRDR", BkgFitStructures::fit_region_init2_noRatioDrift_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionFullNoRDR", BkgFitStructures::fit_region_full_noRatioDrift_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionTimeVarying", BkgFitStructures::fit_region_time_varying_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionDarkness", BkgFitStructures::fit_region_darkness_descriptor, NULL, {NULL,0,NULL,0}}, - - //region-wide fits without diffusion - {"FitRegionInit2TauENoD", BkgFitStructures::fit_region_init2_taue_NoD_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionInit2TauENoRDRNoD",BkgFitStructures::fit_region_init2_taue_NoRDR_NoD_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionFullTauENoD", BkgFitStructures::fit_region_full_taue_NoD_descriptor, NULL, {NULL,0,NULL,0}}, - {"FitRegionFullTauENoRDRNoD", BkgFitStructures::fit_region_full_taue_NoRDR_NoD_descriptor, NULL, {NULL,0,NULL,0}}, - - { NULL, NULL, NULL, {NULL,0,NULL,0} }, // end of table -}; - - -fit_instructions *master_fit_type_table::GetFitInstructionsByName(const char *name) const + // set fit_type_hash_table + fit_type_hash_table = { // nested list-initialization + {"FitWellAmpl", {"wellAmpl","TableEnd"}}, + {"FitWellAmplBuffering", {"wellR","wellAmpl","TableEnd"}}, + {"FitWellPostKey", {"wellR","wellCopies","wellDmult","wellAmplPostKey","TableEnd"}}, + {"FitWellAll", {"wellR","wellCopies","wellDmult","wellAmpl","wellKmult","TableEnd"}}, + {"FitWellPostKeyNoDmult", {"wellR","wellCopies","wellAmplPostKey","TableEnd"}}, + // region-wide fits + {"FitRegionTmidnucPlus", {"R","Copies","Ampl","TMidNuc","TableEnd"}}, + {"FitRegionInit2", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","D","NucModifyRatio","TauRM","TauRO","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionInit2TauE", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","D","NucModifyRatio","TauE","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionInit2TauENoRDR",{"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","D","NucModifyRatio","TauE","CopyDrift","TableEnd"}}, + {"FitRegionFull", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","D","NucModifyRatio","TauRM","TauRO","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionFullTauE", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","D","NucModifyRatio","TauE","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionFullTauENoRDR", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","D","NucModifyRatio","TauE","CopyDrift","TableEnd"}}, + {"FitRegionInit2NoRDR", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","TMidNucDelay","SigmaMult","Krate","D","NucModifyRatio","TauRM","TauRO","CopyDrift","TableEnd"}}, + {"FitRegionFullNoRDR", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","TMidNucDelay","SigmaMult","Krate","D","NucModifyRatio","TauRM","TauRO","CopyDrift","TableEnd"}}, + {"FitRegionTimeVarying",{"TMidNuc","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionDarkness", {"Darkness","Ampl","TableEnd"}}, + //region-wide fits without diffusion + {"FitRegionInit2TauENoD", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","NucModifyRatio","TauE","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionInit2TauENoRDRNoD", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","NucModifyRatio","TauE","CopyDrift","TableEnd"}}, + {"FitRegionFullTauENoD", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","NucModifyRatio","TauE","RatioDrift","CopyDrift","TableEnd"}}, + {"FitRegionFullTauENoRDRNoD", {"R","Copies","Ampl","TShift","TMidNuc","Sigma","Krate","NucModifyRatio","TauE","CopyDrift","TableEnd"}}, + }; + + // set each entry for base_bkg_model_fit_type + for (auto p: fit_type_hash_table) + { + CreateBkgModelFitType(p.first, p.second); + base_bkg_model_fit_type[p.first].CreateBuildInstructions(_nucIdentity, max(_flowKey - _flowStart, 0 ), _flowBlockSize); + } +} + + +fit_descriptor master_fit_type_table::make_fit_param_entry(std::string param) { - for (int i=0;data[i].name != NULL;i++) - { - if (strcmp(data[i].name,name) == 0) - return &data[i].fi; + fit_descriptor fd; + if (param == "wellAmpl") fd = fit_descriptor({DFDA, & BeadParams::AccessAmpl, 0, ParamTypePerFlow }); + else if (param == "wellR") fd = fit_descriptor({DFDR, & BeadParams::AccessR, 0, ParamTypeAllFlow }); + else if (param == "wellCopies") fd = fit_descriptor({DFDP, & BeadParams::AccessCopies, 0, ParamTypeAllFlow }); + else if (param == "wellDmult") fd = fit_descriptor({DFDPDM, & BeadParams::AccessDmult, 0, ParamTypeAllFlow }); + else if (param == "wellKmult") fd = fit_descriptor({DFDDKR, & BeadParams::AccessKmult, 0, ParamTypePerFlow }); + else if (param == "wellAmplPostKey")fd = fit_descriptor({DFDA, & BeadParams::AccessAmplPostKey, 0, ParamTypeNotKey }); + + else if (param == "Ampl") fd = fit_descriptor({DFDA, 0, & reg_params::AccessAmpl, ParamTypePerFlow }); + else if (param == "R") fd = fit_descriptor({DFDR, 0, & reg_params::AccessR, ParamTypeAllFlow }); + else if (param == "Copies") fd = fit_descriptor({DFDP, 0, & reg_params::AccessCopies, ParamTypeAllFlow }); + else if (param == "D") fd = fit_descriptor({DFDD, 0, & reg_params::AccessD, ParamTypePerNuc }); + else if (param == "Darkness") fd = fit_descriptor({DFDERR, 0, & reg_params::AccessDarkness, ParamTypeAllFlow }); + else if (param == "TShift") fd = fit_descriptor({DFDTSH, 0, & reg_params::AccessTShift, ParamTypeAllFlow }); + else if (param == "TMidNuc") fd = fit_descriptor({DFDT0, 0, & reg_params::AccessTMidNuc, ParamTypeAllFlow }); + else if (param == "TMidNucDelay") fd = fit_descriptor({DFDT0DLY, 0, & reg_params::AccessTMidNucDelay,ParamTypePerNuc }); + else if (param == "Sigma") fd = fit_descriptor({DFDSIGMA, 0, & reg_params::AccessSigma, ParamTypeAllFlow }); + else if (param == "SigmaMult") fd = fit_descriptor({DFDSMULT, 0, & reg_params::AccessSigmaMult, ParamTypePerNuc }); + else if (param == "Krate") fd = fit_descriptor({DFDKRATE, 0, & reg_params::AccessKrate, ParamTypePerNuc }); + else if (param == "NucModifyRatio") fd = fit_descriptor({DFDMR, 0, & reg_params::AccessNucModifyRatio, ParamTypePerNuc }); + else if (param == "TauRM") fd = fit_descriptor({DFDTAUMR, 0, & reg_params::AccessTauRM, ParamTypeAllFlow }); + else if (param == "TauRO") fd = fit_descriptor({DFDTAUOR, 0, & reg_params::AccessTauRO, ParamTypeAllFlow }); + else if (param == "TauE") fd = fit_descriptor({DFDTAUE, 0, & reg_params::AccessTauE, ParamTypeAllFlow }); + else if (param == "RatioDrift") fd = fit_descriptor({DFDRDR, 0, & reg_params::AccessRatioDrift, ParamTypeAllFlow }); + else if (param == "CopyDrift") fd = fit_descriptor({DFDPDR, 0, & reg_params::AccessCopyDrift, ParamTypeAllFlow }); + else if (param == "TableEnd") fd = fit_descriptor({TBL_END, 0, 0, ParamTableEnd }); + else { + throw FitTypeException(param + ": Not a valid bead/reg param \n"); } - return NULL; + return fd; } -fit_descriptor *master_fit_type_table::GetFitDescriptorByName(const char *name) const +void master_fit_type_table::CreateBkgModelFitType(const string &fitName, const std::vector &fit_params) { + master_fit_type_entry mfte; + mfte.name = fitName.c_str(); + mfte.fds.resize(fit_params.size()); + for (size_t i=0; isecond.fi); + } + else { + pthread_mutex_unlock(&addFit); + throw FitTypeException(name + " :Not a valid fit type name\n"); } +} - return NULL; +const vector& master_fit_type_table::GetFitDescriptorByName(const string& name) +{ + pthread_mutex_lock(&addFit); + auto it = base_bkg_model_fit_type.find(name); + if (it != base_bkg_model_fit_type.end()) { + pthread_mutex_unlock(&addFit); + return it->second.fds; + } + else { + pthread_mutex_unlock(&addFit); + throw FitTypeException(name + " :Not a valid fit type name\n"); + } } @@ -446,38 +492,35 @@ master_fit_type_table::master_fit_type_table( int flow_start, int flow_key, int flow_block_size - ) + ) :_flowStart(flow_start), _flowKey(flow_key), _flowBlockSize(flow_block_size) { - int my_nuc_block[flow_block_size]; - tears.GetFlowOrderBlock( my_nuc_block, flow_start, flow_start + flow_block_size ); + _nucIdentity = new int[_flowBlockSize]; + tears.GetFlowOrderBlock(_nucIdentity, _flowStart, _flowStart + _flowBlockSize ); - // We want to copy the prototype table. - data = new master_fit_type_entry[ sizeof( base_bkg_model_fit_type ) / - sizeof( master_fit_type_entry ) ]; + // for thread safe adding of dynamic fit types during signal processing - // go through the master table of fit types and generate all the build - // instructions for each type of fitting we are going to do - for (int i=0 ; ; i++) - { - data[i] = base_bkg_model_fit_type[i]; + pthread_mutex_init(&addFit, NULL); - if ( base_bkg_model_fit_type[i].name == NULL ) break; + set_base_bkg_model_fit_type(); - data[i].CreateBuildInstructions(my_nuc_block, std::max( flow_key - flow_start, 0 ), flow_block_size); - } + // go through the master table of fit types and generate all the build + // instructions for each type of fitting we are going to do + + //for (auto it=base_bkg_model_fit_type.begin(); it != base_bkg_model_fit_type.end(); ++it) { + // it->second.CreateBuildInstructions(_nucIdentity, max(_flowKey - _flowStart, 0 ), _flowBlockSize); + //} } master_fit_type_table::~master_fit_type_table() { - for (int i=0;data[i].name != NULL;i++) - { - struct master_fit_type_entry *ft = &data[i]; + for (auto it=base_bkg_model_fit_type.begin(); it != base_bkg_model_fit_type.end(); ++it) { + master_fit_type_entry *ft = &(it->second); // make sure there is a high-level descriptor for this row // if there wasn't one, then the row might contain a hard link to // a statically allocated matrix build instruction which we don't // want to free - if (ft->fd != NULL) + if (ft->fds.size() > 0) { if (ft->mb != NULL) { @@ -496,8 +539,26 @@ master_fit_type_table::~master_fit_type_table() ft->fi.output = NULL; } - // Final cleanup. - delete [] data; + delete [] _nucIdentity; + pthread_mutex_destroy(&addFit); } +void master_fit_type_table::addBkgModelFitType( + const string& fitName, + const vector& paramNames) +{ + pthread_mutex_lock(&addFit); + try { + auto it = base_bkg_model_fit_type.find(fitName); + if (it == base_bkg_model_fit_type.end()) { + CreateBkgModelFitType(fitName, paramNames); + base_bkg_model_fit_type[fitName].CreateBuildInstructions(_nucIdentity, max(_flowKey - _flowStart, 0 ), _flowBlockSize); + } + } + catch(exception &ft) { + pthread_mutex_unlock(&addFit); + throw FitTypeException(ft.what()); + } + pthread_mutex_unlock(&addFit); +} diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.h b/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.h index 926a5fcd..89f11bd6 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.h +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitOptim.h @@ -4,12 +4,18 @@ #include #include +#include +#include +#include +#include +#include #include "BkgMagicDefines.h" #include "BkgFitMatrixPacker.h" #include "BeadParams.h" #include "RegionParams.h" #include "FlowDefaults.h" +using namespace std; typedef enum { @@ -25,6 +31,17 @@ typedef enum #define NOTNUCRISEPARAM ( float * ( nuc_rise_params::* )() )( 0 ) #define FIRSTINDEX 0 +class FitTypeException : public exception +{ + std::string msg; + + public: + FitTypeException(const std::string& s) : msg(s) {} + virtual ~FitTypeException() {} + + const char* what() const throw() { return msg.c_str(); } +}; + struct CpuStep { enum Length { @@ -66,7 +83,6 @@ class mat_table_build_instr mat_table_build_instr( const mat_table_build_instr & ); // Don't do this. mat_table_build_instr & operator=( const mat_table_build_instr & ); // Don't do this, either. - int * affected_flows; public: mat_table_build_instr() { @@ -76,13 +92,15 @@ class mat_table_build_instr bead_params_func = 0; reg_params_func = 0; } - ~mat_table_build_instr() { delete [] affected_flows; } + ~mat_table_build_instr() { if (affected_flows) delete [] affected_flows; } void realloc( int size ) { delete [] affected_flows; affected_flows = new int[size]; } + int * affected_flows; PartialDerivComponent comp; float * ( BeadParams::* bead_params_func )(); float * ( reg_params::* reg_params_func )(); int array_index; + std::string name; int GetAffectedFlow( int which ) const { return affected_flows[which]; } void SetAffectedFlow( int which, int value ) { affected_flows[which] = value; } @@ -115,17 +133,45 @@ struct fit_descriptor float * ( BeadParams::* bead_params_func )(); float * ( reg_params::* reg_params_func )(); ParameterSensitivityClassification ptype; + + fit_descriptor(PartialDerivComponent ncomp, float* (BeadParams::*bpf)(), float* (reg_params::*rpf)(), ParameterSensitivityClassification ntype) : comp(ncomp), bead_params_func(bpf), reg_params_func(rpf),ptype(ntype){} + + fit_descriptor() { + comp = TBL_END; + bead_params_func = NULL; + reg_params_func = NULL; + ptype = ParamTableEnd; + } + + fit_descriptor(const fit_descriptor& fd) { + comp = fd.comp; + bead_params_func = fd.bead_params_func; + reg_params_func = fd.reg_params_func; + ptype = fd.ptype; + } + + fit_descriptor& operator=(const fit_descriptor& fd) { + if (this != &fd) { + comp = fd.comp; + bead_params_func = fd.bead_params_func; + reg_params_func = fd.reg_params_func; + ptype = fd.ptype; + } + return *this; + } }; + struct master_fit_type_entry { // nice human-readable descriptive name for what the fit attempts to do - const char *name; + //const char *name; + std::string name; // high-level fit descriptor list. One entry in the list for each parameter to be // fit, along with a classification of the parameter that indicates whether it's one-per-flow // or one-per-nuc, etc.,... This high level description is used to build the // mat_table_build_instr table. - struct fit_descriptor *fd; + std::vector fds; // mid-level matrix build instructions. This intermediate level table contains multiple entries // for some parameters. (i.e., the Ampl parameter, which is independent per flow is broken out // in this table to one entry per flow, whereas it was a single line in the fit_descriptor....) @@ -141,19 +187,30 @@ struct master_fit_type_entry fit_instructions fi; void CreateBuildInstructions(const int *my_nuc, int flow_key, int flow_block_size); + + master_fit_type_entry(){ + mb = NULL; + } + + master_fit_type_entry(const master_fit_type_entry& mfte) : name(mfte.name),fds(mfte.fds),mb(mfte.mb),fi(mfte.fi){ + } + }; class master_fit_type_table { - // All of the table entries. - master_fit_type_entry *data; + int _flowStart; + int _flowKey; + int _flowBlockSize; + int *_nucIdentity; + std::map base_bkg_model_fit_type; + std::map > fit_type_hash_table; + pthread_mutex_t addFit; // No copying or assignment. master_fit_type_table( const master_fit_type_table & ); master_fit_type_table & operator=( const master_fit_type_table & ); - // The base data that we start with when making our array. - static master_fit_type_entry base_bkg_model_fit_type[]; public: // TODO: PartialDeriv 'affected flows' has to be munged for tango flow order!!! */ @@ -164,13 +221,18 @@ class master_fit_type_table // Cleanup! (Used to be CleanupLevMarSparseMatrices()). ~master_fit_type_table(); - fit_instructions *GetFitInstructionsByName(const char *name) const; - fit_descriptor *GetFitDescriptorByName(const char* name) const; -}; + fit_instructions *GetFitInstructionsByName(const std::string& name); + const std::vector& GetFitDescriptorByName(const std::string& name); + void addBkgModelFitType(const std::string &fitName, const std::vector &fit_params); +private: + void set_base_bkg_model_fit_type(); + fit_descriptor make_fit_param_entry(std::string param); + void CreateBkgModelFitType(const std::string &fitName, const std::vector &fit_params); +}; -void InitializeLevMarFitter(mat_table_build_instr *btbl,fit_instructions *instr, int flow_block_size); +void InitializeLevMarFitter(const mat_table_build_instr *btbl,fit_instructions *instr, int flow_block_size); void DumpBuildInstructionTable(mat_table_build_instr *tbl, int flow_block_size); #endif // BKGFITOPTIM_H diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.cpp b/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.cpp index 159167b0..7a2b85af 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.cpp +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.cpp @@ -1,5 +1,8 @@ /* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ +#include +#include +#include #include "BkgFitStructures.h" using namespace std; @@ -53,7 +56,7 @@ CpuStep BkgFitStructures::Steps[] = int BkgFitStructures::NumSteps = sizeof(Steps) /sizeof(Steps[FIRSTINDEX]); - +/* fit_descriptor BkgFitStructures::fit_well_ampl_descriptor[] = { //{PartialDerivComponent, bead_params_func, reg_params_func, ParameterSensitivityClassification} @@ -454,48 +457,39 @@ fit_descriptor BkgFitStructures::fit_region_full_taue_NoRDR_NoD_descriptor[] = {TBL_END, 0, 0, ParamTableEnd }, }; -int BkgFitStructures::GetNumParamsToFitForDescriptor(fit_descriptor *fd, int flow_key, int flow_block_size) +master_fit_type_entry master_fit_type_table::base_bkg_model_fit_type[] = { - int numParamsToFit = 0; - for (int i=0; fd[i].comp != TBL_END; ++i) - { - switch (fd[i].ptype) - { - case ParamTypeAFlows: - case ParamTypeCFlows: - case ParamTypeGFlows: - case ParamTypeAllFlow: - numParamsToFit++; - break; - case ParamTypeNotKey: - numParamsToFit += max( 0, flow_block_size-flow_key ); - break; - case ParamTypePerFlow: - numParamsToFit += flow_block_size; - break; - case ParamTypePerNuc: - numParamsToFit += NUMNUC; - break; - case ParamTypeAllButFlow0: - numParamsToFit += flow_block_size-1; - break; - default: - break; - } - } - return numParamsToFit; -} +//{"name", &fit_descriptor, NULL, {NULL,0,NULL,0}}, + // individual well fits + {"FitWellAmpl", BkgFitStructures::fit_well_ampl_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitWellAmplBuffering", BkgFitStructures::fit_well_ampl_buffering_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitWellPostKey", BkgFitStructures::fit_well_post_key_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitWellPostKeyNoDmult", BkgFitStructures::fit_well_post_key_descriptor_nodmult, NULL, {NULL,0,NULL,0}}, + + // region-wide fits + {"FitRegionTmidnucPlus", BkgFitStructures::fit_region_tmidnuc_plus_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionInit2", BkgFitStructures::fit_region_init2_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionInit2TauE", BkgFitStructures::fit_region_init2_taue_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionInit2TauENoRDR", BkgFitStructures::fit_region_init2_taue_NoRDR_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionFull", BkgFitStructures::fit_region_full_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionFullTauE", BkgFitStructures::fit_region_full_taue_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionFullTauENoRDR", BkgFitStructures::fit_region_full_taue_NoRDR_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionInit2NoRDR", BkgFitStructures::fit_region_init2_noRatioDrift_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionFullNoRDR", BkgFitStructures::fit_region_full_noRatioDrift_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionTimeVarying", BkgFitStructures::fit_region_time_varying_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionDarkness", BkgFitStructures::fit_region_darkness_descriptor, NULL, {NULL,0,NULL,0}}, + + //region-wide fits without diffusion + {"FitRegionInit2TauENoD", BkgFitStructures::fit_region_init2_taue_NoD_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionInit2TauENoRDRNoD",BkgFitStructures::fit_region_init2_taue_NoRDR_NoD_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionFullTauENoD", BkgFitStructures::fit_region_full_taue_NoD_descriptor, NULL, {NULL,0,NULL,0}}, + {"FitRegionFullTauENoRDRNoD", BkgFitStructures::fit_region_full_taue_NoRDR_NoD_descriptor, NULL, {NULL,0,NULL,0}}, + + { NULL, NULL, NULL, {NULL,0,NULL,0} }, // end of table +}; +*/ -int BkgFitStructures::GetNumParDerivStepsForFitDescriptor(fit_descriptor* fd) { - int numParDerivSteps = 0; - for (int i=0; fd[i].comp != TBL_END; ++i) - { - numParDerivSteps++; - } - return numParDerivSteps; -} -//#define NUMERIC_PartialDeriv_CALC void BuildMatrix(BkgFitMatrixPacker *fit,bool accum, bool debug) { #if 1 @@ -535,4 +529,50 @@ void BuildMatrix(BkgFitMatrixPacker *fit,bool accum, bool debug) #endif } +int BkgFitStructures::GetNumParamsToFitForDescriptor( + const std::vector& fds, + int flow_key, + int flow_block_size) +{ + int numParamsToFit = 0; + if (fds.size() > 0) { + for (int i=0; fds[i].comp != TBL_END; ++i) + { + switch (fds[i].ptype) + { + case ParamTypeAFlows: + case ParamTypeCFlows: + case ParamTypeGFlows: + case ParamTypeAllFlow: + numParamsToFit++; + break; + case ParamTypeNotKey: + numParamsToFit += max( 0, flow_block_size-flow_key ); + break; + case ParamTypePerFlow: + numParamsToFit += flow_block_size; + break; + case ParamTypePerNuc: + numParamsToFit += NUMNUC; + break; + case ParamTypeAllButFlow0: + numParamsToFit += flow_block_size-1; + break; + default: + break; + } + } + } + return numParamsToFit; +} +int BkgFitStructures::GetNumParDerivStepsForFitDescriptor(const std::vector& fds) { + int numParDerivSteps = 0; + if (fds.size() > 0) { + for (int i=0; fds[i].comp != TBL_END; ++i) + { + numParDerivSteps++; + } + } + return numParDerivSteps; +} diff --git a/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.h b/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.h index f8583e91..a78bbc9b 100644 --- a/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.h +++ b/Analysis/BkgModel/Fitters/Complex/BkgFitStructures.h @@ -12,6 +12,7 @@ class BkgFitStructures{ static CpuStep Steps[]; static int NumSteps; + /* static fit_descriptor fit_well_ampl_descriptor[]; static fit_descriptor fit_well_ampl_buffering_descriptor[]; static fit_descriptor fit_well_post_key_descriptor[]; @@ -39,8 +40,35 @@ class BkgFitStructures{ static fit_descriptor fit_region_full_taue_NoD_descriptor[]; static fit_descriptor fit_region_full_taue_NoRDR_NoD_descriptor[]; - static int GetNumParamsToFitForDescriptor(fit_descriptor *fd, int flow_key, int flow_block_size); - static int GetNumParDerivStepsForFitDescriptor(fit_descriptor* fd); + std::vector fit_well_ampl_descriptor; + std::vector fit_well_ampl_buffering_descriptor; + std::vector fit_well_post_key_descriptor; + std::vector fit_well_post_key_descriptor_nodmult; + + // region + std::vector fit_region_tmidnuc_plus_descriptor; + + std::vector fit_region_init2_descriptor; + std::vector fit_region_init2_taue_descriptor; + std::vector fit_region_init2_noRatioDrift_descriptor; + std::vector fit_region_init2_taue_NoRDR_descriptor; + + std::vector fit_region_full_descriptor; + std::vector fit_region_full_taue_descriptor; + std::vector fit_region_full_noRatioDrift_descriptor; + std::vector fit_region_full_taue_NoRDR_descriptor; + + std::vector fit_region_time_varying_descriptor; + std::vector fit_region_darkness_descriptor; + + std::vector fit_region_init2_taue_NoD_descriptor; + std::vector fit_region_init2_taue_NoRDR_NoD_descriptor; + std::vector fit_region_full_taue_NoD_descriptor; + std::vector fit_region_full_taue_NoRDR_NoD_descriptor; + */ + + static int GetNumParamsToFitForDescriptor(const std::vector& fds, int flow_key, int flow_block_size); + static int GetNumParDerivStepsForFitDescriptor(const std::vector& fds); }; diff --git a/Analysis/BkgModel/Fitters/Complex/FitControl.cpp b/Analysis/BkgModel/Fitters/Complex/FitControl.cpp index 22ae3466..259cee98 100644 --- a/Analysis/BkgModel/Fitters/Complex/FitControl.cpp +++ b/Analysis/BkgModel/Fitters/Complex/FitControl.cpp @@ -9,11 +9,12 @@ FitControl_t::FitControl_t( master_fit_type_table *table ) : { PartialDeriv_comp_list = NULL; // well - NullWellPackers(); + //NullWellPackers(); // region - NullRegionPackers(); + //NullRegionPackers(); } +/* void FitControl_t::NullWellPackers() { FitWellAmpl = NULL; @@ -22,8 +23,8 @@ void FitControl_t::NullWellPackers() DontFitWells = NULL; FitWellAll = NULL; } - -void FitControl_t::NullRegionPackers() +*/ +/*void FitControl_t::NullRegionPackers() { FitRegionTmidnucPlus= NULL; FitRegionInit2= NULL; @@ -54,13 +55,18 @@ void FitControl_t::DeleteRegionPackers() if ( DontFitRegion !=NULL ) delete DontFitRegion; // should never happen!!! NullRegionPackers(); } +*/ void FitControl_t::Delete() { - DeleteWellPackers(); + //DeleteWellPackers(); - DeleteRegionPackers(); + //DeleteRegionPackers(); + for(auto it = fittingMatrices.begin(); it!= fittingMatrices.end(); ++it) { + delete it->second; + } + fittingMatrices.clear(); if ( PartialDeriv_comp_list != NULL ) delete [] PartialDeriv_comp_list; } @@ -72,24 +78,30 @@ FitControl_t::~FitControl_t() void FitControl_t::AllocWellPackers ( int hydrogenModelType, int npts, int flow_block_size ) { - DeleteWellPackers(); + //DeleteWellPackers(); - int comp_list_len = BkgFitStructures::NumSteps; + //int comp_list_len = BkgFitStructures::NumSteps; + + AddFitPacker("FitWellAmpl", npts, flow_block_size); + //FitWellAmpl = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellAmpl" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitWellAmplBuffering = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellAmplBuffering" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitWellAmplBuffering", npts, flow_block_size); - FitWellAmpl = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellAmpl" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitWellAmplBuffering = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellAmplBuffering" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); switch( hydrogenModelType ){ case 0: default: - FitWellPostKey = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellPostKey" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitWellPostKey = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellPostKey" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitWellPostKey", npts, flow_block_size); break; case 1: case 2: case 3: - FitWellPostKey = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellPostKeyNoDmult" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitWellPostKey = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellPostKeyNoDmult" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitWellPostKeyNoDmult", npts, flow_block_size); break; } - FitWellAll = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellAll" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitWellAll = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitWellAll" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitWellAll", npts, flow_block_size); } @@ -103,26 +115,34 @@ void FitControl_t::CombinatorialAllocationOfInit2AndFull ( bool no_rdr_fit_start if ( no_rdr_fit_starting_block & ( !fitting_taue ) ) { - FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2NoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullNoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2NoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionInit2NoRDR",npts,flow_block_size); + //FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullNoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionFullNoRDR",npts,flow_block_size); } if ( ( !no_rdr_fit_starting_block ) & ( !fitting_taue ) ) { - FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFull" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionInit2",npts,flow_block_size); + //FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFull" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionFull",npts,flow_block_size); } if ( fitting_taue & ( !no_rdr_fit_starting_block ) ) { - FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauE" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauE" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauE" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionInit2TauE",npts,flow_block_size); + //FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauE" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionFullTauE",npts,flow_block_size); } if ( fitting_taue & ( no_rdr_fit_starting_block ) ) { - FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauENoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauENoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauENoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionInit2TauENoRDR",npts,flow_block_size); + //FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauENoRDR" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionFullTauENoRDR",npts,flow_block_size); } break; @@ -131,14 +151,18 @@ void FitControl_t::CombinatorialAllocationOfInit2AndFull ( bool no_rdr_fit_start case 3: if ( fitting_taue & ( !no_rdr_fit_starting_block ) ) { - FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauENoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauENoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauENoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionInit2TauENoD",npts,flow_block_size); + //FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauENoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionFullTauENoD",npts,flow_block_size); } if ( fitting_taue & ( no_rdr_fit_starting_block ) ) { - FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauENoRDRNoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauENoRDRNoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionInit2 = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionInit2TauENoRDRNoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionInit2TauENoRDRNoD",npts,flow_block_size); + //FitRegionFull = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionFullTauENoRDRNoD" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionFullTauENoRDRNoD",npts,flow_block_size); } break; } @@ -150,13 +174,16 @@ void FitControl_t::AllocRegionPackers ( bool no_rdr_fit_starting_block,bool fitt { int comp_list_len = BkgFitStructures::NumSteps; - DeleteRegionPackers(); - FitRegionTmidnucPlus = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionTmidnucPlus" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //DeleteRegionPackers(); + //FitRegionTmidnucPlus = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionTmidnucPlus" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionTmidnucPlus",npts,flow_block_size); CombinatorialAllocationOfInit2AndFull ( no_rdr_fit_starting_block,fitting_taue,hydrogenModelType,npts, flow_block_size ); - FitRegionTimeVarying = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionTimeVarying" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); - FitRegionDarkness = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionDarkness" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + //FitRegionTimeVarying = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionTimeVarying" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionTimeVarying",npts,flow_block_size); + //FitRegionDarkness = new BkgFitMatrixPacker ( npts,*bkg_model_fit_type->GetFitInstructionsByName ( "FitRegionDarkness" ),PartialDeriv_comp_list,comp_list_len, flow_block_size ); + AddFitPacker("FitRegionDarkness",npts,flow_block_size); } void FitControl_t::AllocPackers (float *tfptr, bool no_rdr_fit_starting_block, bool fitting_taue, int hydrogenModelType, int bead_flow_t, int npts, int flow_block_size ) @@ -173,3 +200,28 @@ void FitControl_t::AllocPackers (float *tfptr, bool no_rdr_fit_starting_block, b AllocWellPackers ( hydrogenModelType, npts, flow_block_size ); AllocRegionPackers ( no_rdr_fit_starting_block, fitting_taue, hydrogenModelType, npts , flow_block_size); } + +bool FitControl_t::AddFitPacker( + const char* fitName, + int numFrames, + int flow_block_size) +{ + BkgFitMatrixPacker* packer = new BkgFitMatrixPacker (numFrames,*bkg_model_fit_type->GetFitInstructionsByName (fitName),PartialDeriv_comp_list,BkgFitStructures::NumSteps, flow_block_size); + auto it = fittingMatrices.find(fitName); + if (it != fittingMatrices.end()) { + return false; + } + else { + fittingMatrices[fitName] = packer; + } + + return true; +} + +BkgFitMatrixPacker* FitControl_t::GetFitPacker(const string& fitName) const { + auto it = fittingMatrices.find(fitName); + if (it != fittingMatrices.end()) + return it->second; + else + return NULL; +} diff --git a/Analysis/BkgModel/Fitters/Complex/FitControl.h b/Analysis/BkgModel/Fitters/Complex/FitControl.h index d62ba3de..79ecce49 100644 --- a/Analysis/BkgModel/Fitters/Complex/FitControl.h +++ b/Analysis/BkgModel/Fitters/Complex/FitControl.h @@ -2,46 +2,30 @@ #ifndef FITCONTROL_H #define FITCONTROL_H +#include +#include #include "BkgFitStructures.h" +using namespace std; -// this should be a map rather than individual pointers -// it is 2011 and the STL has been in existencde for some time class FitControl_t { - master_fit_type_table* bkg_model_fit_type; - public: - // helper class to manipulate PartialDerivriv terms and solve the non-linear - PartialDeriv_comp_list_item *PartialDeriv_comp_list; - // regression calculation - BkgFitMatrixPacker *FitWellAmpl; - BkgFitMatrixPacker *FitWellAmplBuffering; - BkgFitMatrixPacker *FitWellPostKey; - BkgFitMatrixPacker *FitWellAll; // includes kmult - BkgFitMatrixPacker *DontFitWells; - - // used for fitting tau, NucModifyRatio and oR to the whole region, not just individual wells - BkgFitMatrixPacker *FitRegionTmidnucPlus; - BkgFitMatrixPacker *FitRegionInit2; - BkgFitMatrixPacker *FitRegionFull; - BkgFitMatrixPacker *FitRegionDarkness; - BkgFitMatrixPacker *FitRegionTimeVarying; - BkgFitMatrixPacker *DontFitRegion; + master_fit_type_table* bkg_model_fit_type; + map fittingMatrices; + PartialDeriv_comp_list_item *PartialDeriv_comp_list; + public: FitControl_t( master_fit_type_table *table ); - void Delete(); - void DeleteWellPackers(); - void DeleteRegionPackers(); - void NullWellPackers(); - void NullRegionPackers(); ~FitControl_t(); + void AllocPackers(float *tfptr, bool no_rdr_fit_starting_block,bool fitting_taue, int hydrogenModelType, int bead_flow_t, int npts, int flow_block_size); + bool AddFitPacker(const char* fitName, int numFrames, int flow_block_size); + BkgFitMatrixPacker* GetFitPacker(const string& fitName) const; + + private: void AllocWellPackers(int hydrogenModelType, int npts, int flow_block_size); void AllocRegionPackers(bool no_rdr_fit_starting_block, bool fitting_taue, int hydrogenModelType, int npts, int flow_block_size); void CombinatorialAllocationOfInit2AndFull(bool no_rdr_fit_starting_block, bool fitting_taue, int hydrogenModelType, int npts, int flow_block_size); - void AllocPackers(float *tfptr, bool no_rdr_fit_starting_block,bool fitting_taue, int hydrogenModelType, int bead_flow_t, int npts, int flow_block_size); + void Delete(); }; - - - #endif // FITCONTROL_H diff --git a/Analysis/BkgModel/Fitters/Complex/MultiLevMar.cpp b/Analysis/BkgModel/Fitters/Complex/MultiLevMar.cpp index f6efd9a8..c4419b1f 100644 --- a/Analysis/BkgModel/Fitters/Complex/MultiLevMar.cpp +++ b/Analysis/BkgModel/Fitters/Complex/MultiLevMar.cpp @@ -2,6 +2,7 @@ #include "MultiLevMar.h" #include "MiscVec.h" #include +#include "BkgFitMatDat.h" MultiFlowLevMar::MultiFlowLevMar ( SignalProcessingMasterFitter &_bkg, int flow_block_size, master_fit_type_table *table ) : @@ -66,7 +67,8 @@ int MultiFlowLevMar::MultiFlowSpecializedSampledLevMarFitParameters ( int additi { for ( int loc_iter=0 ; ( loc_iter< number_region_iterations_wanted ) & do_just_region; loc_iter++ ) { - DoSampledRegionIteration ( reg_fit,total_iter, flow_key, flow_block_size, flow_block_start ); + int nbeads = DoSampledRegionIteration ( reg_fit,total_iter, flow_key, flow_block_size, flow_block_start ); + IF_OPTIMIZER_DEBUG(bkg.inception_state, bkg.debugSaver.WriteData(reg_fit, bkg.region_data->my_regions.rp ,flow_block_start, bkg.GetRegion(),reg_fit->compNames, nbeads)); total_iter++; } } @@ -90,7 +92,8 @@ int MultiFlowLevMar::MultiFlowSpecializedSampledLevMarFitParameters ( int additi // do one region iteration if ( !skip_region ) { - DoSampledRegionIteration ( reg_fit,total_iter, flow_key, flow_block_size, flow_block_start ); + int nbeads = DoSampledRegionIteration ( reg_fit,total_iter, flow_key, flow_block_size, flow_block_start ); + IF_OPTIMIZER_DEBUG(bkg.inception_state, bkg.debugSaver.WriteData(reg_fit, bkg.region_data->my_regions.rp ,flow_block_start, bkg.GetRegion(),reg_fit->compNames, nbeads)); total_iter++; } } @@ -129,7 +132,8 @@ int MultiFlowLevMar::MultiFlowSpecializedLevMarFitParameters ( int additional_be { for ( int loc_iter=0 ; loc_itermy_regions.rp ,flow_block_start, bkg.GetRegion(),reg_fit->compNames, nbeads)); total_iter++; } } @@ -150,7 +154,8 @@ int MultiFlowLevMar::MultiFlowSpecializedLevMarFitParameters ( int additional_be // do one region iteration if ( !skip_region ) { - DoRegionIteration ( reg_fit, total_iter, flow_key, flow_block_size, flow_block_start ); + int nbeads = DoRegionIteration ( reg_fit, total_iter, flow_key, flow_block_size, flow_block_start ); + IF_OPTIMIZER_DEBUG(bkg.inception_state, bkg.debugSaver.WriteData(reg_fit, bkg.region_data->my_regions.rp ,flow_block_start, bkg.GetRegion(),reg_fit->compNames, nbeads)); total_iter++; } } @@ -186,7 +191,8 @@ int MultiFlowLevMar::MultiFlowSpecializedLevMarFitParametersOnlyRegion ( { for ( int loc_iter=0 ; loc_itermy_regions.rp ,flow_block_start, bkg.GetRegion(),reg_fit->compNames, nbeads)); total_iter++; } } @@ -212,7 +218,7 @@ void MultiFlowLevMar::MultiFlowSpecializedLevMarFitAllWells ( int bead_only_iter } ///-----------------------------------done with entry points -void MultiFlowLevMar::DoSampledRegionIteration ( +int MultiFlowLevMar::DoSampledRegionIteration ( BkgFitMatrixPacker *reg_fit, int iter, int flow_key, int flow_block_size, int flow_block_start ) { @@ -231,9 +237,10 @@ void MultiFlowLevMar::DoSampledRegionIteration ( printf("DoSampledRegionIteration: %d beads less than minimum %d required in region(col=%d,row=%d)\n",reg_wells, lm_state.min_bead_to_fit_region, bkg.region_data->region->col, bkg.region_data->region->row); } IdentifyParametersFromSample ( bkg.region_data->my_beads,bkg.region_data->my_regions, lm_state.well_mask, lm_state.reg_mask, lm_state , flow_block_size); + return reg_wells; } -void MultiFlowLevMar::DoRegionIteration ( +int MultiFlowLevMar::DoRegionIteration ( BkgFitMatrixPacker *reg_fit, int iter, int flow_key, int flow_block_size, int flow_block_start ) { @@ -257,6 +264,7 @@ void MultiFlowLevMar::DoRegionIteration ( lm_state.IncrementRegionGroup(); // better try another bead set if this one doesn't work! } IdentifyParameters ( bkg.region_data->my_beads,bkg.region_data->my_regions, *bkg.region_data_extras.my_flow, flow_block_size, lm_state.well_mask, lm_state.reg_mask,lm_state.skip_beads ); + return reg_wells; } diff --git a/Analysis/BkgModel/Fitters/Complex/MultiLevMar.h b/Analysis/BkgModel/Fitters/Complex/MultiLevMar.h index 9c096cb8..51a0161e 100644 --- a/Analysis/BkgModel/Fitters/Complex/MultiLevMar.h +++ b/Analysis/BkgModel/Fitters/Complex/MultiLevMar.h @@ -75,10 +75,10 @@ class MultiFlowLevMar bool isSample, int flow_key, int flow_block_size, int flow_block_start ); - void DoSampledRegionIteration ( BkgFitMatrixPacker *reg_fit, + int DoSampledRegionIteration ( BkgFitMatrixPacker *reg_fit, int iter, int flow_key, int flow_block_size, int flow_block_start ); - void DoRegionIteration ( BkgFitMatrixPacker *reg_fit, + int DoRegionIteration( BkgFitMatrixPacker *reg_fit, int iter, int flow_key, int flow_block_size, int flow_block_start ); bool DoSampledBeadIteration ( bool well_only_fit, diff --git a/Analysis/LevMarFitterV2.cpp b/Analysis/BkgModel/Fitters/LevMarFitterV2.cpp similarity index 100% rename from Analysis/LevMarFitterV2.cpp rename to Analysis/BkgModel/Fitters/LevMarFitterV2.cpp diff --git a/Analysis/LevMarFitterV2.h b/Analysis/BkgModel/Fitters/LevMarFitterV2.h similarity index 100% rename from Analysis/LevMarFitterV2.h rename to Analysis/BkgModel/Fitters/LevMarFitterV2.h diff --git a/Analysis/BkgModel/Fitters/SpatialCorrelator.cpp b/Analysis/BkgModel/Fitters/SpatialCorrelator.cpp index d3626e10..268826b5 100644 --- a/Analysis/BkgModel/Fitters/SpatialCorrelator.cpp +++ b/Analysis/BkgModel/Fitters/SpatialCorrelator.cpp @@ -11,7 +11,7 @@ #include "SignalProcessingMasterFitter.h" #include "RawWells.h" #include "MathOptim.h" -#include "mixed.h" +//#include "ClonalFilter/mixed.h" #include "BkgDataPointers.h" diff --git a/Analysis/BkgModel/GlobalDefaultsForBkgModel.cpp b/Analysis/BkgModel/GlobalDefaultsForBkgModel.cpp index 5db2c96c..524c008f 100644 --- a/Analysis/BkgModel/GlobalDefaultsForBkgModel.cpp +++ b/Analysis/BkgModel/GlobalDefaultsForBkgModel.cpp @@ -207,9 +207,8 @@ void GlobalDefaultsForBkgModel::GoptDefaultsFromJson(char *fname){ } - // Load optimized defaults from GeneticOptimizer runs -void GlobalDefaultsForBkgModel::SetGoptDefaults ( char *fname ) +void GlobalDefaultsForBkgModel::SetGoptDefaults ( char *fname, char *results_folder ) { if(fname == NULL) return; @@ -222,13 +221,17 @@ void GlobalDefaultsForBkgModel::SetGoptDefaults ( char *fname ) if(isJson){ GoptDefaultsFromJson(fname); } else{ - printf("Abort: %s not a json file", fname); exit(1); - } - region_param_start.BadIdeaComputeDerivedInput(); + DumpExcitingParameters("default"); + region_param_start.BadIdeaComputeDerivedInput(); + // emphasis_vector.txt file used in gopt to change the default gopt params + //bool modified = ReadEmphasisVectorFromFile (results_folder); //GeneticOptimizer run - load its vector + bool modified = ReadEmphasisVectorFromJson (results_folder); //GeneticOptimizer run - load its vector + if (modified) + DumpExcitingParameters("modified"); } void GlobalDefaultsForBkgModel::DumpExcitingParameters(const char *fun_string) @@ -237,7 +240,7 @@ void GlobalDefaultsForBkgModel::DumpExcitingParameters(const char *fun_string) printf ( "%s parameters used: \n",fun_string ); region_param_start.DumpPoorlyStructuredText(); - data_control.DumpPoorlyStructuredText(); + data_control.emphasis_params.DumpPoorlyStructuredText(); fitter_defaults.DumpPoorlyStructuredText(); printf ( "\n" ); @@ -275,7 +278,7 @@ void GlobalDefaultsForBkgModel::SetOpts(OptArgs &opts, Json::Value& json_params) char *tmp_config_file = NULL; tmp_config_file = GetIonConfigFile (filename.c_str()); - SetGoptDefaults(tmp_config_file); + SetGoptDefaults(tmp_config_file,(char*)(results_folder.c_str())); if (tmp_config_file) free(tmp_config_file); } @@ -295,21 +298,20 @@ void GlobalDefaultsForBkgModel::SetOpts(OptArgs &opts, Json::Value& json_params) tmp_config_file = GetIonConfigFile (filename.c_str()); } - SetGoptDefaults(tmp_config_file); + SetGoptDefaults(tmp_config_file,(char*)(results_folder.c_str())); if (tmp_config_file) free(tmp_config_file); - } else { - SetGoptDefaults ((char*)(gopt.c_str())); //parameter file provided cmd-line - + SetGoptDefaults ((char*)(gopt.c_str()),(char*)(results_folder.c_str())); //parameter file provided cmd-line + //ReadEmphasisVectorFromFile ((char*)(results_folder.c_str())); //GeneticOptimizer run - load its vector } } signal_process_control.SetOpts(opts, json_params); bool dont_emphasize_by_compression = RetrieveParameterBool(opts, json_params, '-', "bkg-dont-emphasize-by-compression", false); - data_control.point_emphasis_by_compression = (!dont_emphasize_by_compression); + data_control.emphasis_params.point_emphasis_by_compression = (!dont_emphasize_by_compression); // from SetupXtalkParametersForBkgModel string trace_xtalk_name = RetrieveParameterString(opts, json_params, '-', "xtalk", "disable"); @@ -380,3 +382,199 @@ void GlobalDefaultsForBkgModel::SetOpts(OptArgs &opts, Json::Value& json_params) // nothing: no barcodes, don't do anything } } + + +// This function is used during GeneticOptimizer runs in which case the above SetGoptDefaults is disabled + +bool GlobalDefaultsForBkgModel::ReadEmphasisVectorFromJson ( char *experimentName ) +{ + char fname[512]; + sprintf ( fname,"%s/gopt.delta.json", experimentName ); + + struct stat fstatus; + int status = stat ( fname,&fstatus ); + bool modified = false; + + if ( status == 0 ) // file exists + { + Json::Value all_params; + std::ifstream in(fname, std::ios::in); + + if (!in.good()) { + printf("Opening gopt delta file %s unsuccessful. Aborting\n", fname); + exit(1); + } else { + modified = true; + printf ( "loading emphasis vector parameters from %s\n",fname ); + } + in >> all_params; + in.close(); + + // map param to index + const static std::map string_to_case{ + {"km_const[0]",1}, + {"km_const[1]",2}, + {"km_const[2]",3}, + {"km_const[3]",4}, + {"krate[0]",6}, + {"krate[1]",7}, + {"krate[2]",8}, + {"krate[3]",9}, + {"d_coeff[0]",11}, + {"d_coeff[1]",12}, + {"d_coeff[2]",13}, + {"d_coeff[3]",14}, + {"sigma_mult[0]",16}, + {"sigma_mult[1]",17}, + {"sigma_mult[2]",18}, + {"sigma_mult[3]",19}, + {"t_mid_nuc_delay[0]",21}, + {"t_mid_nuc_delay[1]",22}, + {"t_mid_nuc_delay[2]",23}, + {"t_mid_nuc_delay[3]",24}, + {"sens",25}, + {"tau_R_m",26}, + {"tau_R_o",27}, + {"emphasis[0]",28}, + {"emphasis[1]",29}, + {"emphasis[2]",30}, + {"emphasis[3]",31}, + {"emphasis[4]",32}, + {"emphasis[5]",33}, + {"emphasis[6]",34}, + {"emphasis[7]",35}, + {"emp_amplitude",36}, + {"emp_width",37}, + {"clonal_call_scale[0]",38}, + {"clonal_call_scale[1]",39}, + {"clonal_call_scale[2]",40}, + {"clonal_call_scale[3]",41}, + {"clonal_call_scale[4]",42}, + {"min_tauB",43}, + {"max_tauB",44}, + {"mid_tauB",45}, + {"tauB_smooth_range",46} + }; + + // Loop over each element in the object. + for(auto iter = all_params.begin(); iter != all_params.end(); ++iter) + { + // Make sure to get the value as const reference otherwise you will end up copying + // the whole JSON value recursively which can be expensive if it is a nested object. + //const Json::Value &str = iter->first; + //const Json::Value &v = iter->second; + + // Perform actions here to process each string and value in the JSON object... + string key = iter.key().asString(); + cout << "ReadEmphasisVectorFromJson... " << key << ":" << all_params[key] << endl; + float v = all_params[key].asFloat(); + int iCase = string_to_case.count(key) ? string_to_case.at(key) : 0; + set_GoptParameter_byIndex(iCase,v); + } + } + return modified; +} + + +void GlobalDefaultsForBkgModel::set_GoptParameter_byIndex(int iCase, float v) +{ + // note case 0,5,10,15,20 are not handled + switch(iCase) { + case 1: region_param_start.kmax_default[0] *= v; break; + case 2: region_param_start.kmax_default[1] *= v; break; + case 3: region_param_start.kmax_default[2] *= v; break; + case 4: region_param_start.kmax_default[3] *= v; break; + case 6: region_param_start.krate_default[0] *= v; break; + case 7: region_param_start.krate_default[1] *= v; break; + case 8: region_param_start.krate_default[2] *= v; break; + case 9: region_param_start.krate_default[3] *= v; break; + case 11: region_param_start.d_default[0] *= v; break; + case 12: region_param_start.d_default[1] *= v; break; + case 13: region_param_start.d_default[2] *= v; break; + case 14: region_param_start.d_default[3] *= v; break; + case 16: region_param_start.sigma_mult_default[0] *= v; break; + case 17: region_param_start.sigma_mult_default[1] *= v; break; + case 18: region_param_start.sigma_mult_default[2] *= v; break; + case 19: region_param_start.sigma_mult_default[3] *= v; break; + case 21: region_param_start.t_mid_nuc_delay_default[0] *= v; break; + case 22: region_param_start.t_mid_nuc_delay_default[1] *= v; break; + case 23: region_param_start.t_mid_nuc_delay_default[2] *= v; break; + case 24: region_param_start.t_mid_nuc_delay_default[3] *= v; break; + case 25: region_param_start.sens_default *= v; break; + case 26: region_param_start.tau_R_m_default *= v; break; + case 27: region_param_start.tau_R_o_default *= v; break; + case 28: data_control.emphasis_params.emp[0] *= v; break; + case 29: data_control.emphasis_params.emp[1] *= v; break; + case 30: data_control.emphasis_params.emp[2] *= v; break; + case 31: data_control.emphasis_params.emp[3] *= v; break; + case 32: data_control.emphasis_params.emp[4] *= v; break; + case 33: data_control.emphasis_params.emp[5] *= v; break; + case 34: data_control.emphasis_params.emp[6] *= v; break; + case 35: data_control.emphasis_params.emp[7] *= v; break; + case 36: data_control.emphasis_params.emphasis_ampl_default *= v; break; + case 37: data_control.emphasis_params.emphasis_width_default *= v; break; + case 38: fitter_defaults.clonal_call_scale[0] *= v; break; + case 39: fitter_defaults.clonal_call_scale[1] *= v; break; + case 40: fitter_defaults.clonal_call_scale[2] *= v; break; + case 41: fitter_defaults.clonal_call_scale[3] *= v; break; + case 42: fitter_defaults.clonal_call_scale[4] *= v; break; + case 43: region_param_start.tau_E_default *= v; break; + case 44: region_param_start.min_tauB_default *= v; break; + case 45: region_param_start.max_tauB_default *= v; break; + case 46: region_param_start.tauB_smooth_range_default *= v; break; + case 0: + default: + cout << "ReadEmphasisVectorFromJson...cannot handle parameter index " << iCase << ":" << v << endl; + exit(1); + } +} + + +// note: ReadEmphasisVectorFromFile() could be removed and replaced by ReadEmphasisVectorFromJson() +// keep it for now in case we have problem with ReadEmphasisVectorFromJson() +// note case 0,5,10,15,20 are not handled in ReadEmphasisVectorFromJson() +#define MAX_LINE_LEN 2048 +#define MAX_DATA_PTS 80 +void GlobalDefaultsForBkgModel::ReadEmphasisVectorFromFile ( char *experimentName ) +{ + char fname[512]; + FILE *evect_file; + char *line = new char[MAX_LINE_LEN]; + float read_data[MAX_DATA_PTS]; + int nChar = MAX_LINE_LEN; + + struct stat fstatus; + sprintf ( fname,"%s/emphasis_vector.txt", experimentName ); + int status = stat ( fname,&fstatus ); + //bool modified = false; + if ( status == 0 ) // file exists + { + //modified = true; + printf ( "loading emphasis vector parameters from %s\n",fname ); + evect_file=fopen ( fname,"rt" ); + // first line contains the number of points + int bytes_read = getline ( &line, ( size_t * ) &nChar,evect_file ); + if ( bytes_read > 0 ) + { + int evect_size; + sscanf ( line,"%d",&evect_size ); + printf ("nps=%d",evect_size); + for (int i=0; ( i < evect_size ) && ( i < MAX_DATA_PTS );i++ ) + { + bytes_read = getline ( &line, ( size_t * ) &nChar,evect_file ); + sscanf ( line,"%f",&read_data[i] ); + printf ("\t%f",read_data[i]); + } + printf ("\n"); + for (int i=0; ( i < evect_size ) && ( i < MAX_DATA_PTS );i++ ) + { + if (read_data[i] != 1) { + printf ("Setting parameter %d *= %f\n",i,read_data[i]); + set_GoptParameter_byIndex(i, read_data[i]); + } + } + } + } + delete [] line; + //return modified; +} diff --git a/Analysis/BkgModel/GlobalDefaultsForBkgModel.h b/Analysis/BkgModel/GlobalDefaultsForBkgModel.h index 4277809a..8ba6ae21 100644 --- a/Analysis/BkgModel/GlobalDefaultsForBkgModel.h +++ b/Analysis/BkgModel/GlobalDefaultsForBkgModel.h @@ -167,17 +167,19 @@ class GlobalDefaultsForBkgModel void FixRdrInFirst20Flows(bool fixed_RatioDrift) { signal_process_control.no_RatioDrift_fit_first_20_flows = fixed_RatioDrift; } void SetUse_alternative_etbR_equation(bool if_use_alternative_etbR_equation) { signal_process_control.use_alternative_etbR_equation = if_use_alternative_etbR_equation; } - void SetFittingTauE(bool fit_taue) { signal_process_control.fitting_taue = fit_taue; } + void SetFittingTauE(bool fit_taue) { signal_process_control.fitting_taue = fit_taue; } void SetHydrogenModel( int model ) { signal_process_control.hydrogenModelType = model; } void SetFitGaussNewton(bool _fit_gauss_newton){signal_process_control.fit_gauss_newton = _fit_gauss_newton;}; - void SetEmphasizeByCompression(bool _emp_by_comp){data_control.point_emphasis_by_compression = _emp_by_comp;}; + void SetEmphasizeByCompression(bool _emp_by_comp){data_control.emphasis_params.point_emphasis_by_compression = _emp_by_comp;}; void ReadXtalk(char *name); void SetChipType(const char *name); // i/o from files for parameters - void SetGoptDefaults(char *gopt); - void ReadEmphasisVectorFromFile(char *experimentName); + void SetGoptDefaults(char *gopt, char *results_folder); + void ReadEmphasisVectorFromFile(char *experimentName); + bool ReadEmphasisVectorFromJson(char *experimentName); + void set_GoptParameter_byIndex(int iCase, float v); void DumpExcitingParameters(const char *fun_string); void GoptDefaultsFromJson(char *fname); void GoptDefaultsFromPoorlyStructuredFile(char *fname); diff --git a/Analysis/BkgModel/LocalTrace/BkgTrace.cpp b/Analysis/BkgModel/LocalTrace/BkgTrace.cpp index da6713f7..05c6b8f9 100644 --- a/Analysis/BkgModel/LocalTrace/BkgTrace.cpp +++ b/Analysis/BkgModel/LocalTrace/BkgTrace.cpp @@ -9,6 +9,8 @@ using namespace std; //#define DEBUG_BKTRC 1 +#define WITH_SUBTRACT 1 + double GenAllBtrc_time=0; double ReZero_time=0; @@ -1014,16 +1016,22 @@ void BkgTrace::LoadImgWRezeroOffset(const RawImage *raw, int16_t *out[VEC8_SIZE] } //#define BEADTRACE_CHKBOTH_DBG 1 -void BkgTrace::GenerateAllBeadTrace (Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, int flow_block_size) +void BkgTrace::GenerateAllBeadTrace (Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, + int flow_block_size, float t_start, float t_end) { #ifndef BEADTRACE_CHKBOTH_DBG // Timer t; // t.restart(); if(/*0 && */(((region->w % VEC8_SIZE) == 0) && - ((img->GetImage()->cols % VEC8_SIZE) == 0))) // check that pointers are alligned too. - GenerateAllBeadTrace_vec(region,my_beads,img,iFlowBuffer,fg_buffers, flow_block_size); - else + ((img->GetImage()->cols % VEC8_SIZE) == 0))) {// check that pointers are alligned too. + GenerateAllBeadTrace_vec(region,my_beads,img,iFlowBuffer,fg_buffers, flow_block_size, t_start, t_end); +#ifndef WITH_SUBTRACT + RezeroBeads (t_start, t_end, iFlowBuffer,flow_block_size); +#endif + }else{ GenerateAllBeadTrace_nonvec(region,my_beads,img,iFlowBuffer,fg_buffers, flow_block_size); + RezeroBeads (t_start, t_end, iFlowBuffer,flow_block_size); + } // cout << "Generate Bead Trace: " << t.elapsed() << "s" << endl; @@ -1198,77 +1206,108 @@ void BkgTrace::GenerateAllBeadTraceAnRezero (Region *region, BeadTracker &my_bea void BkgTrace::GenerateAllBeadTrace_vec (Region *region, BeadTracker &my_beads, - Image *img, int iFlowBuffer, FG_BUFFER_TYPE *fgb, int flow_block_size) + Image *img, int iFlowBuffer, FG_BUFFER_TYPE *fgb, int flow_block_size, + float t_start, float t_end) { // these are used by both the background and live-bead - int k; + uint k; int nbdx=0; int npts=time_cp->npts(); - FG_BUFFER_TYPE *fgPtr; - float localT0; + FG_BUFFER_TYPE *fgPtr __attribute__ ((aligned(16))); + int16_t *sptr __attribute__ ((aligned(16))); + int16_t *imgPtr __attribute__ ((aligned(16))); const RawImage *raw = img->GetImage(); + float localT0; int x,y; - int t0ShiftWhole; - float multT; - float t0ShiftFrac; + int t0ShiftWhole=0; + float t0ShiftFrac=0; int my_frame = 0,compFrm,curFrms,curCompFrms; - v8f_u prev; - v8f_u next; - v8f_u tmpAdder; - v8f_u mult; - v8f_u curCompFrmsV; + +#define MY_VEC_SIZE 8 +#define MY_VECF v8f_u +#define MY_VECS v8s_u + + + MY_VECF prev; + MY_VECF next; + MY_VECF dummy={}; + MY_VECF tmpAdder; +#ifdef WITH_SUBTRACT + MY_VECF tmpTrace[npts]; + MY_VECS tmpTraceS[npts]; +#endif + int frameStride=raw->rows*raw->cols; int interf,lastInterf=-1; - int16_t *sptr, *imgPtr; - int storeIdx[VEC8_SIZE]; + int storeIdx[MY_VEC_SIZE]; Timer tmr; +#ifdef WITH_SUBTRACT + int start_pt=0; + int end_pt=0; + float dc_cnt; + float overhang_start=0.0f; + float overhang_end=0.0f; + int overhang_start_pt=1; + int overhang_end_pt=1; + + ComputeDcOffset_params(t_start, t_end, start_pt, end_pt, dc_cnt, + overhang_start, overhang_end); + + if(start_pt > 0) + overhang_start_pt = start_pt-1; + else + overhang_start_pt = 0; + + if(end_pt > 0 && end_pt < npts) + overhang_end_pt = end_pt; + else + overhang_end_pt=0; + MY_VECF ohsv; + MY_VECF ohev; + ohsv.V = dummy.V + overhang_start; + ohev.V = dummy.V + overhang_end; +#endif + fgPtr = &fgb[npts * flow_block_size*nbdx+npts*iFlowBuffer]; for (y = 0; y < region->h; y++) { imgPtr = &raw->image[(y+region->row)*raw->cols+region->col]; - for (x = 0;x < region->w && nbdx < numLBeads; x+=VEC8_SIZE,imgPtr+=VEC8_SIZE) + for (x = 0;x < region->w && nbdx < numLBeads; x+=MY_VEC_SIZE,imgPtr+=MY_VEC_SIZE) { -#ifdef BEADTRACE_DBG - int doDebug=0; - if(my_beads.params_nn[nbdx].x == 1 && my_beads.params_nn[nbdx].y == 0 && - region->row == DBG_ROW && region->col == DBG_COL) - { - doDebug=1; - } -#endif int incr=0; int nbdxCopy=nbdx; float localT0Cnt=0.0f; localT0=0.0f; - for(k=0;kw+(x+k)]; //BUG!! t0_map is a 2D map containing all beads not only live beads. - localT0Cnt += 1.0f; - storeIdx[k]=incr++; - nbdx++; - } - } + for(k=0;kw+(x+k)]; //BUG!! t0_map is a 2D map containing all beads not only live beads. + localT0Cnt += 1.0f; + storeIdx[k]=incr++; + nbdx++; + }else{ +// printf("%d/%d: %f\n",y,x+k,t0_map[y*region->w+(x+k)]); + } + } if(nbdxCopy == nbdx) continue; // there are no live beads in this chunk localT0 /= localT0Cnt; - //allow for negative t0shift, faster traces + //allow for negative t0shift, faster traces if(localT0 < 0-(raw->uncompFrames-2)) localT0 = 0-(raw->uncompFrames-2); if(localT0 > (raw->uncompFrames-2)) localT0 = (raw->uncompFrames-2); - //by using floor() instead of (int) here - //we now can allow for negative t0Shifts + //by using floor() instead of (int) here + //we now can allow for negative t0Shifts t0ShiftWhole=floor(localT0); t0ShiftFrac = localT0 - (float)t0ShiftWhole; @@ -1277,85 +1316,122 @@ void BkgTrace::GenerateAllBeadTrace_vec (Region *region, BeadTracker &my_beads, my_frame = raw->interpolatedFrames[StartAtFrame]-1; compFrm = 0; - tmpAdder.V=LD_VEC8F(0.0f); - prev.V=LD_VEC8F(0.0f); + tmpAdder.V=0.0f+dummy.V; + prev.V=0.0f+dummy.V; curFrms=0; curCompFrms=time_cp->frames_per_point[compFrm]; - interf= raw->interpolatedFrames[my_frame]; - sptr = &imgPtr[interf*frameStride]; + interf= raw->interpolatedFrames[my_frame]; + sptr = &imgPtr[interf*frameStride]; - LD_VEC8S_CVT_VEC8F(sptr,next); + LD_VS_VF(sptr, next); - while ((my_frame < raw->uncompFrames) && (compFrm < npts)) - { - interf= raw->interpolatedFrames[my_frame]; - - if(interf != lastInterf) - { - sptr = &imgPtr[interf*frameStride]; - prev.V = next.V; - LD_VEC8S_CVT_VEC8F(sptr,next); - } - - // interpolate - multT=raw->interpolatedMult[my_frame] - (t0ShiftFrac/raw->interpolatedDiv[my_frame]); - mult.V = LD_VEC8F(multT); - tmpAdder.V += ( (prev.V)-(next.V) ) * (mult.V) + (next.V); -#ifdef BEADTRACE_DBG - if(doDebug) - printf(" tmpAddr(%f) += (%f-%f) * %f + %f\n",tmpAdder.A[0],prev.A[0],next.A[0],mult.A[0],next.A[0]); -#endif - if(++curFrms >= curCompFrms) - { - curCompFrmsV.V = LD_VEC8F((float)curCompFrms); - tmpAdder.V /= curCompFrmsV.V; - // now, turn it back into short int's - v8s_u svalV; - CVT_VEC8F_VEC8S(svalV,tmpAdder); - - for(k=0;kuncompFrames) && (compFrm < npts)) { + interf= raw->interpolatedFrames[my_frame]; + + if(interf != lastInterf) { + sptr = &imgPtr[interf*frameStride]; + prev.V = next.V; + LD_VS_VF(sptr, next); + } + + // interpolate + MY_VECF mult; + float tmpMult = (raw->interpolatedMult[my_frame] + - (t0ShiftFrac/raw->interpolatedDiv[my_frame])); + mult.V = dummy.V + tmpMult; + //BC_VEC(mult,&tmpMult); + tmpAdder.V += ((prev.V)-(next.V) ) * (mult.V) + (next.V); + if(++curFrms >= curCompFrms) { + tmpAdder.V = tmpAdder.V / ((float )curCompFrms); + +#ifdef WITH_SUBTRACT + tmpTrace[compFrm].V = tmpAdder.V; +#else + MY_VECS svalV; + CVT_VF_VS(svalV,tmpAdder); + + for(k=0;k= 0) fgPtr[storeIdx[k]*npts*flow_block_size+compFrm] = svalV.A[k]; -// fgPtr[storeIdx[k]*npts*flow_block_size+compFrm] = tmpAdder.A[k]; } -#ifdef BEADTRACE_DBG - if(doDebug) - printf("V Writing %d\n",svalV.A[0]); #endif - compFrm++; - curCompFrms = time_cp->frames_per_point[compFrm]; - curFrms=0; - tmpAdder.V = LD_VEC8F(0.0f); - } - //reuse current my_frame while not compensated for negative t0 shift - if(t0ShiftWhole < 0) - t0ShiftWhole++; - else - my_frame++; + compFrm++; + curCompFrms = time_cp->frames_per_point[compFrm]; + curFrms=0; + tmpAdder.V = dummy.V + 0.0f; + } + //reuse current my_frame while not compensated for negative t0 shift + if(t0ShiftWhole < 0) + t0ShiftWhole++; + else + my_frame++; - } + } if(compFrm > 0 && compFrm < npts) { for(;compFrm < npts;compFrm++) { - for(k=0;k= 0) fgPtr[storeIdx[k]*npts*flow_block_size + compFrm] = fgPtr[storeIdx[k]*npts*flow_block_size + compFrm-1]; } + +#endif } + } + +#ifdef WITH_SUBTRACT + // do the trace zeroing... + { + MY_VECF dc_zero; + dc_zero.V=dummy.V + 0.0f; + + for (int pt = start_pt; pt < end_pt; pt++){ + dc_zero.V += (tmpTrace[pt].V); + } + + // add end interpolation parts + dc_zero.V += ohsv.V*(tmpTrace[overhang_start_pt].V); + dc_zero.V += ohev.V*(tmpTrace[overhang_end_pt].V); + + // make it into an average + dc_zero.V /= dummy.V + dc_cnt; + + // now, subtract the dc offset from all the points + for (int pt = 0;pt < npts;pt++){ // over real data + tmpTrace[pt].V -= dc_zero.V; + CVT_VF_VS(tmpTraceS[pt],tmpTrace[pt]); + } } - for(k=0;k= 0){ + int stidx=storeIdx[k] * npts * flow_block_size; + + for (int frm=0; frm < compFrm; frm++) { + fgPtr[stidx + frm] = tmpTraceS[frm].V[k]; +// fgPtr[stidx + frm] = tmpTrace[frm].V[k]; + } + } + } +#endif + + for(k=0;k= 0) fgPtr += npts * flow_block_size; // advance one bead } - } + } + } /* diff --git a/Analysis/BkgModel/LocalTrace/BkgTrace.h b/Analysis/BkgModel/LocalTrace/BkgTrace.h index f039395d..a82cc7f1 100644 --- a/Analysis/BkgModel/LocalTrace/BkgTrace.h +++ b/Analysis/BkgModel/LocalTrace/BkgTrace.h @@ -47,9 +47,9 @@ class BkgTrace{ void RezeroOneBead(float t_start, float t_end, int fnum, int ibd, int flow_block_size); void RezeroBeadsAllFlows (float t_start, float t_end); void RezeroUncompressedTraceV(void *Ptr, float t_start, float t_end); - void GenerateAllBeadTrace(Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, int flow_block_size); + void GenerateAllBeadTrace(Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, int flow_block_size,float t_start, float t_end); void GenerateAllBeadTrace_nonvec(Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, FG_BUFFER_TYPE *fgb, int flow_block_size); - void GenerateAllBeadTrace_vec(Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, FG_BUFFER_TYPE *fgb, int flow_block_size); + void GenerateAllBeadTrace_vec(Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, FG_BUFFER_TYPE *fgb, int flow_block_size, float t_start, float t_end); void GenerateAllBeadTraceAnRezero(Region *region, BeadTracker &my_beads, Image *img, int iFlowBuffer, int flow_block_size, float t_start, float t_end); #define BKTRC_VEC_SIZE 8 diff --git a/Analysis/BkgModel/MathModel/MathOptim.cpp b/Analysis/BkgModel/MathModel/MathOptim.cpp index b3bf2674..9a402ff4 100644 --- a/Analysis/BkgModel/MathModel/MathOptim.cpp +++ b/Analysis/BkgModel/MathModel/MathOptim.cpp @@ -79,6 +79,8 @@ float ErfApprox ( float x ) return ( ret * sign ); } + +#if 0 float ExpApprox ( float x ) { int left, right; @@ -112,7 +114,7 @@ float ExpApprox ( float x ) return ( ret ); } - +#endif diff --git a/Analysis/BkgModel/MathModel/MathOptim.h b/Analysis/BkgModel/MathModel/MathOptim.h index 20cfa566..35d5986d 100644 --- a/Analysis/BkgModel/MathModel/MathOptim.h +++ b/Analysis/BkgModel/MathModel/MathOptim.h @@ -136,6 +136,12 @@ class MixtureMemo float ErfApprox ( float x ); float Expm2Approx ( float x ); -float ExpApprox ( float x ); +inline float ExpApprox ( float x ) +{ + x = 1.0 + x / 256.0; + x *= x; x *= x; x *= x; x *= x; + x *= x; x *= x; x *= x; x *= x; + return x; +} #endif // MATHOPTIM_H diff --git a/Analysis/BkgModel/RegionalizedData.cpp b/Analysis/BkgModel/RegionalizedData.cpp index e1827bb8..dd2497fa 100644 --- a/Analysis/BkgModel/RegionalizedData.cpp +++ b/Analysis/BkgModel/RegionalizedData.cpp @@ -118,7 +118,7 @@ void RegionalizedData::SetTimeAndEmphasis (GlobalDefaultsForBkgModel &global_def if (global_defaults.signal_process_control.recompress_tail_raw_trace) // Generate emphasis vector object for standard time compression - SetUpEmphasisForStandardCompression(global_defaults); + std_time_comp_emphasis.SetUpEmphasis(global_defaults.data_control, time_c); } else // check the points that we need @@ -129,36 +129,42 @@ void RegionalizedData::SetTimeAndEmphasis (GlobalDefaultsForBkgModel &global_def // assuming that our t_mid_nuc estimation is decent // see what the emphasis functions needed for "detailed" results are // and assume the "coarse" blank emphasis function will work out fine. - trial_emphasis.SetDefaultValues (global_defaults.data_control.emp,global_defaults.data_control.emphasis_ampl_default, global_defaults.data_control.emphasis_width_default); - trial_emphasis.SetupEmphasisTiming (time_c.npts(), &time_c.frames_per_point[0],&time_c.frameNumber[0]); - trial_emphasis.point_emphasis_by_compression = global_defaults.data_control.point_emphasis_by_compression; - // trial_emphasis.BuildCurrentEmphasisTable (t_mid_nuc_start, FINEXEMPHASIS); + trial_emphasis.SetUpEmphasis(global_defaults.data_control,time_c); trial_emphasis.BuildCurrentEmphasisTable (t0_offset, FINEXEMPHASIS); - //time_c.npts(trial_emphasis.ReportUnusedPoints (CENSOR_THRESHOLD, MIN_CENSOR)); // threshold the points for the number actually needed by emphasis time_c.SetStandardFrames(trial_emphasis.ReportUnusedPoints (CENSOR_THRESHOLD, MIN_CENSOR)); time_c.UseStandardCompression(); - - // don't bother monitoring this now - //printf ("Saved: %f = %d of %d\n", (1.0*time_c.npts) / (1.0*old_pts), time_c.npts, old_pts); - // now give the emphasis data structure (and everything else) using the "used" number of points } - emphasis_data.SetDefaultValues (global_defaults.data_control.emp,global_defaults.data_control.emphasis_ampl_default, global_defaults.data_control.emphasis_width_default); - emphasis_data.SetupEmphasisTiming (time_c.npts(), &time_c.frames_per_point[0],&time_c.frameNumber[0]); - emphasis_data.point_emphasis_by_compression = global_defaults.data_control.point_emphasis_by_compression; - // emphasis_data.BuildCurrentEmphasisTable (t_mid_nuc_start, FINEXEMPHASIS); + emphasis_data.SetUpEmphasis(global_defaults.data_control, time_c); emphasis_data.BuildCurrentEmphasisTable (t0_offset, FINEXEMPHASIS); -#if 0 - static int doneOnce=0; - if(!doneOnce){ - doneOnce=1; - emphasis_data.SaveEmphasisVector(); - } -#endif } +void RegionalizedData::SetCrudeEmphasisVectors() +{ + // head off bad behaviour if this region was skipped during processing of previous block of flows. + // not clear this is such a great way to solve this problem. + if(my_beads.numLBeads != 0) + emphasis_data.BuildCurrentEmphasisTable (GetTypicalMidNucTime (& (my_regions.rp.nuc_shape)), CRUDEXEMPHASIS); // why is this not per nuc? +} + +void RegionalizedData::SetFineEmphasisVectors() +{ + emphasis_data.BuildCurrentEmphasisTable (GetTypicalMidNucTime (& (my_regions.rp.nuc_shape)), FINEXEMPHASIS); +} + +void RegionalizedData::GenerateFineEmphasisForStdTimeCompression() +{ + std_time_comp_emphasis.BuildCurrentEmphasisTable ( + GetTypicalMidNucTime (& (my_regions.rp.nuc_shape)), + FINEXEMPHASIS); +} + +void RegionalizedData::SetUpEmphasisForStandardCompression(GlobalDefaultsForBkgModel &global_defaults) +{ +} + void RegionalizedData::SetupTimeAndBuffers ( GlobalDefaultsForBkgModel &global_defaults,float sigma_guess, float t_mid_nuc_guess, @@ -277,31 +283,17 @@ void RegionalizedData::UpdateTracesFromImage (Image *img, FlowBufferInfo &my_flo float t_offset_beads = my_regions.rp.nuc_shape.sigma; #if 1 + // populate bead traces from image file and // time-shift traces for uniform start times; compress traces to flows buffer - my_trace.GenerateAllBeadTrace (region,my_beads,img, my_flow.flowBufferWritePos, flow_block_size); + my_trace.GenerateAllBeadTrace (region,my_beads,img, my_flow.flowBufferWritePos, flow_block_size,time_c.time_start, t_mid_nuc-t_offset_beads); // subtract mean signal in time before flow starts from traces in flows buffer - my_trace.RezeroBeads (time_c.time_start, t_mid_nuc-t_offset_beads, - my_flow.flowBufferWritePos, flow_block_size); - -#if 0 - if(flow > 19){ - std::cout << "Sampled for Flow " << flow << std::endl; - int nPts = my_trace.time_cp->npts(); - FG_BUFFER_TYPE *fgPtr = &my_trace.fg_buffers[my_flow.flowBufferWritePos*nPts]; - for(int n = 0; n < my_beads.numLBeads; n++){ - if(my_beads.Sampled(n)){ - std::cout << "regId," << 0 <<",x,"<< my_beads.params_nn[n].x << ",y,"<< my_beads.params_nn[n].y; - for (int pt = 0;pt < nPts;pt++){ // over real data - std::cout << "," << fgPtr[pt]; - } - std::cout << std::endl; - } - fgPtr += my_trace.npts * flow_block_size; - } - } -#endif +// now the RezeroBeads is done in GereateAllBeadTrace +// my_trace.RezeroBeads (time_c.time_start, t_mid_nuc-t_offset_beads, +// my_flow.flowBufferWritePos, flow_block_size); + + #else //Do it all at once.. generate bead trace and rezero like it is done in the new GPU pipeline my_trace.GenerateAllBeadTraceAnRezero(region,my_beads,img, my_flow.flowBufferWritePos, flow_block_size, @@ -345,7 +337,7 @@ void RegionalizedData::PickRepresentativeHighQualityWells (float copy_stringency my_beads.my_mean_copy_count = my_beads.KeyNormalizeSampledReads ( true, flow_block_size ); float stringent_filter = my_beads.my_mean_copy_count; int num_sampled = my_beads.NumberSampled(); // weird that I need to know this - if (copy_stringency>0.0f){ + if (copy_stringency>0.0f){ // make filter >stringent< as though the average bead were a certain copy count // this replicates a 'bug' that led to stringent filtering and slight performance changes @@ -354,7 +346,7 @@ void RegionalizedData::PickRepresentativeHighQualityWells (float copy_stringency // but if we wet the filter to be too stringent, we can lose all beads in a region // set a minimum number of beads to succeed with and we can move on - // technically, I'm setting the minimum >rank< here + // technically, I'm setting the minimum >rank< here float min_percentage = min_beads; min_percentage /= num_sampled; min_percentage = 1.0f-min_percentage; @@ -567,25 +559,6 @@ RegionalizedData::~RegionalizedData() } -void RegionalizedData::SetCrudeEmphasisVectors() -{ - // head off bad behaviour if this region was skipped during processing of previous block of flows. - // not clear this is such a great way to solve this problem. - if(my_beads.numLBeads != 0) - emphasis_data.BuildCurrentEmphasisTable (GetTypicalMidNucTime (& (my_regions.rp.nuc_shape)), CRUDEXEMPHASIS); // why is this not per nuc? -} - -void RegionalizedData::SetFineEmphasisVectors() -{ - emphasis_data.BuildCurrentEmphasisTable (GetTypicalMidNucTime (& (my_regions.rp.nuc_shape)), FINEXEMPHASIS); -} - -void RegionalizedData::GenerateFineEmphasisForStdTimeCompression() -{ - std_time_comp_emphasis.BuildCurrentEmphasisTable ( - GetTypicalMidNucTime (& (my_regions.rp.nuc_shape)), - FINEXEMPHASIS); -} @@ -599,20 +572,6 @@ void RegionalizedData::DumpEmptyTrace (FILE *my_fp, int flow_block_size) } } -void RegionalizedData::SetUpEmphasisForStandardCompression(GlobalDefaultsForBkgModel &global_defaults) -{ - std_time_comp_emphasis.SetDefaultValues ( - global_defaults.data_control.emp, - global_defaults.data_control.emphasis_ampl_default, - global_defaults.data_control.emphasis_width_default); - std_time_comp_emphasis.SetupEmphasisTiming ( - time_c.GetStdFrames(), - &((time_c.GetStdFramesPerPoint())[0]), - &((time_c.GetStdFrameNumber())[0])); - std_time_comp_emphasis.point_emphasis_by_compression = - global_defaults.data_control.point_emphasis_by_compression; - -} bool RegionalizedData::isRegionCenter(int ibd) diff --git a/Analysis/BkgModel/SignalProcessingMasterFitter.cpp b/Analysis/BkgModel/SignalProcessingMasterFitter.cpp index e76eb464..49d516dd 100644 --- a/Analysis/BkgModel/SignalProcessingMasterFitter.cpp +++ b/Analysis/BkgModel/SignalProcessingMasterFitter.cpp @@ -10,7 +10,7 @@ #include "SignalProcessingMasterFitter.h" #include "RawWells.h" #include "MathOptim.h" -#include "mixed.h" +#include "ClonalFilter/mixed.h" #include "BkgDataPointers.h" #include "DNTPRiseModel.h" #include "DiffEqModel.h" @@ -424,6 +424,8 @@ void SignalProcessingMasterFitter::BkgModelInit ( bool debug_trace_enable,float if ( ( !NeverProcessRegion() ) && debug_trace_enable ) my_debug.DebugFileOpen ( global_state.dirName, region_data->region ); + IF_OPTIMIZER_DEBUG( inception_state, debugSaver.DebugFileOpen( global_state.dirName ) ); + // Rest of initialization delayed until SetupTimeAndBuffers() called. } @@ -594,11 +596,11 @@ void SignalProcessingMasterFitter::FirstPassSampledRegionParamFit( int flow_key, MultiFlowLevMar first_lev_mar_fit( *this, flow_block_size, table ); - first_lev_mar_fit.MultiFlowSpecializedSampledLevMarFitParameters ( 1, 3, first_lev_mar_fit.fit_control.FitWellAmplBuffering, first_lev_mar_fit.fit_control.FitRegionTmidnucPlus, SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + first_lev_mar_fit.MultiFlowSpecializedSampledLevMarFitParameters ( 1, 3, first_lev_mar_fit.fit_control.GetFitPacker("FitWellAmplBuffering"), first_lev_mar_fit.fit_control.GetFitPacker("FitRegionTmidnucPlus"), SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); region_data->my_beads.my_mean_copy_count = region_data->my_beads.KeyNormalizeSampledReads ( true, flow_block_size ); - first_lev_mar_fit.MultiFlowSpecializedSampledLevMarFitParameters ( 1, 1, first_lev_mar_fit.fit_control.FitWellAmplBuffering, first_lev_mar_fit.fit_control.FitRegionTmidnucPlus, SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + first_lev_mar_fit.MultiFlowSpecializedSampledLevMarFitParameters ( 1, 1, first_lev_mar_fit.fit_control.GetFitPacker("FitWellAmplBuffering"), first_lev_mar_fit.fit_control.GetFitPacker("FitRegionTmidnucPlus"), SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); region_data->my_beads.my_mean_copy_count = region_data->my_beads.KeyNormalizeSampledReads ( true, flow_block_size ); - first_lev_mar_fit.MultiFlowSpecializedSampledLevMarFitParameters ( 1,1, first_lev_mar_fit.fit_control.FitWellAmplBuffering, first_lev_mar_fit.fit_control.FitRegionTmidnucPlus,SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + first_lev_mar_fit.MultiFlowSpecializedSampledLevMarFitParameters ( 1,1, first_lev_mar_fit.fit_control.GetFitPacker("FitWellAmplBuffering"), first_lev_mar_fit.fit_control.GetFitPacker("FitRegionTmidnucPlus"),SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); } // first pass fit using all beads @@ -609,11 +611,11 @@ void SignalProcessingMasterFitter::FirstPassRegionParamFit( int flow_key, int fl MultiFlowLevMar first_lev_mar_fit( *this, flow_block_size, table ); - first_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( 1, 3, first_lev_mar_fit.fit_control.FitWellAmplBuffering, first_lev_mar_fit.fit_control.FitRegionTmidnucPlus, SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + first_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( 1, 3, first_lev_mar_fit.fit_control.GetFitPacker("FitWellAmplBuffering"), first_lev_mar_fit.fit_control.GetFitPacker("FitRegionTmidnucPlus"), SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); region_data->my_beads.my_mean_copy_count = region_data->my_beads.KeyNormalizeReads ( true, false, flow_block_size ); - first_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( 1, 1, first_lev_mar_fit.fit_control.FitWellAmplBuffering, first_lev_mar_fit.fit_control.FitRegionTmidnucPlus, SMALL_LAMBDA, NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + first_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( 1, 1, first_lev_mar_fit.fit_control.GetFitPacker("FitWellAmplBuffering"), first_lev_mar_fit.fit_control.GetFitPacker("FitRegionTmidnucPlus"), SMALL_LAMBDA, NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); region_data->my_beads.my_mean_copy_count = region_data->my_beads.KeyNormalizeReads ( true, false, flow_block_size ); - first_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( 1, 1, first_lev_mar_fit.fit_control.FitWellAmplBuffering, first_lev_mar_fit.fit_control.FitRegionTmidnucPlus, SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + first_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( 1, 1, first_lev_mar_fit.fit_control.GetFitPacker("FitWellAmplBuffering"), first_lev_mar_fit.fit_control.GetFitPacker("FitRegionTmidnucPlus"), SMALL_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); } @@ -627,8 +629,8 @@ void SignalProcessingMasterFitter::PostKeyFitNoRegionalSampling (MultiFlowLevMar fit_timer.restart(); region_data->RezeroByCurrentTiming( flow_block_size ); // rezeroing?? - - post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellPostKey, post_key_fit.fit_control.FitRegionInit2, LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + bool fittaue = global_defaults.signal_process_control.fitting_taue; + post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellPostKey"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionInit2TauE":"FitRegionInit2"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); elapsed_time += fit_timer.elapsed(); // classify beads here? @@ -643,9 +645,9 @@ void SignalProcessingMasterFitter::PostKeyFitNoRegionalSampling (MultiFlowLevMar post_key_fit.lm_state.ref_penalty_scale = global_defaults.signal_process_control.barcode_penalty; // big penalty for getting these wrong! post_key_fit.lm_state.kmult_penalty_scale = global_defaults.signal_process_control.kmult_penalty; // minor penalty for kmult to keep zeros from annoying us if (global_defaults.signal_process_control.fit_region_kmult) - post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellAll, post_key_fit.fit_control.FitRegionInit2, LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellAll"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionInit2TauE":"FitRegionInit2"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); else - post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellPostKey, post_key_fit.fit_control.FitRegionInit2, LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellPostKey"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionInit2TauE":"FitRegionInit2"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); region_data->my_beads.AssignBarcodeState(true, global_defaults.signal_process_control.barcode_radius, global_defaults.signal_process_control.barcode_tie, flow_block_size, flow_block_start); if (global_defaults.signal_process_control.barcode_debug){ @@ -663,9 +665,9 @@ void SignalProcessingMasterFitter::PostKeyFitNoRegionalSampling (MultiFlowLevMar fit_timer.restart(); if (global_defaults.signal_process_control.fit_region_kmult) - post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellAll, post_key_fit.fit_control.FitRegionFull, LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellAll"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionFullTauE":"FitRegionFull"), LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); else - post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellPostKey, post_key_fit.fit_control.FitRegionFull, LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellPostKey"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionFullTauE":"FitRegionFull"), LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); elapsed_time += fit_timer.elapsed(); // last check @@ -683,8 +685,9 @@ void SignalProcessingMasterFitter::PostKeyFitWithRegionalSampling (MultiFlowLevM fit_timer.restart(); region_data->RezeroByCurrentTiming( flow_block_size ); // rezeroing?? + bool fittaue = global_defaults.signal_process_control.fitting_taue; - post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellPostKey, post_key_fit.fit_control.FitRegionInit2, LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellPostKey"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionInit2TauE":"FitRegionInit2"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); elapsed_time += fit_timer.elapsed(); // classify beads here? @@ -699,7 +702,7 @@ void SignalProcessingMasterFitter::PostKeyFitWithRegionalSampling (MultiFlowLevM post_key_fit.lm_state.ref_penalty_scale = global_defaults.signal_process_control.barcode_penalty; // big penalty for getting these wrong! post_key_fit.lm_state.kmult_penalty_scale = global_defaults.signal_process_control.kmult_penalty; // minor penalty for kmult to keep zeros from annoying us - post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellAll, post_key_fit.fit_control.FitRegionInit2, LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellAll"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionInit2TauE":"FitRegionInit2"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); region_data->my_beads.AssignBarcodeState(false, global_defaults.signal_process_control.barcode_radius, global_defaults.signal_process_control.barcode_tie, flow_block_size, flow_block_start); if (global_defaults.signal_process_control.barcode_debug){ region_data->my_beads.barcode_info.ReportClassificationTable(100+region_data->region->index); // show my classification @@ -710,14 +713,14 @@ void SignalProcessingMasterFitter::PostKeyFitWithRegionalSampling (MultiFlowLevM fit_timer.restart(); if (global_defaults.signal_process_control.fit_region_kmult){ - post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellAll, post_key_fit.fit_control.FitRegionFull, LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellAll"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionFullTauE":"FitRegionFull"), LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); } else { - post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.FitWellPostKey, post_key_fit.fit_control.FitRegionFull, LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + post_key_fit.MultiFlowSpecializedSampledLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, post_key_fit.fit_control.GetFitPacker("FitWellPostKey"), post_key_fit.fit_control.GetFitPacker(fittaue?"FitRegionFullTauE":"FitRegionFull"), LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); } elapsed_time += fit_timer.elapsed(); // last check if (barcode_flag){ - region_data->my_beads.AssignBarcodeState(false, global_defaults.signal_process_control.barcode_radius, global_defaults.signal_process_control.barcode_tie, flow_block_size, flow_block_start); + region_data->my_beads.AssignBarcodeState(false, global_defaults.signal_process_control.barcode_radius, global_defaults.signal_process_control.barcode_tie, flow_block_size, flow_block_start); } } @@ -775,7 +778,7 @@ void SignalProcessingMasterFitter::PostKeyFitAllWells ( double &elapsed_time, Ti } for (int i_train=0; i_trainmy_beads.my_mean_copy_count = region_data->my_beads.KeyNormalizeReads ( true, false, flow_block_size ); if (false){ float monitor_etbR = region_data->my_beads.etbRFromReads(); @@ -797,9 +800,9 @@ void SignalProcessingMasterFitter::PostKeyFitAllWells ( double &elapsed_time, Ti // only wells are fit here if (global_defaults.signal_process_control.fit_region_kmult) - all_wells_lev_mar_fit.MultiFlowSpecializedLevMarFitAllWells ( HAPPY_ALL_BEADS, all_wells_lev_mar_fit.fit_control.FitWellAll, LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + all_wells_lev_mar_fit.MultiFlowSpecializedLevMarFitAllWells ( HAPPY_ALL_BEADS, all_wells_lev_mar_fit.fit_control.GetFitPacker("FitWellAll"), LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); else - all_wells_lev_mar_fit.MultiFlowSpecializedLevMarFitAllWells ( HAPPY_ALL_BEADS, all_wells_lev_mar_fit.fit_control.FitWellPostKey, LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + all_wells_lev_mar_fit.MultiFlowSpecializedLevMarFitAllWells ( HAPPY_ALL_BEADS, all_wells_lev_mar_fit.fit_control.GetFitPacker("FitWellPostKey"), LARGER_LAMBDA, FULL_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); elapsed_time += fit_timer.elapsed(); region_data->my_beads.my_mean_copy_count = region_data->my_beads.KeyNormalizeReads ( true, false, flow_block_size ); @@ -833,9 +836,9 @@ void SignalProcessingMasterFitter::FitAmplitudeAndDarkMatter ( MultiFlowLevMar & //@TODO: should I be skipping low-quality bead refits here because we'll be getting their amplitudes in the refinement phase? fit_timer.restart(); if (global_defaults.signal_process_control.enable_dark_matter) - fad_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, fad_lev_mar_fit.fit_control.FitWellAmpl, fad_lev_mar_fit.fit_control.FitRegionDarkness, BIG_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + fad_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, fad_lev_mar_fit.fit_control.GetFitPacker("FitWellAmpl"), fad_lev_mar_fit.fit_control.GetFitPacker("FitRegionDarkness"), BIG_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); else - fad_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, fad_lev_mar_fit.fit_control.FitWellAmpl, fad_lev_mar_fit.fit_control.DontFitRegion, BIG_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + fad_lev_mar_fit.MultiFlowSpecializedLevMarFitParameters ( NO_ADDITIONAL_WELL_ITERATIONS, STANDARD_POST_KEY_ITERATIONS, fad_lev_mar_fit.fit_control.GetFitPacker("FitWellAmpl"), NULL, BIG_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); elapsed_time += fit_timer.elapsed(); } @@ -961,11 +964,15 @@ void SignalProcessingMasterFitter::GuessCrudeAmplitude ( double &elapsed_time, T void SignalProcessingMasterFitter::FitTimeVaryingRegion ( double &elapsed_time, Timer &fit_timer, int flow_key, int flow_block_size, master_fit_type_table *table, int flow_block_start ) { MultiFlowLevMar tvr_lev_mar_fit( *this, flow_block_size, table ); + //example of using new interface + //table->addBkgModelFitType("NoCopydrift",{"TMidNuc","RatioDrift","TableEnd"}); + //tvr_lev_mar_fit.fit_control.AddFitPacker("NoCopydrift",region_data->time_c.npts(),flow_block_size); fit_timer.restart(); // >NOW< we allow any emphasis level given our crude estimates for emphasis region_data->AdaptiveEmphasis(); tvr_lev_mar_fit.ChooseSkipBeads ( true ); - tvr_lev_mar_fit.MultiFlowSpecializedLevMarFitParametersOnlyRegion ( 4, tvr_lev_mar_fit.fit_control.FitRegionTimeVarying, LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + tvr_lev_mar_fit.MultiFlowSpecializedLevMarFitParametersOnlyRegion ( 4, tvr_lev_mar_fit.fit_control.GetFitPacker("FitRegionTimeVarying"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); + //tvr_lev_mar_fit.MultiFlowSpecializedLevMarFitParametersOnlyRegion ( 4, tvr_lev_mar_fit.fit_control.GetFitPacker("NoCopydrift"), LARGER_LAMBDA , NO_NONCLONAL_PENALTY, flow_key, flow_block_size, flow_block_start ); tvr_lev_mar_fit.ChooseSkipBeads ( false ); elapsed_time += fit_timer.elapsed(); } diff --git a/Analysis/BkgModel/SignalProcessingMasterFitter.h b/Analysis/BkgModel/SignalProcessingMasterFitter.h index 64c02adf..6fee1e42 100644 --- a/Analysis/BkgModel/SignalProcessingMasterFitter.h +++ b/Analysis/BkgModel/SignalProcessingMasterFitter.h @@ -372,6 +372,7 @@ class SignalProcessingMasterFitter GlobalWriter global_state; debug_collection my_debug; + DebugSaver debugSaver; // cache math that all bkgmodel objects need to execute PoissonCDFApproxMemo *math_poiss; diff --git a/Analysis/BkgModel/Writers/DebugWriter.cpp b/Analysis/BkgModel/Writers/DebugWriter.cpp index 9f367ce0..9a69f75a 100644 --- a/Analysis/BkgModel/Writers/DebugWriter.cpp +++ b/Analysis/BkgModel/Writers/DebugWriter.cpp @@ -1,12 +1,17 @@ /* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ #include "DebugWriter.h" +#include "BkgModel/Fitters/Complex/BkgFitMatDat.h" + #include #include #include #include "LinuxCompat.h" #include "RawWells.h" #include "SignalProcessingMasterFitter.h" +#include +#include "hdf5_hl.h" +#include #define BKG_MODEL_DEBUG_DIR "/bkg_debug/" @@ -340,3 +345,121 @@ void debug_collection::DumpRegionTrace (SignalProcessingMasterFitter &bkg, int f fflush (my_fp); } // end file exists } + + +hid_t DebugSaver::hdf_file_id = -1; +std::mutex dbg_mutex; + +void DebugSaver::DebugFileOpen( std::string& dirName ) +{ + std::lock_guard guard(dbg_mutex); + + if( hdf_file_id<0 ){ + hdf_file_id = 0; + std::string fname = dirName+"/optimizer.h5"; + hdf_file_id = H5Fcreate(fname.c_str(),H5F_ACC_TRUNC,H5P_DEFAULT,H5P_DEFAULT); + assert(hdf_file_id >= 0); + //CreateDataSet(); //move this somewhere else + } +} + +void DebugSaver::WriteData( const BkgFitMatrixPacker* reg_fit, reg_params& rp, int flow, const Region *region, const std::vector derivativeNames, int nbeads ) +{ + const arma::Mat *jtj = reg_fit->data->jtj; + const arma::Col *rhs = reg_fit->data->rhs; + const arma::Col *delta = reg_fit->data->delta; + + std::lock_guard guard(dbg_mutex); + if( hdf_file_id<0 ) + return; + + /* Save old error handler */ + herr_t ( *old_func ) ( hid_t, void * ); + void *old_client_data; + + H5Eget_auto ( H5E_DEFAULT, &old_func, &old_client_data ); + + /* Silence warnings by turning off error handling */ + H5Eset_auto ( H5E_DEFAULT, NULL, NULL ); + + hsize_t mat_dim[]={jtj->n_rows,jtj->n_cols}; + hsize_t vec_dim[]={delta->n_rows}; + char path[1024]; + snprintf(path, 1024, "regfit/flow_%d/r_%d_c_%d",flow,region->row,region->col); + + std::vector path_parts; + boost::split(path_parts,path,boost::is_any_of("/")); + + + hid_t dataset_id = hdf_file_id; + for( auto group_name=path_parts.begin(); group_name!=path_parts.end(); ++group_name ){ + + hid_t dataset_id_new = H5Gopen(dataset_id, group_name->c_str(),H5P_DEFAULT); + if( dataset_id_new<0) + dataset_id_new = H5Gcreate(dataset_id, group_name->c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if( dataset_id!=hdf_file_id) + H5Gclose(dataset_id); + dataset_id=dataset_id_new; + } + + //we store count in the branch corresponding to the current region + int iteration; + herr_t err = H5LTget_attribute_int(hdf_file_id,path,"count",&iteration); + if( err<0 ) + iteration = 0; + else + ++iteration; + + /* Restore previous error handler */ + H5Eset_auto ( H5E_DEFAULT, old_func, old_client_data ); + + err = H5LTset_attribute_int(hdf_file_id,path,"count",&iteration,1); + + std::string subgroup_name="iter_"; + dataset_id = H5Gcreate(dataset_id, (subgroup_name+std::to_string(iteration)).c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + + //turn parameter names into comma separated string + std::stringstream ss; + for_each(derivativeNames.begin(), derivativeNames.end(), [&ss] (const std::string& s) { ss << s << ","; }); //uses lambda expression + std::string names = ss.str(); + names.pop_back(); //remove extra comma in the end + + H5LTmake_dataset(dataset_id, "jtj", 2, mat_dim, H5T_NATIVE_DOUBLE, jtj->memptr()); + H5LTset_attribute_string(dataset_id,"jtj","paramNames",names.c_str()); + H5LTmake_dataset(dataset_id, "rhs", 1, vec_dim, H5T_NATIVE_DOUBLE, rhs->memptr()); + H5LTset_attribute_string(dataset_id,"rhs","paramNames",names.c_str()); + H5LTmake_dataset(dataset_id, "delta", 1, vec_dim, H5T_NATIVE_DOUBLE, delta->memptr()); + H5LTset_attribute_string(dataset_id,"delta","paramNames",names.c_str()); + + //Save output + float* output= new float[reg_fit->getNumOutputs()]; + ss.str(""); + + for (int i=0;i < reg_fit->getNumOutputs();i++){ + // What is that right place? + float *dptr = (rp.*( reg_fit->getOuputList()[i].reg_params_func))(); + output[i] = dptr[reg_fit->getOuputList()[i].array_index]; + ss<getOuputList()[i].name<<","; + } + names = ss.str(); names.pop_back(); //remove extra comma in the end + vec_dim[0]=static_cast(reg_fit->getNumOutputs()); + H5LTmake_dataset(dataset_id, "output", 1, vec_dim, H5T_NATIVE_FLOAT, output); + H5LTset_attribute_string(dataset_id,"output","paramNames",names.c_str()); + delete[] output; + + vec_dim[0]=1; + H5LTmake_dataset(dataset_id, "nbeads", 1, vec_dim, H5T_NATIVE_INT, &nbeads); + H5Gclose(dataset_id); + H5Fflush(hdf_file_id, H5F_SCOPE_GLOBAL); +} + +DebugSaver::~DebugSaver() +{ + std::lock_guard guard(dbg_mutex); + if(hdf_file_id>=0){ + H5Fclose(hdf_file_id); + hdf_file_id=-1; + } +} diff --git a/Analysis/BkgModel/Writers/DebugWriter.h b/Analysis/BkgModel/Writers/DebugWriter.h index 5aa13055..596a161e 100644 --- a/Analysis/BkgModel/Writers/DebugWriter.h +++ b/Analysis/BkgModel/Writers/DebugWriter.h @@ -8,6 +8,9 @@ #include "BeadTracker.h" #include "RegionParams.h" #include "RegionTracker.h" +#include "BkgModel/Fitters/Complex/BkgFitMatrixPacker.h" +#include "hdf5.h" +#include class SignalProcessingMasterFitter; // forward definition @@ -23,7 +26,6 @@ class debug_collection FILE *region_only_trace_file; FILE *region_1mer_trace_file; FILE *region_0mer_trace_file; - debug_collection(); ~debug_collection(); void DebugFileClose(); @@ -37,4 +39,19 @@ class debug_collection int flow_block_size, int flow_block_start); }; +#define IF_OPTIMIZER_DEBUG( D, X ) { if( D->bkg_control.pest_control.bkgModelHdf5Debug > 3 ) {X;} } +class DebugSaver{ + +private: + static hid_t hdf_file_id; + + +public: + DebugSaver() {} //: hdf_file_id(-1) + ~DebugSaver(); + void DebugFileOpen(std::string& dirName); + void WriteData(const BkgFitMatrixPacker* reg_fit, reg_params &rp, int flow, const Region* region, const std::vector derivativeNames, int nbeads); +}; + + #endif // DEBUGWRITER_H diff --git a/Analysis/CMakeLists.txt b/Analysis/CMakeLists.txt index d7231f56..ccf4c26e 100644 --- a/Analysis/CMakeLists.txt +++ b/Analysis/CMakeLists.txt @@ -68,8 +68,8 @@ if(ION_USE_CUDA) #install(DIRECTORY ${PROJECT_BINARY_DIR}/../${cuda_toolkit_version}/lib64 # DESTINATION /usr/local/cuda) - install(FILES ${PROJECT_BINARY_DIR}/../${cuda_toolkit_version}/lib64/libcudart.so.7.5 - ${PROJECT_BINARY_DIR}/../${cuda_toolkit_version}/lib64/libcudart.so.7.5.18 + install(FILES ${PROJECT_BINARY_DIR}/../${cuda_toolkit_version}/lib64/libcudart.so.8.0 + ${PROJECT_BINARY_DIR}/../${cuda_toolkit_version}/lib64/libcudart.so.8.0.44 DESTINATION /usr/local/cuda/lib64) @@ -87,21 +87,22 @@ if(ION_USE_CUDA) install(PROGRAMS pynvml/pynvml_test.py DESTINATION /usr/local/bin) install(PROGRAMS pynvml/nvidia_smi_test.py DESTINATION /usr/local/bin) - CUDA_ADD_EXECUTABLE(xtalk_sim - xtalk_sim/xtalk_sim.cpp - xtalk_sim/DiffEqModel_Init.cpp - xtalk_sim/DiffEqModel.cpp - xtalk_sim/DelsqCUDA.cu - xtalk_sim/utils.cpp - xtalk_sim/xtalk_sim.cpp - xtalk_sim/sim_incorp_flux.cpp - xtalk_sim/WorkerInfoQueue.cpp) - - target_link_libraries(xtalk_sim pthread) - - add_dependencies(xtalk_sim cuda_toolkit) - - install(PROGRAMS ${PROJECT_BINARY_DIR}/xtalk_sim DESTINATION gpu) +# +# CUDA_ADD_EXECUTABLE(xtalk_sim +# xtalk_sim/xtalk_sim.cpp +# xtalk_sim/DiffEqModel_Init.cpp +# xtalk_sim/DiffEqModel.cpp +# xtalk_sim/DelsqCUDA.cu +# xtalk_sim/utils.cpp +# xtalk_sim/xtalk_sim.cpp +# xtalk_sim/sim_incorp_flux.cpp +# Util/WorkerInfoQueue.cpp) +# +# target_link_libraries(xtalk_sim pthread) +# +# add_dependencies(xtalk_sim cuda_toolkit) +# +# install(PROGRAMS ${PROJECT_BINARY_DIR}/xtalk_sim DESTINATION gpu) endif() @@ -259,9 +260,10 @@ add_library(ion-analysis LinuxCompat.cpp Stats.cpp - LevMarFitterV2.cpp + BkgModel/Fitters/LevMarFitterV2.cpp - mixed.cpp + ClonalFilter/mixed.cpp + ClonalFilter/polyclonal_filter.cpp # T0CalcMt.cpp BaseCaller/PIDloop.cpp @@ -365,9 +367,9 @@ endif() # armadillo doesn't provide those libs? if(ION_USE_MKL) - set(EXTRA_LIBS iomp5 mkl_intel_lp64) + set(EXTRA_LIBS m iomp5 mkl_intel_lp64) else() - set(EXTRA_LIBS blas lapack) + set(EXTRA_LIBS m blas lapack) endif() target_link_libraries(ion-analysis ${ION_HDF5_LIBS} dl ${ION_KMEANS_LIBS} ${ION_BOOST_LIBS} ${EXTRA_LIBS} file-io z) @@ -429,17 +431,17 @@ target_link_libraries(Thumbnail ion-analysis pthread dl) install(TARGETS Thumbnail DESTINATION bin) # If doing pca spline trial -add_executable(PcaSplineExample Image/PcaSplineExample.cpp ${PROJECT_BINARY_DIR}/IonVersion.cpp) -add_dependencies(PcaSplineExample IONVERSION) +#add_executable(PcaSplineExample Image/PcaSplineExample.cpp ${PROJECT_BINARY_DIR}/IonVersion.cpp) +#add_dependencies(PcaSplineExample IONVERSION) # If defined while running cmake look for mkl # e.g. -DMKL_INSTALL_PATH=/home/ionadmin/intel/composer_xe_2013_sp1.0.080/mkl -if (DEFINED MKL_INSTALL_PATH) - message("Compiling PcaSplineExample with mkl") - target_link_libraries(PcaSplineExample ion-analysis ${MKL_LINK_LIBS} -lpthread -lm dl z) -else() - message("Compiling PcaSplineExample with eigen only") - target_link_libraries(PcaSplineExample ion-analysis file-io pthread m dl z) -endif() +#if (DEFINED MKL_INSTALL_PATH) +# message("Compiling PcaSplineExample with mkl") +# target_link_libraries(PcaSplineExample ion-analysis ${MKL_LINK_LIBS} -lpthread -lm dl z) +#else() +# message("Compiling PcaSplineExample with eigen only") +# target_link_libraries(PcaSplineExample ion-analysis file-io pthread m dl z) +#endif() # If doing DfcCompr trial #add_executable(DfcComprExample Image/DfcComprExample.cpp ${PROJECT_BINARY_DIR}/IonVersion.cpp) @@ -535,12 +537,11 @@ include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/Bookkeeping") include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/Reads") include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/Splice") include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/Filter") +include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/Consensus") include_directories("${PROJECT_SOURCE_DIR}/realignment") include_directories("${PROJECT_SOURCE_DIR}/Calibration") set(tvcSRCS - VariantCaller/IndelAssembly/IndelAssembly.cpp - VariantCaller/tvcutils/unify_vcf.cpp VariantCaller/VariantCaller.cpp VariantCaller/BAMWalkerEngine.cpp VariantCaller/OrderedBAMWriter.cpp @@ -549,27 +550,29 @@ set(tvcSRCS VariantCaller/HandleVariant.cpp VariantCaller/HotspotReader.cpp VariantCaller/MetricsManager.cpp - + VariantCaller/HypothesisEvaluator.cpp + VariantCaller/MolecularTag.cpp + VariantCaller/Bookkeeping/MiscUtil.cpp VariantCaller/Bookkeeping/ExtendParameters.cpp VariantCaller/Bookkeeping/InputStructures.cpp VariantCaller/Bookkeeping/VcfFormat.cpp VariantCaller/Reads/ExtendedReadInfo.cpp - + + VariantCaller/IndelAssembly/IndelAssembly.cpp + + VariantCaller/tvcutils/unify_vcf.cpp + VariantCaller/tvcutils/prepare_hotspots.cpp + VariantCaller/Splice/ErrorMotifs.cpp VariantCaller/Splice/LocalContext.cpp VariantCaller/Splice/ClassifyVariant.cpp - VariantCaller/Splice/ErrorMotifs.cpp VariantCaller/Splice/SpliceVariantHypotheses.cpp VariantCaller/Filter/DecisionTreeData.cpp VariantCaller/Filter/VariantAssist.cpp - VariantCaller/HypothesisEvaluator.cpp - - VariantCaller/MolecularTag.cpp - VariantCaller/EnsembleEval/DiagnosticJSON.cpp VariantCaller/EnsembleEval/BiasGenerator.cpp VariantCaller/EnsembleEval/SigmaGenerator.cpp @@ -579,6 +582,10 @@ set(tvcSRCS VariantCaller/EnsembleEval/StackEngine.cpp VariantCaller/EnsembleEval/CrossHypotheses.cpp + VariantCaller/Consensus/Consensus.cpp + VariantCaller/Consensus/FlowSpaceConsensus.cpp + VariantCaller/Consensus/ConsensusParameters.cpp + # TODO: Actually build vcflib as a static library and link to variant caller. # TODO2: Resolve bgzf.c collisions between vcflib and bamtools ${ION_VCFLIB_DIR}/Variant.cpp @@ -598,6 +605,7 @@ set(tvcSRCS Calibration/LinearCalibrationModel.cpp Calibration/FlowAlignment.cpp Util/OptArgs.cpp + AnalysisOrg/IO/OptBase.cpp realignment/Realigner.cpp ${ION_TS_EXTERNAL}/jsoncpp-src-amalgated0.6.0-rc1/jsoncpp.cpp ${PROJECT_BINARY_DIR}/IonVersion.cpp @@ -613,20 +621,29 @@ target_link_libraries(tvc ${ION_BAMTOOLS_LIBS} ${EXTRA_LIBS} z file-io pthread) add_dependencies(tvc IONVERSION bamtools armadillo_proj htslib_proj) install(TARGETS tvc DESTINATION bin) - ## tmol executable ## - -include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/tmol") - -set(tmolSRCS - VariantCaller/tmol/tmol.cpp +# include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/tmol") +#set(tmolSRCS +# VariantCaller/tmol/tmol.cpp +# ${PROJECT_BINARY_DIR}/IonVersion.cpp +#) +#add_executable(tmol ${tmolSRCS}) +#target_link_libraries(tmol ${ION_BAMTOOLS_LIBS} ${EXTRA_LIBS} z pthread) +#add_dependencies(tmol IONVERSION bamtools) +#install(TARGETS tmol DESTINATION bin) + +## vcfcomp executable ## +include_directories("${PROJECT_SOURCE_DIR}/VariantCaller/vcfcomp") + +set(vcfcompSRCS + VariantCaller/vcfcomp/vcfcomp.cpp ${PROJECT_BINARY_DIR}/IonVersion.cpp ) -add_executable(tmol ${tmolSRCS}) -target_link_libraries(tmol ${ION_BAMTOOLS_LIBS} ${EXTRA_LIBS} z pthread) -add_dependencies(tmol IONVERSION bamtools) -install(TARGETS tmol DESTINATION bin) +add_executable(vcfcomp ${vcfcompSRCS}) +target_link_libraries(vcfcomp ${ION_SAMTOOLS_LIBS} ${ION_HTSLIB_LIBS} z pthread) +add_dependencies(vcfcomp IONVERSION samtools) +install(TARGETS vcfcomp DESTINATION bin) # bbctools @@ -653,6 +670,8 @@ bbctools/src/RegionCoverage.cpp bbctools/src/RegionCoverage.h bbctools/src/RegionStatistics.cpp bbctools/src/RegionStatistics.h +bbctools/src/TrackReads.cpp +bbctools/src/TrackReads.h ) add_executable(bbctools ${bbctools_src}) @@ -697,6 +716,7 @@ add_executable(tvcutils VariantCaller/tvcutils/unify_vcf.cpp VariantCaller/tvcutils/split_vcf.cpp VariantCaller/TargetsManager.cpp + realignment/Realigner.cpp Util/OptArgs.cpp # Util/Utils.cpp ${ION_VCFLIB_DIR}/Variant.cpp @@ -929,7 +949,7 @@ if(GTEST_FOUND) add_executable(DualGaussMixModel_Test utest/DualGaussMixModel_Test.cpp) target_link_libraries(DualGaussMixModel_Test ion-analysis ${GTEST_BOTH_LIBRARIES} pthread) - add_test(DualGaussMixModelTest DualGaussMixModel_Test --gtest_output=xml:./) +# add_test(DualGaussMixModelTest DualGaussMixModel_Test --gtest_output=xml:./) # add_executable(ZeromerDiff_Test utest/ZeromerDiff_Test.cpp) # target_link_libraries(ZeromerDiff_Test ion-analysis ${GTEST_BOTH_LIBRARIES} ) @@ -983,6 +1003,10 @@ install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_324.bin" DESTINATION $ install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_p1.1.17.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_p1.0.19.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_p1.0.20.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_PQ.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_510.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_520.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_550.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_521.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_522.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/exclusionMask_530.txt" DESTINATION ${ION_INSTALL_PREFIX}/config) @@ -1029,6 +1053,9 @@ install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.316v2.B5.Recal.h5" DESTIN install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.318.B5.h5" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.318.B5.Recal.h5" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.318D.h5" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.318D.Recal.h5" DESTINATION ${ION_INSTALL_PREFIX}/config) + install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.900" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.900.h5" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/phredTable.900.Recal" DESTINATION ${ION_INSTALL_PREFIX}/config) @@ -1162,6 +1189,7 @@ install(FILES "${PROJECT_SOURCE_DIR}/config/args_P2.3.1_beadfind.json" DESTINATI # #gopt parameters by chip type #gopt parameters +install(FILES "${PROJECT_SOURCE_DIR}/config/gopt_318D.param.json" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/gopt_314v2_Hi-Q.param.json" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/gopt_316v2_Hi-Q.param.json" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/gopt_318v2_Hi-Q.param.json" DESTINATION ${ION_INSTALL_PREFIX}/config) @@ -1205,6 +1233,8 @@ install(FILES "${PROJECT_SOURCE_DIR}/config/xtalk.trace.520.json" DESTINATION ${ install(FILES "${PROJECT_SOURCE_DIR}/config/xtalk.trace.521.json" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/xtalk.trace.522.json" DESTINATION ${ION_INSTALL_PREFIX}/config) install(FILES "${PROJECT_SOURCE_DIR}/config/xtalk.trace.530.json" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/xtalk.550.well.settings.json" DESTINATION ${ION_INSTALL_PREFIX}/config) +install(FILES "${PROJECT_SOURCE_DIR}/config/xtalk.p2.3.1.well.settings.json" DESTINATION ${ION_INSTALL_PREFIX}/config) # Recalibration Panel install(FILES "${PROJECT_SOURCE_DIR}/config/datasets_calibration.json" DESTINATION ${ION_INSTALL_PREFIX}/config) @@ -1236,12 +1266,9 @@ endif() include(../buildTools/cmake/CMakeLists.cpack.txt) #% nice to automate this -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libatlas3gf-base, - libblas3gf, - libc6, +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libgcc1, libgfortran3, - liblapack3gf, libstdc++6, perl, ion-gpu (>=2.5.0)") diff --git a/Analysis/Calibration/Calibration.cpp b/Analysis/Calibration/Calibration.cpp index 88421c79..64a49ab4 100644 --- a/Analysis/Calibration/Calibration.cpp +++ b/Analysis/Calibration/Calibration.cpp @@ -117,7 +117,7 @@ int main (int argc, const char *argv[]) l_thread_time = ExecuteThreadedCalibrationTraining(calib_context); // Activate master linear model after every round of training - master_linear_model.CreateCalibrationModel(false); // make linear model + master_linear_model.CreateCalibrationModel(false); // make linear model master_linear_model.SetModelGainsAndOffsets(); // expand for use in basecalling calibration_thread_time += l_thread_time; diff --git a/Analysis/mixed.cpp b/Analysis/ClonalFilter/mixed.cpp similarity index 97% rename from Analysis/mixed.cpp rename to Analysis/ClonalFilter/mixed.cpp index 675dbf52..f9ac2339 100644 --- a/Analysis/mixed.cpp +++ b/Analysis/ClonalFilter/mixed.cpp @@ -212,7 +212,16 @@ bool fit_normals(vec mean[2], mat sgma[2], vec& alpha, const deque& ppf, // Initial guesses for two normal distributions: init(mean, sgma, alpha); if (opts.verbose) - print_dist(mean, sgma, alpha, converged, 0, opts.mixed_model_option); + print_dist(mean, sgma, alpha, converged, 0, opts.mixed_model_option); // XXX + + // increased stablity of iterative method: + // Add two points to the ppf, ssp deqeue corresponding to the a-priori cluster centers + std::deque my_ppf(ppf); + my_ppf.push_front(mean[0][0]); + my_ppf.push_front(mean[1][0]); + std::deque my_ssq(ssq); + my_ssq.push_front(mean[0][1]); + my_ssq.push_front(mean[1][1]); int max_iters = opts.max_iterations; int iteration = 1; @@ -225,7 +234,7 @@ bool fit_normals(vec mean[2], mat sgma[2], vec& alpha, const deque& ppf, mat new_sgma[2]; vec new_alpha; - calcMeanCovariance(new_sgma, new_mean, new_alpha, mean, sgma, alpha, ppf, ssq, opts.mixed_model_option); + calcMeanCovariance(new_sgma, new_mean, new_alpha, mean, sgma, alpha, my_ppf, my_ssq, opts.mixed_model_option); if(not is_pos_def(sgma[0]) or not is_pos_def(sgma[1])) { not_pos_def = true; @@ -290,10 +299,11 @@ void calcMeanCovariance(mat new_sgma[2], vec new_mean[2], vec &new_alpha, const case 0:{//common covariance bivariate_gaussian clone_dist(mean[0], sgma[0]); bivariate_gaussian mixed_dist(mean[1], sgma[1]); + // Re-estimate parameters for each distribution: int nsamp = ppf.size(); vec sumw(2); - sumw.fill(0.0); + sumw.fill(0.5); mat sum2(2,2); sum2.fill(0.0); @@ -317,11 +327,11 @@ void calcMeanCovariance(mat new_sgma[2], vec new_mean[2], vec &new_alpha, const vec q(2); q[0] = alpha[0] * clone_dist.pdf(x); q[1] = alpha[1] * mixed_dist.pdf(x); - vec w = q / sum(q); // Skip outliers: - if(not w.is_finite()) - continue; + if(sum(q) < 1e-20) + continue; + vec w = q / sum(q); // Running sums for moments are weighted: sumw += w; @@ -647,15 +657,3 @@ ostream& operator<<(ostream& out, const filter_counts& c) //Is it possible to try different models and find the one fitting the data best? -PolyclonalFilterOpts::PolyclonalFilterOpts() -{ - mixed_first_flow = 12; - mixed_last_flow = 72; - max_iterations = 30; - mixed_model_option = 0; - mixed_stringency = 0.5; - use_last_iter_params = true; - verbose = false; - filter_extreme_ppf_only = false; -} - diff --git a/Analysis/mixed.h b/Analysis/ClonalFilter/mixed.h similarity index 93% rename from Analysis/mixed.h rename to Analysis/ClonalFilter/mixed.h index 332993fa..1d6f622d 100644 --- a/Analysis/mixed.h +++ b/Analysis/ClonalFilter/mixed.h @@ -102,7 +102,11 @@ class clonal_filter if(ppf <_ppf_cutoff){ double clonal_pdf = _clonal.pdf (x); double mixed_pdf = _mixed.pdf (x); - return clonal_pdf / (mixed_pdf + clonal_pdf) > stringency; + // Avoid divide by zero: If mixed probablity is too small, we'll just call it clonal. + if (mixed_pdf < 1e-256) + return true; + else + return clonal_pdf / (mixed_pdf + clonal_pdf) > stringency; } else return false; diff --git a/Analysis/ClonalFilter/polyclonal_filter.cpp b/Analysis/ClonalFilter/polyclonal_filter.cpp new file mode 100644 index 00000000..2bfe0ef6 --- /dev/null +++ b/Analysis/ClonalFilter/polyclonal_filter.cpp @@ -0,0 +1,107 @@ +/* Copyright (C) 2016 Thermo Fisher Scientific, All Rights Reserved */ +#include "polyclonal_filter.h" + +PolyclonalFilterOpts::PolyclonalFilterOpts() +{ + SetDefaults(); +} + + +void PolyclonalFilterOpts::PrintHelp(bool analysis_call) +{ + printf (" Polyclonal Filter Options:\n"); + + if (analysis_call) + printf (" --clonal-filter-bkgmodel BOOL enable polyclonal filter during signal processing [on]\n"); + else{ + printf (" --clonal-filter-solve on/off apply polyclonal filter [off]\n"); + printf (" --clonal-filter-tf on/off apply polyclonal filter to TFs [off]\n"); + printf (" --clonal-filter-maxreads INT maximum number of library reads used for polyclonal filter training [100000]\n"); + } + + printf (" --mixed-first-flow INT mixed first flow of polyclonal filter [12]\n"); + printf (" --mixed-last-flow INT mixed last flow of polyclonal filter [72]\n"); + printf (" --max-iterations INT max iterations of polyclonal filter [30]\n"); + printf (" --mixed-model-option INT mixed model option of polyclonal filter [0]\n"); + printf (" --mixed-stringency DOUBLE mixed stringency of polyclonal filter [0.5]\n"); + printf (" --clonal-filter-debug BOOL enable polyclonal filter debug output [off]\n"); + printf (" --clonal-filter-use-last-iter-params BOOL use last EM iteration cluster parameters if no convergence [on]\n"); + printf (" --filter-extreme-ppf-only BOOL Skip polyclonal filter training and filter for extreme ppf only [off]\n"); + printf ("\n"); +} + +//----------------------------------------------------------------------------- + +void PolyclonalFilterOpts::SetDefaults() +{ + enable = false; + + filter_clonal_enabled_tfs = false; + filter_clonal_enabled_lib = false; + filter_clonal_maxreads = 100000; + + mixed_first_flow = 12; + mixed_last_flow = 72; + max_iterations = 30; + mixed_model_option = 0; + mixed_stringency = 0.5; + use_last_iter_params = true; + verbose = false; + filter_extreme_ppf_only = false; +} + +//----------------------------------------------------------------------------- + +void PolyclonalFilterOpts::Disable() +{ + enable = false; + filter_clonal_enabled_tfs = false; + filter_clonal_enabled_lib = false; +} + +//----------------------------------------------------------------------------- + +void PolyclonalFilterOpts::SetOpts(bool analysis_call, OptArgs &opts, Json::Value& json_params, int num_flows) +{ + SetDefaults(); + + // Analysis only option + if (analysis_call) + enable = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-bkgmodel", true); + else { + // Basecaller only option + filter_clonal_enabled_tfs = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-tf", false); + filter_clonal_enabled_lib = RetrieveParameterBool(opts, json_params,'-', "clonal-filter-solve", false); + filter_clonal_maxreads = RetrieveParameterInt (opts, json_params,'-', "clonal-filter-maxreads", 100000); + enable = filter_clonal_enabled_tfs or filter_clonal_enabled_lib; + } + + // Joint Analysis and BaseCaller Options + mixed_first_flow = RetrieveParameterInt(opts, json_params, '-', "mixed-first-flow", 12); + mixed_last_flow = RetrieveParameterInt(opts, json_params, '-', "mixed-last-flow", 72); + // Check prevents memory overrun and segfaults + if (mixed_last_flow > num_flows){ + cerr << "PolyclonalFilterOpts WARNING: mixed-last-flow is larger than number of flows in the run: " << num_flows << ". Disabling polyclonal filter." << endl; + Disable(); + } + + max_iterations = RetrieveParameterInt(opts, json_params, '-', "max-iterations", 30); + mixed_model_option = RetrieveParameterInt(opts, json_params, '-', "mixed-model-option", 0); + verbose = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-debug", false); + use_last_iter_params = RetrieveParameterBool(opts, json_params, '-', "clonal-filter-use-last-iter-params", true); + filter_extreme_ppf_only = RetrieveParameterBool(opts, json_params, '-', "filter-extreme-ppf-only", false); + + // Transform stringency to log scale + double stringency = min(1.0, max(0.0, RetrieveParameterDouble(opts, json_params, '-', "mixed-stringency", 0.5))); + if(stringency > 0.5){ + mixed_stringency = 0.5 * std::log10((stringency - 0.5)*18 + 1) + 0.5; + } + else if (stringency < 0.5){ + mixed_stringency = 0.5 - 0.5 * std::log10(( 0.5 - stringency)*18 + 1); + } + else { + mixed_stringency = 0.5; + } + +} + diff --git a/Analysis/ClonalFilter/polyclonal_filter.h b/Analysis/ClonalFilter/polyclonal_filter.h new file mode 100644 index 00000000..68464dc0 --- /dev/null +++ b/Analysis/ClonalFilter/polyclonal_filter.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Ion Torrent Systems, Inc. All Rights Reserved */ + +#ifndef POLYCLONAL_FILTER_H +#define POLYCLONAL_FILTER_H + +#include "json/json.h" +#include "IO/OptBase.h" +#include "Util/Serialization.h" + +class PolyclonalFilterOpts { + public: + PolyclonalFilterOpts(); + + static void PrintHelp(bool anaysis_call); + + bool enable; + bool filter_clonal_enabled_tfs; + bool filter_clonal_enabled_lib; + int filter_clonal_maxreads; + int mixed_first_flow; + int mixed_last_flow; + int max_iterations; + int mixed_model_option; + double mixed_stringency; + bool verbose; + bool use_last_iter_params; + bool filter_extreme_ppf_only; + + void SetOpts(bool analysis_call, OptArgs &opts, Json::Value& json_params, int num_flows); + void Disable(); + +private: + + void SetDefaults(); + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) + { + ar + & enable + & filter_clonal_enabled_tfs + & filter_clonal_enabled_lib + & filter_clonal_maxreads + & mixed_first_flow + & mixed_last_flow + & max_iterations + & mixed_model_option + & mixed_stringency + & verbose + & use_last_iter_params + & filter_extreme_ppf_only; + } +}; + +#endif // POLYCLONAL_FILTER_H diff --git a/Analysis/DataViewer/AlignmentSpatial.cpp b/Analysis/DataViewer/AlignmentSpatial.cpp new file mode 100644 index 00000000..45be8046 --- /dev/null +++ b/Analysis/DataViewer/AlignmentSpatial.cpp @@ -0,0 +1,370 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "AlignmentSpatial.h" +#include +#include +#include +#include "Calibration/FlowAlignment.h" + +using namespace std; +using namespace BamTools; + + +AlignmentSpatial::AlignmentSpatial(QWidget *parent): SpatialPlot(parent) +{ +} + + +// copied from ion_util.c +int ion_readname_to_xy(const char *readname, int *x, int *y) +{ + int i, val, state; + /* states: + 0 - skipping over read name (before first colon) + 1 - reading in x value (before second colon) + 2 - reading in y value (after second colon) + */ + for(i=val=state=0;'\0' != readname[i];i++) { + if(':' == readname[i]) { + if(1 == state) { + (*y) = val; + } + state++; + val = 0; + } + else if('0' <= readname[i] && readname[i] <= '9') { + val *= 10; + val += (int32_t)(readname[i] - '0'); + } + } + if(2 == state) { + (*x) = val; + return 1; + } + else { + return 0; + } +} + +void AlignmentSpatial::SetOption(QString txt, int state) +{ + qDebug() << __PRETTY_FUNCTION__ << ": " << txt << state; + + if(txt == "Mask UnAligned") + SetMask(0,state); + else if(txt == "Mask Aligned") + SetMask(1,state); + + render(); +} + +void AlignmentSpatial::SetMask(int aligned, int state) +{ + if(mMask){ + + // set the global mask bits for aligned or unaligned reads + if(!state){ + // clear the mask bits for all reads + for(int i=0;i<(rows*cols);i++) + mMask[i] &= ~0x1; + }else{ + for(int i=0;i<(rows*cols);i++){ + if((aligned && (out[i] > 0)) || (!aligned && (out[i] == 0))) + mMask[i] |= 0x1; + } + } + } +} + +void AlignmentSpatial::doConvert(int &loading) +{ + + if(fname == ""){ + free(out); + out=NULL; + last_fname = _fname; + traces_len=0; + } + if (last_fname != _fname && !loading){ + loading=0; + // load a new BAM file + printf("triggered %s %s\n",last_fname.toLatin1().data(),_fname.toLatin1().data()); + fflush(stdout); + last_fname = _fname; + if(out) + free(out); + out=NULL; + + // region size + int nbytes=sizeof(out[0])*rows*cols; + out = (int32_t *)(int32_t *)malloc(nbytes); + +// char tmpName[4096]; +// strcpy(tmpName,fname.toStdString().data()); +// char *ptr = tmpName; +// char *last=NULL; +// while((ptr = strstr(ptr,"/"))){ +// last = ptr; +// ptr++; +// } +// if(last) +// *last = 0; + + _fname = fname;//QString(tmpName); + QDir dir(_fname); + qDebug() << "Dir: " << _fname; + foreach(QFileInfo item, dir.entryInfoList() ){ + if(item.isFile()){ + QString ifn = item.canonicalFilePath(); + char EntryName[4096]; + strcpy(EntryName,ifn.toUtf8().constData()); + qDebug() << "File: " << ifn << " - " << EntryName; +// printf(" File: %s\n",EntryName); + fflush(stdout); + char *bamPtr = strstr(EntryName,".bam"); + if(bamPtr && bamPtr[4] == 0){ + printf("Loading: %s\n",EntryName); + fflush(stdout); + ReadFile(EntryName); + } + } + } + +// ReadFile(fn); + + traces_len=1; + for(int i=0;iLoadFile(cols, rows, Block_X, Block_Y, out, fn, &loading, &seqIndx, &alignments); + if(gbl > (sizeof(threads)/sizeof(threads[0]))) + gbl=0; + +#if 0 + BamReader reader; + if ( !reader.Open(fn) ) { + printf("Could not open input BAM file %s\n",fn); + loading=0; + return; + } + + // region position + int RawExpmt_Y=Block_Y; + int RawExpmt_X=Block_X; + + BamAlignment al; + while ( reader.GetNextAlignment(al) ) { + int x,y; + ion_readname_to_xy(al.Name.c_str(), &x, &y); + int region_x = x - RawExpmt_X; + int region_y = y - RawExpmt_Y; + if (region_x >= 0 && region_x < cols && region_y >=0 && region_y < rows){ + out[region_y*cols + region_x] = al.AlignedBases.length(); + //cout << x << ":" << y << " length " << al.Length << endl; + } + } + reader.Close(); +#endif +} + +float AlignmentSpatial::Get_Data(int frame, int y, int x) +{ + (void)frame; + float rc = 0; + + if(out && y < rows && x < cols){ + rc = out[/*frame*rows*cols +*/ y*cols + x]; + } + return rc; +} + +BamAlignment* AlignmentSpatial::Get_Alignment(int y, int x) +{ + int found = std::find(seqIndx.begin(), seqIndx.end(), y*cols + x) - seqIndx.begin(); + if (found < (int)seqIndx.size()){ + return &alignments[found]; + } + return NULL; +} + +void AlignmentSpatial::Get_ReferenceSeq(BamAlignment *al, string &qseq, string &tseq){ + // tseq: refernce (target) bases for aligned portion of the read + // qseq: read (query) bases for aligned portion of the read + string md; + string tseq_bases; + string qseq_bases; + string pretty_tseq; + string pretty_qseq; + string pretty_aln; + unsigned int left_sc, right_sc; + + al->GetTag("MD", md); + RetrieveBaseAlignment(al->QueryBases, al->CigarData, md, tseq_bases, qseq_bases, + pretty_tseq, pretty_qseq, pretty_aln, left_sc, right_sc); + qseq = pretty_qseq; + tseq = pretty_tseq; +} + +void AlignmentSpatial::UpdateTracePlot() +{ + printf("%s\n",__FUNCTION__); + + QFont myfont("QFont::Courier", 8); + myfont.setStyleHint(QFont::TypeWriter); + _mTracePlot->clearItems(); + + if (traces_len) + { + for(int trc=0;trc=0) && (traces[trc].y > 0)) + { + // draw graph line + QVector x(2); + QVector y(2); + x[0] = 0; x[1] = 0.2; + y[0] = y[1] = MAX_NUM_TRACES - trc; + + _mTracePlot->graph(trc)->setData(x, y); + QString name="Y" + QString::number(traces[trc].x) + "_X" + QString::number(traces[trc].y); + _mTracePlot->graph(trc)->setName(name); + _mTracePlotSet[trc]=1; + + // add sequence text + BamAlignment *al= Get_Alignment(traces[trc].y, traces[trc].x); + if (al){ + QCPItemText *seqText = new QCPItemText(_mTracePlot); + _mTracePlot->addItem(seqText); + seqText->position->setCoords(x[1], y[0]); + seqText->setFont(myfont); + seqText->setPositionAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + string qseq, tseq; + Get_ReferenceSeq(al, qseq, tseq); + string text = qseq + "\n" + tseq; + cout << text << endl; + seqText->setText(text.c_str()); + } + } + } + } + + _mTracePlot->xAxis->setRange(0, 10); + _mTracePlot->yAxis->setRange(0, MAX_NUM_TRACES+0.5); + _mTracePlot->replot(); +} + + +AlignmentRdrThread::AlignmentRdrThread(QObject *parent) + : QThread(parent) +{ +} + +//! [0] + +//! [1] +AlignmentRdrThread::~AlignmentRdrThread() +{ + mutex.lock(); + abort = true; + condition.wakeOne(); + mutex.unlock(); + + wait(); +} +//! [1] + + +//! [2] +void AlignmentRdrThread::LoadFile(int _w, int _h, int _startX, int _startY, int32_t *_out, char *_fileName, int *_loading, vector *_seqIndx, vector *_alignments) +{ + w=_w; + h=_h; + startX=_startX; + startY=_startY; + out=_out; + loading=_loading; + seqIndx= _seqIndx; + alignments= _alignments; + + strcpy(fileName,_fileName); + + QMutexLocker locker(&mutex); + + if (!isRunning()) { + __sync_add_and_fetch(loading,1); + start(LowPriority); + } else { + //restart = true; + condition.wakeOne(); + } +} +//! [2] + +void AlignmentRdrThread::Abort() +{ + abort=true; + mutex.unlock(); +} + +void AlignmentRdrThread::run() +{ +// forever { +// mutex.lock(); + if(abort) + return; // application is closed +// mParent->copyData(); +// mutex.unlock(); + + + BamReader reader; + if ( !reader.Open(fileName) ) { + printf("Could not open input BAM file %s\n",fileName); + //loading=0; + //return; + } + else{ + + // region position +// int RawExpmt_Y=Block_Y; +// int RawExpmt_X=Block_X; + + BamAlignment al; + while ( !abort && reader.GetNextAlignment(al) ) { + int x,y; + ion_readname_to_xy(al.Name.c_str(), &x, &y); + int region_x = x - startX; + int region_y = y - startY; + if (region_x >= 0 && region_x < w && region_y >=0 && region_y < h){ + out[region_y*w + region_x] = al.AlignedBases.length(); +// seqIndx->push_back(region_y*w + region_x); +// alignments->push_back(al); + } + } + reader.Close(); + printf("done loading %s\n",fileName); + } + + __sync_sub_and_fetch (loading,1); + + + +// mutex.lock(); +// if (!restart) +// condition.wait(&mutex); +// restart = false; +// mutex.unlock(); +// } +} + + + diff --git a/Analysis/DataViewer/AlignmentSpatial.h b/Analysis/DataViewer/AlignmentSpatial.h new file mode 100644 index 00000000..ea21a57f --- /dev/null +++ b/Analysis/DataViewer/AlignmentSpatial.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef ALIGNMENTSPATIAL_H +#define ALIGNMENTSPATIAL_H + +#include +#include +#include "SpatialPlot.h" +#include "qcustomplot.h" +#include "api/BamReader.h" + +class AlignmentRdrThread; + +class AlignmentSpatial : public SpatialPlot +{ +public: + AlignmentSpatial(QWidget *parent); + void doConvert(int &loading); + void SetOption(QString option, int state); + +protected: + float Get_Data(int frame, int y, int x); + void ReadFile(char *fn); + void UpdateTracePlot(); + +private: + int32_t *out=NULL; + AlignmentRdrThread *threads[100]={NULL}; + std::vector seqIndx; + std::vector alignments; + BamTools::BamAlignment *Get_Alignment(int y, int x); + void Get_ReferenceSeq(BamTools::BamAlignment *al, std::string &qseq, std::string &tseq); + void SetMask(int aligned, int state); +}; + + +class AlignmentRdrThread : public QThread +{ +// Q_OBJECT + +public: + AlignmentRdrThread(QObject *parent = 0); + ~AlignmentRdrThread(); + void Abort(); + void LoadFile(int w, int h, int startX, int startY, int32_t *out, char *_fileName, int *_loading, std::vector *_seqIndx, std::vector *_alignments); +// void SetParent(SpatialPlot *_parent); + + +signals: +// void renderedImage(const QImage &image); + +protected: + void run() Q_DECL_OVERRIDE; + +private: + + int w; + int h; + int startX; + int startY; + int32_t *out; + int *loading=NULL; + char fileName[4096]; + std::vector *seqIndx; + std::vector *alignments; + + + QMutex mutex; + QWaitCondition condition; + bool abort=0; +}; + + +#endif // ALIGNMENTSPATIAL_H diff --git a/Analysis/DataViewer/AlignmentTab.cpp b/Analysis/DataViewer/AlignmentTab.cpp new file mode 100644 index 00000000..2cdddde6 --- /dev/null +++ b/Analysis/DataViewer/AlignmentTab.cpp @@ -0,0 +1,32 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "AlignmentTab.h" + +static const maskCheckBox_t mcbt1[] = { + {"Mask UnAligned",1,Qt::Unchecked}, + {"Mask Aligned",2,Qt::Unchecked} +}; + +AlignmentTab::AlignmentTab(QString _mName, Dialog *_mParent, QWidget *parent) + : ModelTab(_mName, _mParent, parent) +{ + mSpatialPlot = new AlignmentSpatial(this); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(MakeSlider("flows"),0,0); + grid->addWidget(MakeTraceCB(),1,0); + grid->addWidget(MakeList(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])),0,1,2,1); + grid->setRowStretch(0,0); + grid->setRowStretch(1,0); + + grid->addWidget(mSpatialPlot,2,0,1,4); + grid->addWidget(MakeTracePlot(1),3,0,1,4); + + // Set the graph styles to no line + for(int i=0;igraph(i)->setLineStyle(QCPGraph::lsNone); + mtracePlot->graph(i)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle,4)); + } + + setLayout(grid); +} + diff --git a/Analysis/DataViewer/AlignmentTab.h b/Analysis/DataViewer/AlignmentTab.h new file mode 100644 index 00000000..bd742308 --- /dev/null +++ b/Analysis/DataViewer/AlignmentTab.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef ALIGNMENTTAB_H +#define ALIGNMENTTAB_H + +#include +#include +#include "dialog.h" +#include "modeltab.h" +#include "AlignmentSpatial.h" + +class Dialog; + +class AlignmentTab : public ModelTab +{ + Q_OBJECT +public: + explicit AlignmentTab(QString _mName, Dialog *_mParent, QWidget *parent = 0); + +signals: + +public slots: + +private: + +}; + +#endif // ALIGNMENTTAB_H diff --git a/Analysis/DataViewer/BfMaskSpatial.cpp b/Analysis/DataViewer/BfMaskSpatial.cpp new file mode 100644 index 00000000..d27b111b --- /dev/null +++ b/Analysis/DataViewer/BfMaskSpatial.cpp @@ -0,0 +1,125 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "BfMaskSpatial.h" +#include "RawWells.h" +#include +#include + +using namespace std; + +BfMaskSpatial::BfMaskSpatial(QWidget *parent): SpatialPlot(parent) +{ +} + +void BfMaskSpatial::addMask(uint mask) +{ + qDebug() << __PRETTY_FUNCTION__ << ": " << mask; + mMaskVal |= mask; + render(); +} + +void BfMaskSpatial::RemoveMask(uint mask) +{ + qDebug() << __PRETTY_FUNCTION__ << ": " << mask; + mMaskVal &= ~mask; + render(); +} + +void BfMaskSpatial::SetMask(int inverted, int state) +{ + if(mMask){ + // set the global mask bits for aligned or unaligned reads + if(!state){ + // clear the mask bits for all reads + for(int i=0;i<(rows*cols);i++) + mMask[i] &= ~0x2; + }else{ + for(int i=0;i<(rows*cols);i++){ + if((inverted && ((out[i] & mMaskVal) == 0)) || (!inverted && (out[i] & mMaskVal))) + mMask[i] |= 0x2; + } + } + } +} + +void BfMaskSpatial::doConvert(int &loading) +{ + if(fname == ""){ + free(out); + out=refMask=NULL; + last_fname = _fname; + traces_len=0; + } + if (last_fname != _fname){ + loading=1; + // load a new dat file + printf("triggered %s %s\n",last_fname.toLatin1().data(),_fname.toLatin1().data()); + last_fname = _fname; + if(out) + free(out); + out=refMask=NULL; + + QByteArray ba = fname.toLatin1(); + char * fn = ba.data(); + + { + //Note, fillMask is optional argument w/ default of true. + FILE *in = fopen ( fn, "rb" ); + if(in){ + int elements_read = fread ( &rows, sizeof ( rows ), 1, in ); + assert ( elements_read == 1 ); + elements_read = fread ( &cols, sizeof ( cols ), 1, in ); + assert ( elements_read == 1 ); + int nbytes = sizeof(out[0])*rows*cols; + out = refMask = (uint16_t *)malloc(nbytes); + elements_read = fread ( out, nbytes, 1, in ); + assert ( elements_read == 1 ); + fclose ( in ); + } + } + traces_len=2; + for(int i=0;i +#include "SpatialPlot.h" +#include "qcustomplot.h" +#include "modeltab.h" + + +class BfMaskSpatial : public SpatialPlot +{ +public: + BfMaskSpatial(QWidget *parent); + void doConvert(int &loading); + void addMask(uint mask); + void RemoveMask(uint mask); + void SetOption(QString txt, int state); + void SetOptions(const maskCheckBox_t *_mcbt, int _mNitems); + +protected: + virtual float Get_Data(int idx, int y, int x); + +private: + void SetMask(int aligned, int state); + uint16_t *out=NULL; + uint mMaskVal=0; + const maskCheckBox_t *mcbt=NULL; + int mNitems=-1; +}; + +#endif // BFMASKSPATIAL_H diff --git a/Analysis/DataViewer/BfMaskTab.cpp b/Analysis/DataViewer/BfMaskTab.cpp new file mode 100644 index 00000000..d2ba6765 --- /dev/null +++ b/Analysis/DataViewer/BfMaskTab.cpp @@ -0,0 +1,49 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "BfMaskTab.h" +#include "Mask.h" + + + +static const maskCheckBox_t mcbt1[] = { + {"MaskEmpty",0,Qt::Checked}, + {"MaskBead",1,Qt::Checked}, + {"MaskLive",2,Qt::Checked}, + {"MaskDud",3,Qt::Checked}, + {"MaskReference",4,Qt::Checked}, + {"MaskTF",5,Qt::Checked}, + {"MaskLib",6,Qt::Checked}, + {"MaskPinned",7,Qt::Unchecked}, + {"MaskIgnore",8,Qt::Unchecked}, + {"MaskWashout",9,Qt::Unchecked}, + {"MaskExclude",10,Qt::Unchecked}, +// {"MaskKeypass",11,Qt::Unchecked}, +// {"MaskFilteredBadKey",12,Qt::Unchecked}, +// {"MaskFilteredShort",13,Qt::Unchecked}, +// {"MaskFilteredBadPPF",14,Qt::Unchecked}, +// {"MaskFilteredBadResidual",15,Qt::Unchecked} + {"ApplyMask",16,Qt::Unchecked}, + {"ApplyInvMask",17,Qt::Unchecked}, + +}; + +BfMaskTab::BfMaskTab(QString mName, Dialog *_mParent, QWidget *parent) + : ModelTab(mName,_mParent,parent) +{ + mBfSpatialPlot = new BfMaskSpatial(this); + mBfSpatialPlot->SetOptions(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])); + mSpatialPlot = mBfSpatialPlot; + + QGridLayout *grid = new QGridLayout; + grid->addWidget(MakeSlider(""),0,0); + grid->addWidget(MakeTraceCB(),1,0); + grid->addWidget(MakeList(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])),0,1,2,1); + grid->setRowStretch(0,0); + grid->setRowStretch(1,0); + grid->addWidget(mSpatialPlot,2,0,1,4); + grid->addWidget(MakeTracePlot(),3,0,1,4); + setLayout(grid); + +} + + + diff --git a/Analysis/DataViewer/BfMaskTab.h b/Analysis/DataViewer/BfMaskTab.h new file mode 100644 index 00000000..abda9348 --- /dev/null +++ b/Analysis/DataViewer/BfMaskTab.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef BFMASKTAB_H +#define BFMASKTAB_H + +#include +#include +#include "dialog.h" +#include "modeltab.h" +#include "BfMaskSpatial.h" + +class Dialog; + +class BfMaskTab : public ModelTab +{ + Q_OBJECT +public: + explicit BfMaskTab(QString mName, Dialog *_mParent, QWidget *parent = 0); + +signals: + +public slots: + +private: + BfMaskSpatial *mBfSpatialPlot; +}; + +#endif // BFMASKTAB_H diff --git a/Analysis/DataViewer/DataViewer.pro b/Analysis/DataViewer/DataViewer.pro new file mode 100644 index 00000000..4a762da2 --- /dev/null +++ b/Analysis/DataViewer/DataViewer.pro @@ -0,0 +1,64 @@ +QT += widgets core gui printsupport + +CONFIG += c++11 +INCLUDEPATH += /usr/include/hdf5/serial/ ../ ../Util/ ../Mask/ ../TsInputUtil/ ../Wells/ ../Image/ ../../build/bamtools-2.4.0.20150702+git15eadb925f-install/include/bamtools/ ../VariantCaller/Bookkeeping/ + +QMAKE_CXXFLAGS += -mavx + +HEADERS = \ + dialog.h \ + AlignmentTab.h \ + AlignmentSpatial.h \ + BfMaskTab.h \ + RawTab.h \ + qcustomplot.h \ + SpatialPlot.h \ + RawSpatial.h \ + WellsTab.h \ + WellsSpatial.h \ + BfMaskSpatial.h \ + NoiseTab.h \ + NoiseSpatial.h \ + GainSpatial.h \ + GainTab.h \ + modeltab.h + + +SOURCES = main.cpp \ + dialog.cpp \ + AlignmentTab.cpp \ + BfMaskTab.cpp \ + RawTab.cpp \ + qcustomplot.cpp \ + SpatialPlot.cpp \ + RawSpatial.cpp \ + WellsTab.cpp \ + WellsSpatial.cpp \ + ../Image/CorrNoiseCorrector.cpp \ + ../Image/ComparatorNoiseCorrector.cpp \ + ../Image/AdvCompr.cpp \ + ../Image/PCACompression.cpp \ + ../Image/deInterlace.cpp \ + ../Image/ChipIdDecoder.cpp \ + ../Wells/RawWells.cpp \ + ../Wells/RawWellsV1.cpp \ + ../Util/IonErr.cpp \ + ../Util/Utils.cpp \ + ../Util/IonH5File.cpp \ + ../LinuxCompat.cpp \ + ../TsInputUtil/IonVersion.cpp \ + BfMaskSpatial.cpp \ + NoiseTab.cpp \ + NoiseSpatial.cpp \ + GainSpatial.cpp \ + GainTab.cpp \ + AlignmentSpatial.cpp \ + ../Calibration/FlowAlignment.cpp \ + modeltab.cpp + + +unix:!mac:!vxworks:!integrity:!haiku:LIBS += -lm -lhdf5_serial "../../build/bamtools-2.4.0.20150702+git15eadb925f-install/lib/bamtools/libbamtools.a" -lz + +# install +#target.path = DataViewer +#INSTALLS += target diff --git a/Analysis/DataViewer/DataViewer.pro.user b/Analysis/DataViewer/DataViewer.pro.user new file mode 100644 index 00000000..5f0214e6 --- /dev/null +++ b/Analysis/DataViewer/DataViewer.pro.user @@ -0,0 +1,271 @@ + + + + + + EnvironmentId + {ebf36358-ae97-47f9-9618-542ce8c93f84} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {9cc9bd26-12f2-4344-84f8-4f85402018cb} + 0 + 0 + 0 + + /home/ionadmin/dev/torrent-suite/Analysis/build-DataViewer-Desktop-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /home/ionadmin/dev/torrent-suite/Analysis/build-DataViewer-Desktop-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 2 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy locally + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + DataViewer + + Qt4ProjectManager.Qt4RunConfiguration:/home/ionadmin/dev/torrent-suite/Analysis/DataViewer/DataViewer.pro + + DataViewer.pro + false + false + + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/Analysis/DataViewer/GainSpatial.cpp b/Analysis/DataViewer/GainSpatial.cpp new file mode 100644 index 00000000..acb9bc71 --- /dev/null +++ b/Analysis/DataViewer/GainSpatial.cpp @@ -0,0 +1,102 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "GainSpatial.h" +#include "RawWells.h" +#include +#include + +using namespace std; + +GainSpatial::GainSpatial(QWidget *parent): SpatialPlot(parent) +{ +} + +void GainSpatial::addMask(uint mask) +{ + mMask |= mask; + render(); +} + +void GainSpatial::RemoveMask(uint mask) +{ + mMask &= ~mask; + render(); +} + +void GainSpatial::doConvert(int &loading) +{ + if(fname == ""){ + free(out); + out=gainVals=NULL; + last_fname = _fname; + traces_len=0; + } + if (last_fname != _fname){ + loading=1; + // load a new dat file + printf("triggered %s %s\n",last_fname.toLatin1().data(),_fname.toLatin1().data()); + last_fname = _fname; + if(out) + free(out); + out=gainVals=NULL; + + QByteArray ba = fname.toLatin1(); + char * fn = ba.data(); + + { + //Note, fillMask is optional argument w/ default of true. + FILE *in = fopen ( fn, "rb" ); + if(in){ + int magic=0, version=0, elements_read=0; + int lrows=0,lcols=0; + + elements_read = fread ( &magic, sizeof ( magic ), 1, in ); + if( (elements_read == 1) && ((uint)magic == 0xFF115E3A)){ + elements_read = fread ( &version, sizeof ( version ), 1, in ); + if( elements_read == 1 ){ + elements_read = fread ( &lrows, sizeof ( rows ), 1, in ); + if( elements_read == 1 ){ + elements_read = fread ( &lcols, sizeof ( cols ), 1, in ); + if( elements_read == 1 ){ + int nbytes = sizeof(out[0])*rows*cols; + out = gainVals = (float *)malloc(nbytes); + elements_read = fread ( out, nbytes, 1, in ); + if( elements_read == 1 ){ + // success.. + if((cols ==0 || cols == lcols) && + (rows == 0 || rows == lrows)){ + cols = lcols; + rows = lrows; + } + else{ + free(out); + out=NULL; + } + } + } + } + } + } + fclose ( in ); + } + } + traces_len=2; + for(int i=0;i +#include "SpatialPlot.h" +#include "qcustomplot.h" + +class GainSpatial : public SpatialPlot +{ +public: + GainSpatial(QWidget *parent); + void doConvert(int &loading); + void addMask(uint mask); + void RemoveMask(uint mask); + +protected: + virtual float Get_Data(int idx, int y, int x); + +private: + float *out=NULL; + uint mMask=0; +}; + +#endif // GAINSPATIAL_H diff --git a/Analysis/DataViewer/GainTab.cpp b/Analysis/DataViewer/GainTab.cpp new file mode 100644 index 00000000..35dab684 --- /dev/null +++ b/Analysis/DataViewer/GainTab.cpp @@ -0,0 +1,27 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "GainTab.h" +#include "Mask.h" + + +static const maskCheckBox_t mcbt1[] = { +}; + + +GainTab::GainTab(QString _mName, Dialog *_mParent, QWidget *parent) + : ModelTab(_mName, _mParent,parent) +{ + mGainSpatialPlot = new GainSpatial(this); + mSpatialPlot = mGainSpatialPlot; + + QGridLayout *grid = new QGridLayout; + grid->addWidget(MakeSlider(""),0,0); + grid->addWidget(MakeTraceCB(),1,0); + grid->addWidget(MakeList(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])),0,1,2,1); + grid->setRowStretch(0,0); + grid->setRowStretch(1,0); + grid->addWidget(mSpatialPlot,2,0,1,4); + grid->addWidget(MakeTracePlot(),3,0,1,4); + setLayout(grid); +} + + diff --git a/Analysis/DataViewer/GainTab.h b/Analysis/DataViewer/GainTab.h new file mode 100644 index 00000000..33562a9c --- /dev/null +++ b/Analysis/DataViewer/GainTab.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef GAINTAB_H +#define GAINTAB_H + +#include +#include +#include "dialog.h" +#include "GainSpatial.h" +#include "modeltab.h" + +class Dialog; + +class GainTab : public ModelTab +{ + Q_OBJECT +public: + explicit GainTab(QString _mName, Dialog *_mParent, QWidget *parent = 0); + +public slots: + +private: + GainSpatial *mGainSpatialPlot=NULL; +}; + +#endif // GAINTAB_H diff --git a/Analysis/DataViewer/MainForm.ui.qml b/Analysis/DataViewer/MainForm.ui.qml new file mode 100644 index 00000000..f2cd99ee --- /dev/null +++ b/Analysis/DataViewer/MainForm.ui.qml @@ -0,0 +1,29 @@ +import QtQuick 2.7 + +Rectangle { + property alias mouseArea: mouseArea + property alias textEdit: textEdit + + width: 360 + height: 360 + + MouseArea { + id: mouseArea + anchors.fill: parent + } + + TextEdit { + id: textEdit + text: qsTr("Enter some text...") + verticalAlignment: Text.AlignVCenter + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 20 + Rectangle { + anchors.fill: parent + anchors.margins: -10 + color: "transparent" + border.width: 1 + } + } +} diff --git a/Analysis/DataViewer/NoiseSpatial.cpp b/Analysis/DataViewer/NoiseSpatial.cpp new file mode 100644 index 00000000..342ebea0 --- /dev/null +++ b/Analysis/DataViewer/NoiseSpatial.cpp @@ -0,0 +1,100 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "NoiseSpatial.h" +#include "RawWells.h" +#include "dialog.h" +#include +#include + +using namespace std; + +NoiseSpatial::NoiseSpatial(QWidget *parent): SpatialPlot(parent) +{ +} + +void NoiseSpatial::addMask(uint mask) +{ + mMask |= mask; + render(); +} + +void NoiseSpatial::RemoveMask(uint mask) +{ + mMask &= ~mask; + render(); +} + +void NoiseSpatial::doConvert(int &loading) +{ + if(fname == ""){ + free(out); + out=NULL; + last_fname = _fname; + traces_len=0; + } + if (last_fname != _fname){ + loading=1; + // load a new dat file + printf("triggered %s %s\n",last_fname.toLatin1().data(),_fname.toLatin1().data()); + last_fname = _fname; + if(out) + free(out); + out=NULL; + + QByteArray ba = fname.toLatin1(); + char * fn = ba.data(); + + { + //Note, fillMask is optional argument w/ default of true. + FILE *in = fopen ( fn, "rb" ); + if(in){ + uint magic=0, version=0, elements_read=0; + uint lrows=0,lcols=0; + uint RawExpmt_Y=Block_Y; + uint RawExpmt_X=Block_X; + + elements_read = fread ( &magic, sizeof ( magic ), 1, in ); + if( (elements_read == 1) && (magic == 0xFF115E3A)){ + elements_read = fread ( &version, sizeof ( version ), 1, in ); + if( elements_read == 1 ){ + elements_read = fread ( &lcols, sizeof ( lcols ), 1, in ); + if( elements_read == 1 ){ + elements_read = fread ( &lrows, sizeof ( lrows ), 1, in ); + if( elements_read == 1 ){ + + // we're ready to read in the data... + int nbytes=sizeof(out[0])*rows*cols; + out = (uint16_t *)(uint16_t *)malloc(nbytes); + + fseek(in, (RawExpmt_Y*lcols + RawExpmt_X)*sizeof(out[0]),SEEK_CUR);// seek to the right part of the row + for(int r=0;r +#include "SpatialPlot.h" +#include "qcustomplot.h" + +class Dialog; + +class NoiseSpatial : public SpatialPlot +{ +public: + NoiseSpatial(QWidget *parent); + + void doConvert(int &loading); + void addMask(uint mask); + void RemoveMask(uint mask); + float Get_Data(int idx, int y, int x); + +private: + uint16_t *out=NULL; + uint mMask=0; +}; + +#endif // NOISESPATIAL_H diff --git a/Analysis/DataViewer/NoiseTab.cpp b/Analysis/DataViewer/NoiseTab.cpp new file mode 100644 index 00000000..9bc4281a --- /dev/null +++ b/Analysis/DataViewer/NoiseTab.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "NoiseTab.h" +#include "Mask.h" + +static const maskCheckBox_t mcbt1[] = {}; + +NoiseTab::NoiseTab(QString _mName, Dialog *_mParent, QWidget *parent) + : ModelTab(_mName, _mParent, parent) +{ + + mSpatialPlot = new NoiseSpatial(this); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(MakeSlider(""),0,0); + grid->addWidget(MakeTraceCB(),1,0); + grid->addWidget(MakeList(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])),0,1,2,1); + grid->setRowStretch(0,0); + grid->setRowStretch(1,0); + grid->addWidget(mSpatialPlot,2,0,1,4); + grid->addWidget(MakeTracePlot(),3,0,1,4); + setLayout(grid); + +} + diff --git a/Analysis/DataViewer/NoiseTab.h b/Analysis/DataViewer/NoiseTab.h new file mode 100644 index 00000000..96bc766d --- /dev/null +++ b/Analysis/DataViewer/NoiseTab.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef NOISETAB_H +#define NOISETAB_H + +#include +#include +#include "NoiseSpatial.h" +#include "modeltab.h" + +class Dialog; + +class NoiseTab : public ModelTab +{ + Q_OBJECT +public: + explicit NoiseTab(QString _mName, Dialog *_mParent, QWidget *parent = 0); + +signals: + +public slots: + +private: +}; + +#endif // NOISETAB_H diff --git a/Analysis/DataViewer/README b/Analysis/DataViewer/README new file mode 100644 index 00000000..f69a7a83 --- /dev/null +++ b/Analysis/DataViewer/README @@ -0,0 +1,4 @@ +sudo apt-get install qtcreator +sudo apt-get install qtbase5-examples qtbase5-doc-html qt5-doc qt5-doc-html +sudo apt-get install qt5-default + diff --git a/Analysis/DataViewer/RawSpatial.cpp b/Analysis/DataViewer/RawSpatial.cpp new file mode 100644 index 00000000..31f2cbfc --- /dev/null +++ b/Analysis/DataViewer/RawSpatial.cpp @@ -0,0 +1,391 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "RawSpatial.h" +#include "deInterlace.h" +#include "CorrNoiseCorrector.h" +#include "ComparatorNoiseCorrector.h" +#include "ChipIdDecoder.h" +#include "AdvCompr.h" +#include "Mask.h" + + +RawSpatial::RawSpatial(QWidget *parent): SpatialPlot(parent) +{ +} + +void RawSpatial::SetOption(QString txt, int state) +{ + qDebug() << __PRETTY_FUNCTION__ << ": " << txt << state; + + if(txt == "Zero traces") + zeroState = state; + else if(txt == "Rmv Row Noise") + RowNoiseState = state; + else if(txt == "Rmv Col Noise") + ColNoiseState = state; + else if(txt == "Gain Correct") + gainCorrState = state; + else if(txt == "NeighborSubtract") + neighborState = state; + else if(txt == "RefSubtract") + RefState = state; + else if(txt == "EmptySubtract") + EmptySubState = state; + else if(txt == "Column Flicker") + ColFlState = state; + else if(txt == "Adv Compression") + AdvcState = state; + else if(txt == "Show Masked") + MaskReads = state; + else if(txt == "StdDev") + stdState = state; + + render(); +} + + +void RawSpatial::doConvert(int &loading) +{ + + if((rowNoiseRemoved && !RowNoiseState) || + (colNoiseRemoved && !ColNoiseState) || + (gainCorrApplied && !gainCorrState) || + (neighborSubApplied && !neighborState) || + (RefSubApplied && !RefState) || + (ColFlApplied && !ColFlState) || + (EmptySubApplied && !EmptySubState) || + (AdvcApplied && !AdvcState) || + (stdApplied && !stdState)){ + // re-load the image.... + last_fname.clear(); + } + + if(fname == ""){ + free(out); + out=NULL; + last_fname = _fname; + traces_len=0; + } + if (last_fname != _fname){ + // load a new dat file + loading=1; + printf("triggered %s %s\n",last_fname.toLatin1().data(),_fname.toLatin1().data()); + last_fname = _fname; + if(out) + free(out); + out=NULL; + + if(timestamps) + free(timestamps); + timestamps=NULL; + + if(gainImage) + free(gainImage); + gainImage=NULL; + + + QByteArray ba = fname.toLatin1(); + char * fn = ba.data(); + printf("about to call deInterlace with %s\n",fn); + deInterlace_c (fn, &out, ×tamps, + &rows, &cols, &frames, &uncompFrames, + 0, 0, + 0, 0, 0, 0, 0, &imageState ); + printf("\ndone\n\n"); + rowNoiseRemoved=0; + colNoiseRemoved=0; + gainCorrApplied=0; + neighborSubApplied=0; + RefSubApplied=0; + EmptySubApplied=0; + ColFlApplied=0; + AdvcApplied=0; + stdApplied=0; + if(endX==0){ + startX=0; + startY=0; + endX=cols; + endY=rows; + } + for(uint i=0;i<(uint)frames && i < (sizeof(traces_ts)/sizeof(traces_ts[0]));i++) + traces_ts[i]=((double)timestamps[i])/1000.0f; + traces_len=frames; + loading=0; + + if(out){ + if(mMask){ + free(mMask); + mMask=NULL; + } + mMask = (char *)malloc(rows*cols); + memset(mMask,0,rows*cols); + } + + } + + if(out){ + + + //we have loaded a image into memory.. + if(!rowNoiseRemoved && RowNoiseState){ + ChipIdDecoder::SetGlobalChipId("p2.2.2"); + CorrNoiseCorrector rnc; + /*double RowNoise = */rnc.CorrectCorrNoise(out, rows, cols, frames, 1, 0, 0,0,-1,0); + rowNoiseRemoved=1; + } + if(!colNoiseRemoved && ColNoiseState){ + ChipIdDecoder::SetGlobalChipId("p2.2.2"); + CorrNoiseCorrector rnc; + /*double ColNoise = */rnc.CorrectCorrNoise(out, rows, cols, frames, 2, 0, 0,0,-1,0); + colNoiseRemoved=1; + } + if(!gainCorrApplied && gainCorrState && gainVals){ + GainCorrect(out,rows,cols,frames); + gainCorrApplied=1; + } + if(!neighborSubApplied && neighborState){ + // do neighbor subtraction + NeighborSubtract(out, rows, cols, frames,NULL,0); + neighborSubApplied=1; + } + if(!RefSubApplied && RefState && refMask){ + // do neighbor subtraction + NeighborSubtract(out, rows, cols, frames, refMask,1); + RefSubApplied=1; + } + if(!EmptySubApplied && EmptySubState && refMask){ + // do neighbor subtraction + NeighborSubtract(out, rows, cols, frames, refMask,0); + EmptySubApplied=1; + } + if(!ColFlApplied && ColFlState){ + // do column flicker correction + ComparatorNoiseCorrector cnc; + cnc.CorrectComparatorNoise(out,rows,cols,frames,NULL,0,1,0); + ColFlApplied=1; + } + if(!AdvcApplied && AdvcState){ + ChipIdDecoder::SetGlobalChipId("p2.2.2"); + AdvCompr advc(-1/*ExtFd*/, out, cols, rows, frames, uncompFrames, + timestamps, timestamps, 1, (char *)"",(char *)"",15,1); + advc.Compress(-1,0,0,0); + AdvcApplied=1; + } + if(!stdApplied && stdState){ + // take spatial standard deviation of the pixel offsets + TakeStdDev(); + stdApplied=1; + } + + fflush(stdout); + UpdateTraceData(); + } +} + +void RawSpatial::TakeStdDev() +{ + int spanW=2; + int spanH=2; + + // make a mask of the first frame.. + for(int r=0;r= rows) + endy=rows; + for(int c=0;c= cols) + endx=cols; + + float localSum=0; + int localSumCnt=0; + for(int ry=starty;ry0){ + lsum[y*w+x] =lsum[(y-1)*w+x] + rptr[y*w+x]; + lsumNum[y*w+x]=lsumNum[(y-1)*w+x] + 1; + } + else{ + lsum[y*w+x]=rptr[x]; + lsumNum[y*w+x]=1; + } + } + else{ + if(y>0){ + lsum[y*w+x]=lsum[(y-1)*w+x]; + lsumNum[y*w+x]=lsumNum[(y-1)*w+x]; + } + else + lsum[y*w+x]=0; + } + for(x=1;x0){ + lsum[y*w+x]=lsum[(y-1)*w+x] + lsum[y*w+x-1] - lsum[(y-1)*w+x-1] + rptr[y*w+x]; + lsumNum[y*w+x]=lsumNum[(y-1)*w+x] + lsumNum[y*w+x-1] - lsumNum[(y-1)*w+x-1] + 1; + } + else{ + lsum[y*w+x]= lsum[y*w+x-1] + rptr[y*w+x]; + lsumNum[y*w+x]= lsumNum[y*w+x-1] + 1; + } } + else{ + if(y>0){ + lsum[y*w+x]=lsum[(y-1)*w+x] + lsum[y*w+x-1] - lsum[(y-1)*w+x-1]; + lsumNum[y*w+x]=lsumNum[(y-1)*w+x] + lsumNum[y*w+x-1] - lsumNum[(y-1)*w+x-1]; + } + else{ + lsum[y*w+x]= lsum[y*w+x-1]; + lsumNum[y*w+x]= lsumNum[y*w+x-1]; + } + } + } + } + // save the mean for this frame +// if(saved_mean) +// saved_mean[frame]=(float)lsum[(h-1)*w+(w-1)]/(float)(w*h); + // now, neighbor subtract each well + for(int y=0;y= w) + end_x = w-1; + if(start_y < 0) + start_y=0; + if(end_y >= h) + end_y=h-1; + int end=end_y*w+end_x; + int start=start_y*w+start_x; + int diag1=end_y*w+start_x; + int diag2=start_y*w+end_x; + +// if(y < 12 && x < 12) +// printf(" (%d/%d = %d)",(int)(lsum[end_y][end_x]-lsum[start_y][start_x]),((end_y-start_y+1)*(end_x-start_x+1)), +// (int)((lsum[end_y][end_x]-lsum[start_y][start_x])/((end_y-start_y+1)*(end_x-start_x+1)) + 8192)); + int64_t avg = lsum[end]+lsum[start]-lsum[diag1]-lsum[diag2]; + int64_t num=lsumNum[end]+lsumNum[start]-lsumNum[diag1]-lsumNum[diag2]; + if(num){ + avg /= num; + rptr[y*w+x] = rptr[y*w+x] - (short int)avg/* + 8192*/; + } + else{ +// printf("Problems here %d/%d\n",y,x); + } + } + } + } + free(lsum); + free(lsumNum); +} + + + + diff --git a/Analysis/DataViewer/RawSpatial.h b/Analysis/DataViewer/RawSpatial.h new file mode 100644 index 00000000..afa60483 --- /dev/null +++ b/Analysis/DataViewer/RawSpatial.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef RAWSPATIAL_H +#define RAWSPATIAL_H + +#include +#include "SpatialPlot.h" +#include "qcustomplot.h" + +class RawSpatial : public SpatialPlot +{ +public: + RawSpatial(QWidget *parent); + void doConvert(int &loading); + void SetOption(QString option, int state); + +protected: + virtual float Get_Data(int frame, int y, int x); + + +private: + void NeighborSubtract(short int *raw, int h, int w, int npts, uint16_t *mask, int ref); + void GainCorrect(short int *raw, int h, int w, int npts); + void TakeStdDev(); + + int zeroState=0; + int RowNoiseState=0; + int ColNoiseState=0; + int gainCorrState=0; + int neighborState=0; + int RefState=0; + int EmptySubState=0; + int ColFlState=0; + int AdvcState=0; + int stdState=0; + int MaskReads=0; + + // thread copy of app specific stuff + + // thread specific stuff + short *out=NULL; + int *timestamps=NULL; + int uncompFrames=0; + int imageState=0; + short *gainImage=NULL; + + + int rowNoiseRemoved=0; + int colNoiseRemoved=0; + int gainCorrApplied=0; + int neighborSubApplied=0; + int RefSubApplied=0; + int EmptySubApplied=0; + int ColFlApplied=0; + int AdvcApplied=0; + int stdApplied=0; + +}; + +#endif // RAWSPATIAL_H diff --git a/Analysis/DataViewer/RawTab.cpp b/Analysis/DataViewer/RawTab.cpp new file mode 100644 index 00000000..a8c5d72d --- /dev/null +++ b/Analysis/DataViewer/RawTab.cpp @@ -0,0 +1,43 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "RawTab.h" +#include "qcustomplot.h" +#include "deInterlace.h" +#include "CorrNoiseCorrector.h" + +static const maskCheckBox_t mcbt1[] = { + {"Zero traces",0,Qt::Unchecked}, + {"Rmv Row Noise",1,Qt::Unchecked}, + {"Rmv Col Noise",2,Qt::Unchecked}, + {"Gain Correct",3,Qt::Unchecked}, + {"NeighborSubtract",4,Qt::Unchecked}, + {"RefSubtract",5,Qt::Unchecked}, +// {"EmptySubtract",6,Qt::Unchecked}, + {"Column Flicker",6,Qt::Unchecked}, + {"Adv Compression",7,Qt::Unchecked}, + {"Show Masked",8,Qt::Unchecked}, + {"StdDev",9,Qt::Unchecked}, +}; + +RawTab::RawTab(QString _mName, Dialog *_mParent, QWidget *parent) + : ModelTab(_mName, _mParent, parent) +{ + mSpatialPlot = new RawSpatial(this); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(MakeSlider("frames"),0,0); + grid->addWidget(MakeTraceCB(),1,0); + grid->addWidget(MakeList(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])),0,1,2,1); + grid->setRowStretch(0,0); + grid->setRowStretch(1,0); + grid->addWidget(mSpatialPlot,2,0,1,4); + grid->addWidget(MakeTracePlot(),3,0,1,4); + + // add compression points to graph + for(int i=0;igraph(i)->setScatterStyle(QCPScatterStyle::ssCircle); + } + + setLayout(grid); +} + + diff --git a/Analysis/DataViewer/RawTab.h b/Analysis/DataViewer/RawTab.h new file mode 100644 index 00000000..1ff8df80 --- /dev/null +++ b/Analysis/DataViewer/RawTab.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef RAWTAB_H +#define RAWTAB_H + +#include +#include +#include "dialog.h" +#include "RawSpatial.h" +#include "qcustomplot.h" +#include "modeltab.h" + +class Dialog; + +class RawTab : public ModelTab +{ + Q_OBJECT + +public: + explicit RawTab(QString _mName, Dialog *_mParent, QWidget *parent = 0); + +public slots: + +private: +}; + +#endif // RAWTAB_H diff --git a/Analysis/DataViewer/SpatialPlot.cpp b/Analysis/DataViewer/SpatialPlot.cpp new file mode 100644 index 00000000..0e0737bb --- /dev/null +++ b/Analysis/DataViewer/SpatialPlot.cpp @@ -0,0 +1,1002 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ + +#include +#include + +#include + +#include "SpatialPlot.h" + + +//! [0] +const double ZoomInFactor = 0.8; +const double ZoomOutFactor = 1 / ZoomInFactor; +const int ScrollStep = 20; +//! [0] + +// window variables +int SpatialPlot::startX=0; +int SpatialPlot::endX=0; +int SpatialPlot::startY=0; +int SpatialPlot::endY=0; +int SpatialPlot::frame=0; +int SpatialPlot::flow=0; + +// image variables +int SpatialPlot::rows=0; +int SpatialPlot::cols=0; +int SpatialPlot::frames=0; +int SpatialPlot::flows=0; + +int SpatialPlot::Block_X=0; +int SpatialPlot::Block_Y=0; +char *SpatialPlot::mMask = NULL; + +TraceDescr_t SpatialPlot::traces[MAX_NUM_TRACES] = {{0,0}}; +int SpatialPlot::traces_curSelection=0; +int SpatialPlot::GblTracesVer=0; +uint16_t *SpatialPlot::refMask=NULL; //from BfMaskTab +float *SpatialPlot::gainVals=NULL; //from GainTab + + +//! [1] +SpatialPlot::SpatialPlot(QWidget *parent) + :QWidget(parent) +{ + + connect(&thread, SIGNAL(renderedImage(QImage)), this, SLOT(updatePixmap(QImage))); + + setWindowTitle(tr("SpatialPlot")); +#ifndef QT_NO_CURSOR + setCursor(Qt::CrossCursor); +#endif + resize(550, 400); + setMouseTracking( true ); + setFocusPolicy(Qt::WheelFocus); + + for (int i = 0; i < ColormapSize; ++i) + colormap[i] = rgbFromWaveLength((375.0) + (i * 400.0 / ColormapSize)); + + colormap[0]=0; // make the background black + + thread.SetParent(this); + + memset(traces,0,sizeof(traces)); + for(int trc=0;trc=0) && (traces[trc].y > 0)) + { + for(int idx=0;idx=0) && (traces[trc].y > 0)) + { + QVector x(traces_len); + QVector y(traces_len); + + for (int i=0; i ymax) + ymax = y[i]; + } + + + // create graph and assign data to it: + _mTracePlot->graph(trc)->setData(x, y); + QString name="Y" + QString::number(traces[trc].y) + "_X" + QString::number(traces[trc].x); + _mTracePlot->graph(trc)->setName(name); + _mTracePlotSet[trc]=1; + // set axes ranges, so we see all data: + } + else{ + if(_mTracePlotSet[trc]){ + QString name=""; + _mTracePlot->graph(trc)->setName(name); + _mTracePlotSet[trc]=0; + QVector x(0); + QVector y(0); + _mTracePlot->graph(trc)->setData(x, y); + } + } + } + + { + // add frame/flow line + QVector x(2); + QVector y(2); + x[0] = x[1] = (double)traces_ts[flow_based?flow:frame]; + y[0] = ymin; + y[1] = ymax; + _mTracePlot->graph(MAX_NUM_TRACES)->setData(x, y); + _mTracePlot->graph(MAX_NUM_TRACES)->setName(""); + _mTracePlot->graph(MAX_NUM_TRACES)->setPen(QPen(Qt::DashLine)); + + } + _mTraceRange=ymax-ymin; + _mTracePlot->xAxis->setRange(xmin, xmax); + _mTracePlot->yAxis->setRange(ymin, ymax); + _mTracePlot->replot(); + + } + +} + + +void SpatialPlot::setTracePlot(QCustomPlot *mtracePlot, int _flow_based) +{ + _mTracePlot = mtracePlot; + flow_based=_flow_based; + + double xmin =-1; // set defaults + double xmax = 1; + double ymin = 0; + double ymax = 1; + + // create graph and assign data to it: + for(int i=0;i<(MAX_NUM_TRACES+1);i++){ + mtracePlot->addGraph(); + + if(i < MAX_NUM_TRACES){ + QPen qp; + + uint xcolorIdx= i*ColormapSize/MAX_NUM_TRACES; + uint xcolor=colormap[xcolorIdx % ColormapSize]; + + qp.setColor(QColor(qRed(xcolor),qGreen(xcolor),qBlue(xcolor))); + mtracePlot->graph(i)->setPen(qp); + } + } + + mtracePlot->setInteractions(QCP::iRangeZoom | QCP::iSelectPlottables | QCP::iSelectLegend | QCP::iRangeDrag); // allow selection of graphs via mouse click + // give the axes some labels: + mtracePlot->xAxis->setLabel("x"); + mtracePlot->yAxis->setLabel("y"); + // set axes ranges, so we see all data: + mtracePlot->xAxis->setRange(xmin, xmax); + mtracePlot->yAxis->setRange(ymin, ymax); + mtracePlot->legend->setVisible(true); + //mtracePlot->legend->setAlignment(Qt::AlignTop); + mtracePlot->replot(); + + connect(mtracePlot, SIGNAL(mouseMove(QMouseEvent*)), this,SLOT(showPointToolTip_traces(QMouseEvent*))); + connect(mtracePlot, SIGNAL(mouseDoubleClick(QMouseEvent*)), this,SLOT(mouseDoubleClickEvent_traces(QMouseEvent*))); +} + +void SpatialPlot::showPointToolTip(QMouseEvent *event) +{ + + int x = _mTracePlot->xAxis->pixelToCoord(event->pos().x()); + int y = _mTracePlot->yAxis->pixelToCoord(event->pos().y()); + + _mTracePlot->setToolTip(QString("%1 , %2").arg(x).arg(y)); + + +} + + +void SpatialPlot::Save(QTextStream &strm) +{ + // create graph and assign data to it: + + for(int i=0;i<(MAX_NUM_TRACES);i++){ + + if((traces[i].x >=0) && (traces[i].y > 0)) + { + for(int idx=0;idx<(flow_based?flows:frames);idx++){ + strm << traces_ts[idx] << "," << traces_Val[i][idx] << " "; + } + strm << endl; + } + } +} + +void SpatialPlot::DoubleClick_traces(int x, int y, int ts) +{ + // .... override me ...... + (void)x; + (void)y; + (void)ts; +} + +void SpatialPlot::mouseDoubleClickEvent_traces(QMouseEvent *event) +{ + float x = _mTracePlot->xAxis->pixelToCoord(event->pos().x()); + float y = _mTracePlot->yAxis->pixelToCoord(event->pos().y()); + qDebug() << __PRETTY_FUNCTION__ << ": X" << x << "_Y" << y; + + + // figure out which frame we are looking at... + int min_ts=0; + for(int ts=0;ts=0) && (traces[i].y > 0)) + { + if(std::abs(y - traces_Val[i][min_ts]) < std::abs(y - traces_Val[min_val_idx][min_ts])) + min_val_idx = i; + } + } + + // we now have min_ts and min_val_idx.... + if(std::abs(y-traces_Val[min_val_idx][min_ts]) < (3.0*_mTraceRange/100.0)){ + //_mTracePlot->setToolTip(QString("Y%1_X%2: (%3,%4)").arg(traces[min_val_idx].y).arg(traces[min_val_idx].x).arg(traces_ts[min_ts]).arg(traces_Val[min_val_idx][min_ts])); + DoubleClick_traces(traces[min_val_idx].x,traces[min_val_idx].y,traces_Val[min_val_idx][min_ts]); + } +} + +void SpatialPlot::showPointToolTip_traces(QMouseEvent *event) +{ + + float x = _mTracePlot->xAxis->pixelToCoord(event->pos().x()); + float y = _mTracePlot->yAxis->pixelToCoord(event->pos().y()); + + + // figure out which frame we are looking at... + int min_ts=0; + for(int ts=0;ts=0) && (traces[i].y > 0)) + { + if(std::abs(y - traces_Val[i][min_ts]) < std::abs(y - traces_Val[min_val_idx][min_ts])) + min_val_idx = i; + } + } + + // we now have min_ts and min_val_idx.... + if(std::abs(y-traces_Val[min_val_idx][min_ts]) < (3.0*_mTraceRange/100.0)){ + _mTracePlot->setToolTip(QString("Y%1_X%2: (%3,%4)").arg(traces[min_val_idx].y).arg(traces[min_val_idx].x).arg(traces_ts[min_ts]).arg(traces_Val[min_val_idx][min_ts])); + } +// else{ +// _mTracePlot->setToolTip(""); +// } +} + + +void SpatialPlot::mouseDoubleClickEvent(QMouseEvent *event) +{ + int oobx=0; + int ooby=0; + int ax = GetAX(event->x(),width(),oobx); + int ay = GetAY(event->y(),height(),ooby); + int oob=oobx|ooby; + + DoubleClick(oob?-1:ax,oob?-1:ay); + render(); + printf("%s: %d/%d %d/%d\n",__FUNCTION__,event->x(),event->y(),ax,ay); + initialHints=0; + fflush(stdout); +} +void SpatialPlot::mousePressEvent(QMouseEvent *event){ + LastPressX = event->x(); + LastPressY = event->y(); + printf("%s: %d/%d\n",__FUNCTION__,LastPressX,LastPressY); + fflush(stdout); +} + +void SpatialPlot::mouseReleaseEvent(QMouseEvent *event){ + printf("%s: %d/%d last=%d/%d\n",__FUNCTION__,event->x(),event->y(),LastPressX,LastPressY); + fflush(stdout); + if((LastPressX == event->x()) && + (LastPressY == event->y())){ + // this was a click event.. + printf("%s: calling DoubleClick()\n",__FUNCTION__); + mouseDoubleClickEvent(event);//->x(),event->y(),height(),width()); + } +} + + +//! [10] +void SpatialPlot::resizeEvent(QResizeEvent * /* event */) +{ +// render(); +} +//! [10] + +//! [11] +void SpatialPlot::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Plus: + zoom(ZoomInFactor); + break; + case Qt::Key_Minus: + zoom(ZoomOutFactor); + break; + case Qt::Key_Left: + { + //scroll(-ScrollStep, 0); + int trc=traces_curSelection; + if(traces[trc].x > 0){ + traces[trc].x--; + render(); + } + } + break; + case Qt::Key_Right: + { + //scroll(+ScrollStep, 0); + int trc=traces_curSelection; + if((traces[trc].x+1) < cols){ + traces[trc].x++; + render(); + } + } + break; + case Qt::Key_Down: + { + //scroll(0, -ScrollStep); + int trc=traces_curSelection; + if((traces[trc].y) > 0){ + traces[trc].y--; + render(); + } + + } + break; + case Qt::Key_Up: + { + //scroll(0, +ScrollStep); + int trc=traces_curSelection; + if((traces[trc].y+1) < rows){ + traces[trc].y++; + render(); + } + } + break; + default: + QWidget::keyPressEvent(event); + } +} +//! [11] + +#ifndef QT_NO_WHEELEVENT +//! [12] +void SpatialPlot::wheelEvent(QWheelEvent *event) +{ + int numDegrees = event->delta() / 8; + double numSteps = numDegrees / 15.0f; + double zf = pow(ZoomInFactor, numSteps); + + int oob=0; + int x=event->pos().x(); + int y=event->pos().y(); + int ay=GetAY(y,height(),oob); + int ax=GetAX(x,width(),oob); + if(!oob){ + double WeightX = ((double)(ax - startX)) / ((double)(endX - startX)); + double WeightY = ((double)(ay - startY)) / ((double)(endY - startY)); + printf("%s: %lf %d/%d %d/%d %lf/%lf\n",__FUNCTION__,zf,y,x,ay,ax,WeightY,WeightX); + fflush(stdout); + zoom(zf,WeightX,WeightY); + } +} +//! [12] +#endif + +////! [13] +//void SpatialPlot::mousePressEvent(QMouseEvent *event) +//{ +// if (event->button() == Qt::LeftButton) +// lastDragPos = event->pos(); +//} +////! [13] + +//! [14] +void SpatialPlot::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton && lastDragPos.x()) { + //int width = endX-startX; + QPoint diff = event->pos() - lastDragPos; + int diffX = (diff.x() * (endX-startX)/width()) ; + int diffY = (diff.y() * (endY-startY)/height()) ; + diffY = -diffY; //it's flipped + printf("%s: %d/%d --> %d/%d\n",__FUNCTION__,event->y(),event->x(),lastDragPos.y(),lastDragPos.x()); + printf("%s: %d/%d/%d/%d --> %d/%d/%d/%d\n",__FUNCTION__,startY,startX,endY,endX,startY-diffY,startX-diffX,endY-diffY,endX-diffX); + fflush(stdout); + if(diffX > startX) + diffX = startX; + if(diffY > startY) + diffY = startY; + if((endX-diffX) > cols) + diffX = endX-cols; + if((endY-diffY) > rows) + diffY = endY-rows; + startX -= diffX; + startY -= diffY; + endX -= diffX; + endY -= diffY; + if(diffX || diffY){ + lastDragPos = event->pos(); + initialHints=0; + } + render(); + } + else{ + lastDragPos = event->pos(); + } +} +//! [14] + +#if 0 +//! [15] +void SpatialPlot::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + pixmapOffset += event->pos() - lastDragPos; + lastDragPos = QPoint(); + + int deltaX = (width() - pixmap.width()) / 2 - pixmapOffset.x(); + int deltaY = (height() - pixmap.height()) / 2 - pixmapOffset.y(); + scroll(deltaX, deltaY); + } +} +//! [15] +#endif +//! [16] +void SpatialPlot::updatePixmap(const QImage &image) +{ + pixmap = QPixmap::fromImage(image); + UpdateTracePlot(); + update(); + +} +//! [16] + +//! [17] +void SpatialPlot::zoom(double zoomFactor, double XWeight, double YWeight) +{ + int diffX = (int)((double)(endX-startX) * (1.0-zoomFactor))/2; + int diffY = (int)((double)(endY-startY) * (1.0-zoomFactor))/2; + if(diffX >= 0 && diffX < 4) + diffX=4; + if(diffY >= 0 && diffY < 4) + diffY=4; + + if(diffX <= 0 && diffX > -4) + diffX=-4; + if(diffY <= 0 && diffY > -4) + diffY=-4; + + int diffXS = (int)((double)diffX * XWeight); + int diffXE = diffX-diffXS; + int diffYS = (int)((double)diffY * YWeight); + int diffYE = diffY-diffYS; + + qDebug() << __FUNCTION__ << ": (" << startY << "/" << startX << ") (" << endY << "/" << endX << ") zf:" << zoomFactor << " diffS(" << diffYS << "/" << diffXS << ") diffE(" << diffYE << "/" << diffXE << ")"; + + startX += diffXS; + startY += diffYS; + endX -= diffYE; + endY -= diffYE; + if(startX < 0) + startX=0; + if(startY<0) + startY=0; + if(endX > cols) + endX=cols; + if(endY > rows) + endY=rows; + initialHints=0; + render(); +} +//! [17] + +//! [18] +void SpatialPlot::scroll(int deltaX, int deltaY) +{ + int diffX = (int)((double)(endX-startX) * (deltaX/width())); + int diffY = (int)((double)(endY-startY) * (deltaY/height())); + startX += diffX; + startY -= diffY; + endX += diffX; + endY -= diffY; + initialHints=0; + update(); +} +//! [18] + +void SpatialPlot::doConvertInt() +{ + doConvert(loading); +// loading=0; +} + +void SpatialPlot::doConvert(int &loading){(void)loading;} + + +void SpatialPlot::copyData() +{ + _fname = fname; + +} + +//! [0] +SpatialPlotThread::SpatialPlotThread(QObject *parent) + : QThread(parent) +{ + restart = false; + abort = false; + +} +void SpatialPlotThread::SetParent(SpatialPlot *_parent) +{ + mParent = _parent; +} + +//! [0] + +//! [1] +SpatialPlotThread::~SpatialPlotThread() +{ + mutex.lock(); + abort = true; + condition.wakeOne(); + mutex.unlock(); + + wait(); +} +//! [1] + +void SpatialPlot::copyLastCoord() +{ + lastStartX = startX; + lastStartY = startY; + lastEndX = endX; + lastEndY = endY; +} + +//! [2] +void SpatialPlotThread::render() +{ + mParent->copyLastCoord(); + QMutexLocker locker(&mutex); + + if (!isRunning()) { + start(LowPriority); + } else { + restart = true; + condition.wakeOne(); + } +} +//! [2] + +void SpatialPlotThread::Abort() +{ + abort=true; + mutex.unlock(); +} + +void SpatialPlotThread::run() +{ + forever { + mutex.lock(); + if(abort) + return; // application is closed + mParent->copyData(); + mutex.unlock(); + + mParent->doConvertInt(); + + QImage *image = mParent->doRender(); + if(image){ + emit renderedImage(*image); + free(image); + } + + mutex.lock(); + if (!restart) + condition.wait(&mutex); + restart = false; + mutex.unlock(); + } +} + +// get data coords from screen coords +int SpatialPlot::GetAY(int y, int Height, int & ooby) +{ + double bh = borderHeight; + double rh = (double)(endY-startY); + double screenh = (double)(Height - bh); + int ay = endY - bh - 1 - (int)(((double)y/*-bh*/) * rh / screenh); + if((y >= screenh)) + ooby=1; + if(ay >= rows || ay < 0) + ooby = 1; + return ay; +} + +// get data coords from screen coords +int SpatialPlot::GetAX(int x, int Width, int & oobx) +{ + int bw = borderWidth+scaleBarWidth+2; + double rw = (double)(endX-startX); + double screenw = (double)(Width-bw); + int ax = startX + (int)((double)(x-borderWidth) * rw/screenw); + if(x < borderWidth || (x > (Width-(scaleBarWidth+2)))) + oobx=1; + if(ax >= cols || ax < 0) + oobx=1; + return ax; +} + +// get screen coords from data coords +int SpatialPlot::GetY(int ay, int Height, int & ooby) +{ + double rh = (double)(endY-startY); + double screenh = (double)(Height - borderHeight); + int y = /*borderHeight +*/ (int)((double)(endY-ay-borderHeight-1) * screenh/rh); + if(y < 0) + ooby=1; + if(y >= Height) + ooby=1; + + return y; +} + +// get screen coords from data coords +int SpatialPlot::GetX(int ax, int Width, int & oobx) +{ + int bw = borderWidth+scaleBarWidth+2; + double rw = (double)(endX-startX); + double screenw = (double)(Width-bw); + ax -= startX; + int x = borderWidth + (int)((double)ax * screenw/rw); + if(x < 0) + oobx=1; + if(x >= Width) + oobx=1; + return x; +} + + +QImage *SpatialPlot::doRender() +{ + int Height = height(); + int Width = width(); + QImage *image = new QImage(Width,Height, QImage::Format_RGB32); + + if(startX < 0) + startX=0; + if(startX > cols) + startX = cols; + if(startY<0) + startY=0; + if(startY > rows) + startY = rows; +// printf("rendering from %d/%d/%d to %d/%d --> %d/%d frame %d\n", +// rows,cols,frames,startX,startY,endX,endY, frame); + + int minVal = 16384; + int maxVal = -16384; + // get the max and min values first... + for (int y = 0; y < Height; y++) { + int ooby=0; + int ay = GetAY(y,Height,ooby); + for(int x=0;x= 4))){ + minVal = val; + } + if((val > maxVal) && (val <= 16380)){ + maxVal = val; + } + } + } + } + + maxVal++; + if(maxVal <= minVal) + maxVal = minVal+10; +// if(minVal > 0) +// minVal-=1; + maxPixVal=maxVal; + minPixVal=minVal; + int range = maxVal-minVal; +// if(range < 1) +// range=1; // don't divide by zero! +// printf("range = %d - %d --> %d\n",maxVal,minVal,range); + + for (int y = 0; y < Height; y++) { + uint *scanLine = + reinterpret_cast(image->scanLine(y)); + int ooby=0; + int ay = GetAY(y,Height,ooby); + for(int x=0;x (Width-scaleBarWidth+2)){ + int val = (Height-y-1) * ColormapSize / Height; + scanLine[x] = colormap[val % ColormapSize ]; + }else{ + scanLine[x] = 0;//colormap[0]; + } + }else{ + + int val = Get_Data(flow_based?flow:frame,ay,ax); +// if(ignore_pinned && ((val >= 16380) || (val <= 4))) +// scanLine[x]=colormap[0]; +// else{ + val -= minVal; + val *= ColormapSize; + val /= range; + uint cmap = colormap[val % ColormapSize]; + scanLine[x] = cmap; +// } + } + } + } + + // put the X's on the selected well's + if(rows && cols){ + for(int ts=0;ts= height()) + continue; + uint *scanLine = + reinterpret_cast(image->scanLine(marky)); + if(marky != y){ + scanLine[x]=xcolor; + }else{ + for(int markx=(x-pix);markx<(x+pix);markx++){ + if(markx < 0 || markx >= width()) + continue; + scanLine[markx]=xcolor; + } + } + } + + } + } + } + + UpdateTraceData(); + + return image; +} + +float SpatialPlot::Get_Data(int idx, int y, int x) +{ + (void)idx; + (void)y; + (void)x; + return 0; +} + +//! [10] +uint SpatialPlot::rgbFromWaveLength(double wave) +{ + double r = 0.0; + double g = 0.0; + double b = 0.0; + + if (wave >= 380.0 && wave <= 440.0) { + r = -1.0 * (wave - 440.0) / (440.0 - 380.0); + b = 1.0; + } else if (wave >= 440.0 && wave <= 490.0) { + g = (wave - 440.0) / (490.0 - 440.0); + b = 1.0; + } else if (wave >= 490.0 && wave <= 510.0) { + g = 1.0; + b = -1.0 * (wave - 510.0) / (510.0 - 490.0); + } else if (wave >= 510.0 && wave <= 580.0) { + r = (wave - 510.0) / (580.0 - 510.0); + g = 1.0; + } else if (wave >= 580.0 && wave <= 645.0) { + r = 1.0; + g = -1.0 * (wave - 645.0) / (645.0 - 580.0); + } else if (wave >= 645.0 && wave <= 780.0) { + r = 1.0; + } + + double s = 1.0; + if (wave > 700.0) + s = 0.3 + 0.7 * (780.0 - wave) / (780.0 - 700.0); + else if (wave < 420.0) + s = 0.3 + 0.7 * (wave - 380.0) / (420.0 - 380.0); + + r = pow(r * s, 0.8); + g = pow(g * s, 0.8); + b = pow(b * s, 0.8); + return qRgb(int(r * 255), int(g * 255), int(b * 255)); +} diff --git a/Analysis/DataViewer/SpatialPlot.h b/Analysis/DataViewer/SpatialPlot.h new file mode 100644 index 00000000..814c7457 --- /dev/null +++ b/Analysis/DataViewer/SpatialPlot.h @@ -0,0 +1,241 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPATIALPLOT_H +#define SPATIALPLOT_H + +#include +#include +#include +#include +#include +#include +#include "qcustomplot.h" + + +class SpatialPlot; + +#define MAX_NUM_TRACES 12 +#define MAX_TRACE_LEN 500 + +typedef struct{ + int y; + int x; +}TraceDescr_t; + + +//! [0] +class SpatialPlotThread : public QThread +{ + Q_OBJECT + +public: + SpatialPlotThread(QObject *parent = 0); + ~SpatialPlotThread(); + void Abort(); + void render(); + void SetParent(SpatialPlot *_parent); + + +signals: + void renderedImage(const QImage &image); + +protected: + void run() Q_DECL_OVERRIDE; + +private: + + QMutex mutex; + QWaitCondition condition; + bool restart=0; + bool abort=0; + SpatialPlot *mParent; + + +}; + +//! [0] +class SpatialPlot : public QWidget +{ + Q_OBJECT + +public: + SpatialPlot(QWidget *parent = 0); + ~SpatialPlot(); + void doConvertInt(); + virtual void doConvert(int &loading); + virtual void copyData(); + QImage *doRender(); + void copyLastCoord(); + void setRawTrace(int selection); + void setfilename(QString fileName); + void setSlider(int per); + void setTracePlot(QCustomPlot *TracePlot, int _flow_based=0); + + void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; + void Save(QTextStream &strm); + virtual void SetOption(QString option, int state); + + static void SetBlockCoord(int x, int y){Block_Y=y;Block_X=x;} + +protected: + + virtual void DoubleClick(int x,int y); + virtual void DoubleClick_traces(int x, int y, int ts); + + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; +#endif +// void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; +// void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void render(); + uint rgbFromWaveLength(double wave); + enum { ColormapSize = 512 }; + uint colormap[ColormapSize]; + virtual float Get_Data(int idx, int y, int x); + + static TraceDescr_t traces[MAX_NUM_TRACES]; + static int traces_curSelection; + static int GblTracesVer; + + int LocalTracesVer=0; + float traces_Val[MAX_NUM_TRACES][MAX_TRACE_LEN]; + + float traces_ts[MAX_TRACE_LEN]={0}; + int _mTracePlotSet[MAX_TRACE_LEN]={0}; + int traces_len=0; + void UpdateTraceData(); + virtual void UpdateTracePlot(); + QCustomPlot *_mTracePlot=NULL; + int flow_based=0; + int ignore_pinned=0; + + +protected slots: + virtual void updatePixmap(const QImage &image); + void zoom(double zoomFactor, double XWeight=0.5, double YWeight=0.5); + void showPointToolTip(QMouseEvent *event); + void showPointToolTip_traces(QMouseEvent *event); + +private: + void mouseDoubleClickEvent(QMouseEvent *event); + void mouseDoubleClickEvent_traces(QMouseEvent *event); + + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void scroll(int deltaX, int deltaY); + int GetAY(int y, int Height, int & ooby); + int GetAX(int x, int Width, int & oobx); + + int GetY(int ay, int Height, int & ooby); + int GetX(int ax, int Width, int & oobx); + + SpatialPlotThread thread; + QPixmap pixmap; + QPoint pixmapOffset; + QPoint lastDragPos; + bool firstShow=false; + + // mouse location variables + int LastPressX=0; + int LastPressY=0; + float _mTraceRange=0; + + +protected: + // window variables + static int startX; + static int endX; + static int startY; + static int endY; + static int frame; + static int flow; + + static int Block_Y; + static int Block_X; + + int lastStartX=0; + int lastStartY=0; + int lastEndX=0; + int lastEndY=0; + int initialHints=1; + int loading=0; + + int maxPixVal=0; + int minPixVal=0; + + QString fname=""; + + // thread copy of app specific stuff + QString _fname=""; + QString last_fname=""; + + // image variables + static int rows; + static int cols; + static int frames; + static int flows; + + static const int borderHeight=20; + static const int borderWidth=30; + static const int scaleBarWidth=35; + + static uint16_t *refMask; //from BfMaskTab + static float *gainVals; //from GainTab + static char *mMask; // only display pixels with a 0 + +}; +//! [0] + +#endif // SPATIALPLOT_H diff --git a/Analysis/DataViewer/WellsSpatial.cpp b/Analysis/DataViewer/WellsSpatial.cpp new file mode 100644 index 00000000..2ae6b395 --- /dev/null +++ b/Analysis/DataViewer/WellsSpatial.cpp @@ -0,0 +1,97 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "WellsSpatial.h" +#include "RawWells.h" +#include +#include + +using namespace std; + +WellsSpatial::WellsSpatial(QWidget *parent): SpatialPlot(parent) +{ +} + +void WellsSpatial::SetOption(QString txt, int state) +{ + qDebug() << __PRETTY_FUNCTION__ << ": " << txt << state; + + if(txt == "Show Masked") + MaskReads = state; + + render(); +} + +void WellsSpatial::doConvert(int &loading) +{ + + if(fname == ""){ + free(out); + out=NULL; + last_fname = _fname; + traces_len=0; + } + if (last_fname != _fname){ + loading=1; + // load a new dat file + printf("triggered %s %s\n",last_fname.toLatin1().data(),_fname.toLatin1().data()); + fflush(stdout); + last_fname = _fname; + if(out) + free(out); + out=NULL; + + QByteArray ba = fname.toLatin1(); + char * fn = ba.data(); + + RawWells wells(fn,0,0); + //wells.SetSubsetToLoad(&col[0], &row[0], col.size()); + wells.OpenForRead(); + flows = wells.NumFlows(); + loading=2; +// LIMIT Flows + if(flows > 200) + flows = 200; +// LIMIT Flows + rows = wells.NumRows(); + cols = wells.NumCols(); + out = (float *)malloc(sizeof(float)*rows*cols*flows); + + cout << "#cols=" << cols << endl; + cout << "#rows=" << rows << endl; + cout << "#nFlow=" << flows << endl; + string flowOrder = wells.FlowOrder(); + cout << "#flowOrder=" << flowOrder << endl; + for(int row=0; rowflowValues[flow]; + } + } + } + + traces_len=flows; + for(int i=0;i +#include "SpatialPlot.h" +#include "qcustomplot.h" + +class WellsSpatial : public SpatialPlot +{ +public: + WellsSpatial(QWidget *parent); + void doConvert(int &loading); + void SetOption(QString option, int state); + +protected: + float Get_Data(int flow, int y, int x); + void DoubleClick_traces(int x, int y, int ts); + +private: + int MaskReads=0; + float *out=NULL; +}; + +#endif // WELLSSPATIAL_H diff --git a/Analysis/DataViewer/WellsTab.cpp b/Analysis/DataViewer/WellsTab.cpp new file mode 100644 index 00000000..705812b5 --- /dev/null +++ b/Analysis/DataViewer/WellsTab.cpp @@ -0,0 +1,30 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "WellsTab.h" + +static const maskCheckBox_t mcbt1[] = { + {"Show Masked",8,Qt::Unchecked} + +}; + +WellsTab::WellsTab(QString _mName, Dialog *_mParent, QWidget *parent) + : ModelTab(_mName, _mParent, parent) +{ + mSpatialPlot = new WellsSpatial(this); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(MakeSlider("flows"),0,0); + grid->addWidget(MakeTraceCB(),1,0); + grid->addWidget(MakeList(mcbt1,sizeof(mcbt1)/sizeof(mcbt1[0])),0,1,2,1); + grid->setRowStretch(0,0); + grid->setRowStretch(1,0); + grid->addWidget(mSpatialPlot,2,0,1,4); + grid->addWidget(MakeTracePlot(1),3,0,1,4); + + // Set the graph styles to no line + for(int i=0;igraph(i)->setLineStyle(QCPGraph::lsNone); + mtracePlot->graph(i)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle,4)); + } + + setLayout(grid); +} diff --git a/Analysis/DataViewer/WellsTab.h b/Analysis/DataViewer/WellsTab.h new file mode 100644 index 00000000..d2970d18 --- /dev/null +++ b/Analysis/DataViewer/WellsTab.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef WELLSTAB_H +#define WELLSTAB_H + +#include +#include +#include "dialog.h" +#include "modeltab.h" +#include "WellsSpatial.h" +#include "AlignmentSpatial.h" + +class Dialog; + +class WellsTab : public ModelTab +{ + Q_OBJECT +public: + explicit WellsTab(QString _mName, Dialog *_mParent, QWidget *parent = 0); + +signals: + +public slots: + +private: + +}; + +#endif // WELLSTAB_H diff --git a/Analysis/DataViewer/dialog.cpp b/Analysis/DataViewer/dialog.cpp new file mode 100644 index 00000000..85c3421f --- /dev/null +++ b/Analysis/DataViewer/dialog.cpp @@ -0,0 +1,505 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ + +#include +#include + +#include "dialog.h" + +//! [0] +Dialog::Dialog() +{ + QTabWidget *tabWidget = new QTabWidget; + tabWidget->addTab((mRawTab = new RawTab("Dat",this)), tr("Raw")); + tabWidget->addTab((mBfMaskTab = new BfMaskTab("BfMask",this)), tr("BfMask")); + tabWidget->addTab((mNoiseTab = new NoiseTab("Noise",this)), tr("Noise")); + tabWidget->addTab((mGainTab = new GainTab("Gain",this)), tr("Gain")); + tabWidget->addTab((mWellsTab = new WellsTab("Wells",this)), tr("1.wells")); + tabWidget->addTab((mAlignmentTab = new AlignmentTab("Alignment",this)), tr("Alignment")); + + connect(tabWidget,SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(tabWidget); + + setLayout(mainLayout); + setCentralWidget(tabWidget); + + + setWindowTitle(tr("Ion Dat Explorer")); + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + + QAction *dummyAct = new QAction(tr(""),this); + fileMenu->addAction(dummyAct); + + OpenDatAct = new QAction(tr("&Open Dat file"), this); + OpenDatAct->setStatusTip(tr("Open Dat file")); + connect(OpenDatAct, SIGNAL(triggered()), this, SLOT(browseDatDir())); + fileMenu->addAction(OpenDatAct); + + OpenBfMaskAct = new QAction(tr("&Open BfMask file"), this); + OpenBfMaskAct->setStatusTip(tr("Open Dat file")); + connect(OpenBfMaskAct, SIGNAL(triggered()), this, SLOT(browseBfMaskDir())); + fileMenu->addAction(OpenBfMaskAct); + + OpenNoiseAct = new QAction(tr("&Open Noise file"), this); + OpenNoiseAct->setStatusTip(tr("Open Noise file")); + connect(OpenNoiseAct, SIGNAL(triggered()), this, SLOT(browseNoiseDir())); + fileMenu->addAction(OpenNoiseAct); + + OpenGainAct = new QAction(tr("&Open Gain file"), this); + OpenGainAct->setStatusTip(tr("Open Gain file")); + connect(OpenGainAct, SIGNAL(triggered()), this, SLOT(browseGainDir())); + fileMenu->addAction(OpenGainAct); + + OpenWellsAct = new QAction(tr("&Open Wells file"), this); + OpenWellsAct->setStatusTip(tr("Open Wells file")); + connect(OpenWellsAct, SIGNAL(triggered()), this, SLOT(browseWellsDir())); + fileMenu->addAction(OpenWellsAct); + + OpenBamAct = new QAction(tr("&Open Bam file"), this); + OpenBamAct->setStatusTip(tr("Open Bam file")); + connect(OpenBamAct, SIGNAL(triggered()), this, SLOT(browseBamDir())); + fileMenu->addAction(OpenBamAct); + + QAction *ClearAct = new QAction(tr("&Clear"), this); + ClearAct->setStatusTip(tr("Clear")); + connect(ClearAct, SIGNAL(triggered()), this, SLOT(Clear())); + fileMenu->addAction(ClearAct); + + dummyAct = new QAction(tr(""),this); + fileMenu->addAction(dummyAct); + QAction *SaveAct = new QAction(tr("&SaveData"), this); + SaveAct->setStatusTip(tr("SaveData")); + connect(SaveAct, SIGNAL(triggered()), this, SLOT(Save())); + fileMenu->addAction(SaveAct); + dummyAct = new QAction(tr(""),this); + fileMenu->addAction(dummyAct); + + QAction *aboutAct = new QAction(tr("&about"), this); + aboutAct->setStatusTip(tr("Create a new file")); + connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); + fileMenu->addAction(aboutAct); + + resize(800, 800); + +} +//! [0] + +void Dialog::Clear() +{ + DatFileName=""; + BfMaskFileName=""; + NoiseFileName=""; + GainFileName=""; + WellsFileName=""; + RawExpmtDir = ""; + RawExpmt_X = -1; + RawExpmt_Y = -1; + SpatialPlot::SetBlockCoord(RawExpmt_X,RawExpmt_Y); + AutoCompletePaths(); + +} + + +void Dialog::Save() +{ + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save Charts File"), "","*.csv"); + + printf("%s: %s\n",__FUNCTION__,fileName.toUtf8().constData()); + if (!fileName.isEmpty()) { + // save all the charts to this file.. + QFile file(fileName); + if (file.open(QIODevice::WriteOnly)) { +// std::cerr << "Cannot open file for writing: " +// << qPrintable(file.errorString()) << std::endl; + QTextStream out(&file); + + mRawTab->Save(out); + mBfMaskTab->Save(out); + mWellsTab->Save(out); + mNoiseTab->Save(out); + mGainTab->Save(out); + mAlignmentTab->Save(out); + file.close(); + } + } +} + +void Dialog::about() +//! [13] //! [14] +{ + QMessageBox::about(this, tr("About Application"), + tr("Ion Data Viewer Application \n" + "Version 0.1")); +} + +void Dialog::browseDatDir() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Dat File"), DatFileName,"*.dat*"); + + printf("%s: %s\n",__FUNCTION__,fileName.toLatin1().data()); + if (!fileName.isEmpty()) { + DatFileName=fileName; + mRawTab->setfilename(DatFileName); + + char tmpStr[2048]; + + strcpy(tmpStr,DatFileName.toLatin1().data()); + + char *tmp=tmpStr; + char *prev1=NULL; + char *prev2=NULL; + + while((tmp = strstr(tmp,"/"))){ + prev2=prev1; + prev1=tmp; + tmp++; + } + if(prev2 && prev2[1] == 'X'){ + int X=0; + int Y=0; + prev2[0]=0; + prev2++; + // prev2 points to the block + sscanf(prev2,"X%d_Y%d",&X,&Y); + RawExpmtDir = tmpStr; + RawExpmt_X = X; + RawExpmt_Y = Y; + SpatialPlot::SetBlockCoord(RawExpmt_X,RawExpmt_Y); + printf("%s: X=%d Y=%d basedir=%s\n",__FUNCTION__,X,Y,tmpStr); + fflush(stdout); + }else if(prev2 && prev2[1] == 't'){ + // thumbnail + thumbnail=1; + int X=0; + int Y=0; + prev2[0]=0; + RawExpmtDir = tmpStr; + RawExpmt_X = X; + RawExpmt_Y = Y; + SpatialPlot::SetBlockCoord(RawExpmt_X,RawExpmt_Y); + printf("%s: X=%d Y=%d basedir=%s\n",__FUNCTION__,X,Y,tmpStr); + fflush(stdout); + + } + AutoCompletePaths(); + currentChanged(0); + } +} + +void Dialog::browseBfMaskDir() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open BfMask File"), BfMaskFileName,"*.bin*"); + + printf("%s: %s\n",__FUNCTION__,fileName.toLatin1().data()); + if (!fileName.isEmpty()) { + BfMaskFileName=fileName; + mBfMaskTab->setfilename(BfMaskFileName); + currentChanged(1); + } +} + +void Dialog::browseNoiseDir() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Noise File"), NoiseFileName,"NoisePic3.dat"); + + printf("%s: %s\n",__FUNCTION__,fileName.toLatin1().data()); + if (!fileName.isEmpty()) { + NoiseFileName=fileName; + mNoiseTab->setfilename(NoiseFileName); + currentChanged(2); + } +} + +void Dialog::browseGainDir() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Gain File"), GainFileName,"Gain.lsr"); + + printf("%s: %s\n",__FUNCTION__,fileName.toLatin1().data()); + if (!fileName.isEmpty()) { + GainFileName=fileName; + mGainTab->setfilename(GainFileName); + currentChanged(3); + } +} + + +void Dialog::browseWellsDir() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Wells File"), WellsFileName,"*.wells"); + + printf("%s: %s\n",__FUNCTION__,fileName.toLatin1().data()); + if (!fileName.isEmpty()) { + WellsFileName=fileName; + mWellsTab->setfilename(WellsFileName); + currentChanged(4); + + char tmpStr[2048]; + char *ptr=NULL; + int found=0; + + strcpy(tmpStr,WellsFileName.toLatin1().data()); + + if((ptr = strstr(tmpStr,"/onboard_results/sigproc_results"))){ + *ptr=0; + found=1; + // were on the instrument?? + } + else if((ptr = strstr(tmpStr,"/sigproc_results"))){ + *ptr=0; + found=1; + thumbnail=1; + OutputDir = tmpStr; + RawExpmtDir = tmpStr + QString("/rawdata/"); + RawExpmt_X = 0; + RawExpmt_Y = 0; + SpatialPlot::SetBlockCoord(RawExpmt_X,RawExpmt_Y); + printf("%s: RawExpmtDir = %s\n",__FUNCTION__,RawExpmtDir.toLatin1().data()); + } + + if(found) + AutoCompletePaths(); + +// char *tmp=tmpStr; +// char *prev1=NULL; +// char *prev2=NULL; +// +// while((tmp = strstr(tmp,"/"))){ +// prev2=prev1; +// prev1=tmp; +// tmp++; +// } +// if(prev2 && prev2[1] == 'X'){ +// int X=0; +// int Y=0; +// prev2[0]=0; +// prev2++; +// // prev2 points to the block +// sscanf(prev2,"X%d_Y%d",&X,&Y); +// RawExpmtDir = tmpStr; +// RawExpmt_X = X; +// RawExpmt_Y = Y; +// printf("%s: X=%d Y=%d basedir=%s\n",__FUNCTION__,X,Y,tmpStr); +// AutoCompletePaths(); +// fflush(stdout); +// } + } +} + +void Dialog::browseBamDir() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Bam File"), BamFileName,"*.bam"); + + printf("%s: %s\n",__FUNCTION__,fileName.toLatin1().data()); + if (!fileName.isEmpty()) { + QFileInfo file(fileName); + //file.absoluteDir(); + BamFileName=file.absolutePath(); + mAlignmentTab->setfilename(BamFileName); + currentChanged(5); + } +} + +void Dialog::currentChanged(int index) +{ + QString MainString = "Ion Dat Explorer "; + if(index == 0) + MainString += "(" + DatFileName + ")"; + else if(index == 1) + MainString += "(" + BfMaskFileName + ")"; + else if(index == 2) + MainString += "(" + WellsFileName + ")"; + else if(index == 3) + MainString += "(" + BamFileName + ")"; + + setWindowTitle(MainString); + +} + +void Dialog::AutoCompletePaths() +{ + qDebug() << __PRETTY_FUNCTION__ << ": RawExpmtDir=" << RawExpmtDir; + qDebug() << __PRETTY_FUNCTION__ << ": OutputDir=" << OutputDir; + + if(RawExpmtDir != "" && OutputDir == ""){ + // try to guess the output dir.. + QString opdir=RawExpmtDir + "/sigproc_results/"; + QFileInfo chk1(opdir); + if(chk1.exists() && chk1.isDir()){ + OutputDir=opdir; + qDebug() << __PRETTY_FUNCTION__ << ": OutputDir=" << OutputDir; + } + } + if(RawExpmtDir != "" && OutputDir == "" && !thumbnail){ + // try to guess the output dir.. + QString opdir=RawExpmtDir + "/onboard_results/sigproc_results/"; + QFileInfo chk1(opdir); + if(chk1.exists() && chk1.isDir()){ + OutputDir=opdir; + qDebug() << __PRETTY_FUNCTION__ << ": OutputDir=" << OutputDir; + } + } + if(RawExpmtDir != "" && OutputDir == ""){ + // try to guess the output dir.. + QString tmp = RawExpmtDir; + int pos = tmp.indexOf("/rawdata"); + if(pos){ + tmp.replace("/rawdata",""); + if(tmp != RawExpmtDir){ + QString opdir=tmp + "/sigproc_results/"; + QFileInfo chk1(opdir); + if(chk1.exists() && chk1.isDir()){ + OutputDir=tmp; + qDebug() << __PRETTY_FUNCTION__ << ": OutputDir=" << OutputDir; + } + } + } + } + + QString BlockName = "X" + QString::number(RawExpmt_X) + + "_Y" + QString::number(RawExpmt_Y); + if(thumbnail) + BlockName="thumbnail"; + + qDebug() << __PRETTY_FUNCTION__ << ": BlockName=" << BlockName; + + if(DatFileName == "" && + RawExpmtDir != "" && + RawExpmt_X != -1 && + RawExpmt_Y != -1){ + // auto-fill in the file + QString tmp = RawExpmtDir + "/" + BlockName + "/acq_0000.dat"; + QFileInfo check_file(tmp); + if (check_file.exists() && check_file.isFile()) { + DatFileName = tmp; + } + } + + if(NoiseFileName == "" && + RawExpmtDir != ""){ + // auto-fill in the file + QString tmp = RawExpmtDir + "/NoisePic3.dat"; + QFileInfo check_file(tmp); + if (check_file.exists() && check_file.isFile()) { + NoiseFileName = tmp; + } + } + + if(GainFileName == "" && + RawExpmtDir != "" && + RawExpmt_X != -1 && + RawExpmt_Y != -1){ + // auto-fill in the file + QString tmp = RawExpmtDir + "/" + BlockName + "/Gain.lsr"; + QFileInfo check_file(tmp); + if (check_file.exists() && check_file.isFile()) { + GainFileName = tmp; + } + } + + if(BfMaskFileName == "" && + RawExpmtDir != "" && + RawExpmt_X != -1 && + RawExpmt_Y != -1){ + // auto-fill in the file + QString tmp = OutputDir + "/block_" + BlockName + "/bfmask.bin"; + QFileInfo check_file(tmp); + if (check_file.exists() && check_file.isFile()) { + BfMaskFileName = tmp; + } + else{ + tmp = OutputDir + "/sigproc_results/bfmask.bin"; + QFileInfo check_file2(tmp); + if (check_file2.exists() && check_file2.isFile()) { + BfMaskFileName = tmp; + } + } + } + + if(WellsFileName == "" && + RawExpmtDir != "" && + RawExpmt_X != -1 && + RawExpmt_Y != -1){ + // auto-fill in the file + QString tmp = OutputDir + "/block_" + BlockName + "/1.wells"; + if(thumbnail){ + tmp = OutputDir + "/sigproc_results/1.wells"; + } + QFileInfo check_file(tmp); + if (check_file.exists() && check_file.isFile()) { + WellsFileName = tmp; + } + } + + if(BamFileName == "" && OutputDir != ""){ + QString tmpName=OutputDir; + if(RawExpmtDir != ""){ + // see if rawdata is in the file path.. + const char *tmp = RawExpmtDir.toUtf8().constData(); + char tmpCname[4096]; + strcpy(tmpCname,tmp); + char *ptr = strstr(tmpCname,"/rawdata"); + if(ptr){ + *ptr=0; + tmpName=tmpCname; + } + } + + BamFileName = tmpName; + } + + + + QString tmp=""; + + tmp = "&Open Dat file"; + if(DatFileName != ""){ + tmp += " (" + DatFileName + ")"; + } + OpenDatAct->setText(tmp); + mRawTab->setfilename(DatFileName); + + tmp = "&Open BfMask file"; + if(BfMaskFileName != ""){ + tmp += " (" + BfMaskFileName + ")"; + } + OpenBfMaskAct->setText(tmp); + mBfMaskTab->setfilename(BfMaskFileName); + + tmp = "&Open Noise file"; + if(NoiseFileName != ""){ + tmp += " (" + NoiseFileName + ")"; + } + OpenNoiseAct->setText(tmp); + mNoiseTab->setfilename(NoiseFileName); + + tmp = "&Open Gain file"; + if(GainFileName != ""){ + tmp += " (" + GainFileName + ")"; + } + OpenGainAct->setText(tmp); + mGainTab->setfilename(GainFileName); + + tmp = "&Open Wells file"; + if(WellsFileName != ""){ + tmp += " (" + WellsFileName + ")"; + } + OpenWellsAct->setText(tmp); + mWellsTab->setfilename(WellsFileName); + + tmp = "&Open Bam file"; + if(BamFileName != ""){ + tmp += " (" + BamFileName + ")"; + } + OpenBamAct->setText(tmp); + mAlignmentTab->setfilename(BamFileName); +} + + diff --git a/Analysis/DataViewer/dialog.h b/Analysis/DataViewer/dialog.h new file mode 100644 index 00000000..feccf06d --- /dev/null +++ b/Analysis/DataViewer/dialog.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ + +#ifndef DIALOG_H +#define DIALOG_H + +#include +#include +#include "RawTab.h" +#include "AlignmentTab.h" +#include "BfMaskTab.h" +#include "WellsTab.h" +#include "NoiseTab.h" +#include "GainTab.h" + + +QT_BEGIN_NAMESPACE +class QAction; +class QDialogButtonBox; +class QGroupBox; +class QLabel; +class QLineEdit; +class QMenu; +class QMenuBar; +class QPushButton; +class QTextEdit; +class QComboBox; +QT_END_NAMESPACE + +class FileTab; +class RawTab; +class AlignmentTab; +class BfMaskTab; +class NoiseTab; +class GainTab; +class WellsTab; + +//! [0] +class Dialog : public QMainWindow //QDialog +{ + Q_OBJECT + +public: + Dialog(); + int GetRawExpmt_Y(){return RawExpmt_Y;} + int GetRawExpmt_X(){return RawExpmt_X;} + +private slots: + void about(); + void browseDatDir(); + void browseBfMaskDir(); + void browseNoiseDir(); + void browseGainDir(); + void browseWellsDir(); + void browseBamDir(); + void currentChanged(int index); + void Clear(); + void Save(); + +private: + void AutoCompletePaths(); + + QString RawExpmtDir=""; + QString OutputDir=""; + int RawExpmt_X=-1; + int RawExpmt_Y=-1; + int thumbnail = 0; +// int RawExpmt_W=-1; +// int RawExpmt_H=-1; + + FileTab *mFileTab; + RawTab *mRawTab; + BfMaskTab *mBfMaskTab; + WellsTab *mWellsTab; + NoiseTab *mNoiseTab; + GainTab *mGainTab; + AlignmentTab *mAlignmentTab; + QString DatFileName=""; + QString BfMaskFileName=""; + QString NoiseFileName=""; + QString GainFileName=""; + QString WellsFileName=""; + QString BamFileName=""; + + QAction *OpenDatAct=NULL; + QAction *OpenBfMaskAct=NULL; + QAction *OpenNoiseAct=NULL; + QAction *OpenGainAct=NULL; + QAction *OpenWellsAct=NULL; + QAction *OpenBamAct=NULL; + + +}; +//! [0] + + + +#endif // DIALOG_H diff --git a/Analysis/DataViewer/main.cpp b/Analysis/DataViewer/main.cpp new file mode 100644 index 00000000..181b2780 --- /dev/null +++ b/Analysis/DataViewer/main.cpp @@ -0,0 +1,22 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ + +#include "dialog.h" + +#include + +#include +#include + + +//! [0] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + Dialog dialog; + + dialog.show(); + + return app.exec(); +} +//! [0] diff --git a/Analysis/DataViewer/main.qml b/Analysis/DataViewer/main.qml new file mode 100644 index 00000000..930c6e79 --- /dev/null +++ b/Analysis/DataViewer/main.qml @@ -0,0 +1,16 @@ +import QtQuick 2.7 +import QtQuick.Window 2.2 + +Window { + visible: true + width: 640 + height: 480 + title: qsTr("Hello World") + + MainForm { + anchors.fill: parent + mouseArea.onClicked: { + console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"')) + } + } +} diff --git a/Analysis/DataViewer/modeltab.cpp b/Analysis/DataViewer/modeltab.cpp new file mode 100644 index 00000000..63976ffa --- /dev/null +++ b/Analysis/DataViewer/modeltab.cpp @@ -0,0 +1,123 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#include "modeltab.h" + +ModelTab::ModelTab(QString _mName, Dialog *_mParent, QWidget *parent) : QWidget(parent) +{ + mParent = _mParent; + mName=_mName; +} + +QComboBox *ModelTab::MakeTraceCB() +{ + mTraceComboBox = new QComboBox; + mTraceComboBox->setEditable(false); + for(int i=0;iaddItem(tr("Trace ") + QString::number(i)); + } + mTraceComboBox->setFixedWidth(250); +// mTraceComboBox->setFixedHeight(24); + + connect(mTraceComboBox, SIGNAL(currentIndexChanged(QString)),this,SLOT(mTraceCombobox_currentIndexChanged(QString))); + return mTraceComboBox; +} + +QTableView *ModelTab::MakeList(const maskCheckBox_t *mcbt, int nitems) +{ + QStandardItemModel *model = new QStandardItemModel(5, 3); + int x=0; + int y=0; + for (int r = 0; r < nitems; ++r) + { + item[r] = new QStandardItem(mcbt[r].name); + + item[r]->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + item[r]->setData(mcbt[r].state, Qt::CheckStateRole); + if(mcbt[r].state == Qt::Checked) + mSpatialPlot->SetOption(mcbt[r].name,1); + model->setItem(y++, x, item[r]); + if(y > 4){ + x++; + y=0; + } + } + + connect(model, SIGNAL(dataChanged ( const QModelIndex&, const QModelIndex&)), this, SLOT(slot_changed(const QModelIndex&, const QModelIndex&))); + + QTableView *list = new QTableView(); + list->setShowGrid(false); + QHeaderView *verticalHeader = list->verticalHeader(); + verticalHeader->sectionResizeMode(QHeaderView::Fixed); + verticalHeader->setDefaultSectionSize(15); + verticalHeader->hide(); + list->horizontalHeader()->hide(); + + list->setModel(model); + list->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Stretch); + list->horizontalHeader()->setSectionResizeMode(1,QHeaderView::Stretch); + list->horizontalHeader()->setSectionResizeMode(2,QHeaderView::Stretch); +// list->setFixedWidth(350); + list->setFixedHeight(78); + return (list); +} + + + +QCustomPlot *ModelTab::MakeTracePlot(int flow_based) +{ + mtracePlot = new QCustomPlot(this); + mSpatialPlot->setTracePlot(mtracePlot,flow_based); + return mtracePlot; +} + + +QGroupBox *ModelTab::MakeSlider(QString name) +{ + QGroupBox *frameBox = new QGroupBox(name); + QVBoxLayout *vframeBox = new QVBoxLayout; + + mSlider = new QSlider(Qt::Horizontal); + connect(mSlider, SIGNAL(valueChanged(int)), this, SLOT(SliderChanged(int))); + vframeBox->addWidget(mSlider); + mSlider->setFocusPolicy(Qt::NoFocus); + frameBox->setLayout(vframeBox); + frameBox->setMaximumHeight(50); + frameBox->setMaximumWidth(250); + if(name == ""){ + mSlider->hide(); + } + return(frameBox); +} + +void ModelTab::SliderChanged(int k) +{ + printf("%s: %d\n",__FUNCTION__,k); + mSpatialPlot->setSlider(k); +} + +void ModelTab::mTraceCombobox_currentIndexChanged(const QString &txt) +{ + printf("%s: %s\n",__FUNCTION__,txt.toLatin1().data()); + fflush(stdout); + int selection=0; + sscanf(txt.toLatin1().data(),"Trace %d",&selection); + mSpatialPlot->setRawTrace(selection); +} + +void ModelTab::setfilename(QString filename) +{ + printf("%s:\n",__FUNCTION__); + mSpatialPlot->setfilename(filename); +} + +void ModelTab::slot_changed(const QModelIndex& topLeft, const QModelIndex&bootmRight) +{ + (void)bootmRight; + int index=topLeft.column()*5+topLeft.row(); + qDebug() << __PRETTY_FUNCTION__ << ": Item " << index << " "; + +// if(index < mNitems) + { + bool state = (item[index]->checkState() == Qt::Checked); + mSpatialPlot->SetOption(item[index]->text(),state); + } +} diff --git a/Analysis/DataViewer/modeltab.h b/Analysis/DataViewer/modeltab.h new file mode 100644 index 00000000..b56a7733 --- /dev/null +++ b/Analysis/DataViewer/modeltab.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved */ +#ifndef MODELTAB_H +#define MODELTAB_H + +#include +#include +#include "qcustomplot.h" +#include "SpatialPlot.h" +//#include "dialog.h" + +typedef struct { + const char *name; + int option; + Qt::CheckState state; +}maskCheckBox_t; + +class Dialog; + +class ModelTab : public QWidget +{ + Q_OBJECT +public: + explicit ModelTab(QString _mName, Dialog *_mParent, QWidget *parent = 0); + void setfilename(QString filename); + void Save(QTextStream &strm){strm << mName << endl; mSpatialPlot->Save(strm);} + +signals: + +public slots: + void SliderChanged(int k); + void mTraceCombobox_currentIndexChanged(const QString &txt); + void slot_changed(const QModelIndex& topLeft, const QModelIndex&bootmRight); + +protected: + QTableView *MakeList(const maskCheckBox_t *mcbt, int nitems); + QComboBox *MakeTraceCB(); + QGroupBox *MakeSlider(QString name); + QCustomPlot *MakeTracePlot(int flow_based=0); + + QString mName=""; + QComboBox* combo; + + Dialog *mParent; + QSlider *mSlider; + SpatialPlot *mSpatialPlot; + QCustomPlot *mtracePlot; + QStandardItem* item[25]={NULL}; + QComboBox *mTraceComboBox; + +}; + +#endif // MODELTAB_H diff --git a/Analysis/DataViewer/qcustomplot.cpp b/Analysis/DataViewer/qcustomplot.cpp new file mode 100644 index 00000000..30860827 --- /dev/null +++ b/Analysis/DataViewer/qcustomplot.cpp @@ -0,0 +1,23553 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011-2015 Emanuel Eichhammer ** +** ** +** This program is free software: you can redistribute it and/or modify ** +** it under the terms of the GNU General Public License as published by ** +** the Free Software Foundation, either version 3 of the License, or ** +** (at your option) any later version. ** +** ** +** This program is distributed in the hope that it will be useful, ** +** but WITHOUT ANY WARRANTY; without even the implied warranty of ** +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** +** GNU General Public License for more details. ** +** ** +** You should have received a copy of the GNU General Public License ** +** along with this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Emanuel Eichhammer ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 22.12.15 ** +** Version: 1.3.2 ** +****************************************************************************/ + +#include "qcustomplot.h" + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPainter +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPainter + \brief QPainter subclass used internally + + This QPainter subclass is used to provide some extended functionality e.g. for tweaking position + consistency between antialiased and non-antialiased painting. Further it provides workarounds + for QPainter quirks. + + \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and + restore. So while it is possible to pass a QCPPainter instance to a function that expects a + QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because + it will call the base class implementations of the functions actually hidden by QCPPainter). +*/ + +/*! + Creates a new QCPPainter instance and sets default values +*/ +QCPPainter::QCPPainter() : + QPainter(), + mModes(pmDefault), + mIsAntialiasing(false) +{ + // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and + // a call to begin() will follow +} + +/*! + Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just + like the analogous QPainter constructor, begins painting on \a device immediately. + + Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5. +*/ +QCPPainter::QCPPainter(QPaintDevice *device) : + QPainter(device), + mModes(pmDefault), + mIsAntialiasing(false) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions. + if (isActive()) + setRenderHint(QPainter::NonCosmeticDefaultPen); +#endif +} + +QCPPainter::~QCPPainter() +{ +} + +/*! + Sets the pen of the painter and applies certain fixes to it, depending on the mode of this + QCPPainter. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::setPen(const QPen &pen) +{ + QPainter::setPen(pen); + if (mModes.testFlag(pmNonCosmetic)) + makeNonCosmetic(); +} + +/*! \overload + + Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of + this QCPPainter. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::setPen(const QColor &color) +{ + QPainter::setPen(color); + if (mModes.testFlag(pmNonCosmetic)) + makeNonCosmetic(); +} + +/*! \overload + + Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of + this QCPPainter. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::setPen(Qt::PenStyle penStyle) +{ + QPainter::setPen(penStyle); + if (mModes.testFlag(pmNonCosmetic)) + makeNonCosmetic(); +} + +/*! \overload + + Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when + antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to + integer coordinates and then passes it to the original drawLine. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::drawLine(const QLineF &line) +{ + if (mIsAntialiasing || mModes.testFlag(pmVectorized)) + QPainter::drawLine(line); + else + QPainter::drawLine(line.toLine()); +} + +/*! + Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint + with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between + antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for + AA/Non-AA painting). +*/ +void QCPPainter::setAntialiasing(bool enabled) +{ + setRenderHint(QPainter::Antialiasing, enabled); + if (mIsAntialiasing != enabled) + { + mIsAntialiasing = enabled; + if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs + { + if (mIsAntialiasing) + translate(0.5, 0.5); + else + translate(-0.5, -0.5); + } + } +} + +/*! + Sets the mode of the painter. This controls whether the painter shall adjust its + fixes/workarounds optimized for certain output devices. +*/ +void QCPPainter::setModes(QCPPainter::PainterModes modes) +{ + mModes = modes; +} + +/*! + Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a + device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5, + all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that + behaviour. + + The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets + the render hint as appropriate. + + \note this function hides the non-virtual base class implementation. +*/ +bool QCPPainter::begin(QPaintDevice *device) +{ + bool result = QPainter::begin(device); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions. + if (result) + setRenderHint(QPainter::NonCosmeticDefaultPen); +#endif + return result; +} + +/*! \overload + + Sets the mode of the painter. This controls whether the painter shall adjust its + fixes/workarounds optimized for certain output devices. +*/ +void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled) +{ + if (!enabled && mModes.testFlag(mode)) + mModes &= ~mode; + else if (enabled && !mModes.testFlag(mode)) + mModes |= mode; +} + +/*! + Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to + QPainter, the save/restore functions are reimplemented to also save/restore those members. + + \note this function hides the non-virtual base class implementation. + + \see restore +*/ +void QCPPainter::save() +{ + mAntialiasingStack.push(mIsAntialiasing); + QPainter::save(); +} + +/*! + Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to + QPainter, the save/restore functions are reimplemented to also save/restore those members. + + \note this function hides the non-virtual base class implementation. + + \see save +*/ +void QCPPainter::restore() +{ + if (!mAntialiasingStack.isEmpty()) + mIsAntialiasing = mAntialiasingStack.pop(); + else + qDebug() << Q_FUNC_INFO << "Unbalanced save/restore"; + QPainter::restore(); +} + +/*! + Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen + overrides when the \ref pmNonCosmetic mode is set. +*/ +void QCPPainter::makeNonCosmetic() +{ + if (qFuzzyIsNull(pen().widthF())) + { + QPen p = pen(); + p.setWidth(1); + QPainter::setPen(p); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPScatterStyle +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPScatterStyle + \brief Represents the visual appearance of scatter points + + This class holds information about shape, color and size of scatter points. In plottables like + QCPGraph it is used to store how scatter points shall be drawn. For example, \ref + QCPGraph::setScatterStyle takes a QCPScatterStyle instance. + + A scatter style consists of a shape (\ref setShape), a line color (\ref setPen) and possibly a + fill (\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can + be controlled with \ref setSize. + + \section QCPScatterStyle-defining Specifying a scatter style + + You can set all these configurations either by calling the respective functions on an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1 + + Or you can use one of the various constructors that take different parameter combinations, making + it easy to specify a scatter style in a single call, like so: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2 + + \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable + + There are two constructors which leave the pen undefined: \ref QCPScatterStyle() and \ref + QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \ref + isPenDefined will return false. It leads to scatter points that inherit the pen from the + plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line + color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes + it very convenient to set up typical scatter settings: + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation + + Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works + because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly + into a QCPScatterStyle instance (that's the \ref QCPScatterStyle(ScatterShape shape, double size) + constructor with a default for \a size). In those cases, C++ allows directly supplying a \ref + ScatterShape, where actually a QCPScatterStyle is expected. + + \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps + + QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points. + + For custom shapes, you can provide a QPainterPath with the desired shape to the \ref + setCustomPath function or call the constructor that takes a painter path. The scatter shape will + automatically be set to \ref ssCustom. + + For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively you can use the + constructor that takes a QPixmap. The scatter shape will automatically be set to \ref ssPixmap. + Note that \ref setSize does not influence the appearance of the pixmap. +*/ + +/* start documentation of inline functions */ + +/*! \fn bool QCPScatterStyle::isNone() const + + Returns whether the scatter shape is \ref ssNone. + + \see setShape +*/ + +/*! \fn bool QCPScatterStyle::isPenDefined() const + + Returns whether a pen has been defined for this scatter style. + + The pen is undefined if a constructor is called that does not carry \a pen as parameter. Those are + \ref QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). If the pen is + left undefined, the scatter color will be inherited from the plottable that uses this scatter + style. + + \see setPen +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined. + + Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited + from the plottable that uses this scatter style. +*/ +QCPScatterStyle::QCPScatterStyle() : + mSize(6), + mShape(ssNone), + mPen(Qt::NoPen), + mBrush(Qt::NoBrush), + mPenDefined(false) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or + brush is defined. + + Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited + from the plottable that uses this scatter style. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) : + mSize(size), + mShape(shape), + mPen(Qt::NoPen), + mBrush(Qt::NoBrush), + mPenDefined(false) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color, + and size to \a size. No brush is defined, i.e. the scatter point will not be filled. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) : + mSize(size), + mShape(shape), + mPen(QPen(color)), + mBrush(Qt::NoBrush), + mPenDefined(true) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color, + the brush color to \a fill (with a solid pattern), and size to \a size. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) : + mSize(size), + mShape(shape), + mPen(QPen(color)), + mBrush(QBrush(fill)), + mPenDefined(true) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set to \a pen, the + brush to \a brush, and size to \a size. + + \warning In some cases it might be tempting to directly use a pen style like Qt::NoPen as \a pen + and a color like Qt::blue as \a brush. Notice however, that the corresponding call\n + QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)\n + doesn't necessarily lead C++ to use this constructor in some cases, but might mistake + Qt::NoPen for a QColor and use the + \ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) + constructor instead (which will lead to an unexpected look of the scatter points). To prevent + this, be more explicit with the parameter types. For example, use QBrush(Qt::blue) + instead of just Qt::blue, to clearly point out to the compiler that this constructor is + wanted. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) : + mSize(size), + mShape(shape), + mPen(pen), + mBrush(brush), + mPenDefined(pen.style() != Qt::NoPen) +{ +} + +/*! + Creates a new QCPScatterStyle instance which will show the specified \a pixmap. The scatter shape + is set to \ref ssPixmap. +*/ +QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) : + mSize(5), + mShape(ssPixmap), + mPen(Qt::NoPen), + mBrush(Qt::NoBrush), + mPixmap(pixmap), + mPenDefined(false) +{ +} + +/*! + Creates a new QCPScatterStyle instance with a custom shape that is defined via \a customPath. The + scatter shape is set to \ref ssCustom. + + The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly + different meaning than for built-in scatter points: The custom path will be drawn scaled by a + factor of \a size/6.0. Since the default \a size is 6, the custom path will appear at a its + natural size by default. To double the size of the path for example, set \a size to 12. +*/ +QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) : + mSize(size), + mShape(ssCustom), + mPen(pen), + mBrush(brush), + mCustomPath(customPath), + mPenDefined(pen.style() != Qt::NoPen) +{ +} + +/*! + Sets the size (pixel diameter) of the drawn scatter points to \a size. + + \see setShape +*/ +void QCPScatterStyle::setSize(double size) +{ + mSize = size; +} + +/*! + Sets the shape to \a shape. + + Note that the calls \ref setPixmap and \ref setCustomPath automatically set the shape to \ref + ssPixmap and \ref ssCustom, respectively. + + \see setSize +*/ +void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape) +{ + mShape = shape; +} + +/*! + Sets the pen that will be used to draw scatter points to \a pen. + + If the pen was previously undefined (see \ref isPenDefined), the pen is considered defined after + a call to this function, even if \a pen is Qt::NoPen. + + \see setBrush +*/ +void QCPScatterStyle::setPen(const QPen &pen) +{ + mPenDefined = true; + mPen = pen; +} + +/*! + Sets the brush that will be used to fill scatter points to \a brush. Note that not all scatter + shapes have fillable areas. For example, \ref ssPlus does not while \ref ssCircle does. + + \see setPen +*/ +void QCPScatterStyle::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the pixmap that will be drawn as scatter point to \a pixmap. + + Note that \ref setSize does not influence the appearance of the pixmap. + + The scatter shape is automatically set to \ref ssPixmap. +*/ +void QCPScatterStyle::setPixmap(const QPixmap &pixmap) +{ + setShape(ssPixmap); + mPixmap = pixmap; +} + +/*! + Sets the custom shape that will be drawn as scatter point to \a customPath. + + The scatter shape is automatically set to \ref ssCustom. +*/ +void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) +{ + setShape(ssCustom); + mCustomPath = customPath; +} + +/*! + Applies the pen and the brush of this scatter style to \a painter. If this scatter style has an + undefined pen (\ref isPenDefined), sets the pen of \a painter to \a defaultPen instead. + + This function is used by plottables (or any class that wants to draw scatters) just before a + number of scatters with this style shall be drawn with the \a painter. + + \see drawShape +*/ +void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const +{ + painter->setPen(mPenDefined ? mPen : defaultPen); + painter->setBrush(mBrush); +} + +/*! + Draws the scatter shape with \a painter at position \a pos. + + This function does not modify the pen or the brush on the painter, as \ref applyTo is meant to be + called before scatter points are drawn with \ref drawShape. + + \see applyTo +*/ +void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const +{ + drawShape(painter, pos.x(), pos.y()); +} + +/*! \overload + Draws the scatter shape with \a painter at position \a x and \a y. +*/ +void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const +{ + double w = mSize/2.0; + switch (mShape) + { + case ssNone: break; + case ssDot: + { + painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y)); + break; + } + case ssCross: + { + painter->drawLine(QLineF(x-w, y-w, x+w, y+w)); + painter->drawLine(QLineF(x-w, y+w, x+w, y-w)); + break; + } + case ssPlus: + { + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + break; + } + case ssCircle: + { + painter->drawEllipse(QPointF(x , y), w, w); + break; + } + case ssDisc: + { + QBrush b = painter->brush(); + painter->setBrush(painter->pen().color()); + painter->drawEllipse(QPointF(x , y), w, w); + painter->setBrush(b); + break; + } + case ssSquare: + { + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + break; + } + case ssDiamond: + { + painter->drawLine(QLineF(x-w, y, x, y-w)); + painter->drawLine(QLineF( x, y-w, x+w, y)); + painter->drawLine(QLineF(x+w, y, x, y+w)); + painter->drawLine(QLineF( x, y+w, x-w, y)); + break; + } + case ssStar: + { + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707)); + painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707)); + break; + } + case ssTriangle: + { + painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w)); + painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w)); + painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w)); + break; + } + case ssTriangleInverted: + { + painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w)); + painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w)); + painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w)); + break; + } + case ssCrossSquare: + { + painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95)); + painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w)); + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + break; + } + case ssPlusSquare: + { + painter->drawLine(QLineF(x-w, y, x+w*0.95, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + break; + } + case ssCrossCircle: + { + painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670)); + painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707)); + painter->drawEllipse(QPointF(x, y), w, w); + break; + } + case ssPlusCircle: + { + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + painter->drawEllipse(QPointF(x, y), w, w); + break; + } + case ssPeace: + { + painter->drawLine(QLineF(x, y-w, x, y+w)); + painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707)); + painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707)); + painter->drawEllipse(QPointF(x, y), w, w); + break; + } + case ssPixmap: + { + painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap); + break; + } + case ssCustom: + { + QTransform oldTransform = painter->transform(); + painter->translate(x, y); + painter->scale(mSize/6.0, mSize/6.0); + painter->drawPath(mCustomPath); + painter->setTransform(oldTransform); + break; + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayer + \brief A layer that may contain objects, to control the rendering order + + The Layering system of QCustomPlot is the mechanism to control the rendering order of the + elements inside the plot. + + It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of + one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer, + QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers + bottom to top and successively draws the layerables of the layers. + + A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base + class from which almost all visible objects derive, like axes, grids, graphs, items, etc. + + Initially, QCustomPlot has five layers: "background", "grid", "main", "axes" and "legend" (in + that order). The top two layers "axes" and "legend" contain the default axes and legend, so they + will be drawn on top. In the middle, there is the "main" layer. It is initially empty and set as + the current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc. + are created on this layer by default. Then comes the "grid" layer which contains the QCPGrid + instances (which belong tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background + shall be drawn behind everything else, thus the default QCPAxisRect instance is placed on the + "background" layer. Of course, the layer affiliation of the individual objects can be changed as + required (\ref QCPLayerable::setLayer). + + Controlling the ordering of objects is easy: Create a new layer in the position you want it to + be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with + QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will + be placed on the new layer automatically, due to the current layer setting. Alternatively you + could have also ignored the current layer setting and just moved the objects with + QCPLayerable::setLayer to the desired layer after creating them. + + It is also possible to move whole layers. For example, If you want the grid to be shown in front + of all plottables/items on the "main" layer, just move it above "main" with + QCustomPlot::moveLayer. + + The rendering order within one layer is simply by order of creation or insertion. The item + created last (or added last to the layer), is drawn on top of all other objects on that layer. + + When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below + the deleted layer, see QCustomPlot::removeLayer. +*/ + +/* start documentation of inline functions */ + +/*! \fn QList QCPLayer::children() const + + Returns a list of all layerables on this layer. The order corresponds to the rendering order: + layerables with higher indices are drawn above layerables with lower indices. +*/ + +/*! \fn int QCPLayer::index() const + + Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be + accessed via \ref QCustomPlot::layer. + + Layers with higher indices will be drawn above layers with lower indices. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPLayer instance. + + Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead. + + \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot. + This check is only performed by \ref QCustomPlot::addLayer. +*/ +QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) : + QObject(parentPlot), + mParentPlot(parentPlot), + mName(layerName), + mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function + mVisible(true) +{ + // Note: no need to make sure layerName is unique, because layer + // management is done with QCustomPlot functions. +} + +QCPLayer::~QCPLayer() +{ + // If child layerables are still on this layer, detach them, so they don't try to reach back to this + // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted + // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to + // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.) + + while (!mChildren.isEmpty()) + mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild() + + if (mParentPlot->currentLayer() == this) + qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand."; +} + +/*! + Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this + layer will be invisible. + + This function doesn't change the visibility property of the layerables (\ref + QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the + visibility of the parent layer into account. +*/ +void QCPLayer::setVisible(bool visible) +{ + mVisible = visible; +} + +/*! \internal + + Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will + be prepended to the list, i.e. be drawn beneath the other layerables already in the list. + + This function does not change the \a mLayer member of \a layerable to this layer. (Use + QCPLayerable::setLayer to change the layer of an object, not this function.) + + \see removeChild +*/ +void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) +{ + if (!mChildren.contains(layerable)) + { + if (prepend) + mChildren.prepend(layerable); + else + mChildren.append(layerable); + } else + qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast(layerable); +} + +/*! \internal + + Removes the \a layerable from the list of this layer. + + This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer + to change the layer of an object, not this function.) + + \see addChild +*/ +void QCPLayer::removeChild(QCPLayerable *layerable) +{ + if (!mChildren.removeOne(layerable)) + qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast(layerable); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayerable +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayerable + \brief Base class for all drawable objects + + This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid + etc. + + Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking + the layers accordingly. + + For details about the layering mechanism, see the QCPLayer documentation. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const + + Returns the parent layerable of this layerable. The parent layerable is used to provide + visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables + only get drawn if their parent layerables are visible, too. + + Note that a parent layerable is not necessarily also the QObject parent for memory management. + Further, a layerable doesn't always have a parent layerable, so this function may return 0. + + A parent layerable is set implicitly with when placed inside layout elements and doesn't need to be + set manually by the user. +*/ + +/* end documentation of inline functions */ +/* start documentation of pure virtual functions */ + +/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0 + \internal + + This function applies the default antialiasing setting to the specified \a painter, using the + function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when + \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing + setting may be specified individually, this function should set the antialiasing state of the + most prominent entity. In this case however, the \ref draw function usually calls the specialized + versions of this function before drawing each entity, effectively overriding the setting of the + default antialiasing hint. + + First example: QCPGraph has multiple entities that have an antialiasing setting: The graph + line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased, + QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't + only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's + antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and + QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw + calls the respective specialized applyAntialiasingHint function. + + Second example: QCPItemLine consists only of a line so there is only one antialiasing + setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by + all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the + respective layerable subclass.) Consequently it only has the normal + QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to + care about setting any antialiasing states, because the default antialiasing hint is already set + on the painter when the \ref draw function is called, and that's the state it wants to draw the + line with. +*/ + +/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0 + \internal + + This function draws the layerable with the specified \a painter. It is only called by + QCustomPlot, if the layerable is visible (\ref setVisible). + + Before this function is called, the painter's antialiasing state is set via \ref + applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was + set to \ref clipRect. +*/ + +/* end documentation of pure virtual functions */ +/* start documentation of signals */ + +/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer); + + This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to + a different layer. + + \see setLayer +*/ + +/* end documentation of signals */ + +/*! + Creates a new QCPLayerable instance. + + Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the + derived classes. + + If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a + targetLayer is an empty string, it places itself on the current layer of the plot (see \ref + QCustomPlot::setCurrentLayer). + + It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later + time with \ref initializeParentPlot. + + The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable + parents are mainly used to control visibility in a hierarchy of layerables. This means a + layerable is only drawn, if all its ancestor layerables are also visible. Note that \a + parentLayerable does not become the QObject-parent (for memory management) of this layerable, \a + plot does. It is not uncommon to set the QObject-parent to something else in the constructors of + QCPLayerable subclasses, to guarantee a working destruction hierarchy. +*/ +QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) : + QObject(plot), + mVisible(true), + mParentPlot(plot), + mParentLayerable(parentLayerable), + mLayer(0), + mAntialiased(true) +{ + if (mParentPlot) + { + if (targetLayer.isEmpty()) + setLayer(mParentPlot->currentLayer()); + else if (!setLayer(targetLayer)) + qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed."; + } +} + +QCPLayerable::~QCPLayerable() +{ + if (mLayer) + { + mLayer->removeChild(this); + mLayer = 0; + } +} + +/*! + Sets the visibility of this layerable object. If an object is not visible, it will not be drawn + on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not + possible. +*/ +void QCPLayerable::setVisible(bool on) +{ + mVisible = on; +} + +/*! + Sets the \a layer of this layerable object. The object will be placed on top of the other objects + already on \a layer. + + If \a layer is 0, this layerable will not be on any layer and thus not appear in the plot (or + interact/receive events). + + Returns true if the layer of this layerable was successfully changed to \a layer. +*/ +bool QCPLayerable::setLayer(QCPLayer *layer) +{ + return moveToLayer(layer, false); +} + +/*! \overload + Sets the layer of this layerable object by name + + Returns true on success, i.e. if \a layerName is a valid layer name. +*/ +bool QCPLayerable::setLayer(const QString &layerName) +{ + if (!mParentPlot) + { + qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set"; + return false; + } + if (QCPLayer *layer = mParentPlot->layer(layerName)) + { + return setLayer(layer); + } else + { + qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName; + return false; + } +} + +/*! + Sets whether this object will be drawn antialiased or not. + + Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPLayerable::setAntialiased(bool enabled) +{ + mAntialiased = enabled; +} + +/*! + Returns whether this layerable is visible, taking the visibility of the layerable parent and the + visibility of the layer this layerable is on into account. This is the method that is consulted + to decide whether a layerable shall be drawn or not. + + If this layerable has a direct layerable parent (usually set via hierarchies implemented in + subclasses, like in the case of QCPLayoutElement), this function returns true only if this + layerable has its visibility set to true and the parent layerable's \ref realVisibility returns + true. + + If this layerable doesn't have a direct layerable parent, returns the state of this layerable's + visibility. +*/ +bool QCPLayerable::realVisibility() const +{ + return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility()); +} + +/*! + This function is used to decide whether a click hits a layerable object or not. + + \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the + shortest pixel distance of this point to the object. If the object is either invisible or the + distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the + object is not selectable, -1.0 is returned, too. + + If the object is represented not by single lines but by an area like a \ref QCPItemText or the + bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In + these cases this function thus returns a constant value greater zero but still below the parent + plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99). + + Providing a constant value for area objects allows selecting line objects even when they are + obscured by such area objects, by clicking close to the lines (i.e. closer than + 0.99*selectionTolerance). + + The actual setting of the selection state is not done by this function. This is handled by the + parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified + via the selectEvent/deselectEvent methods. + + \a details is an optional output parameter. Every layerable subclass may place any information + in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot + decides on the basis of this selectTest call, that the object was successfully selected. The + subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part + objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked + is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be + placed in \a details. So in the subsequent \ref selectEvent, the decision which part was + selected doesn't have to be done a second time for a single selection operation. + + You may pass 0 as \a details to indicate that you are not interested in those selection details. + + \see selectEvent, deselectEvent, QCustomPlot::setInteractions +*/ +double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(pos) + Q_UNUSED(onlySelectable) + Q_UNUSED(details) + return -1.0; +} + +/*! \internal + + Sets the parent plot of this layerable. Use this function once to set the parent plot if you have + passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to + another one. + + Note that, unlike when passing a non-null parent plot in the constructor, this function does not + make \a parentPlot the QObject-parent of this layerable. If you want this, call + QObject::setParent(\a parentPlot) in addition to this function. + + Further, you will probably want to set a layer (\ref setLayer) after calling this function, to + make the layerable appear on the QCustomPlot. + + The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized + so they can react accordingly (e.g. also initialize the parent plot of child layerables, like + QCPLayout does). +*/ +void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot) +{ + if (mParentPlot) + { + qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized"; + return; + } + + if (!parentPlot) + qDebug() << Q_FUNC_INFO << "called with parentPlot zero"; + + mParentPlot = parentPlot; + parentPlotInitialized(mParentPlot); +} + +/*! \internal + + Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not + become the QObject-parent (for memory management) of this layerable. + + The parent layerable has influence on the return value of the \ref realVisibility method. Only + layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be + drawn. + + \see realVisibility +*/ +void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable) +{ + mParentLayerable = parentLayerable; +} + +/*! \internal + + Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to + the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is + false, the object will be appended. + + Returns true on success, i.e. if \a layer is a valid layer. +*/ +bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) +{ + if (layer && !mParentPlot) + { + qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set"; + return false; + } + if (layer && layer->parentPlot() != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable"; + return false; + } + + QCPLayer *oldLayer = mLayer; + if (mLayer) + mLayer->removeChild(this); + mLayer = layer; + if (mLayer) + mLayer->addChild(this, prepend); + if (mLayer != oldLayer) + emit layerChanged(mLayer); + return true; +} + +/*! \internal + + Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a + localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is + controlled via \a overrideElement. +*/ +void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const +{ + if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement)) + painter->setAntialiasing(false); + else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement)) + painter->setAntialiasing(true); + else + painter->setAntialiasing(localAntialiased); +} + +/*! \internal + + This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting + of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the + parent plot is set at a later time. + + For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any + QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level + element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To + propagate the parent plot to all the children of the hierarchy, the top level element then uses + this function to pass the parent plot on to its child elements. + + The default implementation does nothing. + + \see initializeParentPlot +*/ +void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot) +{ + Q_UNUSED(parentPlot) +} + +/*! \internal + + Returns the selection category this layerable shall belong to. The selection category is used in + conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and + which aren't. + + Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref + QCP::iSelectOther. This is what the default implementation returns. + + \see QCustomPlot::setInteractions +*/ +QCP::Interaction QCPLayerable::selectionCategory() const +{ + return QCP::iSelectOther; +} + +/*! \internal + + Returns the clipping rectangle of this layerable object. By default, this is the viewport of the + parent QCustomPlot. Specific subclasses may reimplement this function to provide different + clipping rects. + + The returned clipping rect is set on the painter before the draw function of the respective + object is called. +*/ +QRect QCPLayerable::clipRect() const +{ + if (mParentPlot) + return mParentPlot->viewport(); + else + return QRect(); +} + +/*! \internal + + This event is called when the layerable shall be selected, as a consequence of a click by the + user. Subclasses should react to it by setting their selection state appropriately. The default + implementation does nothing. + + \a event is the mouse event that caused the selection. \a additive indicates, whether the user + was holding the multi-select-modifier while performing the selection (see \ref + QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled + (i.e. become selected when unselected and unselected when selected). + + Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e. + returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot). + The \a details data you output from \ref selectTest is fed back via \a details here. You may + use it to transport any kind of information from the selectTest to the possibly subsequent + selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable + that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need + to do the calculation again to find out which part was actually clicked. + + \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must + set the value either to true or false, depending on whether the selection state of this layerable + was actually changed. For layerables that only are selectable as a whole and not in parts, this + is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the + selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the + layerable was previously unselected and now is switched to the selected state. + + \see selectTest, deselectEvent +*/ +void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(additive) + Q_UNUSED(details) + Q_UNUSED(selectionStateChanged) +} + +/*! \internal + + This event is called when the layerable shall be deselected, either as consequence of a user + interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by + unsetting their selection appropriately. + + just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must + return true or false when the selection state of this layerable has changed or not changed, + respectively. + + \see selectTest, selectEvent +*/ +void QCPLayerable::deselectEvent(bool *selectionStateChanged) +{ + Q_UNUSED(selectionStateChanged) +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPRange +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPRange + \brief Represents the range an axis is encompassing. + + contains a \a lower and \a upper double value and provides convenience input, output and + modification functions. + + \see QCPAxis::setRange +*/ + +/*! + Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller + intervals would cause errors due to the 11-bit exponent of double precision numbers, + corresponding to a minimum magnitude of roughly 1e-308. + \see validRange, maxRange +*/ +const double QCPRange::minRange = 1e-280; + +/*! + Maximum values (negative and positive) the range will accept in range-changing functions. + Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers, + corresponding to a maximum magnitude of roughly 1e308. + Since the number of planck-volumes in the entire visible universe is only ~1e183, this should + be enough. + \see validRange, minRange +*/ +const double QCPRange::maxRange = 1e250; + +/*! + Constructs a range with \a lower and \a upper set to zero. +*/ +QCPRange::QCPRange() : + lower(0), + upper(0) +{ +} + +/*! \overload + Constructs a range with the specified \a lower and \a upper values. +*/ +QCPRange::QCPRange(double lower, double upper) : + lower(lower), + upper(upper) +{ + normalize(); +} + +/*! + Returns the size of the range, i.e. \a upper-\a lower +*/ +double QCPRange::size() const +{ + return upper-lower; +} + +/*! + Returns the center of the range, i.e. (\a upper+\a lower)*0.5 +*/ +double QCPRange::center() const +{ + return (upper+lower)*0.5; +} + +/*! + Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values + are swapped. +*/ +void QCPRange::normalize() +{ + if (lower > upper) + qSwap(lower, upper); +} + +/*! + Expands this range such that \a otherRange is contained in the new range. It is assumed that both + this range and \a otherRange are normalized (see \ref normalize). + + If \a otherRange is already inside the current range, this function does nothing. + + \see expanded +*/ +void QCPRange::expand(const QCPRange &otherRange) +{ + if (lower > otherRange.lower) + lower = otherRange.lower; + if (upper < otherRange.upper) + upper = otherRange.upper; +} + + +/*! + Returns an expanded range that contains this and \a otherRange. It is assumed that both this + range and \a otherRange are normalized (see \ref normalize). + + \see expand +*/ +QCPRange QCPRange::expanded(const QCPRange &otherRange) const +{ + QCPRange result = *this; + result.expand(otherRange); + return result; +} + +/*! + Returns a sanitized version of the range. Sanitized means for logarithmic scales, that + the range won't span the positive and negative sign domain, i.e. contain zero. Further + \a lower will always be numerically smaller (or equal) to \a upper. + + If the original range does span positive and negative sign domains or contains zero, + the returned range will try to approximate the original range as good as possible. + If the positive interval of the original range is wider than the negative interval, the + returned range will only contain the positive interval, with lower bound set to \a rangeFac or + \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval + is wider than the positive interval, this time by changing the \a upper bound. +*/ +QCPRange QCPRange::sanitizedForLogScale() const +{ + double rangeFac = 1e-3; + QCPRange sanitizedRange(lower, upper); + sanitizedRange.normalize(); + // can't have range spanning negative and positive values in log plot, so change range to fix it + //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1)) + if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) + { + // case lower is 0 + if (rangeFac < sanitizedRange.upper*rangeFac) + sanitizedRange.lower = rangeFac; + else + sanitizedRange.lower = sanitizedRange.upper*rangeFac; + } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1)) + else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) + { + // case upper is 0 + if (-rangeFac > sanitizedRange.lower*rangeFac) + sanitizedRange.upper = -rangeFac; + else + sanitizedRange.upper = sanitizedRange.lower*rangeFac; + } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) + { + // find out whether negative or positive interval is wider to decide which sign domain will be chosen + if (-sanitizedRange.lower > sanitizedRange.upper) + { + // negative is wider, do same as in case upper is 0 + if (-rangeFac > sanitizedRange.lower*rangeFac) + sanitizedRange.upper = -rangeFac; + else + sanitizedRange.upper = sanitizedRange.lower*rangeFac; + } else + { + // positive is wider, do same as in case lower is 0 + if (rangeFac < sanitizedRange.upper*rangeFac) + sanitizedRange.lower = rangeFac; + else + sanitizedRange.lower = sanitizedRange.upper*rangeFac; + } + } + // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper= lower && value <= upper; +} + +/*! + Checks, whether the specified range is within valid bounds, which are defined + as QCPRange::maxRange and QCPRange::minRange. + A valid range means: + \li range bounds within -maxRange and maxRange + \li range size above minRange + \li range size below maxRange +*/ +bool QCPRange::validRange(double lower, double upper) +{ + return (lower > -maxRange && + upper < maxRange && + qAbs(lower-upper) > minRange && + qAbs(lower-upper) < maxRange && + !(lower > 0 && qIsInf(upper/lower)) && + !(upper < 0 && qIsInf(lower/upper))); +} + +/*! + \overload + Checks, whether the specified range is within valid bounds, which are defined + as QCPRange::maxRange and QCPRange::minRange. + A valid range means: + \li range bounds within -maxRange and maxRange + \li range size above minRange + \li range size below maxRange +*/ +bool QCPRange::validRange(const QCPRange &range) +{ + return (range.lower > -maxRange && + range.upper < maxRange && + qAbs(range.lower-range.upper) > minRange && + qAbs(range.lower-range.upper) < maxRange && + !(range.lower > 0 && qIsInf(range.upper/range.lower)) && + !(range.upper < 0 && qIsInf(range.lower/range.upper))); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPMarginGroup +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPMarginGroup + \brief A margin group allows synchronization of margin sides if working with multiple layout elements. + + QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that + they will all have the same size, based on the largest required margin in the group. + + \n + \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup" + \n + + In certain situations it is desirable that margins at specific sides are synchronized across + layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will + provide a cleaner look to the user if the left and right margins of the two axis rects are of the + same size. The left axis of the top axis rect will then be at the same horizontal position as the + left axis of the lower axis rect, making them appear aligned. The same applies for the right + axes. This is what QCPMarginGroup makes possible. + + To add/remove a specific side of a layout element to/from a margin group, use the \ref + QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call + \ref clear, or just delete the margin group. + + \section QCPMarginGroup-example Example + + First create a margin group: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1 + Then set this group on the layout element sides: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2 + Here, we've used the first two axis rects of the plot and synchronized their left margins with + each other and their right margins with each other. +*/ + +/* start documentation of inline functions */ + +/*! \fn QList QCPMarginGroup::elements(QCP::MarginSide side) const + + Returns a list of all layout elements that have their margin \a side associated with this margin + group. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPMarginGroup instance in \a parentPlot. +*/ +QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) : + QObject(parentPlot), + mParentPlot(parentPlot) +{ + mChildren.insert(QCP::msLeft, QList()); + mChildren.insert(QCP::msRight, QList()); + mChildren.insert(QCP::msTop, QList()); + mChildren.insert(QCP::msBottom, QList()); +} + +QCPMarginGroup::~QCPMarginGroup() +{ + clear(); +} + +/*! + Returns whether this margin group is empty. If this function returns true, no layout elements use + this margin group to synchronize margin sides. +*/ +bool QCPMarginGroup::isEmpty() const +{ + QHashIterator > it(mChildren); + while (it.hasNext()) + { + it.next(); + if (!it.value().isEmpty()) + return false; + } + return true; +} + +/*! + Clears this margin group. The synchronization of the margin sides that use this margin group is + lifted and they will use their individual margin sizes again. +*/ +void QCPMarginGroup::clear() +{ + // make all children remove themselves from this margin group: + QHashIterator > it(mChildren); + while (it.hasNext()) + { + it.next(); + const QList elements = it.value(); + for (int i=elements.size()-1; i>=0; --i) + elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild + } +} + +/*! \internal + + Returns the synchronized common margin for \a side. This is the margin value that will be used by + the layout element on the respective side, if it is part of this margin group. + + The common margin is calculated by requesting the automatic margin (\ref + QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin + group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into + account, too.) +*/ +int QCPMarginGroup::commonMargin(QCP::MarginSide side) const +{ + // query all automatic margins of the layout elements in this margin group side and find maximum: + int result = 0; + const QList elements = mChildren.value(side); + for (int i=0; iautoMargins().testFlag(side)) + continue; + int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side)); + if (m > result) + result = m; + } + return result; +} + +/*! \internal + + Adds \a element to the internal list of child elements, for the margin \a side. + + This function does not modify the margin group property of \a element. +*/ +void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element) +{ + if (!mChildren[side].contains(element)) + mChildren[side].append(element); + else + qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast(element); +} + +/*! \internal + + Removes \a element from the internal list of child elements, for the margin \a side. + + This function does not modify the margin group property of \a element. +*/ +void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element) +{ + if (!mChildren[side].removeOne(element)) + qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast(element); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayoutElement +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayoutElement + \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system". + + This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses. + + A Layout element is a rectangular object which can be placed in layouts. It has an outer rect + (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference + between outer and inner rect is called its margin. The margin can either be set to automatic or + manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be + set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic, + the layout element subclass will control the value itself (via \ref calculateAutoMargin). + + Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level + layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref + QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested. + + Thus in QCustomPlot one can divide layout elements into two categories: The ones that are + invisible by themselves, because they don't draw anything. Their only purpose is to manage the + position and size of other layout elements. This category of layout elements usually use + QCPLayout as base class. Then there is the category of layout elements which actually draw + something. For example, QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does + not necessarily mean that the latter category can't have child layout elements. QCPLegend for + instance, actually derives from QCPLayoutGrid and the individual legend items are child layout + elements in the grid layout. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayout *QCPLayoutElement::layout() const + + Returns the parent layout of this layout element. +*/ + +/*! \fn QRect QCPLayoutElement::rect() const + + Returns the inner rect of this layout element. The inner rect is the outer rect (\ref + setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins). + + In some cases, the area between outer and inner rect is left blank. In other cases the margin + area is used to display peripheral graphics while the main content is in the inner rect. This is + where automatic margin calculation becomes interesting because it allows the layout element to + adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect + draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if + \ref setAutoMargins is enabled) according to the space required by the labels of the axes. +*/ + +/*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event) + + This event is called, if the mouse was pressed while being inside the outer rect of this layout + element. +*/ + +/*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event) + + This event is called, if the mouse is moved inside the outer rect of this layout element. +*/ + +/*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event) + + This event is called, if the mouse was previously pressed inside the outer rect of this layout + element and is now released. +*/ + +/*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event) + + This event is called, if the mouse is double-clicked inside the outer rect of this layout + element. +*/ + +/*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event) + + This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this + layout element. +*/ + +/* end documentation of inline functions */ + +/*! + Creates an instance of QCPLayoutElement and sets default values. +*/ +QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) : + QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout) + mParentLayout(0), + mMinimumSize(), + mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), + mRect(0, 0, 0, 0), + mOuterRect(0, 0, 0, 0), + mMargins(0, 0, 0, 0), + mMinimumMargins(0, 0, 0, 0), + mAutoMargins(QCP::msAll) +{ +} + +QCPLayoutElement::~QCPLayoutElement() +{ + setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any + // unregister at layout: + if (qobject_cast(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor + mParentLayout->take(this); +} + +/*! + Sets the outer rect of this layout element. If the layout element is inside a layout, the layout + sets the position and size of this layout element using this function. + + Calling this function externally has no effect, since the layout will overwrite any changes to + the outer rect upon the next replot. + + The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect. + + \see rect +*/ +void QCPLayoutElement::setOuterRect(const QRect &rect) +{ + if (mOuterRect != rect) + { + mOuterRect = rect; + mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom()); + } +} + +/*! + Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all + sides, this function is used to manually set the margin on those sides. Sides that are still set + to be handled automatically are ignored and may have any value in \a margins. + + The margin is the distance between the outer rect (controlled by the parent layout via \ref + setOuterRect) and the inner \ref rect (which usually contains the main content of this layout + element). + + \see setAutoMargins +*/ +void QCPLayoutElement::setMargins(const QMargins &margins) +{ + if (mMargins != margins) + { + mMargins = margins; + mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom()); + } +} + +/*! + If \ref setAutoMargins is enabled on some or all margins, this function is used to provide + minimum values for those margins. + + The minimum values are not enforced on margin sides that were set to be under manual control via + \ref setAutoMargins. + + \see setAutoMargins +*/ +void QCPLayoutElement::setMinimumMargins(const QMargins &margins) +{ + if (mMinimumMargins != margins) + { + mMinimumMargins = margins; + } +} + +/*! + Sets on which sides the margin shall be calculated automatically. If a side is calculated + automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is + set to be controlled manually, the value may be specified with \ref setMargins. + + Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref + setMarginGroup), to synchronize (align) it with other layout elements in the plot. + + \see setMinimumMargins, setMargins +*/ +void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) +{ + mAutoMargins = sides; +} + +/*! + Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to + respect the \a size here by changing row/column sizes in the layout accordingly. + + If the parent layout size is not sufficient to satisfy all minimum size constraints of its child + layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot + propagates the layout's size constraints to the outside by setting its own minimum QWidget size + accordingly, so violations of \a size should be exceptions. +*/ +void QCPLayoutElement::setMinimumSize(const QSize &size) +{ + if (mMinimumSize != size) + { + mMinimumSize = size; + if (mParentLayout) + mParentLayout->sizeConstraintsChanged(); + } +} + +/*! \overload + + Sets the minimum size for the inner \ref rect of this layout element. +*/ +void QCPLayoutElement::setMinimumSize(int width, int height) +{ + setMinimumSize(QSize(width, height)); +} + +/*! + Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to + respect the \a size here by changing row/column sizes in the layout accordingly. +*/ +void QCPLayoutElement::setMaximumSize(const QSize &size) +{ + if (mMaximumSize != size) + { + mMaximumSize = size; + if (mParentLayout) + mParentLayout->sizeConstraintsChanged(); + } +} + +/*! \overload + + Sets the maximum size for the inner \ref rect of this layout element. +*/ +void QCPLayoutElement::setMaximumSize(int width, int height) +{ + setMaximumSize(QSize(width, height)); +} + +/*! + Sets the margin \a group of the specified margin \a sides. + + Margin groups allow synchronizing specified margins across layout elements, see the documentation + of \ref QCPMarginGroup. + + To unset the margin group of \a sides, set \a group to 0. + + Note that margin groups only work for margin sides that are set to automatic (\ref + setAutoMargins). +*/ +void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group) +{ + QVector sideVector; + if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft); + if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight); + if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop); + if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom); + + for (int i=0; iremoveChild(side, this); + + if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there + { + mMarginGroups.remove(side); + } else // setting to a new group + { + mMarginGroups[side] = group; + group->addChild(side, this); + } + } + } +} + +/*! + Updates the layout element and sub-elements. This function is automatically called before every + replot by the parent layout element. It is called multiple times, once for every \ref + UpdatePhase. The phases are run through in the order of the enum values. For details about what + happens at the different phases, see the documentation of \ref UpdatePhase. + + Layout elements that have child elements should call the \ref update method of their child + elements, and pass the current \a phase unchanged. + + The default implementation executes the automatic margin mechanism in the \ref upMargins phase. + Subclasses should make sure to call the base class implementation. +*/ +void QCPLayoutElement::update(UpdatePhase phase) +{ + if (phase == upMargins) + { + if (mAutoMargins != QCP::msNone) + { + // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group: + QMargins newMargins = mMargins; + QList allMarginSides = QList() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom; + foreach (QCP::MarginSide side, allMarginSides) + { + if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically + { + if (mMarginGroups.contains(side)) + QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group + else + QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly + // apply minimum margin restrictions: + if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side)) + QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side)); + } + } + setMargins(newMargins); + } + } +} + +/*! + Returns the minimum size this layout element (the inner \ref rect) may be compressed to. + + if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this + function to determine the minimum allowed size of this layout element. (A manual minimum size is + considered set if it is non-zero.) +*/ +QSize QCPLayoutElement::minimumSizeHint() const +{ + return mMinimumSize; +} + +/*! + Returns the maximum size this layout element (the inner \ref rect) may be expanded to. + + if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this + function to determine the maximum allowed size of this layout element. (A manual maximum size is + considered set if it is smaller than Qt's QWIDGETSIZE_MAX.) +*/ +QSize QCPLayoutElement::maximumSizeHint() const +{ + return mMaximumSize; +} + +/*! + Returns a list of all child elements in this layout element. If \a recursive is true, all + sub-child elements are included in the list, too. + + \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have + empty cells which yield 0 at the respective index.) +*/ +QList QCPLayoutElement::elements(bool recursive) const +{ + Q_UNUSED(recursive) + return QList(); +} + +/*! + Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer + rect, this method returns a value corresponding to 0.99 times the parent plot's selection + tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is + true, -1.0 is returned. + + See \ref QCPLayerable::selectTest for a general explanation of this virtual method. + + QCPLayoutElement subclasses may reimplement this method to provide more specific selection test + behaviour. +*/ +double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + + if (onlySelectable) + return -1; + + if (QRectF(mOuterRect).contains(pos)) + { + if (mParentPlot) + return mParentPlot->selectionTolerance()*0.99; + else + { + qDebug() << Q_FUNC_INFO << "parent plot not defined"; + return -1; + } + } else + return -1; +} + +/*! \internal + + propagates the parent plot initialization to all child elements, by calling \ref + QCPLayerable::initializeParentPlot on them. +*/ +void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot) +{ + foreach (QCPLayoutElement* el, elements(false)) + { + if (!el->parentPlot()) + el->initializeParentPlot(parentPlot); + } +} + +/*! \internal + + Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a + side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the + returned value will not be smaller than the specified minimum margin. + + The default implementation just returns the respective manual margin (\ref setMargins) or the + minimum margin, whichever is larger. +*/ +int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side) +{ + return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayout +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayout + \brief The abstract base class for layouts + + This is an abstract base class for layout elements whose main purpose is to define the position + and size of other child layout elements. In most cases, layouts don't draw anything themselves + (but there are exceptions to this, e.g. QCPLegend). + + QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts. + + QCPLayout introduces a common interface for accessing and manipulating the child elements. Those + functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref + simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions + to this interface which are more specialized to the form of the layout. For example, \ref + QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid + more conveniently. + + Since this is an abstract base class, you can't instantiate it directly. Rather use one of its + subclasses like QCPLayoutGrid or QCPLayoutInset. + + For a general introduction to the layout system, see the dedicated documentation page \ref + thelayoutsystem "The Layout System". +*/ + +/* start documentation of pure virtual functions */ + +/*! \fn virtual int QCPLayout::elementCount() const = 0 + + Returns the number of elements/cells in the layout. + + \see elements, elementAt +*/ + +/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0 + + Returns the element in the cell with the given \a index. If \a index is invalid, returns 0. + + Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g. + QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check + whether a cell is empty or not. + + \see elements, elementCount, takeAt +*/ + +/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0 + + Removes the element with the given \a index from the layout and returns it. + + If the \a index is invalid or the cell with that index is empty, returns 0. + + Note that some layouts don't remove the respective cell right away but leave an empty cell after + successful removal of the layout element. To collapse empty cells, use \ref simplify. + + \see elementAt, take +*/ + +/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0 + + Removes the specified \a element from the layout and returns true on success. + + If the \a element isn't in this layout, returns false. + + Note that some layouts don't remove the respective cell right away but leave an empty cell after + successful removal of the layout element. To collapse empty cells, use \ref simplify. + + \see takeAt +*/ + +/* end documentation of pure virtual functions */ + +/*! + Creates an instance of QCPLayout and sets default values. Note that since QCPLayout + is an abstract base class, it can't be instantiated directly. +*/ +QCPLayout::QCPLayout() +{ +} + +/*! + First calls the QCPLayoutElement::update base class implementation to update the margins on this + layout. + + Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells. + + Finally, \ref update is called on all child elements. +*/ +void QCPLayout::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + + // set child element rects according to layout: + if (phase == upLayout) + updateLayout(); + + // propagate update call to child elements: + const int elCount = elementCount(); + for (int i=0; iupdate(phase); + } +} + +/* inherits documentation from base class */ +QList QCPLayout::elements(bool recursive) const +{ + const int c = elementCount(); + QList result; +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + result.reserve(c); +#endif + for (int i=0; ielements(recursive); + } + } + return result; +} + +/*! + Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the + default implementation does nothing. + + Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit + simplification while QCPLayoutGrid does. +*/ +void QCPLayout::simplify() +{ +} + +/*! + Removes and deletes the element at the provided \a index. Returns true on success. If \a index is + invalid or points to an empty cell, returns false. + + This function internally uses \ref takeAt to remove the element from the layout and then deletes + the returned element. Note that some layouts don't remove the respective cell right away but leave an + empty cell after successful removal of the layout element. To collapse empty cells, use \ref + simplify. + + \see remove, takeAt +*/ +bool QCPLayout::removeAt(int index) +{ + if (QCPLayoutElement *el = takeAt(index)) + { + delete el; + return true; + } else + return false; +} + +/*! + Removes and deletes the provided \a element. Returns true on success. If \a element is not in the + layout, returns false. + + This function internally uses \ref takeAt to remove the element from the layout and then deletes + the element. Note that some layouts don't remove the respective cell right away but leave an + empty cell after successful removal of the layout element. To collapse empty cells, use \ref + simplify. + + \see removeAt, take +*/ +bool QCPLayout::remove(QCPLayoutElement *element) +{ + if (take(element)) + { + delete element; + return true; + } else + return false; +} + +/*! + Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure + all empty cells are collapsed. + + \see remove, removeAt +*/ +void QCPLayout::clear() +{ + for (int i=elementCount()-1; i>=0; --i) + { + if (elementAt(i)) + removeAt(i); + } + simplify(); +} + +/*! + Subclasses call this method to report changed (minimum/maximum) size constraints. + + If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref + sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of + QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout, + it may update itself and resize cells accordingly. +*/ +void QCPLayout::sizeConstraintsChanged() const +{ + if (QWidget *w = qobject_cast(parent())) + w->updateGeometry(); + else if (QCPLayout *l = qobject_cast(parent())) + l->sizeConstraintsChanged(); +} + +/*! \internal + + Subclasses reimplement this method to update the position and sizes of the child elements/cells + via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing. + + The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay + within that rect. + + \ref getSectionSizes may help with the reimplementation of this function. + + \see update +*/ +void QCPLayout::updateLayout() +{ +} + + +/*! \internal + + Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the + \ref QCPLayerable::parentLayerable and the QObject parent to this layout. + + Further, if \a el didn't previously have a parent plot, calls \ref + QCPLayerable::initializeParentPlot on \a el to set the paret plot. + + This method is used by subclass specific methods that add elements to the layout. Note that this + method only changes properties in \a el. The removal from the old layout and the insertion into + the new layout must be done additionally. +*/ +void QCPLayout::adoptElement(QCPLayoutElement *el) +{ + if (el) + { + el->mParentLayout = this; + el->setParentLayerable(this); + el->setParent(this); + if (!el->parentPlot()) + el->initializeParentPlot(mParentPlot); + } else + qDebug() << Q_FUNC_INFO << "Null element passed"; +} + +/*! \internal + + Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout + and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent + QCustomPlot. + + This method is used by subclass specific methods that remove elements from the layout (e.g. \ref + take or \ref takeAt). Note that this method only changes properties in \a el. The removal from + the old layout must be done additionally. +*/ +void QCPLayout::releaseElement(QCPLayoutElement *el) +{ + if (el) + { + el->mParentLayout = 0; + el->setParentLayerable(0); + el->setParent(mParentPlot); + // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot + } else + qDebug() << Q_FUNC_INFO << "Null element passed"; +} + +/*! \internal + + This is a helper function for the implementation of \ref updateLayout in subclasses. + + It calculates the sizes of one-dimensional sections with provided constraints on maximum section + sizes, minimum section sizes, relative stretch factors and the final total size of all sections. + + The QVector entries refer to the sections. Thus all QVectors must have the same size. + + \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size + imposed, set all vector values to Qt's QWIDGETSIZE_MAX. + + \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size + imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than + \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words, + not exceeding the allowed total size is taken to be more important than not going below minimum + section sizes.) + + \a stretchFactors give the relative proportions of the sections to each other. If all sections + shall be scaled equally, set all values equal. If the first section shall be double the size of + each individual other section, set the first number of \a stretchFactors to double the value of + the other individual values (e.g. {2, 1, 1, 1}). + + \a totalSize is the value that the final section sizes will add up to. Due to rounding, the + actual sum may differ slightly. If you want the section sizes to sum up to exactly that value, + you could distribute the remaining difference on the sections. + + The return value is a QVector containing the section sizes. +*/ +QVector QCPLayout::getSectionSizes(QVector maxSizes, QVector minSizes, QVector stretchFactors, int totalSize) const +{ + if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size()) + { + qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors; + return QVector(); + } + if (stretchFactors.isEmpty()) + return QVector(); + int sectionCount = stretchFactors.size(); + QVector sectionSizes(sectionCount); + // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections): + int minSizeSum = 0; + for (int i=0; i minimumLockedSections; + QList unfinishedSections; + for (int i=0; i result(sectionCount); + for (int i=0; i= 0 && row < mElements.size()) + { + if (column >= 0 && column < mElements.first().size()) + { + if (QCPLayoutElement *result = mElements.at(row).at(column)) + return result; + else + qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column; + } else + qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column; + } else + qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column; + return 0; +} + +/*! + Returns the number of rows in the layout. + + \see columnCount +*/ +int QCPLayoutGrid::rowCount() const +{ + return mElements.size(); +} + +/*! + Returns the number of columns in the layout. + + \see rowCount +*/ +int QCPLayoutGrid::columnCount() const +{ + if (mElements.size() > 0) + return mElements.first().size(); + else + return 0; +} + +/*! + Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it + is first removed from there. If \a row or \a column don't exist yet, the layout is expanded + accordingly. + + Returns true if the element was added successfully, i.e. if the cell at \a row and \a column + didn't already have an element. + + \see element, hasElement, take, remove +*/ +bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) +{ + if (element) + { + if (!hasElement(row, column)) + { + if (element->layout()) // remove from old layout first + element->layout()->take(element); + expandTo(row+1, column+1); + mElements[row][column] = element; + adoptElement(element); + return true; + } else + qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column; + } else + qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column; + return false; +} + +/*! + Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't + empty. + + \see element +*/ +bool QCPLayoutGrid::hasElement(int row, int column) +{ + if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount()) + return mElements.at(row).at(column); + else + return false; +} + +/*! + Sets the stretch \a factor of \a column. + + Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond + their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref + QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + + The default stretch factor of newly created rows/columns is 1. + + \see setColumnStretchFactors, setRowStretchFactor +*/ +void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) +{ + if (column >= 0 && column < columnCount()) + { + if (factor > 0) + mColumnStretchFactors[column] = factor; + else + qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor; + } else + qDebug() << Q_FUNC_INFO << "Invalid column:" << column; +} + +/*! + Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount. + + Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond + their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref + QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + + The default stretch factor of newly created rows/columns is 1. + + \see setColumnStretchFactor, setRowStretchFactors +*/ +void QCPLayoutGrid::setColumnStretchFactors(const QList &factors) +{ + if (factors.size() == mColumnStretchFactors.size()) + { + mColumnStretchFactors = factors; + for (int i=0; i= 0 && row < rowCount()) + { + if (factor > 0) + mRowStretchFactors[row] = factor; + else + qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor; + } else + qDebug() << Q_FUNC_INFO << "Invalid row:" << row; +} + +/*! + Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount. + + Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond + their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref + QCPLayoutElement::setMaximumSize), regardless of the stretch factor. + + The default stretch factor of newly created rows/columns is 1. + + \see setRowStretchFactor, setColumnStretchFactors +*/ +void QCPLayoutGrid::setRowStretchFactors(const QList &factors) +{ + if (factors.size() == mRowStretchFactors.size()) + { + mRowStretchFactors = factors; + for (int i=0; i()); + mRowStretchFactors.append(1); + } + // go through rows and expand columns as necessary: + int newColCount = qMax(columnCount(), newColumnCount); + for (int i=0; i rowCount()) + newIndex = rowCount(); + + mRowStretchFactors.insert(newIndex, 1); + QList newRow; + for (int col=0; col columnCount()) + newIndex = columnCount(); + + mColumnStretchFactors.insert(newIndex, 1); + for (int row=0; row minColWidths, minRowHeights, maxColWidths, maxRowHeights; + getMinimumRowColSizes(&minColWidths, &minRowHeights); + getMaximumRowColSizes(&maxColWidths, &maxRowHeights); + + int totalRowSpacing = (rowCount()-1) * mRowSpacing; + int totalColSpacing = (columnCount()-1) * mColumnSpacing; + QVector colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing); + QVector rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing); + + // go through cells and set rects accordingly: + int yOffset = mRect.top(); + for (int row=0; row 0) + yOffset += rowHeights.at(row-1)+mRowSpacing; + int xOffset = mRect.left(); + for (int col=0; col 0) + xOffset += colWidths.at(col-1)+mColumnSpacing; + if (mElements.at(row).at(col)) + mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row))); + } + } +} + +/* inherits documentation from base class */ +int QCPLayoutGrid::elementCount() const +{ + return rowCount()*columnCount(); +} + +/* inherits documentation from base class */ +QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const +{ + if (index >= 0 && index < elementCount()) + return mElements.at(index / columnCount()).at(index % columnCount()); + else + return 0; +} + +/* inherits documentation from base class */ +QCPLayoutElement *QCPLayoutGrid::takeAt(int index) +{ + if (QCPLayoutElement *el = elementAt(index)) + { + releaseElement(el); + mElements[index / columnCount()][index % columnCount()] = 0; + return el; + } else + { + qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; + return 0; + } +} + +/* inherits documentation from base class */ +bool QCPLayoutGrid::take(QCPLayoutElement *element) +{ + if (element) + { + for (int i=0; i QCPLayoutGrid::elements(bool recursive) const +{ + QList result; + int colC = columnCount(); + int rowC = rowCount(); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + result.reserve(colC*rowC); +#endif + for (int row=0; rowelements(recursive); + } + } + return result; +} + +/*! + Simplifies the layout by collapsing rows and columns which only contain empty cells. +*/ +void QCPLayoutGrid::simplify() +{ + // remove rows with only empty cells: + for (int row=rowCount()-1; row>=0; --row) + { + bool hasElements = false; + for (int col=0; col=0; --col) + { + bool hasElements = false; + for (int row=0; row minColWidths, minRowHeights; + getMinimumRowColSizes(&minColWidths, &minRowHeights); + QSize result(0, 0); + for (int i=0; i maxColWidths, maxRowHeights; + getMaximumRowColSizes(&maxColWidths, &maxRowHeights); + + QSize result(0, 0); + for (int i=0; i *minColWidths, QVector *minRowHeights) const +{ + *minColWidths = QVector(columnCount(), 0); + *minRowHeights = QVector(rowCount(), 0); + for (int row=0; rowminimumSizeHint(); + QSize min = mElements.at(row).at(col)->minimumSize(); + QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height()); + if (minColWidths->at(col) < final.width()) + (*minColWidths)[col] = final.width(); + if (minRowHeights->at(row) < final.height()) + (*minRowHeights)[row] = final.height(); + } + } + } +} + +/*! \internal + + Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights + respectively. + + The maximum height of a row is the smallest maximum height of any element in that row. The + maximum width of a column is the smallest maximum width of any element in that column. + + This is a helper function for \ref updateLayout. + + \see getMinimumRowColSizes +*/ +void QCPLayoutGrid::getMaximumRowColSizes(QVector *maxColWidths, QVector *maxRowHeights) const +{ + *maxColWidths = QVector(columnCount(), QWIDGETSIZE_MAX); + *maxRowHeights = QVector(rowCount(), QWIDGETSIZE_MAX); + for (int row=0; rowmaximumSizeHint(); + QSize max = mElements.at(row).at(col)->maximumSize(); + QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height()); + if (maxColWidths->at(col) > final.width()) + (*maxColWidths)[col] = final.width(); + if (maxRowHeights->at(row) > final.height()) + (*maxRowHeights)[row] = final.height(); + } + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayoutInset +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPLayoutInset + \brief A layout that places child elements aligned to the border or arbitrarily positioned + + Elements are placed either aligned to the border or at arbitrary position in the area of the + layout. Which placement applies is controlled with the \ref InsetPlacement (\ref + setInsetPlacement). + + Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or + addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset + placement will default to \ref ipBorderAligned and the element will be aligned according to the + \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at + arbitrary position and size, defined by \a rect. + + The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively. + + This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout. +*/ + +/* start documentation of inline functions */ + +/*! \fn virtual void QCPLayoutInset::simplify() + + The QCPInsetLayout does not need simplification since it can never have empty cells due to its + linear index structure. This method does nothing. +*/ + +/* end documentation of inline functions */ + +/*! + Creates an instance of QCPLayoutInset and sets default values. +*/ +QCPLayoutInset::QCPLayoutInset() +{ +} + +QCPLayoutInset::~QCPLayoutInset() +{ + // clear all child layout elements. This is important because only the specific layouts know how + // to handle removing elements (clear calls virtual removeAt method to do that). + clear(); +} + +/*! + Returns the placement type of the element with the specified \a index. +*/ +QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const +{ + if (elementAt(index)) + return mInsetPlacement.at(index); + else + { + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; + return ipFree; + } +} + +/*! + Returns the alignment of the element with the specified \a index. The alignment only has a + meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned. +*/ +Qt::Alignment QCPLayoutInset::insetAlignment(int index) const +{ + if (elementAt(index)) + return mInsetAlignment.at(index); + else + { + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; + return 0; + } +} + +/*! + Returns the rect of the element with the specified \a index. The rect only has a + meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree. +*/ +QRectF QCPLayoutInset::insetRect(int index) const +{ + if (elementAt(index)) + return mInsetRect.at(index); + else + { + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; + return QRectF(); + } +} + +/*! + Sets the inset placement type of the element with the specified \a index to \a placement. + + \see InsetPlacement +*/ +void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement) +{ + if (elementAt(index)) + mInsetPlacement[index] = placement; + else + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +} + +/*! + If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function + is used to set the alignment of the element with the specified \a index to \a alignment. + + \a alignment is an or combination of the following alignment flags: Qt::AlignLeft, + Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other + alignment flags will be ignored. +*/ +void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment) +{ + if (elementAt(index)) + mInsetAlignment[index] = alignment; + else + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +} + +/*! + If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the + position and size of the element with the specified \a index to \a rect. + + \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1) + will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right + corner of the layout, with 35% width and height of the parent layout. + + Note that the minimum and maximum sizes of the embedded element (\ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced. +*/ +void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) +{ + if (elementAt(index)) + mInsetRect[index] = rect; + else + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +} + +/* inherits documentation from base class */ +void QCPLayoutInset::updateLayout() +{ + for (int i=0; iminimumSizeHint(); + QSize maxSizeHint = mElements.at(i)->maximumSizeHint(); + finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width()); + finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height()); + finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width()); + finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height()); + if (mInsetPlacement.at(i) == ipFree) + { + insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(), + rect().y()+rect().height()*mInsetRect.at(i).y(), + rect().width()*mInsetRect.at(i).width(), + rect().height()*mInsetRect.at(i).height()); + if (insetRect.size().width() < finalMinSize.width()) + insetRect.setWidth(finalMinSize.width()); + if (insetRect.size().height() < finalMinSize.height()) + insetRect.setHeight(finalMinSize.height()); + if (insetRect.size().width() > finalMaxSize.width()) + insetRect.setWidth(finalMaxSize.width()); + if (insetRect.size().height() > finalMaxSize.height()) + insetRect.setHeight(finalMaxSize.height()); + } else if (mInsetPlacement.at(i) == ipBorderAligned) + { + insetRect.setSize(finalMinSize); + Qt::Alignment al = mInsetAlignment.at(i); + if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x()); + else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width()); + else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter + if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y()); + else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height()); + else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter + } + mElements.at(i)->setOuterRect(insetRect); + } +} + +/* inherits documentation from base class */ +int QCPLayoutInset::elementCount() const +{ + return mElements.size(); +} + +/* inherits documentation from base class */ +QCPLayoutElement *QCPLayoutInset::elementAt(int index) const +{ + if (index >= 0 && index < mElements.size()) + return mElements.at(index); + else + return 0; +} + +/* inherits documentation from base class */ +QCPLayoutElement *QCPLayoutInset::takeAt(int index) +{ + if (QCPLayoutElement *el = elementAt(index)) + { + releaseElement(el); + mElements.removeAt(index); + mInsetPlacement.removeAt(index); + mInsetAlignment.removeAt(index); + mInsetRect.removeAt(index); + return el; + } else + { + qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; + return 0; + } +} + +/* inherits documentation from base class */ +bool QCPLayoutInset::take(QCPLayoutElement *element) +{ + if (element) + { + for (int i=0; irealVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0) + return mParentPlot->selectionTolerance()*0.99; + } + return -1; +} + +/*! + Adds the specified \a element to the layout as an inset aligned at the border (\ref + setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a + alignment. + + \a alignment is an or combination of the following alignment flags: Qt::AlignLeft, + Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other + alignment flags will be ignored. + + \see addElement(QCPLayoutElement *element, const QRectF &rect) +*/ +void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment) +{ + if (element) + { + if (element->layout()) // remove from old layout first + element->layout()->take(element); + mElements.append(element); + mInsetPlacement.append(ipBorderAligned); + mInsetAlignment.append(alignment); + mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4)); + adoptElement(element); + } else + qDebug() << Q_FUNC_INFO << "Can't add null element"; +} + +/*! + Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref + setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a + rect. + + \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1) + will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right + corner of the layout, with 35% width and height of the parent layout. + + \see addElement(QCPLayoutElement *element, Qt::Alignment alignment) +*/ +void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) +{ + if (element) + { + if (element->layout()) // remove from old layout first + element->layout()->take(element); + mElements.append(element); + mInsetPlacement.append(ipFree); + mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop); + mInsetRect.append(rect); + adoptElement(element); + } else + qDebug() << Q_FUNC_INFO << "Can't add null element"; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLineEnding +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLineEnding + \brief Handles the different ending decorations for line-like items + + \image html QCPLineEnding.png "The various ending styles currently supported" + + For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine + has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail. + + The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can + be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of + the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item. + For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite + directions, e.g. "outward". This can be changed by \ref setInverted, which would make the + respective arrow point inward. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify a + QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. + \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead +*/ + +/*! + Creates a QCPLineEnding instance with default values (style \ref esNone). +*/ +QCPLineEnding::QCPLineEnding() : + mStyle(esNone), + mWidth(8), + mLength(10), + mInverted(false) +{ +} + +/*! + Creates a QCPLineEnding instance with the specified values. +*/ +QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) : + mStyle(style), + mWidth(width), + mLength(length), + mInverted(inverted) +{ +} + +/*! + Sets the style of the ending decoration. +*/ +void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style) +{ + mStyle = style; +} + +/*! + Sets the width of the ending decoration, if the style supports it. On arrows, for example, the + width defines the size perpendicular to the arrow's pointing direction. + + \see setLength +*/ +void QCPLineEnding::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets the length of the ending decoration, if the style supports it. On arrows, for example, the + length defines the size in pointing direction. + + \see setWidth +*/ +void QCPLineEnding::setLength(double length) +{ + mLength = length; +} + +/*! + Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point + inward when \a inverted is set to true. + + Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or + discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are + affected by it, which can be used to control to which side the half bar points to. +*/ +void QCPLineEnding::setInverted(bool inverted) +{ + mInverted = inverted; +} + +/*! \internal + + Returns the maximum pixel radius the ending decoration might cover, starting from the position + the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item). + + This is relevant for clipping. Only omit painting of the decoration when the position where the + decoration is supposed to be drawn is farther away from the clipping rect than the returned + distance. +*/ +double QCPLineEnding::boundingDistance() const +{ + switch (mStyle) + { + case esNone: + return 0; + + case esFlatArrow: + case esSpikeArrow: + case esLineArrow: + case esSkewedBar: + return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length + + case esDisc: + case esSquare: + case esDiamond: + case esBar: + case esHalfBar: + return mWidth*1.42; // items that only have a width -> width*sqrt(2) + + } + return 0; +} + +/*! + Starting from the origin of this line ending (which is style specific), returns the length + covered by the line ending symbol, in backward direction. + + For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if + both have the same \ref setLength value, because the spike arrow has an inward curved back, which + reduces the length along its center axis (the drawing origin for arrows is at the tip). + + This function is used for precise, style specific placement of line endings, for example in + QCPAxes. +*/ +double QCPLineEnding::realLength() const +{ + switch (mStyle) + { + case esNone: + case esLineArrow: + case esSkewedBar: + case esBar: + case esHalfBar: + return 0; + + case esFlatArrow: + return mLength; + + case esDisc: + case esSquare: + case esDiamond: + return mWidth*0.5; + + case esSpikeArrow: + return mLength*0.8; + } + return 0; +} + +/*! \internal + + Draws the line ending with the specified \a painter at the position \a pos. The direction of the + line ending is controlled with \a dir. +*/ +void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const +{ + if (mStyle == esNone) + return; + + QVector2D lengthVec(dir.normalized()); + if (lengthVec.isNull()) + lengthVec = QVector2D(1, 0); + QVector2D widthVec(-lengthVec.y(), lengthVec.x()); + lengthVec *= (float)(mLength*(mInverted ? -1 : 1)); + widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1)); + + QPen penBackup = painter->pen(); + QBrush brushBackup = painter->brush(); + QPen miterPen = penBackup; + miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey + QBrush brush(painter->pen().color(), Qt::SolidPattern); + switch (mStyle) + { + case esNone: break; + case esFlatArrow: + { + QPointF points[3] = {pos.toPointF(), + (pos-lengthVec+widthVec).toPointF(), + (pos-lengthVec-widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 3); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esSpikeArrow: + { + QPointF points[4] = {pos.toPointF(), + (pos-lengthVec+widthVec).toPointF(), + (pos-lengthVec*0.8f).toPointF(), + (pos-lengthVec-widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 4); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esLineArrow: + { + QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(), + pos.toPointF(), + (pos-lengthVec-widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->drawPolyline(points, 3); + painter->setPen(penBackup); + break; + } + case esDisc: + { + painter->setBrush(brush); + painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5); + painter->setBrush(brushBackup); + break; + } + case esSquare: + { + QVector2D widthVecPerp(-widthVec.y(), widthVec.x()); + QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(), + (pos-widthVecPerp-widthVec).toPointF(), + (pos+widthVecPerp-widthVec).toPointF(), + (pos+widthVecPerp+widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 4); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esDiamond: + { + QVector2D widthVecPerp(-widthVec.y(), widthVec.x()); + QPointF points[4] = {(pos-widthVecPerp).toPointF(), + (pos-widthVec).toPointF(), + (pos+widthVecPerp).toPointF(), + (pos+widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 4); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esBar: + { + painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF()); + break; + } + case esHalfBar: + { + painter->drawLine((pos+widthVec).toPointF(), pos.toPointF()); + break; + } + case esSkewedBar: + { + if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic)) + { + // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line + painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(), + (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF()); + } else + { + // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly + painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(), + (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF()); + } + break; + } + } +} + +/*! \internal + \overload + + Draws the line ending. The direction is controlled with the \a angle parameter in radians. +*/ +void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const +{ + draw(painter, pos, QVector2D(qCos(angle), qSin(angle))); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPGrid +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPGrid + \brief Responsible for drawing the grid of a QCPAxis. + + This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the + grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref + QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself. + + The axis and grid drawing was split into two classes to allow them to be placed on different + layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid + in the background and the axes in the foreground, and any plottables/items in between. This + described situation is the default setup, see the QCPLayer documentation. +*/ + +/*! + Creates a QCPGrid instance and sets default values. + + You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid. +*/ +QCPGrid::QCPGrid(QCPAxis *parentAxis) : + QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis), + mParentAxis(parentAxis) +{ + // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called + setParent(parentAxis); + setPen(QPen(QColor(200,200,200), 0, Qt::DotLine)); + setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine)); + setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine)); + setSubGridVisible(false); + setAntialiased(false); + setAntialiasedSubGrid(false); + setAntialiasedZeroLine(false); +} + +/*! + Sets whether grid lines at sub tick marks are drawn. + + \see setSubGridPen +*/ +void QCPGrid::setSubGridVisible(bool visible) +{ + mSubGridVisible = visible; +} + +/*! + Sets whether sub grid lines are drawn antialiased. +*/ +void QCPGrid::setAntialiasedSubGrid(bool enabled) +{ + mAntialiasedSubGrid = enabled; +} + +/*! + Sets whether zero lines are drawn antialiased. +*/ +void QCPGrid::setAntialiasedZeroLine(bool enabled) +{ + mAntialiasedZeroLine = enabled; +} + +/*! + Sets the pen with which (major) grid lines are drawn. +*/ +void QCPGrid::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen with which sub grid lines are drawn. +*/ +void QCPGrid::setSubGridPen(const QPen &pen) +{ + mSubGridPen = pen; +} + +/*! + Sets the pen with which zero lines are drawn. + + Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid + lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen. +*/ +void QCPGrid::setZeroLinePen(const QPen &pen) +{ + mZeroLinePen = pen; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing the major grid lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid); +} + +/*! \internal + + Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning + over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen). +*/ +void QCPGrid::draw(QCPPainter *painter) +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + if (mSubGridVisible) + drawSubGridLines(painter); + drawGridLines(painter); +} + +/*! \internal + + Draws the main grid lines and possibly a zero line with the specified painter. + + This is a helper function called by \ref draw. +*/ +void QCPGrid::drawGridLines(QCPPainter *painter) const +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + int lowTick = mParentAxis->mLowestVisibleTick; + int highTick = mParentAxis->mHighestVisibleTick; + double t; // helper variable, result of coordinate-to-pixel transforms + if (mParentAxis->orientation() == Qt::Horizontal) + { + // draw zeroline: + int zeroLineIndex = -1; + if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0) + { + applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); + painter->setPen(mZeroLinePen); + double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero + for (int i=lowTick; i <= highTick; ++i) + { + if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) + { + zeroLineIndex = i; + t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x + painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); + break; + } + } + } + // draw grid lines: + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + for (int i=lowTick; i <= highTick; ++i) + { + if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline + t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x + painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); + } + } else + { + // draw zeroline: + int zeroLineIndex = -1; + if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0) + { + applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); + painter->setPen(mZeroLinePen); + double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero + for (int i=lowTick; i <= highTick; ++i) + { + if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon) + { + zeroLineIndex = i; + t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y + painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); + break; + } + } + } + // draw grid lines: + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + for (int i=lowTick; i <= highTick; ++i) + { + if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline + t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y + painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); + } + } +} + +/*! \internal + + Draws the sub grid lines with the specified painter. + + This is a helper function called by \ref draw. +*/ +void QCPGrid::drawSubGridLines(QCPPainter *painter) const +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid); + double t; // helper variable, result of coordinate-to-pixel transforms + painter->setPen(mSubGridPen); + if (mParentAxis->orientation() == Qt::Horizontal) + { + for (int i=0; imSubTickVector.size(); ++i) + { + t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x + painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); + } + } else + { + for (int i=0; imSubTickVector.size(); ++i) + { + t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y + painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxis +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAxis + \brief Manages a single axis inside a QCustomPlot. + + Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via + QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and + QCustomPlot::yAxis2 (right). + + Axes are always part of an axis rect, see QCPAxisRect. + \image html AxisNamesOverview.png +
Naming convention of axis parts
+ \n + + \image html AxisRectSpacingOverview.png +
Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line + on the left represents the QCustomPlot widget border.
+ +*/ + +/* start of documentation of inline functions */ + +/*! \fn Qt::Orientation QCPAxis::orientation() const + + Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced + from the axis type (left, top, right or bottom). + + \see orientation(AxisType type) +*/ + +/*! \fn QCPGrid *QCPAxis::grid() const + + Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the + grid is displayed. +*/ + +/*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type) + + Returns the orientation of the specified axis type + + \see orientation() +*/ + +/* end of documentation of inline functions */ +/* start of documentation of signals */ + +/*! \fn void QCPAxis::ticksRequest() + + This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick + labels for a replot. + + Modifying the tick positions can be done with \ref setTickVector. If you also want to control the + tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref + setTickVectorLabels. + + If you only want static ticks you probably don't need this signal, since you can just set the + tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and + maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this + signal and set the vector/vectors there. +*/ + +/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange) + + This signal is emitted when the range of this axis has changed. You can connect it to the \ref + setRange slot of another axis to communicate the new range to the other axis, in order for it to + be synchronized. + + You may also manipulate/correct the range with \ref setRange in a slot connected to this signal. + This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper + range shouldn't go beyond certain values. For example, the following slot would limit the x axis + to only positive ranges: + \code + if (newRange.lower < 0) + plot->xAxis->setRange(0, newRange.size()); + \endcode +*/ + +/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange) + \overload + + Additionally to the new range, this signal also provides the previous range held by the axis as + \a oldRange. +*/ + +/*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType); + + This signal is emitted when the scale type changes, by calls to \ref setScaleType +*/ + +/*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection) + + This signal is emitted when the selection state of this axis has changed, either by user interaction + or by a direct call to \ref setSelectedParts. +*/ + +/*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts); + + This signal is emitted when the selectability changes, by calls to \ref setSelectableParts +*/ + +/* end of documentation of signals */ + +/*! + Constructs an Axis instance of Type \a type for the axis rect \a parent. + + Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create + them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however, + create them manually and then inject them also via \ref QCPAxisRect::addAxis. +*/ +QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) : + QCPLayerable(parent->parentPlot(), QString(), parent), + // axis base: + mAxisType(type), + mAxisRect(parent), + mPadding(5), + mOrientation(orientation(type)), + mSelectableParts(spAxis | spTickLabels | spAxisLabel), + mSelectedParts(spNone), + mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedBasePen(QPen(Qt::blue, 2)), + // axis label: + mLabel(), + mLabelFont(mParentPlot->font()), + mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)), + mLabelColor(Qt::black), + mSelectedLabelColor(Qt::blue), + // tick labels: + mTickLabels(true), + mAutoTickLabels(true), + mTickLabelType(ltNumber), + mTickLabelFont(mParentPlot->font()), + mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)), + mTickLabelColor(Qt::black), + mSelectedTickLabelColor(Qt::blue), + mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")), + mDateTimeSpec(Qt::LocalTime), + mNumberPrecision(6), + mNumberFormatChar('g'), + mNumberBeautifulPowers(true), + // ticks and subticks: + mTicks(true), + mTickStep(1), + mSubTickCount(4), + mAutoTickCount(6), + mAutoTicks(true), + mAutoTickStep(true), + mAutoSubTicks(true), + mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedTickPen(QPen(Qt::blue, 2)), + mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedSubTickPen(QPen(Qt::blue, 2)), + // scale and range: + mRange(0, 5), + mRangeReversed(false), + mScaleType(stLinear), + mScaleLogBase(10), + mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)), + // internal members: + mGrid(new QCPGrid(this)), + mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())), + mLowestVisibleTick(0), + mHighestVisibleTick(-1), + mCachedMarginValid(false), + mCachedMargin(0) +{ + setParent(parent); + mGrid->setVisible(false); + setAntialiased(false); + setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again + + if (type == atTop) + { + setTickLabelPadding(3); + setLabelPadding(6); + } else if (type == atRight) + { + setTickLabelPadding(7); + setLabelPadding(12); + } else if (type == atBottom) + { + setTickLabelPadding(3); + setLabelPadding(3); + } else if (type == atLeft) + { + setTickLabelPadding(5); + setLabelPadding(10); + } +} + +QCPAxis::~QCPAxis() +{ + delete mAxisPainter; + delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order +} + +/* No documentation as it is a property getter */ +int QCPAxis::tickLabelPadding() const +{ + return mAxisPainter->tickLabelPadding; +} + +/* No documentation as it is a property getter */ +double QCPAxis::tickLabelRotation() const +{ + return mAxisPainter->tickLabelRotation; +} + +/* No documentation as it is a property getter */ +QCPAxis::LabelSide QCPAxis::tickLabelSide() const +{ + return mAxisPainter->tickLabelSide; +} + +/* No documentation as it is a property getter */ +QString QCPAxis::numberFormat() const +{ + QString result; + result.append(mNumberFormatChar); + if (mNumberBeautifulPowers) + { + result.append(QLatin1Char('b')); + if (mAxisPainter->numberMultiplyCross) + result.append(QLatin1Char('c')); + } + return result; +} + +/* No documentation as it is a property getter */ +int QCPAxis::tickLengthIn() const +{ + return mAxisPainter->tickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPAxis::tickLengthOut() const +{ + return mAxisPainter->tickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPAxis::subTickLengthIn() const +{ + return mAxisPainter->subTickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPAxis::subTickLengthOut() const +{ + return mAxisPainter->subTickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPAxis::labelPadding() const +{ + return mAxisPainter->labelPadding; +} + +/* No documentation as it is a property getter */ +int QCPAxis::offset() const +{ + return mAxisPainter->offset; +} + +/* No documentation as it is a property getter */ +QCPLineEnding QCPAxis::lowerEnding() const +{ + return mAxisPainter->lowerEnding; +} + +/* No documentation as it is a property getter */ +QCPLineEnding QCPAxis::upperEnding() const +{ + return mAxisPainter->upperEnding; +} + +/*! + Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref + stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis + scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step + (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major + ticks, consider choosing a logarithm base of 100, 1000 or even higher. + + If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option + (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10 + [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]" + part). To only display the decimal power, set the number precision to zero with + \ref setNumberPrecision. +*/ +void QCPAxis::setScaleType(QCPAxis::ScaleType type) +{ + if (mScaleType != type) + { + mScaleType = type; + if (mScaleType == stLogarithmic) + setRange(mRange.sanitizedForLogScale()); + mCachedMarginValid = false; + emit scaleTypeChanged(mScaleType); + } +} + +/*! + If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the + scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base. + + Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but + less major ticks, consider choosing \a base 100, 1000 or even higher. +*/ +void QCPAxis::setScaleLogBase(double base) +{ + if (base > 1) + { + mScaleLogBase = base; + mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation + mCachedMarginValid = false; + } else + qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base; +} + +/*! + Sets the range of the axis. + + This slot may be connected with the \ref rangeChanged signal of another axis so this axis + is always synchronized with the other axis range, when it changes. + + To invert the direction of an axis, use \ref setRangeReversed. +*/ +void QCPAxis::setRange(const QCPRange &range) +{ + if (range.lower == mRange.lower && range.upper == mRange.upper) + return; + + if (!QCPRange::validRange(range)) return; + QCPRange oldRange = mRange; + if (mScaleType == stLogarithmic) + { + mRange = range.sanitizedForLogScale(); + } else + { + mRange = range.sanitizedForLinScale(); + } + mCachedMarginValid = false; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectAxes.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPAxis::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font. + + The entire selection mechanism for axes is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you + wish to change the selection state manually. + + This function can change the selection state of a part, independent of the \ref setSelectableParts setting. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, + setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor +*/ +void QCPAxis::setSelectedParts(const SelectableParts &selected) +{ + if (mSelectedParts != selected) + { + mSelectedParts = selected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + \overload + + Sets the lower and upper bound of the axis range. + + To invert the direction of an axis, use \ref setRangeReversed. + + There is also a slot to set a range, see \ref setRange(const QCPRange &range). +*/ +void QCPAxis::setRange(double lower, double upper) +{ + if (lower == mRange.lower && upper == mRange.upper) + return; + + if (!QCPRange::validRange(lower, upper)) return; + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + mCachedMarginValid = false; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + \overload + + Sets the range of the axis. + + The \a position coordinate indicates together with the \a alignment parameter, where the new + range will be positioned. \a size defines the size of the new axis range. \a alignment may be + Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border, + or center of the range to be aligned with \a position. Any other values of \a alignment will + default to Qt::AlignCenter. +*/ +void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment) +{ + if (alignment == Qt::AlignLeft) + setRange(position, position+size); + else if (alignment == Qt::AlignRight) + setRange(position-size, position); + else // alignment == Qt::AlignCenter + setRange(position-size/2.0, position+size/2.0); +} + +/*! + Sets the lower bound of the axis range. The upper bound is not changed. + \see setRange +*/ +void QCPAxis::setRangeLower(double lower) +{ + if (mRange.lower == lower) + return; + + QCPRange oldRange = mRange; + mRange.lower = lower; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + mCachedMarginValid = false; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets the upper bound of the axis range. The lower bound is not changed. + \see setRange +*/ +void QCPAxis::setRangeUpper(double upper) +{ + if (mRange.upper == upper) + return; + + QCPRange oldRange = mRange; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + mCachedMarginValid = false; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal + axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the + direction of increasing values is inverted. + + Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part + of the \ref setRange interface will still reference the mathematically smaller number than the \a + upper part. +*/ +void QCPAxis::setRangeReversed(bool reversed) +{ + if (mRangeReversed != reversed) + { + mRangeReversed = reversed; + mCachedMarginValid = false; + } +} + +/*! + Sets whether the tick positions should be calculated automatically (either from an automatically + generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep). + + If \a on is set to false, you must provide the tick positions manually via \ref setTickVector. + For these manual ticks you may let QCPAxis generate the appropriate labels automatically by + leaving \ref setAutoTickLabels set to true. If you also wish to control the displayed labels + manually, set \ref setAutoTickLabels to false and provide the label strings with \ref + setTickVectorLabels. + + If you need dynamically calculated tick vectors (and possibly tick label vectors), set the + vectors in a slot connected to the \ref ticksRequest signal. + + \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep +*/ +void QCPAxis::setAutoTicks(bool on) +{ + if (mAutoTicks != on) + { + mAutoTicks = on; + mCachedMarginValid = false; + } +} + +/*! + When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be + generated in the visible range, approximately. + + It's not guaranteed that this number of ticks is met exactly, but approximately within a + tolerance of about two. + + Only values greater than zero are accepted as \a approximateCount. + + \see setAutoTickStep, setAutoTicks, setAutoSubTicks +*/ +void QCPAxis::setAutoTickCount(int approximateCount) +{ + if (mAutoTickCount != approximateCount) + { + if (approximateCount > 0) + { + mAutoTickCount = approximateCount; + mCachedMarginValid = false; + } else + qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount; + } +} + +/*! + Sets whether the tick labels are generated automatically. Depending on the tick label type (\ref + ltNumber or \ref ltDateTime), the labels will either show the coordinate as floating point + number (\ref setNumberFormat), or a date/time formatted according to \ref setDateTimeFormat. + + If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This + is usually used in a combination with \ref setAutoTicks set to false for complete control over + tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi" + etc. as tick labels. + + If you need dynamically calculated tick vectors (and possibly tick label vectors), set the + vectors in a slot connected to the \ref ticksRequest signal. + + \see setAutoTicks +*/ +void QCPAxis::setAutoTickLabels(bool on) +{ + if (mAutoTickLabels != on) + { + mAutoTickLabels = on; + mCachedMarginValid = false; + } +} + +/*! + Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated + automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human + readable plots. + + The number of ticks the algorithm aims for within the visible range can be specified with \ref + setAutoTickCount. + + If \a on is set to false, you may set the tick step manually with \ref setTickStep. + + \see setAutoTicks, setAutoSubTicks, setAutoTickCount +*/ +void QCPAxis::setAutoTickStep(bool on) +{ + if (mAutoTickStep != on) + { + mAutoTickStep = on; + mCachedMarginValid = false; + } +} + +/*! + Sets whether the number of sub ticks in one tick interval is determined automatically. This + works, as long as the tick step mantissa is a multiple of 0.5. When \ref setAutoTickStep is + enabled, this is always the case. + + When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually. + + \see setAutoTickCount, setAutoTicks, setAutoTickStep +*/ +void QCPAxis::setAutoSubTicks(bool on) +{ + if (mAutoSubTicks != on) + { + mAutoSubTicks = on; + mCachedMarginValid = false; + } +} + +/*! + Sets whether tick marks are displayed. + + Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve + that, see \ref setTickLabels. +*/ +void QCPAxis::setTicks(bool show) +{ + if (mTicks != show) + { + mTicks = show; + mCachedMarginValid = false; + } +} + +/*! + Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks. +*/ +void QCPAxis::setTickLabels(bool show) +{ + if (mTickLabels != show) + { + mTickLabels = show; + mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the axis base line (including any outward ticks) and the tick labels. + \see setLabelPadding, setPadding +*/ +void QCPAxis::setTickLabelPadding(int padding) +{ + if (mAxisPainter->tickLabelPadding != padding) + { + mAxisPainter->tickLabelPadding = padding; + mCachedMarginValid = false; + } +} + +/*! + Sets whether the tick labels display numbers or dates/times. + + If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply. + + If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply. + + In QCustomPlot, date/time coordinates are double numbers representing the seconds since + 1970-01-01T00:00:00 UTC. This format can be retrieved from QDateTime objects with the + QDateTime::toTime_t() function. Since this only gives a resolution of one second, there is also + the QDateTime::toMSecsSinceEpoch() function which returns the timespan described above in + milliseconds. Divide its return value by 1000.0 to get a value with the format needed for + date/time plotting, with a resolution of one millisecond. + + Using the toMSecsSinceEpoch function allows dates that go back to 2nd January 4713 B.C. + (represented by a negative number), unlike the toTime_t function, which works with unsigned + integers and thus only goes back to 1st January 1970. So both for range and accuracy, use of + toMSecsSinceEpoch()/1000.0 should be preferred as key coordinate for date/time axes. + + \see setTickLabels +*/ +void QCPAxis::setTickLabelType(LabelType type) +{ + if (mTickLabelType != type) + { + mTickLabelType = type; + mCachedMarginValid = false; + } +} + +/*! + Sets the font of the tick labels. + + \see setTickLabels, setTickLabelColor +*/ +void QCPAxis::setTickLabelFont(const QFont &font) +{ + if (font != mTickLabelFont) + { + mTickLabelFont = font; + mCachedMarginValid = false; + } +} + +/*! + Sets the color of the tick labels. + + \see setTickLabels, setTickLabelFont +*/ +void QCPAxis::setTickLabelColor(const QColor &color) +{ + if (color != mTickLabelColor) + { + mTickLabelColor = color; + mCachedMarginValid = false; + } +} + +/*! + Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else, + the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values + from -90 to 90 degrees. + + If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For + other angles, the label is drawn with an offset such that it seems to point toward or away from + the tick mark. +*/ +void QCPAxis::setTickLabelRotation(double degrees) +{ + if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation)) + { + mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0); + mCachedMarginValid = false; + } +} + +/*! + Sets whether the tick labels (numbers) shall appear inside or outside the axis rect. + + The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels + to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels + appear on the inside are additionally clipped to the axis rect. +*/ +void QCPAxis::setTickLabelSide(LabelSide side) +{ + mAxisPainter->tickLabelSide = side; + mCachedMarginValid = false; +} + +/*! + Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime. + for details about the \a format string, see the documentation of QDateTime::toString(). + + Newlines can be inserted with "\n". + + \see setDateTimeSpec +*/ +void QCPAxis::setDateTimeFormat(const QString &format) +{ + if (mDateTimeFormat != format) + { + mDateTimeFormat = format; + mCachedMarginValid = false; + } +} + +/*! + Sets the time spec that is used for the date time values when \ref setTickLabelType is \ref + ltDateTime. + + The default value of QDateTime objects (and also QCustomPlot) is Qt::LocalTime. However, + if the date time values passed to QCustomPlot are given in the UTC spec, set \a + timeSpec to Qt::UTC to get the correct axis labels. + + \see setDateTimeFormat +*/ +void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec) +{ + mDateTimeSpec = timeSpec; +} + +/*! + Sets the number format for the numbers drawn as tick labels (if tick label type is \ref + ltNumber). This \a formatCode is an extended version of the format code used e.g. by + QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats" + section in the detailed description of the QString class. \a formatCode is a string of one, two + or three characters. The first character is identical to the normal format code used by Qt. In + short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed, + whichever is shorter. + + The second and third characters are optional and specific to QCustomPlot:\n + If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. + "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for + "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 + [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot. + If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can + be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the + cross and 183 (0xB7) for the dot. + + If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b' + option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10 + [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]" + part). To only display the decimal power, set the number precision to zero with \ref + setNumberPrecision. + + Examples for \a formatCode: + \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large, + normal scientific format is used + \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with + beautifully typeset decimal powers and a dot as multiplication sign + \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as + multiplication sign + \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal + powers. Format code will be reduced to 'f'. + \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format + code will not be changed. +*/ +void QCPAxis::setNumberFormat(const QString &formatCode) +{ + if (formatCode.isEmpty()) + { + qDebug() << Q_FUNC_INFO << "Passed formatCode is empty"; + return; + } + mCachedMarginValid = false; + + // interpret first char as number format char: + QString allowedFormatChars(QLatin1String("eEfgG")); + if (allowedFormatChars.contains(formatCode.at(0))) + { + mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1()); + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode; + return; + } + if (formatCode.length() < 2) + { + mNumberBeautifulPowers = false; + mAxisPainter->numberMultiplyCross = false; + return; + } + + // interpret second char as indicator for beautiful decimal powers: + if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g'))) + { + mNumberBeautifulPowers = true; + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode; + return; + } + if (formatCode.length() < 3) + { + mAxisPainter->numberMultiplyCross = false; + return; + } + + // interpret third char as indicator for dot or cross multiplication symbol: + if (formatCode.at(2) == QLatin1Char('c')) + { + mAxisPainter->numberMultiplyCross = true; + } else if (formatCode.at(2) == QLatin1Char('d')) + { + mAxisPainter->numberMultiplyCross = false; + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode; + return; + } +} + +/*! + Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec) + for details. The effect of precisions are most notably for number Formats starting with 'e', see + \ref setNumberFormat + + If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref + setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display + usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic + scaling (the redundant "1 [multiplication sign]" part). To only display the decimal power "10 + [superscript] n", set \a precision to zero. +*/ +void QCPAxis::setNumberPrecision(int precision) +{ + if (mNumberPrecision != precision) + { + mNumberPrecision = precision; + mCachedMarginValid = false; + } +} + +/*! + If \ref setAutoTickStep is set to false, use this function to set the tick step manually. + The tick step is the interval between (major) ticks, in plot coordinates. + \see setSubTickCount +*/ +void QCPAxis::setTickStep(double step) +{ + if (mTickStep != step) + { + mTickStep = step; + mCachedMarginValid = false; + } +} + +/*! + If you want full control over what ticks (and possibly labels) the axes show, this function is + used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else + the provided tick vector will be overwritten with automatically generated tick coordinates upon + replot. The labels of the ticks can be generated automatically when \ref setAutoTickLabels is + left enabled. If it is disabled, you can set the labels manually with \ref setTickVectorLabels. + + \a vec is a vector containing the positions of the ticks, in plot coordinates. + + \warning \a vec must be sorted in ascending order, no additional checks are made to ensure this. + + \see setTickVectorLabels +*/ +void QCPAxis::setTickVector(const QVector &vec) +{ + // don't check whether mTickVector != vec here, because it takes longer than we would save + mTickVector = vec; + mCachedMarginValid = false; +} + +/*! + If you want full control over what ticks and labels the axes show, this function is used to set a + number of QStrings that will be displayed at the tick positions which you need to provide with + \ref setTickVector. These two vectors should have the same size. (Note that you need to disable + \ref setAutoTicks and \ref setAutoTickLabels first.) + + \a vec is a vector containing the labels of the ticks. The entries correspond to the respective + indices in the tick vector, passed via \ref setTickVector. + + \see setTickVector +*/ +void QCPAxis::setTickVectorLabels(const QVector &vec) +{ + // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save + mTickVectorLabels = vec; + mCachedMarginValid = false; +} + +/*! + Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the + plot and \a outside is the length they will reach outside the plot. If \a outside is greater than + zero, the tick labels and axis label will increase their distance to the axis accordingly, so + they won't collide with the ticks. + + \see setSubTickLength, setTickLengthIn, setTickLengthOut +*/ +void QCPAxis::setTickLength(int inside, int outside) +{ + setTickLengthIn(inside); + setTickLengthOut(outside); +} + +/*! + Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach + inside the plot. + + \see setTickLengthOut, setTickLength, setSubTickLength +*/ +void QCPAxis::setTickLengthIn(int inside) +{ + if (mAxisPainter->tickLengthIn != inside) + { + mAxisPainter->tickLengthIn = inside; + } +} + +/*! + Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach + outside the plot. If \a outside is greater than zero, the tick labels and axis label will + increase their distance to the axis accordingly, so they won't collide with the ticks. + + \see setTickLengthIn, setTickLength, setSubTickLength +*/ +void QCPAxis::setTickLengthOut(int outside) +{ + if (mAxisPainter->tickLengthOut != outside) + { + mAxisPainter->tickLengthOut = outside; + mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example, + divides the tick intervals in four sub intervals. + + By default, the number of sub ticks is chosen automatically in a reasonable manner as long as the + mantissa of the tick step is a multiple of 0.5. When \ref setAutoTickStep is enabled, this is + always the case. + + If you want to disable automatic sub tick count and use this function to set the count manually, + see \ref setAutoSubTicks. +*/ +void QCPAxis::setSubTickCount(int count) +{ + mSubTickCount = count; +} + +/*! + Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside + the plot and \a outside is the length they will reach outside the plot. If \a outside is greater + than zero, the tick labels and axis label will increase their distance to the axis accordingly, + so they won't collide with the ticks. + + \see setTickLength, setSubTickLengthIn, setSubTickLengthOut +*/ +void QCPAxis::setSubTickLength(int inside, int outside) +{ + setSubTickLengthIn(inside); + setSubTickLengthOut(outside); +} + +/*! + Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside + the plot. + + \see setSubTickLengthOut, setSubTickLength, setTickLength +*/ +void QCPAxis::setSubTickLengthIn(int inside) +{ + if (mAxisPainter->subTickLengthIn != inside) + { + mAxisPainter->subTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach + outside the plot. If \a outside is greater than zero, the tick labels will increase their + distance to the axis accordingly, so they won't collide with the ticks. + + \see setSubTickLengthIn, setSubTickLength, setTickLength +*/ +void QCPAxis::setSubTickLengthOut(int outside) +{ + if (mAxisPainter->subTickLengthOut != outside) + { + mAxisPainter->subTickLengthOut = outside; + mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the pen, the axis base line is drawn with. + + \see setTickPen, setSubTickPen +*/ +void QCPAxis::setBasePen(const QPen &pen) +{ + mBasePen = pen; +} + +/*! + Sets the pen, tick marks will be drawn with. + + \see setTickLength, setBasePen +*/ +void QCPAxis::setTickPen(const QPen &pen) +{ + mTickPen = pen; +} + +/*! + Sets the pen, subtick marks will be drawn with. + + \see setSubTickCount, setSubTickLength, setBasePen +*/ +void QCPAxis::setSubTickPen(const QPen &pen) +{ + mSubTickPen = pen; +} + +/*! + Sets the font of the axis label. + + \see setLabelColor +*/ +void QCPAxis::setLabelFont(const QFont &font) +{ + if (mLabelFont != font) + { + mLabelFont = font; + mCachedMarginValid = false; + } +} + +/*! + Sets the color of the axis label. + + \see setLabelFont +*/ +void QCPAxis::setLabelColor(const QColor &color) +{ + mLabelColor = color; +} + +/*! + Sets the text of the axis label that will be shown below/above or next to the axis, depending on + its orientation. To disable axis labels, pass an empty string as \a str. +*/ +void QCPAxis::setLabel(const QString &str) +{ + if (mLabel != str) + { + mLabel = str; + mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the tick labels and the axis label. + + \see setTickLabelPadding, setPadding +*/ +void QCPAxis::setLabelPadding(int padding) +{ + if (mAxisPainter->labelPadding != padding) + { + mAxisPainter->labelPadding = padding; + mCachedMarginValid = false; + } +} + +/*! + Sets the padding of the axis. + + When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space, + that is left blank. + + The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled. + + \see setLabelPadding, setTickLabelPadding +*/ +void QCPAxis::setPadding(int padding) +{ + if (mPadding != padding) + { + mPadding = padding; + mCachedMarginValid = false; + } +} + +/*! + Sets the offset the axis has to its axis rect side. + + If an axis rect side has multiple axes and automatic margin calculation is enabled for that side, + only the offset of the inner most axis has meaning (even if it is set to be invisible). The + offset of the other, outer axes is controlled automatically, to place them at appropriate + positions. +*/ +void QCPAxis::setOffset(int offset) +{ + mAxisPainter->offset = offset; +} + +/*! + Sets the font that is used for tick labels when they are selected. + + \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedTickLabelFont(const QFont &font) +{ + if (font != mSelectedTickLabelFont) + { + mSelectedTickLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts + } +} + +/*! + Sets the font that is used for the axis label when it is selected. + + \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedLabelFont(const QFont &font) +{ + mSelectedLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts +} + +/*! + Sets the color that is used for tick labels when they are selected. + + \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedTickLabelColor(const QColor &color) +{ + if (color != mSelectedTickLabelColor) + { + mSelectedTickLabelColor = color; + } +} + +/*! + Sets the color that is used for the axis label when it is selected. + + \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedLabelColor(const QColor &color) +{ + mSelectedLabelColor = color; +} + +/*! + Sets the pen that is used to draw the axis base line when selected. + + \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedBasePen(const QPen &pen) +{ + mSelectedBasePen = pen; +} + +/*! + Sets the pen that is used to draw the (major) ticks when selected. + + \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedTickPen(const QPen &pen) +{ + mSelectedTickPen = pen; +} + +/*! + Sets the pen that is used to draw the subticks when selected. + + \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedSubTickPen(const QPen &pen) +{ + mSelectedSubTickPen = pen; +} + +/*! + Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available + styles. + + For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending. + Note that this meaning does not change when the axis range is reversed with \ref + setRangeReversed. + + \see setUpperEnding +*/ +void QCPAxis::setLowerEnding(const QCPLineEnding &ending) +{ + mAxisPainter->lowerEnding = ending; +} + +/*! + Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available + styles. + + For horizontal axes, this method refers to the right ending, for vertical axes the top ending. + Note that this meaning does not change when the axis range is reversed with \ref + setRangeReversed. + + \see setLowerEnding +*/ +void QCPAxis::setUpperEnding(const QCPLineEnding &ending) +{ + mAxisPainter->upperEnding = ending; +} + +/*! + If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper + bounds of the range. The range is simply moved by \a diff. + + If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This + corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff). +*/ +void QCPAxis::moveRange(double diff) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + mRange.lower += diff; + mRange.upper += diff; + } else // mScaleType == stLogarithmic + { + mRange.lower *= diff; + mRange.upper *= diff; + } + mCachedMarginValid = false; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a + factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at + coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates + around 1.0 will have moved symmetrically closer to 1.0). +*/ +void QCPAxis::scaleRange(double factor, double center) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + QCPRange newRange; + newRange.lower = (mRange.lower-center)*factor + center; + newRange.upper = (mRange.upper-center)*factor + center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLinScale(); + } else // mScaleType == stLogarithmic + { + if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range + { + QCPRange newRange; + newRange.lower = qPow(mRange.lower/center, factor)*center; + newRange.upper = qPow(mRange.upper/center, factor)*center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLogScale(); + } else + qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center; + } + mCachedMarginValid = false; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will + be done around the center of the current axis range. + + For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs + plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the + axis rect has. + + This is an operation that changes the range of this axis once, it doesn't fix the scale ratio + indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent + won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent + will follow. +*/ +void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) +{ + int otherPixelSize, ownPixelSize; + + if (otherAxis->orientation() == Qt::Horizontal) + otherPixelSize = otherAxis->axisRect()->width(); + else + otherPixelSize = otherAxis->axisRect()->height(); + + if (orientation() == Qt::Horizontal) + ownPixelSize = axisRect()->width(); + else + ownPixelSize = axisRect()->height(); + + double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize; + setRange(range().center(), newRangeSize, Qt::AlignCenter); +} + +/*! + Changes the axis range such that all plottables associated with this axis are fully visible in + that dimension. + + \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPAxis::rescale(bool onlyVisiblePlottables) +{ + QList p = plottables(); + QCPRange newRange; + bool haveRange = false; + for (int i=0; irealVisibility() && onlyVisiblePlottables) + continue; + QCPRange plottableRange; + bool currentFoundRange; + QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth; + if (mScaleType == stLogarithmic) + signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive); + if (p.at(i)->keyAxis() == this) + plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain); + else + plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain); + if (currentFoundRange) + { + if (!haveRange) + newRange = plottableRange; + else + newRange.expand(plottableRange); + haveRange = true; + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (mScaleType == stLinear) + { + newRange.lower = center-mRange.size()/2.0; + newRange.upper = center+mRange.size()/2.0; + } else // mScaleType == stLogarithmic + { + newRange.lower = center/qSqrt(mRange.upper/mRange.lower); + newRange.upper = center*qSqrt(mRange.upper/mRange.lower); + } + } + setRange(newRange); + } +} + +/*! + Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates. +*/ +double QCPAxis::pixelToCoord(double value) const +{ + if (orientation() == Qt::Horizontal) + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower; + else + return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper; + } else // mScaleType == stLogarithmic + { + if (!mRangeReversed) + return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower; + else + return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper; + } + } else // orientation() == Qt::Vertical + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower; + else + return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper; + } else // mScaleType == stLogarithmic + { + if (!mRangeReversed) + return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower; + else + return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper; + } + } +} + +/*! + Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget. +*/ +double QCPAxis::coordToPixel(double value) const +{ + if (orientation() == Qt::Horizontal) + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left(); + else + return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left(); + } else // mScaleType == stLogarithmic + { + if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200; + else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200; + else + { + if (!mRangeReversed) + return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left(); + else + return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left(); + } + } + } else // orientation() == Qt::Vertical + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height(); + else + return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height(); + } else // mScaleType == stLogarithmic + { + if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200; + else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200; + else + { + if (!mRangeReversed) + return mAxisRect->bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height(); + else + return mAxisRect->bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height(); + } + } + } +} + +/*! + Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function + is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this + function does not change the current selection state of the axis. + + If the axis is not visible (\ref setVisible), this function always returns \ref spNone. + + \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions +*/ +QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const +{ + if (!mVisible) + return spNone; + + if (mAxisPainter->axisSelectionBox().contains(pos.toPoint())) + return spAxis; + else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint())) + return spTickLabels; + else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) + return spAxisLabel; + else + return spNone; +} + +/* inherits documentation from base class */ +double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mParentPlot) return -1; + SelectablePart part = getPartAt(pos); + if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone) + return -1; + + if (details) + details->setValue(part); + return mParentPlot->selectionTolerance()*0.99; +} + +/*! + Returns a list of all the plottables that have this axis as key or value axis. + + If you are only interested in plottables of type QCPGraph, see \ref graphs. + + \see graphs, items +*/ +QList QCPAxis::plottables() const +{ + QList result; + if (!mParentPlot) return result; + + for (int i=0; imPlottables.size(); ++i) + { + if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this) + result.append(mParentPlot->mPlottables.at(i)); + } + return result; +} + +/*! + Returns a list of all the graphs that have this axis as key or value axis. + + \see plottables, items +*/ +QList QCPAxis::graphs() const +{ + QList result; + if (!mParentPlot) return result; + + for (int i=0; imGraphs.size(); ++i) + { + if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this) + result.append(mParentPlot->mGraphs.at(i)); + } + return result; +} + +/*! + Returns a list of all the items that are associated with this axis. An item is considered + associated with an axis if at least one of its positions uses the axis as key or value axis. + + \see plottables, graphs +*/ +QList QCPAxis::items() const +{ + QList result; + if (!mParentPlot) return result; + + for (int itemId=0; itemIdmItems.size(); ++itemId) + { + QList positions = mParentPlot->mItems.at(itemId)->positions(); + for (int posId=0; posIdkeyAxis() == this || positions.at(posId)->valueAxis() == this) + { + result.append(mParentPlot->mItems.at(itemId)); + break; + } + } + } + return result; +} + +/*! + Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to + QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.) +*/ +QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side) +{ + switch (side) + { + case QCP::msLeft: return atLeft; + case QCP::msRight: return atRight; + case QCP::msTop: return atTop; + case QCP::msBottom: return atBottom; + default: break; + } + qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side; + return atLeft; +} + +/*! + Returns the axis type that describes the opposite axis of an axis with the specified \a type. +*/ +QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type) +{ + switch (type) + { + case atLeft: return atRight; break; + case atRight: return atLeft; break; + case atBottom: return atTop; break; + case atTop: return atBottom; break; + default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break; + } +} + +/*! \internal + + This function is called to prepare the tick vector, sub tick vector and tick label vector. If + \ref setAutoTicks is set to true, appropriate tick values are determined automatically via \ref + generateAutoTicks. If it's set to false, the signal ticksRequest is emitted, which can be used to + provide external tick positions. Then the sub tick vectors and tick label vectors are created. +*/ +void QCPAxis::setupTickVectors() +{ + if (!mParentPlot) return; + if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return; + + // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself + if (mAutoTicks) + { + generateAutoTicks(); + } else + { + emit ticksRequest(); + } + + visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick); + if (mTickVector.isEmpty()) + { + mSubTickVector.clear(); + return; + } + + // generate subticks between ticks: + mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount); + if (mSubTickCount > 0) + { + double subTickStep = 0; + double subTickPosition = 0; + int subTickIndex = 0; + bool done = false; + int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick-1 : mLowestVisibleTick; + int highTick = mHighestVisibleTick < mTickVector.size()-1 ? mHighestVisibleTick+1 : mHighestVisibleTick; + for (int i=lowTick+1; i<=highTick; ++i) + { + subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1); + for (int k=1; k<=mSubTickCount; ++k) + { + subTickPosition = mTickVector.at(i-1) + k*subTickStep; + if (subTickPosition < mRange.lower) + continue; + if (subTickPosition > mRange.upper) + { + done = true; + break; + } + mSubTickVector[subTickIndex] = subTickPosition; + subTickIndex++; + } + if (done) break; + } + mSubTickVector.resize(subTickIndex); + } + + // generate tick labels according to tick positions: + if (mAutoTickLabels) + { + int vecsize = mTickVector.size(); + mTickVectorLabels.resize(vecsize); + if (mTickLabelType == ltNumber) + { + for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i) + mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision); + } else if (mTickLabelType == ltDateTime) + { + for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i) + { +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz") + mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat); +#else + mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat); +#endif + } + } + } else // mAutoTickLabels == false + { + if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels + { + emit ticksRequest(); + } + // make sure provided tick label vector has correct (minimal) length: + if (mTickVectorLabels.size() < mTickVector.size()) + mTickVectorLabels.resize(mTickVector.size()); + } +} + +/*! \internal + + If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to + generate reasonable tick positions (and subtick count). The algorithm tries to create + approximately mAutoTickCount ticks (set via \ref setAutoTickCount). + + If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is generated at every + power of the current logarithm base, set via \ref setScaleLogBase. +*/ +void QCPAxis::generateAutoTicks() +{ + if (mScaleType == stLinear) + { + if (mAutoTickStep) + { + // Generate tick positions according to linear scaling: + mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers + double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc. + double tickStepMantissa = mTickStep/magnitudeFactor; + if (tickStepMantissa < 5) + { + // round digit after decimal point to 0.5 + mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor; + } else + { + // round to first digit in multiples of 2 + mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor; + } + } + if (mAutoSubTicks) + mSubTickCount = calculateAutoSubTickCount(mTickStep); + // Generate tick positions according to mTickStep: + qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision + qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision + int tickcount = lastStep-firstStep+1; + if (tickcount < 0) tickcount = 0; + mTickVector.resize(tickcount); + for (int i=0; i 0 && mRange.upper > 0) // positive range + { + double lowerMag = basePow(qFloor(baseLog(mRange.lower))); + double currentMag = lowerMag; + mTickVector.clear(); + mTickVector.append(currentMag); + while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case + { + currentMag *= mScaleLogBase; + mTickVector.append(currentMag); + } + } else if (mRange.lower < 0 && mRange.upper < 0) // negative range + { + double lowerMag = -basePow(qCeil(baseLog(-mRange.lower))); + double currentMag = lowerMag; + mTickVector.clear(); + mTickVector.append(currentMag); + while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case + { + currentMag /= mScaleLogBase; + mTickVector.append(currentMag); + } + } else // invalid range for logarithmic scale, because lower and upper have different sign + { + mTickVector.clear(); + qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper; + } + } +} + +/*! \internal + + Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a + tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For + Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks, + because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667. Note + that a subtick count of 4 means dividing the major tick step into 5 sections. + + This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps + with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no + fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is + returned. +*/ +int QCPAxis::calculateAutoSubTickCount(double tickStep) const +{ + int result = mSubTickCount; // default to current setting, if no proper value can be found + + // get mantissa of tickstep: + double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc. + double tickStepMantissa = tickStep/magnitudeFactor; + + // separate integer and fractional part of mantissa: + double epsilon = 0.01; + double intPartf; + int intPart; + double fracPart = modf(tickStepMantissa, &intPartf); + intPart = intPartf; + + // handle cases with (almost) integer mantissa: + if (fracPart < epsilon || 1.0-fracPart < epsilon) + { + if (1.0-fracPart < epsilon) + ++intPart; + switch (intPart) + { + case 1: result = 4; break; // 1.0 -> 0.2 substep + case 2: result = 3; break; // 2.0 -> 0.5 substep + case 3: result = 2; break; // 3.0 -> 1.0 substep + case 4: result = 3; break; // 4.0 -> 1.0 substep + case 5: result = 4; break; // 5.0 -> 1.0 substep + case 6: result = 2; break; // 6.0 -> 2.0 substep + case 7: result = 6; break; // 7.0 -> 1.0 substep + case 8: result = 3; break; // 8.0 -> 2.0 substep + case 9: result = 2; break; // 9.0 -> 3.0 substep + } + } else + { + // handle cases with significantly fractional mantissa: + if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa + { + switch (intPart) + { + case 1: result = 2; break; // 1.5 -> 0.5 substep + case 2: result = 4; break; // 2.5 -> 0.5 substep + case 3: result = 4; break; // 3.5 -> 0.7 substep + case 4: result = 2; break; // 4.5 -> 1.5 substep + case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on) + case 6: result = 4; break; // 6.5 -> 1.3 substep + case 7: result = 2; break; // 7.5 -> 2.5 substep + case 8: result = 4; break; // 8.5 -> 1.7 substep + case 9: result = 4; break; // 9.5 -> 1.9 substep + } + } + // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default + } + + return result; +} + +/* inherits documentation from base class */ +void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + SelectablePart part = details.value(); + if (mSelectableParts.testFlag(part)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(additive ? mSelectedParts^part : part); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAxis::deselectEvent(bool *selectionStateChanged) +{ + SelectableParts selBefore = mSelectedParts; + setSelectedParts(mSelectedParts & ~mSelectableParts); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing axis lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes); +} + +/*! \internal + + Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance. + +*/ +void QCPAxis::draw(QCPPainter *painter) +{ + const int lowTick = mLowestVisibleTick; + const int highTick = mHighestVisibleTick; + QVector subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter + QVector tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter + QVector tickLabels; // the final vector passed to QCPAxisPainter + tickPositions.reserve(highTick-lowTick+1); + tickLabels.reserve(highTick-lowTick+1); + subTickPositions.reserve(mSubTickVector.size()); + + if (mTicks) + { + for (int i=lowTick; i<=highTick; ++i) + { + tickPositions.append(coordToPixel(mTickVector.at(i))); + if (mTickLabels) + tickLabels.append(mTickVectorLabels.at(i)); + } + + if (mSubTickCount > 0) + { + const int subTickCount = mSubTickVector.size(); + for (int i=0; itype = mAxisType; + mAxisPainter->basePen = getBasePen(); + mAxisPainter->labelFont = getLabelFont(); + mAxisPainter->labelColor = getLabelColor(); + mAxisPainter->label = mLabel; + mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber; + mAxisPainter->tickPen = getTickPen(); + mAxisPainter->subTickPen = getSubTickPen(); + mAxisPainter->tickLabelFont = getTickLabelFont(); + mAxisPainter->tickLabelColor = getTickLabelColor(); + mAxisPainter->axisRect = mAxisRect->rect(); + mAxisPainter->viewportRect = mParentPlot->viewport(); + mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic; + mAxisPainter->reversedEndings = mRangeReversed; + mAxisPainter->tickPositions = tickPositions; + mAxisPainter->tickLabels = tickLabels; + mAxisPainter->subTickPositions = subTickPositions; + mAxisPainter->draw(painter); +} + +/*! \internal + + Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in + the current range. The return values are indices of the tick vector, not the positions of the + ticks themselves. + + The actual use of this function is when an external tick vector is provided, since it might + exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of + subticks. + + If all ticks are outside the axis range, an inverted range is returned, i.e. highIndex will be + smaller than lowIndex. There is one case, where this function returns indices that are not really + visible in the current axis range: When the tick spacing is larger than the axis range size and + one tick is below the axis range and the next tick is already above the axis range. Because in + such cases it is usually desirable to know the tick pair, to draw proper subticks. +*/ +void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const +{ + bool lowFound = false; + bool highFound = false; + lowIndex = 0; + highIndex = -1; + + for (int i=0; i < mTickVector.size(); ++i) + { + if (mTickVector.at(i) >= mRange.lower) + { + lowFound = true; + lowIndex = i; + break; + } + } + for (int i=mTickVector.size()-1; i >= 0; --i) + { + if (mTickVector.at(i) <= mRange.upper) + { + highFound = true; + highIndex = i; + break; + } + } + + if (!lowFound && highFound) + lowIndex = highIndex+1; + else if (lowFound && !highFound) + highIndex = lowIndex-1; +} + +/*! \internal + + A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic + scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation. + This is set to 1.0/qLn(mScaleLogBase) in \ref setScaleLogBase. + + \see basePow, setScaleLogBase, setScaleType +*/ +double QCPAxis::baseLog(double value) const +{ + return qLn(value)*mScaleLogBaseLogInv; +} + +/*! \internal + + A power function with the base mScaleLogBase, used mostly for coordinate transforms in + logarithmic scales with arbitrary log base. + + \see baseLog, setScaleLogBase, setScaleType +*/ +double QCPAxis::basePow(double value) const +{ + return qPow(mScaleLogBase, value); +} + +/*! \internal + + Returns the pen that is used to draw the axis base line. Depending on the selection state, this + is either mSelectedBasePen or mBasePen. +*/ +QPen QCPAxis::getBasePen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen; +} + +/*! \internal + + Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this + is either mSelectedTickPen or mTickPen. +*/ +QPen QCPAxis::getTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen; +} + +/*! \internal + + Returns the pen that is used to draw the subticks. Depending on the selection state, this + is either mSelectedSubTickPen or mSubTickPen. +*/ +QPen QCPAxis::getSubTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen; +} + +/*! \internal + + Returns the font that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelFont or mTickLabelFont. +*/ +QFont QCPAxis::getTickLabelFont() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont; +} + +/*! \internal + + Returns the font that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelFont or mLabelFont. +*/ +QFont QCPAxis::getLabelFont() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont; +} + +/*! \internal + + Returns the color that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelColor or mTickLabelColor. +*/ +QColor QCPAxis::getTickLabelColor() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor; +} + +/*! \internal + + Returns the color that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelColor or mLabelColor. +*/ +QColor QCPAxis::getLabelColor() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor; +} + +/*! \internal + + Returns the appropriate outward margin for this axis. It is needed if \ref + QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref + atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom + margin and so forth. For the calculation, this function goes through similar steps as \ref draw, + so changing one function likely requires the modification of the other one as well. + + The margin consists of the outward tick length, tick label padding, tick label size, label + padding, label size, and padding. + + The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc. + unchanged are very fast. +*/ +int QCPAxis::calculateMargin() +{ + if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis + return 0; + + if (mCachedMarginValid) + return mCachedMargin; + + // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels + int margin = 0; + + int lowTick, highTick; + visibleTickBounds(lowTick, highTick); + QVector tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter + QVector tickLabels; // the final vector passed to QCPAxisPainter + tickPositions.reserve(highTick-lowTick+1); + tickLabels.reserve(highTick-lowTick+1); + if (mTicks) + { + for (int i=lowTick; i<=highTick; ++i) + { + tickPositions.append(coordToPixel(mTickVector.at(i))); + if (mTickLabels) + tickLabels.append(mTickVectorLabels.at(i)); + } + } + // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size. + // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters + mAxisPainter->type = mAxisType; + mAxisPainter->labelFont = getLabelFont(); + mAxisPainter->label = mLabel; + mAxisPainter->tickLabelFont = mTickLabelFont; + mAxisPainter->axisRect = mAxisRect->rect(); + mAxisPainter->viewportRect = mParentPlot->viewport(); + mAxisPainter->tickPositions = tickPositions; + mAxisPainter->tickLabels = tickLabels; + margin += mAxisPainter->size(); + margin += mPadding; + + mCachedMargin = margin; + mCachedMarginValid = true; + return margin; +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAxis::selectionCategory() const +{ + return QCP::iSelectAxes; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisPainterPrivate +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAxisPainterPrivate + + \internal + \brief (Private) + + This is a private class and not part of the public QCustomPlot interface. + + It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and + axis label. It also buffers the labels to reduce replot times. The parameters are configured by + directly accessing the public member variables. +*/ + +/*! + Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every + redraw, to utilize the caching mechanisms. +*/ +QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) : + type(QCPAxis::atLeft), + basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + lowerEnding(QCPLineEnding::esNone), + upperEnding(QCPLineEnding::esNone), + labelPadding(0), + tickLabelPadding(0), + tickLabelRotation(0), + tickLabelSide(QCPAxis::lsOutside), + substituteExponent(true), + numberMultiplyCross(false), + tickLengthIn(5), + tickLengthOut(0), + subTickLengthIn(2), + subTickLengthOut(0), + tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + offset(0), + abbreviateDecimalPowers(false), + reversedEndings(false), + mParentPlot(parentPlot), + mLabelCache(16) // cache at most 16 (tick) labels +{ +} + +QCPAxisPainterPrivate::~QCPAxisPainterPrivate() +{ +} + +/*! \internal + + Draws the axis with the specified \a painter. + + The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set + here, too. +*/ +void QCPAxisPainterPrivate::draw(QCPPainter *painter) +{ + QByteArray newHash = generateLabelParameterHash(); + if (newHash != mLabelParameterHash) + { + mLabelCache.clear(); + mLabelParameterHash = newHash; + } + + QPoint origin; + switch (type) + { + case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break; + case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break; + case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break; + case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break; + } + + double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes) + switch (type) + { + case QCPAxis::atTop: yCor = -1; break; + case QCPAxis::atRight: xCor = 1; break; + default: break; + } + int margin = 0; + // draw baseline: + QLineF baseLine; + painter->setPen(basePen); + if (QCPAxis::orientation(type) == Qt::Horizontal) + baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor)); + else + baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor)); + if (reversedEndings) + baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later + painter->drawLine(baseLine); + + // draw ticks: + if (!tickPositions.isEmpty()) + { + painter->setPen(tickPen); + int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis) + if (QCPAxis::orientation(type) == Qt::Horizontal) + { + for (int i=0; idrawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor)); + } else + { + for (int i=0; idrawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor)); + } + } + + // draw subticks: + if (!subTickPositions.isEmpty()) + { + painter->setPen(subTickPen); + // direction of ticks ("inward" is right for left axis and left for right axis) + int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; + if (QCPAxis::orientation(type) == Qt::Horizontal) + { + for (int i=0; idrawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor)); + } else + { + for (int i=0; idrawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor)); + } + } + margin += qMax(0, qMax(tickLengthOut, subTickLengthOut)); + + // draw axis base endings: + bool antialiasingBackup = painter->antialiasing(); + painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't + painter->setBrush(QBrush(basePen.color())); + QVector2D baseLineVector(baseLine.dx(), baseLine.dy()); + if (lowerEnding.style() != QCPLineEnding::esNone) + lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector); + if (upperEnding.style() != QCPLineEnding::esNone) + upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector); + painter->setAntialiasing(antialiasingBackup); + + // tick labels: + QRect oldClipRect; + if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect + { + oldClipRect = painter->clipRegion().boundingRect(); + painter->setClipRect(axisRect); + } + QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label + if (!tickLabels.isEmpty()) + { + if (tickLabelSide == QCPAxis::lsOutside) + margin += tickLabelPadding; + painter->setFont(tickLabelFont); + painter->setPen(QPen(tickLabelColor)); + const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size()); + int distanceToAxis = margin; + if (tickLabelSide == QCPAxis::lsInside) + distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding); + for (int i=0; isetClipRect(oldClipRect); + + // axis label: + QRect labelBounds; + if (!label.isEmpty()) + { + margin += labelPadding; + painter->setFont(labelFont); + painter->setPen(QPen(labelColor)); + labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label); + if (type == QCPAxis::atLeft) + { + QTransform oldTransform = painter->transform(); + painter->translate((origin.x()-margin-labelBounds.height()), origin.y()); + painter->rotate(-90); + painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + painter->setTransform(oldTransform); + } + else if (type == QCPAxis::atRight) + { + QTransform oldTransform = painter->transform(); + painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height()); + painter->rotate(90); + painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + painter->setTransform(oldTransform); + } + else if (type == QCPAxis::atTop) + painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + else if (type == QCPAxis::atBottom) + painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + } + + // set selection boxes: + int selectionTolerance = 0; + if (mParentPlot) + selectionTolerance = mParentPlot->selectionTolerance(); + else + qDebug() << Q_FUNC_INFO << "mParentPlot is null"; + int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance); + int selAxisInSize = selectionTolerance; + int selTickLabelSize; + int selTickLabelOffset; + if (tickLabelSide == QCPAxis::lsOutside) + { + selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width()); + selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding; + } else + { + selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width()); + selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding); + } + int selLabelSize = labelBounds.height(); + int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding; + if (type == QCPAxis::atLeft) + { + mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom()); + mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom()); + mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom()); + } else if (type == QCPAxis::atRight) + { + mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom()); + mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom()); + mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom()); + } else if (type == QCPAxis::atTop) + { + mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize); + mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset); + mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset); + } else if (type == QCPAxis::atBottom) + { + mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize); + mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset); + mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset); + } + mAxisSelectionBox = mAxisSelectionBox.normalized(); + mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized(); + mLabelSelectionBox = mLabelSelectionBox.normalized(); + // draw hitboxes for debug purposes: + //painter->setBrush(Qt::NoBrush); + //painter->drawRects(QVector() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox); +} + +/*! \internal + + Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone + direction) needed to fit the axis. +*/ +int QCPAxisPainterPrivate::size() const +{ + int result = 0; + + // get length of tick marks pointing outwards: + if (!tickPositions.isEmpty()) + result += qMax(0, qMax(tickLengthOut, subTickLengthOut)); + + // calculate size of tick labels: + if (tickLabelSide == QCPAxis::lsOutside) + { + QSize tickLabelsSize(0, 0); + if (!tickLabels.isEmpty()) + { + for (int i=0; iplottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled + { + CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache + if (!cachedLabel) // no cached label existed, create it + { + cachedLabel = new CachedLabel; + TickLabelData labelData = getTickLabelData(painter->font(), text); + cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft(); + cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); + cachedLabel->pixmap.fill(Qt::transparent); + QCPPainter cachePainter(&cachedLabel->pixmap); + cachePainter.setPen(painter->pen()); + drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData); + } + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left(); + else + labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top(); + } + if (!labelClippedByBorder) + { + painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap); + finalSize = cachedLabel->pixmap.size(); + } + mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created + } else // label caching disabled, draw text directly on surface: + { + TickLabelData labelData = getTickLabelData(painter->font(), text); + QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData); + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left(); + else + labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top(); + } + if (!labelClippedByBorder) + { + drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData); + finalSize = labelData.rotatedTotalBounds.size(); + } + } + + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a + y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to + directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when + QCP::phCacheLabels plotting hint is not set. +*/ +void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const +{ + // backup painter settings that we're about to change: + QTransform oldTransform = painter->transform(); + QFont oldFont = painter->font(); + + // transform painter to position/rotation: + painter->translate(x, y); + if (!qFuzzyIsNull(tickLabelRotation)) + painter->rotate(tickLabelRotation); + + // draw text: + if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used + { + painter->setFont(labelData.baseFont); + painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart); + painter->setFont(labelData.expFont); + painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart); + } else + { + painter->setFont(labelData.baseFont); + painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart); + } + + // reset painter settings to what it was before: + painter->setTransform(oldTransform); + painter->setFont(oldFont); +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Transforms the passed \a text and \a font to a tickLabelData structure that can then be further + processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and + exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes. +*/ +QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const +{ + TickLabelData result; + + // determine whether beautiful decimal powers should be used + bool useBeautifulPowers = false; + int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart + int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart + if (substituteExponent) + { + ePos = text.indexOf(QLatin1Char('e')); + if (ePos > 0 && text.at(ePos-1).isDigit()) + { + eLast = ePos; + while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit())) + ++eLast; + if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power + useBeautifulPowers = true; + } + } + + // calculate text bounding rects and do string preparation for beautiful decimal powers: + result.baseFont = font; + if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line + result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding + if (useBeautifulPowers) + { + // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent: + result.basePart = text.left(ePos); + // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base: + if (abbreviateDecimalPowers && result.basePart == QLatin1String("1")) + result.basePart = QLatin1String("10"); + else + result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10"); + result.expPart = text.mid(ePos+1); + // clip "+" and leading zeros off expPart: + while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e' + result.expPart.remove(1, 1); + if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+')) + result.expPart.remove(0, 1); + // prepare smaller font for exponent: + result.expFont = font; + if (result.expFont.pointSize() > 0) + result.expFont.setPointSize(result.expFont.pointSize()*0.75); + else + result.expFont.setPixelSize(result.expFont.pixelSize()*0.75); + // calculate bounding rects of base part, exponent part and total one: + result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart); + result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart); + result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA + } else // useBeautifulPowers == false + { + result.basePart = text; + result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart); + } + result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler + + // calculate possibly different bounding rect after rotation: + result.rotatedTotalBounds = result.totalBounds; + if (!qFuzzyIsNull(tickLabelRotation)) + { + QTransform transform; + transform.rotate(tickLabelRotation); + result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds); + } + + return result; +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Calculates the offset at which the top left corner of the specified tick label shall be drawn. + The offset is relative to a point right next to the tick the label belongs to. + + This function is thus responsible for e.g. centering tick labels under ticks and positioning them + appropriately when they are rotated. +*/ +QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const +{ + /* + calculate label offset from base point at tick (non-trivial, for best visual appearance): short + explanation for bottom axis: The anchor, i.e. the point in the label that is placed + horizontally under the corresponding tick is always on the label side that is closer to the + axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height + is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text + will be centered under the tick (i.e. displaced horizontally by half its height). At the same + time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick + labels. + */ + bool doRotation = !qFuzzyIsNull(tickLabelRotation); + bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes. + double radians = tickLabelRotation/180.0*M_PI; + int x=0, y=0; + if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = -qCos(radians)*labelData.totalBounds.width(); + y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0; + } else + { + x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height(); + y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0; + } + } else + { + x = -labelData.totalBounds.width(); + y = -labelData.totalBounds.height()/2.0; + } + } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = +qSin(radians)*labelData.totalBounds.height(); + y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0; + } else + { + x = 0; + y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0; + } + } else + { + x = 0; + y = -labelData.totalBounds.height()/2.0; + } + } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0; + y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height(); + } else + { + x = -qSin(-radians)*labelData.totalBounds.height()/2.0; + y = -qCos(-radians)*labelData.totalBounds.height(); + } + } else + { + x = -labelData.totalBounds.width()/2.0; + y = -labelData.totalBounds.height(); + } + } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = +qSin(radians)*labelData.totalBounds.height()/2.0; + y = 0; + } else + { + x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0; + y = +qSin(-radians)*labelData.totalBounds.width(); + } + } else + { + x = -labelData.totalBounds.width()/2.0; + y = 0; + } + } + + return QPointF(x, y); +} + +/*! \internal + + Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label + to be drawn, depending on number format etc. Since only the largest tick label is wanted for the + margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a + smaller width/height. +*/ +void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const +{ + // note: this function must return the same tick label sizes as the placeTickLabel function. + QSize finalSize; + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label + { + const CachedLabel *cachedLabel = mLabelCache.object(text); + finalSize = cachedLabel->pixmap.size(); + } else // label caching disabled or no label with this text cached: + { + TickLabelData labelData = getTickLabelData(font, text); + finalSize = labelData.rotatedTotalBounds.size(); + } + + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractPlottable +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractPlottable + \brief The abstract base class for all data representing objects in a plot. + + It defines a very basic interface like name, pen, brush, visibility etc. Since this class is + abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to + create new ways of displaying data (see "Creating own plottables" below). + + All further specifics are in the subclasses, for example: + \li A normal graph with possibly a line, scatter points and error bars: \ref QCPGraph + (typically created with \ref QCustomPlot::addGraph) + \li A parametric curve: \ref QCPCurve + \li A bar chart: \ref QCPBars + \li A statistical box plot: \ref QCPStatisticalBox + \li A color encoded two-dimensional map: \ref QCPColorMap + \li An OHLC/Candlestick chart: \ref QCPFinancial + + \section plottables-subclassing Creating own plottables + + To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure + virtual functions, you must implement: + \li \ref clearData + \li \ref selectTest + \li \ref draw + \li \ref drawLegendIcon + \li \ref getKeyRange + \li \ref getValueRange + + See the documentation of those functions for what they need to do. + + For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot + coordinates to pixel coordinates. This function is quite convenient, because it takes the + orientation of the key and value axes into account for you (x and y are swapped when the key axis + is vertical and the value axis horizontal). If you are worried about performance (i.e. you need + to translate many points in a loop like QCPGraph), you can directly use \ref + QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis + yourself. + + Here are some important members you inherit from QCPAbstractPlottable: + + + + + + + + + + + + + + + + + + + + + + + + + + +
QCustomPlot *\b mParentPlotA pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.
QString \b mNameThe name of the plottable.
QPen \b mPenThe generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable (e.g QCPGraph uses this pen for its graph lines and scatters)
QPen \b mSelectedPenThe generic pen that should be used when the plottable is selected (hint: \ref mainPen gives you the right pen, depending on selection state).
QBrush \b mBrushThe generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable (e.g. QCPGraph uses this brush to control filling under the graph)
QBrush \b mSelectedBrushThe generic brush that should be used when the plottable is selected (hint: \ref mainBrush gives you the right brush, depending on selection state).
QPointer\b mKeyAxis, \b mValueAxisThe key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates to pixels in either the key or value dimension. + Make sure to check whether the pointer is null before using it. If one of the axes is null, don't draw the plottable.
bool \b mSelectedindicates whether the plottable is selected or not.
+*/ + +/* start of documentation of pure virtual functions */ + +/*! \fn void QCPAbstractPlottable::clearData() = 0 + Clears all data in the plottable. +*/ + +/*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0 + \internal + + called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation + of this plottable inside \a rect, next to the plottable name. + + The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't + appear outside the legend icon border. +*/ + +/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0 + \internal + + called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can + set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the + returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain + to \ref sdNegative and all positive points will be ignored for range calculation. For no + restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output + parameter that indicates whether a range could be found or not. If this is false, you shouldn't + use the returned range (e.g. no points in data). + + Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by + this function may have size zero, which wouldn't count as a valid range. + + \see rescaleAxes, getValueRange +*/ + +/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0 + \internal + + called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can + set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the + returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain + to \ref sdNegative and all positive points will be ignored for range calculation. For no + restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output + parameter that indicates whether a range could be found or not. If this is false, you shouldn't + use the returned range (e.g. no points in data). + + Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by + this function may have size zero, which wouldn't count as a valid range. + + \see rescaleAxes, getKeyRange +*/ + +/* end of documentation of pure virtual functions */ +/* start of documentation of signals */ + +/*! \fn void QCPAbstractPlottable::selectionChanged(bool selected) + + This signal is emitted when the selection state of this plottable has changed, either by user + interaction or by a direct call to \ref setSelected. +*/ + +/*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable); + + This signal is emitted when the selectability of this plottable has changed. + + \see setSelectable +*/ + +/* end of documentation of signals */ + +/*! + Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as + its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance + and have perpendicular orientations. If either of these restrictions is violated, a corresponding + message is printed to the debug output (qDebug), the construction is not aborted, though. + + Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables, + it can't be directly instantiated. + + You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead. +*/ +QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()), + mName(), + mAntialiasedFill(true), + mAntialiasedScatters(true), + mAntialiasedErrorBars(false), + mPen(Qt::black), + mSelectedPen(Qt::black), + mBrush(Qt::NoBrush), + mSelectedBrush(Qt::NoBrush), + mKeyAxis(keyAxis), + mValueAxis(valueAxis), + mSelectable(true), + mSelected(false) +{ + if (keyAxis->parentPlot() != valueAxis->parentPlot()) + qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis."; + if (keyAxis->orientation() == valueAxis->orientation()) + qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other."; +} + +/*! + The name is the textual representation of this plottable as it is displayed in the legend + (\ref QCPLegend). It may contain any UTF-8 characters, including newlines. +*/ +void QCPAbstractPlottable::setName(const QString &name) +{ + mName = name; +} + +/*! + Sets whether fills of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPAbstractPlottable::setAntialiasedFill(bool enabled) +{ + mAntialiasedFill = enabled; +} + +/*! + Sets whether the scatter symbols of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPAbstractPlottable::setAntialiasedScatters(bool enabled) +{ + mAntialiasedScatters = enabled; +} + +/*! + Sets whether the error bars of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled) +{ + mAntialiasedErrorBars = enabled; +} + + +/*! + The pen is used to draw basic lines that make up the plottable representation in the + plot. + + For example, the \ref QCPGraph subclass draws its graph lines with this pen. + + \see setBrush +*/ +void QCPAbstractPlottable::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + When the plottable is selected, this pen is used to draw basic lines instead of the normal + pen set via \ref setPen. + + \see setSelected, setSelectable, setSelectedBrush, selectTest +*/ +void QCPAbstractPlottable::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + The brush is used to draw basic fills of the plottable representation in the + plot. The Fill can be a color, gradient or texture, see the usage of QBrush. + + For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when + it's not set to Qt::NoBrush. + + \see setPen +*/ +void QCPAbstractPlottable::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + When the plottable is selected, this brush is used to draw fills instead of the normal + brush set via \ref setBrush. + + \see setSelected, setSelectable, setSelectedPen, selectTest +*/ +void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal + to the plottable's value axis. This function performs no checks to make sure this is the case. + The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the + y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setValueAxis +*/ +void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis) +{ + mKeyAxis = axis; +} + +/*! + The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is + orthogonal to the plottable's key axis. This function performs no checks to make sure this is the + case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and + the y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setKeyAxis +*/ +void QCPAbstractPlottable::setValueAxis(QCPAxis *axis) +{ + mValueAxis = axis; +} + +/*! + Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectPlottables.) + + However, even when \a selectable was set to false, it is possible to set the selection manually, + by calling \ref setSelected directly. + + \see setSelected +*/ +void QCPAbstractPlottable::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets whether this plottable is selected or not. When selected, it uses a different pen and brush + to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush. + + The entire selection mechanism for plottables is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when + you wish to change the selection state manually. + + This function can change the selection state even when \ref setSelectable was set to false. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see setSelectable, selectTest +*/ +void QCPAbstractPlottable::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/*! + Rescales the key and value axes associated with this plottable to contain all displayed data, so + the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make + sure not to rescale to an illegal range i.e. a range containing different signs and/or zero. + Instead it will stay in the current sign domain and ignore all parts of the plottable that lie + outside of that domain. + + \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show + multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has + \a onlyEnlarge set to false (the default), and all subsequent set to true. + + \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale +*/ +void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const +{ + rescaleKeyAxis(onlyEnlarge); + rescaleValueAxis(onlyEnlarge); +} + +/*! + Rescales the key axis of the plottable so the whole plottable is visible. + + See \ref rescaleAxes for detailed behaviour. +*/ +void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + + SignDomain signDomain = sdBoth; + if (keyAxis->scaleType() == QCPAxis::stLogarithmic) + signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive); + + bool foundRange; + QCPRange newRange = getKeyRange(foundRange, signDomain); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(keyAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (keyAxis->scaleType() == QCPAxis::stLinear) + { + newRange.lower = center-keyAxis->range().size()/2.0; + newRange.upper = center+keyAxis->range().size()/2.0; + } else // scaleType() == stLogarithmic + { + newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower); + newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower); + } + } + keyAxis->setRange(newRange); + } +} + +/*! + Rescales the value axis of the plottable so the whole plottable is visible. + + Returns true if the axis was actually scaled. This might not be the case if this plottable has an + invalid range, e.g. because it has no data points. + + See \ref rescaleAxes for detailed behaviour. +*/ +void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const +{ + QCPAxis *valueAxis = mValueAxis.data(); + if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; } + + SignDomain signDomain = sdBoth; + if (valueAxis->scaleType() == QCPAxis::stLogarithmic) + signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive); + + bool foundRange; + QCPRange newRange = getValueRange(foundRange, signDomain); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(valueAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (valueAxis->scaleType() == QCPAxis::stLinear) + { + newRange.lower = center-valueAxis->range().size()/2.0; + newRange.upper = center+valueAxis->range().size()/2.0; + } else // scaleType() == stLogarithmic + { + newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower); + newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower); + } + } + valueAxis->setRange(newRange); + } +} + +/*! + Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend). + + Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable + needs a more specialized representation in the legend, this function will take this into account + and instead create the specialized subclass of QCPAbstractLegendItem. + + Returns true on success, i.e. when the legend exists and a legend item associated with this plottable isn't already in + the legend. + + \see removeFromLegend, QCPLegend::addItem +*/ +bool QCPAbstractPlottable::addToLegend() +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + + if (!mParentPlot->legend->hasItemWithPlottable(this)) + { + mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this)); + return true; + } else + return false; +} + +/*! + Removes the plottable from the legend of the parent QCustomPlot. This means the + QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable + is removed. + + Returns true on success, i.e. if the legend exists and a legend item associated with this + plottable was found and removed. + + \see addToLegend, QCPLegend::removeItem +*/ +bool QCPAbstractPlottable::removeFromLegend() const +{ + if (!mParentPlot->legend) + return false; + + if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this)) + return mParentPlot->legend->removeItem(lip); + else + return false; +} + +/* inherits documentation from base class */ +QRect QCPAbstractPlottable::clipRect() const +{ + if (mKeyAxis && mValueAxis) + return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect(); + else + return QRect(); +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAbstractPlottable::selectionCategory() const +{ + return QCP::iSelectPlottables; +} + +/*! \internal + + Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface, + taking the orientations of the axes associated with this plottable into account (e.g. whether key + represents x or y). + + \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y. + + \see pixelsToCoords, QCPAxis::coordToPixel +*/ +void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (keyAxis->orientation() == Qt::Horizontal) + { + x = keyAxis->coordToPixel(key); + y = valueAxis->coordToPixel(value); + } else + { + y = keyAxis->coordToPixel(key); + x = valueAxis->coordToPixel(value); + } +} + +/*! \internal + \overload + + Returns the input as pixel coordinates in a QPointF. +*/ +const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } + + if (keyAxis->orientation() == Qt::Horizontal) + return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value)); + else + return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key)); +} + +/*! \internal + + Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates, + taking the orientations of the axes associated with this plottable into account (e.g. whether key + represents x or y). + + \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value. + + \see coordsToPixels, QCPAxis::coordToPixel +*/ +void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (keyAxis->orientation() == Qt::Horizontal) + { + key = keyAxis->pixelToCoord(x); + value = valueAxis->pixelToCoord(y); + } else + { + key = keyAxis->pixelToCoord(y); + value = valueAxis->pixelToCoord(x); + } +} + +/*! \internal + \overload + + Returns the pixel input \a pixelPos as plot coordinates \a key and \a value. +*/ +void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const +{ + pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value); +} + +/*! \internal + + Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the + graph is not selected and mSelectedPen when it is. +*/ +QPen QCPAbstractPlottable::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the + graph is not selected and mSelectedBrush when it is. +*/ +QBrush QCPAbstractPlottable::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint +*/ +void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable fills. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint +*/ +void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable scatter points. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint +*/ +void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable error bars. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint +*/ +void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars); +} + +/*! \internal + + Finds the shortest squared distance of \a point to the line segment defined by \a start and \a + end. + + This function may be used to help with the implementation of the \ref selectTest function for + specific plottables. + + \note This function is identical to QCPAbstractItem::distSqrToLine +*/ +double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const +{ + QVector2D a(start); + QVector2D b(end); + QVector2D p(point); + QVector2D v(b-a); + + double vLengthSqr = v.lengthSquared(); + if (!qFuzzyIsNull(vLengthSqr)) + { + double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr; + if (mu < 0) + return (a-p).lengthSquared(); + else if (mu > 1) + return (b-p).lengthSquared(); + else + return ((a + mu*v)-p).lengthSquared(); + } else + return (a-p).lengthSquared(); +} + +/* inherits documentation from base class */ +void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemAnchor +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemAnchor + \brief An anchor of an item to which positions can be attached to. + + An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't + control anything on its item, but provides a way to tie other items via their positions to the + anchor. + + For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight. + Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can + attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by + calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the + QCPItemRect. This way the start of the line will now always follow the respective anchor location + on the rect item. + + Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an + anchor to other positions. + + To learn how to provide anchors in your own item subclasses, see the subclassing section of the + QCPAbstractItem documentation. +*/ + +/* start documentation of inline functions */ + +/*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition() + + Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if + it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor). + + This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids + dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with + gcc compiler). +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if + you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as + explained in the subclassing section of the QCPAbstractItem documentation. +*/ +QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) : + mName(name), + mParentPlot(parentPlot), + mParentItem(parentItem), + mAnchorId(anchorId) +{ +} + +QCPItemAnchor::~QCPItemAnchor() +{ + // unregister as parent at children: + foreach (QCPItemPosition *child, mChildrenX.toList()) + { + if (child->parentAnchorX() == this) + child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX + } + foreach (QCPItemPosition *child, mChildrenY.toList()) + { + if (child->parentAnchorY() == this) + child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY + } +} + +/*! + Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface. + + The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the + parent item, QCPItemAnchor is just an intermediary. +*/ +QPointF QCPItemAnchor::pixelPoint() const +{ + if (mParentItem) + { + if (mAnchorId > -1) + { + return mParentItem->anchorPixelPoint(mAnchorId); + } else + { + qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId; + return QPointF(); + } + } else + { + qDebug() << Q_FUNC_INFO << "no parent item set"; + return QPointF(); + } +} + +/*! \internal + + Adds \a pos to the childX list of this anchor, which keeps track of which children use this + anchor as parent anchor for the respective coordinate. This is necessary to notify the children + prior to destruction of the anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::addChildX(QCPItemPosition *pos) +{ + if (!mChildrenX.contains(pos)) + mChildrenX.insert(pos); + else + qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast(pos); +} + +/*! \internal + + Removes \a pos from the childX list of this anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::removeChildX(QCPItemPosition *pos) +{ + if (!mChildrenX.remove(pos)) + qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast(pos); +} + +/*! \internal + + Adds \a pos to the childY list of this anchor, which keeps track of which children use this + anchor as parent anchor for the respective coordinate. This is necessary to notify the children + prior to destruction of the anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::addChildY(QCPItemPosition *pos) +{ + if (!mChildrenY.contains(pos)) + mChildrenY.insert(pos); + else + qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast(pos); +} + +/*! \internal + + Removes \a pos from the childY list of this anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::removeChildY(QCPItemPosition *pos) +{ + if (!mChildrenY.remove(pos)) + qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast(pos); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemPosition +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemPosition + \brief Manages the position of an item. + + Every item has at least one public QCPItemPosition member pointer which provides ways to position the + item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two: + \a topLeft and \a bottomRight. + + QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type + defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel + coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also + possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref + setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y + direction, while following a plot coordinate in the X direction. + + A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie + multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords) + are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0) + means directly ontop of the parent anchor. For example, You could attach the \a start position of + a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line + always be centered under the text label, no matter where the text is moved to. For more advanced + plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see + \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X + direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B + in Y. + + Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent + anchor for other positions. + + To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This + works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref + setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified + pixel values. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const + + Returns the current position type. + + If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the + type of the X coordinate. In that case rather use \a typeX() and \a typeY(). + + \see setType +*/ + +/*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const + + Returns the current parent anchor. + + If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY), + this method returns the parent anchor of the Y coordinate. In that case rather use \a + parentAnchorX() and \a parentAnchorY(). + + \see setParentAnchor +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if + you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as + explained in the subclassing section of the QCPAbstractItem documentation. +*/ +QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) : + QCPItemAnchor(parentPlot, parentItem, name), + mPositionTypeX(ptAbsolute), + mPositionTypeY(ptAbsolute), + mKey(0), + mValue(0), + mParentAnchorX(0), + mParentAnchorY(0) +{ +} + +QCPItemPosition::~QCPItemPosition() +{ + // unregister as parent at children: + // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then + // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint + foreach (QCPItemPosition *child, mChildrenX.toList()) + { + if (child->parentAnchorX() == this) + child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX + } + foreach (QCPItemPosition *child, mChildrenY.toList()) + { + if (child->parentAnchorY() == this) + child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY + } + // unregister as child in parent: + if (mParentAnchorX) + mParentAnchorX->removeChildX(this); + if (mParentAnchorY) + mParentAnchorY->removeChildY(this); +} + +/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */ +QCPAxisRect *QCPItemPosition::axisRect() const +{ + return mAxisRect.data(); +} + +/*! + Sets the type of the position. The type defines how the coordinates passed to \ref setCoords + should be handled and how the QCPItemPosition should behave in the plot. + + The possible values for \a type can be separated in two main categories: + + \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords + and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes. + By default, the QCustomPlot's x- and yAxis are used. + + \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This + corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref + ptAxisRectRatio. They differ only in the way the absolute position is described, see the + documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify + the axis rect with \ref setAxisRect. By default this is set to the main axis rect. + + Note that the position type \ref ptPlotCoords is only available (and sensible) when the position + has no parent anchor (\ref setParentAnchor). + + If the type is changed, the apparent pixel position on the plot is preserved. This means + the coordinates as retrieved with coords() and set with \ref setCoords may change in the process. + + This method sets the type for both X and Y directions. It is also possible to set different types + for X and Y, see \ref setTypeX, \ref setTypeY. +*/ +void QCPItemPosition::setType(QCPItemPosition::PositionType type) +{ + setTypeX(type); + setTypeY(type); +} + +/*! + This method sets the position type of the X coordinate to \a type. + + For a detailed description of what a position type is, see the documentation of \ref setType. + + \see setType, setTypeY +*/ +void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type) +{ + if (mPositionTypeX != type) + { + // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect + // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning. + bool retainPixelPosition = true; + if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis)) + retainPixelPosition = false; + if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect)) + retainPixelPosition = false; + + QPointF pixel; + if (retainPixelPosition) + pixel = pixelPoint(); + + mPositionTypeX = type; + + if (retainPixelPosition) + setPixelPoint(pixel); + } +} + +/*! + This method sets the position type of the Y coordinate to \a type. + + For a detailed description of what a position type is, see the documentation of \ref setType. + + \see setType, setTypeX +*/ +void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type) +{ + if (mPositionTypeY != type) + { + // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect + // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning. + bool retainPixelPosition = true; + if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis)) + retainPixelPosition = false; + if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect)) + retainPixelPosition = false; + + QPointF pixel; + if (retainPixelPosition) + pixel = pixelPoint(); + + mPositionTypeY = type; + + if (retainPixelPosition) + setPixelPoint(pixel); + } +} + +/*! + Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now + follow any position changes of the anchor. The local coordinate system of positions with a parent + anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence + the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.) + + if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved + during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position + will be exactly on top of the parent anchor. + + To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0. + + If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is + set to \ref ptAbsolute, to keep the position in a valid state. + + This method sets the parent anchor for both X and Y directions. It is also possible to set + different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY. +*/ +bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition) +{ + bool successX = setParentAnchorX(parentAnchor, keepPixelPosition); + bool successY = setParentAnchorY(parentAnchor, keepPixelPosition); + return successX && successY; +} + +/*! + This method sets the parent anchor of the X coordinate to \a parentAnchor. + + For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor. + + \see setParentAnchor, setParentAnchorY +*/ +bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition) +{ + // make sure self is not assigned as parent: + if (parentAnchor == this) + { + qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast(parentAnchor); + return false; + } + // make sure no recursive parent-child-relationships are created: + QCPItemAnchor *currentParent = parentAnchor; + while (currentParent) + { + if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition()) + { + // is a QCPItemPosition, might have further parent, so keep iterating + if (currentParentPos == this) + { + qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast(parentAnchor); + return false; + } + currentParent = currentParentPos->parentAnchorX(); + } else + { + // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the + // same, to prevent a position being child of an anchor which itself depends on the position, + // because they're both on the same item: + if (currentParent->mParentItem == mParentItem) + { + qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast(parentAnchor); + return false; + } + break; + } + } + + // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute: + if (!mParentAnchorX && mPositionTypeX == ptPlotCoords) + setTypeX(ptAbsolute); + + // save pixel position: + QPointF pixelP; + if (keepPixelPosition) + pixelP = pixelPoint(); + // unregister at current parent anchor: + if (mParentAnchorX) + mParentAnchorX->removeChildX(this); + // register at new parent anchor: + if (parentAnchor) + parentAnchor->addChildX(this); + mParentAnchorX = parentAnchor; + // restore pixel position under new parent: + if (keepPixelPosition) + setPixelPoint(pixelP); + else + setCoords(0, coords().y()); + return true; +} + +/*! + This method sets the parent anchor of the Y coordinate to \a parentAnchor. + + For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor. + + \see setParentAnchor, setParentAnchorX +*/ +bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition) +{ + // make sure self is not assigned as parent: + if (parentAnchor == this) + { + qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast(parentAnchor); + return false; + } + // make sure no recursive parent-child-relationships are created: + QCPItemAnchor *currentParent = parentAnchor; + while (currentParent) + { + if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition()) + { + // is a QCPItemPosition, might have further parent, so keep iterating + if (currentParentPos == this) + { + qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast(parentAnchor); + return false; + } + currentParent = currentParentPos->parentAnchorY(); + } else + { + // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the + // same, to prevent a position being child of an anchor which itself depends on the position, + // because they're both on the same item: + if (currentParent->mParentItem == mParentItem) + { + qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast(parentAnchor); + return false; + } + break; + } + } + + // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute: + if (!mParentAnchorY && mPositionTypeY == ptPlotCoords) + setTypeY(ptAbsolute); + + // save pixel position: + QPointF pixelP; + if (keepPixelPosition) + pixelP = pixelPoint(); + // unregister at current parent anchor: + if (mParentAnchorY) + mParentAnchorY->removeChildY(this); + // register at new parent anchor: + if (parentAnchor) + parentAnchor->addChildY(this); + mParentAnchorY = parentAnchor; + // restore pixel position under new parent: + if (keepPixelPosition) + setPixelPoint(pixelP); + else + setCoords(coords().x(), 0); + return true; +} + +/*! + Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type + (\ref setType, \ref setTypeX, \ref setTypeY). + + For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position + on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the + QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the + plot coordinate system defined by the axes set by \ref setAxes. By default those are the + QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available + coordinate types and their meaning. + + If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a + value must also be provided in the different coordinate systems. Here, the X type refers to \a + key, and the Y type refers to \a value. + + \see setPixelPoint +*/ +void QCPItemPosition::setCoords(double key, double value) +{ + mKey = key; + mValue = value; +} + +/*! \overload + + Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the + meaning of \a value of the \ref setCoords(double key, double value) method. +*/ +void QCPItemPosition::setCoords(const QPointF &pos) +{ + setCoords(pos.x(), pos.y()); +} + +/*! + Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It + includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor). + + \see setPixelPoint +*/ +QPointF QCPItemPosition::pixelPoint() const +{ + QPointF result; + + // determine X: + switch (mPositionTypeX) + { + case ptAbsolute: + { + result.rx() = mKey; + if (mParentAnchorX) + result.rx() += mParentAnchorX->pixelPoint().x(); + break; + } + case ptViewportRatio: + { + result.rx() = mKey*mParentPlot->viewport().width(); + if (mParentAnchorX) + result.rx() += mParentAnchorX->pixelPoint().x(); + else + result.rx() += mParentPlot->viewport().left(); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + result.rx() = mKey*mAxisRect.data()->width(); + if (mParentAnchorX) + result.rx() += mParentAnchorX->pixelPoint().x(); + else + result.rx() += mAxisRect.data()->left(); + } else + qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal) + result.rx() = mKeyAxis.data()->coordToPixel(mKey); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal) + result.rx() = mValueAxis.data()->coordToPixel(mValue); + else + qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined"; + break; + } + } + + // determine Y: + switch (mPositionTypeY) + { + case ptAbsolute: + { + result.ry() = mValue; + if (mParentAnchorY) + result.ry() += mParentAnchorY->pixelPoint().y(); + break; + } + case ptViewportRatio: + { + result.ry() = mValue*mParentPlot->viewport().height(); + if (mParentAnchorY) + result.ry() += mParentAnchorY->pixelPoint().y(); + else + result.ry() += mParentPlot->viewport().top(); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + result.ry() = mValue*mAxisRect.data()->height(); + if (mParentAnchorY) + result.ry() += mParentAnchorY->pixelPoint().y(); + else + result.ry() += mAxisRect.data()->top(); + } else + qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical) + result.ry() = mKeyAxis.data()->coordToPixel(mKey); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical) + result.ry() = mValueAxis.data()->coordToPixel(mValue); + else + qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined"; + break; + } + } + + return result; +} + +/*! + When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the + coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and + yAxis of the QCustomPlot. +*/ +void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis) +{ + mKeyAxis = keyAxis; + mValueAxis = valueAxis; +} + +/*! + When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the + coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of + the QCustomPlot. +*/ +void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect) +{ + mAxisRect = axisRect; +} + +/*! + Sets the apparent pixel position. This works no matter what type (\ref setType) this + QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed + appropriately, to make the position finally appear at the specified pixel values. + + Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is + identical to that of \ref setCoords. + + \see pixelPoint, setCoords +*/ +void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint) +{ + double x = pixelPoint.x(); + double y = pixelPoint.y(); + + switch (mPositionTypeX) + { + case ptAbsolute: + { + if (mParentAnchorX) + x -= mParentAnchorX->pixelPoint().x(); + break; + } + case ptViewportRatio: + { + if (mParentAnchorX) + x -= mParentAnchorX->pixelPoint().x(); + else + x -= mParentPlot->viewport().left(); + x /= (double)mParentPlot->viewport().width(); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + if (mParentAnchorX) + x -= mParentAnchorX->pixelPoint().x(); + else + x -= mAxisRect.data()->left(); + x /= (double)mAxisRect.data()->width(); + } else + qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal) + x = mKeyAxis.data()->pixelToCoord(x); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal) + y = mValueAxis.data()->pixelToCoord(x); + else + qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined"; + break; + } + } + + switch (mPositionTypeY) + { + case ptAbsolute: + { + if (mParentAnchorY) + y -= mParentAnchorY->pixelPoint().y(); + break; + } + case ptViewportRatio: + { + if (mParentAnchorY) + y -= mParentAnchorY->pixelPoint().y(); + else + y -= mParentPlot->viewport().top(); + y /= (double)mParentPlot->viewport().height(); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + if (mParentAnchorY) + y -= mParentAnchorY->pixelPoint().y(); + else + y -= mAxisRect.data()->top(); + y /= (double)mAxisRect.data()->height(); + } else + qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical) + x = mKeyAxis.data()->pixelToCoord(y); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical) + y = mValueAxis.data()->pixelToCoord(y); + else + qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined"; + break; + } + } + + setCoords(x, y); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractItem + \brief The abstract base class for all items in a plot. + + In QCustomPlot, items are supplemental graphical elements that are neither plottables + (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus + plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each + specific item has at least one QCPItemPosition member which controls the positioning. Some items + are defined by more than one coordinate and thus have two or more QCPItemPosition members (For + example, QCPItemRect has \a topLeft and \a bottomRight). + + This abstract base class defines a very basic interface like visibility and clipping. Since this + class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass + yourself to create new items. + + The built-in items are: + + + + + + + + + + +
QCPItemLineA line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).
QCPItemStraightLineA straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.
QCPItemCurveA curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).
QCPItemRectA rectangle
QCPItemEllipseAn ellipse
QCPItemPixmapAn arbitrary pixmap
QCPItemTextA text label
QCPItemBracketA bracket which may be used to reference/highlight certain parts in the plot.
QCPItemTracerAn item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.
+ + \section items-clipping Clipping + + Items are by default clipped to the main axis rect (they are only visible inside the axis rect). + To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect + "setClipToAxisRect(false)". + + On the other hand if you want the item to be clipped to a different axis rect, specify it via + \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and + in principle is independent of the coordinate axes the item might be tied to via its position + members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping + also contains the axes used for the item positions. + + \section items-using Using items + + First you instantiate the item you want to use and add it to the plot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1 + by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just + set the plot coordinates where the line should start/end: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2 + If we don't want the line to be positioned in plot coordinates but a different coordinate system, + e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3 + Then we can set the coordinates, this time in pixels: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4 + and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5 + + For more advanced plots, it is even possible to set different types and parent anchors per X/Y + coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref + QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition. + + \section items-subclassing Creating own items + + To create an own item, you implement a subclass of QCPAbstractItem. These are the pure + virtual functions, you must implement: + \li \ref selectTest + \li \ref draw + + See the documentation of those functions for what they need to do. + + \subsection items-positioning Allowing the item to be positioned + + As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall + have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add + a public member of type QCPItemPosition like so: + + \code QCPItemPosition * const myPosition;\endcode + + the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition + instance it points to, can be modified, of course). + The initialization of this pointer is made easy with the \ref createPosition function. Just assign + the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition + takes a string which is the name of the position, typically this is identical to the variable name. + For example, the constructor of QCPItemExample could look like this: + + \code + QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + myPosition(createPosition("myPosition")) + { + // other constructor code + } + \endcode + + \subsection items-drawing The draw function + + To give your item a visual representation, reimplement the \ref draw function and use the passed + QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the + position member(s) via \ref QCPItemPosition::pixelPoint. + + To optimize performance you should calculate a bounding rect first (don't forget to take the pen + width into account), check whether it intersects the \ref clipRect, and only draw the item at all + if this is the case. + + \subsection items-selection The selectTest function + + Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and + \ref rectSelectTest. With these, the implementation of the selection test becomes significantly + simpler for most items. See the documentation of \ref selectTest for what the function parameters + mean and what the function should return. + + \subsection anchors Providing anchors + + Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public + member, e.g. + + \code QCPItemAnchor * const bottom;\endcode + + and create it in the constructor with the \ref createAnchor function, assigning it a name and an + anchor id (an integer enumerating all anchors on the item, you may create an own enum for this). + Since anchors can be placed anywhere, relative to the item's position(s), your item needs to + provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int + anchorId) function. + + In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel + position when anything attached to the anchor needs to know the coordinates. +*/ + +/* start of documentation of inline functions */ + +/*! \fn QList QCPAbstractItem::positions() const + + Returns all positions of the item in a list. + + \see anchors, position +*/ + +/*! \fn QList QCPAbstractItem::anchors() const + + Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always + also an anchor, the list will also contain the positions of this item. + + \see positions, anchor +*/ + +/* end of documentation of inline functions */ +/* start documentation of pure virtual functions */ + +/*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0 + \internal + + Draws this item with the provided \a painter. + + The cliprect of the provided painter is set to the rect returned by \ref clipRect before this + function is called. The clipRect depends on the clipping settings defined by \ref + setClipToAxisRect and \ref setClipAxisRect. +*/ + +/* end documentation of pure virtual functions */ +/* start documentation of signals */ + +/*! \fn void QCPAbstractItem::selectionChanged(bool selected) + This signal is emitted when the selection state of this item has changed, either by user interaction + or by a direct call to \ref setSelected. +*/ + +/* end documentation of signals */ + +/*! + Base class constructor which initializes base class members. +*/ +QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) : + QCPLayerable(parentPlot), + mClipToAxisRect(false), + mSelectable(true), + mSelected(false) +{ + QList rects = parentPlot->axisRects(); + if (rects.size() > 0) + { + setClipToAxisRect(true); + setClipAxisRect(rects.first()); + } +} + +QCPAbstractItem::~QCPAbstractItem() +{ + // don't delete mPositions because every position is also an anchor and thus in mAnchors + qDeleteAll(mAnchors); +} + +/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */ +QCPAxisRect *QCPAbstractItem::clipAxisRect() const +{ + return mClipAxisRect.data(); +} + +/*! + Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the + entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect. + + \see setClipAxisRect +*/ +void QCPAbstractItem::setClipToAxisRect(bool clip) +{ + mClipToAxisRect = clip; + if (mClipToAxisRect) + setParentLayerable(mClipAxisRect.data()); +} + +/*! + Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref + setClipToAxisRect is set to true. + + \see setClipToAxisRect +*/ +void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect) +{ + mClipAxisRect = rect; + if (mClipToAxisRect) + setParentLayerable(mClipAxisRect.data()); +} + +/*! + Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.) + + However, even when \a selectable was set to false, it is possible to set the selection manually, + by calling \ref setSelected. + + \see QCustomPlot::setInteractions, setSelected +*/ +void QCPAbstractItem::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets whether this item is selected or not. When selected, it might use a different visual + appearance (e.g. pen and brush), this depends on the specific item though. + + The entire selection mechanism for items is handled automatically when \ref + QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this + function when you wish to change the selection state manually. + + This function can change the selection state even when \ref setSelectable was set to false. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see setSelectable, selectTest +*/ +void QCPAbstractItem::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/*! + Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by + that name, returns 0. + + This function provides an alternative way to access item positions. Normally, you access + positions direcly by their member pointers (which typically have the same variable name as \a + name). + + \see positions, anchor +*/ +QCPItemPosition *QCPAbstractItem::position(const QString &name) const +{ + for (int i=0; iname() == name) + return mPositions.at(i); + } + qDebug() << Q_FUNC_INFO << "position with name not found:" << name; + return 0; +} + +/*! + Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by + that name, returns 0. + + This function provides an alternative way to access item anchors. Normally, you access + anchors direcly by their member pointers (which typically have the same variable name as \a + name). + + \see anchors, position +*/ +QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const +{ + for (int i=0; iname() == name) + return mAnchors.at(i); + } + qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name; + return 0; +} + +/*! + Returns whether this item has an anchor with the specified \a name. + + Note that you can check for positions with this function, too. This is because every position is + also an anchor (QCPItemPosition inherits from QCPItemAnchor). + + \see anchor, position +*/ +bool QCPAbstractItem::hasAnchor(const QString &name) const +{ + for (int i=0; iname() == name) + return true; + } + return false; +} + +/*! \internal + + Returns the rect the visual representation of this item is clipped to. This depends on the + current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect. + + If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned. + + \see draw +*/ +QRect QCPAbstractItem::clipRect() const +{ + if (mClipToAxisRect && mClipAxisRect) + return mClipAxisRect.data()->rect(); + else + return mParentPlot->viewport(); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing item lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeItems); +} + +/*! \internal + + Finds the shortest squared distance of \a point to the line segment defined by \a start and \a + end. + + This function may be used to help with the implementation of the \ref selectTest function for + specific items. + + \note This function is identical to QCPAbstractPlottable::distSqrToLine + + \see rectSelectTest +*/ +double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const +{ + QVector2D a(start); + QVector2D b(end); + QVector2D p(point); + QVector2D v(b-a); + + double vLengthSqr = v.lengthSquared(); + if (!qFuzzyIsNull(vLengthSqr)) + { + double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr; + if (mu < 0) + return (a-p).lengthSquared(); + else if (mu > 1) + return (b-p).lengthSquared(); + else + return ((a + mu*v)-p).lengthSquared(); + } else + return (a-p).lengthSquared(); +} + +/*! \internal + + A convenience function which returns the selectTest value for a specified \a rect and a specified + click position \a pos. \a filledRect defines whether a click inside the rect should also be + considered a hit or whether only the rect border is sensitive to hits. + + This function may be used to help with the implementation of the \ref selectTest function for + specific items. + + For example, if your item consists of four rects, call this function four times, once for each + rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned + values. + + \see distSqrToLine +*/ +double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const +{ + double result = -1; + + // distance to border: + QList lines; + lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight()) + << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight()); + double minDistSqr = std::numeric_limits::max(); + for (int i=0; i mParentPlot->selectionTolerance()*0.99) + { + if (rect.contains(pos)) + result = mParentPlot->selectionTolerance()*0.99; + } + return result; +} + +/*! \internal + + Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in + item subclasses if they want to provide anchors (QCPItemAnchor). + + For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor + ids and returns the respective pixel points of the specified anchor. + + \see createAnchor +*/ +QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const +{ + qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId; + return QPointF(); +} + +/*! \internal + + Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified + \a name must be a unique string that is usually identical to the variable name of the position + member (This is needed to provide the name-based \ref position access to positions). + + Don't delete positions created by this function manually, as the item will take care of it. + + Use this function in the constructor (initialization list) of the specific item subclass to + create each position member. Don't create QCPItemPositions with \b new yourself, because they + won't be registered with the item properly. + + \see createAnchor +*/ +QCPItemPosition *QCPAbstractItem::createPosition(const QString &name) +{ + if (hasAnchor(name)) + qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name; + QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name); + mPositions.append(newPosition); + mAnchors.append(newPosition); // every position is also an anchor + newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis); + newPosition->setType(QCPItemPosition::ptPlotCoords); + if (mParentPlot->axisRect()) + newPosition->setAxisRect(mParentPlot->axisRect()); + newPosition->setCoords(0, 0); + return newPosition; +} + +/*! \internal + + Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified + \a name must be a unique string that is usually identical to the variable name of the anchor + member (This is needed to provide the name based \ref anchor access to anchors). + + The \a anchorId must be a number identifying the created anchor. It is recommended to create an + enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor + to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns + the correct pixel coordinates for the passed anchor id. + + Don't delete anchors created by this function manually, as the item will take care of it. + + Use this function in the constructor (initialization list) of the specific item subclass to + create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they + won't be registered with the item properly. + + \see createPosition +*/ +QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId) +{ + if (hasAnchor(name)) + qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name; + QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId); + mAnchors.append(newAnchor); + return newAnchor; +} + +/* inherits documentation from base class */ +void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAbstractItem::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAbstractItem::selectionCategory() const +{ + return QCP::iSelectItems; +} + + +/*! \file */ + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCustomPlot +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCustomPlot + + \brief The central class of the library. This is the QWidget which displays the plot and + interacts with the user. + + For tutorials on how to use QCustomPlot, see the website\n + http://www.qcustomplot.com/ +*/ + +/* start of documentation of inline functions */ + +/*! \fn QRect QCustomPlot::viewport() const + + Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is + drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the + plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left + (0, 0) and size of the QCustomPlot widget. + + Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically + an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger + and contains also the axes themselves, their tick numbers, their labels, the plot title etc. + + Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport is temporarily + modified to allow saving plots with sizes independent of the current widget size. +*/ + +/*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const + + Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just + one cell with the main QCPAxisRect inside. +*/ + +/* end of documentation of inline functions */ +/* start of documentation of signals */ + +/*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse double click event. +*/ + +/*! \fn void QCustomPlot::mousePress(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse press event. + + It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot + connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref + QCPAxisRect::setRangeDragAxes. +*/ + +/*! \fn void QCustomPlot::mouseMove(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse move event. + + It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot + connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref + QCPAxisRect::setRangeDragAxes. + + \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here, + because the dragging starting point was saved the moment the mouse was pressed. Thus it only has + a meaning for the range drag axes that were set at that moment. If you want to change the drag + axes, consider doing this in the \ref mousePress signal instead. +*/ + +/*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse release event. + + It is emitted before QCustomPlot handles any other mechanisms like object selection. So a + slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or + \ref QCPAbstractPlottable::setSelectable. +*/ + +/*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse wheel event. + + It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot + connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref + QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor. +*/ + +/*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event) + + This signal is emitted when a plottable is clicked. + + \a event is the mouse event that caused the click and \a plottable is the plottable that received + the click. + + \see plottableDoubleClick +*/ + +/*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event) + + This signal is emitted when a plottable is double clicked. + + \a event is the mouse event that caused the click and \a plottable is the plottable that received + the click. + + \see plottableClick +*/ + +/*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event) + + This signal is emitted when an item is clicked. + + \a event is the mouse event that caused the click and \a item is the item that received the + click. + + \see itemDoubleClick +*/ + +/*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event) + + This signal is emitted when an item is double clicked. + + \a event is the mouse event that caused the click and \a item is the item that received the + click. + + \see itemClick +*/ + +/*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event) + + This signal is emitted when an axis is clicked. + + \a event is the mouse event that caused the click, \a axis is the axis that received the click and + \a part indicates the part of the axis that was clicked. + + \see axisDoubleClick +*/ + +/*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event) + + This signal is emitted when an axis is double clicked. + + \a event is the mouse event that caused the click, \a axis is the axis that received the click and + \a part indicates the part of the axis that was clicked. + + \see axisClick +*/ + +/*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event) + + This signal is emitted when a legend (item) is clicked. + + \a event is the mouse event that caused the click, \a legend is the legend that received the + click and \a item is the legend item that received the click. If only the legend and no item is + clicked, \a item is 0. This happens for a click inside the legend padding or the space between + two items. + + \see legendDoubleClick +*/ + +/*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event) + + This signal is emitted when a legend (item) is double clicked. + + \a event is the mouse event that caused the click, \a legend is the legend that received the + click and \a item is the legend item that received the click. If only the legend and no item is + clicked, \a item is 0. This happens for a click inside the legend padding or the space between + two items. + + \see legendClick +*/ + +/*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title) + + This signal is emitted when a plot title is clicked. + + \a event is the mouse event that caused the click and \a title is the plot title that received + the click. + + \see titleDoubleClick +*/ + +/*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title) + + This signal is emitted when a plot title is double clicked. + + \a event is the mouse event that caused the click and \a title is the plot title that received + the click. + + \see titleClick +*/ + +/*! \fn void QCustomPlot::selectionChangedByUser() + + This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by + clicking. It is not emitted when the selection state of an object has changed programmatically by + a direct call to setSelected() on an object or by calling \ref deselectAll. + + In addition to this signal, selectable objects also provide individual signals, for example + QCPAxis::selectionChanged or QCPAbstractPlottable::selectionChanged. Note that those signals are + emitted even if the selection state is changed programmatically. + + See the documentation of \ref setInteractions for details about the selection mechanism. + + \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends +*/ + +/*! \fn void QCustomPlot::beforeReplot() + + This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref + replot). + + It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them + replot synchronously, it won't cause an infinite recursion. + + \see replot, afterReplot +*/ + +/*! \fn void QCustomPlot::afterReplot() + + This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref + replot). + + It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them + replot synchronously, it won't cause an infinite recursion. + + \see replot, beforeReplot +*/ + +/* end of documentation of signals */ +/* start of documentation of public members */ + +/*! \var QCPAxis *QCustomPlot::xAxis + + A pointer to the primary x Axis (bottom) of the main axis rect of the plot. + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become 0. +*/ + +/*! \var QCPAxis *QCustomPlot::yAxis + + A pointer to the primary y Axis (left) of the main axis rect of the plot. + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become 0. +*/ + +/*! \var QCPAxis *QCustomPlot::xAxis2 + + A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are + invisible by default. Use QCPAxis::setVisible to change this (or use \ref + QCPAxisRect::setupFullAxesBox). + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become 0. +*/ + +/*! \var QCPAxis *QCustomPlot::yAxis2 + + A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are + invisible by default. Use QCPAxis::setVisible to change this (or use \ref + QCPAxisRect::setupFullAxesBox). + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become 0. +*/ + +/*! \var QCPLegend *QCustomPlot::legend + + A pointer to the default legend of the main axis rect. The legend is invisible by default. Use + QCPLegend::setVisible to change this. + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple legends to the plot, use the layout system interface to + access the new legend. For example, legends can be placed inside an axis rect's \ref + QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If + the default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointer becomes 0. +*/ + +/* end of documentation of public members */ + +/*! + Constructs a QCustomPlot and sets reasonable default values. +*/ +QCustomPlot::QCustomPlot(QWidget *parent) : + QWidget(parent), + xAxis(0), + yAxis(0), + xAxis2(0), + yAxis2(0), + legend(0), + mPlotLayout(0), + mAutoAddPlottableToLegend(true), + mAntialiasedElements(QCP::aeNone), + mNotAntialiasedElements(QCP::aeNone), + mInteractions(0), + mSelectionTolerance(8), + mNoAntialiasingOnDrag(false), + mBackgroundBrush(Qt::white, Qt::SolidPattern), + mBackgroundScaled(true), + mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), + mCurrentLayer(0), + mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint), + mMultiSelectModifier(Qt::ControlModifier), + mPaintBuffer(size()), + mMouseEventElement(0), + mReplotting(false) +{ + setAttribute(Qt::WA_NoMousePropagation); + setAttribute(Qt::WA_OpaquePaintEvent); + setMouseTracking(true); + QLocale currentLocale = locale(); + currentLocale.setNumberOptions(QLocale::OmitGroupSeparator); + setLocale(currentLocale); + + // create initial layers: + mLayers.append(new QCPLayer(this, QLatin1String("background"))); + mLayers.append(new QCPLayer(this, QLatin1String("grid"))); + mLayers.append(new QCPLayer(this, QLatin1String("main"))); + mLayers.append(new QCPLayer(this, QLatin1String("axes"))); + mLayers.append(new QCPLayer(this, QLatin1String("legend"))); + updateLayerIndices(); + setCurrentLayer(QLatin1String("main")); + + // create initial layout, axis rect and legend: + mPlotLayout = new QCPLayoutGrid; + mPlotLayout->initializeParentPlot(this); + mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry + mPlotLayout->setLayer(QLatin1String("main")); + QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true); + mPlotLayout->addElement(0, 0, defaultAxisRect); + xAxis = defaultAxisRect->axis(QCPAxis::atBottom); + yAxis = defaultAxisRect->axis(QCPAxis::atLeft); + xAxis2 = defaultAxisRect->axis(QCPAxis::atTop); + yAxis2 = defaultAxisRect->axis(QCPAxis::atRight); + legend = new QCPLegend; + legend->setVisible(false); + defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop); + defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12)); + + defaultAxisRect->setLayer(QLatin1String("background")); + xAxis->setLayer(QLatin1String("axes")); + yAxis->setLayer(QLatin1String("axes")); + xAxis2->setLayer(QLatin1String("axes")); + yAxis2->setLayer(QLatin1String("axes")); + xAxis->grid()->setLayer(QLatin1String("grid")); + yAxis->grid()->setLayer(QLatin1String("grid")); + xAxis2->grid()->setLayer(QLatin1String("grid")); + yAxis2->grid()->setLayer(QLatin1String("grid")); + legend->setLayer(QLatin1String("legend")); + + setViewport(rect()); // needs to be called after mPlotLayout has been created + + replot(); +} + +QCustomPlot::~QCustomPlot() +{ + clearPlottables(); + clearItems(); + + if (mPlotLayout) + { + delete mPlotLayout; + mPlotLayout = 0; + } + + mCurrentLayer = 0; + qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed + mLayers.clear(); +} + +/*! + Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement. + + This overrides the antialiasing settings for whole element groups, normally controlled with the + \a setAntialiasing function on the individual elements. If an element is neither specified in + \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on + each individual element instance is used. + + For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be + drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set + to. + + if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is + removed from there. + + \see setNotAntialiasedElements +*/ +void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements) +{ + mAntialiasedElements = antialiasedElements; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mNotAntialiasedElements |= ~mAntialiasedElements; +} + +/*! + Sets whether the specified \a antialiasedElement is forcibly drawn antialiased. + + See \ref setAntialiasedElements for details. + + \see setNotAntialiasedElement +*/ +void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled) +{ + if (!enabled && mAntialiasedElements.testFlag(antialiasedElement)) + mAntialiasedElements &= ~antialiasedElement; + else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement)) + mAntialiasedElements |= antialiasedElement; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mNotAntialiasedElements |= ~mAntialiasedElements; +} + +/*! + Sets which elements are forcibly drawn not antialiased as an \a or combination of + QCP::AntialiasedElement. + + This overrides the antialiasing settings for whole element groups, normally controlled with the + \a setAntialiasing function on the individual elements. If an element is neither specified in + \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on + each individual element instance is used. + + For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be + drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set + to. + + if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is + removed from there. + + \see setAntialiasedElements +*/ +void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements) +{ + mNotAntialiasedElements = notAntialiasedElements; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mAntialiasedElements |= ~mNotAntialiasedElements; +} + +/*! + Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased. + + See \ref setNotAntialiasedElements for details. + + \see setAntialiasedElement +*/ +void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled) +{ + if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement)) + mNotAntialiasedElements &= ~notAntialiasedElement; + else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement)) + mNotAntialiasedElements |= notAntialiasedElement; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mAntialiasedElements |= ~mNotAntialiasedElements; +} + +/*! + If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the + plottable to the legend (QCustomPlot::legend). + + \see addPlottable, addGraph, QCPLegend::addItem +*/ +void QCustomPlot::setAutoAddPlottableToLegend(bool on) +{ + mAutoAddPlottableToLegend = on; +} + +/*! + Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction + enums. There are the following types of interactions: + + Axis range manipulation is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the + respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel. + For details how to control which axes the user may drag/zoom and in what orientations, see \ref + QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes, + \ref QCPAxisRect::setRangeZoomAxes. + + Plottable selection is controlled by \ref QCP::iSelectPlottables. If \ref QCP::iSelectPlottables is + set, the user may select plottables (graphs, curves, bars,...) by clicking on them or in their + vicinity (\ref setSelectionTolerance). Whether the user can actually select a plottable can + further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the specific + plottable. To find out whether a specific plottable is selected, call + QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call + \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience + function \ref selectedGraphs. + + Item selection is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user + may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find + out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of + all currently selected items, call \ref selectedItems. + + Axis selection is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user + may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick + labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for + each axis. To retrieve a list of all axes that currently contain selected parts, call \ref + selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts(). + + Legend selection is controlled with \ref QCP::iSelectLegend. If this is set, the user may + select the legend itself or individual items by clicking on them. What parts exactly are + selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the + legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To + find out which child items are selected, call \ref QCPLegend::selectedItems. + + All other selectable elements The selection of all other selectable objects (e.g. + QCPPlotTitle, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the + user may select those objects by clicking on them. To find out which are currently selected, you + need to check their selected state explicitly. + + If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is + emitted. Each selectable object additionally emits an individual selectionChanged signal whenever + their selection state has changed, i.e. not only by user interaction. + + To allow multiple objects to be selected by holding the selection modifier (\ref + setMultiSelectModifier), set the flag \ref QCP::iMultiSelect. + + \note In addition to the selection mechanism presented here, QCustomPlot always emits + corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and + \ref plottableDoubleClick for example. + + \see setInteraction, setSelectionTolerance +*/ +void QCustomPlot::setInteractions(const QCP::Interactions &interactions) +{ + mInteractions = interactions; +} + +/*! + Sets the single \a interaction of this QCustomPlot to \a enabled. + + For details about the interaction system, see \ref setInteractions. + + \see setInteractions +*/ +void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled) +{ + if (!enabled && mInteractions.testFlag(interaction)) + mInteractions &= ~interaction; + else if (enabled && !mInteractions.testFlag(interaction)) + mInteractions |= interaction; +} + +/*! + Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or + not. + + If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a + potential selection when the minimum distance between the click position and the graph line is + smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks + directly inside the area and ignore this selection tolerance. In other words, it only has meaning + for parts of objects that are too thin to exactly hit with a click and thus need such a + tolerance. + + \see setInteractions, QCPLayerable::selectTest +*/ +void QCustomPlot::setSelectionTolerance(int pixels) +{ + mSelectionTolerance = pixels; +} + +/*! + Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes + ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves + performance during dragging. Thus it creates a more responsive user experience. As soon as the + user stops dragging, the last replot is done with normal antialiasing, to restore high image + quality. + + \see setAntialiasedElements, setNotAntialiasedElements +*/ +void QCustomPlot::setNoAntialiasingOnDrag(bool enabled) +{ + mNoAntialiasingOnDrag = enabled; +} + +/*! + Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint. + + \see setPlottingHint +*/ +void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints) +{ + mPlottingHints = hints; +} + +/*! + Sets the specified plotting \a hint to \a enabled. + + \see setPlottingHints +*/ +void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled) +{ + QCP::PlottingHints newHints = mPlottingHints; + if (!enabled) + newHints &= ~hint; + else + newHints |= hint; + + if (newHints != mPlottingHints) + setPlottingHints(newHints); +} + +/*! + Sets the keyboard modifier that will be recognized as multi-select-modifier. + + If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects + by clicking on them one after the other while holding down \a modifier. + + By default the multi-select-modifier is set to Qt::ControlModifier. + + \see setInteractions +*/ +void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier) +{ + mMultiSelectModifier = modifier; +} + +/*! + Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout + (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect. + + This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref + savePdf, etc. by temporarily changing the viewport size. +*/ +void QCustomPlot::setViewport(const QRect &rect) +{ + mViewport = rect; + if (mPlotLayout) + mPlotLayout->setOuterRect(mViewport); +} + +/*! + Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn + below all other objects in the plot. + + For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be + enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is + preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call, + consider using the overloaded version of this function. + + If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will + first be filled with that brush, before drawing the background pixmap. This can be useful for + background pixmaps with translucent areas. + + \see setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::setBackground(const QPixmap &pm) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); +} + +/*! + Sets the background brush of the viewport (see \ref setViewport). + + Before drawing everything else, the background is filled with \a brush. If a background pixmap + was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport + before the background pixmap is drawn. This can be useful for background pixmaps with translucent + areas. + + Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be + useful for exporting to image formats which support transparency, e.g. \ref savePng. + + \see setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::setBackground(const QBrush &brush) +{ + mBackgroundBrush = brush; +} + +/*! \overload + + Allows setting the background pixmap of the viewport, whether it shall be scaled and how it + shall be scaled in one call. + + \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); + mBackgroundScaled = scaled; + mBackgroundScaledMode = mode; +} + +/*! + Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is + set to true, control whether and how the aspect ratio of the original pixmap is preserved with + \ref setBackgroundScaledMode. + + Note that the scaled version of the original pixmap is buffered, so there is no performance + penalty on replots. (Except when the viewport dimensions are changed continuously.) + + \see setBackground, setBackgroundScaledMode +*/ +void QCustomPlot::setBackgroundScaled(bool scaled) +{ + mBackgroundScaled = scaled; +} + +/*! + If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this + function to define whether and how the aspect ratio of the original pixmap is preserved. + + \see setBackground, setBackgroundScaled +*/ +void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode) +{ + mBackgroundScaledMode = mode; +} + +/*! + Returns the plottable with \a index. If the index is invalid, returns 0. + + There is an overloaded version of this function with no parameter which returns the last added + plottable, see QCustomPlot::plottable() + + \see plottableCount, addPlottable +*/ +QCPAbstractPlottable *QCustomPlot::plottable(int index) +{ + if (index >= 0 && index < mPlottables.size()) + { + return mPlottables.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return 0; + } +} + +/*! \overload + + Returns the last plottable that was added with \ref addPlottable. If there are no plottables in + the plot, returns 0. + + \see plottableCount, addPlottable +*/ +QCPAbstractPlottable *QCustomPlot::plottable() +{ + if (!mPlottables.isEmpty()) + { + return mPlottables.last(); + } else + return 0; +} + +/*! + Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to + the legend (QCustomPlot::legend). QCustomPlot takes ownership of the plottable. + + Returns true on success, i.e. when \a plottable isn't already in the plot and the parent plot of + \a plottable is this QCustomPlot (the latter is controlled by what axes were passed in the + plottable's constructor). + + \see plottable, plottableCount, removePlottable, clearPlottables +*/ +bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable) +{ + if (mPlottables.contains(plottable)) + { + qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast(plottable); + return false; + } + if (plottable->parentPlot() != this) + { + qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast(plottable); + return false; + } + + mPlottables.append(plottable); + // possibly add plottable to legend: + if (mAutoAddPlottableToLegend) + plottable->addToLegend(); + // special handling for QCPGraphs to maintain the simple graph interface: + if (QCPGraph *graph = qobject_cast(plottable)) + mGraphs.append(graph); + if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor) + plottable->setLayer(currentLayer()); + return true; +} + +/*! + Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend). + + Returns true on success. + + \see addPlottable, clearPlottables +*/ +bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable) +{ + if (!mPlottables.contains(plottable)) + { + qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast(plottable); + return false; + } + + // remove plottable from legend: + plottable->removeFromLegend(); + // special handling for QCPGraphs to maintain the simple graph interface: + if (QCPGraph *graph = qobject_cast(plottable)) + mGraphs.removeOne(graph); + // remove plottable: + delete plottable; + mPlottables.removeOne(plottable); + return true; +} + +/*! \overload + + Removes the plottable by its \a index. +*/ +bool QCustomPlot::removePlottable(int index) +{ + if (index >= 0 && index < mPlottables.size()) + return removePlottable(mPlottables[index]); + else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return false; + } +} + +/*! + Removes all plottables from the plot (and the QCustomPlot::legend, if necessary). + + Returns the number of plottables removed. + + \see removePlottable +*/ +int QCustomPlot::clearPlottables() +{ + int c = mPlottables.size(); + for (int i=c-1; i >= 0; --i) + removePlottable(mPlottables[i]); + return c; +} + +/*! + Returns the number of currently existing plottables in the plot + + \see plottable, addPlottable +*/ +int QCustomPlot::plottableCount() const +{ + return mPlottables.size(); +} + +/*! + Returns a list of the selected plottables. If no plottables are currently selected, the list is empty. + + There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs. + + \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected +*/ +QList QCustomPlot::selectedPlottables() const +{ + QList result; + foreach (QCPAbstractPlottable *plottable, mPlottables) + { + if (plottable->selected()) + result.append(plottable); + } + return result; +} + +/*! + Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines + (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple + plottables come into consideration, the one closest to \a pos is returned. + + If \a onlySelectable is true, only plottables that are selectable + (QCPAbstractPlottable::setSelectable) are considered. + + If there is no plottable at \a pos, the return value is 0. + + \see itemAt, layoutElementAt +*/ +QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const +{ + QCPAbstractPlottable *resultPlottable = 0; + double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value + + foreach (QCPAbstractPlottable *plottable, mPlottables) + { + if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable + continue; + if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes + { + double currentDistance = plottable->selectTest(pos, false); + if (currentDistance >= 0 && currentDistance < resultDistance) + { + resultPlottable = plottable; + resultDistance = currentDistance; + } + } + } + + return resultPlottable; +} + +/*! + Returns whether this QCustomPlot instance contains the \a plottable. + + \see addPlottable +*/ +bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const +{ + return mPlottables.contains(plottable); +} + +/*! + Returns the graph with \a index. If the index is invalid, returns 0. + + There is an overloaded version of this function with no parameter which returns the last created + graph, see QCustomPlot::graph() + + \see graphCount, addGraph +*/ +QCPGraph *QCustomPlot::graph(int index) const +{ + if (index >= 0 && index < mGraphs.size()) + { + return mGraphs.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return 0; + } +} + +/*! \overload + + Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot, + returns 0. + + \see graphCount, addGraph +*/ +QCPGraph *QCustomPlot::graph() const +{ + if (!mGraphs.isEmpty()) + { + return mGraphs.last(); + } else + return 0; +} + +/*! + Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the + bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a + keyAxis and \a valueAxis must reside in this QCustomPlot. + + \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically + "y") for the graph. + + Returns a pointer to the newly created graph, or 0 if adding the graph failed. + + \see graph, graphCount, removeGraph, clearGraphs +*/ +QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) +{ + if (!keyAxis) keyAxis = xAxis; + if (!valueAxis) valueAxis = yAxis; + if (!keyAxis || !valueAxis) + { + qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)"; + return 0; + } + if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) + { + qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent"; + return 0; + } + + QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis); + if (addPlottable(newGraph)) + { + newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size())); + return newGraph; + } else + { + delete newGraph; + return 0; + } +} + +/*! + Removes the specified \a graph from the plot and, if necessary, from the QCustomPlot::legend. If + any other graphs in the plot have a channel fill set towards the removed graph, the channel fill + property of those graphs is reset to zero (no channel fill). + + Returns true on success. + + \see clearGraphs +*/ +bool QCustomPlot::removeGraph(QCPGraph *graph) +{ + return removePlottable(graph); +} + +/*! \overload + + Removes the graph by its \a index. +*/ +bool QCustomPlot::removeGraph(int index) +{ + if (index >= 0 && index < mGraphs.size()) + return removeGraph(mGraphs[index]); + else + return false; +} + +/*! + Removes all graphs from the plot (and the QCustomPlot::legend, if necessary). + + Returns the number of graphs removed. + + \see removeGraph +*/ +int QCustomPlot::clearGraphs() +{ + int c = mGraphs.size(); + for (int i=c-1; i >= 0; --i) + removeGraph(mGraphs[i]); + return c; +} + +/*! + Returns the number of currently existing graphs in the plot + + \see graph, addGraph +*/ +int QCustomPlot::graphCount() const +{ + return mGraphs.size(); +} + +/*! + Returns a list of the selected graphs. If no graphs are currently selected, the list is empty. + + If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars, + etc., use \ref selectedPlottables. + + \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected +*/ +QList QCustomPlot::selectedGraphs() const +{ + QList result; + foreach (QCPGraph *graph, mGraphs) + { + if (graph->selected()) + result.append(graph); + } + return result; +} + +/*! + Returns the item with \a index. If the index is invalid, returns 0. + + There is an overloaded version of this function with no parameter which returns the last added + item, see QCustomPlot::item() + + \see itemCount, addItem +*/ +QCPAbstractItem *QCustomPlot::item(int index) const +{ + if (index >= 0 && index < mItems.size()) + { + return mItems.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return 0; + } +} + +/*! \overload + + Returns the last item, that was added with \ref addItem. If there are no items in the plot, + returns 0. + + \see itemCount, addItem +*/ +QCPAbstractItem *QCustomPlot::item() const +{ + if (!mItems.isEmpty()) + { + return mItems.last(); + } else + return 0; +} + +/*! + Adds the specified item to the plot. QCustomPlot takes ownership of the item. + + Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a + item is this QCustomPlot. + + \see item, itemCount, removeItem, clearItems +*/ +bool QCustomPlot::addItem(QCPAbstractItem *item) +{ + if (!mItems.contains(item) && item->parentPlot() == this) + { + mItems.append(item); + return true; + } else + { + qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast(item); + return false; + } +} + +/*! + Removes the specified item from the plot. + + Returns true on success. + + \see addItem, clearItems +*/ +bool QCustomPlot::removeItem(QCPAbstractItem *item) +{ + if (mItems.contains(item)) + { + delete item; + mItems.removeOne(item); + return true; + } else + { + qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast(item); + return false; + } +} + +/*! \overload + + Removes the item by its \a index. +*/ +bool QCustomPlot::removeItem(int index) +{ + if (index >= 0 && index < mItems.size()) + return removeItem(mItems[index]); + else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return false; + } +} + +/*! + Removes all items from the plot. + + Returns the number of items removed. + + \see removeItem +*/ +int QCustomPlot::clearItems() +{ + int c = mItems.size(); + for (int i=c-1; i >= 0; --i) + removeItem(mItems[i]); + return c; +} + +/*! + Returns the number of currently existing items in the plot + + \see item, addItem +*/ +int QCustomPlot::itemCount() const +{ + return mItems.size(); +} + +/*! + Returns a list of the selected items. If no items are currently selected, the list is empty. + + \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected +*/ +QList QCustomPlot::selectedItems() const +{ + QList result; + foreach (QCPAbstractItem *item, mItems) + { + if (item->selected()) + result.append(item); + } + return result; +} + +/*! + Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref + QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref + setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is + returned. + + If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are + considered. + + If there is no item at \a pos, the return value is 0. + + \see plottableAt, layoutElementAt +*/ +QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const +{ + QCPAbstractItem *resultItem = 0; + double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value + + foreach (QCPAbstractItem *item, mItems) + { + if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable + continue; + if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it + { + double currentDistance = item->selectTest(pos, false); + if (currentDistance >= 0 && currentDistance < resultDistance) + { + resultItem = item; + resultDistance = currentDistance; + } + } + } + + return resultItem; +} + +/*! + Returns whether this QCustomPlot contains the \a item. + + \see addItem +*/ +bool QCustomPlot::hasItem(QCPAbstractItem *item) const +{ + return mItems.contains(item); +} + +/*! + Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is + returned. + + Layer names are case-sensitive. + + \see addLayer, moveLayer, removeLayer +*/ +QCPLayer *QCustomPlot::layer(const QString &name) const +{ + foreach (QCPLayer *layer, mLayers) + { + if (layer->name() == name) + return layer; + } + return 0; +} + +/*! \overload + + Returns the layer by \a index. If the index is invalid, 0 is returned. + + \see addLayer, moveLayer, removeLayer +*/ +QCPLayer *QCustomPlot::layer(int index) const +{ + if (index >= 0 && index < mLayers.size()) + { + return mLayers.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return 0; + } +} + +/*! + Returns the layer that is set as current layer (see \ref setCurrentLayer). +*/ +QCPLayer *QCustomPlot::currentLayer() const +{ + return mCurrentLayer; +} + +/*! + Sets the layer with the specified \a name to be the current layer. All layerables (\ref + QCPLayerable), e.g. plottables and items, are created on the current layer. + + Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot. + + Layer names are case-sensitive. + + \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer +*/ +bool QCustomPlot::setCurrentLayer(const QString &name) +{ + if (QCPLayer *newCurrentLayer = layer(name)) + { + return setCurrentLayer(newCurrentLayer); + } else + { + qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name; + return false; + } +} + +/*! \overload + + Sets the provided \a layer to be the current layer. + + Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot. + + \see addLayer, moveLayer, removeLayer +*/ +bool QCustomPlot::setCurrentLayer(QCPLayer *layer) +{ + if (!mLayers.contains(layer)) + { + qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast(layer); + return false; + } + + mCurrentLayer = layer; + return true; +} + +/*! + Returns the number of currently existing layers in the plot + + \see layer, addLayer +*/ +int QCustomPlot::layerCount() const +{ + return mLayers.size(); +} + +/*! + Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which + must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer. + + Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a + valid layer inside this QCustomPlot. + + If \a otherLayer is 0, the highest layer in the QCustomPlot will be used. + + For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer. + + \see layer, moveLayer, removeLayer +*/ +bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode) +{ + if (!otherLayer) + otherLayer = mLayers.last(); + if (!mLayers.contains(otherLayer)) + { + qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast(otherLayer); + return false; + } + if (layer(name)) + { + qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name; + return false; + } + + QCPLayer *newLayer = new QCPLayer(this, name); + mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer); + updateLayerIndices(); + return true; +} + +/*! + Removes the specified \a layer and returns true on success. + + All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below + \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both + cases, the total rendering order of all layerables in the QCustomPlot is preserved. + + If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom + layer) becomes the new current layer. + + It is not possible to remove the last layer of the plot. + + \see layer, addLayer, moveLayer +*/ +bool QCustomPlot::removeLayer(QCPLayer *layer) +{ + if (!mLayers.contains(layer)) + { + qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast(layer); + return false; + } + if (mLayers.size() < 2) + { + qDebug() << Q_FUNC_INFO << "can't remove last layer"; + return false; + } + + // append all children of this layer to layer below (if this is lowest layer, prepend to layer above) + int removedIndex = layer->index(); + bool isFirstLayer = removedIndex==0; + QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1); + QList children = layer->children(); + if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same) + { + for (int i=children.size()-1; i>=0; --i) + children.at(i)->moveToLayer(targetLayer, true); + } else // append normally + { + for (int i=0; imoveToLayer(targetLayer, false); + } + // if removed layer is current layer, change current layer to layer below/above: + if (layer == mCurrentLayer) + setCurrentLayer(targetLayer); + // remove layer: + delete layer; + mLayers.removeOne(layer); + updateLayerIndices(); + return true; +} + +/*! + Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or + below is controlled with \a insertMode. + + Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the + QCustomPlot. + + \see layer, addLayer, moveLayer +*/ +bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode) +{ + if (!mLayers.contains(layer)) + { + qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast(layer); + return false; + } + if (!mLayers.contains(otherLayer)) + { + qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast(otherLayer); + return false; + } + + if (layer->index() > otherLayer->index()) + mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0)); + else if (layer->index() < otherLayer->index()) + mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1)); + + updateLayerIndices(); + return true; +} + +/*! + Returns the number of axis rects in the plot. + + All axis rects can be accessed via QCustomPlot::axisRect(). + + Initially, only one axis rect exists in the plot. + + \see axisRect, axisRects +*/ +int QCustomPlot::axisRectCount() const +{ + return axisRects().size(); +} + +/*! + Returns the axis rect with \a index. + + Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were + added, all of them may be accessed with this function in a linear fashion (even when they are + nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout). + + \see axisRectCount, axisRects +*/ +QCPAxisRect *QCustomPlot::axisRect(int index) const +{ + const QList rectList = axisRects(); + if (index >= 0 && index < rectList.size()) + { + return rectList.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index; + return 0; + } +} + +/*! + Returns all axis rects in the plot. + + \see axisRectCount, axisRect +*/ +QList QCustomPlot::axisRects() const +{ + QList result; + QStack elementStack; + if (mPlotLayout) + elementStack.push(mPlotLayout); + + while (!elementStack.isEmpty()) + { + foreach (QCPLayoutElement *element, elementStack.pop()->elements(false)) + { + if (element) + { + elementStack.push(element); + if (QCPAxisRect *ar = qobject_cast(element)) + result.append(ar); + } + } + } + + return result; +} + +/*! + Returns the layout element at pixel position \a pos. If there is no element at that position, + returns 0. + + Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on + any of its parent elements is set to false, it will not be considered. + + \see itemAt, plottableAt +*/ +QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const +{ + QCPLayoutElement *currentElement = mPlotLayout; + bool searchSubElements = true; + while (searchSubElements && currentElement) + { + searchSubElements = false; + foreach (QCPLayoutElement *subElement, currentElement->elements(false)) + { + if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0) + { + currentElement = subElement; + searchSubElements = true; + break; + } + } + } + return currentElement; +} + +/*! + Returns the axes that currently have selected parts, i.e. whose selection state is not \ref + QCPAxis::spNone. + + \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts, + QCPAxis::setSelectableParts +*/ +QList QCustomPlot::selectedAxes() const +{ + QList result, allAxes; + foreach (QCPAxisRect *rect, axisRects()) + allAxes << rect->axes(); + + foreach (QCPAxis *axis, allAxes) + { + if (axis->selectedParts() != QCPAxis::spNone) + result.append(axis); + } + + return result; +} + +/*! + Returns the legends that currently have selected parts, i.e. whose selection state is not \ref + QCPLegend::spNone. + + \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts, + QCPLegend::setSelectableParts, QCPLegend::selectedItems +*/ +QList QCustomPlot::selectedLegends() const +{ + QList result; + + QStack elementStack; + if (mPlotLayout) + elementStack.push(mPlotLayout); + + while (!elementStack.isEmpty()) + { + foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false)) + { + if (subElement) + { + elementStack.push(subElement); + if (QCPLegend *leg = qobject_cast(subElement)) + { + if (leg->selectedParts() != QCPLegend::spNone) + result.append(leg); + } + } + } + } + + return result; +} + +/*! + Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot. + + Since calling this function is not a user interaction, this does not emit the \ref + selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the + objects were previously selected. + + \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends +*/ +void QCustomPlot::deselectAll() +{ + foreach (QCPLayer *layer, mLayers) + { + foreach (QCPLayerable *layerable, layer->children()) + layerable->deselectEvent(0); + } +} + +/*! + Causes a complete replot into the internal buffer. Finally, update() is called, to redraw the + buffer on the QCustomPlot widget surface. This is the method that must be called to make changes, + for example on the axis ranges or data points of graphs, visible. + + Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the + QCustomPlot widget and user interactions (object selection and range dragging/zooming). + + Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref + afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two + signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite + recursion. +*/ +void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority) +{ + if (mReplotting) // incase signals loop back to replot slot + return; + mReplotting = true; + emit beforeReplot(); + + mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); + QCPPainter painter; + painter.begin(&mPaintBuffer); + if (painter.isActive()) + { + painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem + if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) + painter.fillRect(mViewport, mBackgroundBrush); + draw(&painter); + painter.end(); + if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate) + repaint(); + else + update(); + } else // might happen if QCustomPlot has width or height zero + qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero."; + + emit afterReplot(); + mReplotting = false; +} + +/*! + Rescales the axes such that all plottables (like graphs) in the plot are fully visible. + + if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true + (QCPLayerable::setVisible), will be used to rescale the axes. + + \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale +*/ +void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables) +{ + QList allAxes; + foreach (QCPAxisRect *rect, axisRects()) + allAxes << rect->axes(); + + foreach (QCPAxis *axis, allAxes) + axis->rescale(onlyVisiblePlottables); +} + +/*! + Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale + of texts and lines will be derived from the specified \a width and \a height. This means, the + output will look like the normal on-screen output of a QCustomPlot widget with the corresponding + pixel width and height. If either \a width or \a height is zero, the exported image will have the + same dimensions as the QCustomPlot widget currently has. + + \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens + are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what + zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see the QPainter + and QPen documentation. + + The objects of the plot will appear in the current selection state. If you don't want any + selected objects to be painted in their selected look, deselect everything with \ref deselectAll + before calling this function. + + Returns true on success. + + \warning + \li If you plan on editing the exported PDF file with a vector graphics editor like + Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines + (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks). + \li If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting + PDF file. + + \note On Android systems, this method does nothing and issues an according qDebug warning + message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set. + + \see savePng, saveBmp, saveJpg, saveRastered +*/ +bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle) +{ + bool success = false; +#ifdef QT_NO_PRINTER + Q_UNUSED(fileName) + Q_UNUSED(noCosmeticPen) + Q_UNUSED(width) + Q_UNUSED(height) + Q_UNUSED(pdfCreator) + Q_UNUSED(pdfTitle) + qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created."; +#else + int newWidth, newHeight; + if (width == 0 || height == 0) + { + newWidth = this->width(); + newHeight = this->height(); + } else + { + newWidth = width; + newHeight = height; + } + + QPrinter printer(QPrinter::ScreenResolution); + printer.setOutputFileName(fileName); + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setColorMode(QPrinter::Color); + printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator); + printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle); + QRect oldViewport = viewport(); + setViewport(QRect(0, 0, newWidth, newHeight)); +#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0) + printer.setFullPage(true); + printer.setPaperSize(viewport().size(), QPrinter::DevicePixel); +#else + QPageLayout pageLayout; + pageLayout.setMode(QPageLayout::FullPageMode); + pageLayout.setOrientation(QPageLayout::Portrait); + pageLayout.setMargins(QMarginsF(0, 0, 0, 0)); + pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch)); + printer.setPageLayout(pageLayout); +#endif + QCPPainter printpainter; + if (printpainter.begin(&printer)) + { + printpainter.setMode(QCPPainter::pmVectorized); + printpainter.setMode(QCPPainter::pmNoCaching); + printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen); + printpainter.setWindow(mViewport); + if (mBackgroundBrush.style() != Qt::NoBrush && + mBackgroundBrush.color() != Qt::white && + mBackgroundBrush.color() != Qt::transparent && + mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent + printpainter.fillRect(viewport(), mBackgroundBrush); + draw(&printpainter); + printpainter.end(); + success = true; + } + setViewport(oldViewport); +#endif // QT_NO_PRINTER + return success; +} + +/*! + Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width + and \a height in pixels. If either \a width or \a height is zero, the exported image will have + the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not + scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter. + + For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an + image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths, + texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full + 200*200 pixel resolution. + + If you use a high scaling factor, it is recommended to enable antialiasing for all elements via + temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows + QCustomPlot to place objects with sub-pixel accuracy. + + \warning If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + The objects of the plot will appear in the current selection state. If you don't want any selected + objects to be painted in their selected look, deselect everything with \ref deselectAll before calling + this function. + + If you want the PNG to have a transparent background, call \ref setBackground(const QBrush + &brush) with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving. + + PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or + -1 to use the default setting. + + Returns true on success. If this function fails, most likely the PNG format isn't supported by + the system, see Qt docs about QImageWriter::supportedImageFormats(). + + \see savePdf, saveBmp, saveJpg, saveRastered +*/ +bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality) +{ + return saveRastered(fileName, width, height, scale, "PNG", quality); +} + +/*! + Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width + and \a height in pixels. If either \a width or \a height is zero, the exported image will have + the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not + scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter. + + For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an + image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths, + texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full + 200*200 pixel resolution. + + If you use a high scaling factor, it is recommended to enable antialiasing for all elements via + temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows + QCustomPlot to place objects with sub-pixel accuracy. + + \warning If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + The objects of the plot will appear in the current selection state. If you don't want any selected + objects to be painted in their selected look, deselect everything with \ref deselectAll before calling + this function. + + JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or + -1 to use the default setting. + + Returns true on success. If this function fails, most likely the JPG format isn't supported by + the system, see Qt docs about QImageWriter::supportedImageFormats(). + + \see savePdf, savePng, saveBmp, saveRastered +*/ +bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality) +{ + return saveRastered(fileName, width, height, scale, "JPG", quality); +} + +/*! + Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width + and \a height in pixels. If either \a width or \a height is zero, the exported image will have + the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not + scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter. + + For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an + image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths, + texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full + 200*200 pixel resolution. + + If you use a high scaling factor, it is recommended to enable antialiasing for all elements via + temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows + QCustomPlot to place objects with sub-pixel accuracy. + + \warning If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + The objects of the plot will appear in the current selection state. If you don't want any selected + objects to be painted in their selected look, deselect everything with \ref deselectAll before calling + this function. + + Returns true on success. If this function fails, most likely the BMP format isn't supported by + the system, see Qt docs about QImageWriter::supportedImageFormats(). + + \see savePdf, savePng, saveJpg, saveRastered +*/ +bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale) +{ + return saveRastered(fileName, width, height, scale, "BMP"); +} + +/*! \internal + + Returns a minimum size hint that corresponds to the minimum size of the top level layout + (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum + size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot. + This is especially important, when placed in a QLayout where other components try to take in as + much space as possible (e.g. QMdiArea). +*/ +QSize QCustomPlot::minimumSizeHint() const +{ + return mPlotLayout->minimumSizeHint(); +} + +/*! \internal + + Returns a size hint that is the same as \ref minimumSizeHint. + +*/ +QSize QCustomPlot::sizeHint() const +{ + return mPlotLayout->minimumSizeHint(); +} + +/*! \internal + + Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but + draws the internal buffer on the widget surface. +*/ +void QCustomPlot::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter painter(this); + painter.drawPixmap(0, 0, mPaintBuffer); +} + +/*! \internal + + Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to + the new size. The viewport (which becomes the outer rect of mPlotLayout) is resized + appropriately. Finally a \ref replot is performed. +*/ +void QCustomPlot::resizeEvent(QResizeEvent *event) +{ + // resize and repaint the buffer: + mPaintBuffer = QPixmap(event->size()); + setViewport(rect()); + replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts +} + +/*! \internal + + Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then emits + the specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref + axisDoubleClick, etc.). Finally determines the affected layout element and forwards the event to + it. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) +{ + emit mouseDoubleClick(event); + + QVariant details; + QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); + + // emit specialized object double click signals: + if (QCPAbstractPlottable *ap = qobject_cast(clickedLayerable)) + emit plottableDoubleClick(ap, event); + else if (QCPAxis *ax = qobject_cast(clickedLayerable)) + emit axisDoubleClick(ax, details.value(), event); + else if (QCPAbstractItem *ai = qobject_cast(clickedLayerable)) + emit itemDoubleClick(ai, event); + else if (QCPLegend *lg = qobject_cast(clickedLayerable)) + emit legendDoubleClick(lg, 0, event); + else if (QCPAbstractLegendItem *li = qobject_cast(clickedLayerable)) + emit legendDoubleClick(li->parentLegend(), li, event); + else if (QCPPlotTitle *pt = qobject_cast(clickedLayerable)) + emit titleDoubleClick(event, pt); + + // call double click event of affected layout element: + if (QCPLayoutElement *el = layoutElementAt(event->pos())) + el->mouseDoubleClickEvent(event); + + // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case): + if (mMouseEventElement) + { + mMouseEventElement->mouseReleaseEvent(event); + mMouseEventElement = 0; + } + + //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want. +} + +/*! \internal + + Event handler for when a mouse button is pressed. Emits the mousePress signal. Then determines + the affected layout element and forwards the event to it. + + \see mouseMoveEvent, mouseReleaseEvent +*/ +void QCustomPlot::mousePressEvent(QMouseEvent *event) +{ + emit mousePress(event); + mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release) + + // call event of affected layout element: + mMouseEventElement = layoutElementAt(event->pos()); + if (mMouseEventElement) + mMouseEventElement->mousePressEvent(event); + + QWidget::mousePressEvent(event); +} + +/*! \internal + + Event handler for when the cursor is moved. Emits the \ref mouseMove signal. + + If a layout element has mouse capture focus (a mousePressEvent happened on top of the layout + element before), the mouseMoveEvent is forwarded to that element. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCustomPlot::mouseMoveEvent(QMouseEvent *event) +{ + emit mouseMove(event); + + // call event of affected layout element: + if (mMouseEventElement) + mMouseEventElement->mouseMoveEvent(event); + + QWidget::mouseMoveEvent(event); +} + +/*! \internal + + Event handler for when a mouse button is released. Emits the \ref mouseRelease signal. + + If the mouse was moved less than a certain threshold in any direction since the \ref + mousePressEvent, it is considered a click which causes the selection mechanism (if activated via + \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse + click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.) + + If a layout element has mouse capture focus (a \ref mousePressEvent happened on top of the layout + element before), the \ref mouseReleaseEvent is forwarded to that element. + + \see mousePressEvent, mouseMoveEvent +*/ +void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) +{ + emit mouseRelease(event); + bool doReplot = false; + + if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation + { + if (event->button() == Qt::LeftButton) + { + // handle selection mechanism: + QVariant details; + QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details); + bool selectionStateChanged = false; + bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier); + // deselect all other layerables if not additive selection: + if (!additive) + { + foreach (QCPLayer *layer, mLayers) + { + foreach (QCPLayerable *layerable, layer->children()) + { + if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory())) + { + bool selChanged = false; + layerable->deselectEvent(&selChanged); + selectionStateChanged |= selChanged; + } + } + } + } + if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory())) + { + // a layerable was actually clicked, call its selectEvent: + bool selChanged = false; + clickedLayerable->selectEvent(event, additive, details, &selChanged); + selectionStateChanged |= selChanged; + } + if (selectionStateChanged) + { + doReplot = true; + emit selectionChangedByUser(); + } + } + + // emit specialized object click signals: + QVariant details; + QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false + if (QCPAbstractPlottable *ap = qobject_cast(clickedLayerable)) + emit plottableClick(ap, event); + else if (QCPAxis *ax = qobject_cast(clickedLayerable)) + emit axisClick(ax, details.value(), event); + else if (QCPAbstractItem *ai = qobject_cast(clickedLayerable)) + emit itemClick(ai, event); + else if (QCPLegend *lg = qobject_cast(clickedLayerable)) + emit legendClick(lg, 0, event); + else if (QCPAbstractLegendItem *li = qobject_cast(clickedLayerable)) + emit legendClick(li->parentLegend(), li, event); + else if (QCPPlotTitle *pt = qobject_cast(clickedLayerable)) + emit titleClick(event, pt); + } + + // call event of affected layout element: + if (mMouseEventElement) + { + mMouseEventElement->mouseReleaseEvent(event); + mMouseEventElement = 0; + } + + if (doReplot || noAntialiasingOnDrag()) + replot(); + + QWidget::mouseReleaseEvent(event); +} + +/*! \internal + + Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then + determines the affected layout element and forwards the event to it. + +*/ +void QCustomPlot::wheelEvent(QWheelEvent *event) +{ + emit mouseWheel(event); + + // call event of affected layout element: + if (QCPLayoutElement *el = layoutElementAt(event->pos())) + el->wheelEvent(event); + + QWidget::wheelEvent(event); +} + +/*! \internal + + This is the main draw function. It draws the entire plot, including background pixmap, with the + specified \a painter. Note that it does not fill the background with the background brush (as the + user may specify with \ref setBackground(const QBrush &brush)), this is up to the respective + functions calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter). +*/ +void QCustomPlot::draw(QCPPainter *painter) +{ + // run through layout phases: + mPlotLayout->update(QCPLayoutElement::upPreparation); + mPlotLayout->update(QCPLayoutElement::upMargins); + mPlotLayout->update(QCPLayoutElement::upLayout); + + // draw viewport background pixmap: + drawBackground(painter); + + // draw all layered objects (grid, axes, plottables, items, legend,...): + foreach (QCPLayer *layer, mLayers) + { + foreach (QCPLayerable *child, layer->children()) + { + if (child->realVisibility()) + { + painter->save(); + painter->setClipRect(child->clipRect().translated(0, -1)); + child->applyDefaultAntialiasingHint(painter); + child->draw(painter); + painter->restore(); + } + } + } + + /* Debug code to draw all layout element rects + foreach (QCPLayoutElement* el, findChildren()) + { + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine)); + painter->drawRect(el->rect()); + painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine)); + painter->drawRect(el->outerRect()); + } + */ +} + +/*! \internal + + Draws the viewport background pixmap of the plot. + + If a pixmap was provided via \ref setBackground, this function buffers the scaled version + depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside + the viewport with the provided \a painter. The scaled version is buffered in + mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when + the axis rect has changed in a way that requires a rescale of the background pixmap (this is + dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was + set. + + Note that this function does not draw a fill with the background brush (\ref setBackground(const + QBrush &brush)) beneath the pixmap. + + \see setBackground, setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::drawBackground(QCPPainter *painter) +{ + // Note: background color is handled in individual replot/save functions + + // draw background pixmap (on top of fill, if brush specified): + if (!mBackgroundPixmap.isNull()) + { + if (mBackgroundScaled) + { + // check whether mScaledBackground needs to be updated: + QSize scaledSize(mBackgroundPixmap.size()); + scaledSize.scale(mViewport.size(), mBackgroundScaledMode); + if (mScaledBackgroundPixmap.size() != scaledSize) + mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation); + painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect()); + } else + { + painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height())); + } + } +} + + +/*! \internal + + This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot + so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly. +*/ +void QCustomPlot::axisRemoved(QCPAxis *axis) +{ + if (xAxis == axis) + xAxis = 0; + if (xAxis2 == axis) + xAxis2 = 0; + if (yAxis == axis) + yAxis = 0; + if (yAxis2 == axis) + yAxis2 = 0; + + // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers +} + +/*! \internal + + This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so + it may clear its QCustomPlot::legend member accordingly. +*/ +void QCustomPlot::legendRemoved(QCPLegend *legend) +{ + if (this->legend == legend) + this->legend = 0; +} + +/*! \internal + + Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called + after every operation that changes the layer indices, like layer removal, layer creation, layer + moving. +*/ +void QCustomPlot::updateLayerIndices() const +{ + for (int i=0; imIndex = i; +} + +/*! \internal + + Returns the layerable at pixel position \a pos. If \a onlySelectable is set to true, only those + layerables that are selectable will be considered. (Layerable subclasses communicate their + selectability via the QCPLayerable::selectTest method, by returning -1.) + + \a selectionDetails is an output parameter that contains selection specifics of the affected + layerable. This is useful if the respective layerable shall be given a subsequent + QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains + information about which part of the layerable was hit, in multi-part layerables (e.g. + QCPAxis::SelectablePart). +*/ +QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const +{ + for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex) + { + const QList layerables = mLayers.at(layerIndex)->children(); + double minimumDistance = selectionTolerance()*1.1; + QCPLayerable *minimumDistanceLayerable = 0; + for (int i=layerables.size()-1; i>=0; --i) + { + if (!layerables.at(i)->realVisibility()) + continue; + QVariant details; + double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details); + if (dist >= 0 && dist < minimumDistance) + { + minimumDistance = dist; + minimumDistanceLayerable = layerables.at(i); + if (selectionDetails) *selectionDetails = details; + } + } + if (minimumDistance < selectionTolerance()) + return minimumDistanceLayerable; + } + return 0; +} + +/*! + Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is + sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead + to a full resolution file with width 200.) If the \a format supports compression, \a quality may + be between 0 and 100 to control it. + + Returns true on success. If this function fails, most likely the given \a format isn't supported + by the system, see Qt docs about QImageWriter::supportedImageFormats(). + + \see saveBmp, saveJpg, savePng, savePdf +*/ +bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality) +{ + QPixmap buffer = toPixmap(width, height, scale); + if (!buffer.isNull()) + return buffer.save(fileName, format, quality); + else + return false; +} + +/*! + Renders the plot to a pixmap and returns it. + + The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and + scale 2.0 lead to a full resolution pixmap with width 200.) + + \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf +*/ +QPixmap QCustomPlot::toPixmap(int width, int height, double scale) +{ + // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too. + int newWidth, newHeight; + if (width == 0 || height == 0) + { + newWidth = this->width(); + newHeight = this->height(); + } else + { + newWidth = width; + newHeight = height; + } + int scaledWidth = qRound(scale*newWidth); + int scaledHeight = qRound(scale*newHeight); + + QPixmap result(scaledWidth, scaledHeight); + result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later + QCPPainter painter; + painter.begin(&result); + if (painter.isActive()) + { + QRect oldViewport = viewport(); + setViewport(QRect(0, 0, newWidth, newHeight)); + painter.setMode(QCPPainter::pmNoCaching); + if (!qFuzzyCompare(scale, 1.0)) + { + if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales + painter.setMode(QCPPainter::pmNonCosmetic); + painter.scale(scale, scale); + } + if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill + painter.fillRect(mViewport, mBackgroundBrush); + draw(&painter); + setViewport(oldViewport); + painter.end(); + } else // might happen if pixmap has width or height zero + { + qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap"; + return QPixmap(); + } + return result; +} + +/*! + Renders the plot using the passed \a painter. + + The plot is sized to \a width and \a height in pixels. If the \a painter's scale is not 1.0, the resulting plot will + appear scaled accordingly. + + \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter + on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with + the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter. + + \see toPixmap +*/ +void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) +{ + // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too. + int newWidth, newHeight; + if (width == 0 || height == 0) + { + newWidth = this->width(); + newHeight = this->height(); + } else + { + newWidth = width; + newHeight = height; + } + + if (painter->isActive()) + { + QRect oldViewport = viewport(); + setViewport(QRect(0, 0, newWidth, newHeight)); + painter->setMode(QCPPainter::pmNoCaching); + if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here + painter->fillRect(mViewport, mBackgroundBrush); + draw(painter); + setViewport(oldViewport); + } else + qDebug() << Q_FUNC_INFO << "Passed painter is not active"; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorGradient +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorGradient + \brief Defines a color gradient for use with e.g. \ref QCPColorMap + + This class describes a color gradient which can be used to encode data with color. For example, + QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which + take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color) + with a \a position from 0 to 1. In between these defined color positions, the + color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation. + + Alternatively, load one of the preset color gradients shown in the image below, with \ref + loadPreset, or by directly specifying the preset in the constructor. + + \image html QCPColorGradient.png + + The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly + converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref + GradientPreset to all the \a setGradient methods, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient + + The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the + color gradient shall be applied periodically (wrapping around) to data values that lie outside + the data range specified on the plottable instance can be controlled with \ref setPeriodic. +*/ + +/*! + Constructs a new QCPColorGradient initialized with the colors and color interpolation according + to \a preset. + + The color level count is initialized to 350. +*/ +QCPColorGradient::QCPColorGradient(GradientPreset preset) : + mLevelCount(350), + mColorInterpolation(ciRGB), + mPeriodic(false), + mColorBufferInvalidated(true) +{ + mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount); + loadPreset(preset); +} + +/* undocumented operator */ +bool QCPColorGradient::operator==(const QCPColorGradient &other) const +{ + return ((other.mLevelCount == this->mLevelCount) && + (other.mColorInterpolation == this->mColorInterpolation) && + (other.mPeriodic == this->mPeriodic) && + (other.mColorStops == this->mColorStops)); +} + +/*! + Sets the number of discretization levels of the color gradient to \a n. The default is 350 which + is typically enough to create a smooth appearance. + + \image html QCPColorGradient-levelcount.png +*/ +void QCPColorGradient::setLevelCount(int n) +{ + if (n < 2) + { + qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n; + n = 2; + } + if (n != mLevelCount) + { + mLevelCount = n; + mColorBufferInvalidated = true; + } +} + +/*! + Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the + colors are the values of the passed QMap \a colorStops. In between these color stops, the color + is interpolated according to \ref setColorInterpolation. + + A more convenient way to create a custom gradient may be to clear all color stops with \ref + clearColorStops and then adding them one by one with \ref setColorStopAt. + + \see clearColorStops +*/ +void QCPColorGradient::setColorStops(const QMap &colorStops) +{ + mColorStops = colorStops; + mColorBufferInvalidated = true; +} + +/*! + Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between + these color stops, the color is interpolated according to \ref setColorInterpolation. + + \see setColorStops, clearColorStops +*/ +void QCPColorGradient::setColorStopAt(double position, const QColor &color) +{ + mColorStops.insert(position, color); + mColorBufferInvalidated = true; +} + +/*! + Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be + interpolated linearly in RGB or in HSV color space. + + For example, a sweep in RGB space from red to green will have a muddy brown intermediate color, + whereas in HSV space the intermediate color is yellow. +*/ +void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation) +{ + if (interpolation != mColorInterpolation) + { + mColorInterpolation = interpolation; + mColorBufferInvalidated = true; + } +} + +/*! + Sets whether data points that are outside the configured data range (e.g. \ref + QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether + they all have the same color, corresponding to the respective gradient boundary color. + + \image html QCPColorGradient-periodic.png + + As shown in the image above, gradients that have the same start and end color are especially + suitable for a periodic gradient mapping, since they produce smooth color transitions throughout + the color map. A preset that has this property is \ref gpHues. + + In practice, using periodic color gradients makes sense when the data corresponds to a periodic + dimension, such as an angle or a phase. If this is not the case, the color encoding might become + ambiguous, because multiple different data values are shown as the same color. +*/ +void QCPColorGradient::setPeriodic(bool enabled) +{ + mPeriodic = enabled; +} + +/*! + This method is used to quickly convert a \a data array to colors. The colors will be output in + the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this + function. The data range that shall be used for mapping the data value to the gradient is passed + in \a range. \a logarithmic indicates whether the data values shall be mapped to colors + logarithmically. + + if \a data actually contains 2D-data linearized via [row*columnCount + column], you can + set \a dataIndexFactor to columnCount to convert a column instead of a row of the data + array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data + is addressed data[i*dataIndexFactor]. +*/ +void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic) +{ + // If you change something here, make sure to also adapt ::color() + if (!data) + { + qDebug() << Q_FUNC_INFO << "null pointer given as data"; + return; + } + if (!scanLine) + { + qDebug() << Q_FUNC_INFO << "null pointer given as scanLine"; + return; + } + if (mColorBufferInvalidated) + updateColorBuffer(); + + if (!logarithmic) + { + const double posToIndexFactor = (mLevelCount-1)/range.size(); + if (mPeriodic) + { + for (int i=0; i= mLevelCount) + index = mLevelCount-1; + scanLine[i] = mColorBuffer.at(index); + } + } + } else // logarithmic == true + { + if (mPeriodic) + { + for (int i=0; i= mLevelCount) + index = mLevelCount-1; + scanLine[i] = mColorBuffer.at(index); + } + } + } +} + +/*! \internal + + This method is used to colorize a single data value given in \a position, to colors. The data + range that shall be used for mapping the data value to the gradient is passed in \a range. \a + logarithmic indicates whether the data value shall be mapped to a color logarithmically. + + If an entire array of data values shall be converted, rather use \ref colorize, for better + performance. +*/ +QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic) +{ + // If you change something here, make sure to also adapt ::colorize() + if (mColorBufferInvalidated) + updateColorBuffer(); + int index = 0; + if (!logarithmic) + index = (position-range.lower)*(mLevelCount-1)/range.size(); + else + index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1); + if (mPeriodic) + { + index = index % mLevelCount; + if (index < 0) + index += mLevelCount; + } else + { + if (index < 0) + index = 0; + else if (index >= mLevelCount) + index = mLevelCount-1; + } + return mColorBuffer.at(index); +} + +/*! + Clears the current color stops and loads the specified \a preset. A preset consists of predefined + color stops and the corresponding color interpolation method. + + The available presets are: + \image html QCPColorGradient.png +*/ +void QCPColorGradient::loadPreset(GradientPreset preset) +{ + clearColorStops(); + switch (preset) + { + case gpGrayscale: + setColorInterpolation(ciRGB); + setColorStopAt(0, Qt::black); + setColorStopAt(1, Qt::white); + break; + case gpHot: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(50, 0, 0)); + setColorStopAt(0.2, QColor(180, 10, 0)); + setColorStopAt(0.4, QColor(245, 50, 0)); + setColorStopAt(0.6, QColor(255, 150, 10)); + setColorStopAt(0.8, QColor(255, 255, 50)); + setColorStopAt(1, QColor(255, 255, 255)); + break; + case gpCold: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(0, 0, 50)); + setColorStopAt(0.2, QColor(0, 10, 180)); + setColorStopAt(0.4, QColor(0, 50, 245)); + setColorStopAt(0.6, QColor(10, 150, 255)); + setColorStopAt(0.8, QColor(50, 255, 255)); + setColorStopAt(1, QColor(255, 255, 255)); + break; + case gpNight: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(10, 20, 30)); + setColorStopAt(1, QColor(250, 255, 250)); + break; + case gpCandy: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(0, 0, 255)); + setColorStopAt(1, QColor(255, 250, 250)); + break; + case gpGeography: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(70, 170, 210)); + setColorStopAt(0.20, QColor(90, 160, 180)); + setColorStopAt(0.25, QColor(45, 130, 175)); + setColorStopAt(0.30, QColor(100, 140, 125)); + setColorStopAt(0.5, QColor(100, 140, 100)); + setColorStopAt(0.6, QColor(130, 145, 120)); + setColorStopAt(0.7, QColor(140, 130, 120)); + setColorStopAt(0.9, QColor(180, 190, 190)); + setColorStopAt(1, QColor(210, 210, 230)); + break; + case gpIon: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(50, 10, 10)); + setColorStopAt(0.45, QColor(0, 0, 255)); + setColorStopAt(0.8, QColor(0, 255, 255)); + setColorStopAt(1, QColor(0, 255, 0)); + break; + case gpThermal: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(0, 0, 50)); + setColorStopAt(0.15, QColor(20, 0, 120)); + setColorStopAt(0.33, QColor(200, 30, 140)); + setColorStopAt(0.6, QColor(255, 100, 0)); + setColorStopAt(0.85, QColor(255, 255, 40)); + setColorStopAt(1, QColor(255, 255, 255)); + break; + case gpPolar: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(50, 255, 255)); + setColorStopAt(0.18, QColor(10, 70, 255)); + setColorStopAt(0.28, QColor(10, 10, 190)); + setColorStopAt(0.5, QColor(0, 0, 0)); + setColorStopAt(0.72, QColor(190, 10, 10)); + setColorStopAt(0.82, QColor(255, 70, 10)); + setColorStopAt(1, QColor(255, 255, 50)); + break; + case gpSpectrum: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(50, 0, 50)); + setColorStopAt(0.15, QColor(0, 0, 255)); + setColorStopAt(0.35, QColor(0, 255, 255)); + setColorStopAt(0.6, QColor(255, 255, 0)); + setColorStopAt(0.75, QColor(255, 30, 0)); + setColorStopAt(1, QColor(50, 0, 0)); + break; + case gpJet: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(0, 0, 100)); + setColorStopAt(0.15, QColor(0, 50, 255)); + setColorStopAt(0.35, QColor(0, 255, 255)); + setColorStopAt(0.65, QColor(255, 255, 0)); + setColorStopAt(0.85, QColor(255, 30, 0)); + setColorStopAt(1, QColor(100, 0, 0)); + break; + case gpHues: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(255, 0, 0)); + setColorStopAt(1.0/3.0, QColor(0, 0, 255)); + setColorStopAt(2.0/3.0, QColor(0, 255, 0)); + setColorStopAt(1, QColor(255, 0, 0)); + break; + } +} + +/*! + Clears all color stops. + + \see setColorStops, setColorStopAt +*/ +void QCPColorGradient::clearColorStops() +{ + mColorStops.clear(); + mColorBufferInvalidated = true; +} + +/*! + Returns an inverted gradient. The inverted gradient has all properties as this \ref + QCPColorGradient, but the order of the color stops is inverted. + + \see setColorStops, setColorStopAt +*/ +QCPColorGradient QCPColorGradient::inverted() const +{ + QCPColorGradient result(*this); + result.clearColorStops(); + for (QMap::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it) + result.setColorStopAt(1.0-it.key(), it.value()); + return result; +} + +/*! \internal + + Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly + convert positions to colors. This is where the interpolation between color stops is calculated. +*/ +void QCPColorGradient::updateColorBuffer() +{ + if (mColorBuffer.size() != mLevelCount) + mColorBuffer.resize(mLevelCount); + if (mColorStops.size() > 1) + { + double indexToPosFactor = 1.0/(double)(mLevelCount-1); + for (int i=0; i::const_iterator it = mColorStops.lowerBound(position); + if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop + { + mColorBuffer[i] = (it-1).value().rgb(); + } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop + { + mColorBuffer[i] = it.value().rgb(); + } else // position is in between stops (or on an intermediate stop), interpolate color + { + QMap::const_iterator high = it; + QMap::const_iterator low = it-1; + double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1 + switch (mColorInterpolation) + { + case ciRGB: + { + mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(), + (1-t)*low.value().green() + t*high.value().green(), + (1-t)*low.value().blue() + t*high.value().blue()); + break; + } + case ciHSV: + { + QColor lowHsv = low.value().toHsv(); + QColor highHsv = high.value().toHsv(); + double hue = 0; + double hueDiff = highHsv.hueF()-lowHsv.hueF(); + if (hueDiff > 0.5) + hue = lowHsv.hueF() - t*(1.0-hueDiff); + else if (hueDiff < -0.5) + hue = lowHsv.hueF() + t*(1.0+hueDiff); + else + hue = lowHsv.hueF() + t*hueDiff; + if (hue < 0) hue += 1.0; + else if (hue >= 1.0) hue -= 1.0; + mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb(); + break; + } + } + } + } + } else if (mColorStops.size() == 1) + { + mColorBuffer.fill(mColorStops.constBegin().value().rgb()); + } else // mColorStops is empty, fill color buffer with black + { + mColorBuffer.fill(qRgb(0, 0, 0)); + } + mColorBufferInvalidated = false; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisRect +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAxisRect + \brief Holds multiple axes and arranges them in a rectangular shape. + + This class represents an axis rect, a rectangular area that is bounded on all sides with an + arbitrary number of axes. + + Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the + layout system allows to have multiple axis rects, e.g. arranged in a grid layout + (QCustomPlot::plotLayout). + + By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be + accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index. + If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be + invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref + addAxes. To remove an axis, use \ref removeAxis. + + The axis rect layerable itself only draws a background pixmap or color, if specified (\ref + setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an + explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be + placed on other layers, independently of the axis rect. + + Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref + insetLayout and can be used to have other layout elements (or even other layouts with multiple + elements) hovering inside the axis rect. + + If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The + behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel + is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable + via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are + only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref + QCP::iRangeZoom. + + \image html AxisRectSpacingOverview.png +
Overview of the spacings and paddings that define the geometry of an axis. The dashed + line on the far left indicates the viewport/widget border.
+*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const + + Returns the inset layout of this axis rect. It can be used to place other layout elements (or + even layouts with multiple other elements) inside/on top of an axis rect. + + \see QCPLayoutInset +*/ + +/*! \fn int QCPAxisRect::left() const + + Returns the pixel position of the left border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::right() const + + Returns the pixel position of the right border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::top() const + + Returns the pixel position of the top border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::bottom() const + + Returns the pixel position of the bottom border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::width() const + + Returns the pixel width of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::height() const + + Returns the pixel height of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QSize QCPAxisRect::size() const + + Returns the pixel size of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::topLeft() const + + Returns the top left corner of this axis rect in pixels. Margins are not taken into account here, + so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::topRight() const + + Returns the top right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::bottomLeft() const + + Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::bottomRight() const + + Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::center() const + + Returns the center of this axis rect in pixels. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four + sides, the top and right axes are set invisible initially. +*/ +QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) : + QCPLayoutElement(parentPlot), + mBackgroundBrush(Qt::NoBrush), + mBackgroundScaled(true), + mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), + mInsetLayout(new QCPLayoutInset), + mRangeDrag(Qt::Horizontal|Qt::Vertical), + mRangeZoom(Qt::Horizontal|Qt::Vertical), + mRangeZoomFactorHorz(0.85), + mRangeZoomFactorVert(0.85), + mDragging(false) +{ + mInsetLayout->initializeParentPlot(mParentPlot); + mInsetLayout->setParentLayerable(this); + mInsetLayout->setParent(this); + + setMinimumSize(50, 50); + setMinimumMargins(QMargins(15, 15, 15, 15)); + mAxes.insert(QCPAxis::atLeft, QList()); + mAxes.insert(QCPAxis::atRight, QList()); + mAxes.insert(QCPAxis::atTop, QList()); + mAxes.insert(QCPAxis::atBottom, QList()); + + if (setupDefaultAxes) + { + QCPAxis *xAxis = addAxis(QCPAxis::atBottom); + QCPAxis *yAxis = addAxis(QCPAxis::atLeft); + QCPAxis *xAxis2 = addAxis(QCPAxis::atTop); + QCPAxis *yAxis2 = addAxis(QCPAxis::atRight); + setRangeDragAxes(xAxis, yAxis); + setRangeZoomAxes(xAxis, yAxis); + xAxis2->setVisible(false); + yAxis2->setVisible(false); + xAxis->grid()->setVisible(true); + yAxis->grid()->setVisible(true); + xAxis2->grid()->setVisible(false); + yAxis2->grid()->setVisible(false); + xAxis2->grid()->setZeroLinePen(Qt::NoPen); + yAxis2->grid()->setZeroLinePen(Qt::NoPen); + xAxis2->grid()->setVisible(false); + yAxis2->grid()->setVisible(false); + } +} + +QCPAxisRect::~QCPAxisRect() +{ + delete mInsetLayout; + mInsetLayout = 0; + + QList axesList = axes(); + for (int i=0; i ax(mAxes.value(type)); + if (index >= 0 && index < ax.size()) + { + return ax.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index; + return 0; + } +} + +/*! + Returns all axes on the axis rect sides specified with \a types. + + \a types may be a single \ref QCPAxis::AxisType or an or-combination, to get the axes of + multiple sides. + + \see axis +*/ +QList QCPAxisRect::axes(QCPAxis::AxisTypes types) const +{ + QList result; + if (types.testFlag(QCPAxis::atLeft)) + result << mAxes.value(QCPAxis::atLeft); + if (types.testFlag(QCPAxis::atRight)) + result << mAxes.value(QCPAxis::atRight); + if (types.testFlag(QCPAxis::atTop)) + result << mAxes.value(QCPAxis::atTop); + if (types.testFlag(QCPAxis::atBottom)) + result << mAxes.value(QCPAxis::atBottom); + return result; +} + +/*! \overload + + Returns all axes of this axis rect. +*/ +QList QCPAxisRect::axes() const +{ + QList result; + QHashIterator > it(mAxes); + while (it.hasNext()) + { + it.next(); + result << it.value(); + } + return result; +} + +/*! + Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a + new QCPAxis instance is created internally. + + You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis to an axis that was + previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership + of the axis, so you may not delete it afterwards. Further, the \a axis must have been created + with this axis rect as parent and with the same axis type as specified in \a type. If this is not + the case, a debug output is generated, the axis is not added, and the method returns 0. + + This method can not be used to move \a axis between axis rects. The same \a axis instance must + not be added multiple times to the same or different axis rects. + + If an axis rect side already contains one or more axes, the lower and upper endings of the new + axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref + QCPLineEnding::esHalfBar. + + \see addAxes, setupFullAxesBox +*/ +QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis) +{ + QCPAxis *newAxis = axis; + if (!newAxis) + { + newAxis = new QCPAxis(this, type); + } else // user provided existing axis instance, do some sanity checks + { + if (newAxis->axisType() != type) + { + qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter"; + return 0; + } + if (newAxis->axisRect() != this) + { + qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect"; + return 0; + } + if (axes().contains(newAxis)) + { + qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect"; + return 0; + } + } + if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset + { + bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom); + newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert)); + newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert)); + } + mAxes[type].append(newAxis); + return newAxis; +} + +/*! + Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an + or-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once. + + Returns a list of the added axes. + + \see addAxis, setupFullAxesBox +*/ +QList QCPAxisRect::addAxes(QCPAxis::AxisTypes types) +{ + QList result; + if (types.testFlag(QCPAxis::atLeft)) + result << addAxis(QCPAxis::atLeft); + if (types.testFlag(QCPAxis::atRight)) + result << addAxis(QCPAxis::atRight); + if (types.testFlag(QCPAxis::atTop)) + result << addAxis(QCPAxis::atTop); + if (types.testFlag(QCPAxis::atBottom)) + result << addAxis(QCPAxis::atBottom); + return result; +} + +/*! + Removes the specified \a axis from the axis rect and deletes it. + + Returns true on success, i.e. if \a axis was a valid axis in this axis rect. + + \see addAxis +*/ +bool QCPAxisRect::removeAxis(QCPAxis *axis) +{ + // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers: + QHashIterator > it(mAxes); + while (it.hasNext()) + { + it.next(); + if (it.value().contains(axis)) + { + mAxes[it.key()].removeOne(axis); + if (qobject_cast(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot) + parentPlot()->axisRemoved(axis); + delete axis; + return true; + } + } + qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast(axis); + return false; +} + +/*! + Convenience function to create an axis on each side that doesn't have any axes yet and set their + visibility to true. Further, the top/right axes are assigned the following properties of the + bottom/left axes: + + \li range (\ref QCPAxis::setRange) + \li range reversed (\ref QCPAxis::setRangeReversed) + \li scale type (\ref QCPAxis::setScaleType) + \li scale log base (\ref QCPAxis::setScaleLogBase) + \li ticks (\ref QCPAxis::setTicks) + \li auto (major) tick count (\ref QCPAxis::setAutoTickCount) + \li sub tick count (\ref QCPAxis::setSubTickCount) + \li auto sub ticks (\ref QCPAxis::setAutoSubTicks) + \li tick step (\ref QCPAxis::setTickStep) + \li auto tick step (\ref QCPAxis::setAutoTickStep) + \li number format (\ref QCPAxis::setNumberFormat) + \li number precision (\ref QCPAxis::setNumberPrecision) + \li tick label type (\ref QCPAxis::setTickLabelType) + \li date time format (\ref QCPAxis::setDateTimeFormat) + \li date time spec (\ref QCPAxis::setDateTimeSpec) + + Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false. + + If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom + and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes. +*/ +void QCPAxisRect::setupFullAxesBox(bool connectRanges) +{ + QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2; + if (axisCount(QCPAxis::atBottom) == 0) + xAxis = addAxis(QCPAxis::atBottom); + else + xAxis = axis(QCPAxis::atBottom); + + if (axisCount(QCPAxis::atLeft) == 0) + yAxis = addAxis(QCPAxis::atLeft); + else + yAxis = axis(QCPAxis::atLeft); + + if (axisCount(QCPAxis::atTop) == 0) + xAxis2 = addAxis(QCPAxis::atTop); + else + xAxis2 = axis(QCPAxis::atTop); + + if (axisCount(QCPAxis::atRight) == 0) + yAxis2 = addAxis(QCPAxis::atRight); + else + yAxis2 = axis(QCPAxis::atRight); + + xAxis->setVisible(true); + yAxis->setVisible(true); + xAxis2->setVisible(true); + yAxis2->setVisible(true); + xAxis2->setTickLabels(false); + yAxis2->setTickLabels(false); + + xAxis2->setRange(xAxis->range()); + xAxis2->setRangeReversed(xAxis->rangeReversed()); + xAxis2->setScaleType(xAxis->scaleType()); + xAxis2->setScaleLogBase(xAxis->scaleLogBase()); + xAxis2->setTicks(xAxis->ticks()); + xAxis2->setAutoTickCount(xAxis->autoTickCount()); + xAxis2->setSubTickCount(xAxis->subTickCount()); + xAxis2->setAutoSubTicks(xAxis->autoSubTicks()); + xAxis2->setTickStep(xAxis->tickStep()); + xAxis2->setAutoTickStep(xAxis->autoTickStep()); + xAxis2->setNumberFormat(xAxis->numberFormat()); + xAxis2->setNumberPrecision(xAxis->numberPrecision()); + xAxis2->setTickLabelType(xAxis->tickLabelType()); + xAxis2->setDateTimeFormat(xAxis->dateTimeFormat()); + xAxis2->setDateTimeSpec(xAxis->dateTimeSpec()); + + yAxis2->setRange(yAxis->range()); + yAxis2->setRangeReversed(yAxis->rangeReversed()); + yAxis2->setScaleType(yAxis->scaleType()); + yAxis2->setScaleLogBase(yAxis->scaleLogBase()); + yAxis2->setTicks(yAxis->ticks()); + yAxis2->setAutoTickCount(yAxis->autoTickCount()); + yAxis2->setSubTickCount(yAxis->subTickCount()); + yAxis2->setAutoSubTicks(yAxis->autoSubTicks()); + yAxis2->setTickStep(yAxis->tickStep()); + yAxis2->setAutoTickStep(yAxis->autoTickStep()); + yAxis2->setNumberFormat(yAxis->numberFormat()); + yAxis2->setNumberPrecision(yAxis->numberPrecision()); + yAxis2->setTickLabelType(yAxis->tickLabelType()); + yAxis2->setDateTimeFormat(yAxis->dateTimeFormat()); + yAxis2->setDateTimeSpec(yAxis->dateTimeSpec()); + + if (connectRanges) + { + connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange))); + connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange))); + } +} + +/*! + Returns a list of all the plottables that are associated with this axis rect. + + A plottable is considered associated with an axis rect if its key or value axis (or both) is in + this axis rect. + + \see graphs, items +*/ +QList QCPAxisRect::plottables() const +{ + // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries + QList result; + for (int i=0; imPlottables.size(); ++i) + { + if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this) + result.append(mParentPlot->mPlottables.at(i)); + } + return result; +} + +/*! + Returns a list of all the graphs that are associated with this axis rect. + + A graph is considered associated with an axis rect if its key or value axis (or both) is in + this axis rect. + + \see plottables, items +*/ +QList QCPAxisRect::graphs() const +{ + // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries + QList result; + for (int i=0; imGraphs.size(); ++i) + { + if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this) + result.append(mParentPlot->mGraphs.at(i)); + } + return result; +} + +/*! + Returns a list of all the items that are associated with this axis rect. + + An item is considered associated with an axis rect if any of its positions has key or value axis + set to an axis that is in this axis rect, or if any of its positions has \ref + QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref + QCPAbstractItem::setClipAxisRect) is set to this axis rect. + + \see plottables, graphs +*/ +QList QCPAxisRect::items() const +{ + // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries + // and miss those items that have this axis rect as clipAxisRect. + QList result; + for (int itemId=0; itemIdmItems.size(); ++itemId) + { + if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this) + { + result.append(mParentPlot->mItems.at(itemId)); + continue; + } + QList positions = mParentPlot->mItems.at(itemId)->positions(); + for (int posId=0; posIdaxisRect() == this || + positions.at(posId)->keyAxis()->axisRect() == this || + positions.at(posId)->valueAxis()->axisRect() == this) + { + result.append(mParentPlot->mItems.at(itemId)); + break; + } + } + } + return result; +} + +/*! + This method is called automatically upon replot and doesn't need to be called by users of + QCPAxisRect. + + Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update), + and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its + QCPInsetLayout::update function. +*/ +void QCPAxisRect::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + + switch (phase) + { + case upPreparation: + { + QList allAxes = axes(); + for (int i=0; isetupTickVectors(); + break; + } + case upLayout: + { + mInsetLayout->setOuterRect(rect()); + break; + } + default: break; + } + + // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout): + mInsetLayout->update(phase); +} + +/* inherits documentation from base class */ +QList QCPAxisRect::elements(bool recursive) const +{ + QList result; + if (mInsetLayout) + { + result << mInsetLayout; + if (recursive) + result << mInsetLayout->elements(recursive); + } + return result; +} + +/* inherits documentation from base class */ +void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + painter->setAntialiasing(false); +} + +/* inherits documentation from base class */ +void QCPAxisRect::draw(QCPPainter *painter) +{ + drawBackground(painter); +} + +/*! + Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the + axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect + backgrounds are usually drawn below everything else. + + For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be + enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio + is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call, + consider using the overloaded version of this function. + + Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref + setBackground(const QBrush &brush). + + \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush) +*/ +void QCPAxisRect::setBackground(const QPixmap &pm) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); +} + +/*! \overload + + Sets \a brush as the background brush. The axis rect background will be filled with this brush. + Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds + are usually drawn below everything else. + + The brush will be drawn before (under) any background pixmap, which may be specified with \ref + setBackground(const QPixmap &pm). + + To disable drawing of a background brush, set \a brush to Qt::NoBrush. + + \see setBackground(const QPixmap &pm) +*/ +void QCPAxisRect::setBackground(const QBrush &brush) +{ + mBackgroundBrush = brush; +} + +/*! \overload + + Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it + shall be scaled in one call. + + \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); + mBackgroundScaled = scaled; + mBackgroundScaledMode = mode; +} + +/*! + Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled + is set to true, you may control whether and how the aspect ratio of the original pixmap is + preserved with \ref setBackgroundScaledMode. + + Note that the scaled version of the original pixmap is buffered, so there is no performance + penalty on replots. (Except when the axis rect dimensions are changed continuously.) + + \see setBackground, setBackgroundScaledMode +*/ +void QCPAxisRect::setBackgroundScaled(bool scaled) +{ + mBackgroundScaled = scaled; +} + +/*! + If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to + define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved. + \see setBackground, setBackgroundScaled +*/ +void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) +{ + mBackgroundScaledMode = mode; +} + +/*! + Returns the range drag axis of the \a orientation provided. + + \see setRangeDragAxes +*/ +QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) +{ + return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data()); +} + +/*! + Returns the range zoom axis of the \a orientation provided. + + \see setRangeZoomAxes +*/ +QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) +{ + return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data()); +} + +/*! + Returns the range zoom factor of the \a orientation provided. + + \see setRangeZoomFactor +*/ +double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) +{ + return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert); +} + +/*! + Sets which axis orientation may be range dragged by the user with mouse interaction. + What orientation corresponds to which specific axis can be set with + \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By + default, the horizontal axis is the bottom axis (xAxis) and the vertical axis + is the left axis (yAxis). + + To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref + QCustomPlot::setInteractions. To enable range dragging for both directions, pass Qt::Horizontal | + Qt::Vertical as \a orientations. + + In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions + contains \ref QCP::iRangeDrag to enable the range dragging interaction. + + \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag +*/ +void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) +{ + mRangeDrag = orientations; +} + +/*! + Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation + corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal, + QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical + axis is the left axis (yAxis). + + To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref + QCustomPlot::setInteractions. To enable range zooming for both directions, pass Qt::Horizontal | + Qt::Vertical as \a orientations. + + In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions + contains \ref QCP::iRangeZoom to enable the range zooming interaction. + + \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag +*/ +void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) +{ + mRangeZoom = orientations; +} + +/*! + Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging + on the QCustomPlot widget. + + \see setRangeZoomAxes +*/ +void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical) +{ + mRangeDragHorzAxis = horizontal; + mRangeDragVertAxis = vertical; +} + +/*! + Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the + QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors + are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor). + + \see setRangeDragAxes +*/ +void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical) +{ + mRangeZoomHorzAxis = horizontal; + mRangeZoomVertAxis = vertical; +} + +/*! + Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with + \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to + let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal + and which is vertical, can be set with \ref setRangeZoomAxes. + + When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user) + will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the + same scrolling direction will zoom out. +*/ +void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor) +{ + mRangeZoomFactorHorz = horizontalFactor; + mRangeZoomFactorVert = verticalFactor; +} + +/*! \overload + + Sets both the horizontal and vertical zoom \a factor. +*/ +void QCPAxisRect::setRangeZoomFactor(double factor) +{ + mRangeZoomFactorHorz = factor; + mRangeZoomFactorVert = factor; +} + +/*! \internal + + Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a + pixmap. + + If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an + according filling inside the axis rect with the provided \a painter. + + Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version + depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside + the axis rect with the provided \a painter. The scaled version is buffered in + mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when + the axis rect has changed in a way that requires a rescale of the background pixmap (this is + dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was + set. + + \see setBackground, setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPAxisRect::drawBackground(QCPPainter *painter) +{ + // draw background fill: + if (mBackgroundBrush != Qt::NoBrush) + painter->fillRect(mRect, mBackgroundBrush); + + // draw background pixmap (on top of fill, if brush specified): + if (!mBackgroundPixmap.isNull()) + { + if (mBackgroundScaled) + { + // check whether mScaledBackground needs to be updated: + QSize scaledSize(mBackgroundPixmap.size()); + scaledSize.scale(mRect.size(), mBackgroundScaledMode); + if (mScaledBackgroundPixmap.size() != scaledSize) + mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation); + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect()); + } else + { + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height())); + } + } +} + +/*! \internal + + This function makes sure multiple axes on the side specified with \a type don't collide, but are + distributed according to their respective space requirement (QCPAxis::calculateMargin). + + It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the + one with index zero. + + This function is called by \ref calculateAutoMargin. +*/ +void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type) +{ + const QList axesList = mAxes.value(type); + if (axesList.isEmpty()) + return; + + bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false + for (int i=1; ioffset() + axesList.at(i-1)->calculateMargin(); + if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible) + { + if (!isFirstVisible) + offset += axesList.at(i)->tickLengthIn(); + isFirstVisible = false; + } + axesList.at(i)->setOffset(offset); + } +} + +/* inherits documentation from base class */ +int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side) +{ + if (!mAutoMargins.testFlag(side)) + qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin"; + + updateAxesOffset(QCPAxis::marginSideToAxisType(side)); + + // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call + const QList axesList = mAxes.value(QCPAxis::marginSideToAxisType(side)); + if (axesList.size() > 0) + return axesList.last()->offset() + axesList.last()->calculateMargin(); + else + return 0; +} + +/*! \internal + + Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is + pressed, the range dragging interaction is initialized (the actual range manipulation happens in + the \ref mouseMoveEvent). + + The mDragging flag is set to true and some anchor points are set that are needed to determine the + distance the mouse was dragged in the mouse move/release events later. + + \see mouseMoveEvent, mouseReleaseEvent +*/ +void QCPAxisRect::mousePressEvent(QMouseEvent *event) +{ + mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release) + if (event->buttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + if (mRangeDragHorzAxis) + mDragStartHorzRange = mRangeDragHorzAxis.data()->range(); + if (mRangeDragVertAxis) + mDragStartVertRange = mRangeDragVertAxis.data()->range(); + } + } +} + +/*! \internal + + Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a + preceding \ref mousePressEvent, the range is moved accordingly. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCPAxisRect::mouseMoveEvent(QMouseEvent *event) +{ + // Mouse range dragging interaction: + if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + if (mRangeDrag.testFlag(Qt::Horizontal)) + { + if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data()) + { + if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear) + { + double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x()); + rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff); + } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic) + { + double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x()); + rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff); + } + } + } + if (mRangeDrag.testFlag(Qt::Vertical)) + { + if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data()) + { + if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear) + { + double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y()); + rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff); + } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic) + { + double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y()); + rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff); + } + } + } + if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot + { + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(); + } + } +} + +/* inherits documentation from base class */ +void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the + ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of + the scaling operation is the current cursor position inside the axis rect. The scaling factor is + dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural + zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor. + + Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse + wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be + multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as + exponent of the range zoom factor. This takes care of the wheel direction automatically, by + inverting the factor, when the wheel step is negative (f^-1 = 1/f). +*/ +void QCPAxisRect::wheelEvent(QWheelEvent *event) +{ + // Mouse range zooming interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) + { + if (mRangeZoom != 0) + { + double factor; + double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually + if (mRangeZoom.testFlag(Qt::Horizontal)) + { + factor = qPow(mRangeZoomFactorHorz, wheelSteps); + if (mRangeZoomHorzAxis.data()) + mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x())); + } + if (mRangeZoom.testFlag(Qt::Vertical)) + { + factor = qPow(mRangeZoomFactorVert, wheelSteps); + if (mRangeZoomVertAxis.data()) + mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y())); + } + mParentPlot->replot(); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractLegendItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractLegendItem + \brief The abstract base class for all entries in a QCPLegend. + + It defines a very basic interface for entries in a QCPLegend. For representing plottables in the + legend, the subclass \ref QCPPlottableLegendItem is more suitable. + + Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry + that's not even associated with a plottable). + + You must implement the following pure virtual functions: + \li \ref draw (from QCPLayerable) + + You inherit the following members you may use: + + + + + + + + +
QCPLegend *\b mParentLegendA pointer to the parent QCPLegend.
QFont \b mFontThe generic font of the item. You should use this font for all or at least the most prominent text of the item.
+*/ + +/* start of documentation of signals */ + +/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected) + + This signal is emitted when the selection state of this legend item has changed, either by user + interaction or by a direct call to \ref setSelected. +*/ + +/* end of documentation of signals */ + +/*! + Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not + cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately. +*/ +QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) : + QCPLayoutElement(parent->parentPlot()), + mParentLegend(parent), + mFont(parent->font()), + mTextColor(parent->textColor()), + mSelectedFont(parent->selectedFont()), + mSelectedTextColor(parent->selectedTextColor()), + mSelectable(true), + mSelected(false) +{ + setLayer(QLatin1String("legend")); + setMargins(QMargins(8, 2, 8, 2)); +} + +/*! + Sets the default font of this specific legend item to \a font. + + \see setTextColor, QCPLegend::setFont +*/ +void QCPAbstractLegendItem::setFont(const QFont &font) +{ + mFont = font; +} + +/*! + Sets the default text color of this specific legend item to \a color. + + \see setFont, QCPLegend::setTextColor +*/ +void QCPAbstractLegendItem::setTextColor(const QColor &color) +{ + mTextColor = color; +} + +/*! + When this legend item is selected, \a font is used to draw generic text, instead of the normal + font set with \ref setFont. + + \see setFont, QCPLegend::setSelectedFont +*/ +void QCPAbstractLegendItem::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; +} + +/*! + When this legend item is selected, \a color is used to draw generic text, instead of the normal + color set with \ref setTextColor. + + \see setTextColor, QCPLegend::setSelectedTextColor +*/ +void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color) +{ + mSelectedTextColor = color; +} + +/*! + Sets whether this specific legend item is selectable. + + \see setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAbstractLegendItem::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets whether this specific legend item is selected. + + It is possible to set the selection state of this item by calling this function directly, even if + setSelectable is set to false. + + \see setSelectableParts, QCustomPlot::setInteractions +*/ +void QCPAbstractLegendItem::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/* inherits documentation from base class */ +double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (!mParentPlot) return -1; + if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems))) + return -1; + + if (mRect.contains(pos.toPoint())) + return mParentPlot->selectionTolerance()*0.99; + else + return -1; +} + +/* inherits documentation from base class */ +void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems); +} + +/* inherits documentation from base class */ +QRect QCPAbstractLegendItem::clipRect() const +{ + return mOuterRect; +} + +/* inherits documentation from base class */ +void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems)) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems)) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPlottableLegendItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPlottableLegendItem + \brief A legend item representing a plottable with an icon and the plottable name. + + This is the standard legend item for plottables. It displays an icon of the plottable next to the + plottable name. The icon is drawn by the respective plottable itself (\ref + QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable. + For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the + middle. + + Legend items of this type are always associated with one plottable (retrievable via the + plottable() function and settable with the constructor). You may change the font of the plottable + name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref + QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding. + + The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend + creates/removes legend items of this type in the default implementation. However, these functions + may be reimplemented such that a different kind of legend item (e.g a direct subclass of + QCPAbstractLegendItem) is used for that plottable. + + Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of + QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout + interface, QCPLegend has specialized functions for handling legend items conveniently, see the + documentation of \ref QCPLegend. +*/ + +/*! + Creates a new legend item associated with \a plottable. + + Once it's created, it can be added to the legend via \ref QCPLegend::addItem. + + A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref + QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend. +*/ +QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) : + QCPAbstractLegendItem(parent), + mPlottable(plottable) +{ +} + +/*! \internal + + Returns the pen that shall be used to draw the icon border, taking into account the selection + state of this item. +*/ +QPen QCPPlottableLegendItem::getIconBorderPen() const +{ + return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen(); +} + +/*! \internal + + Returns the text color that shall be used to draw text, taking into account the selection state + of this item. +*/ +QColor QCPPlottableLegendItem::getTextColor() const +{ + return mSelected ? mSelectedTextColor : mTextColor; +} + +/*! \internal + + Returns the font that shall be used to draw text, taking into account the selection state of this + item. +*/ +QFont QCPPlottableLegendItem::getFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + +/*! \internal + + Draws the item with \a painter. The size and position of the drawn legend item is defined by the + parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint + of this legend item. +*/ +void QCPPlottableLegendItem::draw(QCPPainter *painter) +{ + if (!mPlottable) return; + painter->setFont(getFont()); + painter->setPen(QPen(getTextColor())); + QSizeF iconSize = mParentLegend->iconSize(); + QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); + QRectF iconRect(mRect.topLeft(), iconSize); + int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops + painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name()); + // draw icon: + painter->save(); + painter->setClipRect(iconRect, Qt::IntersectClip); + mPlottable->drawLegendIcon(painter, iconRect); + painter->restore(); + // draw icon border: + if (getIconBorderPen().style() != Qt::NoPen) + { + painter->setPen(getIconBorderPen()); + painter->setBrush(Qt::NoBrush); + painter->drawRect(iconRect); + } +} + +/*! \internal + + Calculates and returns the size of this item. This includes the icon, the text and the padding in + between. +*/ +QSize QCPPlottableLegendItem::minimumSizeHint() const +{ + if (!mPlottable) return QSize(); + QSize result(0, 0); + QRect textRect; + QFontMetrics fontMetrics(getFont()); + QSize iconSize = mParentLegend->iconSize(); + textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); + result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right()); + result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom()); + return result; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLegend +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLegend + \brief Manages a legend inside a QCustomPlot. + + A legend is a small box somewhere in the plot which lists plottables with their name and icon. + + Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The + respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However, + QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref + itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc. + + The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a + QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are + placed in the grid layout of the legend. QCPLegend only adds an interface specialized for + handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any + other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface. + However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will + only return the number of items with QCPAbstractLegendItems type). + + By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset + layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another + position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend + outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface. +*/ + +/* start of documentation of signals */ + +/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection); + + This signal is emitted when the selection state of this legend has changed. + + \see setSelectedParts, setSelectableParts +*/ + +/* end of documentation of signals */ + +/*! + Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values. + + Note that by default, QCustomPlot already contains a legend ready to be used as + QCustomPlot::legend +*/ +QCPLegend::QCPLegend() +{ + setRowSpacing(0); + setColumnSpacing(10); + setMargins(QMargins(2, 3, 2, 2)); + setAntialiased(false); + setIconSize(32, 18); + + setIconTextPadding(7); + + setSelectableParts(spLegendBox | spItems); + setSelectedParts(spNone); + + setBorderPen(QPen(Qt::black)); + setSelectedBorderPen(QPen(Qt::blue, 2)); + setIconBorderPen(Qt::NoPen); + setSelectedIconBorderPen(QPen(Qt::blue, 2)); + setBrush(Qt::white); + setSelectedBrush(Qt::white); + setTextColor(Qt::black); + setSelectedTextColor(Qt::blue); +} + +QCPLegend::~QCPLegend() +{ + clearItems(); + if (qobject_cast(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot) + mParentPlot->legendRemoved(this); +} + +/* no doc for getter, see setSelectedParts */ +QCPLegend::SelectableParts QCPLegend::selectedParts() const +{ + // check whether any legend elements selected, if yes, add spItems to return value + bool hasSelectedItems = false; + for (int i=0; iselected()) + { + hasSelectedItems = true; + break; + } + } + if (hasSelectedItems) + return mSelectedParts | spItems; + else + return mSelectedParts & ~spItems; +} + +/*! + Sets the pen, the border of the entire legend is drawn with. +*/ +void QCPLegend::setBorderPen(const QPen &pen) +{ + mBorderPen = pen; +} + +/*! + Sets the brush of the legend background. +*/ +void QCPLegend::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will + use this font by default. However, a different font can be specified on a per-item-basis by + accessing the specific legend item. + + This function will also set \a font on all already existing legend items. + + \see QCPAbstractLegendItem::setFont +*/ +void QCPLegend::setFont(const QFont &font) +{ + mFont = font; + for (int i=0; isetFont(mFont); + } +} + +/*! + Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph) + will use this color by default. However, a different colors can be specified on a per-item-basis + by accessing the specific legend item. + + This function will also set \a color on all already existing legend items. + + \see QCPAbstractLegendItem::setTextColor +*/ +void QCPLegend::setTextColor(const QColor &color) +{ + mTextColor = color; + for (int i=0; isetTextColor(color); + } +} + +/*! + Sets the size of legend icons. Legend items that draw an icon (e.g. a visual + representation of the graph) will use this size by default. +*/ +void QCPLegend::setIconSize(const QSize &size) +{ + mIconSize = size; +} + +/*! \overload +*/ +void QCPLegend::setIconSize(int width, int height) +{ + mIconSize.setWidth(width); + mIconSize.setHeight(height); +} + +/*! + Sets the horizontal space in pixels between the legend icon and the text next to it. + Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the + name of the graph) will use this space by default. +*/ +void QCPLegend::setIconTextPadding(int padding) +{ + mIconTextPadding = padding; +} + +/*! + Sets the pen used to draw a border around each legend icon. Legend items that draw an + icon (e.g. a visual representation of the graph) will use this pen by default. + + If no border is wanted, set this to \a Qt::NoPen. +*/ +void QCPLegend::setIconBorderPen(const QPen &pen) +{ + mIconBorderPen = pen; +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPLegend::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected + doesn't contain \ref spItems, those items become deselected. + + The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions + contains iSelectLegend. You only need to call this function when you wish to change the selection + state manually. + + This function can change the selection state of a part even when \ref setSelectableParts was set to a + value that actually excludes the part. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set + before, because there's no way to specify which exact items to newly select. Do this by calling + \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush, + setSelectedFont +*/ +void QCPLegend::setSelectedParts(const SelectableParts &selected) +{ + SelectableParts newSelected = selected; + mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed + + if (mSelectedParts != newSelected) + { + if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that) + { + qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function"; + newSelected &= ~spItems; + } + if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection + { + for (int i=0; isetSelected(false); + } + } + mSelectedParts = newSelected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + When the legend box is selected, this pen is used to draw the border instead of the normal pen + set via \ref setBorderPen. + + \see setSelectedParts, setSelectableParts, setSelectedBrush +*/ +void QCPLegend::setSelectedBorderPen(const QPen &pen) +{ + mSelectedBorderPen = pen; +} + +/*! + Sets the pen legend items will use to draw their icon borders, when they are selected. + + \see setSelectedParts, setSelectableParts, setSelectedFont +*/ +void QCPLegend::setSelectedIconBorderPen(const QPen &pen) +{ + mSelectedIconBorderPen = pen; +} + +/*! + When the legend box is selected, this brush is used to draw the legend background instead of the normal brush + set via \ref setBrush. + + \see setSelectedParts, setSelectableParts, setSelectedBorderPen +*/ +void QCPLegend::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + Sets the default font that is used by legend items when they are selected. + + This function will also set \a font on all already existing legend items. + + \see setFont, QCPAbstractLegendItem::setSelectedFont +*/ +void QCPLegend::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; + for (int i=0; isetSelectedFont(font); + } +} + +/*! + Sets the default text color that is used by legend items when they are selected. + + This function will also set \a color on all already existing legend items. + + \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor +*/ +void QCPLegend::setSelectedTextColor(const QColor &color) +{ + mSelectedTextColor = color; + for (int i=0; isetSelectedTextColor(color); + } +} + +/*! + Returns the item with index \a i. + + \see itemCount +*/ +QCPAbstractLegendItem *QCPLegend::item(int index) const +{ + return qobject_cast(elementAt(index)); +} + +/*! + Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*). + If such an item isn't in the legend, returns 0. + + \see hasItemWithPlottable +*/ +QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const +{ + for (int i=0; i(item(i))) + { + if (pli->plottable() == plottable) + return pli; + } + } + return 0; +} + +/*! + Returns the number of items currently in the legend. + \see item +*/ +int QCPLegend::itemCount() const +{ + return elementCount(); +} + +/*! + Returns whether the legend contains \a itm. +*/ +bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const +{ + for (int i=0; iitem(i)) + return true; + } + return false; +} + +/*! + Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*). + If such an item isn't in the legend, returns false. + + \see itemWithPlottable +*/ +bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const +{ + return itemWithPlottable(plottable); +} + +/*! + Adds \a item to the legend, if it's not present already. + + Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added. + + The legend takes ownership of the item. +*/ +bool QCPLegend::addItem(QCPAbstractLegendItem *item) +{ + if (!hasItem(item)) + { + return addElement(rowCount(), 0, item); + } else + return false; +} + +/*! + Removes the item with index \a index from the legend. + + Returns true, if successful. + + \see itemCount, clearItems +*/ +bool QCPLegend::removeItem(int index) +{ + if (QCPAbstractLegendItem *ali = item(index)) + { + bool success = remove(ali); + simplify(); + return success; + } else + return false; +} + +/*! \overload + + Removes \a item from the legend. + + Returns true, if successful. + + \see clearItems +*/ +bool QCPLegend::removeItem(QCPAbstractLegendItem *item) +{ + bool success = remove(item); + simplify(); + return success; +} + +/*! + Removes all items from the legend. +*/ +void QCPLegend::clearItems() +{ + for (int i=itemCount()-1; i>=0; --i) + removeItem(i); +} + +/*! + Returns the legend items that are currently selected. If no items are selected, + the list is empty. + + \see QCPAbstractLegendItem::setSelected, setSelectable +*/ +QList QCPLegend::selectedItems() const +{ + QList result; + for (int i=0; iselected()) + result.append(ali); + } + } + return result; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing main legend elements. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend); +} + +/*! \internal + + Returns the pen used to paint the border of the legend, taking into account the selection state + of the legend box. +*/ +QPen QCPLegend::getBorderPen() const +{ + return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen; +} + +/*! \internal + + Returns the brush used to paint the background of the legend, taking into account the selection + state of the legend box. +*/ +QBrush QCPLegend::getBrush() const +{ + return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush; +} + +/*! \internal + + Draws the legend box with the provided \a painter. The individual legend items are layerables + themselves, thus are drawn independently. +*/ +void QCPLegend::draw(QCPPainter *painter) +{ + // draw background rect: + painter->setBrush(getBrush()); + painter->setPen(getBorderPen()); + painter->drawRect(mOuterRect); +} + +/* inherits documentation from base class */ +double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mParentPlot) return -1; + if (onlySelectable && !mSelectableParts.testFlag(spLegendBox)) + return -1; + + if (mOuterRect.contains(pos.toPoint())) + { + if (details) details->setValue(spLegendBox); + return mParentPlot->selectionTolerance()*0.99; + } + return -1; +} + +/* inherits documentation from base class */ +void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + mSelectedParts = selectedParts(); // in case item selection has changed + if (details.value() == spLegendBox && mSelectableParts.testFlag(spLegendBox)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent) + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPLegend::deselectEvent(bool *selectionStateChanged) +{ + mSelectedParts = selectedParts(); // in case item selection has changed + if (mSelectableParts.testFlag(spLegendBox)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(selectedParts() & ~spLegendBox); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +QCP::Interaction QCPLegend::selectionCategory() const +{ + return QCP::iSelectLegend; +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAbstractLegendItem::selectionCategory() const +{ + return QCP::iSelectLegend; +} + +/* inherits documentation from base class */ +void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) +{ + Q_UNUSED(parentPlot) +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPlotTitle +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPlotTitle + \brief A layout element displaying a plot title text + + The text may be specified with \ref setText, theformatting can be controlled with \ref setFont + and \ref setTextColor. + + A plot title can be added as follows: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation + + Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for + easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the + signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref + QCustomPlot::titleDoubleClick signal. +*/ + +/* start documentation of signals */ + +/*! \fn void QCPPlotTitle::selectionChanged(bool selected) + + This signal is emitted when the selection state has changed to \a selected, either by user + interaction or by a direct call to \ref setSelected. + + \see setSelected, setSelectable +*/ + +/* end documentation of signals */ + +/*! + Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText). + + To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text). +*/ +QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) : + QCPLayoutElement(parentPlot), + mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)), + mTextColor(Qt::black), + mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)), + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + if (parentPlot) + { + setLayer(parentPlot->currentLayer()); + mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold); + mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold); + } + setMargins(QMargins(5, 5, 5, 0)); +} + +/*! \overload + + Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text. +*/ +QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) : + QCPLayoutElement(parentPlot), + mText(text), + mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)), + mTextColor(Qt::black), + mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)), + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + setLayer(QLatin1String("axes")); + setMargins(QMargins(5, 5, 5, 0)); +} + +/*! + Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n". + + \see setFont, setTextColor +*/ +void QCPPlotTitle::setText(const QString &text) +{ + mText = text; +} + +/*! + Sets the \a font of the title text. + + \see setTextColor, setSelectedFont +*/ +void QCPPlotTitle::setFont(const QFont &font) +{ + mFont = font; +} + +/*! + Sets the \a color of the title text. + + \see setFont, setSelectedTextColor +*/ +void QCPPlotTitle::setTextColor(const QColor &color) +{ + mTextColor = color; +} + +/*! + Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected). + + \see setFont +*/ +void QCPPlotTitle::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; +} + +/*! + Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected). + + \see setTextColor +*/ +void QCPPlotTitle::setSelectedTextColor(const QColor &color) +{ + mSelectedTextColor = color; +} + +/*! + Sets whether the user may select this plot title to \a selectable. + + Note that even when \a selectable is set to false, the selection state may be changed + programmatically via \ref setSelected. +*/ +void QCPPlotTitle::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets the selection state of this plot title to \a selected. If the selection has changed, \ref + selectionChanged is emitted. + + Note that this function can change the selection state independently of the current \ref + setSelectable state. +*/ +void QCPPlotTitle::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/* inherits documentation from base class */ +void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeNone); +} + +/* inherits documentation from base class */ +void QCPPlotTitle::draw(QCPPainter *painter) +{ + painter->setFont(mainFont()); + painter->setPen(QPen(mainTextColor())); + painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect); +} + +/* inherits documentation from base class */ +QSize QCPPlotTitle::minimumSizeHint() const +{ + QFontMetrics metrics(mFont); + QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size(); + result.rwidth() += mMargins.left() + mMargins.right(); + result.rheight() += mMargins.top() + mMargins.bottom(); + return result; +} + +/* inherits documentation from base class */ +QSize QCPPlotTitle::maximumSizeHint() const +{ + QFontMetrics metrics(mFont); + QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size(); + result.rheight() += mMargins.top() + mMargins.bottom(); + result.setWidth(QWIDGETSIZE_MAX); + return result; +} + +/* inherits documentation from base class */ +void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPPlotTitle::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + if (mTextBoundingRect.contains(pos.toPoint())) + return mParentPlot->selectionTolerance()*0.99; + else + return -1; +} + +/*! \internal + + Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to + true, else mFont is returned. +*/ +QFont QCPPlotTitle::mainFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + +/*! \internal + + Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to + true, else mTextColor is returned. +*/ +QColor QCPPlotTitle::mainTextColor() const +{ + return mSelected ? mSelectedTextColor : mTextColor; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorScale +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorScale + \brief A color scale for use with color coding data such as QCPColorMap + + This layout element can be placed on the plot to correlate a color gradient with data values. It + is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps". + + \image html QCPColorScale.png + + The color scale can be either horizontal or vertical, as shown in the image above. The + orientation and the side where the numbers appear is controlled with \ref setType. + + Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are + connected, they share their gradient, data range and data scale type (\ref setGradient, \ref + setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color + scale, to make them all synchronize these properties. + + To have finer control over the number display and axis behaviour, you can directly access the + \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if + you want to change the number of automatically generated ticks, call + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount + + Placing a color scale next to the main axis rect works like with any other layout element: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation + In this case we have placed it to the right of the default axis rect, so it wasn't necessary to + call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color + scale can be set with \ref setLabel. + + For optimum appearance (like in the image above), it may be desirable to line up the axis rect and + the borders of the color scale. Use a \ref QCPMarginGroup to achieve this: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup + + Color scales are initialized with a non-zero minimum top and bottom margin (\ref + setMinimumMargins), because vertical color scales are most common and the minimum top/bottom + margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a + horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you + might want to also change the minimum margins accordingly, e.g. setMinimumMargins(QMargins(6, 0, 6, 0)). +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPAxis *QCPColorScale::axis() const + + Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the + appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its + interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref + setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref + QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on + the QCPColorScale or on its QCPAxis. + + If the type of the color scale is changed with \ref setType, the axis returned by this method + will change, too, to either the left, right, bottom or top axis, depending on which type was set. +*/ + +/* end documentation of signals */ +/* start documentation of signals */ + +/*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange); + + This signal is emitted when the data range changes. + + \see setDataRange +*/ + +/*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + + This signal is emitted when the data scale type changes. + + \see setDataScaleType +*/ + +/*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient); + + This signal is emitted when the gradient changes. + + \see setGradient +*/ + +/* end documentation of signals */ + +/*! + Constructs a new QCPColorScale. +*/ +QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) : + QCPLayoutElement(parentPlot), + mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight + mDataScaleType(QCPAxis::stLinear), + mBarWidth(20), + mAxisRect(new QCPColorScaleAxisRectPrivate(this)) +{ + setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used) + setType(QCPAxis::atRight); + setDataRange(QCPRange(0, 6)); +} + +QCPColorScale::~QCPColorScale() +{ + delete mAxisRect; +} + +/* undocumented getter */ +QString QCPColorScale::label() const +{ + if (!mColorAxis) + { + qDebug() << Q_FUNC_INFO << "internal color axis undefined"; + return QString(); + } + + return mColorAxis.data()->label(); +} + +/* undocumented getter */ +bool QCPColorScale::rangeDrag() const +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return false; + } + + return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType); +} + +/* undocumented getter */ +bool QCPColorScale::rangeZoom() const +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return false; + } + + return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType); +} + +/*! + Sets at which side of the color scale the axis is placed, and thus also its orientation. + + Note that after setting \a type to a different value, the axis returned by \ref axis() will + be a different one. The new axis will adopt the following properties from the previous axis: The + range, scale type, log base and label. +*/ +void QCPColorScale::setType(QCPAxis::AxisType type) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + if (mType != type) + { + mType = type; + QCPRange rangeTransfer(0, 6); + double logBaseTransfer = 10; + QString labelTransfer; + // revert some settings on old axis: + if (mColorAxis) + { + rangeTransfer = mColorAxis.data()->range(); + labelTransfer = mColorAxis.data()->label(); + logBaseTransfer = mColorAxis.data()->scaleLogBase(); + mColorAxis.data()->setLabel(QString()); + disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + } + QList allAxisTypes = QList() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop; + foreach (QCPAxis::AxisType atype, allAxisTypes) + { + mAxisRect.data()->axis(atype)->setTicks(atype == mType); + mAxisRect.data()->axis(atype)->setTickLabels(atype== mType); + } + // set new mColorAxis pointer: + mColorAxis = mAxisRect.data()->axis(mType); + // transfer settings to new axis: + mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa) + mColorAxis.data()->setLabel(labelTransfer); + mColorAxis.data()->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here + connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0, + QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0); + } +} + +/*! + Sets the range spanned by the color gradient and that is shown by the axis in the color scale. + + It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is + also equivalent to directly accessing the \ref axis and setting its range with \ref + QCPAxis::setRange. + + \see setDataScaleType, setGradient, rescaleDataRange +*/ +void QCPColorScale::setDataRange(const QCPRange &dataRange) +{ + if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper) + { + mDataRange = dataRange; + if (mColorAxis) + mColorAxis.data()->setRange(mDataRange); + emit dataRangeChanged(mDataRange); + } +} + +/*! + Sets the scale type of the color scale, i.e. whether values are linearly associated with colors + or logarithmically. + + It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is + also equivalent to directly accessing the \ref axis and setting its scale type with \ref + QCPAxis::setScaleType. + + \see setDataRange, setGradient +*/ +void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType) +{ + if (mDataScaleType != scaleType) + { + mDataScaleType = scaleType; + if (mColorAxis) + mColorAxis.data()->setScaleType(mDataScaleType); + if (mDataScaleType == QCPAxis::stLogarithmic) + setDataRange(mDataRange.sanitizedForLogScale()); + emit dataScaleTypeChanged(mDataScaleType); + } +} + +/*! + Sets the color gradient that will be used to represent data values. + + It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps. + + \see setDataRange, setDataScaleType +*/ +void QCPColorScale::setGradient(const QCPColorGradient &gradient) +{ + if (mGradient != gradient) + { + mGradient = gradient; + if (mAxisRect) + mAxisRect.data()->mGradientImageInvalidated = true; + emit gradientChanged(mGradient); + } +} + +/*! + Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on + the internal \ref axis. +*/ +void QCPColorScale::setLabel(const QString &str) +{ + if (!mColorAxis) + { + qDebug() << Q_FUNC_INFO << "internal color axis undefined"; + return; + } + + mColorAxis.data()->setLabel(str); +} + +/*! + Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed + will have. +*/ +void QCPColorScale::setBarWidth(int width) +{ + mBarWidth = width; +} + +/*! + Sets whether the user can drag the data range (\ref setDataRange). + + Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref + QCustomPlot::setInteractions) to allow range dragging. +*/ +void QCPColorScale::setRangeDrag(bool enabled) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + + if (enabled) + mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType)); + else + mAxisRect.data()->setRangeDrag(0); +} + +/*! + Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel. + + Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref + QCustomPlot::setInteractions) to allow range dragging. +*/ +void QCPColorScale::setRangeZoom(bool enabled) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + + if (enabled) + mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType)); + else + mAxisRect.data()->setRangeZoom(0); +} + +/*! + Returns a list of all the color maps associated with this color scale. +*/ +QList QCPColorScale::colorMaps() const +{ + QList result; + for (int i=0; iplottableCount(); ++i) + { + if (QCPColorMap *cm = qobject_cast(mParentPlot->plottable(i))) + if (cm->colorScale() == this) + result.append(cm); + } + return result; +} + +/*! + Changes the data range such that all color maps associated with this color scale are fully mapped + to the gradient in the data dimension. + + \see setDataRange +*/ +void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) +{ + QList maps = colorMaps(); + QCPRange newRange; + bool haveRange = false; + int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace) + if (mDataScaleType == QCPAxis::stLogarithmic) + sign = (mDataRange.upper < 0 ? -1 : 1); + for (int i=0; irealVisibility() && onlyVisibleMaps) + continue; + QCPRange mapRange; + if (maps.at(i)->colorScale() == this) + { + bool currentFoundRange = true; + mapRange = maps.at(i)->data()->dataBounds(); + if (sign == 1) + { + if (mapRange.lower <= 0 && mapRange.upper > 0) + mapRange.lower = mapRange.upper*1e-3; + else if (mapRange.lower <= 0 && mapRange.upper <= 0) + currentFoundRange = false; + } else if (sign == -1) + { + if (mapRange.upper >= 0 && mapRange.lower < 0) + mapRange.upper = mapRange.lower*1e-3; + else if (mapRange.upper >= 0 && mapRange.lower >= 0) + currentFoundRange = false; + } + if (currentFoundRange) + { + if (!haveRange) + newRange = mapRange; + else + newRange.expand(mapRange); + haveRange = true; + } + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (mDataScaleType == QCPAxis::stLinear) + { + newRange.lower = center-mDataRange.size()/2.0; + newRange.upper = center+mDataRange.size()/2.0; + } else // mScaleType == stLogarithmic + { + newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower); + newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower); + } + } + setDataRange(newRange); + } +} + +/* inherits documentation from base class */ +void QCPColorScale::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + + mAxisRect.data()->update(phase); + + switch (phase) + { + case upMargins: + { + if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop) + { + setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom()); + setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom()); + } else + { + setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX); + setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0); + } + break; + } + case upLayout: + { + mAxisRect.data()->setOuterRect(rect()); + break; + } + default: break; + } +} + +/* inherits documentation from base class */ +void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + painter->setAntialiasing(false); +} + +/* inherits documentation from base class */ +void QCPColorScale::mousePressEvent(QMouseEvent *event) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->mousePressEvent(event); +} + +/* inherits documentation from base class */ +void QCPColorScale::mouseMoveEvent(QMouseEvent *event) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->mouseMoveEvent(event); +} + +/* inherits documentation from base class */ +void QCPColorScale::mouseReleaseEvent(QMouseEvent *event) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->mouseReleaseEvent(event); +} + +/* inherits documentation from base class */ +void QCPColorScale::wheelEvent(QWheelEvent *event) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->wheelEvent(event); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorScaleAxisRectPrivate +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorScaleAxisRectPrivate + + \internal + \brief An axis rect subclass for use in a QCPColorScale + + This is a private class and not part of the public QCustomPlot interface. + + It provides the axis rect functionality for the QCPColorScale class. +*/ + + +/*! + Creates a new instance, as a child of \a parentColorScale. +*/ +QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) : + QCPAxisRect(parentColorScale->parentPlot(), true), + mParentColorScale(parentColorScale), + mGradientImageInvalidated(true) +{ + setParentLayerable(parentColorScale); + setMinimumMargins(QMargins(0, 0, 0, 0)); + QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) + { + axis(type)->setVisible(true); + axis(type)->grid()->setVisible(false); + axis(type)->setPadding(0); + connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts))); + connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts))); + } + + connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType))); + connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType))); + connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType))); + connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType))); + + // make layer transfers of color scale transfer to axis rect and axes + // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect: + connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*))); + foreach (QCPAxis::AxisType type, allAxisTypes) + connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*))); +} + +/*! \internal + Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws + it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation. +*/ +void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter) +{ + if (mGradientImageInvalidated) + updateGradientImage(); + + bool mirrorHorz = false; + bool mirrorVert = false; + if (mParentColorScale->mColorAxis) + { + mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop); + mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight); + } + + painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert)); + QCPAxisRect::draw(painter); +} + +/*! \internal + + Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to + generate a gradient image. This gradient image will be used in the \ref draw method. +*/ +void QCPColorScaleAxisRectPrivate::updateGradientImage() +{ + if (rect().isEmpty()) + return; + + int n = mParentColorScale->mGradient.levelCount(); + int w, h; + QVector data(n); + for (int i=0; imType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop) + { + w = n; + h = rect().height(); + mGradientImage = QImage(w, h, QImage::Format_RGB32); + QVector pixels; + for (int y=0; y(mGradientImage.scanLine(y))); + mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n); + for (int y=1; y(mGradientImage.scanLine(y)); + const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1)); + for (int x=0; x allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) + { + if (QCPAxis *senderAxis = qobject_cast(sender())) + if (senderAxis->axisType() == type) + continue; + + if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) + { + if (selectedParts.testFlag(QCPAxis::spAxis)) + axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis); + else + axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis); + } + } +} + +/*! \internal + + This slot is connected to the selectableChanged signals of the four axes in the constructor. It + synchronizes the selectability of the axes. +*/ +void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts) +{ + // synchronize axis base selectability: + QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) + { + if (QCPAxis *senderAxis = qobject_cast(sender())) + if (senderAxis->axisType() == type) + continue; + + if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) + { + if (selectableParts.testFlag(QCPAxis::spAxis)) + axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis); + else + axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPData + \brief Holds the data of one single data point for QCPGraph. + + The container for storing multiple data points is \ref QCPDataMap. + + The stored data is: + \li \a key: coordinate on the key axis of this data point + \li \a value: coordinate on the value axis of this data point + \li \a keyErrorMinus: negative error in the key dimension (for error bars) + \li \a keyErrorPlus: positive error in the key dimension (for error bars) + \li \a valueErrorMinus: negative error in the value dimension (for error bars) + \li \a valueErrorPlus: positive error in the value dimension (for error bars) + + \see QCPDataMap +*/ + +/*! + Constructs a data point with key, value and all errors set to zero. +*/ +QCPData::QCPData() : + key(0), + value(0), + keyErrorPlus(0), + keyErrorMinus(0), + valueErrorPlus(0), + valueErrorMinus(0) +{ +} + +/*! + Constructs a data point with the specified \a key and \a value. All errors are set to zero. +*/ +QCPData::QCPData(double key, double value) : + key(key), + value(value), + keyErrorPlus(0), + keyErrorMinus(0), + valueErrorPlus(0), + valueErrorMinus(0) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPGraph +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPGraph + \brief A plottable representing a graph in a plot. + + \image html QCPGraph.png + + Usually you create new graphs by calling QCustomPlot::addGraph. The resulting instance can be + accessed via QCustomPlot::graph. + + To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can + also access and modify the graph's data via the \ref data method, which returns a pointer to the + internal \ref QCPDataMap. + + Graphs are used to display single-valued data. Single-valued means that there should only be one + data point per unique key coordinate. In other words, the graph can't have \a loops. If you do + want to plot non-single-valued curves, rather use the QCPCurve plottable. + + Gaps in the graph line can be created by adding data points with NaN as value + (qQNaN() or std::numeric_limits::quiet_NaN()) in between the two data points that shall be + separated. + + \section appearance Changing the appearance + + The appearance of the graph is mainly determined by the line style, scatter style, brush and pen + of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen). + + \subsection filling Filling under or between graphs + + QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to + the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill, + just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent. + + By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill + between this graph and another one, call \ref setChannelFillGraph with the other graph as + parameter. + + \see QCustomPlot::addGraph, QCustomPlot::graph +*/ + +/* start of documentation of inline functions */ + +/*! \fn QCPDataMap *QCPGraph::data() const + + Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to + directly manipulate the data, which may be more convenient and faster than using the regular \ref + setData or \ref addData methods, in certain situations. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot + then takes ownership of the graph. + + To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function. +*/ +QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis) +{ + mData = new QCPDataMap; + + setPen(QPen(Qt::blue, 0)); + setErrorPen(QPen(Qt::black)); + setBrush(Qt::NoBrush); + setSelectedPen(QPen(QColor(80, 80, 255), 2.5)); + setSelectedBrush(Qt::NoBrush); + + setLineStyle(lsLine); + setErrorType(etNone); + setErrorBarSize(6); + setErrorBarSkipSymbol(true); + setChannelFillGraph(0); + setAdaptiveSampling(true); +} + +QCPGraph::~QCPGraph() +{ + delete mData; +} + +/*! + Replaces the current data with the provided \a data. + + If \a copy is set to true, data points in \a data will only be copied. if false, the graph + takes ownership of the passed data and replaces the internal data pointer with it. This is + significantly faster than copying for large datasets. + + Alternatively, you can also access and modify the graph's data via the \ref data method, which + returns a pointer to the internal \ref QCPDataMap. +*/ +void QCPGraph::setData(QCPDataMap *data, bool copy) +{ + if (mData == data) + { + qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast(data); + return; + } + if (copy) + { + *mData = *data; + } else + { + delete mData; + mData = data; + } +} + +/*! \overload + + Replaces the current data with the provided points in \a key and \a value pairs. The provided + vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. +*/ +void QCPGraph::setData(const QVector &key, const QVector &value) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + QCPData newData; + for (int i=0; iinsertMulti(newData.key, newData); + } +} + +/*! + Replaces the current data with the provided points in \a key and \a value pairs. Additionally the + symmetrical value error of the data points are set to the values in \a valueError. + For error bars to show appropriately, see \ref setErrorType. + The provided vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + For asymmetrical errors (plus different from minus), see the overloaded version of this function. +*/ +void QCPGraph::setDataValueError(const QVector &key, const QVector &value, const QVector &valueError) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + n = qMin(n, valueError.size()); + QCPData newData; + for (int i=0; iinsertMulti(key[i], newData); + } +} + +/*! + \overload + Replaces the current data with the provided points in \a key and \a value pairs. Additionally the + negative value error of the data points are set to the values in \a valueErrorMinus, the positive + value error to \a valueErrorPlus. + For error bars to show appropriately, see \ref setErrorType. + The provided vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. +*/ +void QCPGraph::setDataValueError(const QVector &key, const QVector &value, const QVector &valueErrorMinus, const QVector &valueErrorPlus) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + n = qMin(n, valueErrorMinus.size()); + n = qMin(n, valueErrorPlus.size()); + QCPData newData; + for (int i=0; iinsertMulti(key[i], newData); + } +} + +/*! + Replaces the current data with the provided points in \a key and \a value pairs. Additionally the + symmetrical key error of the data points are set to the values in \a keyError. + For error bars to show appropriately, see \ref setErrorType. + The provided vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + For asymmetrical errors (plus different from minus), see the overloaded version of this function. +*/ +void QCPGraph::setDataKeyError(const QVector &key, const QVector &value, const QVector &keyError) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + n = qMin(n, keyError.size()); + QCPData newData; + for (int i=0; iinsertMulti(key[i], newData); + } +} + +/*! + \overload + Replaces the current data with the provided points in \a key and \a value pairs. Additionally the + negative key error of the data points are set to the values in \a keyErrorMinus, the positive + key error to \a keyErrorPlus. + For error bars to show appropriately, see \ref setErrorType. + The provided vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. +*/ +void QCPGraph::setDataKeyError(const QVector &key, const QVector &value, const QVector &keyErrorMinus, const QVector &keyErrorPlus) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + n = qMin(n, keyErrorMinus.size()); + n = qMin(n, keyErrorPlus.size()); + QCPData newData; + for (int i=0; iinsertMulti(key[i], newData); + } +} + +/*! + Replaces the current data with the provided points in \a key and \a value pairs. Additionally the + symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError. + For error bars to show appropriately, see \ref setErrorType. + The provided vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + For asymmetrical errors (plus different from minus), see the overloaded version of this function. +*/ +void QCPGraph::setDataBothError(const QVector &key, const QVector &value, const QVector &keyError, const QVector &valueError) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + n = qMin(n, valueError.size()); + n = qMin(n, keyError.size()); + QCPData newData; + for (int i=0; iinsertMulti(key[i], newData); + } +} + +/*! + \overload + Replaces the current data with the provided points in \a key and \a value pairs. Additionally the + negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive + key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus. + For error bars to show appropriately, see \ref setErrorType. + The provided vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. +*/ +void QCPGraph::setDataBothError(const QVector &key, const QVector &value, const QVector &keyErrorMinus, const QVector &keyErrorPlus, const QVector &valueErrorMinus, const QVector &valueErrorPlus) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + n = qMin(n, valueErrorMinus.size()); + n = qMin(n, valueErrorPlus.size()); + n = qMin(n, keyErrorMinus.size()); + n = qMin(n, keyErrorPlus.size()); + QCPData newData; + for (int i=0; iinsertMulti(key[i], newData); + } +} + + +/*! + Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to + \ref lsNone and \ref setScatterStyle to the desired scatter style. + + \see setScatterStyle +*/ +void QCPGraph::setLineStyle(LineStyle ls) +{ + mLineStyle = ls; +} + +/*! + Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points + are drawn (e.g. for line-only-plots with appropriate line style). + + \see QCPScatterStyle, setLineStyle +*/ +void QCPGraph::setScatterStyle(const QCPScatterStyle &style) +{ + mScatterStyle = style; +} + +/*! + Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data + point. If you set \a errorType to something other than \ref etNone, make sure to actually pass + error data via the specific setData functions along with the data points (e.g. \ref + setDataValueError, \ref setDataKeyError, \ref setDataBothError). + + \see ErrorType +*/ +void QCPGraph::setErrorType(ErrorType errorType) +{ + mErrorType = errorType; +} + +/*! + Sets the pen with which the error bars will be drawn. + \see setErrorBarSize, setErrorType +*/ +void QCPGraph::setErrorPen(const QPen &pen) +{ + mErrorPen = pen; +} + +/*! + Sets the width of the handles at both ends of an error bar in pixels. +*/ +void QCPGraph::setErrorBarSize(double size) +{ + mErrorBarSize = size; +} + +/*! + If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but + leave some free space around the symbol. + + This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to determine the size + of the area to leave blank. So when drawing Pixmaps as scatter points (\ref + QCPScatterStyle::ssPixmap), the scatter size must be set manually to a value corresponding to the + size of the Pixmap, if the error bars should leave gaps to its boundaries. + + \ref setErrorType, setErrorBarSize, setScatterStyle +*/ +void QCPGraph::setErrorBarSkipSymbol(bool enabled) +{ + mErrorBarSkipSymbol = enabled; +} + +/*! + Sets the target graph for filling the area between this graph and \a targetGraph with the current + brush (\ref setBrush). + + When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To + disable any filling, set the brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph) +{ + // prevent setting channel target to this graph itself: + if (targetGraph == this) + { + qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself"; + mChannelFillGraph = 0; + return; + } + // prevent setting channel target to a graph not in the plot: + if (targetGraph && targetGraph->mParentPlot != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "targetGraph not in same plot"; + mChannelFillGraph = 0; + return; + } + + mChannelFillGraph = targetGraph; +} + +/*! + Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive + sampling technique can drastically improve the replot performance for graphs with a larger number + of points (e.g. above 10,000), without notably changing the appearance of the graph. + + By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive + sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no + disadvantage in almost all cases. + + \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling" + + As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are + reproduced reliably, as well as the overall shape of the data set. The replot time reduces + dramatically though. This allows QCustomPlot to display large amounts of data in realtime. + + \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling" + + Care must be taken when using high-density scatter plots in combination with adaptive sampling. + The adaptive sampling algorithm treats scatter plots more carefully than line plots which still + gives a significant reduction of replot times, but not quite as much as for line plots. This is + because scatter plots inherently need more data points to be preserved in order to still resemble + the original, non-adaptive-sampling plot. As shown above, the results still aren't quite + identical, as banding occurs for the outer data points. This is in fact intentional, such that + the boundaries of the data cloud stay visible to the viewer. How strong the banding appears, + depends on the point density, i.e. the number of points in the plot. + + For some situations with scatter plots it might thus be desirable to manually turn adaptive + sampling off. For example, when saving the plot to disk. This can be achieved by setting \a + enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled + back to true afterwards. +*/ +void QCPGraph::setAdaptiveSampling(bool enabled) +{ + mAdaptiveSampling = enabled; +} + +/*! + Adds the provided data points in \a dataMap to the current data. + + Alternatively, you can also access and modify the graph's data via the \ref data method, which + returns a pointer to the internal \ref QCPDataMap. + + \see removeData +*/ +void QCPGraph::addData(const QCPDataMap &dataMap) +{ + mData->unite(dataMap); +} + +/*! \overload + Adds the provided single data point in \a data to the current data. + + Alternatively, you can also access and modify the graph's data via the \ref data method, which + returns a pointer to the internal \ref QCPDataMap. + + \see removeData +*/ +void QCPGraph::addData(const QCPData &data) +{ + mData->insertMulti(data.key, data); +} + +/*! \overload + Adds the provided single data point as \a key and \a value pair to the current data. + + Alternatively, you can also access and modify the graph's data via the \ref data method, which + returns a pointer to the internal \ref QCPDataMap. + + \see removeData +*/ +void QCPGraph::addData(double key, double value) +{ + QCPData newData; + newData.key = key; + newData.value = value; + mData->insertMulti(newData.key, newData); +} + +/*! \overload + Adds the provided data points as \a key and \a value pairs to the current data. + + Alternatively, you can also access and modify the graph's data via the \ref data method, which + returns a pointer to the internal \ref QCPDataMap. + + \see removeData +*/ +void QCPGraph::addData(const QVector &keys, const QVector &values) +{ + int n = qMin(keys.size(), values.size()); + QCPData newData; + for (int i=0; iinsertMulti(newData.key, newData); + } +} + +/*! + Removes all data points with keys smaller than \a key. + \see addData, clearData +*/ +void QCPGraph::removeDataBefore(double key) +{ + QCPDataMap::iterator it = mData->begin(); + while (it != mData->end() && it.key() < key) + it = mData->erase(it); +} + +/*! + Removes all data points with keys greater than \a key. + \see addData, clearData +*/ +void QCPGraph::removeDataAfter(double key) +{ + if (mData->isEmpty()) return; + QCPDataMap::iterator it = mData->upperBound(key); + while (it != mData->end()) + it = mData->erase(it); +} + +/*! + Removes all data points with keys between \a fromKey and \a toKey. + if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove + a single data point with known key, use \ref removeData(double key). + + \see addData, clearData +*/ +void QCPGraph::removeData(double fromKey, double toKey) +{ + if (fromKey >= toKey || mData->isEmpty()) return; + QCPDataMap::iterator it = mData->upperBound(fromKey); + QCPDataMap::iterator itEnd = mData->upperBound(toKey); + while (it != itEnd) + it = mData->erase(it); +} + +/*! \overload + + Removes a single data point at \a key. If the position is not known with absolute precision, + consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around + the suspected position, depeding on the precision with which the key is known. + + \see addData, clearData +*/ +void QCPGraph::removeData(double key) +{ + mData->remove(key); +} + +/*! + Removes all data points. + \see removeData, removeDataAfter, removeDataBefore +*/ +void QCPGraph::clearData() +{ + mData->clear(); +} + +/* inherits documentation from base class */ +double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if ((onlySelectable && !mSelectable) || mData->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + return pointDistance(pos); + else + return -1; +} + +/*! \overload + + Allows to define whether error bars are taken into consideration when determining the new axis + range. + + \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const +{ + rescaleKeyAxis(onlyEnlarge, includeErrorBars); + rescaleValueAxis(onlyEnlarge, includeErrorBars); +} + +/*! \overload + + Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration + when determining the new axis range. + + \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis +*/ +void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const +{ + // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change + // that getKeyRange is passed the includeErrorBars value. + if (mData->isEmpty()) return; + + QCPAxis *keyAxis = mKeyAxis.data(); + if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + + SignDomain signDomain = sdBoth; + if (keyAxis->scaleType() == QCPAxis::stLogarithmic) + signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive); + + bool foundRange; + QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars); + + if (foundRange) + { + if (onlyEnlarge) + { + if (keyAxis->range().lower < newRange.lower) + newRange.lower = keyAxis->range().lower; + if (keyAxis->range().upper > newRange.upper) + newRange.upper = keyAxis->range().upper; + } + keyAxis->setRange(newRange); + } +} + +/*! \overload + + Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration + when determining the new axis range. + + \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis +*/ +void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const +{ + // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change + // is that getValueRange is passed the includeErrorBars value. + if (mData->isEmpty()) return; + + QCPAxis *valueAxis = mValueAxis.data(); + if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; } + + SignDomain signDomain = sdBoth; + if (valueAxis->scaleType() == QCPAxis::stLogarithmic) + signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive); + + bool foundRange; + QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars); + + if (foundRange) + { + if (onlyEnlarge) + { + if (valueAxis->range().lower < newRange.lower) + newRange.lower = valueAxis->range().lower; + if (valueAxis->range().upper > newRange.upper) + newRange.upper = valueAxis->range().upper; + } + valueAxis->setRange(newRange); + } +} + +/* inherits documentation from base class */ +void QCPGraph::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return; + if (mLineStyle == lsNone && mScatterStyle.isNone()) return; + + // allocate line and (if necessary) point vectors: + QVector *lineData = new QVector; + QVector *scatterData = 0; + if (!mScatterStyle.isNone()) + scatterData = new QVector; + + // fill vectors with data appropriate to plot style: + getPlotData(lineData, scatterData); + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + QCPDataMap::const_iterator it; + for (it = mData->constBegin(); it != mData->constEnd(); ++it) + { + if (QCP::isInvalidData(it.value().key, it.value().value) || + QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) || + QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus)) + qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name(); + } +#endif + + // draw fill of graph: + if (mLineStyle != lsNone) + drawFill(painter, lineData); + + // draw line: + if (mLineStyle == lsImpulse) + drawImpulsePlot(painter, lineData); + else if (mLineStyle != lsNone) + drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot + + // draw scatters: + if (scatterData) + drawScatterPlot(painter, scatterData); + + // free allocated line and point vectors: + delete lineData; + if (scatterData) + delete scatterData; +} + +/* inherits documentation from base class */ +void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw fill: + if (mBrush.style() != Qt::NoBrush) + { + applyFillAntialiasingHint(painter); + painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush); + } + // draw line vertically centered: + if (mLineStyle != lsNone) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens + } + // draw scatter symbol: + if (!mScatterStyle.isNone()) + { + applyScattersAntialiasingHint(painter); + // scale scatter pixmap if it's too large to fit in legend icon rect: + if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height())) + { + QCPScatterStyle scaledStyle(mScatterStyle); + scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + scaledStyle.applyTo(painter, mPen); + scaledStyle.drawShape(painter, QRectF(rect).center()); + } else + { + mScatterStyle.applyTo(painter, mPen); + mScatterStyle.drawShape(painter, QRectF(rect).center()); + } + } +} + +/*! \internal + + This function branches out to the line style specific "get(...)PlotData" functions, according to + the line style of the graph. + + \a lineData will be filled with raw points that will be drawn with the according draw functions, + e.g. \ref drawLinePlot and \ref drawImpulsePlot. These aren't necessarily the original data + points, since for step plots for example, additional points are needed for drawing lines that + make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left + untouched. + + \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the + scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is + \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped. + + \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData, + getStepCenterPlotData, getImpulsePlotData +*/ +void QCPGraph::getPlotData(QVector *lineData, QVector *scatterData) const +{ + switch(mLineStyle) + { + case lsNone: getScatterPlotData(scatterData); break; + case lsLine: getLinePlotData(lineData, scatterData); break; + case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break; + case lsStepRight: getStepRightPlotData(lineData, scatterData); break; + case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break; + case lsImpulse: getImpulsePlotData(lineData, scatterData); break; + } +} + +/*! \internal + + If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone, + this function serves at providing the visible data points in \a scatterData, so the \ref + drawScatterPlot function can draw the scatter points accordingly. + + If line style is not \ref lsNone, this function is not called and the data for the scatter points + are (if needed) calculated inside the corresponding other "get(...)PlotData" functions. + + \see drawScatterPlot +*/ +void QCPGraph::getScatterPlotData(QVector *scatterData) const +{ + getPreparedData(0, scatterData); +} + +/*! \internal + + Places the raw data points needed for a normal linearly connected graph in \a linePixelData. + + As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter) + points that are visible for drawing scatter points, if necessary. If drawing scatter points is + disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a + scatterData, and the function will skip filling the vector. + + \see drawLinePlot +*/ +void QCPGraph::getLinePlotData(QVector *linePixelData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; } + + QVector lineData; + getPreparedData(&lineData, scatterData); + linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill + linePixelData->resize(lineData.size()); + + // transform lineData points to pixels: + if (keyAxis->orientation() == Qt::Vertical) + { + for (int i=0; icoordToPixel(lineData.at(i).value)); + (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key)); + } + } else // key axis is horizontal + { + for (int i=0; icoordToPixel(lineData.at(i).key)); + (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value)); + } + } +} + +/*! + \internal + Places the raw data points needed for a step plot with left oriented steps in \a lineData. + + As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter) + points that are visible for drawing scatter points, if necessary. If drawing scatter points is + disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a + scatterData, and the function will skip filling the vector. + + \see drawLinePlot +*/ +void QCPGraph::getStepLeftPlotData(QVector *linePixelData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; } + + QVector lineData; + getPreparedData(&lineData, scatterData); + linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill + linePixelData->resize(lineData.size()*2); + + // calculate steps from lineData and transform to pixel coordinates: + if (keyAxis->orientation() == Qt::Vertical) + { + double lastValue = valueAxis->coordToPixel(lineData.first().value); + double key; + for (int i=0; icoordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+0].setX(lastValue); + (*linePixelData)[i*2+0].setY(key); + lastValue = valueAxis->coordToPixel(lineData.at(i).value); + (*linePixelData)[i*2+1].setX(lastValue); + (*linePixelData)[i*2+1].setY(key); + } + } else // key axis is horizontal + { + double lastValue = valueAxis->coordToPixel(lineData.first().value); + double key; + for (int i=0; icoordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+0].setX(key); + (*linePixelData)[i*2+0].setY(lastValue); + lastValue = valueAxis->coordToPixel(lineData.at(i).value); + (*linePixelData)[i*2+1].setX(key); + (*linePixelData)[i*2+1].setY(lastValue); + } + } +} + +/*! + \internal + Places the raw data points needed for a step plot with right oriented steps in \a lineData. + + As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter) + points that are visible for drawing scatter points, if necessary. If drawing scatter points is + disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a + scatterData, and the function will skip filling the vector. + + \see drawLinePlot +*/ +void QCPGraph::getStepRightPlotData(QVector *linePixelData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; } + + QVector lineData; + getPreparedData(&lineData, scatterData); + linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill + linePixelData->resize(lineData.size()*2); + + // calculate steps from lineData and transform to pixel coordinates: + if (keyAxis->orientation() == Qt::Vertical) + { + double lastKey = keyAxis->coordToPixel(lineData.first().key); + double value; + for (int i=0; icoordToPixel(lineData.at(i).value); + (*linePixelData)[i*2+0].setX(value); + (*linePixelData)[i*2+0].setY(lastKey); + lastKey = keyAxis->coordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+1].setX(value); + (*linePixelData)[i*2+1].setY(lastKey); + } + } else // key axis is horizontal + { + double lastKey = keyAxis->coordToPixel(lineData.first().key); + double value; + for (int i=0; icoordToPixel(lineData.at(i).value); + (*linePixelData)[i*2+0].setX(lastKey); + (*linePixelData)[i*2+0].setY(value); + lastKey = keyAxis->coordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+1].setX(lastKey); + (*linePixelData)[i*2+1].setY(value); + } + } +} + +/*! + \internal + Places the raw data points needed for a step plot with centered steps in \a lineData. + + As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter) + points that are visible for drawing scatter points, if necessary. If drawing scatter points is + disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a + scatterData, and the function will skip filling the vector. + + \see drawLinePlot +*/ +void QCPGraph::getStepCenterPlotData(QVector *linePixelData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; } + + QVector lineData; + getPreparedData(&lineData, scatterData); + linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill + linePixelData->resize(lineData.size()*2); + // calculate steps from lineData and transform to pixel coordinates: + if (keyAxis->orientation() == Qt::Vertical) + { + double lastKey = keyAxis->coordToPixel(lineData.first().key); + double lastValue = valueAxis->coordToPixel(lineData.first().value); + double key; + (*linePixelData)[0].setX(lastValue); + (*linePixelData)[0].setY(lastKey); + for (int i=1; icoordToPixel(lineData.at(i).key)+lastKey)*0.5; + (*linePixelData)[i*2-1].setX(lastValue); + (*linePixelData)[i*2-1].setY(key); + lastValue = valueAxis->coordToPixel(lineData.at(i).value); + lastKey = keyAxis->coordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+0].setX(lastValue); + (*linePixelData)[i*2+0].setY(key); + } + (*linePixelData)[lineData.size()*2-1].setX(lastValue); + (*linePixelData)[lineData.size()*2-1].setY(lastKey); + } else // key axis is horizontal + { + double lastKey = keyAxis->coordToPixel(lineData.first().key); + double lastValue = valueAxis->coordToPixel(lineData.first().value); + double key; + (*linePixelData)[0].setX(lastKey); + (*linePixelData)[0].setY(lastValue); + for (int i=1; icoordToPixel(lineData.at(i).key)+lastKey)*0.5; + (*linePixelData)[i*2-1].setX(key); + (*linePixelData)[i*2-1].setY(lastValue); + lastValue = valueAxis->coordToPixel(lineData.at(i).value); + lastKey = keyAxis->coordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+0].setX(key); + (*linePixelData)[i*2+0].setY(lastValue); + } + (*linePixelData)[lineData.size()*2-1].setX(lastKey); + (*linePixelData)[lineData.size()*2-1].setY(lastValue); + } + +} + +/*! + \internal + Places the raw data points needed for an impulse plot in \a lineData. + + As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter) + points that are visible for drawing scatter points, if necessary. If drawing scatter points is + disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a + scatterData, and the function will skip filling the vector. + + \see drawImpulsePlot +*/ +void QCPGraph::getImpulsePlotData(QVector *linePixelData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; } + + QVector lineData; + getPreparedData(&lineData, scatterData); + linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill + + // transform lineData points to pixels: + if (keyAxis->orientation() == Qt::Vertical) + { + double zeroPointX = valueAxis->coordToPixel(0); + double key; + for (int i=0; icoordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+0].setX(zeroPointX); + (*linePixelData)[i*2+0].setY(key); + (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value)); + (*linePixelData)[i*2+1].setY(key); + } + } else // key axis is horizontal + { + double zeroPointY = valueAxis->coordToPixel(0); + double key; + for (int i=0; icoordToPixel(lineData.at(i).key); + (*linePixelData)[i*2+0].setX(key); + (*linePixelData)[i*2+0].setY(zeroPointY); + (*linePixelData)[i*2+1].setX(key); + (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value)); + } + } +} + +/*! \internal + + Draws the fill of the graph with the specified brush. + + If the fill is a normal fill towards the zero-value-line, only the \a lineData is required (and + two extra points at the zero-value-line, which are added by \ref addFillBasePoints and removed by + \ref removeFillBasePoints after the fill drawing is done). + + If the fill is a channel fill between this QCPGraph and another QCPGraph (mChannelFillGraph), the + more complex polygon is calculated with the \ref getChannelFillPolygon function. + + \see drawLinePlot +*/ +void QCPGraph::drawFill(QCPPainter *painter, QVector *lineData) const +{ + if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot + if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return; + + applyFillAntialiasingHint(painter); + if (!mChannelFillGraph) + { + // draw base fill under graph, fill goes all the way to the zero-value-line: + addFillBasePoints(lineData); + painter->setPen(Qt::NoPen); + painter->setBrush(mainBrush()); + painter->drawPolygon(QPolygonF(*lineData)); + removeFillBasePoints(lineData); + } else + { + // draw channel fill between this graph and mChannelFillGraph: + painter->setPen(Qt::NoPen); + painter->setBrush(mainBrush()); + painter->drawPolygon(getChannelFillPolygon(lineData)); + } +} + +/*! \internal + + Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent + of the line style and are always drawn if the scatter style's shape is not \ref + QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData" + functions, together with the (line style dependent) line data. + + \see drawLinePlot, drawImpulsePlot +*/ +void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + // draw error bars: + if (mErrorType != etNone) + { + applyErrorBarsAntialiasingHint(painter); + painter->setPen(mErrorPen); + if (keyAxis->orientation() == Qt::Vertical) + { + for (int i=0; isize(); ++i) + drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i)); + } else + { + for (int i=0; isize(); ++i) + drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i)); + } + } + + // draw scatter point symbols: + applyScattersAntialiasingHint(painter); + mScatterStyle.applyTo(painter, mPen); + if (keyAxis->orientation() == Qt::Vertical) + { + for (int i=0; isize(); ++i) + if (!qIsNaN(scatterData->at(i).value)) + mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key)); + } else + { + for (int i=0; isize(); ++i) + if (!qIsNaN(scatterData->at(i).value)) + mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value)); + } +} + +/*! \internal + + Draws line graphs from the provided data. It connects all points in \a lineData, which was + created by one of the "get(...)PlotData" functions for line styles that require simple line + connections between the point vector they create. These are for example \ref getLinePlotData, + \ref getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData. + + \see drawScatterPlot, drawImpulsePlot +*/ +void QCPGraph::drawLinePlot(QCPPainter *painter, QVector *lineData) const +{ + // draw line of graph: + if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mainPen()); + painter->setBrush(Qt::NoBrush); + + /* Draws polyline in batches, currently not used: + int p = 0; + while (p < lineData->size()) + { + int batch = qMin(25, lineData->size()-p); + if (p != 0) + { + ++batch; + --p; // to draw the connection lines between two batches + } + painter->drawPolyline(lineData->constData()+p, batch); + p += batch; + } + */ + + // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: + if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && + painter->pen().style() == Qt::SolidLine && + !painter->modes().testFlag(QCPPainter::pmVectorized) && + !painter->modes().testFlag(QCPPainter::pmNoCaching)) + { + int i = 0; + bool lastIsNan = false; + const int lineDataSize = lineData->size(); + while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect + while (i < lineDataSize) + { + if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line + { + if (!lastIsNan) + painter->drawLine(lineData->at(i-1), lineData->at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; + ++i; + } + } else + { + int segmentStart = 0; + int i = 0; + const int lineDataSize = lineData->size(); + while (i < lineDataSize) + { + if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()) || qIsInf(lineData->at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block + { + painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point + segmentStart = i+1; + } + ++i; + } + // draw last segment: + painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); + } + } +} + +/*! \internal + + Draws impulses from the provided data, i.e. it connects all line pairs in \a lineData, which was + created by \ref getImpulsePlotData. + + \see drawScatterPlot, drawLinePlot +*/ +void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector *lineData) const +{ + // draw impulses: + if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + QPen pen = mainPen(); + pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawLines(*lineData); + } +} + +/*! \internal + + Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into + consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point + densities. + + 0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't + needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a + scatterData should be 0 to prevent unnecessary calculations. + + This method is used by the various "get(...)PlotData" methods to get the basic working set of data. +*/ +void QCPGraph::getPreparedData(QVector *lineData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + // get visible data range: + QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point + getVisibleDataBounds(lower, upper); + if (lower == mData->constEnd() || upper == mData->constEnd()) + return; + + // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling: + int maxCount = std::numeric_limits::max(); + if (mAdaptiveSampling) + { + int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key())); + maxCount = 2*keyPixelSpan+2; + } + int dataCount = countDataInBounds(lower, upper, maxCount); + + if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average + { + if (lineData) + { + QCPDataMap::const_iterator it = lower; + QCPDataMap::const_iterator upperEnd = upper+1; + double minValue = it.value().value; + double maxValue = it.value().value; + QCPDataMap::const_iterator currentIntervalFirstPoint = it; + int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction + int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey + double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound)); + double lastIntervalEndKey = currentIntervalStartKey; + double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates + bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) + int intervalDataCount = 1; + ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect + while (it != upperEnd) + { + if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary + { + if (it.value().value < minValue) + minValue = it.value().value; + else if (it.value().value > maxValue) + maxValue = it.value().value; + ++intervalDataCount; + } else // new pixel interval started + { + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster + { + if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); + if (it.key() > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value)); + } else + lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value)); + lastIntervalEndKey = (it-1).value().key; + minValue = it.value().value; + maxValue = it.value().value; + currentIntervalFirstPoint = it; + currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound)); + if (keyEpsilonVariable) + keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); + intervalDataCount = 1; + } + ++it; + } + // handle last interval: + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster + { + if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); + } else + lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value)); + } + + if (scatterData) + { + double valueMaxRange = valueAxis->range().upper; + double valueMinRange = valueAxis->range().lower; + QCPDataMap::const_iterator it = lower; + QCPDataMap::const_iterator upperEnd = upper+1; + double minValue = it.value().value; + double maxValue = it.value().value; + QCPDataMap::const_iterator minValueIt = it; + QCPDataMap::const_iterator maxValueIt = it; + QCPDataMap::const_iterator currentIntervalStart = it; + int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction + int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey + double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound)); + double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates + bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) + int intervalDataCount = 1; + ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect + while (it != upperEnd) + { + if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary + { + if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange) + { + minValue = it.value().value; + minValueIt = it; + } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange) + { + maxValue = it.value().value; + maxValueIt = it; + } + ++intervalDataCount; + } else // new pixel started + { + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them + { + // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): + double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); + int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average + QCPDataMap::const_iterator intervalIt = currentIntervalStart; + int c = 0; + while (intervalIt != it) + { + if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange) + scatterData->append(intervalIt.value()); + ++c; + ++intervalIt; + } + } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange) + scatterData->append(currentIntervalStart.value()); + minValue = it.value().value; + maxValue = it.value().value; + currentIntervalStart = it; + currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound)); + if (keyEpsilonVariable) + keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); + intervalDataCount = 1; + } + ++it; + } + // handle last interval: + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them + { + // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): + double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); + int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average + QCPDataMap::const_iterator intervalIt = currentIntervalStart; + int c = 0; + while (intervalIt != it) + { + if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange) + scatterData->append(intervalIt.value()); + ++c; + ++intervalIt; + } + } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange) + scatterData->append(currentIntervalStart.value()); + } + } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters + { + QVector *dataVector = 0; + if (lineData) + dataVector = lineData; + else if (scatterData) + dataVector = scatterData; + if (dataVector) + { + QCPDataMap::const_iterator it = lower; + QCPDataMap::const_iterator upperEnd = upper+1; + dataVector->reserve(dataCount+2); // +2 for possible fill end points + while (it != upperEnd) + { + dataVector->append(it.value()); + ++it; + } + } + if (lineData && scatterData) + *scatterData = *dataVector; + } +} + +/*! \internal + + called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data + point. \a x and \a y pixel positions of the data point are passed since they are already known in + pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a + data is therefore only used for the errors, not key and value. +*/ +void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const +{ + if (qIsNaN(data.value)) + return; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + double a, b; // positions of error bar bounds in pixels + double barWidthHalf = mErrorBarSize*0.5; + double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true + + if (keyAxis->orientation() == Qt::Vertical) + { + // draw key error vertically and value error horizontally + if (mErrorType == etKey || mErrorType == etBoth) + { + a = keyAxis->coordToPixel(data.key-data.keyErrorMinus); + b = keyAxis->coordToPixel(data.key+data.keyErrorPlus); + if (keyAxis->rangeReversed()) + qSwap(a,b); + // draw spine: + if (mErrorBarSkipSymbol) + { + if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin + painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin)); + if (y-b > skipSymbolMargin) + painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b)); + } else + painter->drawLine(QLineF(x, a, x, b)); + // draw handles: + painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a)); + painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b)); + } + if (mErrorType == etValue || mErrorType == etBoth) + { + a = valueAxis->coordToPixel(data.value-data.valueErrorMinus); + b = valueAxis->coordToPixel(data.value+data.valueErrorPlus); + if (valueAxis->rangeReversed()) + qSwap(a,b); + // draw spine: + if (mErrorBarSkipSymbol) + { + if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin + painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y)); + if (b-x > skipSymbolMargin) + painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y)); + } else + painter->drawLine(QLineF(a, y, b, y)); + // draw handles: + painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf)); + painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf)); + } + } else // mKeyAxis->orientation() is Qt::Horizontal + { + // draw value error vertically and key error horizontally + if (mErrorType == etKey || mErrorType == etBoth) + { + a = keyAxis->coordToPixel(data.key-data.keyErrorMinus); + b = keyAxis->coordToPixel(data.key+data.keyErrorPlus); + if (keyAxis->rangeReversed()) + qSwap(a,b); + // draw spine: + if (mErrorBarSkipSymbol) + { + if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin + painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y)); + if (b-x > skipSymbolMargin) + painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y)); + } else + painter->drawLine(QLineF(a, y, b, y)); + // draw handles: + painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf)); + painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf)); + } + if (mErrorType == etValue || mErrorType == etBoth) + { + a = valueAxis->coordToPixel(data.value-data.valueErrorMinus); + b = valueAxis->coordToPixel(data.value+data.valueErrorPlus); + if (valueAxis->rangeReversed()) + qSwap(a,b); + // draw spine: + if (mErrorBarSkipSymbol) + { + if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin + painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin)); + if (y-b > skipSymbolMargin) + painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b)); + } else + painter->drawLine(QLineF(x, a, x, b)); + // draw handles: + painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a)); + painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b)); + } + } +} + +/*! \internal + + called by \ref getPreparedData to determine which data (key) range is visible at the current key + axis range setting, so only that needs to be processed. + + \a lower returns an iterator to the lowest data point that needs to be taken into account when + plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a + lower may still be just outside the visible range. + + \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie + just outside of the visible range. + + if the graph contains no data, both \a lower and \a upper point to constEnd. +*/ +void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const +{ + if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + if (mData->isEmpty()) + { + lower = mData->constEnd(); + upper = mData->constEnd(); + return; + } + + // get visible data range as QMap iterators + QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower); + QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper); + bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range + bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range + + lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn + upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn +} + +/*! \internal + + Counts the number of data points between \a lower and \a upper (including them), up to a maximum + of \a maxCount. + + This function is used by \ref getPreparedData to determine whether adaptive sampling shall be + used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points + only needs to be done until \a maxCount is reached, which should be set to the number of data + points at which adaptive sampling sets in. +*/ +int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const +{ + if (upper == mData->constEnd() && lower == mData->constEnd()) + return 0; + QCPDataMap::const_iterator it = lower; + int count = 1; + while (it != upper && count < maxCount) + { + ++it; + ++count; + } + return count; +} + +/*! \internal + + The line data vector generated by e.g. getLinePlotData contains only the line that connects the + data points. If the graph needs to be filled, two additional points need to be added at the + value-zero-line in the lower and upper key positions of the graph. This function calculates these + points and adds them to the end of \a lineData. Since the fill is typically drawn before the line + stroke, these added points need to be removed again after the fill is done, with the + removeFillBasePoints function. + + The expanding of \a lineData by two points will not cause unnecessary memory reallocations, + because the data vector generation functions (getLinePlotData etc.) reserve two extra points when + they allocate memory for \a lineData. + + \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint +*/ +void QCPGraph::addFillBasePoints(QVector *lineData) const +{ + if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; } + if (lineData->isEmpty()) return; + + // append points that close the polygon fill at the key axis: + if (mKeyAxis.data()->orientation() == Qt::Vertical) + { + *lineData << upperFillBasePoint(lineData->last().y()); + *lineData << lowerFillBasePoint(lineData->first().y()); + } else + { + *lineData << upperFillBasePoint(lineData->last().x()); + *lineData << lowerFillBasePoint(lineData->first().x()); + } +} + +/*! \internal + + removes the two points from \a lineData that were added by \ref addFillBasePoints. + + \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint +*/ +void QCPGraph::removeFillBasePoints(QVector *lineData) const +{ + if (!lineData) { qDebug() << Q_FUNC_INFO << "passed null as lineData"; return; } + if (lineData->isEmpty()) return; + + lineData->remove(lineData->size()-2, 2); +} + +/*! \internal + + called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon + on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale + case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative + infinity. So this case is handled separately by just closing the fill polygon on the axis which + lies in the direction towards the zero value. + + \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key + axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned + point, respectively. + + \see upperFillBasePoint, addFillBasePoints +*/ +QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } + + QPointF point; + if (valueAxis->scaleType() == QCPAxis::stLinear) + { + if (keyAxis->axisType() == QCPAxis::atLeft) + { + point.setX(valueAxis->coordToPixel(0)); + point.setY(lowerKey); + } else if (keyAxis->axisType() == QCPAxis::atRight) + { + point.setX(valueAxis->coordToPixel(0)); + point.setY(lowerKey); + } else if (keyAxis->axisType() == QCPAxis::atTop) + { + point.setX(lowerKey); + point.setY(valueAxis->coordToPixel(0)); + } else if (keyAxis->axisType() == QCPAxis::atBottom) + { + point.setX(lowerKey); + point.setY(valueAxis->coordToPixel(0)); + } + } else // valueAxis->mScaleType == QCPAxis::stLogarithmic + { + // In logarithmic scaling we can't just draw to value zero so we just fill all the way + // to the axis which is in the direction towards zero + if (keyAxis->orientation() == Qt::Vertical) + { + if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || + (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis + point.setX(keyAxis->axisRect()->right()); + else + point.setX(keyAxis->axisRect()->left()); + point.setY(lowerKey); + } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom) + { + point.setX(lowerKey); + if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || + (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis + point.setY(keyAxis->axisRect()->top()); + else + point.setY(keyAxis->axisRect()->bottom()); + } + } + return point; +} + +/*! \internal + + called by \ref addFillBasePoints to conveniently assign the point which closes the fill + polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis + scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or + negative infinity. So this case is handled separately by just closing the fill polygon on the + axis which lies in the direction towards the zero value. + + \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key + axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned + point, respectively. + + \see lowerFillBasePoint, addFillBasePoints +*/ +QPointF QCPGraph::upperFillBasePoint(double upperKey) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } + + QPointF point; + if (valueAxis->scaleType() == QCPAxis::stLinear) + { + if (keyAxis->axisType() == QCPAxis::atLeft) + { + point.setX(valueAxis->coordToPixel(0)); + point.setY(upperKey); + } else if (keyAxis->axisType() == QCPAxis::atRight) + { + point.setX(valueAxis->coordToPixel(0)); + point.setY(upperKey); + } else if (keyAxis->axisType() == QCPAxis::atTop) + { + point.setX(upperKey); + point.setY(valueAxis->coordToPixel(0)); + } else if (keyAxis->axisType() == QCPAxis::atBottom) + { + point.setX(upperKey); + point.setY(valueAxis->coordToPixel(0)); + } + } else // valueAxis->mScaleType == QCPAxis::stLogarithmic + { + // In logarithmic scaling we can't just draw to value 0 so we just fill all the way + // to the axis which is in the direction towards 0 + if (keyAxis->orientation() == Qt::Vertical) + { + if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || + (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis + point.setX(keyAxis->axisRect()->right()); + else + point.setX(keyAxis->axisRect()->left()); + point.setY(upperKey); + } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom) + { + point.setX(upperKey); + if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || + (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis + point.setY(keyAxis->axisRect()->top()); + else + point.setY(keyAxis->axisRect()->bottom()); + } + } + return point; +} + +/*! \internal + + Generates the polygon needed for drawing channel fills between this graph (data passed via \a + lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref + getPlotData function). May return an empty polygon if the key ranges have no overlap or fill + target graph and this graph don't have same orientation (i.e. both key axes horizontal or both + key axes vertical). For increased performance (due to implicit sharing), keep the returned + QPolygonF const. +*/ +const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *lineData) const +{ + if (!mChannelFillGraph) + return QPolygonF(); + + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); } + if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); } + + if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation()) + return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis) + + if (lineData->isEmpty()) return QPolygonF(); + QVector otherData; + mChannelFillGraph.data()->getPlotData(&otherData, 0); + if (otherData.isEmpty()) return QPolygonF(); + QVector thisData; + thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function + for (int i=0; isize(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve() + thisData << lineData->at(i); + + // pointers to be able to swap them, depending which data range needs cropping: + QVector *staticData = &thisData; + QVector *croppedData = &otherData; + + // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType): + if (keyAxis->orientation() == Qt::Horizontal) + { + // x is key + // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys: + if (staticData->first().x() > staticData->last().x()) + { + int size = staticData->size(); + for (int i=0; ifirst().x() > croppedData->last().x()) + { + int size = croppedData->size(); + for (int i=0; ifirst().x() < croppedData->first().x()) // other one must be cropped + qSwap(staticData, croppedData); + int lowBound = findIndexBelowX(croppedData, staticData->first().x()); + if (lowBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(0, lowBound); + // set lowest point of cropped data to fit exactly key position of first static data + // point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + double slope; + if (croppedData->at(1).x()-croppedData->at(0).x() != 0) + slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x()); + else + slope = 0; + (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x())); + (*croppedData)[0].setX(staticData->first().x()); + + // crop upper bound: + if (staticData->last().x() > croppedData->last().x()) // other one must be cropped + qSwap(staticData, croppedData); + int highBound = findIndexAboveX(croppedData, staticData->last().x()); + if (highBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(highBound+1, croppedData->size()-(highBound+1)); + // set highest point of cropped data to fit exactly key position of last static data + // point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + int li = croppedData->size()-1; // last index + if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0) + slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x()); + else + slope = 0; + (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x())); + (*croppedData)[li].setX(staticData->last().x()); + } else // mKeyAxis->orientation() == Qt::Vertical + { + // y is key + // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x, + // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate. + // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys: + if (staticData->first().y() < staticData->last().y()) + { + int size = staticData->size(); + for (int i=0; ifirst().y() < croppedData->last().y()) + { + int size = croppedData->size(); + for (int i=0; ifirst().y() > croppedData->first().y()) // other one must be cropped + qSwap(staticData, croppedData); + int lowBound = findIndexAboveY(croppedData, staticData->first().y()); + if (lowBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(0, lowBound); + // set lowest point of cropped data to fit exactly key position of first static data + // point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + double slope; + if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots + slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y()); + else + slope = 0; + (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y())); + (*croppedData)[0].setY(staticData->first().y()); + + // crop upper bound: + if (staticData->last().y() < croppedData->last().y()) // other one must be cropped + qSwap(staticData, croppedData); + int highBound = findIndexBelowY(croppedData, staticData->last().y()); + if (highBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(highBound+1, croppedData->size()-(highBound+1)); + // set highest point of cropped data to fit exactly key position of last static data + // point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + int li = croppedData->size()-1; // last index + if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots + slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y()); + else + slope = 0; + (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y())); + (*croppedData)[li].setY(staticData->last().y()); + } + + // return joined: + for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted + thisData << otherData.at(i); + return QPolygonF(thisData); +} + +/*! \internal + + Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in + \a data points are ordered ascending, as is the case when plotting with horizontal key axis. + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexAboveX(const QVector *data, double x) const +{ + for (int i=data->size()-1; i>=0; --i) + { + if (data->at(i).x() < x) + { + if (isize()-1) + return i+1; + else + return data->size()-1; + } + } + return -1; +} + +/*! \internal + + Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in + \a data points are ordered ascending, as is the case when plotting with horizontal key axis. + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexBelowX(const QVector *data, double x) const +{ + for (int i=0; isize(); ++i) + { + if (data->at(i).x() > x) + { + if (i>0) + return i-1; + else + return 0; + } + } + return -1; +} + +/*! \internal + + Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in + \a data points are ordered descending, as is the case when plotting with vertical key axis. + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexAboveY(const QVector *data, double y) const +{ + for (int i=0; isize(); ++i) + { + if (data->at(i).y() < y) + { + if (i>0) + return i-1; + else + return 0; + } + } + return -1; +} + +/*! \internal + + Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a + pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in + \ref selectTest. + + If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape + is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0. +*/ +double QCPGraph::pointDistance(const QPointF &pixelPoint) const +{ + if (mData->isEmpty()) + return -1.0; + if (mLineStyle == lsNone && mScatterStyle.isNone()) + return -1.0; + + // calculate minimum distances to graph representation: + if (mLineStyle == lsNone) + { + // no line displayed, only calculate distance to scatter points: + QVector scatterData; + getScatterPlotData(&scatterData); + if (scatterData.size() > 0) + { + double minDistSqr = std::numeric_limits::max(); + for (int i=0; i lineData; + getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here + if (lineData.size() > 1) // at least one line segment, compare distance to line segments + { + double minDistSqr = std::numeric_limits::max(); + if (mLineStyle == lsImpulse) + { + // impulse plot differs from other line styles in that the lineData points are only pairwise connected: + for (int i=0; i 0) // only single data point, calculate distance to that point + { + return QVector2D(lineData.at(0)-pixelPoint).length(); + } else // no data available in view to calculate distance to + return -1.0; + } +} + +/*! \internal + + Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in + \a data points are ordered descending, as is the case when plotting with vertical key axis (since + keys are ordered ascending). + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexBelowY(const QVector *data, double y) const +{ + for (int i=data->size()-1; i>=0; --i) + { + if (data->at(i).y() > y) + { + if (isize()-1) + return i+1; + else + return data->size()-1; + } + } + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const +{ + // just call the specialized version which takes an additional argument whether error bars + // should also be taken into consideration for range calculation. We set this to true here. + return getKeyRange(foundRange, inSignDomain, true); +} + +/* inherits documentation from base class */ +QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const +{ + // just call the specialized version which takes an additional argument whether error bars + // should also be taken into consideration for range calculation. We set this to true here. + return getValueRange(foundRange, inSignDomain, true); +} + +/*! \overload + + Allows to specify whether the error bars should be included in the range calculation. + + \see getKeyRange(bool &foundRange, SignDomain inSignDomain) +*/ +QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current, currentErrorMinus, currentErrorPlus; + + if (inSignDomain == sdBoth) // range may be anywhere + { + QCPDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + if (!qIsNaN(it.value().value)) + { + current = it.value().key; + currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); + if (current-currentErrorMinus < range.lower || !haveLower) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if (current+currentErrorPlus > range.upper || !haveUpper) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + } + ++it; + } + } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain + { + QCPDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + if (!qIsNaN(it.value().value)) + { + current = it.value().key; + currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. + { + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } + } + ++it; + } + } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain + { + QCPDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + if (!qIsNaN(it.value().value)) + { + current = it.value().key; + currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. + { + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } + } + ++it; + } + } + + foundRange = haveLower && haveUpper; + return range; +} + +/*! \overload + + Allows to specify whether the error bars should be included in the range calculation. + + \see getValueRange(bool &foundRange, SignDomain inSignDomain) +*/ +QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current, currentErrorMinus, currentErrorPlus; + + if (inSignDomain == sdBoth) // range may be anywhere + { + QCPDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().value; + if (!qIsNaN(current)) + { + currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); + if (current-currentErrorMinus < range.lower || !haveLower) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if (current+currentErrorPlus > range.upper || !haveUpper) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + } + ++it; + } + } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain + { + QCPDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().value; + if (!qIsNaN(current)) + { + currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. + { + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } + } + ++it; + } + } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain + { + QCPDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().value; + if (!qIsNaN(current)) + { + currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. + { + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } + } + ++it; + } + } + + foundRange = haveLower && haveUpper; + return range; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPCurveData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPCurveData + \brief Holds the data of one single data point for QCPCurve. + + The container for storing multiple data points is \ref QCPCurveDataMap. + + The stored data is: + \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector (x(t), y(t))) + \li \a key: coordinate on the key axis of this curve point + \li \a value: coordinate on the value axis of this curve point + + \see QCPCurveDataMap +*/ + +/*! + Constructs a curve data point with t, key and value set to zero. +*/ +QCPCurveData::QCPCurveData() : + t(0), + key(0), + value(0) +{ +} + +/*! + Constructs a curve data point with the specified \a t, \a key and \a value. +*/ +QCPCurveData::QCPCurveData(double t, double key, double value) : + t(t), + key(key), + value(value) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPCurve +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPCurve + \brief A plottable representing a parametric curve in a plot. + + \image html QCPCurve.png + + Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate, + so their visual representation can have \a loops. This is realized by introducing a third + coordinate \a t, which defines the order of the points described by the other two coordinates \a + x and \a y. + + To plot data, assign it with the \ref setData or \ref addData functions. + + Gaps in the curve can be created by adding data points with NaN as key and value + (qQNaN() or std::numeric_limits::quiet_NaN()) in between the two data points that shall be + separated. + + \section appearance Changing the appearance + + The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush). + \section usage Usage + + Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So + the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance and add it to the customPlot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1 + and then modify the properties of the newly created plottable, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2 +*/ + +/*! + Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot + then takes ownership of the graph. +*/ +QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis) +{ + mData = new QCPCurveDataMap; + mPen.setColor(Qt::blue); + mPen.setStyle(Qt::SolidLine); + mBrush.setColor(Qt::blue); + mBrush.setStyle(Qt::NoBrush); + mSelectedPen = mPen; + mSelectedPen.setWidthF(2.5); + mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen + mSelectedBrush = mBrush; + + setScatterStyle(QCPScatterStyle()); + setLineStyle(lsLine); +} + +QCPCurve::~QCPCurve() +{ + delete mData; +} + +/*! + Replaces the current data with the provided \a data. + + If \a copy is set to true, data points in \a data will only be copied. if false, the plottable + takes ownership of the passed data and replaces the internal data pointer with it. This is + significantly faster than copying for large datasets. +*/ +void QCPCurve::setData(QCPCurveDataMap *data, bool copy) +{ + if (mData == data) + { + qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast(data); + return; + } + if (copy) + { + *mData = *data; + } else + { + delete mData; + mData = data; + } +} + +/*! \overload + + Replaces the current data with the provided points in \a t, \a key and \a value tuples. The + provided vectors should have equal length. Else, the number of added points will be the size of + the smallest vector. +*/ +void QCPCurve::setData(const QVector &t, const QVector &key, const QVector &value) +{ + mData->clear(); + int n = t.size(); + n = qMin(n, key.size()); + n = qMin(n, value.size()); + QCPCurveData newData; + for (int i=0; iinsertMulti(newData.t, newData); + } +} + +/*! \overload + + Replaces the current data with the provided \a key and \a value pairs. The t parameter + of each data point will be set to the integer index of the respective key/value pair. +*/ +void QCPCurve::setData(const QVector &key, const QVector &value) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + QCPCurveData newData; + for (int i=0; iinsertMulti(newData.t, newData); + } +} + +/*! + Sets the visual appearance of single data points in the plot. If set to \ref + QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate + line style). + + \see QCPScatterStyle, setLineStyle +*/ +void QCPCurve::setScatterStyle(const QCPScatterStyle &style) +{ + mScatterStyle = style; +} + +/*! + Sets how the single data points are connected in the plot or how they are represented visually + apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref + setScatterStyle to the desired scatter style. + + \see setScatterStyle +*/ +void QCPCurve::setLineStyle(QCPCurve::LineStyle style) +{ + mLineStyle = style; +} + +/*! + Adds the provided data points in \a dataMap to the current data. + \see removeData +*/ +void QCPCurve::addData(const QCPCurveDataMap &dataMap) +{ + mData->unite(dataMap); +} + +/*! \overload + Adds the provided single data point in \a data to the current data. + \see removeData +*/ +void QCPCurve::addData(const QCPCurveData &data) +{ + mData->insertMulti(data.t, data); +} + +/*! \overload + Adds the provided single data point as \a t, \a key and \a value tuple to the current data + \see removeData +*/ +void QCPCurve::addData(double t, double key, double value) +{ + QCPCurveData newData; + newData.t = t; + newData.key = key; + newData.value = value; + mData->insertMulti(newData.t, newData); +} + +/*! \overload + + Adds the provided single data point as \a key and \a value pair to the current data The t + parameter of the data point is set to the t of the last data point plus 1. If there is no last + data point, t will be set to 0. + + \see removeData +*/ +void QCPCurve::addData(double key, double value) +{ + QCPCurveData newData; + if (!mData->isEmpty()) + newData.t = (mData->constEnd()-1).key()+1; + else + newData.t = 0; + newData.key = key; + newData.value = value; + mData->insertMulti(newData.t, newData); +} + +/*! \overload + Adds the provided data points as \a t, \a key and \a value tuples to the current data. + \see removeData +*/ +void QCPCurve::addData(const QVector &ts, const QVector &keys, const QVector &values) +{ + int n = ts.size(); + n = qMin(n, keys.size()); + n = qMin(n, values.size()); + QCPCurveData newData; + for (int i=0; iinsertMulti(newData.t, newData); + } +} + +/*! + Removes all data points with curve parameter t smaller than \a t. + \see addData, clearData +*/ +void QCPCurve::removeDataBefore(double t) +{ + QCPCurveDataMap::iterator it = mData->begin(); + while (it != mData->end() && it.key() < t) + it = mData->erase(it); +} + +/*! + Removes all data points with curve parameter t greater than \a t. + \see addData, clearData +*/ +void QCPCurve::removeDataAfter(double t) +{ + if (mData->isEmpty()) return; + QCPCurveDataMap::iterator it = mData->upperBound(t); + while (it != mData->end()) + it = mData->erase(it); +} + +/*! + Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is + greater or equal to \a tot, the function does nothing. To remove a single data point with known + t, use \ref removeData(double t). + + \see addData, clearData +*/ +void QCPCurve::removeData(double fromt, double tot) +{ + if (fromt >= tot || mData->isEmpty()) return; + QCPCurveDataMap::iterator it = mData->upperBound(fromt); + QCPCurveDataMap::iterator itEnd = mData->upperBound(tot); + while (it != itEnd) + it = mData->erase(it); +} + +/*! \overload + + Removes a single data point at curve parameter \a t. If the position is not known with absolute + precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness + interval around the suspected position, depeding on the precision with which the curve parameter + is known. + + \see addData, clearData +*/ +void QCPCurve::removeData(double t) +{ + mData->remove(t); +} + +/*! + Removes all data points. + \see removeData, removeDataAfter, removeDataBefore +*/ +void QCPCurve::clearData() +{ + mData->clear(); +} + +/* inherits documentation from base class */ +double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if ((onlySelectable && !mSelectable) || mData->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + return pointDistance(pos); + else + return -1; +} + +/* inherits documentation from base class */ +void QCPCurve::draw(QCPPainter *painter) +{ + if (mData->isEmpty()) return; + + // allocate line vector: + QVector *lineData = new QVector; + + // fill with curve data: + getCurveData(lineData); + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + QCPCurveDataMap::const_iterator it; + for (it = mData->constBegin(); it != mData->constEnd(); ++it) + { + if (QCP::isInvalidData(it.value().t) || + QCP::isInvalidData(it.value().key, it.value().value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name(); + } +#endif + + // draw curve fill: + if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) + { + applyFillAntialiasingHint(painter); + painter->setPen(Qt::NoPen); + painter->setBrush(mainBrush()); + painter->drawPolygon(QPolygonF(*lineData)); + } + + // draw curve line: + if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mainPen()); + painter->setBrush(Qt::NoBrush); + // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: + if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && + painter->pen().style() == Qt::SolidLine && + !painter->modes().testFlag(QCPPainter::pmVectorized) && + !painter->modes().testFlag(QCPPainter::pmNoCaching)) + { + int i = 0; + bool lastIsNan = false; + const int lineDataSize = lineData->size(); + while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect + while (i < lineDataSize) + { + if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line + { + if (!lastIsNan) + painter->drawLine(lineData->at(i-1), lineData->at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; + ++i; + } + } else + { + int segmentStart = 0; + int i = 0; + const int lineDataSize = lineData->size(); + while (i < lineDataSize) + { + if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line + { + painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point + segmentStart = i+1; + } + ++i; + } + // draw last segment: + painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); + } + } + + // draw scatters: + if (!mScatterStyle.isNone()) + drawScatterPlot(painter, lineData); + + // free allocated line data: + delete lineData; +} + +/* inherits documentation from base class */ +void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw fill: + if (mBrush.style() != Qt::NoBrush) + { + applyFillAntialiasingHint(painter); + painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush); + } + // draw line vertically centered: + if (mLineStyle != lsNone) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens + } + // draw scatter symbol: + if (!mScatterStyle.isNone()) + { + applyScattersAntialiasingHint(painter); + // scale scatter pixmap if it's too large to fit in legend icon rect: + if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height())) + { + QCPScatterStyle scaledStyle(mScatterStyle); + scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + scaledStyle.applyTo(painter, mPen); + scaledStyle.drawShape(painter, QRectF(rect).center()); + } else + { + mScatterStyle.applyTo(painter, mPen); + mScatterStyle.drawShape(painter, QRectF(rect).center()); + } + } +} + +/*! \internal + + Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of + the line style and are always drawn if scatter shape is not \ref QCPScatterStyle::ssNone. +*/ +void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector *pointData) const +{ + // draw scatter point symbols: + applyScattersAntialiasingHint(painter); + mScatterStyle.applyTo(painter, mPen); + for (int i=0; isize(); ++i) + if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y())) + mScatterStyle.drawShape(painter, pointData->at(i)); +} + +/*! \internal + + called by QCPCurve::draw to generate a point vector (in pixel coordinates) which represents the + line of the curve. + + Line segments that aren't visible in the current axis rect are handled in an optimized way. They + are projected onto a rectangle slightly larger than the visible axis rect and simplified + regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside + the visible axis rect by generating new temporary points on the outer rect if necessary. + + Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref + getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints. +*/ +void QCPCurve::getCurveData(QVector *lineData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + // add margins to rect to compensate for stroke width + double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety + if (!mScatterStyle.isNone()) + strokeMargin = qMax(strokeMargin, mScatterStyle.size()); + double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1)); + double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1)); + double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1)); + double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1)); + int currentRegion; + QCPCurveDataMap::const_iterator it = mData->constBegin(); + QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1; + int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom); + QVector trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right) + while (it != mData->constEnd()) + { + currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom); + if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R + { + if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal + { + QPointF crossA, crossB; + if (prevRegion == 5) // we're coming from R, so add this point optimized + { + lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom)); + // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point + *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom); + } else if (mayTraverse(prevRegion, currentRegion) && + getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB)) + { + // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point: + QVector beforeTraverseCornerPoints, afterTraverseCornerPoints; + getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints); + if (it != mData->constBegin()) + { + *lineData << beforeTraverseCornerPoints; + lineData->append(crossA); + lineData->append(crossB); + *lineData << afterTraverseCornerPoints; + } else + { + lineData->append(crossB); + *lineData << afterTraverseCornerPoints; + trailingPoints << beforeTraverseCornerPoints << crossA ; + } + } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s) + { + *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom); + } + } else // segment does end in R, so we add previous point optimized and this point at original position + { + if (it == mData->constBegin()) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end + trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom); + else + lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom)); + lineData->append(coordsToPixels(it.value().key, it.value().value)); + } + } else // region didn't change + { + if (currentRegion == 5) // still in R, keep adding original points + { + lineData->append(coordsToPixels(it.value().key, it.value().value)); + } else // still outside R, no need to add anything + { + // see how this is not doing anything? That's the main optimization... + } + } + prevIt = it; + prevRegion = currentRegion; + ++it; + } + *lineData << trailingPoints; +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveData. + + It returns the region of the given point (\a x, \a y) with respect to a rectangle defined by \a + rectLeft, \a rectTop, \a rectRight, and \a rectBottom. + + The regions are enumerated from top to bottom and left to right: + + + + + +
147
258
369
+ + With the rectangle being region 5, and the outer regions extending infinitely outwards. In the + curve optimization algorithm, region 5 is considered to be the visible portion of the plot. +*/ +int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const +{ + if (x < rectLeft) // region 123 + { + if (y > rectTop) + return 1; + else if (y < rectBottom) + return 3; + else + return 2; + } else if (x > rectRight) // region 789 + { + if (y > rectTop) + return 7; + else if (y < rectBottom) + return 9; + else + return 8; + } else // region 456 + { + if (y > rectTop) + return 4; + else if (y < rectBottom) + return 6; + else + return 5; + } +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveData. + + This method is used in case the current segment passes from inside the visible rect (region 5, + see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by + the line connecting (\a key, \a value) with (\a otherKey, \a otherValue). + + It returns the intersection point of the segment with the border of region 5. + + For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or + whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or + leaving it. It is important though that \a otherRegion correctly identifies the other region not + equal to 5. +*/ +QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const +{ + double intersectKey = rectLeft; // initial value is just fail-safe + double intersectValue = rectTop; // initial value is just fail-safe + switch (otherRegion) + { + case 1: // top and left edge + { + intersectValue = rectTop; + intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other: + { + intersectKey = rectLeft; + intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + } + break; + } + case 2: // left edge + { + intersectKey = rectLeft; + intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + break; + } + case 3: // bottom and left edge + { + intersectValue = rectBottom; + intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other: + { + intersectKey = rectLeft; + intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + } + break; + } + case 4: // top edge + { + intersectValue = rectTop; + intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + break; + } + case 5: + { + break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table + } + case 6: // bottom edge + { + intersectValue = rectBottom; + intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + break; + } + case 7: // top and right edge + { + intersectValue = rectTop; + intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other: + { + intersectKey = rectRight; + intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + } + break; + } + case 8: // right edge + { + intersectKey = rectRight; + intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + break; + } + case 9: // bottom and right edge + { + intersectValue = rectBottom; + intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue); + if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other: + { + intersectKey = rectRight; + intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey); + } + break; + } + } + return coordsToPixels(intersectKey, intersectValue); +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveData. + + In situations where a single segment skips over multiple regions it might become necessary to add + extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment + doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts. + This method provides these points that must be added, assuming the original segment doesn't + start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by + \ref getTraverseCornerPoints.) + + For example, consider a segment which directly goes from region 4 to 2 but originally is far out + to the top left such that it doesn't cross region 5. Naively optimizing these points by + projecting them on the top and left borders of region 5 will create a segment that surely crosses + 5, creating a visual artifact in the plot. This method prevents this by providing extra points at + the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without + traversing 5. +*/ +QVector QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const +{ + QVector result; + switch (prevRegion) + { + case 1: + { + switch (currentRegion) + { + case 2: { result << coordsToPixels(rectLeft, rectTop); break; } + case 4: { result << coordsToPixels(rectLeft, rectTop); break; } + case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; } + case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; } + case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; } + case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; } + case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R + { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); } + else + { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); } + break; + } + } + break; + } + case 2: + { + switch (currentRegion) + { + case 1: { result << coordsToPixels(rectLeft, rectTop); break; } + case 3: { result << coordsToPixels(rectLeft, rectBottom); break; } + case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; } + case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; } + case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; } + case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; } + } + break; + } + case 3: + { + switch (currentRegion) + { + case 2: { result << coordsToPixels(rectLeft, rectBottom); break; } + case 6: { result << coordsToPixels(rectLeft, rectBottom); break; } + case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; } + case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; } + case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; } + case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; } + case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R + { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); } + else + { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); } + break; + } + } + break; + } + case 4: + { + switch (currentRegion) + { + case 1: { result << coordsToPixels(rectLeft, rectTop); break; } + case 7: { result << coordsToPixels(rectRight, rectTop); break; } + case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; } + case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; } + case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; } + case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; } + } + break; + } + case 5: + { + switch (currentRegion) + { + case 1: { result << coordsToPixels(rectLeft, rectTop); break; } + case 7: { result << coordsToPixels(rectRight, rectTop); break; } + case 9: { result << coordsToPixels(rectRight, rectBottom); break; } + case 3: { result << coordsToPixels(rectLeft, rectBottom); break; } + } + break; + } + case 6: + { + switch (currentRegion) + { + case 3: { result << coordsToPixels(rectLeft, rectBottom); break; } + case 9: { result << coordsToPixels(rectRight, rectBottom); break; } + case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; } + case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; } + case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; } + case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; } + } + break; + } + case 7: + { + switch (currentRegion) + { + case 4: { result << coordsToPixels(rectRight, rectTop); break; } + case 8: { result << coordsToPixels(rectRight, rectTop); break; } + case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; } + case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; } + case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; } + case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; } + case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R + { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); } + else + { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); } + break; + } + } + break; + } + case 8: + { + switch (currentRegion) + { + case 7: { result << coordsToPixels(rectRight, rectTop); break; } + case 9: { result << coordsToPixels(rectRight, rectBottom); break; } + case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; } + case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; } + case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; } + case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; } + } + break; + } + case 9: + { + switch (currentRegion) + { + case 6: { result << coordsToPixels(rectRight, rectBottom); break; } + case 8: { result << coordsToPixels(rectRight, rectBottom); break; } + case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; } + case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; } + case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; } + case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; } + case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R + { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); } + else + { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); } + break; + } + } + break; + } + } + return result; +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveData. + + This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref + getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion + nor \a currentRegion is 5 itself. + + If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the + segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref + getTraverse). +*/ +bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const +{ + switch (prevRegion) + { + case 1: + { + switch (currentRegion) + { + case 4: + case 7: + case 2: + case 3: return false; + default: return true; + } + } + case 2: + { + switch (currentRegion) + { + case 1: + case 3: return false; + default: return true; + } + } + case 3: + { + switch (currentRegion) + { + case 1: + case 2: + case 6: + case 9: return false; + default: return true; + } + } + case 4: + { + switch (currentRegion) + { + case 1: + case 7: return false; + default: return true; + } + } + case 5: return false; // should never occur + case 6: + { + switch (currentRegion) + { + case 3: + case 9: return false; + default: return true; + } + } + case 7: + { + switch (currentRegion) + { + case 1: + case 4: + case 8: + case 9: return false; + default: return true; + } + } + case 8: + { + switch (currentRegion) + { + case 7: + case 9: return false; + default: return true; + } + } + case 9: + { + switch (currentRegion) + { + case 3: + case 6: + case 8: + case 7: return false; + default: return true; + } + } + default: return true; + } +} + + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveData. + + This method assumes that the \ref mayTraverse test has returned true, so there is a chance the + segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible + region 5. + + The return value of this method indicates whether the segment actually traverses region 5 or not. + + If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and + exit points of region 5. They will become the optimized points for that segment. +*/ +bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const +{ + QList intersections; // x of QPointF corresponds to key and y to value + if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis + { + // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here + intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method + intersections.append(QPointF(key, rectTop)); + } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis + { + // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here + intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method + intersections.append(QPointF(rectRight, value)); + } else // line is skewed + { + double gamma; + double keyPerValue = (key-prevKey)/(value-prevValue); + // check top of rect: + gamma = prevKey + (rectTop-prevValue)*keyPerValue; + if (gamma >= rectLeft && gamma <= rectRight) + intersections.append(QPointF(gamma, rectTop)); + // check bottom of rect: + gamma = prevKey + (rectBottom-prevValue)*keyPerValue; + if (gamma >= rectLeft && gamma <= rectRight) + intersections.append(QPointF(gamma, rectBottom)); + double valuePerKey = 1.0/keyPerValue; + // check left of rect: + gamma = prevValue + (rectLeft-prevKey)*valuePerKey; + if (gamma >= rectBottom && gamma <= rectTop) + intersections.append(QPointF(rectLeft, gamma)); + // check right of rect: + gamma = prevValue + (rectRight-prevKey)*valuePerKey; + if (gamma >= rectBottom && gamma <= rectTop) + intersections.append(QPointF(rectRight, gamma)); + } + + // handle cases where found points isn't exactly 2: + if (intersections.size() > 2) + { + // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between: + double distSqrMax = 0; + QPointF pv1, pv2; + for (int i=0; i distSqrMax) + { + pv1 = intersections.at(i); + pv2 = intersections.at(k); + distSqrMax = distSqr; + } + } + } + intersections = QList() << pv1 << pv2; + } else if (intersections.size() != 2) + { + // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment + return false; + } + + // possibly re-sort points so optimized point segment has same direction as original segment: + if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction + intersections.move(0, 1); + crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y()); + crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y()); + return true; +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveData. + + This method assumes that the \ref getTraverse test has returned true, so the segment definitely + traverses the visible region 5 when going from \a prevRegion to \a currentRegion. + + In certain situations it is not sufficient to merely generate the entry and exit points of the + segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in + addition to traversing region 5, skips another region outside of region 5, which makes it + necessary to add an optimized corner point there (very similar to the job \ref + getOptimizedCornerPoints does for segments that are completely in outside regions and don't + traverse 5). + + As an example, consider a segment going from region 1 to region 6, traversing the lower left + corner of region 5. In this configuration, the segment additionally crosses the border between + region 1 and 2 before entering region 5. This makes it necessary to add an additional point in + the top left corner, before adding the optimized traverse points. So in this case, the output + parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be + empty. + + In some cases, such as when going from region 1 to 9, it may even be necessary to add additional + corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse + return the respective corner points. +*/ +void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector &beforeTraverse, QVector &afterTraverse) const +{ + switch (prevRegion) + { + case 1: + { + switch (currentRegion) + { + case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; } + case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; } + case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; } + } + break; + } + case 2: + { + switch (currentRegion) + { + case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; } + case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; } + } + break; + } + case 3: + { + switch (currentRegion) + { + case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; } + case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; } + case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; } + } + break; + } + case 4: + { + switch (currentRegion) + { + case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; } + case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; } + } + break; + } + case 5: { break; } // shouldn't happen because this method only handles full traverses + case 6: + { + switch (currentRegion) + { + case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; } + case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; } + } + break; + } + case 7: + { + switch (currentRegion) + { + case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; } + case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; } + case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; } + } + break; + } + case 8: + { + switch (currentRegion) + { + case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; } + case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; } + } + break; + } + case 9: + { + switch (currentRegion) + { + case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; } + case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; } + case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; } + } + break; + } + } +} + +/*! \internal + + Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a + pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in + \ref selectTest. +*/ +double QCPCurve::pointDistance(const QPointF &pixelPoint) const +{ + if (mData->isEmpty()) + { + qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data"; + return 500; + } + if (mData->size() == 1) + { + QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value); + return QVector2D(dataPoint-pixelPoint).length(); + } + + // calculate minimum distance to line segments: + QVector *lineData = new QVector; + getCurveData(lineData); + double minDistSqr = std::numeric_limits::max(); + for (int i=0; isize()-1; ++i) + { + double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint); + if (currentDistSqr < minDistSqr) + minDistSqr = currentDistSqr; + } + delete lineData; + return qSqrt(minDistSqr); +} + +/* inherits documentation from base class */ +QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current; + + QCPCurveDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().key; + if (!qIsNaN(current) && !qIsNaN(it.value().value)) + { + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + } + ++it; + } + + foundRange = haveLower && haveUpper; + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current; + + QCPCurveDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().value; + if (!qIsNaN(current) && !qIsNaN(it.value().key)) + { + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + } + ++it; + } + + foundRange = haveLower && haveUpper; + return range; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPBarsGroup +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPBarsGroup + \brief Groups multiple QCPBars together so they appear side by side + + \image html QCPBarsGroup.png + + When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable + to have them appearing next to each other at each key. This is what adding the respective QCPBars + plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each + other, see \ref QCPBars::moveAbove.) + + \section qcpbarsgroup-usage Usage + + To add a QCPBars plottable to the group, create a new group and then add the respective bars + intances: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation + Alternatively to appending to the group like shown above, you can also set the group on the + QCPBars plottable via \ref QCPBars::setBarsGroup. + + The spacing between the bars can be configured via \ref setSpacingType and \ref setSpacing. The + bars in this group appear in the plot in the order they were appended. To insert a bars plottable + at a certain index position, or to reposition a bars plottable which is already in the group, use + \ref insert. + + To remove specific bars from the group, use either \ref remove or call \ref + QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable. + + To clear the entire group, call \ref clear, or simply delete the group. + + \section qcpbarsgroup-example Example + + The image above is generated with the following code: + \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example +*/ + +/* start of documentation of inline functions */ + +/*! \fn QList QCPBarsGroup::bars() const + + Returns all bars currently in this group. + + \see bars(int index) +*/ + +/*! \fn int QCPBarsGroup::size() const + + Returns the number of QCPBars plottables that are part of this group. + +*/ + +/*! \fn bool QCPBarsGroup::isEmpty() const + + Returns whether this bars group is empty. + + \see size +*/ + +/*! \fn bool QCPBarsGroup::contains(QCPBars *bars) + + Returns whether the specified \a bars plottable is part of this group. + +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a new bars group for the specified QCustomPlot instance. +*/ +QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) : + QObject(parentPlot), + mParentPlot(parentPlot), + mSpacingType(stAbsolute), + mSpacing(4) +{ +} + +QCPBarsGroup::~QCPBarsGroup() +{ + clear(); +} + +/*! + Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType. + + The actual spacing can then be specified with \ref setSpacing. + + \see setSpacing +*/ +void QCPBarsGroup::setSpacingType(SpacingType spacingType) +{ + mSpacingType = spacingType; +} + +/*! + Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is + defined by the current \ref SpacingType, which can be set with \ref setSpacingType. + + \see setSpacingType +*/ +void QCPBarsGroup::setSpacing(double spacing) +{ + mSpacing = spacing; +} + +/*! + Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars + exists, returns 0. + + \see bars(), size +*/ +QCPBars *QCPBarsGroup::bars(int index) const +{ + if (index >= 0 && index < mBars.size()) + { + return mBars.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return 0; + } +} + +/*! + Removes all QCPBars plottables from this group. + + \see isEmpty +*/ +void QCPBarsGroup::clear() +{ + foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay + bars->setBarsGroup(0); // removes itself via removeBars +} + +/*! + Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref + QCPBars::setBarsGroup on the \a bars instance. + + \see insert, remove +*/ +void QCPBarsGroup::append(QCPBars *bars) +{ + if (!bars) + { + qDebug() << Q_FUNC_INFO << "bars is 0"; + return; + } + + if (!mBars.contains(bars)) + bars->setBarsGroup(this); + else + qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast(bars); +} + +/*! + Inserts the specified \a bars plottable into this group at the specified index position \a i. + This gives you full control over the ordering of the bars. + + \a bars may already be part of this group. In that case, \a bars is just moved to the new index + position. + + \see append, remove +*/ +void QCPBarsGroup::insert(int i, QCPBars *bars) +{ + if (!bars) + { + qDebug() << Q_FUNC_INFO << "bars is 0"; + return; + } + + // first append to bars list normally: + if (!mBars.contains(bars)) + bars->setBarsGroup(this); + // then move to according position: + mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1)); +} + +/*! + Removes the specified \a bars plottable from this group. + + \see contains, clear +*/ +void QCPBarsGroup::remove(QCPBars *bars) +{ + if (!bars) + { + qDebug() << Q_FUNC_INFO << "bars is 0"; + return; + } + + if (mBars.contains(bars)) + bars->setBarsGroup(0); + else + qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast(bars); +} + +/*! \internal + + Adds the specified \a bars to the internal mBars list of bars. This method does not change the + barsGroup property on \a bars. + + \see unregisterBars +*/ +void QCPBarsGroup::registerBars(QCPBars *bars) +{ + if (!mBars.contains(bars)) + mBars.append(bars); +} + +/*! \internal + + Removes the specified \a bars from the internal mBars list of bars. This method does not change + the barsGroup property on \a bars. + + \see registerBars +*/ +void QCPBarsGroup::unregisterBars(QCPBars *bars) +{ + mBars.removeOne(bars); +} + +/*! \internal + + Returns the pixel offset in the key dimension the specified \a bars plottable should have at the + given key coordinate \a keyCoord. The offset is relative to the pixel position of the key + coordinate \a keyCoord. +*/ +double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord) +{ + // find list of all base bars in case some mBars are stacked: + QList baseBars; + foreach (const QCPBars *b, mBars) + { + while (b->barBelow()) + b = b->barBelow(); + if (!baseBars.contains(b)) + baseBars.append(b); + } + // find base bar this "bars" is stacked on: + const QCPBars *thisBase = bars; + while (thisBase->barBelow()) + thisBase = thisBase->barBelow(); + + // determine key pixel offset of this base bars considering all other base bars in this barsgroup: + double result = 0; + int index = baseBars.indexOf(thisBase); + if (index >= 0) + { + int startIndex; + double lowerPixelWidth, upperPixelWidth; + if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose) + { + return result; + } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center + { + if (baseBars.size() % 2 == 0) // even number of bars + { + startIndex = baseBars.size()/2-1; + result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing + } else // uneven number of bars + { + startIndex = (baseBars.size()-1)/2-1; + baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar + result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing + } + for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars + { + baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result -= qAbs(upperPixelWidth-lowerPixelWidth); + result -= getPixelSpacing(baseBars.at(i), keyCoord); + } + // finally half of our bars width: + baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; + } else // bar is to the right of center + { + if (baseBars.size() % 2 == 0) // even number of bars + { + startIndex = baseBars.size()/2; + result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing + } else // uneven number of bars + { + startIndex = (baseBars.size()-1)/2+1; + baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar + result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing + } + for (int i=startIndex; igetPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result += qAbs(upperPixelWidth-lowerPixelWidth); + result += getPixelSpacing(baseBars.at(i), keyCoord); + } + // finally half of our bars width: + baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; + } + } + return result; +} + +/*! \internal + + Returns the spacing in pixels which is between this \a bars and the following one, both at the + key coordinate \a keyCoord. + + \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only + needed to get acces to the key axis transformation and axis rect for the modes \ref + stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in + \ref stPlotCoords on a logarithmic axis. +*/ +double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord) +{ + switch (mSpacingType) + { + case stAbsolute: + { + return mSpacing; + } + case stAxisRectRatio: + { + if (bars->keyAxis()->orientation() == Qt::Horizontal) + return bars->keyAxis()->axisRect()->width()*mSpacing; + else + return bars->keyAxis()->axisRect()->height()*mSpacing; + } + case stPlotCoords: + { + double keyPixel = bars->keyAxis()->coordToPixel(keyCoord); + return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel; + } + } + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPBarData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPBarData + \brief Holds the data of one single data point (one bar) for QCPBars. + + The container for storing multiple data points is \ref QCPBarDataMap. + + The stored data is: + \li \a key: coordinate on the key axis of this bar + \li \a value: height coordinate on the value axis of this bar + + \see QCPBarDataaMap +*/ + +/*! + Constructs a bar data point with key and value set to zero. +*/ +QCPBarData::QCPBarData() : + key(0), + value(0) +{ +} + +/*! + Constructs a bar data point with the specified \a key and \a value. +*/ +QCPBarData::QCPBarData(double key, double value) : + key(key), + value(value) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPBars +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPBars + \brief A plottable representing a bar chart in a plot. + + \image html QCPBars.png + + To plot data, assign it with the \ref setData or \ref addData functions. + + \section appearance Changing the appearance + + The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush). + The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth. + + Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other + (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear + stacked. + + If you would like to group multiple QCPBars plottables together so they appear side by side as + shown below, use QCPBarsGroup. + + \image html QCPBarsGroup.png + + \section usage Usage + + Like all data representing objects in QCustomPlot, the QCPBars is a plottable + (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies + (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1 + add it to the customPlot with QCustomPlot::addPlottable: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2 + and then modify the properties of the newly created plottable, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3 +*/ + +/* start of documentation of inline functions */ + +/*! \fn QCPBars *QCPBars::barBelow() const + Returns the bars plottable that is directly below this bars plottable. + If there is no such plottable, returns 0. + + \see barAbove, moveBelow, moveAbove +*/ + +/*! \fn QCPBars *QCPBars::barAbove() const + Returns the bars plottable that is directly above this bars plottable. + If there is no such plottable, returns 0. + + \see barBelow, moveBelow, moveAbove +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot + then takes ownership of the bar chart. +*/ +QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + mData(new QCPBarDataMap), + mWidth(0.75), + mWidthType(wtPlotCoords), + mBarsGroup(0), + mBaseValue(0) +{ + // modify inherited properties from abstract plottable: + mPen.setColor(Qt::blue); + mPen.setStyle(Qt::SolidLine); + mBrush.setColor(QColor(40, 50, 255, 30)); + mBrush.setStyle(Qt::SolidPattern); + mSelectedPen = mPen; + mSelectedPen.setWidthF(2.5); + mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen + mSelectedBrush = mBrush; +} + +QCPBars::~QCPBars() +{ + setBarsGroup(0); + if (mBarBelow || mBarAbove) + connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking + delete mData; +} + +/*! + Sets the width of the bars. + + How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...), + depends on the currently set width type, see \ref setWidthType and \ref WidthType. +*/ +void QCPBars::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets how the width of the bars is defined. See the documentation of \ref WidthType for an + explanation of the possible values for \a widthType. + + The default value is \ref wtPlotCoords. + + \see setWidth +*/ +void QCPBars::setWidthType(QCPBars::WidthType widthType) +{ + mWidthType = widthType; +} + +/*! + Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref + QCPBarsGroup::append. + + To remove this QCPBars from any group, set \a barsGroup to 0. +*/ +void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup) +{ + // deregister at old group: + if (mBarsGroup) + mBarsGroup->unregisterBars(this); + mBarsGroup = barsGroup; + // register at new group: + if (mBarsGroup) + mBarsGroup->registerBars(this); +} + +/*! + Sets the base value of this bars plottable. + + The base value defines where on the value coordinate the bars start. How far the bars extend from + the base value is given by their individual value data. For example, if the base value is set to + 1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at + 3. + + For stacked bars, only the base value of the bottom-most QCPBars has meaning. + + The default base value is 0. +*/ +void QCPBars::setBaseValue(double baseValue) +{ + mBaseValue = baseValue; +} + +/*! + Replaces the current data with the provided \a data. + + If \a copy is set to true, data points in \a data will only be copied. if false, the plottable + takes ownership of the passed data and replaces the internal data pointer with it. This is + significantly faster than copying for large datasets. +*/ +void QCPBars::setData(QCPBarDataMap *data, bool copy) +{ + if (mData == data) + { + qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast(data); + return; + } + if (copy) + { + *mData = *data; + } else + { + delete mData; + mData = data; + } +} + +/*! \overload + + Replaces the current data with the provided points in \a key and \a value tuples. The + provided vectors should have equal length. Else, the number of added points will be the size of + the smallest vector. +*/ +void QCPBars::setData(const QVector &key, const QVector &value) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, value.size()); + QCPBarData newData; + for (int i=0; iinsertMulti(newData.key, newData); + } +} + +/*! + Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear + below the bars of \a bars. The move target \a bars must use the same key and value axis as this + plottable. + + Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already + has a bars object below itself, this bars object is inserted between the two. If this bars object + is already between two other bars, the two other bars will be stacked on top of each other after + the operation. + + To remove this bars plottable from any stacking, set \a bars to 0. + + \see moveBelow, barAbove, barBelow +*/ +void QCPBars::moveBelow(QCPBars *bars) +{ + if (bars == this) return; + if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data())) + { + qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars"; + return; + } + // remove from stacking: + connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0 + // if new bar given, insert this bar below it: + if (bars) + { + if (bars->mBarBelow) + connectBars(bars->mBarBelow.data(), this); + connectBars(this, bars); + } +} + +/*! + Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear + above the bars of \a bars. The move target \a bars must use the same key and value axis as this + plottable. + + Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already + has a bars object above itself, this bars object is inserted between the two. If this bars object + is already between two other bars, the two other bars will be stacked on top of each other after + the operation. + + To remove this bars plottable from any stacking, set \a bars to 0. + + \see moveBelow, barBelow, barAbove +*/ +void QCPBars::moveAbove(QCPBars *bars) +{ + if (bars == this) return; + if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data())) + { + qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars"; + return; + } + // remove from stacking: + connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0 + // if new bar given, insert this bar above it: + if (bars) + { + if (bars->mBarAbove) + connectBars(this, bars->mBarAbove.data()); + connectBars(bars, this); + } +} + +/*! + Adds the provided data points in \a dataMap to the current data. + \see removeData +*/ +void QCPBars::addData(const QCPBarDataMap &dataMap) +{ + mData->unite(dataMap); +} + +/*! \overload + Adds the provided single data point in \a data to the current data. + \see removeData +*/ +void QCPBars::addData(const QCPBarData &data) +{ + mData->insertMulti(data.key, data); +} + +/*! \overload + Adds the provided single data point as \a key and \a value tuple to the current data + \see removeData +*/ +void QCPBars::addData(double key, double value) +{ + QCPBarData newData; + newData.key = key; + newData.value = value; + mData->insertMulti(newData.key, newData); +} + +/*! \overload + Adds the provided data points as \a key and \a value tuples to the current data. + \see removeData +*/ +void QCPBars::addData(const QVector &keys, const QVector &values) +{ + int n = keys.size(); + n = qMin(n, values.size()); + QCPBarData newData; + for (int i=0; iinsertMulti(newData.key, newData); + } +} + +/*! + Removes all data points with key smaller than \a key. + \see addData, clearData +*/ +void QCPBars::removeDataBefore(double key) +{ + QCPBarDataMap::iterator it = mData->begin(); + while (it != mData->end() && it.key() < key) + it = mData->erase(it); +} + +/*! + Removes all data points with key greater than \a key. + \see addData, clearData +*/ +void QCPBars::removeDataAfter(double key) +{ + if (mData->isEmpty()) return; + QCPBarDataMap::iterator it = mData->upperBound(key); + while (it != mData->end()) + it = mData->erase(it); +} + +/*! + Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is + greater or equal to \a toKey, the function does nothing. To remove a single data point with known + key, use \ref removeData(double key). + + \see addData, clearData +*/ +void QCPBars::removeData(double fromKey, double toKey) +{ + if (fromKey >= toKey || mData->isEmpty()) return; + QCPBarDataMap::iterator it = mData->upperBound(fromKey); + QCPBarDataMap::iterator itEnd = mData->upperBound(toKey); + while (it != itEnd) + it = mData->erase(it); +} + +/*! \overload + + Removes a single data point at \a key. If the position is not known with absolute precision, + consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval + around the suspected position, depeding on the precision with which the key is known. + + \see addData, clearData +*/ +void QCPBars::removeData(double key) +{ + mData->remove(key); +} + +/*! + Removes all data points. + \see removeData, removeDataAfter, removeDataBefore +*/ +void QCPBars::clearData() +{ + mData->clear(); +} + +/* inherits documentation from base class */ +double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + { + QCPBarDataMap::ConstIterator it; + for (it = mData->constBegin(); it != mData->constEnd(); ++it) + { + if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos)) + return mParentPlot->selectionTolerance()*0.99; + } + } + return -1; +} + +/* inherits documentation from base class */ +void QCPBars::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mData->isEmpty()) return; + + QCPBarDataMap::const_iterator it, lower, upperEnd; + getVisibleDataBounds(lower, upperEnd); + for (it = lower; it != upperEnd; ++it) + { + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + if (QCP::isInvalidData(it.value().key, it.value().value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name(); +#endif + QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value); + // draw bar fill: + if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) + { + applyFillAntialiasingHint(painter); + painter->setPen(Qt::NoPen); + painter->setBrush(mainBrush()); + painter->drawPolygon(barPolygon); + } + // draw bar line: + if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mainPen()); + painter->setBrush(Qt::NoBrush); + painter->drawPolyline(barPolygon); + } + } +} + +/* inherits documentation from base class */ +void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw filled rect: + applyDefaultAntialiasingHint(painter); + painter->setBrush(mBrush); + painter->setPen(mPen); + QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67); + r.moveCenter(rect.center()); + painter->drawRect(r); +} + +/*! \internal + + called by \ref draw to determine which data (key) range is visible at the current key axis range + setting, so only that needs to be processed. It also takes into account the bar width. + + \a lower returns an iterator to the lowest data point that needs to be taken into account when + plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a + lower may still be just outside the visible range. + + \a upperEnd returns an iterator one higher than the highest visible data point. Same as before, \a + upperEnd may also lie just outside of the visible range. + + if the bars plottable contains no data, both \a lower and \a upperEnd point to constEnd. +*/ +void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const +{ + if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + if (mData->isEmpty()) + { + lower = mData->constEnd(); + upperEnd = mData->constEnd(); + return; + } + + // get visible data range as QMap iterators + lower = mData->lowerBound(mKeyAxis.data()->range().lower); + upperEnd = mData->upperBound(mKeyAxis.data()->range().upper); + double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower); + double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper); + bool isVisible = false; + // walk left from lbound to find lower bar that actually is completely outside visible pixel range: + QCPBarDataMap::const_iterator it = lower; + while (it != mData->constBegin()) + { + --it; + QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect(); + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound)); + else // keyaxis is vertical + isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound)); + if (isVisible) + lower = it; + else + break; + } + // walk right from ubound to find upper bar that actually is completely outside visible pixel range: + it = upperEnd; + while (it != mData->constEnd()) + { + QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect(); + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound)); + else // keyaxis is vertical + isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound)); + if (isVisible) + upperEnd = it+1; + else + break; + ++it; + } +} + +/*! \internal + + Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom + and shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref + setBaseValue). +*/ +QPolygonF QCPBars::getBarPolygon(double key, double value) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); } + + QPolygonF result; + double lowerPixelWidth, upperPixelWidth; + getPixelWidth(key, lowerPixelWidth, upperPixelWidth); + double base = getStackedBaseValue(key, value >= 0); + double basePixel = valueAxis->coordToPixel(base); + double valuePixel = valueAxis->coordToPixel(base+value); + double keyPixel = keyAxis->coordToPixel(key); + if (mBarsGroup) + keyPixel += mBarsGroup->keyPixelOffset(this, key); + if (keyAxis->orientation() == Qt::Horizontal) + { + result << QPointF(keyPixel+lowerPixelWidth, basePixel); + result << QPointF(keyPixel+lowerPixelWidth, valuePixel); + result << QPointF(keyPixel+upperPixelWidth, valuePixel); + result << QPointF(keyPixel+upperPixelWidth, basePixel); + } else + { + result << QPointF(basePixel, keyPixel+lowerPixelWidth); + result << QPointF(valuePixel, keyPixel+lowerPixelWidth); + result << QPointF(valuePixel, keyPixel+upperPixelWidth); + result << QPointF(basePixel, keyPixel+upperPixelWidth); + } + return result; +} + +/*! \internal + + This function is used to determine the width of the bar at coordinate \a key, according to the + specified width (\ref setWidth) and width type (\ref setWidthType). + + The output parameters \a lower and \a upper return the number of pixels the bar extends to lower + and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a + lower is negative and \a upper positive). +*/ +void QCPBars::getPixelWidth(double key, double &lower, double &upper) const +{ + switch (mWidthType) + { + case wtAbsolute: + { + upper = mWidth*0.5; + lower = -upper; + if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical))) + qSwap(lower, upper); + break; + } + case wtAxisRectRatio: + { + if (mKeyAxis && mKeyAxis.data()->axisRect()) + { + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5; + else + upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5; + lower = -upper; + if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical))) + qSwap(lower, upper); + } else + qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined"; + break; + } + case wtPlotCoords: + { + if (mKeyAxis) + { + double keyPixel = mKeyAxis.data()->coordToPixel(key); + upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel; + lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel; + // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by + // coordinate transform which includes range direction + } else + qDebug() << Q_FUNC_INFO << "No key axis defined"; + break; + } + } +} + +/*! \internal + + This function is called to find at which value to start drawing the base of a bar at \a key, when + it is stacked on top of another QCPBars (e.g. with \ref moveAbove). + + positive and negative bars are separated per stack (positive are stacked above baseValue upwards, + negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the + bar for which we need the base value is negative, set \a positive to false. +*/ +double QCPBars::getStackedBaseValue(double key, bool positive) const +{ + if (mBarBelow) + { + double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack + // find bars of mBarBelow that are approximately at key and find largest one: + double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point + if (key == 0) + epsilon = 1e-6; + QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon); + QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon); + while (it != itEnd) + { + if ((positive && it.value().value > max) || + (!positive && it.value().value < max)) + max = it.value().value; + ++it; + } + // recurse down the bar-stack to find the total height: + return max + mBarBelow.data()->getStackedBaseValue(key, positive); + } else + return mBaseValue; +} + +/*! \internal + + Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s) + currently above lower and below upper will become disconnected to lower/upper. + + If lower is zero, upper will be disconnected at the bottom. + If upper is zero, lower will be disconnected at the top. +*/ +void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) +{ + if (!lower && !upper) return; + + if (!lower) // disconnect upper at bottom + { + // disconnect old bar below upper: + if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper) + upper->mBarBelow.data()->mBarAbove = 0; + upper->mBarBelow = 0; + } else if (!upper) // disconnect lower at top + { + // disconnect old bar above lower: + if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower) + lower->mBarAbove.data()->mBarBelow = 0; + lower->mBarAbove = 0; + } else // connect lower and upper + { + // disconnect old bar above lower: + if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower) + lower->mBarAbove.data()->mBarBelow = 0; + // disconnect old bar below upper: + if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper) + upper->mBarBelow.data()->mBarAbove = 0; + lower->mBarAbove = upper; + upper->mBarBelow = lower; + } +} + +/* inherits documentation from base class */ +QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current; + QCPBarDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().key; + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + // determine exact range of bars by including bar width and barsgroup offset: + if (haveLower && mKeyAxis) + { + double lowerPixelWidth, upperPixelWidth, keyPixel; + getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth); + keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth; + if (mBarsGroup) + keyPixel += mBarsGroup->keyPixelOffset(this, range.lower); + range.lower = mKeyAxis.data()->pixelToCoord(keyPixel); + } + if (haveUpper && mKeyAxis) + { + double lowerPixelWidth, upperPixelWidth, keyPixel; + getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth); + keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth; + if (mBarsGroup) + keyPixel += mBarsGroup->keyPixelOffset(this, range.upper); + range.upper = mKeyAxis.data()->pixelToCoord(keyPixel); + } + foundRange = haveLower && haveUpper; + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const +{ + QCPRange range; + range.lower = mBaseValue; + range.upper = mBaseValue; + bool haveLower = true; // set to true, because baseValue should always be visible in bar charts + bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts + double current; + + QCPBarDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0); + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + + foundRange = true; // return true because bar charts always have the 0-line visible + return range; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPStatisticalBox +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPStatisticalBox + \brief A plottable representing a single statistical box in a plot. + + \image html QCPStatisticalBox.png + + To plot data, assign it with the individual parameter functions or use \ref setData to set all + parameters at once. The individual functions are: + \li \ref setMinimum + \li \ref setLowerQuartile + \li \ref setMedian + \li \ref setUpperQuartile + \li \ref setMaximum + + Additionally you can define a list of outliers, drawn as scatter datapoints: + \li \ref setOutliers + + \section appearance Changing the appearance + + The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You may change + the width of the box with \ref setWidth in plot coordinates (not pixels). + + Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref + setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top + (maximum) and bottom (minimum). + + The median indicator line has its own pen, \ref setMedianPen. + + If the whisker backbone pen is changed, make sure to set the capStyle to Qt::FlatCap. Else, the + backbone line might exceed the whisker bars by a few pixels due to the pen cap being not + perfectly flat. + + The Outlier data points are drawn as normal scatter points. Their look can be controlled with + \ref setOutlierStyle + + \section usage Usage + + Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable. + Usually, you first create an instance and add it to the customPlot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1 + and then modify the properties of the newly created plottable, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2 +*/ + +/*! + Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its + value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and + not have the same orientation. If either of these restrictions is violated, a corresponding + message is printed to the debug output (qDebug), the construction is not aborted, though. + + The constructed statistical box can be added to the plot with QCustomPlot::addPlottable, + QCustomPlot then takes ownership of the statistical box. +*/ +QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + mKey(0), + mMinimum(0), + mLowerQuartile(0), + mMedian(0), + mUpperQuartile(0), + mMaximum(0) +{ + setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6)); + setWhiskerWidth(0.2); + setWidth(0.5); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2.5)); + setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap)); + setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap)); + setWhiskerBarPen(QPen(Qt::black)); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); +} + +/*! + Sets the key coordinate of the statistical box. +*/ +void QCPStatisticalBox::setKey(double key) +{ + mKey = key; +} + +/*! + Sets the parameter "minimum" of the statistical box plot. This is the position of the lower + whisker, typically the minimum measurement of the sample that's not considered an outlier. + + \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth +*/ +void QCPStatisticalBox::setMinimum(double value) +{ + mMinimum = value; +} + +/*! + Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the + box. The lower and the upper quartiles are the two statistical quartiles around the median of the + sample, they contain 50% of the sample data. + + \see setUpperQuartile, setPen, setBrush, setWidth +*/ +void QCPStatisticalBox::setLowerQuartile(double value) +{ + mLowerQuartile = value; +} + +/*! + Sets the parameter "median" of the statistical box plot. This is the value of the median mark + inside the quartile box. The median separates the sample data in half (50% of the sample data is + below/above the median). + + \see setMedianPen +*/ +void QCPStatisticalBox::setMedian(double value) +{ + mMedian = value; +} + +/*! + Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the + box. The lower and the upper quartiles are the two statistical quartiles around the median of the + sample, they contain 50% of the sample data. + + \see setLowerQuartile, setPen, setBrush, setWidth +*/ +void QCPStatisticalBox::setUpperQuartile(double value) +{ + mUpperQuartile = value; +} + +/*! + Sets the parameter "maximum" of the statistical box plot. This is the position of the upper + whisker, typically the maximum measurement of the sample that's not considered an outlier. + + \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth +*/ +void QCPStatisticalBox::setMaximum(double value) +{ + mMaximum = value; +} + +/*! + Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample + that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers + and displayed as such. + + \see setOutlierStyle +*/ +void QCPStatisticalBox::setOutliers(const QVector &values) +{ + mOutliers = values; +} + +/*! + Sets all parameters of the statistical box plot at once. + + \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum +*/ +void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum) +{ + setKey(key); + setMinimum(minimum); + setLowerQuartile(lowerQuartile); + setMedian(median); + setUpperQuartile(upperQuartile); + setMaximum(maximum); +} + +/*! + Sets the width of the box in key coordinates. + + \see setWhiskerWidth +*/ +void QCPStatisticalBox::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates. + + \see setWidth +*/ +void QCPStatisticalBox::setWhiskerWidth(double width) +{ + mWhiskerWidth = width; +} + +/*! + Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis). + + Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker backbone from reaching + a few pixels past the whisker bars, when using a non-zero pen width. + + \see setWhiskerBarPen +*/ +void QCPStatisticalBox::setWhiskerPen(const QPen &pen) +{ + mWhiskerPen = pen; +} + +/*! + Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at + each end of the whisker backbone). + + \see setWhiskerPen +*/ +void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen) +{ + mWhiskerBarPen = pen; +} + +/*! + Sets the pen used for drawing the median indicator line inside the statistical box. +*/ +void QCPStatisticalBox::setMedianPen(const QPen &pen) +{ + mMedianPen = pen; +} + +/*! + Sets the appearance of the outlier data points. + + \see setOutliers +*/ +void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style) +{ + mOutlierStyle = style; +} + +/* inherits documentation from base class */ +void QCPStatisticalBox::clearData() +{ + setOutliers(QVector()); + setKey(0); + setMinimum(0); + setLowerQuartile(0); + setMedian(0); + setUpperQuartile(0); + setMaximum(0); +} + +/* inherits documentation from base class */ +double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + { + double posKey, posValue; + pixelsToCoords(pos, posKey, posValue); + // quartile box: + QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5); + QCPRange valueRange(mLowerQuartile, mUpperQuartile); + if (keyRange.contains(posKey) && valueRange.contains(posValue)) + return mParentPlot->selectionTolerance()*0.99; + + // min/max whiskers: + if (QCPRange(mMinimum, mMaximum).contains(posValue)) + return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey)); + } + return -1; +} + +/* inherits documentation from base class */ +void QCPStatisticalBox::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + if (QCP::isInvalidData(mKey, mMedian) || + QCP::isInvalidData(mLowerQuartile, mUpperQuartile) || + QCP::isInvalidData(mMinimum, mMaximum)) + qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name(); + for (int i=0; isave(); + painter->setClipRect(quartileBox, Qt::IntersectClip); + drawMedian(painter); + painter->restore(); + + drawWhiskers(painter); + drawOutliers(painter); +} + +/* inherits documentation from base class */ +void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw filled rect: + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->setBrush(mBrush); + QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67); + r.moveCenter(rect.center()); + painter->drawRect(r); +} + +/*! \internal + + Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel + coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so + the median doesn't draw outside the quartile box). +*/ +void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const +{ + QRectF box; + box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile)); + box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile)); + applyDefaultAntialiasingHint(painter); + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + painter->drawRect(box); + if (quartileBox) + *quartileBox = box; +} + +/*! \internal + + Draws the median line inside the quartile box. +*/ +void QCPStatisticalBox::drawMedian(QCPPainter *painter) const +{ + QLineF medianLine; + medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian)); + medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian)); + applyDefaultAntialiasingHint(painter); + painter->setPen(mMedianPen); + painter->drawLine(medianLine); +} + +/*! \internal + + Draws both whisker backbones and bars. +*/ +void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const +{ + QLineF backboneMin, backboneMax, barMin, barMax; + backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum)); + backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum)); + barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum)); + barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum)); + applyErrorBarsAntialiasingHint(painter); + painter->setPen(mWhiskerPen); + painter->drawLine(backboneMin); + painter->drawLine(backboneMax); + painter->setPen(mWhiskerBarPen); + painter->drawLine(barMin); + painter->drawLine(barMax); +} + +/*! \internal + + Draws the outlier scatter points. +*/ +void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const +{ + applyScattersAntialiasingHint(painter); + mOutlierStyle.applyTo(painter, mPen); + for (int i=0; i 0) + return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5); + else if (mKey > 0) + return QCPRange(mKey, mKey+mWidth*0.5); + else + { + foundRange = false; + return QCPRange(); + } + } + foundRange = false; + return QCPRange(); +} + +/* inherits documentation from base class */ +QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const +{ + QVector values; // values that must be considered (i.e. all outliers and the five box-parameters) + values.reserve(mOutliers.size() + 5); + values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum; + values << mOutliers; + // go through values and find the ones in legal range: + bool haveUpper = false; + bool haveLower = false; + double upper = 0; + double lower = 0; + for (int i=0; i 0) || + (inSignDomain == sdBoth)) + { + if (values.at(i) > upper || !haveUpper) + { + upper = values.at(i); + haveUpper = true; + } + if (values.at(i) < lower || !haveLower) + { + lower = values.at(i); + haveLower = true; + } + } + } + // return the bounds if we found some sensible values: + if (haveLower && haveUpper) + { + foundRange = true; + return QCPRange(lower, upper); + } else // might happen if all values are in other sign domain + { + foundRange = false; + return QCPRange(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorMapData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorMapData + \brief Holds the two-dimensional data of a QCPColorMap plottable. + + This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref + QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a + color, depending on the value. + + The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize). + Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref + setKeyRange, \ref setValueRange). + + The data cells can be accessed in two ways: They can be directly addressed by an integer index + with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot + coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are + provided by the functions \ref coordToCell and \ref cellToCoord. + + This class also buffers the minimum and maximum values that are in the data set, to provide + QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value + that is greater than the current maximum increases this maximum to the new value. However, + setting the cell that currently holds the maximum value to a smaller value doesn't decrease the + maximum again, because finding the true new maximum would require going through the entire data + array, which might be time consuming. The same holds for the data minimum. This functionality is + given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the + true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience + parameter \a recalculateDataBounds which may be set to true to automatically call \ref + recalculateDataBounds internally. +*/ + +/* start of documentation of inline functions */ + +/*! \fn bool QCPColorMapData::isEmpty() const + + Returns whether this instance carries no data. This is equivalent to having a size where at least + one of the dimensions is 0 (see \ref setSize). +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction + and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap + at the coordinates \a keyRange and \a valueRange. + + \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange +*/ +QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) : + mKeySize(0), + mValueSize(0), + mKeyRange(keyRange), + mValueRange(valueRange), + mIsEmpty(true), + mData(0), + mDataModified(true) +{ + setSize(keySize, valueSize); + fill(0); +} + +QCPColorMapData::~QCPColorMapData() +{ + if (mData) + delete[] mData; +} + +/*! + Constructs a new QCPColorMapData instance copying the data and range of \a other. +*/ +QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) : + mKeySize(0), + mValueSize(0), + mIsEmpty(true), + mData(0), + mDataModified(true) +{ + *this = other; +} + +/*! + Overwrites this color map data instance with the data stored in \a other. +*/ +QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other) +{ + if (&other != this) + { + const int keySize = other.keySize(); + const int valueSize = other.valueSize(); + setSize(keySize, valueSize); + setRange(other.keyRange(), other.valueRange()); + if (!mIsEmpty) + memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize); + mDataBounds = other.mDataBounds; + mDataModified = true; + } + return *this; +} + +/* undocumented getter */ +double QCPColorMapData::data(double key, double value) +{ + int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5; + int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5; + if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize) + return mData[valueCell*mKeySize + keyCell]; + else + return 0; +} + +/* undocumented getter */ +double QCPColorMapData::cell(int keyIndex, int valueIndex) +{ + if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize) + return mData[valueIndex*mKeySize + keyIndex]; + else + return 0; +} + +/*! + Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in + the value dimension. + + The current data is discarded and the map cells are set to 0, unless the map had already the + requested size. + + Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref + isEmpty returns true. + + \see setRange, setKeySize, setValueSize +*/ +void QCPColorMapData::setSize(int keySize, int valueSize) +{ + if (keySize != mKeySize || valueSize != mValueSize) + { + mKeySize = keySize; + mValueSize = valueSize; + if (mData) + delete[] mData; + mIsEmpty = mKeySize == 0 || mValueSize == 0; + if (!mIsEmpty) + { +#ifdef __EXCEPTIONS + try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message +#endif + mData = new double[mKeySize*mValueSize]; +#ifdef __EXCEPTIONS + } catch (...) { mData = 0; } +#endif + if (mData) + fill(0); + else + qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize; + } else + mData = 0; + mDataModified = true; + } +} + +/*! + Resizes the data array to have \a keySize cells in the key dimension. + + The current data is discarded and the map cells are set to 0, unless the map had already the + requested size. + + Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true. + + \see setKeyRange, setSize, setValueSize +*/ +void QCPColorMapData::setKeySize(int keySize) +{ + setSize(keySize, mValueSize); +} + +/*! + Resizes the data array to have \a valueSize cells in the value dimension. + + The current data is discarded and the map cells are set to 0, unless the map had already the + requested size. + + Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true. + + \see setValueRange, setSize, setKeySize +*/ +void QCPColorMapData::setValueSize(int valueSize) +{ + setSize(mKeySize, valueSize); +} + +/*! + Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area + covered by the color map in plot coordinates. + + The outer cells will be centered on the range boundaries given to this function. For example, if + the key size (\ref setKeySize) is 3 and \a keyRange is set to QCPRange(2, 3) there will + be cells centered on the key coordinates 2, 2.5 and 3. + + \see setSize +*/ +void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange) +{ + setKeyRange(keyRange); + setValueRange(valueRange); +} + +/*! + Sets the coordinate range the data shall be distributed over in the key dimension. Together with + the value range, This defines the rectangular area covered by the color map in plot coordinates. + + The outer cells will be centered on the range boundaries given to this function. For example, if + the key size (\ref setKeySize) is 3 and \a keyRange is set to QCPRange(2, 3) there will + be cells centered on the key coordinates 2, 2.5 and 3. + + \see setRange, setValueRange, setSize +*/ +void QCPColorMapData::setKeyRange(const QCPRange &keyRange) +{ + mKeyRange = keyRange; +} + +/*! + Sets the coordinate range the data shall be distributed over in the value dimension. Together with + the key range, This defines the rectangular area covered by the color map in plot coordinates. + + The outer cells will be centered on the range boundaries given to this function. For example, if + the value size (\ref setValueSize) is 3 and \a valueRange is set to QCPRange(2, 3) there + will be cells centered on the value coordinates 2, 2.5 and 3. + + \see setRange, setKeyRange, setSize +*/ +void QCPColorMapData::setValueRange(const QCPRange &valueRange) +{ + mValueRange = valueRange; +} + +/*! + Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a + z. + + \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or + value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, + you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to + determine the cell index. Rather directly access the cell index with \ref + QCPColorMapData::setCell. + + \see setCell, setRange +*/ +void QCPColorMapData::setData(double key, double value, double z) +{ + int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5; + int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5; + if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize) + { + mData[valueCell*mKeySize + keyCell] = z; + if (z < mDataBounds.lower) + mDataBounds.lower = z; + if (z > mDataBounds.upper) + mDataBounds.upper = z; + mDataModified = true; + } +} + +/*! + Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices + enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see + \ref setSize). + + In the standard plot configuration (horizontal key axis and vertical value axis, both not + range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with + indices (keySize-1, valueSize-1) is in the top right corner of the color map. + + \see setData, setSize +*/ +void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z) +{ + if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize) + { + mData[valueIndex*mKeySize + keyIndex] = z; + if (z < mDataBounds.lower) + mDataBounds.lower = z; + if (z > mDataBounds.upper) + mDataBounds.upper = z; + mDataModified = true; + } +} + +/*! + Goes through the data and updates the buffered minimum and maximum data values. + + Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange + and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten + with a smaller or larger value respectively, since the buffered maximum/minimum values have been + updated the last time. Why this is the case is explained in the class description (\ref + QCPColorMapData). + + Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a + recalculateDataBounds for convenience. Setting this to true will call this method for you, before + doing the rescale. +*/ +void QCPColorMapData::recalculateDataBounds() +{ + if (mKeySize > 0 && mValueSize > 0) + { + double minHeight = mData[0]; + double maxHeight = mData[0]; + const int dataCount = mValueSize*mKeySize; + for (int i=0; i maxHeight) + maxHeight = mData[i]; + if (mData[i] < minHeight) + minHeight = mData[i]; + } + mDataBounds.lower = minHeight; + mDataBounds.upper = maxHeight; + } +} + +/*! + Frees the internal data memory. + + This is equivalent to calling \ref setSize "setSize(0, 0)". +*/ +void QCPColorMapData::clear() +{ + setSize(0, 0); +} + +/*! + Sets all cells to the value \a z. +*/ +void QCPColorMapData::fill(double z) +{ + const int dataCount = mValueSize*mKeySize; + for (int i=0; i(data); + return; + } + if (copy) + { + *mMapData = *data; + } else + { + delete mMapData; + mMapData = data; + } + mMapImageInvalidated = true; +} + +/*! + Sets the data range of this color map to \a dataRange. The data range defines which data values + are mapped to the color gradient. + + To make the data range span the full range of the data set, use \ref rescaleDataRange. + + \see QCPColorScale::setDataRange +*/ +void QCPColorMap::setDataRange(const QCPRange &dataRange) +{ + if (!QCPRange::validRange(dataRange)) return; + if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper) + { + if (mDataScaleType == QCPAxis::stLogarithmic) + mDataRange = dataRange.sanitizedForLogScale(); + else + mDataRange = dataRange.sanitizedForLinScale(); + mMapImageInvalidated = true; + emit dataRangeChanged(mDataRange); + } +} + +/*! + Sets whether the data is correlated with the color gradient linearly or logarithmically. + + \see QCPColorScale::setDataScaleType +*/ +void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType) +{ + if (mDataScaleType != scaleType) + { + mDataScaleType = scaleType; + mMapImageInvalidated = true; + emit dataScaleTypeChanged(mDataScaleType); + if (mDataScaleType == QCPAxis::stLogarithmic) + setDataRange(mDataRange.sanitizedForLogScale()); + } +} + +/*! + Sets the color gradient that is used to represent the data. For more details on how to create an + own gradient or use one of the preset gradients, see \ref QCPColorGradient. + + The colors defined by the gradient will be used to represent data values in the currently set + data range, see \ref setDataRange. Data points that are outside this data range will either be + colored uniformly with the respective gradient boundary color, or the gradient will repeat, + depending on \ref QCPColorGradient::setPeriodic. + + \see QCPColorScale::setGradient +*/ +void QCPColorMap::setGradient(const QCPColorGradient &gradient) +{ + if (mGradient != gradient) + { + mGradient = gradient; + mMapImageInvalidated = true; + emit gradientChanged(mGradient); + } +} + +/*! + Sets whether the color map image shall use bicubic interpolation when displaying the color map + shrinked or expanded, and not at a 1:1 pixel-to-data scale. + + \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled" +*/ +void QCPColorMap::setInterpolate(bool enabled) +{ + mInterpolate = enabled; + mMapImageInvalidated = true; // because oversampling factors might need to change +} + +/*! + Sets whether the outer most data rows and columns are clipped to the specified key and value + range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange). + + if \a enabled is set to false, the data points at the border of the color map are drawn with the + same width and height as all other data points. Since the data points are represented by + rectangles of one color centered on the data coordinate, this means that the shown color map + extends by half a data point over the specified key/value range in each direction. + + \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled" +*/ +void QCPColorMap::setTightBoundary(bool enabled) +{ + mTightBoundary = enabled; +} + +/*! + Associates the color scale \a colorScale with this color map. + + This means that both the color scale and the color map synchronize their gradient, data range and + data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps + can be associated with one single color scale. This causes the color maps to also synchronize + those properties, via the mutual color scale. + + This function causes the color map to adopt the current color gradient, data range and data scale + type of \a colorScale. After this call, you may change these properties at either the color map + or the color scale, and the setting will be applied to both. + + Pass 0 as \a colorScale to disconnect the color scale from this color map again. +*/ +void QCPColorMap::setColorScale(QCPColorScale *colorScale) +{ + if (mColorScale) // unconnect signals from old color scale + { + disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange))); + disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType))); + disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient))); + disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient))); + disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + } + mColorScale = colorScale; + if (mColorScale) // connect signals to new color scale + { + setGradient(mColorScale.data()->gradient()); + setDataRange(mColorScale.data()->dataRange()); + setDataScaleType(mColorScale.data()->dataScaleType()); + connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange))); + connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType))); + connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient))); + connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient))); + connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + } +} + +/*! + Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the + current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods, + only for the third data dimension of the color map. + + The minimum and maximum values of the data set are buffered in the internal QCPColorMapData + instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref + QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For + performance reasons, however, they are only updated in an expanding fashion. So the buffered + maximum can only increase and the buffered minimum can only decrease. In consequence, changes to + the data that actually lower the maximum of the data set (by overwriting the cell holding the + current maximum with a smaller value), aren't recognized and the buffered maximum overestimates + the true maximum of the data set. The same happens for the buffered minimum. To recalculate the + true minimum and maximum by explicitly looking at each cell, the method + QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a + recalculateDataBounds calls this method before setting the data range to the buffered minimum and + maximum. + + \see setDataRange +*/ +void QCPColorMap::rescaleDataRange(bool recalculateDataBounds) +{ + if (recalculateDataBounds) + mMapData->recalculateDataBounds(); + setDataRange(mMapData->dataBounds()); +} + +/*! + Takes the current appearance of the color map and updates the legend icon, which is used to + represent this color map in the legend (see \ref QCPLegend). + + The \a transformMode specifies whether the rescaling is done by a faster, low quality image + scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm + (Qt::SmoothTransformation). + + The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to + the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured + legend icon size, the thumb will be rescaled during drawing of the legend item. + + \see setDataRange +*/ +void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize) +{ + if (mMapImage.isNull() && !data()->isEmpty()) + updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet) + + if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again + { + bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed(); + bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed(); + mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode); + } +} + +/*! + Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also + resizes the map to 0x0 cells. +*/ +void QCPColorMap::clearData() +{ + mMapData->clear(); +} + +/* inherits documentation from base class */ +double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + { + double posKey, posValue; + pixelsToCoords(pos, posKey, posValue); + if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue)) + return mParentPlot->selectionTolerance()*0.99; + } + return -1; +} + +/*! \internal + + Updates the internal map image buffer by going through the internal \ref QCPColorMapData and + turning the data values into color pixels with \ref QCPColorGradient::colorize. + + This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image + has been invalidated for a different reason (e.g. a change of the data range with \ref + setDataRange). + + If the map cell count is low, the image created will be oversampled in order to avoid a + QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images + without smooth transform enabled. Accordingly, oversampling isn't performed if \ref + setInterpolate is true. +*/ +void QCPColorMap::updateMapImage() +{ + QCPAxis *keyAxis = mKeyAxis.data(); + if (!keyAxis) return; + if (mMapData->isEmpty()) return; + + const int keySize = mMapData->keySize(); + const int valueSize = mMapData->valueSize(); + int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + + // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation: + if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor)) + mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32); + else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor)) + mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32); + + QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + // resize undersampled map image to actual key/value cell sizes: + if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize)) + mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32); + else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize)) + mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32); + localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image + } else if (!mUndersampledMapImage.isNull()) + mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it + + const double *rawData = mMapData->mData; + if (keyAxis->orientation() == Qt::Horizontal) + { + const int lineCount = valueSize; + const int rowCount = keySize; + for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); + } + } else // keyAxis->orientation() == Qt::Vertical + { + const int lineCount = keySize; + const int rowCount = valueSize; + for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); + } + } + + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + if (keyAxis->orientation() == Qt::Horizontal) + mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + else + mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + } + mMapData->mDataModified = false; + mMapImageInvalidated = false; +} + +/* inherits documentation from base class */ +void QCPColorMap::draw(QCPPainter *painter) +{ + if (mMapData->isEmpty()) return; + if (!mKeyAxis || !mValueAxis) return; + applyDefaultAntialiasingHint(painter); + + if (mMapData->mDataModified || mMapImageInvalidated) + updateMapImage(); + + // use buffer if painting vectorized (PDF): + bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized); + QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized + QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in + QPixmap mapBuffer; + double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps + if (useBuffer) + { + mapBufferTarget = painter->clipRegion().boundingRect(); + mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize()); + mapBuffer.fill(Qt::transparent); + localPainter = new QCPPainter(&mapBuffer); + localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio); + localPainter->translate(-mapBufferTarget.topLeft()); + } + + QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), + coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(); + // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary): + double halfCellWidth = 0; // in pixels + double halfCellHeight = 0; // in pixels + if (keyAxis()->orientation() == Qt::Horizontal) + { + if (mMapData->keySize() > 1) + halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1); + if (mMapData->valueSize() > 1) + halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1); + } else // keyAxis orientation is Qt::Vertical + { + if (mMapData->keySize() > 1) + halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1); + if (mMapData->valueSize() > 1) + halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1); + } + imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight); + bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed(); + bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed(); + bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform); + localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate); + QRegion clipBackup; + if (mTightBoundary) + { + clipBackup = localPainter->clipRegion(); + QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), + coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(); + localPainter->setClipRect(tightClipRect, Qt::IntersectClip); + } + localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY)); + if (mTightBoundary) + localPainter->setClipRegion(clipBackup); + localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup); + + if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter + { + delete localPainter; + painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer); + } +} + +/* inherits documentation from base class */ +void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + applyDefaultAntialiasingHint(painter); + // draw map thumbnail: + if (!mLegendIcon.isNull()) + { + QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation); + QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height()); + iconRect.moveCenter(rect.center()); + painter->drawPixmap(iconRect.topLeft(), scaledIcon); + } + /* + // draw frame: + painter->setBrush(Qt::NoBrush); + painter->setPen(Qt::black); + painter->drawRect(rect.adjusted(1, 1, 0, 0)); + */ +} + +/* inherits documentation from base class */ +QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const +{ + foundRange = true; + QCPRange result = mMapData->keyRange(); + result.normalize(); + if (inSignDomain == QCPAbstractPlottable::sdPositive) + { + if (result.lower <= 0 && result.upper > 0) + result.lower = result.upper*1e-3; + else if (result.lower <= 0 && result.upper <= 0) + foundRange = false; + } else if (inSignDomain == QCPAbstractPlottable::sdNegative) + { + if (result.upper >= 0 && result.lower < 0) + result.upper = result.lower*1e-3; + else if (result.upper >= 0 && result.lower >= 0) + foundRange = false; + } + return result; +} + +/* inherits documentation from base class */ +QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const +{ + foundRange = true; + QCPRange result = mMapData->valueRange(); + result.normalize(); + if (inSignDomain == QCPAbstractPlottable::sdPositive) + { + if (result.lower <= 0 && result.upper > 0) + result.lower = result.upper*1e-3; + else if (result.lower <= 0 && result.upper <= 0) + foundRange = false; + } else if (inSignDomain == QCPAbstractPlottable::sdNegative) + { + if (result.upper >= 0 && result.lower < 0) + result.upper = result.lower*1e-3; + else if (result.upper >= 0 && result.lower >= 0) + foundRange = false; + } + return result; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPFinancialData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPFinancialData + \brief Holds the data of one single data point for QCPFinancial. + + The container for storing multiple data points is \ref QCPFinancialDataMap. + + The stored data is: + \li \a key: coordinate on the key axis of this data point + \li \a open: The opening value at the data point + \li \a high: The high/maximum value at the data point + \li \a low: The low/minimum value at the data point + \li \a close: The closing value at the data point + + \see QCPFinancialDataMap +*/ + +/*! + Constructs a data point with key and all values set to zero. +*/ +QCPFinancialData::QCPFinancialData() : + key(0), + open(0), + high(0), + low(0), + close(0) +{ +} + +/*! + Constructs a data point with the specified \a key and OHLC values. +*/ +QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) : + key(key), + open(open), + high(high), + low(low), + close(close) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPFinancial +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPFinancial + \brief A plottable representing a financial stock chart + + \image html QCPFinancial.png + + This plottable represents time series data binned to certain intervals, mainly used for stock + charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be + set via \ref setChartStyle. + + The data is passed via \ref setData as a set of open/high/low/close values at certain keys + (typically times). This means the data must be already binned appropriately. If data is only + available as a series of values (e.g. \a price against \a time), you can use the static + convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed + to \ref setData. + + The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and is given in plot + key coordinates. A typical choice is to set it to (or slightly less than) one bin interval width. + + \section appearance Changing the appearance + + Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored, + lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush). + + If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are + represented with a different pen and brush than negative changes (\a close < \a open). These can + be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref + setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection + however, the normal selected pen/brush (\ref setSelectedPen, \ref setSelectedBrush) is used, + irrespective of whether the chart is single- or two-colored. + +*/ + +/* start of documentation of inline functions */ + +/*! \fn QCPFinancialDataMap *QCPFinancial::data() const + + Returns a pointer to the internal data storage of type \ref QCPFinancialDataMap. You may use it to + directly manipulate the data, which may be more convenient and faster than using the regular \ref + setData or \ref addData methods, in certain situations. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The constructed QCPFinancial can be added to the plot with QCustomPlot::addPlottable, QCustomPlot + then takes ownership of the financial chart. +*/ +QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + mData(0), + mChartStyle(csOhlc), + mWidth(0.5), + mTwoColored(false), + mBrushPositive(QBrush(QColor(210, 210, 255))), + mBrushNegative(QBrush(QColor(255, 210, 210))), + mPenPositive(QPen(QColor(10, 40, 180))), + mPenNegative(QPen(QColor(180, 40, 10))) +{ + mData = new QCPFinancialDataMap; + + setSelectedPen(QPen(QColor(80, 80, 255), 2.5)); + setSelectedBrush(QBrush(QColor(80, 80, 255))); +} + +QCPFinancial::~QCPFinancial() +{ + delete mData; +} + +/*! + Replaces the current data with the provided \a data. + + If \a copy is set to true, data points in \a data will only be copied. if false, the plottable + takes ownership of the passed data and replaces the internal data pointer with it. This is + significantly faster than copying for large datasets. + + Alternatively, you can also access and modify the plottable's data via the \ref data method, which + returns a pointer to the internal \ref QCPFinancialDataMap. + + \see timeSeriesToOhlc +*/ +void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy) +{ + if (mData == data) + { + qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast(data); + return; + } + if (copy) + { + *mData = *data; + } else + { + delete mData; + mData = data; + } +} + +/*! \overload + + Replaces the current data with the provided open/high/low/close data. The provided vectors should + have equal length. Else, the number of added points will be the size of the smallest vector. + + \see timeSeriesToOhlc +*/ +void QCPFinancial::setData(const QVector &key, const QVector &open, const QVector &high, const QVector &low, const QVector &close) +{ + mData->clear(); + int n = key.size(); + n = qMin(n, open.size()); + n = qMin(n, high.size()); + n = qMin(n, low.size()); + n = qMin(n, close.size()); + for (int i=0; iinsertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i])); + } +} + +/*! + Sets which representation style shall be used to display the OHLC data. +*/ +void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style) +{ + mChartStyle = style; +} + +/*! + Sets the width of the individual bars/candlesticks to \a width in plot key coordinates. + + A typical choice is to set it to (or slightly less than) one bin interval width. +*/ +void QCPFinancial::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets whether this chart shall contrast positive from negative trends per data point by using two + separate colors to draw the respective bars/candlesticks. + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative +*/ +void QCPFinancial::setTwoColored(bool twoColored) +{ + mTwoColored = twoColored; +} + +/*! + If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills + of data points with a positive trend (i.e. bars/candlesticks with close >= open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setBrushNegative, setPenPositive, setPenNegative +*/ +void QCPFinancial::setBrushPositive(const QBrush &brush) +{ + mBrushPositive = brush; +} + +/*! + If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills + of data points with a negative trend (i.e. bars/candlesticks with close < open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setBrushPositive, setPenNegative, setPenPositive +*/ +void QCPFinancial::setBrushNegative(const QBrush &brush) +{ + mBrushNegative = brush; +} + +/*! + If \ref setTwoColored is set to true, this function controls the pen that is used to draw + outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setPenNegative, setBrushPositive, setBrushNegative +*/ +void QCPFinancial::setPenPositive(const QPen &pen) +{ + mPenPositive = pen; +} + +/*! + If \ref setTwoColored is set to true, this function controls the pen that is used to draw + outlines of data points with a negative trend (i.e. bars/candlesticks with close < open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setPenPositive, setBrushNegative, setBrushPositive +*/ +void QCPFinancial::setPenNegative(const QPen &pen) +{ + mPenNegative = pen; +} + +/*! + Adds the provided data points in \a dataMap to the current data. + + Alternatively, you can also access and modify the data via the \ref data method, which returns a + pointer to the internal \ref QCPFinancialDataMap. + + \see removeData +*/ +void QCPFinancial::addData(const QCPFinancialDataMap &dataMap) +{ + mData->unite(dataMap); +} + +/*! \overload + + Adds the provided single data point in \a data to the current data. + + Alternatively, you can also access and modify the data via the \ref data method, which returns a + pointer to the internal \ref QCPFinancialData. + + \see removeData +*/ +void QCPFinancial::addData(const QCPFinancialData &data) +{ + mData->insertMulti(data.key, data); +} + +/*! \overload + + Adds the provided single data point given by \a key, \a open, \a high, \a low, and \a close to + the current data. + + Alternatively, you can also access and modify the data via the \ref data method, which returns a + pointer to the internal \ref QCPFinancialData. + + \see removeData +*/ +void QCPFinancial::addData(double key, double open, double high, double low, double close) +{ + mData->insertMulti(key, QCPFinancialData(key, open, high, low, close)); +} + +/*! \overload + + Adds the provided open/high/low/close data to the current data. + + Alternatively, you can also access and modify the data via the \ref data method, which returns a + pointer to the internal \ref QCPFinancialData. + + \see removeData +*/ +void QCPFinancial::addData(const QVector &key, const QVector &open, const QVector &high, const QVector &low, const QVector &close) +{ + int n = key.size(); + n = qMin(n, open.size()); + n = qMin(n, high.size()); + n = qMin(n, low.size()); + n = qMin(n, close.size()); + for (int i=0; iinsertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i])); + } +} + +/*! + Removes all data points with keys smaller than \a key. + + \see addData, clearData +*/ +void QCPFinancial::removeDataBefore(double key) +{ + QCPFinancialDataMap::iterator it = mData->begin(); + while (it != mData->end() && it.key() < key) + it = mData->erase(it); +} + +/*! + Removes all data points with keys greater than \a key. + + \see addData, clearData +*/ +void QCPFinancial::removeDataAfter(double key) +{ + if (mData->isEmpty()) return; + QCPFinancialDataMap::iterator it = mData->upperBound(key); + while (it != mData->end()) + it = mData->erase(it); +} + +/*! + Removes all data points with keys between \a fromKey and \a toKey. if \a fromKey is greater or + equal to \a toKey, the function does nothing. To remove a single data point with known key, use + \ref removeData(double key). + + \see addData, clearData +*/ +void QCPFinancial::removeData(double fromKey, double toKey) +{ + if (fromKey >= toKey || mData->isEmpty()) return; + QCPFinancialDataMap::iterator it = mData->upperBound(fromKey); + QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey); + while (it != itEnd) + it = mData->erase(it); +} + +/*! \overload + + Removes a single data point at \a key. If the position is not known with absolute precision, + consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval + around the suspected position, depeding on the precision with which the key is known. + + \see addData, clearData +*/ +void QCPFinancial::removeData(double key) +{ + mData->remove(key); +} + +/*! + Removes all data points. + + \see removeData, removeDataAfter, removeDataBefore +*/ +void QCPFinancial::clearData() +{ + mData->clear(); +} + +/* inherits documentation from base class */ +double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint())) + { + // get visible data range: + QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point + getVisibleDataBounds(lower, upper); + if (lower == mData->constEnd() || upper == mData->constEnd()) + return -1; + // perform select test according to configured style: + switch (mChartStyle) + { + case QCPFinancial::csOhlc: + return ohlcSelectTest(pos, lower, upper+1); break; + case QCPFinancial::csCandlestick: + return candlestickSelectTest(pos, lower, upper+1); break; + } + } + return -1; +} + +/*! + A convenience function that converts time series data (\a value against \a time) to OHLC binned + data points. The return value can then be passed on to \ref setData. + + The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given. + For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour + each, set \a timeBinSize to 3600. + + \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The + value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys. + It merely defines the mathematical offset/phase of the bins that will be used to process the + data. +*/ +QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector &time, const QVector &value, double timeBinSize, double timeBinOffset) +{ + QCPFinancialDataMap map; + int count = qMin(time.size(), value.size()); + if (count == 0) + return QCPFinancialDataMap(); + + QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first()); + int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5); + for (int i=0; i currentBinData.high) currentBinData.high = value.at(i); + if (i == count-1) // last data point is in current bin, finalize bin: + { + currentBinData.close = value.at(i); + currentBinData.key = timeBinOffset+(index)*timeBinSize; + map.insert(currentBinData.key, currentBinData); + } + } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map: + { + // finalize current bin: + currentBinData.close = value.at(i-1); + currentBinData.key = timeBinOffset+(index-1)*timeBinSize; + map.insert(currentBinData.key, currentBinData); + // start next bin: + currentBinIndex = index; + currentBinData.open = value.at(i); + currentBinData.high = value.at(i); + currentBinData.low = value.at(i); + } + } + + return map; +} + +/* inherits documentation from base class */ +void QCPFinancial::draw(QCPPainter *painter) +{ + // get visible data range: + QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point + getVisibleDataBounds(lower, upper); + if (lower == mData->constEnd() || upper == mData->constEnd()) + return; + + // draw visible data range according to configured style: + switch (mChartStyle) + { + case QCPFinancial::csOhlc: + drawOhlcPlot(painter, lower, upper+1); break; + case QCPFinancial::csCandlestick: + drawCandlestickPlot(painter, lower, upper+1); break; + } +} + +/* inherits documentation from base class */ +void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing + if (mChartStyle == csOhlc) + { + if (mTwoColored) + { + // draw upper left half icon with positive color: + painter->setBrush(mBrushPositive); + painter->setPen(mPenPositive); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft())); + // draw bottom right hald icon with negative color: + painter->setBrush(mBrushNegative); + painter->setPen(mPenNegative); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft())); + } else + { + painter->setBrush(mBrush); + painter->setPen(mPen); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft())); + } + } else if (mChartStyle == csCandlestick) + { + if (mTwoColored) + { + // draw upper left half icon with positive color: + painter->setBrush(mBrushPositive); + painter->setPen(mPenPositive); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft())); + // draw bottom right hald icon with negative color: + painter->setBrush(mBrushNegative); + painter->setPen(mPenNegative); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft())); + } else + { + painter->setBrush(mBrush); + painter->setPen(mPen); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft())); + } + } +} + +/* inherits documentation from base class */ +QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current; + QCPFinancialDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + current = it.value().key; + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + // determine exact range by including width of bars/flags: + if (haveLower && mKeyAxis) + range.lower = range.lower-mWidth*0.5; + if (haveUpper && mKeyAxis) + range.upper = range.upper+mWidth*0.5; + foundRange = haveLower && haveUpper; + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPFinancial::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + QCPFinancialDataMap::const_iterator it = mData->constBegin(); + while (it != mData->constEnd()) + { + // high: + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0)) + { + if (it.value().high < range.lower || !haveLower) + { + range.lower = it.value().high; + haveLower = true; + } + if (it.value().high > range.upper || !haveUpper) + { + range.upper = it.value().high; + haveUpper = true; + } + } + // low: + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0)) + { + if (it.value().low < range.lower || !haveLower) + { + range.lower = it.value().low; + haveLower = true; + } + if (it.value().low > range.upper || !haveUpper) + { + range.upper = it.value().low; + haveUpper = true; + } + } + ++it; + } + + foundRange = haveLower && haveUpper; + return range; +} + +/*! \internal + + Draws the data from \a begin to \a end as OHLC bars with the provided \a painter. + + This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc. +*/ +void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QPen linePen; + + if (keyAxis->orientation() == Qt::Horizontal) + { + for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) + { + if (mSelected) + linePen = mSelectedPen; + else if (mTwoColored) + linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative; + else + linePen = mPen; + painter->setPen(linePen); + double keyPixel = keyAxis->coordToPixel(it.value().key); + double openPixel = valueAxis->coordToPixel(it.value().open); + double closePixel = valueAxis->coordToPixel(it.value().close); + // draw backbone: + painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low))); + // draw open: + double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides + painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel)); + // draw close: + painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel)); + } + } else + { + for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) + { + if (mSelected) + linePen = mSelectedPen; + else if (mTwoColored) + linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative; + else + linePen = mPen; + painter->setPen(linePen); + double keyPixel = keyAxis->coordToPixel(it.value().key); + double openPixel = valueAxis->coordToPixel(it.value().open); + double closePixel = valueAxis->coordToPixel(it.value().close); + // draw backbone: + painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel)); + // draw open: + double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides + painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel)); + // draw close: + painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels)); + } + } +} + +/*! \internal + + Draws the data from \a begin to \a end as Candlesticks with the provided \a painter. + + This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick. +*/ +void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QPen linePen; + QBrush boxBrush; + + if (keyAxis->orientation() == Qt::Horizontal) + { + for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) + { + if (mSelected) + { + linePen = mSelectedPen; + boxBrush = mSelectedBrush; + } else if (mTwoColored) + { + if (it.value().close >= it.value().open) + { + linePen = mPenPositive; + boxBrush = mBrushPositive; + } else + { + linePen = mPenNegative; + boxBrush = mBrushNegative; + } + } else + { + linePen = mPen; + boxBrush = mBrush; + } + painter->setPen(linePen); + painter->setBrush(boxBrush); + double keyPixel = keyAxis->coordToPixel(it.value().key); + double openPixel = valueAxis->coordToPixel(it.value().open); + double closePixel = valueAxis->coordToPixel(it.value().close); + // draw high: + painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close)))); + // draw low: + painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close)))); + // draw open-close box: + double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); + painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel))); + } + } else // keyAxis->orientation() == Qt::Vertical + { + for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it) + { + if (mSelected) + { + linePen = mSelectedPen; + boxBrush = mSelectedBrush; + } else if (mTwoColored) + { + if (it.value().close >= it.value().open) + { + linePen = mPenPositive; + boxBrush = mBrushPositive; + } else + { + linePen = mPenNegative; + boxBrush = mBrushNegative; + } + } else + { + linePen = mPen; + boxBrush = mBrush; + } + painter->setPen(linePen); + painter->setBrush(boxBrush); + double keyPixel = keyAxis->coordToPixel(it.value().key); + double openPixel = valueAxis->coordToPixel(it.value().open); + double closePixel = valueAxis->coordToPixel(it.value().close); + // draw high: + painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel)); + // draw low: + painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel)); + // draw open-close box: + double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); + painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels))); + } + } +} + +/*! \internal + + This method is a helper function for \ref selectTest. It is used to test for selection when the + chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end. +*/ +double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + double minDistSqr = std::numeric_limits::max(); + QCPFinancialDataMap::const_iterator it; + if (keyAxis->orientation() == Qt::Horizontal) + { + for (it = begin; it != end; ++it) + { + double keyPixel = keyAxis->coordToPixel(it.value().key); + // calculate distance to backbone: + double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos); + if (currentDistSqr < minDistSqr) + minDistSqr = currentDistSqr; + } + } else // keyAxis->orientation() == Qt::Vertical + { + for (it = begin; it != end; ++it) + { + double keyPixel = keyAxis->coordToPixel(it.value().key); + // calculate distance to backbone: + double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos); + if (currentDistSqr < minDistSqr) + minDistSqr = currentDistSqr; + } + } + return qSqrt(minDistSqr); +} + +/*! \internal + + This method is a helper function for \ref selectTest. It is used to test for selection when the + chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a + end. +*/ +double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + double minDistSqr = std::numeric_limits::max(); + QCPFinancialDataMap::const_iterator it; + if (keyAxis->orientation() == Qt::Horizontal) + { + for (it = begin; it != end; ++it) + { + double currentDistSqr; + // determine whether pos is in open-close-box: + QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5); + QCPRange boxValueRange(it.value().close, it.value().open); + double posKey, posValue; + pixelsToCoords(pos, posKey, posValue); + if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box + { + currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99; + } else + { + // calculate distance to high/low lines: + double keyPixel = keyAxis->coordToPixel(it.value().key); + double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos); + double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos); + currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr); + } + if (currentDistSqr < minDistSqr) + minDistSqr = currentDistSqr; + } + } else // keyAxis->orientation() == Qt::Vertical + { + for (it = begin; it != end; ++it) + { + double currentDistSqr; + // determine whether pos is in open-close-box: + QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5); + QCPRange boxValueRange(it.value().close, it.value().open); + double posKey, posValue; + pixelsToCoords(pos, posKey, posValue); + if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box + { + currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99; + } else + { + // calculate distance to high/low lines: + double keyPixel = keyAxis->coordToPixel(it.value().key); + double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos); + double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos); + currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr); + } + if (currentDistSqr < minDistSqr) + minDistSqr = currentDistSqr; + } + } + return qSqrt(minDistSqr); +} + +/*! \internal + + called by the drawing methods to determine which data (key) range is visible at the current key + axis range setting, so only that needs to be processed. + + \a lower returns an iterator to the lowest data point that needs to be taken into account when + plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a + lower may still be just outside the visible range. + + \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie + just outside of the visible range. + + if the plottable contains no data, both \a lower and \a upper point to constEnd. + + \see QCPGraph::getVisibleDataBounds +*/ +void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const +{ + if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + if (mData->isEmpty()) + { + lower = mData->constEnd(); + upper = mData->constEnd(); + return; + } + + // get visible data range as QMap iterators + QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower); + QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper); + bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range + bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range + + lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn + upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemStraightLine +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemStraightLine + \brief A straight line that spans infinitely in both directions + + \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a point1 and \a point2, which define the straight line. +*/ + +/*! + Creates a straight line item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + point1(createPosition(QLatin1String("point1"))), + point2(createPosition(QLatin1String("point2"))) +{ + point1->setCoords(0, 0); + point2->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); +} + +QCPItemStraightLine::~QCPItemStraightLine() +{ +} + +/*! + Sets the pen that will be used to draw the line + + \see setSelectedPen +*/ +void QCPItemStraightLine::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line when selected + + \see setPen, setSelected +*/ +void QCPItemStraightLine::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/* inherits documentation from base class */ +double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos)); +} + +/* inherits documentation from base class */ +void QCPItemStraightLine::draw(QCPPainter *painter) +{ + QVector2D start(point1->pixelPoint()); + QVector2D end(point2->pixelPoint()); + // get visible segment of straight line inside clipRect: + double clipPad = mainPen().widthF(); + QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad)); + // paint visible segment, if existent: + if (!line.isNull()) + { + painter->setPen(mainPen()); + painter->drawLine(line); + } +} + +/*! \internal + + finds the shortest distance of \a point to the straight line defined by the base point \a + base and the direction vector \a vec. + + This is a helper function for \ref selectTest. +*/ +double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const +{ + return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length(); +} + +/*! \internal + + Returns the section of the straight line defined by \a base and direction vector \a + vec, that is visible in the specified \a rect. + + This is a helper function for \ref draw. +*/ +QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const +{ + double bx, by; + double gamma; + QLineF result; + if (vec.x() == 0 && vec.y() == 0) + return result; + if (qFuzzyIsNull(vec.x())) // line is vertical + { + // check top of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y(); + if (gamma >= 0 && gamma <= rect.width()) + result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical + } else if (qFuzzyIsNull(vec.y())) // line is horizontal + { + // check left of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x(); + if (gamma >= 0 && gamma <= rect.height()) + result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal + } else // line is skewed + { + QList pointVectors; + // check top of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QVector2D(bx+gamma, by)); + // check bottom of rect: + bx = rect.left(); + by = rect.bottom(); + gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QVector2D(bx+gamma, by)); + // check left of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QVector2D(bx, by+gamma)); + // check right of rect: + bx = rect.right(); + by = rect.top(); + gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QVector2D(bx, by+gamma)); + + // evaluate points: + if (pointVectors.size() == 2) + { + result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF()); + } else if (pointVectors.size() > 2) + { + // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance: + double distSqrMax = 0; + QVector2D pv1, pv2; + for (int i=0; i distSqrMax) + { + pv1 = pointVectors.at(i); + pv2 = pointVectors.at(k); + distSqrMax = distSqr; + } + } + } + result.setPoints(pv1.toPointF(), pv2.toPointF()); + } + } + return result; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemStraightLine::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemLine +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemLine + \brief A line from one point to another + + \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a start and \a end, which define the end points of the line. + + With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow. +*/ + +/*! + Creates a line item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + start(createPosition(QLatin1String("start"))), + end(createPosition(QLatin1String("end"))) +{ + start->setCoords(0, 0); + end->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); +} + +QCPItemLine::~QCPItemLine() +{ +} + +/*! + Sets the pen that will be used to draw the line + + \see setSelectedPen +*/ +void QCPItemLine::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line when selected + + \see setPen, setSelected +*/ +void QCPItemLine::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the line ending style of the head. The head corresponds to the \a end position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode + + \see setTail +*/ +void QCPItemLine::setHead(const QCPLineEnding &head) +{ + mHead = head; +} + +/*! + Sets the line ending style of the tail. The tail corresponds to the \a start position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode + + \see setHead +*/ +void QCPItemLine::setTail(const QCPLineEnding &tail) +{ + mTail = tail; +} + +/* inherits documentation from base class */ +double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos)); +} + +/* inherits documentation from base class */ +void QCPItemLine::draw(QCPPainter *painter) +{ + QVector2D startVec(start->pixelPoint()); + QVector2D endVec(end->pixelPoint()); + if (startVec.toPoint() == endVec.toPoint()) + return; + // get visible segment of straight line inside clipRect: + double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance()); + clipPad = qMax(clipPad, (double)mainPen().widthF()); + QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad)); + // paint visible segment, if existent: + if (!line.isNull()) + { + painter->setPen(mainPen()); + painter->drawLine(line); + painter->setBrush(Qt::SolidPattern); + if (mTail.style() != QCPLineEnding::esNone) + mTail.draw(painter, startVec, startVec-endVec); + if (mHead.style() != QCPLineEnding::esNone) + mHead.draw(painter, endVec, endVec-startVec); + } +} + +/*! \internal + + Returns the section of the line defined by \a start and \a end, that is visible in the specified + \a rect. + + This is a helper function for \ref draw. +*/ +QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const +{ + bool containsStart = rect.contains(start.x(), start.y()); + bool containsEnd = rect.contains(end.x(), end.y()); + if (containsStart && containsEnd) + return QLineF(start.toPointF(), end.toPointF()); + + QVector2D base = start; + QVector2D vec = end-start; + double bx, by; + double gamma, mu; + QLineF result; + QList pointVectors; + + if (!qFuzzyIsNull(vec.y())) // line is not horizontal + { + // check top of rect: + bx = rect.left(); + by = rect.top(); + mu = (by-base.y())/vec.y(); + if (mu >= 0 && mu <= 1) + { + gamma = base.x()-bx + mu*vec.x(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QVector2D(bx+gamma, by)); + } + // check bottom of rect: + bx = rect.left(); + by = rect.bottom(); + mu = (by-base.y())/vec.y(); + if (mu >= 0 && mu <= 1) + { + gamma = base.x()-bx + mu*vec.x(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QVector2D(bx+gamma, by)); + } + } + if (!qFuzzyIsNull(vec.x())) // line is not vertical + { + // check left of rect: + bx = rect.left(); + by = rect.top(); + mu = (bx-base.x())/vec.x(); + if (mu >= 0 && mu <= 1) + { + gamma = base.y()-by + mu*vec.y(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QVector2D(bx, by+gamma)); + } + // check right of rect: + bx = rect.right(); + by = rect.top(); + mu = (bx-base.x())/vec.x(); + if (mu >= 0 && mu <= 1) + { + gamma = base.y()-by + mu*vec.y(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QVector2D(bx, by+gamma)); + } + } + + if (containsStart) + pointVectors.append(start); + if (containsEnd) + pointVectors.append(end); + + // evaluate points: + if (pointVectors.size() == 2) + { + result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF()); + } else if (pointVectors.size() > 2) + { + // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance: + double distSqrMax = 0; + QVector2D pv1, pv2; + for (int i=0; i distSqrMax) + { + pv1 = pointVectors.at(i); + pv2 = pointVectors.at(k); + distSqrMax = distSqr; + } + } + } + result.setPoints(pv1.toPointF(), pv2.toPointF()); + } + return result; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemLine::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemCurve +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemCurve + \brief A curved line from one point to another + + \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions." + + It has four positions, \a start and \a end, which define the end points of the line, and two + control points which define the direction the line exits from the start and the direction from + which it approaches the end: \a startDir and \a endDir. + + With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an + arrow. + + Often it is desirable for the control points to stay at fixed relative positions to the start/end + point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start, + and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir. +*/ + +/*! + Creates a curve item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + start(createPosition(QLatin1String("start"))), + startDir(createPosition(QLatin1String("startDir"))), + endDir(createPosition(QLatin1String("endDir"))), + end(createPosition(QLatin1String("end"))) +{ + start->setCoords(0, 0); + startDir->setCoords(0.5, 0); + endDir->setCoords(0, 0.5); + end->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); +} + +QCPItemCurve::~QCPItemCurve() +{ +} + +/*! + Sets the pen that will be used to draw the line + + \see setSelectedPen +*/ +void QCPItemCurve::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line when selected + + \see setPen, setSelected +*/ +void QCPItemCurve::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the line ending style of the head. The head corresponds to the \a end position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode + + \see setTail +*/ +void QCPItemCurve::setHead(const QCPLineEnding &head) +{ + mHead = head; +} + +/*! + Sets the line ending style of the tail. The tail corresponds to the \a start position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode + + \see setHead +*/ +void QCPItemCurve::setTail(const QCPLineEnding &tail) +{ + mTail = tail; +} + +/* inherits documentation from base class */ +double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QPointF startVec(start->pixelPoint()); + QPointF startDirVec(startDir->pixelPoint()); + QPointF endDirVec(endDir->pixelPoint()); + QPointF endVec(end->pixelPoint()); + + QPainterPath cubicPath(startVec); + cubicPath.cubicTo(startDirVec, endDirVec, endVec); + + QPolygonF polygon = cubicPath.toSubpathPolygons().first(); + double minDistSqr = std::numeric_limits::max(); + for (int i=1; ipixelPoint()); + QPointF startDirVec(startDir->pixelPoint()); + QPointF endDirVec(endDir->pixelPoint()); + QPointF endVec(end->pixelPoint()); + if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash + return; + + QPainterPath cubicPath(startVec); + cubicPath.cubicTo(startDirVec, endDirVec, endVec); + + // paint visible segment, if existent: + QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF()); + QRect cubicRect = cubicPath.controlPointRect().toRect(); + if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position + cubicRect.adjust(0, 0, 1, 1); + if (clip.intersects(cubicRect)) + { + painter->setPen(mainPen()); + painter->drawPath(cubicPath); + painter->setBrush(Qt::SolidPattern); + if (mTail.style() != QCPLineEnding::esNone) + mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI); + if (mHead.style() != QCPLineEnding::esNone) + mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI); + } +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemCurve::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemRect +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemRect + \brief A rectangle + + \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a topLeft and \a bottomRight, which define the rectangle. +*/ + +/*! + Creates a rectangle item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + topLeft(createPosition(QLatin1String("topLeft"))), + bottomRight(createPosition(QLatin1String("bottomRight"))), + top(createAnchor(QLatin1String("top"), aiTop)), + topRight(createAnchor(QLatin1String("topRight"), aiTopRight)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)), + left(createAnchor(QLatin1String("left"), aiLeft)) +{ + topLeft->setCoords(0, 1); + bottomRight->setCoords(1, 0); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); +} + +QCPItemRect::~QCPItemRect() +{ +} + +/*! + Sets the pen that will be used to draw the line of the rectangle + + \see setSelectedPen, setBrush +*/ +void QCPItemRect::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line of the rectangle when selected + + \see setPen, setSelected +*/ +void QCPItemRect::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to + Qt::NoBrush. + + \see setSelectedBrush, setPen +*/ +void QCPItemRect::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a + brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPItemRect::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/* inherits documentation from base class */ +double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized(); + bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0; + return rectSelectTest(rect, pos, filledRect); +} + +/* inherits documentation from base class */ +void QCPItemRect::draw(QCPPainter *painter) +{ + QPointF p1 = topLeft->pixelPoint(); + QPointF p2 = bottomRight->pixelPoint(); + if (p1.toPoint() == p2.toPoint()) + return; + QRectF rect = QRectF(p1, p2).normalized(); + double clipPad = mainPen().widthF(); + QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad); + if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect + { + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + painter->drawRect(rect); + } +} + +/* inherits documentation from base class */ +QPointF QCPItemRect::anchorPixelPoint(int anchorId) const +{ + QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()); + switch (anchorId) + { + case aiTop: return (rect.topLeft()+rect.topRight())*0.5; + case aiTopRight: return rect.topRight(); + case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; + case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; + case aiBottomLeft: return rect.bottomLeft(); + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return QPointF(); +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemRect::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemRect::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemText +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemText + \brief A text label + + \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions." + + Its position is defined by the member \a position and the setting of \ref setPositionAlignment. + The latter controls which part of the text rect shall be aligned with \a position. + + The text alignment itself (i.e. left, center, right) can be controlled with \ref + setTextAlignment. + + The text may be rotated around the \a position point with \ref setRotation. +*/ + +/*! + Creates a text item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemText::QCPItemText(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + position(createPosition(QLatin1String("position"))), + topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)), + top(createAnchor(QLatin1String("top"), aiTop)), + topRight(createAnchor(QLatin1String("topRight"), aiTopRight)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)), + left(createAnchor(QLatin1String("left"), aiLeft)) +{ + position->setCoords(0, 0); + + setRotation(0); + setTextAlignment(Qt::AlignTop|Qt::AlignHCenter); + setPositionAlignment(Qt::AlignCenter); + setText(QLatin1String("text")); + + setPen(Qt::NoPen); + setSelectedPen(Qt::NoPen); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); + setColor(Qt::black); + setSelectedColor(Qt::blue); +} + +QCPItemText::~QCPItemText() +{ +} + +/*! + Sets the color of the text. +*/ +void QCPItemText::setColor(const QColor &color) +{ + mColor = color; +} + +/*! + Sets the color of the text that will be used when the item is selected. +*/ +void QCPItemText::setSelectedColor(const QColor &color) +{ + mSelectedColor = color; +} + +/*! + Sets the pen that will be used do draw a rectangular border around the text. To disable the + border, set \a pen to Qt::NoPen. + + \see setSelectedPen, setBrush, setPadding +*/ +void QCPItemText::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used do draw a rectangular border around the text, when the item is + selected. To disable the border, set \a pen to Qt::NoPen. + + \see setPen +*/ +void QCPItemText::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used do fill the background of the text. To disable the + background, set \a brush to Qt::NoBrush. + + \see setSelectedBrush, setPen, setPadding +*/ +void QCPItemText::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the + background, set \a brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPItemText::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + Sets the font of the text. + + \see setSelectedFont, setColor +*/ +void QCPItemText::setFont(const QFont &font) +{ + mFont = font; +} + +/*! + Sets the font of the text that will be used when the item is selected. + + \see setFont +*/ +void QCPItemText::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; +} + +/*! + Sets the text that will be displayed. Multi-line texts are supported by inserting a line break + character, e.g. '\n'. + + \see setFont, setColor, setTextAlignment +*/ +void QCPItemText::setText(const QString &text) +{ + mText = text; +} + +/*! + Sets which point of the text rect shall be aligned with \a position. + + Examples: + \li If \a alignment is Qt::AlignHCenter | Qt::AlignTop, the text will be positioned such + that the top of the text rect will be horizontally centered on \a position. + \li If \a alignment is Qt::AlignLeft | Qt::AlignBottom, \a position will indicate the + bottom left corner of the text rect. + + If you want to control the alignment of (multi-lined) text within the text rect, use \ref + setTextAlignment. +*/ +void QCPItemText::setPositionAlignment(Qt::Alignment alignment) +{ + mPositionAlignment = alignment; +} + +/*! + Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight). +*/ +void QCPItemText::setTextAlignment(Qt::Alignment alignment) +{ + mTextAlignment = alignment; +} + +/*! + Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated + around \a position. +*/ +void QCPItemText::setRotation(double degrees) +{ + mRotation = degrees; +} + +/*! + Sets the distance between the border of the text rectangle and the text. The appearance (and + visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush. +*/ +void QCPItemText::setPadding(const QMargins &padding) +{ + mPadding = padding; +} + +/* inherits documentation from base class */ +double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + // The rect may be rotated, so we transform the actual clicked pos to the rotated + // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects: + QPointF positionPixels(position->pixelPoint()); + QTransform inputTransform; + inputTransform.translate(positionPixels.x(), positionPixels.y()); + inputTransform.rotate(-mRotation); + inputTransform.translate(-positionPixels.x(), -positionPixels.y()); + QPointF rotatedPos = inputTransform.map(pos); + QFontMetrics fontMetrics(mFont); + QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText); + QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom()); + QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment); + textBoxRect.moveTopLeft(textPos.toPoint()); + + return rectSelectTest(textBoxRect, rotatedPos, true); +} + +/* inherits documentation from base class */ +void QCPItemText::draw(QCPPainter *painter) +{ + QPointF pos(position->pixelPoint()); + QTransform transform = painter->transform(); + transform.translate(pos.x(), pos.y()); + if (!qFuzzyIsNull(mRotation)) + transform.rotate(mRotation); + painter->setFont(mainFont()); + QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText); + QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom()); + QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation + textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top())); + textBoxRect.moveTopLeft(textPos.toPoint()); + double clipPad = mainPen().widthF(); + QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad); + if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect()))) + { + painter->setTransform(transform); + if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) || + (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)) + { + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + painter->drawRect(textBoxRect); + } + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(mainColor())); + painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText); + } +} + +/* inherits documentation from base class */ +QPointF QCPItemText::anchorPixelPoint(int anchorId) const +{ + // get actual rect points (pretty much copied from draw function): + QPointF pos(position->pixelPoint()); + QTransform transform; + transform.translate(pos.x(), pos.y()); + if (!qFuzzyIsNull(mRotation)) + transform.rotate(mRotation); + QFontMetrics fontMetrics(mainFont()); + QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText); + QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom()); + QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation + textBoxRect.moveTopLeft(textPos.toPoint()); + QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect)); + + switch (anchorId) + { + case aiTopLeft: return rectPoly.at(0); + case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5; + case aiTopRight: return rectPoly.at(1); + case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5; + case aiBottomRight: return rectPoly.at(2); + case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5; + case aiBottomLeft: return rectPoly.at(3); + case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return QPointF(); +} + +/*! \internal + + Returns the point that must be given to the QPainter::drawText function (which expects the top + left point of the text rect), according to the position \a pos, the text bounding box \a rect and + the requested \a positionAlignment. + + For example, if \a positionAlignment is Qt::AlignLeft | Qt::AlignBottom the returned point + will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally + drawn at that point, the lower left corner of the resulting text rect is at \a pos. +*/ +QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const +{ + if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop)) + return pos; + + QPointF result = pos; // start at top left + if (positionAlignment.testFlag(Qt::AlignHCenter)) + result.rx() -= rect.width()/2.0; + else if (positionAlignment.testFlag(Qt::AlignRight)) + result.rx() -= rect.width(); + if (positionAlignment.testFlag(Qt::AlignVCenter)) + result.ry() -= rect.height()/2.0; + else if (positionAlignment.testFlag(Qt::AlignBottom)) + result.ry() -= rect.height(); + return result; +} + +/*! \internal + + Returns the font that should be used for drawing text. Returns mFont when the item is not selected + and mSelectedFont when it is. +*/ +QFont QCPItemText::mainFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + +/*! \internal + + Returns the color that should be used for drawing text. Returns mColor when the item is not + selected and mSelectedColor when it is. +*/ +QColor QCPItemText::mainColor() const +{ + return mSelected ? mSelectedColor : mColor; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemText::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemText::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemEllipse +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemEllipse + \brief An ellipse + + \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in. +*/ + +/*! + Creates an ellipse item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + topLeft(createPosition(QLatin1String("topLeft"))), + bottomRight(createPosition(QLatin1String("bottomRight"))), + topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)), + top(createAnchor(QLatin1String("top"), aiTop)), + topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)), + left(createAnchor(QLatin1String("left"), aiLeft)), + center(createAnchor(QLatin1String("center"), aiCenter)) +{ + topLeft->setCoords(0, 1); + bottomRight->setCoords(1, 0); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2)); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); +} + +QCPItemEllipse::~QCPItemEllipse() +{ +} + +/*! + Sets the pen that will be used to draw the line of the ellipse + + \see setSelectedPen, setBrush +*/ +void QCPItemEllipse::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line of the ellipse when selected + + \see setPen, setSelected +*/ +void QCPItemEllipse::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to + Qt::NoBrush. + + \see setSelectedBrush, setPen +*/ +void QCPItemEllipse::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a + brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPItemEllipse::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/* inherits documentation from base class */ +double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + double result = -1; + QPointF p1 = topLeft->pixelPoint(); + QPointF p2 = bottomRight->pixelPoint(); + QPointF center((p1+p2)/2.0); + double a = qAbs(p1.x()-p2.x())/2.0; + double b = qAbs(p1.y()-p2.y())/2.0; + double x = pos.x()-center.x(); + double y = pos.y()-center.y(); + + // distance to border: + double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b)); + result = qAbs(c-1)*qSqrt(x*x+y*y); + // filled ellipse, allow click inside to count as hit: + if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) + { + if (x*x/(a*a) + y*y/(b*b) <= 1) + result = mParentPlot->selectionTolerance()*0.99; + } + return result; +} + +/* inherits documentation from base class */ +void QCPItemEllipse::draw(QCPPainter *painter) +{ + QPointF p1 = topLeft->pixelPoint(); + QPointF p2 = bottomRight->pixelPoint(); + if (p1.toPoint() == p2.toPoint()) + return; + QRectF ellipseRect = QRectF(p1, p2).normalized(); + QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF()); + if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect + { + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); +#ifdef __EXCEPTIONS + try // drawEllipse sometimes throws exceptions if ellipse is too big + { +#endif + painter->drawEllipse(ellipseRect); +#ifdef __EXCEPTIONS + } catch (...) + { + qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible"; + setVisible(false); + } +#endif + } +} + +/* inherits documentation from base class */ +QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const +{ + QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()); + switch (anchorId) + { + case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2); + case aiTop: return (rect.topLeft()+rect.topRight())*0.5; + case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2); + case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; + case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2); + case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; + case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2); + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5; + case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return QPointF(); +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemEllipse::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemEllipse::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemPixmap +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemPixmap + \brief An arbitrary pixmap + + \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will + be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to + fit the rectangle or be drawn aligned to the topLeft position. + + If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown + on the right side of the example image), the pixmap will be flipped in the respective + orientations. +*/ + +/*! + Creates a rectangle item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + topLeft(createPosition(QLatin1String("topLeft"))), + bottomRight(createPosition(QLatin1String("bottomRight"))), + top(createAnchor(QLatin1String("top"), aiTop)), + topRight(createAnchor(QLatin1String("topRight"), aiTopRight)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)), + left(createAnchor(QLatin1String("left"), aiLeft)), + mScaledPixmapInvalidated(true) +{ + topLeft->setCoords(0, 1); + bottomRight->setCoords(1, 0); + + setPen(Qt::NoPen); + setSelectedPen(QPen(Qt::blue)); + setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation); +} + +QCPItemPixmap::~QCPItemPixmap() +{ +} + +/*! + Sets the pixmap that will be displayed. +*/ +void QCPItemPixmap::setPixmap(const QPixmap &pixmap) +{ + mPixmap = pixmap; + mScaledPixmapInvalidated = true; + if (mPixmap.isNull()) + qDebug() << Q_FUNC_INFO << "pixmap is null"; +} + +/*! + Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a + bottomRight positions. +*/ +void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode) +{ + mScaled = scaled; + mAspectRatioMode = aspectRatioMode; + mTransformationMode = transformationMode; + mScaledPixmapInvalidated = true; +} + +/*! + Sets the pen that will be used to draw a border around the pixmap. + + \see setSelectedPen, setBrush +*/ +void QCPItemPixmap::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw a border around the pixmap when selected + + \see setPen, setSelected +*/ +void QCPItemPixmap::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/* inherits documentation from base class */ +double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + return rectSelectTest(getFinalRect(), pos, true); +} + +/* inherits documentation from base class */ +void QCPItemPixmap::draw(QCPPainter *painter) +{ + bool flipHorz = false; + bool flipVert = false; + QRect rect = getFinalRect(&flipHorz, &flipVert); + double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF(); + QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad); + if (boundingRect.intersects(clipRect())) + { + updateScaledPixmap(rect, flipHorz, flipVert); + painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap); + QPen pen = mainPen(); + if (pen.style() != Qt::NoPen) + { + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawRect(rect); + } + } +} + +/* inherits documentation from base class */ +QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const +{ + bool flipHorz; + bool flipVert; + QRect rect = getFinalRect(&flipHorz, &flipVert); + // we actually want denormal rects (negative width/height) here, so restore + // the flipped state: + if (flipHorz) + rect.adjust(rect.width(), 0, -rect.width(), 0); + if (flipVert) + rect.adjust(0, rect.height(), 0, -rect.height()); + + switch (anchorId) + { + case aiTop: return (rect.topLeft()+rect.topRight())*0.5; + case aiTopRight: return rect.topRight(); + case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; + case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; + case aiBottomLeft: return rect.bottomLeft(); + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return QPointF(); +} + +/*! \internal + + Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The + parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped + horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a + bottomRight.) + + This function only creates the scaled pixmap when the buffered pixmap has a different size than + the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does + not cause expensive rescaling every time. + + If scaling is disabled, sets mScaledPixmap to a null QPixmap. +*/ +void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert) +{ + if (mPixmap.isNull()) + return; + + if (mScaled) + { + if (finalRect.isNull()) + finalRect = getFinalRect(&flipHorz, &flipVert); + if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()) + { + mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode); + if (flipHorz || flipVert) + mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert)); + } + } else if (!mScaledPixmap.isNull()) + mScaledPixmap = QPixmap(); + mScaledPixmapInvalidated = false; +} + +/*! \internal + + Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions + and scaling settings. + + The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn + flipped horizontally or vertically in the returned rect. (The returned rect itself is always + normalized, i.e. the top left corner of the rect is actually further to the top/left than the + bottom right corner). This is the case when the item position \a topLeft is further to the + bottom/right than \a bottomRight. + + If scaling is disabled, returns a rect with size of the original pixmap and the top left corner + aligned with the item position \a topLeft. The position \a bottomRight is ignored. +*/ +QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const +{ + QRect result; + bool flipHorz = false; + bool flipVert = false; + QPoint p1 = topLeft->pixelPoint().toPoint(); + QPoint p2 = bottomRight->pixelPoint().toPoint(); + if (p1 == p2) + return QRect(p1, QSize(0, 0)); + if (mScaled) + { + QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y()); + QPoint topLeft = p1; + if (newSize.width() < 0) + { + flipHorz = true; + newSize.rwidth() *= -1; + topLeft.setX(p2.x()); + } + if (newSize.height() < 0) + { + flipVert = true; + newSize.rheight() *= -1; + topLeft.setY(p2.y()); + } + QSize scaledSize = mPixmap.size(); + scaledSize.scale(newSize, mAspectRatioMode); + result = QRect(topLeft, scaledSize); + } else + { + result = QRect(p1, mPixmap.size()); + } + if (flippedHorz) + *flippedHorz = flipHorz; + if (flippedVert) + *flippedVert = flipVert; + return result; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemPixmap::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemTracer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemTracer + \brief Item that sticks to QCPGraph data points + + \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions." + + The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt + the coordinate axes of the graph and update its \a position to be on the graph's data. This means + the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a + QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a + position will have no effect because they will be overriden in the next redraw (this is when the + coordinate update happens). + + If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will + stay at the corresponding end of the graph. + + With \ref setInterpolating you may specify whether the tracer may only stay exactly on data + points or whether it interpolates data points linearly, if given a key that lies between two data + points of the graph. + + The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer + have no own visual appearance (set the style to \ref tsNone), and just connect other item + positions to the tracer \a position (used as an anchor) via \ref + QCPItemPosition::setParentAnchor. + + \note The tracer position is only automatically updated upon redraws. So when the data of the + graph changes and immediately afterwards (without a redraw) the a position coordinates of the + tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref + updatePosition must be called manually, prior to reading the tracer coordinates. +*/ + +/*! + Creates a tracer item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + position(createPosition(QLatin1String("position"))), + mGraph(0) +{ + position->setCoords(0, 0); + + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2)); + setStyle(tsCrosshair); + setSize(6); + setInterpolating(false); + setGraphKey(0); +} + +QCPItemTracer::~QCPItemTracer() +{ +} + +/*! + Sets the pen that will be used to draw the line of the tracer + + \see setSelectedPen, setBrush +*/ +void QCPItemTracer::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line of the tracer when selected + + \see setPen, setSelected +*/ +void QCPItemTracer::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used to draw any fills of the tracer + + \see setSelectedBrush, setPen +*/ +void QCPItemTracer::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used to draw any fills of the tracer, when selected. + + \see setBrush, setSelected +*/ +void QCPItemTracer::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare + does, \ref tsCrosshair does not). +*/ +void QCPItemTracer::setSize(double size) +{ + mSize = size; +} + +/*! + Sets the style/visual appearance of the tracer. + + If you only want to use the tracer \a position as an anchor for other items, set \a style to + \ref tsNone. +*/ +void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style) +{ + mStyle = style; +} + +/*! + Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type + QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph. + + To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed + freely like any other item position. This is the state the tracer will assume when its graph gets + deleted while still attached to it. + + \see setGraphKey +*/ +void QCPItemTracer::setGraph(QCPGraph *graph) +{ + if (graph) + { + if (graph->parentPlot() == mParentPlot) + { + position->setType(QCPItemPosition::ptPlotCoords); + position->setAxes(graph->keyAxis(), graph->valueAxis()); + mGraph = graph; + updatePosition(); + } else + qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item"; + } else + { + mGraph = 0; + } +} + +/*! + Sets the key of the graph's data point the tracer will be positioned at. This is the only free + coordinate of a tracer when attached to a graph. + + Depending on \ref setInterpolating, the tracer will be either positioned on the data point + closest to \a key, or will stay exactly at \a key and interpolate the value linearly. + + \see setGraph, setInterpolating +*/ +void QCPItemTracer::setGraphKey(double key) +{ + mGraphKey = key; +} + +/*! + Sets whether the value of the graph's data points shall be interpolated, when positioning the + tracer. + + If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on + the data point of the graph which is closest to the key, but which is not necessarily exactly + there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and + the appropriate value will be interpolated from the graph's data points linearly. + + \see setGraph, setGraphKey +*/ +void QCPItemTracer::setInterpolating(bool enabled) +{ + mInterpolating = enabled; +} + +/* inherits documentation from base class */ +double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QPointF center(position->pixelPoint()); + double w = mSize/2.0; + QRect clip = clipRect(); + switch (mStyle) + { + case tsNone: return -1; + case tsPlus: + { + if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos), + distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos))); + break; + } + case tsCrosshair: + { + return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos), + distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos))); + } + case tsCircle: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + { + // distance to border: + double centerDist = QVector2D(center-pos).length(); + double circleLine = w; + double result = qAbs(centerDist-circleLine); + // filled ellipse, allow click inside to count as hit: + if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) + { + if (centerDist <= circleLine) + result = mParentPlot->selectionTolerance()*0.99; + } + return result; + } + break; + } + case tsSquare: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + { + QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w)); + bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0; + return rectSelectTest(rect, pos, filledRect); + } + break; + } + } + return -1; +} + +/* inherits documentation from base class */ +void QCPItemTracer::draw(QCPPainter *painter) +{ + updatePosition(); + if (mStyle == tsNone) + return; + + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + QPointF center(position->pixelPoint()); + double w = mSize/2.0; + QRect clip = clipRect(); + switch (mStyle) + { + case tsNone: return; + case tsPlus: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + { + painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0))); + painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w))); + } + break; + } + case tsCrosshair: + { + if (center.y() > clip.top() && center.y() < clip.bottom()) + painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y())); + if (center.x() > clip.left() && center.x() < clip.right()) + painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom())); + break; + } + case tsCircle: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + painter->drawEllipse(center, w, w); + break; + } + case tsSquare: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w))); + break; + } + } +} + +/*! + If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a + position to reside on the graph data, depending on the configured key (\ref setGraphKey). + + It is called automatically on every redraw and normally doesn't need to be called manually. One + exception is when you want to read the tracer coordinates via \a position and are not sure that + the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw. + In that situation, call this function before accessing \a position, to make sure you don't get + out-of-date coordinates. + + If there is no graph set on this tracer, this function does nothing. +*/ +void QCPItemTracer::updatePosition() +{ + if (mGraph) + { + if (mParentPlot->hasPlottable(mGraph)) + { + if (mGraph->data()->size() > 1) + { + QCPDataMap::const_iterator first = mGraph->data()->constBegin(); + QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1; + if (mGraphKey < first.key()) + position->setCoords(first.key(), first.value().value); + else if (mGraphKey > last.key()) + position->setCoords(last.key(), last.value().value); + else + { + QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey); + if (it != first) // mGraphKey is somewhere between iterators + { + QCPDataMap::const_iterator prevIt = it-1; + if (mInterpolating) + { + // interpolate between iterators around mGraphKey: + double slope = 0; + if (!qFuzzyCompare((double)it.key(), (double)prevIt.key())) + slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key()); + position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value); + } else + { + // find iterator with key closest to mGraphKey: + if (mGraphKey < (prevIt.key()+it.key())*0.5) + it = prevIt; + position->setCoords(it.key(), it.value().value); + } + } else // mGraphKey is exactly on first iterator + position->setCoords(it.key(), it.value().value); + } + } else if (mGraph->data()->size() == 1) + { + QCPDataMap::const_iterator it = mGraph->data()->constBegin(); + position->setCoords(it.key(), it.value().value); + } else + qDebug() << Q_FUNC_INFO << "graph has no data"; + } else + qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)"; + } +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemTracer::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemTracer::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemBracket +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemBracket + \brief A bracket for referencing/highlighting certain parts in the plot. + + \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a left and \a right, which define the span of the bracket. If \a left is + actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the + example image. + + The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket + stretches away from the embraced span, can be controlled with \ref setLength. + + \image html QCPItemBracket-length.png +
Demonstrating the effect of different values for \ref setLength, for styles \ref + bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.
+ + It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine + or QCPItemCurve) or a text label (QCPItemText), to the bracket. +*/ + +/*! + Creates a bracket item and sets default values. + + The constructed item can be added to the plot with QCustomPlot::addItem. +*/ +QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + left(createPosition(QLatin1String("left"))), + right(createPosition(QLatin1String("right"))), + center(createAnchor(QLatin1String("center"), aiCenter)) +{ + left->setCoords(0, 0); + right->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2)); + setLength(8); + setStyle(bsCalligraphic); +} + +QCPItemBracket::~QCPItemBracket() +{ +} + +/*! + Sets the pen that will be used to draw the bracket. + + Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the + stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use + \ref setLength, which has a similar effect. + + \see setSelectedPen +*/ +void QCPItemBracket::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the bracket when selected + + \see setPen, setSelected +*/ +void QCPItemBracket::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the \a length in pixels how far the bracket extends in the direction towards the embraced + span of the bracket (i.e. perpendicular to the left-right-direction) + + \image html QCPItemBracket-length.png +
Demonstrating the effect of different values for \ref setLength, for styles \ref + bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.
+*/ +void QCPItemBracket::setLength(double length) +{ + mLength = length; +} + +/*! + Sets the style of the bracket, i.e. the shape/visual appearance. + + \see setPen +*/ +void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style) +{ + mStyle = style; +} + +/* inherits documentation from base class */ +double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QVector2D leftVec(left->pixelPoint()); + QVector2D rightVec(right->pixelPoint()); + if (leftVec.toPoint() == rightVec.toPoint()) + return -1; + + QVector2D widthVec = (rightVec-leftVec)*0.5f; + QVector2D lengthVec(-widthVec.y(), widthVec.x()); + lengthVec = lengthVec.normalized()*mLength; + QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec; + + switch (mStyle) + { + case QCPItemBracket::bsSquare: + case QCPItemBracket::bsRound: + { + double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos); + double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos); + double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos); + return qSqrt(qMin(qMin(a, b), c)); + } + case QCPItemBracket::bsCurly: + case QCPItemBracket::bsCalligraphic: + { + double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos); + double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos); + double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos); + double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos); + return qSqrt(qMin(qMin(a, b), qMin(c, d))); + } + } + return -1; +} + +/* inherits documentation from base class */ +void QCPItemBracket::draw(QCPPainter *painter) +{ + QVector2D leftVec(left->pixelPoint()); + QVector2D rightVec(right->pixelPoint()); + if (leftVec.toPoint() == rightVec.toPoint()) + return; + + QVector2D widthVec = (rightVec-leftVec)*0.5f; + QVector2D lengthVec(-widthVec.y(), widthVec.x()); + lengthVec = lengthVec.normalized()*mLength; + QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec; + + QPolygon boundingPoly; + boundingPoly << leftVec.toPoint() << rightVec.toPoint() + << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint(); + QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF()); + if (clip.intersects(boundingPoly.boundingRect())) + { + painter->setPen(mainPen()); + switch (mStyle) + { + case bsSquare: + { + painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF()); + painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); + painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + break; + } + case bsRound: + { + painter->setBrush(Qt::NoBrush); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + painter->drawPath(path); + break; + } + case bsCurly: + { + painter->setBrush(Qt::NoBrush); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + painter->drawPath(path); + break; + } + case bsCalligraphic: + { + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(mainPen().color())); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + + path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + + path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF()); + path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); + + painter->drawPath(path); + break; + } + } + } +} + +/* inherits documentation from base class */ +QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const +{ + QVector2D leftVec(left->pixelPoint()); + QVector2D rightVec(right->pixelPoint()); + if (leftVec.toPoint() == rightVec.toPoint()) + return leftVec.toPointF(); + + QVector2D widthVec = (rightVec-leftVec)*0.5f; + QVector2D lengthVec(-widthVec.y(), widthVec.x()); + lengthVec = lengthVec.normalized()*mLength; + QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec; + + switch (anchorId) + { + case aiCenter: + return centerVec.toPointF(); + } + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return QPointF(); +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemBracket::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + diff --git a/Analysis/DataViewer/qcustomplot.h b/Analysis/DataViewer/qcustomplot.h new file mode 100644 index 00000000..bb998f1c --- /dev/null +++ b/Analysis/DataViewer/qcustomplot.h @@ -0,0 +1,3768 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011-2015 Emanuel Eichhammer ** +** ** +** This program is free software: you can redistribute it and/or modify ** +** it under the terms of the GNU General Public License as published by ** +** the Free Software Foundation, either version 3 of the License, or ** +** (at your option) any later version. ** +** ** +** This program is distributed in the hope that it will be useful, ** +** but WITHOUT ANY WARRANTY; without even the implied warranty of ** +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** +** GNU General Public License for more details. ** +** ** +** You should have received a copy of the GNU General Public License ** +** along with this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Emanuel Eichhammer ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 22.12.15 ** +** Version: 1.3.2 ** +****************************************************************************/ + +#ifndef QCUSTOMPLOT_H +#define QCUSTOMPLOT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +# include +# include +# include +#else +# include +# include +#endif + +class QCPPainter; +class QCustomPlot; +class QCPLayerable; +class QCPLayoutElement; +class QCPLayout; +class QCPAxis; +class QCPAxisRect; +class QCPAxisPainterPrivate; +class QCPAbstractPlottable; +class QCPGraph; +class QCPAbstractItem; +class QCPItemPosition; +class QCPLayer; +class QCPPlotTitle; +class QCPLegend; +class QCPAbstractLegendItem; +class QCPColorMap; +class QCPColorScale; +class QCPBars; + + +/*! \file */ + + +// decl definitions for shared library compilation/usage: +#if defined(QCUSTOMPLOT_COMPILE_LIBRARY) +# define QCP_LIB_DECL Q_DECL_EXPORT +#elif defined(QCUSTOMPLOT_USE_LIBRARY) +# define QCP_LIB_DECL Q_DECL_IMPORT +#else +# define QCP_LIB_DECL +#endif + +/*! + The QCP Namespace contains general enums and QFlags used throughout the QCustomPlot library +*/ +namespace QCP +{ +/*! + Defines the sides of a rectangular entity to which margins can be applied. + + \see QCPLayoutElement::setAutoMargins, QCPAxisRect::setAutoMargins +*/ +enum MarginSide { msLeft = 0x01 ///< 0x01 left margin + ,msRight = 0x02 ///< 0x02 right margin + ,msTop = 0x04 ///< 0x04 top margin + ,msBottom = 0x08 ///< 0x08 bottom margin + ,msAll = 0xFF ///< 0xFF all margins + ,msNone = 0x00 ///< 0x00 no margin + }; +Q_DECLARE_FLAGS(MarginSides, MarginSide) + +/*! + Defines what objects of a plot can be forcibly drawn antialiased/not antialiased. If an object is + neither forcibly drawn antialiased nor forcibly drawn not antialiased, it is up to the respective + element how it is drawn. Typically it provides a \a setAntialiased function for this. + + \c AntialiasedElements is a flag of or-combined elements of this enum type. + + \see QCustomPlot::setAntialiasedElements, QCustomPlot::setNotAntialiasedElements +*/ +enum AntialiasedElement { aeAxes = 0x0001 ///< 0x0001 Axis base line and tick marks + ,aeGrid = 0x0002 ///< 0x0002 Grid lines + ,aeSubGrid = 0x0004 ///< 0x0004 Sub grid lines + ,aeLegend = 0x0008 ///< 0x0008 Legend box + ,aeLegendItems = 0x0010 ///< 0x0010 Legend items + ,aePlottables = 0x0020 ///< 0x0020 Main lines of plottables (excluding error bars, see element \ref aeErrorBars) + ,aeItems = 0x0040 ///< 0x0040 Main lines of items + ,aeScatters = 0x0080 ///< 0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) + ,aeErrorBars = 0x0100 ///< 0x0100 Error bars + ,aeFills = 0x0200 ///< 0x0200 Borders of fills (e.g. under or between graphs) + ,aeZeroLine = 0x0400 ///< 0x0400 Zero-lines, see \ref QCPGrid::setZeroLinePen + ,aeAll = 0xFFFF ///< 0xFFFF All elements + ,aeNone = 0x0000 ///< 0x0000 No elements + }; +Q_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement) + +/*! + Defines plotting hints that control various aspects of the quality and speed of plotting. + + \see QCustomPlot::setPlottingHints +*/ +enum PlottingHint { phNone = 0x000 ///< 0x000 No hints are set + ,phFastPolylines = 0x001 ///< 0x001 Graph/Curve lines are drawn with a faster method. This reduces the quality + ///< especially of the line segment joins. (Only relevant for solid line pens.) + ,phForceRepaint = 0x002 ///< 0x002 causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called with parameter \ref QCustomPlot::rpHint. + ///< This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse). + ,phCacheLabels = 0x004 ///< 0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance. + }; +Q_DECLARE_FLAGS(PlottingHints, PlottingHint) + +/*! + Defines the mouse interactions possible with QCustomPlot. + + \c Interactions is a flag of or-combined elements of this enum type. + + \see QCustomPlot::setInteractions +*/ +enum Interaction { iRangeDrag = 0x001 ///< 0x001 Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes) + ,iRangeZoom = 0x002 ///< 0x002 Axis ranges are zoomable with the mouse wheel (see \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes) + ,iMultiSelect = 0x004 ///< 0x004 The user can select multiple objects by holding the modifier set by \ref QCustomPlot::setMultiSelectModifier while clicking + ,iSelectPlottables = 0x008 ///< 0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) + ,iSelectAxes = 0x010 ///< 0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts) + ,iSelectLegend = 0x020 ///< 0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) + ,iSelectItems = 0x040 ///< 0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem) + ,iSelectOther = 0x080 ///< 0x080 All other objects are selectable (e.g. your own derived layerables, the plot title,...) + }; +Q_DECLARE_FLAGS(Interactions, Interaction) + +/*! \internal + + Returns whether the specified \a value is considered an invalid data value for plottables (i.e. + is \e nan or \e +/-inf). This function is used to check data validity upon replots, when the + compiler flag \c QCUSTOMPLOT_CHECK_DATA is set. +*/ +inline bool isInvalidData(double value) +{ + return qIsNaN(value) || qIsInf(value); +} + +/*! \internal + \overload + + Checks two arguments instead of one. +*/ +inline bool isInvalidData(double value1, double value2) +{ + return isInvalidData(value1) || isInvalidData(value2); +} + +/*! \internal + + Sets the specified \a side of \a margins to \a value + + \see getMarginValue +*/ +inline void setMarginValue(QMargins &margins, QCP::MarginSide side, int value) +{ + switch (side) + { + case QCP::msLeft: margins.setLeft(value); break; + case QCP::msRight: margins.setRight(value); break; + case QCP::msTop: margins.setTop(value); break; + case QCP::msBottom: margins.setBottom(value); break; + case QCP::msAll: margins = QMargins(value, value, value, value); break; + default: break; + } +} + +/*! \internal + + Returns the value of the specified \a side of \a margins. If \a side is \ref QCP::msNone or + \ref QCP::msAll, returns 0. + + \see setMarginValue +*/ +inline int getMarginValue(const QMargins &margins, QCP::MarginSide side) +{ + switch (side) + { + case QCP::msLeft: return margins.left(); + case QCP::msRight: return margins.right(); + case QCP::msTop: return margins.top(); + case QCP::msBottom: return margins.bottom(); + default: break; + } + return 0; +} + +} // end of namespace QCP + +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::AntialiasedElements) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::PlottingHints) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::MarginSides) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::Interactions) + + +class QCP_LIB_DECL QCPScatterStyle +{ + Q_GADGET +public: + /*! + Defines the shape used for scatter points. + + On plottables/items that draw scatters, the sizes of these visualizations (with exception of + \ref ssDot and \ref ssPixmap) can be controlled with the \ref setSize function. Scatters are + drawn with the pen and brush specified with \ref setPen and \ref setBrush. + */ + Q_ENUMS(ScatterShape) + enum ScatterShape { ssNone ///< no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) + ,ssDot ///< \enumimage{ssDot.png} a single pixel (use \ref ssDisc or \ref ssCircle if you want a round shape with a certain radius) + ,ssCross ///< \enumimage{ssCross.png} a cross + ,ssPlus ///< \enumimage{ssPlus.png} a plus + ,ssCircle ///< \enumimage{ssCircle.png} a circle + ,ssDisc ///< \enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle) + ,ssSquare ///< \enumimage{ssSquare.png} a square + ,ssDiamond ///< \enumimage{ssDiamond.png} a diamond + ,ssStar ///< \enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus + ,ssTriangle ///< \enumimage{ssTriangle.png} an equilateral triangle, standing on baseline + ,ssTriangleInverted ///< \enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner + ,ssCrossSquare ///< \enumimage{ssCrossSquare.png} a square with a cross inside + ,ssPlusSquare ///< \enumimage{ssPlusSquare.png} a square with a plus inside + ,ssCrossCircle ///< \enumimage{ssCrossCircle.png} a circle with a cross inside + ,ssPlusCircle ///< \enumimage{ssPlusCircle.png} a circle with a plus inside + ,ssPeace ///< \enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines + ,ssPixmap ///< a custom pixmap specified by \ref setPixmap, centered on the data point coordinates + ,ssCustom ///< custom painter operations are performed per scatter (As QPainterPath, see \ref setCustomPath) + }; + + QCPScatterStyle(); + QCPScatterStyle(ScatterShape shape, double size=6); + QCPScatterStyle(ScatterShape shape, const QColor &color, double size); + QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size); + QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size); + QCPScatterStyle(const QPixmap &pixmap); + QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush=Qt::NoBrush, double size=6); + + // getters: + double size() const { return mSize; } + ScatterShape shape() const { return mShape; } + QPen pen() const { return mPen; } + QBrush brush() const { return mBrush; } + QPixmap pixmap() const { return mPixmap; } + QPainterPath customPath() const { return mCustomPath; } + + // setters: + void setSize(double size); + void setShape(ScatterShape shape); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setPixmap(const QPixmap &pixmap); + void setCustomPath(const QPainterPath &customPath); + + // non-property methods: + bool isNone() const { return mShape == ssNone; } + bool isPenDefined() const { return mPenDefined; } + void applyTo(QCPPainter *painter, const QPen &defaultPen) const; + void drawShape(QCPPainter *painter, QPointF pos) const; + void drawShape(QCPPainter *painter, double x, double y) const; + +protected: + // property members: + double mSize; + ScatterShape mShape; + QPen mPen; + QBrush mBrush; + QPixmap mPixmap; + QPainterPath mCustomPath; + + // non-property members: + bool mPenDefined; +}; +Q_DECLARE_TYPEINFO(QCPScatterStyle, Q_MOVABLE_TYPE); + + +class QCP_LIB_DECL QCPPainter : public QPainter +{ + Q_GADGET +public: + /*! + Defines special modes the painter can operate in. They disable or enable certain subsets of features/fixes/workarounds, + depending on whether they are wanted on the respective output device. + */ + enum PainterMode { pmDefault = 0x00 ///< 0x00 Default mode for painting on screen devices + ,pmVectorized = 0x01 ///< 0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes. + ,pmNoCaching = 0x02 ///< 0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels + ,pmNonCosmetic = 0x04 ///< 0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.) + }; + Q_FLAGS(PainterMode PainterModes) + Q_DECLARE_FLAGS(PainterModes, PainterMode) + + QCPPainter(); + QCPPainter(QPaintDevice *device); + ~QCPPainter(); + + // getters: + bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); } + PainterModes modes() const { return mModes; } + + // setters: + void setAntialiasing(bool enabled); + void setMode(PainterMode mode, bool enabled=true); + void setModes(PainterModes modes); + + // methods hiding non-virtual base class functions (QPainter bug workarounds): + bool begin(QPaintDevice *device); + void setPen(const QPen &pen); + void setPen(const QColor &color); + void setPen(Qt::PenStyle penStyle); + void drawLine(const QLineF &line); + void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));} + void save(); + void restore(); + + // non-virtual methods: + void makeNonCosmetic(); + +protected: + // property members: + PainterModes mModes; + bool mIsAntialiasing; + + // non-property members: + QStack mAntialiasingStack; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPainter::PainterModes) + + +class QCP_LIB_DECL QCPLayer : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot) + Q_PROPERTY(QString name READ name) + Q_PROPERTY(int index READ index) + Q_PROPERTY(QList children READ children) + Q_PROPERTY(bool visible READ visible WRITE setVisible) + /// \endcond +public: + QCPLayer(QCustomPlot* parentPlot, const QString &layerName); + ~QCPLayer(); + + // getters: + QCustomPlot *parentPlot() const { return mParentPlot; } + QString name() const { return mName; } + int index() const { return mIndex; } + QList children() const { return mChildren; } + bool visible() const { return mVisible; } + + // setters: + void setVisible(bool visible); + +protected: + // property members: + QCustomPlot *mParentPlot; + QString mName; + int mIndex; + QList mChildren; + bool mVisible; + + // non-virtual methods: + void addChild(QCPLayerable *layerable, bool prepend); + void removeChild(QCPLayerable *layerable); + +private: + Q_DISABLE_COPY(QCPLayer) + + friend class QCustomPlot; + friend class QCPLayerable; +}; + +class QCP_LIB_DECL QCPLayerable : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool visible READ visible WRITE setVisible) + Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot) + Q_PROPERTY(QCPLayerable* parentLayerable READ parentLayerable) + Q_PROPERTY(QCPLayer* layer READ layer WRITE setLayer NOTIFY layerChanged) + Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased) + /// \endcond +public: + QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0); + ~QCPLayerable(); + + // getters: + bool visible() const { return mVisible; } + QCustomPlot *parentPlot() const { return mParentPlot; } + QCPLayerable *parentLayerable() const { return mParentLayerable.data(); } + QCPLayer *layer() const { return mLayer; } + bool antialiased() const { return mAntialiased; } + + // setters: + void setVisible(bool on); + Q_SLOT bool setLayer(QCPLayer *layer); + bool setLayer(const QString &layerName); + void setAntialiased(bool enabled); + + // introduced virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + // non-property methods: + bool realVisibility() const; + +signals: + void layerChanged(QCPLayer *newLayer); + +protected: + // property members: + bool mVisible; + QCustomPlot *mParentPlot; + QPointer mParentLayerable; + QCPLayer *mLayer; + bool mAntialiased; + + // introduced virtual methods: + virtual void parentPlotInitialized(QCustomPlot *parentPlot); + virtual QCP::Interaction selectionCategory() const; + virtual QRect clipRect() const; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0; + virtual void draw(QCPPainter *painter) = 0; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + + // non-property methods: + void initializeParentPlot(QCustomPlot *parentPlot); + void setParentLayerable(QCPLayerable* parentLayerable); + bool moveToLayer(QCPLayer *layer, bool prepend); + void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const; + +private: + Q_DISABLE_COPY(QCPLayerable) + + friend class QCustomPlot; + friend class QCPAxisRect; +}; + + +class QCP_LIB_DECL QCPRange +{ +public: + double lower, upper; + + QCPRange(); + QCPRange(double lower, double upper); + + bool operator==(const QCPRange& other) const { return lower == other.lower && upper == other.upper; } + bool operator!=(const QCPRange& other) const { return !(*this == other); } + + QCPRange &operator+=(const double& value) { lower+=value; upper+=value; return *this; } + QCPRange &operator-=(const double& value) { lower-=value; upper-=value; return *this; } + QCPRange &operator*=(const double& value) { lower*=value; upper*=value; return *this; } + QCPRange &operator/=(const double& value) { lower/=value; upper/=value; return *this; } + friend inline const QCPRange operator+(const QCPRange&, double); + friend inline const QCPRange operator+(double, const QCPRange&); + friend inline const QCPRange operator-(const QCPRange& range, double value); + friend inline const QCPRange operator*(const QCPRange& range, double value); + friend inline const QCPRange operator*(double value, const QCPRange& range); + friend inline const QCPRange operator/(const QCPRange& range, double value); + + double size() const; + double center() const; + void normalize(); + void expand(const QCPRange &otherRange); + QCPRange expanded(const QCPRange &otherRange) const; + QCPRange sanitizedForLogScale() const; + QCPRange sanitizedForLinScale() const; + bool contains(double value) const; + + static bool validRange(double lower, double upper); + static bool validRange(const QCPRange &range); + static const double minRange; //1e-280; + static const double maxRange; //1e280; + +}; +Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE); + +/* documentation of inline functions */ + +/*! \fn QCPRange &QCPRange::operator+=(const double& value) + + Adds \a value to both boundaries of the range. +*/ + +/*! \fn QCPRange &QCPRange::operator-=(const double& value) + + Subtracts \a value from both boundaries of the range. +*/ + +/*! \fn QCPRange &QCPRange::operator*=(const double& value) + + Multiplies both boundaries of the range by \a value. +*/ + +/*! \fn QCPRange &QCPRange::operator/=(const double& value) + + Divides both boundaries of the range by \a value. +*/ + +/* end documentation of inline functions */ + +/*! + Adds \a value to both boundaries of the range. +*/ +inline const QCPRange operator+(const QCPRange& range, double value) +{ + QCPRange result(range); + result += value; + return result; +} + +/*! + Adds \a value to both boundaries of the range. +*/ +inline const QCPRange operator+(double value, const QCPRange& range) +{ + QCPRange result(range); + result += value; + return result; +} + +/*! + Subtracts \a value from both boundaries of the range. +*/ +inline const QCPRange operator-(const QCPRange& range, double value) +{ + QCPRange result(range); + result -= value; + return result; +} + +/*! + Multiplies both boundaries of the range by \a value. +*/ +inline const QCPRange operator*(const QCPRange& range, double value) +{ + QCPRange result(range); + result *= value; + return result; +} + +/*! + Multiplies both boundaries of the range by \a value. +*/ +inline const QCPRange operator*(double value, const QCPRange& range) +{ + QCPRange result(range); + result *= value; + return result; +} + +/*! + Divides both boundaries of the range by \a value. +*/ +inline const QCPRange operator/(const QCPRange& range, double value) +{ + QCPRange result(range); + result /= value; + return result; +} + + +class QCP_LIB_DECL QCPMarginGroup : public QObject +{ + Q_OBJECT +public: + QCPMarginGroup(QCustomPlot *parentPlot); + ~QCPMarginGroup(); + + // non-virtual methods: + QList elements(QCP::MarginSide side) const { return mChildren.value(side); } + bool isEmpty() const; + void clear(); + +protected: + // non-property members: + QCustomPlot *mParentPlot; + QHash > mChildren; + + // non-virtual methods: + int commonMargin(QCP::MarginSide side) const; + void addChild(QCP::MarginSide side, QCPLayoutElement *element); + void removeChild(QCP::MarginSide side, QCPLayoutElement *element); + +private: + Q_DISABLE_COPY(QCPMarginGroup) + + friend class QCPLayoutElement; +}; + + +class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPLayout* layout READ layout) + Q_PROPERTY(QRect rect READ rect) + Q_PROPERTY(QRect outerRect READ outerRect WRITE setOuterRect) + Q_PROPERTY(QMargins margins READ margins WRITE setMargins) + Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins) + Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize) + Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize) + /// \endcond +public: + /*! + Defines the phases of the update process, that happens just before a replot. At each phase, + \ref update is called with the according UpdatePhase value. + */ + enum UpdatePhase { upPreparation ///< Phase used for any type of preparation that needs to be done before margin calculation and layout + ,upMargins ///< Phase in which the margins are calculated and set + ,upLayout ///< Final phase in which the layout system places the rects of the elements + }; + Q_ENUMS(UpdatePhase) + + explicit QCPLayoutElement(QCustomPlot *parentPlot=0); + virtual ~QCPLayoutElement(); + + // getters: + QCPLayout *layout() const { return mParentLayout; } + QRect rect() const { return mRect; } + QRect outerRect() const { return mOuterRect; } + QMargins margins() const { return mMargins; } + QMargins minimumMargins() const { return mMinimumMargins; } + QCP::MarginSides autoMargins() const { return mAutoMargins; } + QSize minimumSize() const { return mMinimumSize; } + QSize maximumSize() const { return mMaximumSize; } + QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, (QCPMarginGroup*)0); } + QHash marginGroups() const { return mMarginGroups; } + + // setters: + void setOuterRect(const QRect &rect); + void setMargins(const QMargins &margins); + void setMinimumMargins(const QMargins &margins); + void setAutoMargins(QCP::MarginSides sides); + void setMinimumSize(const QSize &size); + void setMinimumSize(int width, int height); + void setMaximumSize(const QSize &size); + void setMaximumSize(int width, int height); + void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group); + + // introduced virtual methods: + virtual void update(UpdatePhase phase); + virtual QSize minimumSizeHint() const; + virtual QSize maximumSizeHint() const; + virtual QList elements(bool recursive) const; + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +protected: + // property members: + QCPLayout *mParentLayout; + QSize mMinimumSize, mMaximumSize; + QRect mRect, mOuterRect; + QMargins mMargins, mMinimumMargins; + QCP::MarginSides mAutoMargins; + QHash mMarginGroups; + + // introduced virtual methods: + virtual int calculateAutoMargin(QCP::MarginSide side); + // events: + virtual void mousePressEvent(QMouseEvent *event) {Q_UNUSED(event)} + virtual void mouseMoveEvent(QMouseEvent *event) {Q_UNUSED(event)} + virtual void mouseReleaseEvent(QMouseEvent *event) {Q_UNUSED(event)} + virtual void mouseDoubleClickEvent(QMouseEvent *event) {Q_UNUSED(event)} + virtual void wheelEvent(QWheelEvent *event) {Q_UNUSED(event)} + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const { Q_UNUSED(painter) } + virtual void draw(QCPPainter *painter) { Q_UNUSED(painter) } + virtual void parentPlotInitialized(QCustomPlot *parentPlot); + +private: + Q_DISABLE_COPY(QCPLayoutElement) + + friend class QCustomPlot; + friend class QCPLayout; + friend class QCPMarginGroup; +}; + + +class QCP_LIB_DECL QCPLayout : public QCPLayoutElement +{ + Q_OBJECT +public: + explicit QCPLayout(); + + // reimplemented virtual methods: + virtual void update(UpdatePhase phase); + virtual QList elements(bool recursive) const; + + // introduced virtual methods: + virtual int elementCount() const = 0; + virtual QCPLayoutElement* elementAt(int index) const = 0; + virtual QCPLayoutElement* takeAt(int index) = 0; + virtual bool take(QCPLayoutElement* element) = 0; + virtual void simplify(); + + // non-virtual methods: + bool removeAt(int index); + bool remove(QCPLayoutElement* element); + void clear(); + +protected: + // introduced virtual methods: + virtual void updateLayout(); + + // non-virtual methods: + void sizeConstraintsChanged() const; + void adoptElement(QCPLayoutElement *el); + void releaseElement(QCPLayoutElement *el); + QVector getSectionSizes(QVector maxSizes, QVector minSizes, QVector stretchFactors, int totalSize) const; + +private: + Q_DISABLE_COPY(QCPLayout) + friend class QCPLayoutElement; +}; + + +class QCP_LIB_DECL QCPLayoutGrid : public QCPLayout +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(int rowCount READ rowCount) + Q_PROPERTY(int columnCount READ columnCount) + Q_PROPERTY(QList columnStretchFactors READ columnStretchFactors WRITE setColumnStretchFactors) + Q_PROPERTY(QList rowStretchFactors READ rowStretchFactors WRITE setRowStretchFactors) + Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing) + Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing) + /// \endcond +public: + explicit QCPLayoutGrid(); + virtual ~QCPLayoutGrid(); + + // getters: + int rowCount() const; + int columnCount() const; + QList columnStretchFactors() const { return mColumnStretchFactors; } + QList rowStretchFactors() const { return mRowStretchFactors; } + int columnSpacing() const { return mColumnSpacing; } + int rowSpacing() const { return mRowSpacing; } + + // setters: + void setColumnStretchFactor(int column, double factor); + void setColumnStretchFactors(const QList &factors); + void setRowStretchFactor(int row, double factor); + void setRowStretchFactors(const QList &factors); + void setColumnSpacing(int pixels); + void setRowSpacing(int pixels); + + // reimplemented virtual methods: + virtual void updateLayout(); + virtual int elementCount() const; + virtual QCPLayoutElement* elementAt(int index) const; + virtual QCPLayoutElement* takeAt(int index); + virtual bool take(QCPLayoutElement* element); + virtual QList elements(bool recursive) const; + virtual void simplify(); + virtual QSize minimumSizeHint() const; + virtual QSize maximumSizeHint() const; + + // non-virtual methods: + QCPLayoutElement *element(int row, int column) const; + bool addElement(int row, int column, QCPLayoutElement *element); + bool hasElement(int row, int column); + void expandTo(int newRowCount, int newColumnCount); + void insertRow(int newIndex); + void insertColumn(int newIndex); + +protected: + // property members: + QList > mElements; + QList mColumnStretchFactors; + QList mRowStretchFactors; + int mColumnSpacing, mRowSpacing; + + // non-virtual methods: + void getMinimumRowColSizes(QVector *minColWidths, QVector *minRowHeights) const; + void getMaximumRowColSizes(QVector *maxColWidths, QVector *maxRowHeights) const; + +private: + Q_DISABLE_COPY(QCPLayoutGrid) +}; + + +class QCP_LIB_DECL QCPLayoutInset : public QCPLayout +{ + Q_OBJECT +public: + /*! + Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset. + */ + enum InsetPlacement { ipFree ///< The element may be positioned/sized arbitrarily, see \ref setInsetRect + ,ipBorderAligned ///< The element is aligned to one of the layout sides, see \ref setInsetAlignment + }; + + explicit QCPLayoutInset(); + virtual ~QCPLayoutInset(); + + // getters: + InsetPlacement insetPlacement(int index) const; + Qt::Alignment insetAlignment(int index) const; + QRectF insetRect(int index) const; + + // setters: + void setInsetPlacement(int index, InsetPlacement placement); + void setInsetAlignment(int index, Qt::Alignment alignment); + void setInsetRect(int index, const QRectF &rect); + + // reimplemented virtual methods: + virtual void updateLayout(); + virtual int elementCount() const; + virtual QCPLayoutElement* elementAt(int index) const; + virtual QCPLayoutElement* takeAt(int index); + virtual bool take(QCPLayoutElement* element); + virtual void simplify() {} + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + // non-virtual methods: + void addElement(QCPLayoutElement *element, Qt::Alignment alignment); + void addElement(QCPLayoutElement *element, const QRectF &rect); + +protected: + // property members: + QList mElements; + QList mInsetPlacement; + QList mInsetAlignment; + QList mInsetRect; + +private: + Q_DISABLE_COPY(QCPLayoutInset) +}; + + +class QCP_LIB_DECL QCPLineEnding +{ + Q_GADGET +public: + /*! + Defines the type of ending decoration for line-like items, e.g. an arrow. + + \image html QCPLineEnding.png + + The width and length of these decorations can be controlled with the functions \ref setWidth + and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only + support a width, the length property is ignored. + + \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail, QCPAxis::setLowerEnding, QCPAxis::setUpperEnding + */ + Q_ENUMS(EndingStyle) + enum EndingStyle { esNone ///< No ending decoration + ,esFlatArrow ///< A filled arrow head with a straight/flat back (a triangle) + ,esSpikeArrow ///< A filled arrow head with an indented back + ,esLineArrow ///< A non-filled arrow head with open back + ,esDisc ///< A filled circle + ,esSquare ///< A filled square + ,esDiamond ///< A filled diamond (45° rotated square) + ,esBar ///< A bar perpendicular to the line + ,esHalfBar ///< A bar perpendicular to the line, pointing out to only one side (to which side can be changed with \ref setInverted) + ,esSkewedBar ///< A bar that is skewed (skew controllable via \ref setLength) + }; + + QCPLineEnding(); + QCPLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false); + + // getters: + EndingStyle style() const { return mStyle; } + double width() const { return mWidth; } + double length() const { return mLength; } + bool inverted() const { return mInverted; } + + // setters: + void setStyle(EndingStyle style); + void setWidth(double width); + void setLength(double length); + void setInverted(bool inverted); + + // non-property methods: + double boundingDistance() const; + double realLength() const; + void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const; + void draw(QCPPainter *painter, const QVector2D &pos, double angle) const; + +protected: + // property members: + EndingStyle mStyle; + double mWidth, mLength; + bool mInverted; +}; +Q_DECLARE_TYPEINFO(QCPLineEnding, Q_MOVABLE_TYPE); + + +class QCP_LIB_DECL QCPGrid :public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool subGridVisible READ subGridVisible WRITE setSubGridVisible) + Q_PROPERTY(bool antialiasedSubGrid READ antialiasedSubGrid WRITE setAntialiasedSubGrid) + Q_PROPERTY(bool antialiasedZeroLine READ antialiasedZeroLine WRITE setAntialiasedZeroLine) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen) + Q_PROPERTY(QPen zeroLinePen READ zeroLinePen WRITE setZeroLinePen) + /// \endcond +public: + QCPGrid(QCPAxis *parentAxis); + + // getters: + bool subGridVisible() const { return mSubGridVisible; } + bool antialiasedSubGrid() const { return mAntialiasedSubGrid; } + bool antialiasedZeroLine() const { return mAntialiasedZeroLine; } + QPen pen() const { return mPen; } + QPen subGridPen() const { return mSubGridPen; } + QPen zeroLinePen() const { return mZeroLinePen; } + + // setters: + void setSubGridVisible(bool visible); + void setAntialiasedSubGrid(bool enabled); + void setAntialiasedZeroLine(bool enabled); + void setPen(const QPen &pen); + void setSubGridPen(const QPen &pen); + void setZeroLinePen(const QPen &pen); + +protected: + // property members: + bool mSubGridVisible; + bool mAntialiasedSubGrid, mAntialiasedZeroLine; + QPen mPen, mSubGridPen, mZeroLinePen; + // non-property members: + QCPAxis *mParentAxis; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual void draw(QCPPainter *painter); + + // non-virtual methods: + void drawGridLines(QCPPainter *painter) const; + void drawSubGridLines(QCPPainter *painter) const; + + friend class QCPAxis; +}; + + +class QCP_LIB_DECL QCPAxis : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(AxisType axisType READ axisType) + Q_PROPERTY(QCPAxisRect* axisRect READ axisRect) + Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY scaleTypeChanged) + Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase) + Q_PROPERTY(QCPRange range READ range WRITE setRange NOTIFY rangeChanged) + Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed) + Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks) + Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount) + Q_PROPERTY(bool autoTickLabels READ autoTickLabels WRITE setAutoTickLabels) + Q_PROPERTY(bool autoTickStep READ autoTickStep WRITE setAutoTickStep) + Q_PROPERTY(bool autoSubTicks READ autoSubTicks WRITE setAutoSubTicks) + Q_PROPERTY(bool ticks READ ticks WRITE setTicks) + Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels) + Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding) + Q_PROPERTY(LabelType tickLabelType READ tickLabelType WRITE setTickLabelType) + Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont) + Q_PROPERTY(QColor tickLabelColor READ tickLabelColor WRITE setTickLabelColor) + Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation) + Q_PROPERTY(LabelSide tickLabelSide READ tickLabelSide WRITE setTickLabelSide) + Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat) + Q_PROPERTY(Qt::TimeSpec dateTimeSpec READ dateTimeSpec WRITE setDateTimeSpec) + Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat) + Q_PROPERTY(int numberPrecision READ numberPrecision WRITE setNumberPrecision) + Q_PROPERTY(double tickStep READ tickStep WRITE setTickStep) + Q_PROPERTY(QVector tickVector READ tickVector WRITE setTickVector) + Q_PROPERTY(QVector tickVectorLabels READ tickVectorLabels WRITE setTickVectorLabels) + Q_PROPERTY(int tickLengthIn READ tickLengthIn WRITE setTickLengthIn) + Q_PROPERTY(int tickLengthOut READ tickLengthOut WRITE setTickLengthOut) + Q_PROPERTY(int subTickCount READ subTickCount WRITE setSubTickCount) + Q_PROPERTY(int subTickLengthIn READ subTickLengthIn WRITE setSubTickLengthIn) + Q_PROPERTY(int subTickLengthOut READ subTickLengthOut WRITE setSubTickLengthOut) + Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen) + Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen) + Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen) + Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont) + Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor) + Q_PROPERTY(QString label READ label WRITE setLabel) + Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding) + Q_PROPERTY(int padding READ padding WRITE setPadding) + Q_PROPERTY(int offset READ offset WRITE setOffset) + Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged) + Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged) + Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont) + Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont) + Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor) + Q_PROPERTY(QColor selectedLabelColor READ selectedLabelColor WRITE setSelectedLabelColor) + Q_PROPERTY(QPen selectedBasePen READ selectedBasePen WRITE setSelectedBasePen) + Q_PROPERTY(QPen selectedTickPen READ selectedTickPen WRITE setSelectedTickPen) + Q_PROPERTY(QPen selectedSubTickPen READ selectedSubTickPen WRITE setSelectedSubTickPen) + Q_PROPERTY(QCPLineEnding lowerEnding READ lowerEnding WRITE setLowerEnding) + Q_PROPERTY(QCPLineEnding upperEnding READ upperEnding WRITE setUpperEnding) + Q_PROPERTY(QCPGrid* grid READ grid) + /// \endcond +public: + /*! + Defines at which side of the axis rect the axis will appear. This also affects how the tick + marks are drawn, on which side the labels are placed etc. + */ + enum AxisType { atLeft = 0x01 ///< 0x01 Axis is vertical and on the left side of the axis rect + ,atRight = 0x02 ///< 0x02 Axis is vertical and on the right side of the axis rect + ,atTop = 0x04 ///< 0x04 Axis is horizontal and on the top side of the axis rect + ,atBottom = 0x08 ///< 0x08 Axis is horizontal and on the bottom side of the axis rect + }; + Q_FLAGS(AxisType AxisTypes) + Q_DECLARE_FLAGS(AxisTypes, AxisType) + /*! + When automatic tick label generation is enabled (\ref setAutoTickLabels), defines how the + coordinate of the tick is interpreted, i.e. translated into a string. + + \see setTickLabelType + */ + enum LabelType { ltNumber ///< Tick coordinate is regarded as normal number and will be displayed as such. (see \ref setNumberFormat) + ,ltDateTime ///< Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displayed and formatted as such. (for details, see \ref setDateTimeFormat) + }; + Q_ENUMS(LabelType) + /*! + Defines on which side of the axis the tick labels (numbers) shall appear. + + \see setTickLabelSide + */ + enum LabelSide { lsInside ///< Tick labels will be displayed inside the axis rect and clipped to the inner axis rect + ,lsOutside ///< Tick labels will be displayed outside the axis rect + }; + Q_ENUMS(LabelSide) + /*! + Defines the scale of an axis. + \see setScaleType + */ + enum ScaleType { stLinear ///< Linear scaling + ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power (see \ref setScaleLogBase). + }; + Q_ENUMS(ScaleType) + /*! + Defines the selectable parts of an axis. + \see setSelectableParts, setSelectedParts + */ + enum SelectablePart { spNone = 0 ///< None of the selectable parts + ,spAxis = 0x001 ///< The axis backbone and tick marks + ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually) + ,spAxisLabel = 0x004 ///< The axis label + }; + Q_FLAGS(SelectablePart SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + explicit QCPAxis(QCPAxisRect *parent, AxisType type); + virtual ~QCPAxis(); + + // getters: + AxisType axisType() const { return mAxisType; } + QCPAxisRect *axisRect() const { return mAxisRect; } + ScaleType scaleType() const { return mScaleType; } + double scaleLogBase() const { return mScaleLogBase; } + const QCPRange range() const { return mRange; } + bool rangeReversed() const { return mRangeReversed; } + bool autoTicks() const { return mAutoTicks; } + int autoTickCount() const { return mAutoTickCount; } + bool autoTickLabels() const { return mAutoTickLabels; } + bool autoTickStep() const { return mAutoTickStep; } + bool autoSubTicks() const { return mAutoSubTicks; } + bool ticks() const { return mTicks; } + bool tickLabels() const { return mTickLabels; } + int tickLabelPadding() const; + LabelType tickLabelType() const { return mTickLabelType; } + QFont tickLabelFont() const { return mTickLabelFont; } + QColor tickLabelColor() const { return mTickLabelColor; } + double tickLabelRotation() const; + LabelSide tickLabelSide() const; + QString dateTimeFormat() const { return mDateTimeFormat; } + Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; } + QString numberFormat() const; + int numberPrecision() const { return mNumberPrecision; } + double tickStep() const { return mTickStep; } + QVector tickVector() const { return mTickVector; } + QVector tickVectorLabels() const { return mTickVectorLabels; } + int tickLengthIn() const; + int tickLengthOut() const; + int subTickCount() const { return mSubTickCount; } + int subTickLengthIn() const; + int subTickLengthOut() const; + QPen basePen() const { return mBasePen; } + QPen tickPen() const { return mTickPen; } + QPen subTickPen() const { return mSubTickPen; } + QFont labelFont() const { return mLabelFont; } + QColor labelColor() const { return mLabelColor; } + QString label() const { return mLabel; } + int labelPadding() const; + int padding() const { return mPadding; } + int offset() const; + SelectableParts selectedParts() const { return mSelectedParts; } + SelectableParts selectableParts() const { return mSelectableParts; } + QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; } + QFont selectedLabelFont() const { return mSelectedLabelFont; } + QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; } + QColor selectedLabelColor() const { return mSelectedLabelColor; } + QPen selectedBasePen() const { return mSelectedBasePen; } + QPen selectedTickPen() const { return mSelectedTickPen; } + QPen selectedSubTickPen() const { return mSelectedSubTickPen; } + QCPLineEnding lowerEnding() const; + QCPLineEnding upperEnding() const; + QCPGrid *grid() const { return mGrid; } + + // setters: + Q_SLOT void setScaleType(QCPAxis::ScaleType type); + void setScaleLogBase(double base); + Q_SLOT void setRange(const QCPRange &range); + void setRange(double lower, double upper); + void setRange(double position, double size, Qt::AlignmentFlag alignment); + void setRangeLower(double lower); + void setRangeUpper(double upper); + void setRangeReversed(bool reversed); + void setAutoTicks(bool on); + void setAutoTickCount(int approximateCount); + void setAutoTickLabels(bool on); + void setAutoTickStep(bool on); + void setAutoSubTicks(bool on); + void setTicks(bool show); + void setTickLabels(bool show); + void setTickLabelPadding(int padding); + void setTickLabelType(LabelType type); + void setTickLabelFont(const QFont &font); + void setTickLabelColor(const QColor &color); + void setTickLabelRotation(double degrees); + void setTickLabelSide(LabelSide side); + void setDateTimeFormat(const QString &format); + void setDateTimeSpec(const Qt::TimeSpec &timeSpec); + void setNumberFormat(const QString &formatCode); + void setNumberPrecision(int precision); + void setTickStep(double step); + void setTickVector(const QVector &vec); + void setTickVectorLabels(const QVector &vec); + void setTickLength(int inside, int outside=0); + void setTickLengthIn(int inside); + void setTickLengthOut(int outside); + void setSubTickCount(int count); + void setSubTickLength(int inside, int outside=0); + void setSubTickLengthIn(int inside); + void setSubTickLengthOut(int outside); + void setBasePen(const QPen &pen); + void setTickPen(const QPen &pen); + void setSubTickPen(const QPen &pen); + void setLabelFont(const QFont &font); + void setLabelColor(const QColor &color); + void setLabel(const QString &str); + void setLabelPadding(int padding); + void setPadding(int padding); + void setOffset(int offset); + void setSelectedTickLabelFont(const QFont &font); + void setSelectedLabelFont(const QFont &font); + void setSelectedTickLabelColor(const QColor &color); + void setSelectedLabelColor(const QColor &color); + void setSelectedBasePen(const QPen &pen); + void setSelectedTickPen(const QPen &pen); + void setSelectedSubTickPen(const QPen &pen); + Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts); + void setLowerEnding(const QCPLineEnding &ending); + void setUpperEnding(const QCPLineEnding &ending); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + // non-property methods: + Qt::Orientation orientation() const { return mOrientation; } + void moveRange(double diff); + void scaleRange(double factor, double center); + void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0); + void rescale(bool onlyVisiblePlottables=false); + double pixelToCoord(double value) const; + double coordToPixel(double value) const; + SelectablePart getPartAt(const QPointF &pos) const; + QList plottables() const; + QList graphs() const; + QList items() const; + + static AxisType marginSideToAxisType(QCP::MarginSide side); + static Qt::Orientation orientation(AxisType type) { return type==atBottom||type==atTop ? Qt::Horizontal : Qt::Vertical; } + static AxisType opposite(AxisType type); + +signals: + void ticksRequest(); + void rangeChanged(const QCPRange &newRange); + void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + void scaleTypeChanged(QCPAxis::ScaleType scaleType); + void selectionChanged(const QCPAxis::SelectableParts &parts); + void selectableChanged(const QCPAxis::SelectableParts &parts); + +protected: + // property members: + // axis base: + AxisType mAxisType; + QCPAxisRect *mAxisRect; + //int mOffset; // in QCPAxisPainter + int mPadding; + Qt::Orientation mOrientation; + SelectableParts mSelectableParts, mSelectedParts; + QPen mBasePen, mSelectedBasePen; + //QCPLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter + // axis label: + //int mLabelPadding; // in QCPAxisPainter + QString mLabel; + QFont mLabelFont, mSelectedLabelFont; + QColor mLabelColor, mSelectedLabelColor; + // tick labels: + //int mTickLabelPadding; // in QCPAxisPainter + bool mTickLabels, mAutoTickLabels; + //double mTickLabelRotation; // in QCPAxisPainter + LabelType mTickLabelType; + QFont mTickLabelFont, mSelectedTickLabelFont; + QColor mTickLabelColor, mSelectedTickLabelColor; + QString mDateTimeFormat; + Qt::TimeSpec mDateTimeSpec; + int mNumberPrecision; + QLatin1Char mNumberFormatChar; + bool mNumberBeautifulPowers; + //bool mNumberMultiplyCross; // QCPAxisPainter + // ticks and subticks: + bool mTicks; + double mTickStep; + int mSubTickCount, mAutoTickCount; + bool mAutoTicks, mAutoTickStep, mAutoSubTicks; + //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // QCPAxisPainter + QPen mTickPen, mSelectedTickPen; + QPen mSubTickPen, mSelectedSubTickPen; + // scale and range: + QCPRange mRange; + bool mRangeReversed; + ScaleType mScaleType; + double mScaleLogBase, mScaleLogBaseLogInv; + + // non-property members: + QCPGrid *mGrid; + QCPAxisPainterPrivate *mAxisPainter; + int mLowestVisibleTick, mHighestVisibleTick; + QVector mTickVector; + QVector mTickVectorLabels; + QVector mSubTickVector; + bool mCachedMarginValid; + int mCachedMargin; + + // introduced virtual methods: + virtual void setupTickVectors(); + virtual void generateAutoTicks(); + virtual int calculateAutoSubTickCount(double tickStep) const; + virtual int calculateMargin(); + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual void draw(QCPPainter *painter); + virtual QCP::Interaction selectionCategory() const; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + + // non-virtual methods: + void visibleTickBounds(int &lowIndex, int &highIndex) const; + double baseLog(double value) const; + double basePow(double value) const; + QPen getBasePen() const; + QPen getTickPen() const; + QPen getSubTickPen() const; + QFont getTickLabelFont() const; + QFont getLabelFont() const; + QColor getTickLabelColor() const; + QColor getLabelColor() const; + +private: + Q_DISABLE_COPY(QCPAxis) + + friend class QCustomPlot; + friend class QCPGrid; + friend class QCPAxisRect; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::SelectableParts) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::AxisTypes) +Q_DECLARE_METATYPE(QCPAxis::SelectablePart) + + +class QCPAxisPainterPrivate +{ +public: + explicit QCPAxisPainterPrivate(QCustomPlot *parentPlot); + virtual ~QCPAxisPainterPrivate(); + + virtual void draw(QCPPainter *painter); + virtual int size() const; + void clearCache(); + + QRect axisSelectionBox() const { return mAxisSelectionBox; } + QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; } + QRect labelSelectionBox() const { return mLabelSelectionBox; } + + // public property members: + QCPAxis::AxisType type; + QPen basePen; + QCPLineEnding lowerEnding, upperEnding; // directly accessed by QCPAxis setters/getters + int labelPadding; // directly accessed by QCPAxis setters/getters + QFont labelFont; + QColor labelColor; + QString label; + int tickLabelPadding; // directly accessed by QCPAxis setters/getters + double tickLabelRotation; // directly accessed by QCPAxis setters/getters + QCPAxis::LabelSide tickLabelSide; // directly accessed by QCPAxis setters/getters + bool substituteExponent; + bool numberMultiplyCross; // directly accessed by QCPAxis setters/getters + int tickLengthIn, tickLengthOut, subTickLengthIn, subTickLengthOut; // directly accessed by QCPAxis setters/getters + QPen tickPen, subTickPen; + QFont tickLabelFont; + QColor tickLabelColor; + QRect axisRect, viewportRect; + double offset; // directly accessed by QCPAxis setters/getters + bool abbreviateDecimalPowers; + bool reversedEndings; + + QVector subTickPositions; + QVector tickPositions; + QVector tickLabels; + +protected: + struct CachedLabel + { + QPointF offset; + QPixmap pixmap; + }; + struct TickLabelData + { + QString basePart, expPart; + QRect baseBounds, expBounds, totalBounds, rotatedTotalBounds; + QFont baseFont, expFont; + }; + QCustomPlot *mParentPlot; + QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters + QCache mLabelCache; + QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox; + + virtual QByteArray generateLabelParameterHash() const; + + virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize); + virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const; + virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const; + virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const; + virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const; +}; + + +class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(bool antialiasedFill READ antialiasedFill WRITE setAntialiasedFill) + Q_PROPERTY(bool antialiasedScatters READ antialiasedScatters WRITE setAntialiasedScatters) + Q_PROPERTY(bool antialiasedErrorBars READ antialiasedErrorBars WRITE setAntialiasedErrorBars) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis) + Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged) + /// \endcond +public: + QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis); + + // getters: + QString name() const { return mName; } + bool antialiasedFill() const { return mAntialiasedFill; } + bool antialiasedScatters() const { return mAntialiasedScatters; } + bool antialiasedErrorBars() const { return mAntialiasedErrorBars; } + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + QCPAxis *keyAxis() const { return mKeyAxis.data(); } + QCPAxis *valueAxis() const { return mValueAxis.data(); } + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setName(const QString &name); + void setAntialiasedFill(bool enabled); + void setAntialiasedScatters(bool enabled); + void setAntialiasedErrorBars(bool enabled); + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + void setKeyAxis(QCPAxis *axis); + void setValueAxis(QCPAxis *axis); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // introduced virtual methods: + virtual void clearData() = 0; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0; + virtual bool addToLegend(); + virtual bool removeFromLegend() const; + + // non-property methods: + void rescaleAxes(bool onlyEnlarge=false) const; + void rescaleKeyAxis(bool onlyEnlarge=false) const; + void rescaleValueAxis(bool onlyEnlarge=false) const; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + +protected: + /*! + Represents negative and positive sign domain for passing to \ref getKeyRange and \ref getValueRange. + */ + enum SignDomain { sdNegative ///< The negative sign domain, i.e. numbers smaller than zero + ,sdBoth ///< Both sign domains, including zero, i.e. all (rational) numbers + ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero + }; + + // property members: + QString mName; + bool mAntialiasedFill, mAntialiasedScatters, mAntialiasedErrorBars; + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + QPointer mKeyAxis, mValueAxis; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual QRect clipRect() const; + virtual void draw(QCPPainter *painter) = 0; + virtual QCP::Interaction selectionCategory() const; + void applyDefaultAntialiasingHint(QCPPainter *painter) const; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + + // introduced virtual methods: + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0; + + // non-virtual methods: + void coordsToPixels(double key, double value, double &x, double &y) const; + const QPointF coordsToPixels(double key, double value) const; + void pixelsToCoords(double x, double y, double &key, double &value) const; + void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const; + QPen mainPen() const; + QBrush mainBrush() const; + void applyFillAntialiasingHint(QCPPainter *painter) const; + void applyScattersAntialiasingHint(QCPPainter *painter) const; + void applyErrorBarsAntialiasingHint(QCPPainter *painter) const; + double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const; + +private: + Q_DISABLE_COPY(QCPAbstractPlottable) + + friend class QCustomPlot; + friend class QCPAxis; + friend class QCPPlottableLegendItem; +}; + + +class QCP_LIB_DECL QCPItemAnchor +{ +public: + QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1); + virtual ~QCPItemAnchor(); + + // getters: + QString name() const { return mName; } + virtual QPointF pixelPoint() const; + +protected: + // property members: + QString mName; + + // non-property members: + QCustomPlot *mParentPlot; + QCPAbstractItem *mParentItem; + int mAnchorId; + QSet mChildrenX, mChildrenY; + + // introduced virtual methods: + virtual QCPItemPosition *toQCPItemPosition() { return 0; } + + // non-virtual methods: + void addChildX(QCPItemPosition* pos); // called from pos when this anchor is set as parent + void removeChildX(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted + void addChildY(QCPItemPosition* pos); // called from pos when this anchor is set as parent + void removeChildY(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted + +private: + Q_DISABLE_COPY(QCPItemAnchor) + + friend class QCPItemPosition; +}; + + + +class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor +{ +public: + /*! + Defines the ways an item position can be specified. Thus it defines what the numbers passed to + \ref setCoords actually mean. + + \see setType + */ + enum PositionType { ptAbsolute ///< Static positioning in pixels, starting from the top left corner of the viewport/widget. + ,ptViewportRatio ///< Static positioning given by a fraction of the viewport size. For example, if you call setCoords(0, 0), the position will be at the top + ///< left corner of the viewport/widget. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and + ///< vertically at the top of the viewport/widget, etc. + ,ptAxisRectRatio ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect). For example, if you call setCoords(0, 0), the position will be at the top + ///< left corner of the axis rect. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and + ///< vertically at the top of the axis rect, etc. You can also go beyond the axis rect by providing negative coordinates or coordinates larger than 1. + ,ptPlotCoords ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes). + }; + + QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name); + virtual ~QCPItemPosition(); + + // getters: + PositionType type() const { return typeX(); } + PositionType typeX() const { return mPositionTypeX; } + PositionType typeY() const { return mPositionTypeY; } + QCPItemAnchor *parentAnchor() const { return parentAnchorX(); } + QCPItemAnchor *parentAnchorX() const { return mParentAnchorX; } + QCPItemAnchor *parentAnchorY() const { return mParentAnchorY; } + double key() const { return mKey; } + double value() const { return mValue; } + QPointF coords() const { return QPointF(mKey, mValue); } + QCPAxis *keyAxis() const { return mKeyAxis.data(); } + QCPAxis *valueAxis() const { return mValueAxis.data(); } + QCPAxisRect *axisRect() const; + virtual QPointF pixelPoint() const; + + // setters: + void setType(PositionType type); + void setTypeX(PositionType type); + void setTypeY(PositionType type); + bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); + bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); + bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); + void setCoords(double key, double value); + void setCoords(const QPointF &coords); + void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis); + void setAxisRect(QCPAxisRect *axisRect); + void setPixelPoint(const QPointF &pixelPoint); + +protected: + // property members: + PositionType mPositionTypeX, mPositionTypeY; + QPointer mKeyAxis, mValueAxis; + QPointer mAxisRect; + double mKey, mValue; + QCPItemAnchor *mParentAnchorX, *mParentAnchorY; + + // reimplemented virtual methods: + virtual QCPItemPosition *toQCPItemPosition() { return this; } + +private: + Q_DISABLE_COPY(QCPItemPosition) + +}; + + +class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect) + Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged) + /// \endcond +public: + QCPAbstractItem(QCustomPlot *parentPlot); + virtual ~QCPAbstractItem(); + + // getters: + bool clipToAxisRect() const { return mClipToAxisRect; } + QCPAxisRect *clipAxisRect() const; + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setClipToAxisRect(bool clip); + void setClipAxisRect(QCPAxisRect *rect); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0; + + // non-virtual methods: + QList positions() const { return mPositions; } + QList anchors() const { return mAnchors; } + QCPItemPosition *position(const QString &name) const; + QCPItemAnchor *anchor(const QString &name) const; + bool hasAnchor(const QString &name) const; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + +protected: + // property members: + bool mClipToAxisRect; + QPointer mClipAxisRect; + QList mPositions; + QList mAnchors; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual QCP::Interaction selectionCategory() const; + virtual QRect clipRect() const; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual void draw(QCPPainter *painter) = 0; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + + // introduced virtual methods: + virtual QPointF anchorPixelPoint(int anchorId) const; + + // non-virtual methods: + double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const; + double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const; + QCPItemPosition *createPosition(const QString &name); + QCPItemAnchor *createAnchor(const QString &name, int anchorId); + +private: + Q_DISABLE_COPY(QCPAbstractItem) + + friend class QCustomPlot; + friend class QCPItemAnchor; +}; + + +class QCP_LIB_DECL QCustomPlot : public QWidget +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QRect viewport READ viewport WRITE setViewport) + Q_PROPERTY(QPixmap background READ background WRITE setBackground) + Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled) + Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode) + Q_PROPERTY(QCPLayoutGrid* plotLayout READ plotLayout) + Q_PROPERTY(bool autoAddPlottableToLegend READ autoAddPlottableToLegend WRITE setAutoAddPlottableToLegend) + Q_PROPERTY(int selectionTolerance READ selectionTolerance WRITE setSelectionTolerance) + Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag) + Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier) + /// \endcond +public: + /*! + Defines how a layer should be inserted relative to an other layer. + + \see addLayer, moveLayer + */ + enum LayerInsertMode { limBelow ///< Layer is inserted below other layer + ,limAbove ///< Layer is inserted above other layer + }; + Q_ENUMS(LayerInsertMode) + + /*! + Defines with what timing the QCustomPlot surface is refreshed after a replot. + + \see replot + */ + enum RefreshPriority { rpImmediate ///< The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot + ,rpQueued ///< Queues the refresh such that it is performed at a slightly delayed point in time after the replot, by calling QWidget::update() after the replot + ,rpHint ///< Whether to use immediate repaint or queued update depends on whether the plotting hint \ref QCP::phForceRepaint is set, see \ref setPlottingHints. + }; + + explicit QCustomPlot(QWidget *parent = 0); + virtual ~QCustomPlot(); + + // getters: + QRect viewport() const { return mViewport; } + QPixmap background() const { return mBackgroundPixmap; } + bool backgroundScaled() const { return mBackgroundScaled; } + Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } + QCPLayoutGrid *plotLayout() const { return mPlotLayout; } + QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; } + QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; } + bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; } + const QCP::Interactions interactions() const { return mInteractions; } + int selectionTolerance() const { return mSelectionTolerance; } + bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; } + QCP::PlottingHints plottingHints() const { return mPlottingHints; } + Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; } + + // setters: + void setViewport(const QRect &rect); + void setBackground(const QPixmap &pm); + void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding); + void setBackground(const QBrush &brush); + void setBackgroundScaled(bool scaled); + void setBackgroundScaledMode(Qt::AspectRatioMode mode); + void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements); + void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true); + void setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements); + void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true); + void setAutoAddPlottableToLegend(bool on); + void setInteractions(const QCP::Interactions &interactions); + void setInteraction(const QCP::Interaction &interaction, bool enabled=true); + void setSelectionTolerance(int pixels); + void setNoAntialiasingOnDrag(bool enabled); + void setPlottingHints(const QCP::PlottingHints &hints); + void setPlottingHint(QCP::PlottingHint hint, bool enabled=true); + void setMultiSelectModifier(Qt::KeyboardModifier modifier); + + // non-property methods: + // plottable interface: + QCPAbstractPlottable *plottable(int index); + QCPAbstractPlottable *plottable(); + bool addPlottable(QCPAbstractPlottable *plottable); + bool removePlottable(QCPAbstractPlottable *plottable); + bool removePlottable(int index); + int clearPlottables(); + int plottableCount() const; + QList selectedPlottables() const; + QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const; + bool hasPlottable(QCPAbstractPlottable *plottable) const; + + // specialized interface for QCPGraph: + QCPGraph *graph(int index) const; + QCPGraph *graph() const; + QCPGraph *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0); + bool removeGraph(QCPGraph *graph); + bool removeGraph(int index); + int clearGraphs(); + int graphCount() const; + QList selectedGraphs() const; + + // item interface: + QCPAbstractItem *item(int index) const; + QCPAbstractItem *item() const; + bool addItem(QCPAbstractItem* item); + bool removeItem(QCPAbstractItem *item); + bool removeItem(int index); + int clearItems(); + int itemCount() const; + QList selectedItems() const; + QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const; + bool hasItem(QCPAbstractItem *item) const; + + // layer interface: + QCPLayer *layer(const QString &name) const; + QCPLayer *layer(int index) const; + QCPLayer *currentLayer() const; + bool setCurrentLayer(const QString &name); + bool setCurrentLayer(QCPLayer *layer); + int layerCount() const; + bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove); + bool removeLayer(QCPLayer *layer); + bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove); + + // axis rect/layout interface: + int axisRectCount() const; + QCPAxisRect* axisRect(int index=0) const; + QList axisRects() const; + QCPLayoutElement* layoutElementAt(const QPointF &pos) const; + Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false); + + QList selectedAxes() const; + QList selectedLegends() const; + Q_SLOT void deselectAll(); + + bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator=QString(), const QString &pdfTitle=QString()); + bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1); + bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1); + bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0); + bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1); + QPixmap toPixmap(int width=0, int height=0, double scale=1.0); + void toPainter(QCPPainter *painter, int width=0, int height=0); + Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint); + + QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2; + QCPLegend *legend; + +signals: + void mouseDoubleClick(QMouseEvent *event); + void mousePress(QMouseEvent *event); + void mouseMove(QMouseEvent *event); + void mouseRelease(QMouseEvent *event); + void mouseWheel(QWheelEvent *event); + + void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event); + void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event); + void itemClick(QCPAbstractItem *item, QMouseEvent *event); + void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event); + void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event); + void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event); + void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event); + void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event); + void titleClick(QMouseEvent *event, QCPPlotTitle *title); + void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title); + + void selectionChangedByUser(); + void beforeReplot(); + void afterReplot(); + +protected: + // property members: + QRect mViewport; + QCPLayoutGrid *mPlotLayout; + bool mAutoAddPlottableToLegend; + QList mPlottables; + QList mGraphs; // extra list of plottables also in mPlottables that are of type QCPGraph + QList mItems; + QList mLayers; + QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements; + QCP::Interactions mInteractions; + int mSelectionTolerance; + bool mNoAntialiasingOnDrag; + QBrush mBackgroundBrush; + QPixmap mBackgroundPixmap; + QPixmap mScaledBackgroundPixmap; + bool mBackgroundScaled; + Qt::AspectRatioMode mBackgroundScaledMode; + QCPLayer *mCurrentLayer; + QCP::PlottingHints mPlottingHints; + Qt::KeyboardModifier mMultiSelectModifier; + + // non-property members: + QPixmap mPaintBuffer; + QPoint mMousePressPos; + QPointer mMouseEventElement; + bool mReplotting; + + // reimplemented virtual methods: + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + virtual void paintEvent(QPaintEvent *event); + virtual void resizeEvent(QResizeEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent *event); + + // introduced virtual methods: + virtual void draw(QCPPainter *painter); + virtual void axisRemoved(QCPAxis *axis); + virtual void legendRemoved(QCPLegend *legend); + + // non-virtual methods: + void updateLayerIndices() const; + QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const; + void drawBackground(QCPPainter *painter); + + friend class QCPLegend; + friend class QCPAxis; + friend class QCPLayer; + friend class QCPAxisRect; +}; + + +class QCP_LIB_DECL QCPColorGradient +{ + Q_GADGET +public: + /*! + Defines the color spaces in which color interpolation between gradient stops can be performed. + + \see setColorInterpolation + */ + enum ColorInterpolation { ciRGB ///< Color channels red, green and blue are linearly interpolated + ,ciHSV ///< Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the shortest angle distance) + }; + Q_ENUMS(ColorInterpolation) + + /*! + Defines the available presets that can be loaded with \ref loadPreset. See the documentation + there for an image of the presets. + */ + enum GradientPreset { gpGrayscale ///< Continuous lightness from black to white (suited for non-biased data representation) + ,gpHot ///< Continuous lightness from black over firey colors to white (suited for non-biased data representation) + ,gpCold ///< Continuous lightness from black over icey colors to white (suited for non-biased data representation) + ,gpNight ///< Continuous lightness from black over weak blueish colors to white (suited for non-biased data representation) + ,gpCandy ///< Blue over pink to white + ,gpGeography ///< Colors suitable to represent different elevations on geographical maps + ,gpIon ///< Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allows more precise magnitude estimates) + ,gpThermal ///< Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white + ,gpPolar ///< Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle and red for positive values + ,gpSpectrum ///< An approximation of the visible light spectrum (creates banding illusion but allows more precise magnitude estimates) + ,gpJet ///< Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion but allows more precise magnitude estimates) + ,gpHues ///< Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and phases, see \ref setPeriodic) + }; + Q_ENUMS(GradientPreset) + + QCPColorGradient(GradientPreset preset=gpCold); + bool operator==(const QCPColorGradient &other) const; + bool operator!=(const QCPColorGradient &other) const { return !(*this == other); } + + // getters: + int levelCount() const { return mLevelCount; } + QMap colorStops() const { return mColorStops; } + ColorInterpolation colorInterpolation() const { return mColorInterpolation; } + bool periodic() const { return mPeriodic; } + + // setters: + void setLevelCount(int n); + void setColorStops(const QMap &colorStops); + void setColorStopAt(double position, const QColor &color); + void setColorInterpolation(ColorInterpolation interpolation); + void setPeriodic(bool enabled); + + // non-property methods: + void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false); + QRgb color(double position, const QCPRange &range, bool logarithmic=false); + void loadPreset(GradientPreset preset); + void clearColorStops(); + QCPColorGradient inverted() const; + +protected: + void updateColorBuffer(); + + // property members: + int mLevelCount; + QMap mColorStops; + ColorInterpolation mColorInterpolation; + bool mPeriodic; + + // non-property members: + QVector mColorBuffer; + bool mColorBufferInvalidated; +}; + + +class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPixmap background READ background WRITE setBackground) + Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled) + Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode) + Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag) + Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom) + /// \endcond +public: + explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true); + virtual ~QCPAxisRect(); + + // getters: + QPixmap background() const { return mBackgroundPixmap; } + bool backgroundScaled() const { return mBackgroundScaled; } + Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } + Qt::Orientations rangeDrag() const { return mRangeDrag; } + Qt::Orientations rangeZoom() const { return mRangeZoom; } + QCPAxis *rangeDragAxis(Qt::Orientation orientation); + QCPAxis *rangeZoomAxis(Qt::Orientation orientation); + double rangeZoomFactor(Qt::Orientation orientation); + + // setters: + void setBackground(const QPixmap &pm); + void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding); + void setBackground(const QBrush &brush); + void setBackgroundScaled(bool scaled); + void setBackgroundScaledMode(Qt::AspectRatioMode mode); + void setRangeDrag(Qt::Orientations orientations); + void setRangeZoom(Qt::Orientations orientations); + void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical); + void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical); + void setRangeZoomFactor(double horizontalFactor, double verticalFactor); + void setRangeZoomFactor(double factor); + + // non-property methods: + int axisCount(QCPAxis::AxisType type) const; + QCPAxis *axis(QCPAxis::AxisType type, int index=0) const; + QList axes(QCPAxis::AxisTypes types) const; + QList axes() const; + QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=0); + QList addAxes(QCPAxis::AxisTypes types); + bool removeAxis(QCPAxis *axis); + QCPLayoutInset *insetLayout() const { return mInsetLayout; } + + void setupFullAxesBox(bool connectRanges=false); + QList plottables() const; + QList graphs() const; + QList items() const; + + // read-only interface imitating a QRect: + int left() const { return mRect.left(); } + int right() const { return mRect.right(); } + int top() const { return mRect.top(); } + int bottom() const { return mRect.bottom(); } + int width() const { return mRect.width(); } + int height() const { return mRect.height(); } + QSize size() const { return mRect.size(); } + QPoint topLeft() const { return mRect.topLeft(); } + QPoint topRight() const { return mRect.topRight(); } + QPoint bottomLeft() const { return mRect.bottomLeft(); } + QPoint bottomRight() const { return mRect.bottomRight(); } + QPoint center() const { return mRect.center(); } + + // reimplemented virtual methods: + virtual void update(UpdatePhase phase); + virtual QList elements(bool recursive) const; + +protected: + // property members: + QBrush mBackgroundBrush; + QPixmap mBackgroundPixmap; + QPixmap mScaledBackgroundPixmap; + bool mBackgroundScaled; + Qt::AspectRatioMode mBackgroundScaledMode; + QCPLayoutInset *mInsetLayout; + Qt::Orientations mRangeDrag, mRangeZoom; + QPointer mRangeDragHorzAxis, mRangeDragVertAxis, mRangeZoomHorzAxis, mRangeZoomVertAxis; + double mRangeZoomFactorHorz, mRangeZoomFactorVert; + // non-property members: + QCPRange mDragStartHorzRange, mDragStartVertRange; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + QPoint mDragStart; + bool mDragging; + QHash > mAxes; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual void draw(QCPPainter *painter); + virtual int calculateAutoMargin(QCP::MarginSide side); + // events: + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent *event); + + // non-property methods: + void drawBackground(QCPPainter *painter); + void updateAxesOffset(QCPAxis::AxisType type); + +private: + Q_DISABLE_COPY(QCPAxisRect) + + friend class QCustomPlot; +}; + + +class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPLegend* parentLegend READ parentLegend) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectionChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectableChanged) + /// \endcond +public: + explicit QCPAbstractLegendItem(QCPLegend *parent); + + // getters: + QCPLegend *parentLegend() const { return mParentLegend; } + QFont font() const { return mFont; } + QColor textColor() const { return mTextColor; } + QFont selectedFont() const { return mSelectedFont; } + QColor selectedTextColor() const { return mSelectedTextColor; } + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setFont(const QFont &font); + void setTextColor(const QColor &color); + void setSelectedFont(const QFont &font); + void setSelectedTextColor(const QColor &color); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + +protected: + // property members: + QCPLegend *mParentLegend; + QFont mFont; + QColor mTextColor; + QFont mSelectedFont; + QColor mSelectedTextColor; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual QCP::Interaction selectionCategory() const; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual QRect clipRect() const; + virtual void draw(QCPPainter *painter) = 0; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + +private: + Q_DISABLE_COPY(QCPAbstractLegendItem) + + friend class QCPLegend; +}; + + +class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem +{ + Q_OBJECT +public: + QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable); + + // getters: + QCPAbstractPlottable *plottable() { return mPlottable; } + +protected: + // property members: + QCPAbstractPlottable *mPlottable; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual QSize minimumSizeHint() const; + + // non-virtual methods: + QPen getIconBorderPen() const; + QColor getTextColor() const; + QFont getFont() const; +}; + + +class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) + Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding) + Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen) + Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectionChanged) + Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectableChanged) + Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen) + Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor) + /// \endcond +public: + /*! + Defines the selectable parts of a legend + + \see setSelectedParts, setSelectableParts + */ + enum SelectablePart { spNone = 0x000 ///< 0x000 None + ,spLegendBox = 0x001 ///< 0x001 The legend box (frame) + ,spItems = 0x002 ///< 0x002 Legend items individually (see \ref selectedItems) + }; + Q_FLAGS(SelectablePart SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + explicit QCPLegend(); + virtual ~QCPLegend(); + + // getters: + QPen borderPen() const { return mBorderPen; } + QBrush brush() const { return mBrush; } + QFont font() const { return mFont; } + QColor textColor() const { return mTextColor; } + QSize iconSize() const { return mIconSize; } + int iconTextPadding() const { return mIconTextPadding; } + QPen iconBorderPen() const { return mIconBorderPen; } + SelectableParts selectableParts() const { return mSelectableParts; } + SelectableParts selectedParts() const; + QPen selectedBorderPen() const { return mSelectedBorderPen; } + QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; } + QBrush selectedBrush() const { return mSelectedBrush; } + QFont selectedFont() const { return mSelectedFont; } + QColor selectedTextColor() const { return mSelectedTextColor; } + + // setters: + void setBorderPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setFont(const QFont &font); + void setTextColor(const QColor &color); + void setIconSize(const QSize &size); + void setIconSize(int width, int height); + void setIconTextPadding(int padding); + void setIconBorderPen(const QPen &pen); + Q_SLOT void setSelectableParts(const SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const SelectableParts &selectedParts); + void setSelectedBorderPen(const QPen &pen); + void setSelectedIconBorderPen(const QPen &pen); + void setSelectedBrush(const QBrush &brush); + void setSelectedFont(const QFont &font); + void setSelectedTextColor(const QColor &color); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + // non-virtual methods: + QCPAbstractLegendItem *item(int index) const; + QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const; + int itemCount() const; + bool hasItem(QCPAbstractLegendItem *item) const; + bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const; + bool addItem(QCPAbstractLegendItem *item); + bool removeItem(int index); + bool removeItem(QCPAbstractLegendItem *item); + void clearItems(); + QList selectedItems() const; + +signals: + void selectionChanged(QCPLegend::SelectableParts parts); + void selectableChanged(QCPLegend::SelectableParts parts); + +protected: + // property members: + QPen mBorderPen, mIconBorderPen; + QBrush mBrush; + QFont mFont; + QColor mTextColor; + QSize mIconSize; + int mIconTextPadding; + SelectableParts mSelectedParts, mSelectableParts; + QPen mSelectedBorderPen, mSelectedIconBorderPen; + QBrush mSelectedBrush; + QFont mSelectedFont; + QColor mSelectedTextColor; + + // reimplemented virtual methods: + virtual void parentPlotInitialized(QCustomPlot *parentPlot); + virtual QCP::Interaction selectionCategory() const; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual void draw(QCPPainter *painter); + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + + // non-virtual methods: + QPen getBorderPen() const; + QBrush getBrush() const; + +private: + Q_DISABLE_COPY(QCPLegend) + + friend class QCustomPlot; + friend class QCPAbstractLegendItem; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts) +Q_DECLARE_METATYPE(QCPLegend::SelectablePart) + + +class QCP_LIB_DECL QCPPlotTitle : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged) + /// \endcond +public: + explicit QCPPlotTitle(QCustomPlot *parentPlot); + explicit QCPPlotTitle(QCustomPlot *parentPlot, const QString &text); + + // getters: + QString text() const { return mText; } + QFont font() const { return mFont; } + QColor textColor() const { return mTextColor; } + QFont selectedFont() const { return mSelectedFont; } + QColor selectedTextColor() const { return mSelectedTextColor; } + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setText(const QString &text); + void setFont(const QFont &font); + void setTextColor(const QColor &color); + void setSelectedFont(const QFont &font); + void setSelectedTextColor(const QColor &color); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + +protected: + // property members: + QString mText; + QFont mFont; + QColor mTextColor; + QFont mSelectedFont; + QColor mSelectedTextColor; + QRect mTextBoundingRect; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + virtual void draw(QCPPainter *painter); + virtual QSize minimumSizeHint() const; + virtual QSize maximumSizeHint() const; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + + // non-virtual methods: + QFont mainFont() const; + QColor mainTextColor() const; + +private: + Q_DISABLE_COPY(QCPPlotTitle) +}; + + +class QCPColorScaleAxisRectPrivate : public QCPAxisRect +{ + Q_OBJECT +public: + explicit QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale); +protected: + QCPColorScale *mParentColorScale; + QImage mGradientImage; + bool mGradientImageInvalidated; + // re-using some methods of QCPAxisRect to make them available to friend class QCPColorScale + using QCPAxisRect::calculateAutoMargin; + using QCPAxisRect::mousePressEvent; + using QCPAxisRect::mouseMoveEvent; + using QCPAxisRect::mouseReleaseEvent; + using QCPAxisRect::wheelEvent; + using QCPAxisRect::update; + virtual void draw(QCPPainter *painter); + void updateGradientImage(); + Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts); + Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts); + friend class QCPColorScale; +}; + + +class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPAxis::AxisType type READ type WRITE setType) + Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged) + Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged) + Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged) + Q_PROPERTY(QString label READ label WRITE setLabel) + Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth) + Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag) + Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom) + /// \endcond +public: + explicit QCPColorScale(QCustomPlot *parentPlot); + virtual ~QCPColorScale(); + + // getters: + QCPAxis *axis() const { return mColorAxis.data(); } + QCPAxis::AxisType type() const { return mType; } + QCPRange dataRange() const { return mDataRange; } + QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; } + QCPColorGradient gradient() const { return mGradient; } + QString label() const; + int barWidth () const { return mBarWidth; } + bool rangeDrag() const; + bool rangeZoom() const; + + // setters: + void setType(QCPAxis::AxisType type); + Q_SLOT void setDataRange(const QCPRange &dataRange); + Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType); + Q_SLOT void setGradient(const QCPColorGradient &gradient); + void setLabel(const QString &str); + void setBarWidth(int width); + void setRangeDrag(bool enabled); + void setRangeZoom(bool enabled); + + // non-property methods: + QList colorMaps() const; + void rescaleDataRange(bool onlyVisibleMaps); + + // reimplemented virtual methods: + virtual void update(UpdatePhase phase); + +signals: + void dataRangeChanged(QCPRange newRange); + void dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + void gradientChanged(QCPColorGradient newGradient); + +protected: + // property members: + QCPAxis::AxisType mType; + QCPRange mDataRange; + QCPAxis::ScaleType mDataScaleType; + QCPColorGradient mGradient; + int mBarWidth; + + // non-property members: + QPointer mAxisRect; + QPointer mColorAxis; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; + // events: + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent *event); + +private: + Q_DISABLE_COPY(QCPColorScale) + + friend class QCPColorScaleAxisRectPrivate; +}; + + +/*! \file */ + + + +class QCP_LIB_DECL QCPData +{ +public: + QCPData(); + QCPData(double key, double value); + double key, value; + double keyErrorPlus, keyErrorMinus; + double valueErrorPlus, valueErrorMinus; +}; +Q_DECLARE_TYPEINFO(QCPData, Q_MOVABLE_TYPE); + +/*! \typedef QCPDataMap + Container for storing \ref QCPData items in a sorted fashion. The key of the map + is the key member of the QCPData instance. + + This is the container in which QCPGraph holds its data. + \see QCPData, QCPGraph::setData +*/ +typedef QMap QCPDataMap; +typedef QMapIterator QCPDataMapIterator; +typedef QMutableMapIterator QCPDataMutableMapIterator; + + +class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle) + Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle) + Q_PROPERTY(ErrorType errorType READ errorType WRITE setErrorType) + Q_PROPERTY(QPen errorPen READ errorPen WRITE setErrorPen) + Q_PROPERTY(double errorBarSize READ errorBarSize WRITE setErrorBarSize) + Q_PROPERTY(bool errorBarSkipSymbol READ errorBarSkipSymbol WRITE setErrorBarSkipSymbol) + Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph) + Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling) + /// \endcond +public: + /*! + Defines how the graph's line is represented visually in the plot. The line is drawn with the + current pen of the graph (\ref setPen). + \see setLineStyle + */ + enum LineStyle { lsNone ///< data points are not connected with any lines (e.g. data only represented + ///< with symbols according to the scatter style, see \ref setScatterStyle) + ,lsLine ///< data points are connected by a straight line + ,lsStepLeft ///< line is drawn as steps where the step height is the value of the left data point + ,lsStepRight ///< line is drawn as steps where the step height is the value of the right data point + ,lsStepCenter ///< line is drawn as steps where the step is in between two data points + ,lsImpulse ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line + }; + Q_ENUMS(LineStyle) + /*! + Defines what kind of error bars are drawn for each data point + */ + enum ErrorType { etNone ///< No error bars are shown + ,etKey ///< Error bars for the key dimension of the data point are shown + ,etValue ///< Error bars for the value dimension of the data point are shown + ,etBoth ///< Error bars for both key and value dimensions of the data point are shown + }; + Q_ENUMS(ErrorType) + + explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPGraph(); + + // getters: + QCPDataMap *data() const { return mData; } + LineStyle lineStyle() const { return mLineStyle; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + ErrorType errorType() const { return mErrorType; } + QPen errorPen() const { return mErrorPen; } + double errorBarSize() const { return mErrorBarSize; } + bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; } + QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); } + bool adaptiveSampling() const { return mAdaptiveSampling; } + + // setters: + void setData(QCPDataMap *data, bool copy=false); + void setData(const QVector &key, const QVector &value); + void setDataKeyError(const QVector &key, const QVector &value, const QVector &keyError); + void setDataKeyError(const QVector &key, const QVector &value, const QVector &keyErrorMinus, const QVector &keyErrorPlus); + void setDataValueError(const QVector &key, const QVector &value, const QVector &valueError); + void setDataValueError(const QVector &key, const QVector &value, const QVector &valueErrorMinus, const QVector &valueErrorPlus); + void setDataBothError(const QVector &key, const QVector &value, const QVector &keyError, const QVector &valueError); + void setDataBothError(const QVector &key, const QVector &value, const QVector &keyErrorMinus, const QVector &keyErrorPlus, const QVector &valueErrorMinus, const QVector &valueErrorPlus); + void setLineStyle(LineStyle ls); + void setScatterStyle(const QCPScatterStyle &style); + void setErrorType(ErrorType errorType); + void setErrorPen(const QPen &pen); + void setErrorBarSize(double size); + void setErrorBarSkipSymbol(bool enabled); + void setChannelFillGraph(QCPGraph *targetGraph); + void setAdaptiveSampling(bool enabled); + + // non-property methods: + void addData(const QCPDataMap &dataMap); + void addData(const QCPData &data); + void addData(double key, double value); + void addData(const QVector &keys, const QVector &values); + void removeDataBefore(double key); + void removeDataAfter(double key); + void removeData(double fromKey, double toKey); + void removeData(double key); + + // reimplemented virtual methods: + virtual void clearData(); + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + using QCPAbstractPlottable::rescaleAxes; + using QCPAbstractPlottable::rescaleKeyAxis; + using QCPAbstractPlottable::rescaleValueAxis; + void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface + void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface + void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface + +protected: + // property members: + QCPDataMap *mData; + QPen mErrorPen; + LineStyle mLineStyle; + QCPScatterStyle mScatterStyle; + ErrorType mErrorType; + double mErrorBarSize; + bool mErrorBarSkipSymbol; + QPointer mChannelFillGraph; + bool mAdaptiveSampling; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface + + // introduced virtual methods: + virtual void drawFill(QCPPainter *painter, QVector *lineData) const; + virtual void drawScatterPlot(QCPPainter *painter, QVector *scatterData) const; + virtual void drawLinePlot(QCPPainter *painter, QVector *lineData) const; + virtual void drawImpulsePlot(QCPPainter *painter, QVector *lineData) const; + + // non-virtual methods: + void getPreparedData(QVector *lineData, QVector *scatterData) const; + void getPlotData(QVector *lineData, QVector *scatterData) const; + void getScatterPlotData(QVector *scatterData) const; + void getLinePlotData(QVector *linePixelData, QVector *scatterData) const; + void getStepLeftPlotData(QVector *linePixelData, QVector *scatterData) const; + void getStepRightPlotData(QVector *linePixelData, QVector *scatterData) const; + void getStepCenterPlotData(QVector *linePixelData, QVector *scatterData) const; + void getImpulsePlotData(QVector *linePixelData, QVector *scatterData) const; + void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const; + void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const; + int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const; + void addFillBasePoints(QVector *lineData) const; + void removeFillBasePoints(QVector *lineData) const; + QPointF lowerFillBasePoint(double lowerKey) const; + QPointF upperFillBasePoint(double upperKey) const; + const QPolygonF getChannelFillPolygon(const QVector *lineData) const; + int findIndexBelowX(const QVector *data, double x) const; + int findIndexAboveX(const QVector *data, double x) const; + int findIndexBelowY(const QVector *data, double y) const; + int findIndexAboveY(const QVector *data, double y) const; + double pointDistance(const QPointF &pixelPoint) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + + +/*! \file */ + + + +class QCP_LIB_DECL QCPCurveData +{ +public: + QCPCurveData(); + QCPCurveData(double t, double key, double value); + double t, key, value; +}; +Q_DECLARE_TYPEINFO(QCPCurveData, Q_MOVABLE_TYPE); + +/*! \typedef QCPCurveDataMap + Container for storing \ref QCPCurveData items in a sorted fashion. The key of the map + is the t member of the QCPCurveData instance. + + This is the container in which QCPCurve holds its data. + \see QCPCurveData, QCPCurve::setData +*/ + +typedef QMap QCPCurveDataMap; +typedef QMapIterator QCPCurveDataMapIterator; +typedef QMutableMapIterator QCPCurveDataMutableMapIterator; + + +class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle) + Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle) + /// \endcond +public: + /*! + Defines how the curve's line is represented visually in the plot. The line is drawn with the + current pen of the curve (\ref setPen). + \see setLineStyle + */ + enum LineStyle { lsNone ///< No line is drawn between data points (e.g. only scatters) + ,lsLine ///< Data points are connected with a straight line + }; + explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPCurve(); + + // getters: + QCPCurveDataMap *data() const { return mData; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + LineStyle lineStyle() const { return mLineStyle; } + + // setters: + void setData(QCPCurveDataMap *data, bool copy=false); + void setData(const QVector &t, const QVector &key, const QVector &value); + void setData(const QVector &key, const QVector &value); + void setScatterStyle(const QCPScatterStyle &style); + void setLineStyle(LineStyle style); + + // non-property methods: + void addData(const QCPCurveDataMap &dataMap); + void addData(const QCPCurveData &data); + void addData(double t, double key, double value); + void addData(double key, double value); + void addData(const QVector &ts, const QVector &keys, const QVector &values); + void removeDataBefore(double t); + void removeDataAfter(double t); + void removeData(double fromt, double tot); + void removeData(double t); + + // reimplemented virtual methods: + virtual void clearData(); + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +protected: + // property members: + QCPCurveDataMap *mData; + QCPScatterStyle mScatterStyle; + LineStyle mLineStyle; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + + // introduced virtual methods: + virtual void drawScatterPlot(QCPPainter *painter, const QVector *pointData) const; + + // non-virtual methods: + void getCurveData(QVector *lineData) const; + int getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const; + QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const; + QVector getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const; + bool mayTraverse(int prevRegion, int currentRegion) const; + bool getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const; + void getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector &beforeTraverse, QVector &afterTraverse) const; + double pointDistance(const QPointF &pixelPoint) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + + +/*! \file */ + + + +class QCP_LIB_DECL QCPBarsGroup : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(SpacingType spacingType READ spacingType WRITE setSpacingType) + Q_PROPERTY(double spacing READ spacing WRITE setSpacing) + /// \endcond +public: + /*! + Defines the ways the spacing between bars in the group can be specified. Thus it defines what + the number passed to \ref setSpacing actually means. + + \see setSpacingType, setSpacing + */ + enum SpacingType { stAbsolute ///< Bar spacing is in absolute pixels + ,stAxisRectRatio ///< Bar spacing is given by a fraction of the axis rect size + ,stPlotCoords ///< Bar spacing is in key coordinates and thus scales with the key axis range + }; + QCPBarsGroup(QCustomPlot *parentPlot); + ~QCPBarsGroup(); + + // getters: + SpacingType spacingType() const { return mSpacingType; } + double spacing() const { return mSpacing; } + + // setters: + void setSpacingType(SpacingType spacingType); + void setSpacing(double spacing); + + // non-virtual methods: + QList bars() const { return mBars; } + QCPBars* bars(int index) const; + int size() const { return mBars.size(); } + bool isEmpty() const { return mBars.isEmpty(); } + void clear(); + bool contains(QCPBars *bars) const { return mBars.contains(bars); } + void append(QCPBars *bars); + void insert(int i, QCPBars *bars); + void remove(QCPBars *bars); + +protected: + // non-property members: + QCustomPlot *mParentPlot; + SpacingType mSpacingType; + double mSpacing; + QList mBars; + + // non-virtual methods: + void registerBars(QCPBars *bars); + void unregisterBars(QCPBars *bars); + + // virtual methods: + double keyPixelOffset(const QCPBars *bars, double keyCoord); + double getPixelSpacing(const QCPBars *bars, double keyCoord); + +private: + Q_DISABLE_COPY(QCPBarsGroup) + + friend class QCPBars; +}; + + +class QCP_LIB_DECL QCPBarData +{ +public: + QCPBarData(); + QCPBarData(double key, double value); + double key, value; +}; +Q_DECLARE_TYPEINFO(QCPBarData, Q_MOVABLE_TYPE); + +/*! \typedef QCPBarDataMap + Container for storing \ref QCPBarData items in a sorted fashion. The key of the map + is the key member of the QCPBarData instance. + + This is the container in which QCPBars holds its data. + \see QCPBarData, QCPBars::setData +*/ +typedef QMap QCPBarDataMap; +typedef QMapIterator QCPBarDataMapIterator; +typedef QMutableMapIterator QCPBarDataMutableMapIterator; + + +class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(double width READ width WRITE setWidth) + Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType) + Q_PROPERTY(QCPBarsGroup* barsGroup READ barsGroup WRITE setBarsGroup) + Q_PROPERTY(double baseValue READ baseValue WRITE setBaseValue) + Q_PROPERTY(QCPBars* barBelow READ barBelow) + Q_PROPERTY(QCPBars* barAbove READ barAbove) + /// \endcond +public: + /*! + Defines the ways the width of the bar can be specified. Thus it defines what the number passed + to \ref setWidth actually means. + + \see setWidthType, setWidth + */ + enum WidthType { wtAbsolute ///< Bar width is in absolute pixels + ,wtAxisRectRatio ///< Bar width is given by a fraction of the axis rect size + ,wtPlotCoords ///< Bar width is in key coordinates and thus scales with the key axis range + }; + Q_ENUMS(WidthType) + + explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPBars(); + + // getters: + double width() const { return mWidth; } + WidthType widthType() const { return mWidthType; } + QCPBarsGroup *barsGroup() const { return mBarsGroup; } + double baseValue() const { return mBaseValue; } + QCPBars *barBelow() const { return mBarBelow.data(); } + QCPBars *barAbove() const { return mBarAbove.data(); } + QCPBarDataMap *data() const { return mData; } + + // setters: + void setWidth(double width); + void setWidthType(WidthType widthType); + void setBarsGroup(QCPBarsGroup *barsGroup); + void setBaseValue(double baseValue); + void setData(QCPBarDataMap *data, bool copy=false); + void setData(const QVector &key, const QVector &value); + + // non-property methods: + void moveBelow(QCPBars *bars); + void moveAbove(QCPBars *bars); + void addData(const QCPBarDataMap &dataMap); + void addData(const QCPBarData &data); + void addData(double key, double value); + void addData(const QVector &keys, const QVector &values); + void removeDataBefore(double key); + void removeDataAfter(double key); + void removeData(double fromKey, double toKey); + void removeData(double key); + + // reimplemented virtual methods: + virtual void clearData(); + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +protected: + // property members: + QCPBarDataMap *mData; + double mWidth; + WidthType mWidthType; + QCPBarsGroup *mBarsGroup; + double mBaseValue; + QPointer mBarBelow, mBarAbove; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + + // non-virtual methods: + void getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const; + QPolygonF getBarPolygon(double key, double value) const; + void getPixelWidth(double key, double &lower, double &upper) const; + double getStackedBaseValue(double key, bool positive) const; + static void connectBars(QCPBars* lower, QCPBars* upper); + + friend class QCustomPlot; + friend class QCPLegend; + friend class QCPBarsGroup; +}; + + +/*! \file */ + + + +class QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(double key READ key WRITE setKey) + Q_PROPERTY(double minimum READ minimum WRITE setMinimum) + Q_PROPERTY(double lowerQuartile READ lowerQuartile WRITE setLowerQuartile) + Q_PROPERTY(double median READ median WRITE setMedian) + Q_PROPERTY(double upperQuartile READ upperQuartile WRITE setUpperQuartile) + Q_PROPERTY(double maximum READ maximum WRITE setMaximum) + Q_PROPERTY(QVector outliers READ outliers WRITE setOutliers) + Q_PROPERTY(double width READ width WRITE setWidth) + Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth) + Q_PROPERTY(QPen whiskerPen READ whiskerPen WRITE setWhiskerPen) + Q_PROPERTY(QPen whiskerBarPen READ whiskerBarPen WRITE setWhiskerBarPen) + Q_PROPERTY(QPen medianPen READ medianPen WRITE setMedianPen) + Q_PROPERTY(QCPScatterStyle outlierStyle READ outlierStyle WRITE setOutlierStyle) + /// \endcond +public: + explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis); + + // getters: + double key() const { return mKey; } + double minimum() const { return mMinimum; } + double lowerQuartile() const { return mLowerQuartile; } + double median() const { return mMedian; } + double upperQuartile() const { return mUpperQuartile; } + double maximum() const { return mMaximum; } + QVector outliers() const { return mOutliers; } + double width() const { return mWidth; } + double whiskerWidth() const { return mWhiskerWidth; } + QPen whiskerPen() const { return mWhiskerPen; } + QPen whiskerBarPen() const { return mWhiskerBarPen; } + QPen medianPen() const { return mMedianPen; } + QCPScatterStyle outlierStyle() const { return mOutlierStyle; } + + // setters: + void setKey(double key); + void setMinimum(double value); + void setLowerQuartile(double value); + void setMedian(double value); + void setUpperQuartile(double value); + void setMaximum(double value); + void setOutliers(const QVector &values); + void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum); + void setWidth(double width); + void setWhiskerWidth(double width); + void setWhiskerPen(const QPen &pen); + void setWhiskerBarPen(const QPen &pen); + void setMedianPen(const QPen &pen); + void setOutlierStyle(const QCPScatterStyle &style); + + // non-property methods: + virtual void clearData(); + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +protected: + // property members: + QVector mOutliers; + double mKey, mMinimum, mLowerQuartile, mMedian, mUpperQuartile, mMaximum; + double mWidth; + double mWhiskerWidth; + QPen mWhiskerPen, mWhiskerBarPen, mMedianPen; + QCPScatterStyle mOutlierStyle; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + + // introduced virtual methods: + virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const; + virtual void drawMedian(QCPPainter *painter) const; + virtual void drawWhiskers(QCPPainter *painter) const; + virtual void drawOutliers(QCPPainter *painter) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + + +class QCP_LIB_DECL QCPColorMapData +{ +public: + QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange); + ~QCPColorMapData(); + QCPColorMapData(const QCPColorMapData &other); + QCPColorMapData &operator=(const QCPColorMapData &other); + + // getters: + int keySize() const { return mKeySize; } + int valueSize() const { return mValueSize; } + QCPRange keyRange() const { return mKeyRange; } + QCPRange valueRange() const { return mValueRange; } + QCPRange dataBounds() const { return mDataBounds; } + double data(double key, double value); + double cell(int keyIndex, int valueIndex); + + // setters: + void setSize(int keySize, int valueSize); + void setKeySize(int keySize); + void setValueSize(int valueSize); + void setRange(const QCPRange &keyRange, const QCPRange &valueRange); + void setKeyRange(const QCPRange &keyRange); + void setValueRange(const QCPRange &valueRange); + void setData(double key, double value, double z); + void setCell(int keyIndex, int valueIndex, double z); + + // non-property methods: + void recalculateDataBounds(); + void clear(); + void fill(double z); + bool isEmpty() const { return mIsEmpty; } + void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const; + void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const; + +protected: + // property members: + int mKeySize, mValueSize; + QCPRange mKeyRange, mValueRange; + bool mIsEmpty; + // non-property members: + double *mData; + QCPRange mDataBounds; + bool mDataModified; + + friend class QCPColorMap; +}; + + +class QCP_LIB_DECL QCPColorMap : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged) + Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged) + Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged) + Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate) + Q_PROPERTY(bool tightBoundary READ tightBoundary WRITE setTightBoundary) + Q_PROPERTY(QCPColorScale* colorScale READ colorScale WRITE setColorScale) + /// \endcond +public: + explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPColorMap(); + + // getters: + QCPColorMapData *data() const { return mMapData; } + QCPRange dataRange() const { return mDataRange; } + QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; } + bool interpolate() const { return mInterpolate; } + bool tightBoundary() const { return mTightBoundary; } + QCPColorGradient gradient() const { return mGradient; } + QCPColorScale *colorScale() const { return mColorScale.data(); } + + // setters: + void setData(QCPColorMapData *data, bool copy=false); + Q_SLOT void setDataRange(const QCPRange &dataRange); + Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType); + Q_SLOT void setGradient(const QCPColorGradient &gradient); + void setInterpolate(bool enabled); + void setTightBoundary(bool enabled); + void setColorScale(QCPColorScale *colorScale); + + // non-property methods: + void rescaleDataRange(bool recalculateDataBounds=false); + Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18)); + + // reimplemented virtual methods: + virtual void clearData(); + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + +signals: + void dataRangeChanged(QCPRange newRange); + void dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + void gradientChanged(QCPColorGradient newGradient); + +protected: + // property members: + QCPRange mDataRange; + QCPAxis::ScaleType mDataScaleType; + QCPColorMapData *mMapData; + QCPColorGradient mGradient; + bool mInterpolate; + bool mTightBoundary; + QPointer mColorScale; + // non-property members: + QImage mMapImage, mUndersampledMapImage; + QPixmap mLegendIcon; + bool mMapImageInvalidated; + + // introduced virtual methods: + virtual void updateMapImage(); + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + + +/*! \file */ + + + +class QCP_LIB_DECL QCPFinancialData +{ +public: + QCPFinancialData(); + QCPFinancialData(double key, double open, double high, double low, double close); + double key, open, high, low, close; +}; +Q_DECLARE_TYPEINFO(QCPFinancialData, Q_MOVABLE_TYPE); + +/*! \typedef QCPFinancialDataMap + Container for storing \ref QCPFinancialData items in a sorted fashion. The key of the map + is the key member of the QCPFinancialData instance. + + This is the container in which QCPFinancial holds its data. + \see QCPFinancial, QCPFinancial::setData +*/ +typedef QMap QCPFinancialDataMap; +typedef QMapIterator QCPFinancialDataMapIterator; +typedef QMutableMapIterator QCPFinancialDataMutableMapIterator; + + +class QCP_LIB_DECL QCPFinancial : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(ChartStyle chartStyle READ chartStyle WRITE setChartStyle) + Q_PROPERTY(double width READ width WRITE setWidth) + Q_PROPERTY(bool twoColored READ twoColored WRITE setTwoColored) + Q_PROPERTY(QBrush brushPositive READ brushPositive WRITE setBrushPositive) + Q_PROPERTY(QBrush brushNegative READ brushNegative WRITE setBrushNegative) + Q_PROPERTY(QPen penPositive READ penPositive WRITE setPenPositive) + Q_PROPERTY(QPen penNegative READ penNegative WRITE setPenNegative) + /// \endcond +public: + /*! + Defines the possible representations of OHLC data in the plot. + + \see setChartStyle + */ + enum ChartStyle { csOhlc ///< Open-High-Low-Close bar representation + ,csCandlestick ///< Candlestick representation + }; + Q_ENUMS(ChartStyle) + + explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPFinancial(); + + // getters: + QCPFinancialDataMap *data() const { return mData; } + ChartStyle chartStyle() const { return mChartStyle; } + double width() const { return mWidth; } + bool twoColored() const { return mTwoColored; } + QBrush brushPositive() const { return mBrushPositive; } + QBrush brushNegative() const { return mBrushNegative; } + QPen penPositive() const { return mPenPositive; } + QPen penNegative() const { return mPenNegative; } + + + // setters: + void setData(QCPFinancialDataMap *data, bool copy=false); + void setData(const QVector &key, const QVector &open, const QVector &high, const QVector &low, const QVector &close); + void setChartStyle(ChartStyle style); + void setWidth(double width); + void setTwoColored(bool twoColored); + void setBrushPositive(const QBrush &brush); + void setBrushNegative(const QBrush &brush); + void setPenPositive(const QPen &pen); + void setPenNegative(const QPen &pen); + + // non-property methods: + void addData(const QCPFinancialDataMap &dataMap); + void addData(const QCPFinancialData &data); + void addData(double key, double open, double high, double low, double close); + void addData(const QVector &key, const QVector &open, const QVector &high, const QVector &low, const QVector &close); + void removeDataBefore(double key); + void removeDataAfter(double key); + void removeData(double fromKey, double toKey); + void removeData(double key); + + // reimplemented virtual methods: + virtual void clearData(); + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + // static methods: + static QCPFinancialDataMap timeSeriesToOhlc(const QVector &time, const QVector &value, double timeBinSize, double timeBinOffset = 0); + +protected: + // property members: + QCPFinancialDataMap *mData; + ChartStyle mChartStyle; + double mWidth; + bool mTwoColored; + QBrush mBrushPositive, mBrushNegative; + QPen mPenPositive, mPenNegative; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; + + // non-virtual methods: + void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end); + void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end); + double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const; + double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const; + void getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + + +class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + /// \endcond +public: + QCPItemStraightLine(QCustomPlot *parentPlot); + virtual ~QCPItemStraightLine(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const point1; + QCPItemPosition * const point2; + +protected: + // property members: + QPen mPen, mSelectedPen; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + + // non-virtual methods: + double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const; + QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const; + QPen mainPen() const; +}; + + +class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QCPLineEnding head READ head WRITE setHead) + Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail) + /// \endcond +public: + QCPItemLine(QCustomPlot *parentPlot); + virtual ~QCPItemLine(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QCPLineEnding head() const { return mHead; } + QCPLineEnding tail() const { return mTail; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setHead(const QCPLineEnding &head); + void setTail(const QCPLineEnding &tail); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const start; + QCPItemPosition * const end; + +protected: + // property members: + QPen mPen, mSelectedPen; + QCPLineEnding mHead, mTail; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + + // non-virtual methods: + QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const; + QPen mainPen() const; +}; + + +class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QCPLineEnding head READ head WRITE setHead) + Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail) + /// \endcond +public: + QCPItemCurve(QCustomPlot *parentPlot); + virtual ~QCPItemCurve(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QCPLineEnding head() const { return mHead; } + QCPLineEnding tail() const { return mTail; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setHead(const QCPLineEnding &head); + void setTail(const QCPLineEnding &tail); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const start; + QCPItemPosition * const startDir; + QCPItemPosition * const endDir; + QCPItemPosition * const end; + +protected: + // property members: + QPen mPen, mSelectedPen; + QCPLineEnding mHead, mTail; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + + // non-virtual methods: + QPen mainPen() const; +}; + + +class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + /// \endcond +public: + QCPItemRect(QCustomPlot *parentPlot); + virtual ~QCPItemRect(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const topLeft; + QCPItemPosition * const bottomRight; + QCPItemAnchor * const top; + QCPItemAnchor * const topRight; + QCPItemAnchor * const right; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeft; + QCPItemAnchor * const left; + +protected: + enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft}; + + // property members: + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual QPointF anchorPixelPoint(int anchorId) const; + + // non-virtual methods: + QPen mainPen() const; + QBrush mainBrush() const; +}; + + +class QCP_LIB_DECL QCPItemText : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(Qt::Alignment positionAlignment READ positionAlignment WRITE setPositionAlignment) + Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment) + Q_PROPERTY(double rotation READ rotation WRITE setRotation) + Q_PROPERTY(QMargins padding READ padding WRITE setPadding) + /// \endcond +public: + QCPItemText(QCustomPlot *parentPlot); + virtual ~QCPItemText(); + + // getters: + QColor color() const { return mColor; } + QColor selectedColor() const { return mSelectedColor; } + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + QFont font() const { return mFont; } + QFont selectedFont() const { return mSelectedFont; } + QString text() const { return mText; } + Qt::Alignment positionAlignment() const { return mPositionAlignment; } + Qt::Alignment textAlignment() const { return mTextAlignment; } + double rotation() const { return mRotation; } + QMargins padding() const { return mPadding; } + + // setters; + void setColor(const QColor &color); + void setSelectedColor(const QColor &color); + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + void setFont(const QFont &font); + void setSelectedFont(const QFont &font); + void setText(const QString &text); + void setPositionAlignment(Qt::Alignment alignment); + void setTextAlignment(Qt::Alignment alignment); + void setRotation(double degrees); + void setPadding(const QMargins &padding); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const position; + QCPItemAnchor * const topLeft; + QCPItemAnchor * const top; + QCPItemAnchor * const topRight; + QCPItemAnchor * const right; + QCPItemAnchor * const bottomRight; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeft; + QCPItemAnchor * const left; + +protected: + enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft}; + + // property members: + QColor mColor, mSelectedColor; + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + QFont mFont, mSelectedFont; + QString mText; + Qt::Alignment mPositionAlignment; + Qt::Alignment mTextAlignment; + double mRotation; + QMargins mPadding; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual QPointF anchorPixelPoint(int anchorId) const; + + // non-virtual methods: + QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const; + QFont mainFont() const; + QColor mainColor() const; + QPen mainPen() const; + QBrush mainBrush() const; +}; + + +class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + /// \endcond +public: + QCPItemEllipse(QCustomPlot *parentPlot); + virtual ~QCPItemEllipse(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const topLeft; + QCPItemPosition * const bottomRight; + QCPItemAnchor * const topLeftRim; + QCPItemAnchor * const top; + QCPItemAnchor * const topRightRim; + QCPItemAnchor * const right; + QCPItemAnchor * const bottomRightRim; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeftRim; + QCPItemAnchor * const left; + QCPItemAnchor * const center; + +protected: + enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft, aiCenter}; + + // property members: + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual QPointF anchorPixelPoint(int anchorId) const; + + // non-virtual methods: + QPen mainPen() const; + QBrush mainBrush() const; +}; + + +class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) + Q_PROPERTY(bool scaled READ scaled WRITE setScaled) + Q_PROPERTY(Qt::AspectRatioMode aspectRatioMode READ aspectRatioMode) + Q_PROPERTY(Qt::TransformationMode transformationMode READ transformationMode) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + /// \endcond +public: + QCPItemPixmap(QCustomPlot *parentPlot); + virtual ~QCPItemPixmap(); + + // getters: + QPixmap pixmap() const { return mPixmap; } + bool scaled() const { return mScaled; } + Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; } + Qt::TransformationMode transformationMode() const { return mTransformationMode; } + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + + // setters; + void setPixmap(const QPixmap &pixmap); + void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation); + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const topLeft; + QCPItemPosition * const bottomRight; + QCPItemAnchor * const top; + QCPItemAnchor * const topRight; + QCPItemAnchor * const right; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeft; + QCPItemAnchor * const left; + +protected: + enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft}; + + // property members: + QPixmap mPixmap; + QPixmap mScaledPixmap; + bool mScaled; + bool mScaledPixmapInvalidated; + Qt::AspectRatioMode mAspectRatioMode; + Qt::TransformationMode mTransformationMode; + QPen mPen, mSelectedPen; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual QPointF anchorPixelPoint(int anchorId) const; + + // non-virtual methods: + void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false); + QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const; + QPen mainPen() const; +}; + + +class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(double size READ size WRITE setSize) + Q_PROPERTY(TracerStyle style READ style WRITE setStyle) + Q_PROPERTY(QCPGraph* graph READ graph WRITE setGraph) + Q_PROPERTY(double graphKey READ graphKey WRITE setGraphKey) + Q_PROPERTY(bool interpolating READ interpolating WRITE setInterpolating) + /// \endcond +public: + /*! + The different visual appearances a tracer item can have. Some styles size may be controlled with \ref setSize. + + \see setStyle + */ + enum TracerStyle { tsNone ///< The tracer is not visible + ,tsPlus ///< A plus shaped crosshair with limited size + ,tsCrosshair ///< A plus shaped crosshair which spans the complete axis rect + ,tsCircle ///< A circle + ,tsSquare ///< A square + }; + Q_ENUMS(TracerStyle) + + QCPItemTracer(QCustomPlot *parentPlot); + virtual ~QCPItemTracer(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + double size() const { return mSize; } + TracerStyle style() const { return mStyle; } + QCPGraph *graph() const { return mGraph; } + double graphKey() const { return mGraphKey; } + bool interpolating() const { return mInterpolating; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + void setSize(double size); + void setStyle(TracerStyle style); + void setGraph(QCPGraph *graph); + void setGraphKey(double key); + void setInterpolating(bool enabled); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + // non-virtual methods: + void updatePosition(); + + QCPItemPosition * const position; + +protected: + // property members: + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + double mSize; + TracerStyle mStyle; + QCPGraph *mGraph; + double mGraphKey; + bool mInterpolating; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + + // non-virtual methods: + QPen mainPen() const; + QBrush mainBrush() const; +}; + + +class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(double length READ length WRITE setLength) + Q_PROPERTY(BracketStyle style READ style WRITE setStyle) + /// \endcond +public: + enum BracketStyle { bsSquare ///< A brace with angled edges + ,bsRound ///< A brace with round edges + ,bsCurly ///< A curly brace + ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression + }; + + QCPItemBracket(QCustomPlot *parentPlot); + virtual ~QCPItemBracket(); + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + double length() const { return mLength; } + BracketStyle style() const { return mStyle; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setLength(double length); + void setStyle(BracketStyle style); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + + QCPItemPosition * const left; + QCPItemPosition * const right; + QCPItemAnchor * const center; + +protected: + // property members: + enum AnchorIndex {aiCenter}; + QPen mPen, mSelectedPen; + double mLength; + BracketStyle mStyle; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter); + virtual QPointF anchorPixelPoint(int anchorId) const; + + // non-virtual methods: + QPen mainPen() const; +}; + +#endif // QCUSTOMPLOT_H + diff --git a/Analysis/DataViewer/qml.qrc b/Analysis/DataViewer/qml.qrc new file mode 100644 index 00000000..7684346a --- /dev/null +++ b/Analysis/DataViewer/qml.qrc @@ -0,0 +1,6 @@ + + + main.qml + MainForm.ui.qml + + diff --git a/Analysis/Image/AdvCompr.cpp b/Analysis/Image/AdvCompr.cpp index a5a473b2..22a0bea3 100755 --- a/Analysis/Image/AdvCompr.cpp +++ b/Analysis/Image/AdvCompr.cpp @@ -4,13 +4,14 @@ #include "AdvCompr.h" #ifndef WIN32 #include "PCACompression.h" +#ifdef PCASPLINE #include "PcaSpline.h" +#endif #include "Utils.h" #endif //#define SMOOTH_BEGINNING_OF_TRACE 1 //#define POST_CLEAR_FRONT_PORCH 1 -//#define PCA_MEAN_SMOOTHING 3 //#define PCA_CONVERGE_BASIS_VECTORS 1 //#define SCALE_TRACES 1 @@ -23,16 +24,22 @@ //#define USE_LESS_BASIS_VECTS 3 //#define OUTPUT_VECTOR_COEFFS 1 //#define CLEAR_MEAN 1 -//#define CENTER_TRACES 1 -#define XTALK_FRAC (0.20f) -#define XTALK_FRAC_MODE73 (0.30f) -#define XTALK_FRAC_MODE76 (0.25f) +#ifndef RDR_MAX_REGIONS +#define RDR_MAX_REGIONS 96 +#endif + + +#define XTALK_FRAC (0.30f) #define XTALK_FRAC_EARLY 1 #define GAIN_CORR_IN_ADVTEST 1 +#define NOT_PINNED(idx) (mMask == NULL || (mMask[(idx)])) +#define V8_SUM(a) (a.A[0]+a.A[1]+a.A[2]+a.A[3]+a.A[4]+a.A[5]+a.A[6]+a.A[7]) +#define SIGS_ACC(idx,frame) (mCorr_sigs[((idx)*(npts) + (frame))]) +#define NOISE_ACC(idx,frame) (mCorr_noise[((idx)*(npts) + (frame))]) // limits on the gain term we allow @@ -56,13 +63,13 @@ uint32_t AdvCompr::threadMemLen[ADVC_MAX_REGIONS] = {0}; AdvCompr::AdvCompr(FILE_HANDLE _fd, short int *_raw, int _w, int _h, int _nImgPts, int _nUncompImgPts, int *_timestamps_compr, int *_timestamps_uncompr, int _testUncompr, char *_fname, - char *_options, int _frameRate) + char *_options, int _frameRate, int RemoveRowNoise) { ThreadNum=-1; GblMemPtr=NULL; GblMemLen=0; - do_cnc = true; - do_rnc = true; + do_cnc = false;//true; +// do_rnc = true; w = _w; h = _h; npts = _nImgPts; @@ -78,11 +85,12 @@ AdvCompr::AdvCompr(FILE_HANDLE _fd, short int *_raw, int _w, int _h, nPcaSplineBasisVectors=0; nDfcCoeff=0; frameRate=_frameRate; + CorrectRowNoise = RemoveRowNoise; nBasisVectors=0; SplineStrategy[0]=0; - options=_options; + inp_options=_options; if (_fname) strncpy(fname, _fname, sizeof(fname)); @@ -114,61 +122,6 @@ AdvCompr::~AdvCompr() #ifndef WIN32 -void AdvCompr::ZeroTraceMean(short int *raw, int h, int w, int npts) -{ - int frameStride=w*h; - - // first, zero all the trace means - for(int y=0;y 1000){ - start_ts = ts; - } - if(end_ts == 0 && timestamps_compr[ts] > 3000){ - end_ts = ts; - } - } - - - for(int idx=0;idx0){ lsum[y*w+x] =lsum[(y-1)*w+x] + rptr[y*w+x]; lsumNum[y*w+x]=lsumNum[(y-1)*w+x] + 1; @@ -202,7 +155,7 @@ void AdvCompr::NeighborSubtract(short int *raw, int h, int w, int npts, float *s lsum[y*w+x]=0; } for(x=1;x0){ lsum[y*w+x]=lsum[(y-1)*w+x] + lsum[y*w+x-1] - lsum[(y-1)*w+x-1] + rptr[y*w+x]; lsumNum[y*w+x]=lsumNum[(y-1)*w+x] + lsumNum[y*w+x-1] - lsumNum[(y-1)*w+x-1] + 1; @@ -229,7 +182,7 @@ void AdvCompr::NeighborSubtract(short int *raw, int h, int w, int npts, float *s // now, neighbor subtract each well for(int y=0;y= 0) Write(fd, &FileHdrV4, sizeof(FileHdrV4)); @@ -354,44 +324,41 @@ int AdvCompr::Compress(int _threadNum, uint32_t region, int targetAvg) Write(fd, timestamps_newfr, sizeof(timestamps_newfr[0]) * npts_newfr); } + double start_xtc=AdvCTimer(); int total_iter = 0; -#ifdef SCALE_TRACES - ComputeGain_FullChip(); + +#ifdef XTALK_FRAC_EARLY + findPinned(); + if(CorrectRowNoise) + ApplyGain_FullChip_xtalkcorr_sumRows(XTALK_FRAC, w, h, npts, (short unsigned int *)raw); + else + ApplyGain_FullChip_xtalkcorr(XTALK_FRAC, w, h, npts, (short unsigned int *)raw); + +#else + ApplyGain_FullChip(0); #endif - ApplyGain_FullChip(/*do_rnc?0:*/targetAvg); + timing.xtalk += AdvCTimer() - start_xtc; + if(CorrectRowNoise){ + if(region >= RDR_MAX_REGIONS) + NNSubtractComparatorSigs_tn(100); + else + NNSubtractComparatorSigs(500); + } +#if 0 #ifndef PYEXT - if(do_rnc) { +// if(do_rnc) + { double start2 = AdvCTimer(); - CorrNoiseCorrector rnc; - rnc.CorrectCorrNoise(raw, h, w, npts, 3, (region >= 96), 0,0,ThreadNum,targetAvg); + rnc.CorrectCorrNoise(raw, h, w, npts, 1, (region >= 96), 0,0,ThreadNum,targetAvg); timing.rcn += AdvCTimer() - start2; - -#ifdef XTALK_FRAC_EARLY - { - float corrFrac=XTALK_FRAC; -#ifdef BB_DC - if(fpgaImage.ChipMode == 73) - corrFrac=XTALK_FRAC_MODE73; - if(fpgaImage.ChipMode == 76) - corrFrac=XTALK_FRAC_MODE76; + } #endif - xtalkCorrect_raw(corrFrac, w, h, npts, raw); - } #endif - } - else if (do_cnc) { - double start2 = AdvCTimer(); - ComparatorNoiseCorrector cnc; - cnc.CorrectComparatorNoise(raw, h, w, npts, NULL, 0, true, - false, ThreadNum); - timing.ccn += AdvCTimer() - start2; - memcpy(mMask,raw,w*h*sizeof(raw[0])); // use the first frame as a pinned mask - } -#endif + for (int rblk = 0; rblk < rblocks; rblk++) { @@ -443,64 +410,19 @@ int AdvCompr::Compress(int _threadNum, uint32_t region, int targetAvg) t0est = findt0(&t0estIdx); // printf("t0est=%d t0estIdx=%d\n",t0est,t0estIdx); -#ifdef SMOOTH_BEGINNING_OF_TRACE - smoothBeginning(); // locates the traces better. -#endif - - SetMeanOfFramesToZero_SubMean(npts); // subtracts mean as well -#ifdef CLEAR_BEGINNING_OF_TRACE -// float tmpMean[npts]; -// memcpy(tmpMean,mean_trc,sizeof(float)*npts); -// memset(mean_trc,0,sizeof(float)*npts); -// SetMeanOfFramesToZero_SubMean(npts); // subtracts mean as well -// memcpy(mean_trc,tmpMean,sizeof(float)*npts); - ClearBeginningOfTrace(); -#endif - - SmoothMean(); - -#ifdef CLEAR_MEAN - for(int pt=0;pt= 0) WriteTraceBlock(); @@ -519,51 +441,6 @@ int AdvCompr::Compress(int _threadNum, uint32_t region, int targetAvg) return total_iter; } -void AdvCompr::SmoothMean() -{ -#ifdef PCA_MEAN_SMOOTHING - - float nmean_trc[hdr.npts]; - for (int pt = 0; pt < hdr.npts; pt++){ - int span=PCA_MEAN_SMOOTHING; - int start_pt=pt-span; - int end_pt=pt+span; - if(start_pt<0) - start_pt=0; - if(end_pt>hdr.npts) - end_pt=hdr.npts; - - float avg=0; - for(int npt=start_pt;npt 15) - end = 15; -// v8s zeroV=LD_VEC8S(0); -// int frameStride=w*h; - - // mean has already been subtracted, so just zero the points up to t0est - for(int pt=0;pt mean_highest_point){ - mean_highest_point = mean_trc[pt]; - highest_pt = pt; - } - } -#else // use the last point as the height highest_pt=npts-1; lowest_pt=1; mean_highest_point=mean_trc[highest_pt]; mean_lowest_point = mean_trc[lowest_pt]; -#endif float mean_height=mean_highest_point - mean_lowest_point; @@ -708,200 +551,12 @@ void AdvCompr::ComputeGain_FullChip() // } } -void AdvCompr::adjustForDrift() -{ - // create a mean trace - int frameStride=w*h; - float meanTrc[npts]; - short int meanTrcSub[npts]; - uint64_t sum=0; - int pt=0; - - for (pt=0;pt 1000) - break; - } - int onesec_ts_pt = pt; - float onesec_ts = timestamps_compr[pt]; - float slope = (meanTrc[pt]-meanTrc[0])/((float)timestamps_compr[pt]); - - for(pt=0;pt 1000) -// break; -// } -// float onesec_ts = timestamps_compr[pt]; -// -// // we now have the last timestamp before the valve opens -// // this should be a flat region -// float slope = (mean_trc[pt]-mean_trc[0])/((float)timestamps_compr[pt]); -// -//// printf("B Slope=%f lastpt=%d %.0f %.0f %.0f %.0f %.0f\n",slope,pt,mean_trc[0],mean_trc[1],mean_trc[2],mean_trc[3],mean_trc[4]); -// -// for(pt=0;pt 12) - end_pt=12; - // we already have t0est... - float weight[npts]; - float totalWeight=0; - int prev=0; - for (int i = 0; i < end_pt && i < npts; i++){ - // get how many samples went into this timepoint - weight[i] = (timestamps_compr[i] - prev) + 10 / timestamps_compr[0]; - totalWeight += weight[i]; - prev = timestamps_compr[i]; - } - - // smooth from 0 to end_ts - for (int nt = 0; nt < ntrcs; nt++){ - float avg=0; - for (int pt = 0; pt < end_pt; pt++){ - avg += ((float)TRCS_ACCESS(nt,pt))*weight[pt]; - } - avg /= totalWeight; - short int avgS = avg; - for (int pt = 0; pt < end_pt; pt++){ - TRCS_ACCESS(nt,pt) = avgS; - } - } - // now, smooth the mean trc - { - float avg=0; - for (int pt = 0; pt < end_pt; pt++){ - avg += mean_trc[pt]*weight[pt]; - } - avg /= totalWeight; - short int avgS = avg; - for (int pt = 0; pt < end_pt; pt++){ - mean_trc[pt] = avgS; - } - } - -} - -void AdvCompr::PostClearFrontPorch() -{ - // first, figure out where t0 should really be.. - int num_interesting_nvect = 3; - - int t0IdxEst[10]={0}; - float t0=0; - int t0Int=0; - - if(hdr.nBasisVec < num_interesting_nvect) - num_interesting_nvect = hdr.nBasisVec; - - for (int nvect = 0; nvect < num_interesting_nvect; nvect++) - { - float maxVal = 0; - for (int pt = 0; pt < npts; pt++){ - float val = fabs(COEFF_ACCESS(pt,nvect)-COEFF_ACCESS(0,nvect)); - if(val > maxVal) - maxVal = val; - } - float triggerLevel = maxVal/10.0f; -// printf("%s: vect %d maxVal %.2f triggerLeve = %.2f (",__FUNCTION__,nvect,maxVal,triggerLevel); -// -// for (int pt = 0; pt < npts; pt++){ -// printf(" %.2f",COEFF_ACCESS(pt,nvect)-COEFF_ACCESS(0,nvect)); -// } -// printf(")\n"); - - for (int pt = 0; pt < npts; pt++){ - if(fabs(COEFF_ACCESS(pt,nvect)-COEFF_ACCESS(0,nvect)) > triggerLevel){ - t0IdxEst[nvect] = pt; -// printf("%s: vect %d --> %d\n",__FUNCTION__,nvect,t0IdxEst[nvect]); - break; - } - } - } - for (int nvect = 0; nvect < num_interesting_nvect; nvect++) - { - t0 += t0IdxEst[nvect]; - } - t0 /= (float)num_interesting_nvect; - t0Int = (int)(t0+0.5f); -// printf("%s(%d): t0 %.1f --> %d\n",__FUNCTION__,num_interesting_nvect,t0,t0Int); - - - if(t0Int < 0) - t0Int = 0; - if(t0Int >= npts) - t0Int = npts-1; - // second, clear the average trace up to t0. - for(int pt=1;pt highV.A[k]); -#endif + tmpV.V -= rowNoiseV; + +// tmpV.V = LD_VEC8F(mCorr_sigs[r*npts+pt]); //DEBUG - // gain correct -// tmpV.V *= *lgcV; meanV.V += tmpV.V; //sum all wells for mean trace *tPtrV = tmpV.V; itrc += VEC8_SIZE; @@ -1169,6 +841,15 @@ int AdvCompr::PopulateTraceBlock_ComputeMean() mean_trc[pt] = meanPt/(float)ntrcs; } + // copy the second point to the first + mean_trc[0] = mean_trc[1]; + v8f *rtrcV0 = (v8f *) &TRCS_ACCESS(0,0); + v8f *rtrcV1 = (v8f *) &TRCS_ACCESS(0,1); + + for(int idx=0;idx= 0 &&*/ ++good_count == sample_rate) { *sample_col_begin++ = *col_start; -// good_count = 0; check_count++; if(check_count == nSamples) break; @@ -1437,28 +1064,6 @@ void AdvCompr::CreateSampleMatrix() timing.createSample += AdvCTimer() - start; } -void AdvCompr::ComputeEmphasisVector(float *gv, float mult, float adder, float width) -{ -// float dt; -// const float width=10.0f; - float gvssq = 0.0f; - int t0estLocal=t0estIdx+7;// needs to point to the middle of the incorporation - - for (int i=0;i < npts;i++) - { - float dt=i-t0estLocal; - gv[i]=mult*exp(-dt*dt/width)+adder; -// ptgv[i]=COEFF_ACCESS(i,nvect)*gv[i]; - gvssq += gv[i]*gv[i]; - } - gvssq = sqrt(gvssq); - - for (int i=0;i < npts;i++) -// gv[i]/=gvssq; - gv[i] = 1.0f; -} - - float AdvCompr::ExtractVectors() { @@ -1471,8 +1076,6 @@ float AdvCompr::ExtractVectors() for (pt = 0; pt < npts; pt++) gv[pt] = 1.0f; -// ComputeEmphasisVector(gv, 2.0f, 1.4f, 10.0f); - for (int nvect = 0; nvect < hdr.nBasisVec; nvect++) { float ssum = 0; @@ -1544,7 +1147,7 @@ int AdvCompr::GetBitsNeeded() { for (i = 0; i < ntrcs; i++) { - if (trcs_state[i]) + if (trcs_state[i]==0) continue; if (TRC_COEFF_ACC(nv,i) > maxVals[nv]) maxVals[nv] = TRC_COEFF_ACC(nv,i); @@ -1679,7 +1282,7 @@ uint64_t AdvCompr::PackBits(uint64_t *buffer, uint64_t *bufferEnd) { nlvec = nvec; - if (trcs_state[itrc] == 0) + if (trcs_state[itrc]) { for (nv = 0; nv < nlvec; nv++) { @@ -1854,7 +1457,7 @@ int AdvCompr::WriteTraceBlock() // write out the bits needed per vec Write(fd, bitsNeeded, sizeof(bitsNeeded[0]) * nvect); - if(frameRate != 15){ + if(frameRate != 15 && timeTransform){ float nMeanTrc[npts_newfr]; TimeTransform_trace(npts,npts_newfr,timestamps_compr,timestamps_newfr,mean_trc,nMeanTrc,1); @@ -1864,14 +1467,6 @@ int AdvCompr::WriteTraceBlock() float nBasisVects[nvect*npts_newfr]; TimeTransform_trace(npts,npts_newfr,timestamps_compr,timestamps_newfr,basis_vectors,nBasisVects,nvect); -// printf("new basis_vectors:\n"); -// for(int bv=0;bv high || fptr[idx] < low){ + if(mMask[idx]){ + pinned++; + mMask[idx] = 0; + } + } + } + } +// printf("adv: pinned: %d\n",pinned); + +// for(int y=0;y0) { + if (y-span > 0) { + nn_sum[frame] -= SIGS_ACC(start_y-1, frame); + } + if (y+span <= h) { + nn_sum[frame] += SIGS_ACC(end_y, frame); + } + } + nn_avg = nn_sum[frame]; + nn_avg /= (float)(end_y-start_y); + float tmp = SIGS_ACC(y,frame) - nn_avg; + if(tmp != tmp) + tmp=0; + NOISE_ACC(y,frame) = tmp; + + } + } + +#if 0 + // apply the correction right here.. + v8s *srcPtr = (v8s *)&raw[0]; + int frameStrideV=(h*w/8); + + for (int y = 0; y < h; y++) { + for(int x=0;x= 0 && ThreadNum < ADVC_MAX_REGIONS)) diff --git a/Analysis/Image/ChipIdDecoder.cpp b/Analysis/Image/ChipIdDecoder.cpp index 2c1e409b..114c216a 100644 --- a/Analysis/Image/ChipIdDecoder.cpp +++ b/Analysis/Image/ChipIdDecoder.cpp @@ -22,6 +22,7 @@ ChipIdDecodeArrayType ChipIdDecoder::chip_id_str_lookup_array[] = { "p2.2.2", ChipId2_2_2 }, { "900", ChipId_old_P1 }, //for backward compatibility and basecalling of old old chips { "p1.0.20", ChipId1_0_20 }, + { "560", ChipId560 }, { "550", ChipId550 }, { "540", ChipId540 }, { "530", ChipId530 }, @@ -93,6 +94,7 @@ bool ChipIdDecoder::IsProtonChip(){ case ChipId1_0_19: case ChipId1_0_20: case ChipId_old_P1: + case ChipId560: case ChipId550: case ChipId540: case ChipId530: @@ -150,8 +152,8 @@ bool ChipIdDecoder::IsPtwo(){ case ChipId1_2_18: case ChipId2_2_1: case ChipId2_2_2: - case ChipId550: case ChipId551: + case ChipId560: return true; break; default: @@ -213,6 +215,7 @@ bool ChipIdDecoder::BigEnoughForGPU(){ case ChipId2_2_1: case ChipId2_2_2: case ChipId_old_P1: + case ChipId560: case ChipId550: case ChipId540: case ChipId530: diff --git a/Analysis/Image/ChipIdDecoder.h b/Analysis/Image/ChipIdDecoder.h index aafab860..df8a5958 100644 --- a/Analysis/Image/ChipIdDecoder.h +++ b/Analysis/Image/ChipIdDecoder.h @@ -30,7 +30,8 @@ typedef enum ChipId2_1_1 = 21, ChipId2_3_1 = 22, ChipId1_1_541 = 23, - ChipId522 = 24 + ChipId522 = 24, + ChipId560 = 25 } ChipIdEnum; typedef struct diff --git a/Analysis/Image/CorrNoiseCorrector.cpp b/Analysis/Image/CorrNoiseCorrector.cpp index e12d4c6f..8ec118dc 100644 --- a/Analysis/Image/CorrNoiseCorrector.cpp +++ b/Analysis/Image/CorrNoiseCorrector.cpp @@ -78,7 +78,7 @@ double CorrNoiseCorrector::CorrectCorrNoise(RawImage *raw, int correctRows, int double CorrNoiseCorrector::CorrectCorrNoise(short *_image, int _rows, int _cols, int _frames, int correctRows, - int _thumbnail, bool override, bool verbose, int threadNum, int avg) + int _thumbnail, bool override, bool verbose, int threadNum, int avg, int frame_mult) { char *allocPtr; double rc=0; @@ -102,6 +102,8 @@ double CorrNoiseCorrector::CorrectCorrNoise(short *_image, int _rows, int _cols, rows=_rows; cols=_cols; frames=_frames; + if(frame_mult) + fmult=frame_mult; mCorr_mask_len = rows*cols*2; mCorr_mask=(short int *)memalign(64,mCorr_mask_len); @@ -350,7 +352,7 @@ void CorrNoiseCorrector::DebugSaveRowNoise(int correctRows) // make sure our applied correction won't pin any currently un-pinned pixels void CorrNoiseCorrector::FixouterPixels() { - int frameStride=rows*cols; + int frameStride=rows*cols*fmult; int endFrmOffset = (frames-1)*frameStride; for (int idx = 0; idx < rows*cols; idx++){ short int *srcPtr = (short int *) (&image[idx]); @@ -403,7 +405,7 @@ void CorrNoiseCorrector::FixouterPixels() void CorrNoiseCorrector::ReZeroPinnedPixels_cpFirstFrame() { - int frameStride=rows*cols; + int frameStride=rows*cols*fmult; #ifdef SCALE_TRACES int64_t avg_height=0; int64_t avg_cnt=0; @@ -497,8 +499,8 @@ void CorrNoiseCorrector::ReZeroPinnedPixels_cpFirstFrame() double CorrNoiseCorrector::ApplyCorrection_rows() { int y; - int frameStride=rows*cols; - int frameStrideV=rows*cols/8; + int frameStride=rows*cols*fmult; + int frameStrideV=(rows*cols/8)*fmult; double startTime = CNCTimer(); double TotalAvgNoiseSq=0; double TotalAvgNoiseSum=0; @@ -574,7 +576,7 @@ void CorrNoiseCorrector::ApplyCorrection_cols() { for(int y=0 ;y< rows; y++){ v8f_u *corr=(v8f_u *)&NOISE_ACC(0,0,frame); - v8s *srcPtr=(v8s *)&image[frame*rows*cols + y*cols]; + v8s *srcPtr=(v8s *)&image[frame*fmult*rows*cols + y*cols]; for(int x=0 ;x< cols; x+=8,corr++,srcPtr++){ v8s_u corrS; CVT_VEC8F_VEC8S(corrS,(*corr)); @@ -622,7 +624,7 @@ void CorrNoiseCorrector::SumRows() for (frame = 0; frame < frames; frame++) { for (y = 0; y < rows; y++){ - short int *sptr = (short int *) (&image[frame * cols*rows + y * cols]); + short int *sptr = (short int *) (&image[frame * fmult * cols*rows + y * cols]); v8s *maskSumPtr=(v8s *)&mCorr_mask[y*cols]; for(int reg=0;regtimestamps[0]); - if(!headerOnly && timeTransform && rc != 0 && raw->timestamps[0] > 0 && (raw->timestamps[0] < 60 || raw->timestamps[0] > 72)) - { - // more than 10% off from 15fps - // use PCA to time-transform this file to 15fps - AdvComprTest(rawFileName,this,(char *)"7"); // 7 is write a file that's pca compressed and time transformed - free(raw->timestamps); - raw->timestamps=NULL; - free(raw->image); - raw->image=NULL; - raw->rows=saved_rows; - raw->cols=saved_cols; - raw->frames=saved_frames; - raw->uncompFrames=saved_uncompframes; - raw->imageState = saved_ImageState; - char newFname[2048]; - strcpy(newFname,rawFileName); - char *ptr = strstr(newFname,datPostfix); - if(ptr) - sprintf(ptr,"_testPCA.%s", datPostfix); - rc = deInterlace_c ( ( char * ) newFname,&raw->image,&raw->timestamps, - &raw->rows,&raw->cols,&raw->frames,&raw->uncompFrames, - 0,0, - ImageCropping::chipSubRegion.col,ImageCropping::chipSubRegion.row, - ImageCropping::chipSubRegion.col+ImageCropping::chipSubRegion.w,ImageCropping::chipSubRegion.row+ImageCropping::chipSubRegion.h, - ignoreChecksumErrors, &raw->imageState ); - } +// if(!headerOnly && timeTransform && rc != 0 && raw->timestamps[0] > 0 && (raw->timestamps[0] < 60 || raw->timestamps[0] > 72)) +// { +// // more than 10% off from 15fps +// // use PCA to time-transform this file to 15fps +// AdvComprTest(rawFileName,this,(char *)"7"); // 7 is write a file that's pca compressed and time transformed +// free(raw->timestamps); +// raw->timestamps=NULL; +// free(raw->image); +// raw->image=NULL; +// raw->rows=saved_rows; +// raw->cols=saved_cols; +// raw->frames=saved_frames; +// raw->uncompFrames=saved_uncompframes; +// raw->imageState = saved_ImageState; +// char newFname[2048]; +// strcpy(newFname,rawFileName); +// char *ptr = strstr(newFname,datPostfix); +// if(ptr) +// sprintf(ptr,"_testPCA.%s", datPostfix); +// rc = deInterlace_c ( ( char * ) newFname,&raw->image,&raw->timestamps, +// &raw->rows,&raw->cols,&raw->frames,&raw->uncompFrames, +// 0,0, +// ImageCropping::chipSubRegion.col,ImageCropping::chipSubRegion.row, +// ImageCropping::chipSubRegion.col+ImageCropping::chipSubRegion.w,ImageCropping::chipSubRegion.row+ImageCropping::chipSubRegion.h, +// ignoreChecksumErrors, &raw->imageState ); +// } if ( ImageCropping::chipSubRegion.h != 0 ) diff --git a/Analysis/Image/LSRowImageProcessor.cpp b/Analysis/Image/LSRowImageProcessor.cpp index ae4db104..15e3efae 100644 --- a/Analysis/Image/LSRowImageProcessor.cpp +++ b/Analysis/Image/LSRowImageProcessor.cpp @@ -246,7 +246,7 @@ bool LSRowImageProcessor::GenerateGroupCorrection(int group_num,float *vect_outp // make sure derived coefficients are valid for(int row=0;row < nLen;row++) - if(isnan(coeffs(row))) + if(std::isnan(coeffs(row))) { result_ok = false; break; diff --git a/Analysis/Image/PCACompression.cpp b/Analysis/Image/PCACompression.cpp index a6619f95..5e68e0a6 100755 --- a/Analysis/Image/PCACompression.cpp +++ b/Analysis/Image/PCACompression.cpp @@ -32,6 +32,8 @@ int PCACompr::Compress() else ComputeOrderVect(nvect, order++); + smoothNextVector(nvect,1); + EnsureOrthogonal(nvect, (nvect < nRvect)); SubtractVector(nvect, 1); @@ -50,6 +52,28 @@ int PCACompr::Compress() return(total_iter); } +void PCACompr::smoothNextVector(int nvect, int blur) +{ + float trc[npts]; + + for (int j=0;j < npts;j++) + trc[j] = COEFF_ACCESS(j,nvect); + + for (int j=0;j < npts;j++){ + float sum=0.0f; + int start_frame = std::max(0,j-blur); + int end_frame = std::min(npts,j+blur+1); + for(int fr=start_frame;fr end_threshold) && (iter < 10)) && (!failed)); + while (((fabs(tmag - last_tmag)/tmag > end_threshold) && (iter < 20)) && (!failed)); if (failed) { diff --git a/Analysis/Image/PCACompression.h b/Analysis/Image/PCACompression.h index e72b1dfd..2072d627 100755 --- a/Analysis/Image/PCACompression.h +++ b/Analysis/Image/PCACompression.h @@ -30,6 +30,7 @@ class PCACompr{ void EnsureOrthogonal(int nvect, int check); int ComputeNextVector(int nvect, int skip); void ComputeEmphasisVector(float *gv, float mult, float adder, float width); + void smoothNextVector(int nvect, int blur); int nRvect; diff --git a/Analysis/Image/Vecs.h b/Analysis/Image/Vecs.h index df2133d0..f9198c4e 100644 --- a/Analysis/Image/Vecs.h +++ b/Analysis/Image/Vecs.h @@ -16,7 +16,9 @@ #define VEC2_SIZE 2 #define VEC4_SIZE 4 #define VEC8_SIZE 8 +#define VEC16_SIZE 16 #define VEC32_SIZE 32 +#define VEC64_SIZE 64 #define VEC4F_SIZE_B (VEC4_SIZE*4) #define VEC4I_SIZE_B (VEC4_SIZE*4) @@ -27,8 +29,12 @@ #define VEC8F_SIZE_B (VEC8_SIZE*4) #define VEC8I_SIZE_B (VEC8_SIZE*4) #define VEC8S_SIZE_B (VEC8_SIZE*2) - +#define VEC16S_SIZE_B (VEC16_SIZE*2) +#define VEC16F_SIZE_B (VEC16_SIZE*4) +#define VEC32S_SIZE_B (VEC32_SIZE*2) #define VEC32F_SIZE_B (VEC32_SIZE*4) +#define VEC64S_SIZE_B (VEC64_SIZE*2) +#define VEC64F_SIZE_B (VEC64_SIZE*4) #define LD_VEC4F(x) (v4f) {x,x,x,x} #define LD_VEC4I(x) (v4i) {x,x,x,x} @@ -39,7 +45,6 @@ #define LD_VEC8S(x) (v8s) {x,x,x,x,x,x,x,x} #define LD_VEC8SU(x) (v8su) {x,x,x,x,x,x,x,x} -#define LD_VEC32F(x) (v32f) {x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x}; #ifdef WIN32 //typedef struct{ @@ -51,6 +56,16 @@ typedef int v4i __attribute__ ((vector_size (VEC4I_SIZE_B))); typedef short int v4s __attribute__ ((vector_size (VEC4S_SIZE_B))); typedef long long int v4di __attribute__ ((vector_size (VEC4DI_SIZE_B))); typedef long long int v2di __attribute__ ((vector_size (VEC2DI_SIZE_B))); +typedef float v8f __attribute__ ((vector_size (VEC8F_SIZE_B))); +typedef float v16f __attribute__ ((vector_size (VEC16F_SIZE_B))); +typedef int v8i __attribute__ ((vector_size (VEC8I_SIZE_B))); +typedef short int v8s __attribute__ ((vector_size (VEC8S_SIZE_B))); +typedef short unsigned int v8su __attribute__ ((vector_size (VEC8S_SIZE_B))); +typedef short int v16s __attribute__ ((vector_size (VEC16S_SIZE_B))); +typedef short int v32s __attribute__ ((vector_size (VEC32S_SIZE_B))); +typedef float v32f __attribute__ ((vector_size (VEC32F_SIZE_B))); +typedef short int v64s __attribute__ ((vector_size (VEC64S_SIZE_B))); +typedef float v64f __attribute__ ((vector_size (VEC64F_SIZE_B))); typedef union{ v4f V; @@ -66,42 +81,64 @@ typedef union{ short int A[VEC4_SIZE]; }v4s_u; - - -typedef float v8f __attribute__ ((vector_size (VEC8F_SIZE_B))); -typedef int v8i __attribute__ ((vector_size (VEC8I_SIZE_B))); -typedef short int v8s __attribute__ ((vector_size (VEC8S_SIZE_B))); -typedef short unsigned int v8su __attribute__ ((vector_size (VEC8S_SIZE_B))); -typedef float v32f __attribute__ ((vector_size (VEC32F_SIZE_B))); - -typedef union{ - v8f V; - float A[VEC8_SIZE]; -}v8f_u; typedef union{ v8i V; int A[VEC8_SIZE]; }v8i_u; typedef union{ v8s V; + v8s V8[1]; short int A[VEC8_SIZE]; }v8s_u; +typedef union{ + v8f V; + v8f V8[1]; + float A[VEC8_SIZE]; +}v8f_u; typedef union{ v8su V; short unsigned int A[VEC8_SIZE]; }v8su_u; +typedef union{ + v16s V; + v8s V8[2]; + short int A[VEC16_SIZE]; +}v16s_u; +typedef union{ + v16f V; + v8f V8[2]; + float A[VEC16_SIZE]; +}v16f_u; + +typedef union{ + v32s V; + v8s V8[4]; + short int A[VEC32_SIZE]; +}v32s_u; typedef union{ v32f V; + v8f V8[4]; float A[VEC32_SIZE]; }v32f_u; +typedef union{ + v64s V; + v8s V8[8]; + short int A[VEC64_SIZE]; +}v64s_u; +typedef union{ + v64f V; + v8f V8[8]; + float A[VEC64_SIZE]; +}v64f_u; -#ifdef __AVX__ + +#if 0 //def __AVX__ // Clang: __builtin_ia32_pshufd has been removed (Jun 2009) (Use _mm_shuffle_epi32 directly) // http://llvm.org/viewvc/llvm-project?view=revision&revision=72995 // http://llvm.org/viewvc/llvm-project?view=revision&revision=72996 -#define LD_VEC8S_CVT_VEC8F(src_ptr,output_var) {\ +#define LD8_VS_VFI(src_ptr,output_var) {\ /* convert the 2 4(16-bit ints) to 2 4(32-bit ints) samples*/ \ register v4i srcData=*((v4i*)src_ptr); \ register v4i LtmpL = __builtin_ia32_pmovsxwd128 ((v8s)srcData); \ @@ -112,16 +149,25 @@ typedef union{ LvalV = __builtin_ia32_vinsertf128_si256(LvalV,LtmpL,0); \ LvalV = __builtin_ia32_vinsertf128_si256(LvalV,LtmpH,1); \ /* then, convert the 8(32-bit ints) to 8(32-bit floats) */ \ - (output_var).V = __builtin_ia32_cvtdq2ps256(LvalV); \ + (output_var) = __builtin_ia32_cvtdq2ps256(LvalV); \ } + #else -#define LD_VEC8S_CVT_VEC8F(src_ptr,output_var) {\ +#define LD8_VS_VFI(src_ptr,output_var) {\ for(int k_idx=0;k_idx #include "LinuxCompat.h" #include - +#include +#include +#include Mask::Mask ( int _w, int _h ) { @@ -57,11 +59,6 @@ Mask::Mask ( const char *fileName, bool fillMask ) fclose ( in ); } -Mask::~Mask() -{ - mask.clear(); -} - void Mask::Init ( int _w, int _h ) { Init ( _w,_h,MaskEmpty ); @@ -156,12 +153,12 @@ void Mask::Set ( int x, int y, MaskType type ) } } -// adds specified type flag to all wells in the mask +// sets specified type flag to all wells in the mask; resets all other flags void Mask::SetAll(MaskType type ) { int i; for ( i=0;i 0) { + sscanf(line.c_str(), "%d\t%d", &maskStart, &maskEnd); + + if( y>=offset_y && y=offset_x ){ //fill in the beginning of the row + for( int x = 0; x < std::min(maskStart-offset_x, size_x); ++x){ + mask[x+((y-offset_y)*this->w)] = MaskExclude; + masked++; + } + } + + if( maskEnd < (offset_x+size_x) ){ //fill in the end of the row + //note that maskEnd==0 of the whole row is excluded + for( int x = std::max(0, maskEnd - offset_x); xw)] = MaskExclude; + masked++; + } + } + } + } + } + std::cerr << "Total Masked Wells: " << masked << "\n"; + return 0; +} + int Mask::DumpStats ( Region region, char *fileName, bool showWashouts ) const { int x; diff --git a/Analysis/Mask/Mask.h b/Analysis/Mask/Mask.h index 499d0099..48de3abd 100644 --- a/Analysis/Mask/Mask.h +++ b/Analysis/Mask/Mask.h @@ -50,7 +50,7 @@ class Mask Mask ( int w, int h ); Mask ( Mask *origmask ); Mask ( const char *fileName, bool fillMask=true ); - virtual ~Mask(); + virtual ~Mask(){} Mask() { mask.clear(); } @@ -118,6 +118,8 @@ class Mask int MaskList ( char *fileName, MaskType these, Region region ) const; int WriteRaw ( const char *fileName ) const; int SetMask ( const char *fileName ); + int SetMaskFullChipText(const char *fileName, int offset_x, int offset_y, int size_x, int size_y); + void LoadMaskAndAbortOnFailure(const char *maskFileName); void UpdateBeadFindOutcomes( Region &wholeChip, char const *maskFileName, bool not_single_beadfind, int update_stats, char const *maskStatsName); int DumpStats ( Region region, char *fileName, bool showWashouts = true ) const; @@ -137,7 +139,8 @@ class Mask // void OnlySomeWells(std::vector mWellIdx); std::vector mask; - protected: + +protected: int32_t w, h; int32_t xOffset, yOffset; //uint16_t *mask; diff --git a/Analysis/Mask/PinnedInFlow.cpp b/Analysis/Mask/PinnedInFlow.cpp index 3f985855..311a271d 100644 --- a/Analysis/Mask/PinnedInFlow.cpp +++ b/Analysis/Mask/PinnedInFlow.cpp @@ -233,60 +233,109 @@ int PinnedInFlow::Update (int flow, Image *img, float *gainPtr) } #ifdef __AVX__ - if ((cols % VEC8_SIZE) == 0 && gainPtr != NULL) - { + if ((cols % VEC8_SIZE) == 0) { int k, idx; int frameStride = rows * cols; short int *src; +#define MY_VEC_SIZE 8 +#define MY_VECF v8f_u + v8f_u highV, lowV; - v8f_u tmpV; - v8f *gainPtrV; - v8f_u pinnedV; + v8f_u dummy8 = { }; + MY_VECF tmpV; + MY_VECF *gainPtrV; + MY_VECF pinnedV; + MY_VECF dummy = { }; short int *sptr; - highV.V = LD_VEC8F((const float) pinHigh); - lowV.V = LD_VEC8F((const float)pinLow); + highV.V = dummy8.V + (float) pinHigh; + lowV.V = dummy8.V + (float) pinLow; src = (short int *) (raw->image); - gainPtrV=(v8f *)gainPtr; - for(idx=0;idxV = tmpS.V; + } + gainPtrV++; + // if any of the 8 pixels are pinned + int somePinned = 0; + for (uint l = 0; l < sizeof(pinnedV.V8) / sizeof(pinnedV.V8[0]); + l++) { + if (__builtin_ia32_ptestnzc256((v4di) pinnedV.V8[l], + (v4di ) { -1, -1, -1, -1 })) + somePinned = 1; + } - CVT_VEC8F_VEC8S((*(v8s_u *)sptr),tmpV); + if (somePinned) { + for (k = 0; k < MY_VEC_SIZE; k++) { + if (pinnedV.A[k]) + SetPinned(idx + k, flow); + } + } } - gainPtrV++; - // if any of the 8 pixels are pinned - if(__builtin_ia32_ptestnzc256((v4di)pinnedV.V,(v4di){-1,-1,-1,-1}) ) - { - for (k = 0; k < VEC8_SIZE; k++) - { - if(pinnedV.A[k]) - SetPinned(idx+k, flow); + } else { + // no gain correction + for (idx = 0; idx < frameStride; idx += MY_VEC_SIZE, src += + MY_VEC_SIZE) { + pinnedV.V = dummy.V + 0; + + for (frame = 0; frame < frames; frame++) { + sptr = &src[frame * frameStride]; + + LD_VS_VF(sptr, tmpV); + + // now, do the pinned pixel comparisons + for (uint l = 0; + l < sizeof(pinnedV.V8) / sizeof(pinnedV.V8[0]); + l++) { + pinnedV.V8[l] += __builtin_ia32_cmpps256(tmpV.V8[l], + lowV.V, _CMP_LT_OS); + pinnedV.V8[l] += __builtin_ia32_cmpps256(highV.V, + tmpV.V8[l], _CMP_LT_OS); + } + } + // if any of the 8 pixels are pinned + int somePinned = 0; + for (uint l = 0; l < sizeof(pinnedV.V8) / sizeof(pinnedV.V8[0]); + l++) { + if (__builtin_ia32_ptestnzc256((v4di) pinnedV.V8[l], + (v4di ) { -1, -1, -1, -1 })) + somePinned = 1; + } + + if (somePinned) { + for (k = 0; k < MY_VEC_SIZE; k++) { + if (pinnedV.A[k]) + SetPinned(idx + k, flow); + } } } - } - } - else + } + }else #endif { int16_t *pixPtr; diff --git a/Analysis/Separator/BeadfindStats.cpp b/Analysis/Separator/BeadfindStats.cpp index 9f469742..ae967d7f 100644 --- a/Analysis/Separator/BeadfindStats.cpp +++ b/Analysis/Separator/BeadfindStats.cpp @@ -37,10 +37,11 @@ void CalcBfT0(DifSepOpt &opts, std::vector &t0vec, Mask &mask, int gridMo img.FilterForPinned(&mask, MaskEmpty, false); T0Calc t0; t0.SetWindowSize(3); - t0.SetMinFirstHingeSlope(-5.0/raw->baseFrameRate); - t0.SetMaxFirstHingeSlope(300.0/raw->baseFrameRate); - t0.SetMinSecondHingeSlope(-20000.0/raw->baseFrameRate); - t0.SetMaxSecondHingeSlope(-10.0/raw->baseFrameRate); + t0.SetMinFirstHingeSlope(opts.beadfindBfThreshold[0]/raw->baseFrameRate); + t0.SetMaxFirstHingeSlope(opts.beadfindBfThreshold[1]/raw->baseFrameRate); + t0.SetMinSecondHingeSlope(opts.beadfindBfThreshold[2]/raw->baseFrameRate); + t0.SetMaxSecondHingeSlope(opts.beadfindBfThreshold[3]/raw->baseFrameRate); + t0.SetDebugLevel(opts.outputDebug); short *data = raw->image; int frames = raw->frames; t0.SetMask(&mask); @@ -112,6 +113,10 @@ int main(int argc, const char *argv[]) { opts.t0MeshStepX = size; opts.t0MeshStepY = size; } + + opts.beadfindBfThreshold = std::vector({-5, 300, -20000, -10}); + + Mask mask; int row = 0, col = 0; std::vector t0; @@ -130,7 +135,7 @@ int main(int argc, const char *argv[]) { } } - SetExcludeMask(loc_context, &mask, ChipIdDecoder::GetChipType(), mask.H(), mask.W()); + SetExcludeMask(loc_context, &mask, ChipIdDecoder::GetChipType(), mask.H(), mask.W(), std::string(""), false); size_t numWells = mask.H() * mask.W(); int excludeWells = 0; for (size_t i = 0; i < numWells; i++) { diff --git a/Analysis/Separator/DifferentialSeparator.cpp b/Analysis/Separator/DifferentialSeparator.cpp index 7345f796..6c70ccd1 100644 --- a/Analysis/Separator/DifferentialSeparator.cpp +++ b/Analysis/Separator/DifferentialSeparator.cpp @@ -1347,11 +1347,13 @@ void DifferentialSeparator::CalcBfT0(DifSepOpt &opts, std::vector &t0vec, copy(raw->timestamps, raw->timestamps+raw->frames, &mBFTimePoints[0]); T0Calc t0; t0.SetWindowSize(3); - t0.SetMinFirstHingeSlope(-5.0/raw->baseFrameRate); - t0.SetMaxFirstHingeSlope(300.0/raw->baseFrameRate); - t0.SetMinSecondHingeSlope(-20000.0/raw->baseFrameRate); - t0.SetMaxSecondHingeSlope(-10.0/raw->baseFrameRate); + t0.SetMinFirstHingeSlope(opts.beadfindBfThreshold[0]/raw->baseFrameRate); + t0.SetMaxFirstHingeSlope(opts.beadfindBfThreshold[1]/raw->baseFrameRate); + t0.SetMinSecondHingeSlope(opts.beadfindBfThreshold[2]/raw->baseFrameRate); + t0.SetMaxSecondHingeSlope(opts.beadfindBfThreshold[3]/raw->baseFrameRate); t0.SetBadWells(&mFilteredWells[0]); + t0.SetDebugLevel(opts.outputDebug); + short *data = raw->image; int frames = raw->frames; t0.SetMask(&mMask); @@ -1420,11 +1422,13 @@ void DifferentialSeparator::CalcAcqT0(DifSepOpt &opts, std::vector &t0vec copy(raw->timestamps, raw->timestamps+raw->frames, &mBFTimePoints[0]); T0Calc t0; t0.SetWindowSize(4); - t0.SetMinFirstHingeSlope(-10.0/raw->baseFrameRate); - t0.SetMaxFirstHingeSlope(3.0/raw->baseFrameRate); - t0.SetMinSecondHingeSlope(5.0/raw->baseFrameRate); - t0.SetMaxSecondHingeSlope(500.0/raw->baseFrameRate); + t0.SetMinFirstHingeSlope(opts.beadfindAcqThreshold[0]/raw->baseFrameRate); + t0.SetMaxFirstHingeSlope(opts.beadfindAcqThreshold[1]/raw->baseFrameRate); + t0.SetMinSecondHingeSlope(opts.beadfindAcqThreshold[2]/raw->baseFrameRate); + t0.SetMaxSecondHingeSlope(opts.beadfindAcqThreshold[3]/raw->baseFrameRate); t0.SetBadWells(&mFilteredWells[0]); + t0.SetDebugLevel(opts.outputDebug); + short *data = raw->image; int frames = raw->frames; t0.SetMask(&mMask); @@ -1487,11 +1491,13 @@ void DifferentialSeparator::CalcAcqT0(DifSepOpt &opts, std::vector &t0vec copy(raw->timestamps, raw->timestamps+raw->frames, &mBFTimePoints[0]); T0Calc t0; t0.SetWindowSize(4); - t0.SetMinFirstHingeSlope(-10.0/raw->baseFrameRate); - t0.SetMaxFirstHingeSlope(3.0/raw->baseFrameRate); - t0.SetMinSecondHingeSlope(5.0/raw->baseFrameRate); - t0.SetMaxSecondHingeSlope(500.0/raw->baseFrameRate); + t0.SetMinFirstHingeSlope(opts.beadfindAcqThreshold[0]/raw->baseFrameRate); + t0.SetMaxFirstHingeSlope(opts.beadfindAcqThreshold[1]/raw->baseFrameRate); + t0.SetMinSecondHingeSlope(opts.beadfindAcqThreshold[2]/raw->baseFrameRate); + t0.SetMaxSecondHingeSlope(opts.beadfindAcqThreshold[3]/raw->baseFrameRate); t0.SetBadWells(&mFilteredWells[0]); + t0.SetDebugLevel(opts.outputDebug); + short *data = raw->image; int frames = raw->frames; t0.SetMask(&mMask); diff --git a/Analysis/Separator/DifferentialSeparator.h b/Analysis/Separator/DifferentialSeparator.h index e031ad81..c5dad178 100644 --- a/Analysis/Separator/DifferentialSeparator.h +++ b/Analysis/Separator/DifferentialSeparator.h @@ -162,6 +162,8 @@ class DifSepOpt bool col_pair_pixel_xtalk_correct; float pair_xtalk_fraction; bool corr_noise_correct; + std::vector beadfindAcqThreshold; + std::vector beadfindBfThreshold; }; /** diff --git a/Analysis/Separator/T0Model.h b/Analysis/Separator/T0Model.h index b9e51e01..ad2a079f 100644 --- a/Analysis/Separator/T0Model.h +++ b/Analysis/Separator/T0Model.h @@ -338,7 +338,7 @@ class T0Finder { } /** Look at trace data provided by Y and find when nuc hits the wells */ - bool FindT0Time(float *Y, float *X, size_t size) { + bool FindT0Time(float *Y, float *X, size_t size, FILE* fdbg) { mX.resize(size); for (size_t i = 0; i < mX.size(); i++) { mX[i] = X[i]; @@ -356,6 +356,13 @@ class T0Finder { t0.SetFirstRange(i - mWindowSize, i); t0.SetSecondRange(i+1, i + mWindowSize + 1); t0.FitModel(Y, &mX[0]); + if( fdbg ){ + bool firstOk = (t0.mHingeModel.GetFirst().GetSlope() >= mFirstSlopeRange[0] && + t0.mHingeModel.GetFirst().GetSlope() <= mFirstSlopeRange[1]); + bool secondOk = (t0.mHingeModel.GetSecond().GetSlope() >= mSecondSlopeRange[0] && + t0.mHingeModel.GetSecond().GetSlope() <= mSecondSlopeRange[1]); + fprintf(fdbg, "%g %g %g %g %g %d %d\n", t0.GetT0Est()/66., 66.*t0.mHingeModel.GetFirst().GetSlope(), 66.*t0.mHingeModel.GetSecond().GetSlope(), t0.GetSsqDiff(), t0.GetSsqRatio(), firstOk, secondOk ); + } if (first) { mBestT0 = t0; maxSsqDiff = 0.0; @@ -371,6 +378,8 @@ class T0Finder { } } } + if( fdbg ) + fprintf(fdbg, "%g %g %g %g %g\n", mBestT0.GetT0Est()/66., 66.*mBestT0.mHingeModel.GetFirst().GetSlope(), 66.*mBestT0.mHingeModel.GetSecond().GetSlope(), mBestT0.GetSsqDiff(), mBestT0.GetSsqRatio() ); return found; } diff --git a/Analysis/T0Calc.h b/Analysis/T0Calc.h index a7d1ef41..4a17d321 100644 --- a/Analysis/T0Calc.h +++ b/Analysis/T0Calc.h @@ -63,7 +63,7 @@ class T0Calc { void SetMinFirstHingeSlope(float minSlope) { mMinFirstHingeSlope = minSlope; } void SetMaxSecondHingeSlope(float maxSlope) { mMaxSecondHingeSlope = maxSlope; } void SetMinSecondHingeSlope(float minSlope) { mMinSecondHingeSlope = minSlope; } - + void SetDebugLevel( int debugLevel ) { outputDebug = debugLevel; } void SetMask(Mask *mask) { mMask = mask; } /** Setup our t0 calculator. */ @@ -102,7 +102,7 @@ class T0Calc { /** Algorithm to fit t0 for this trace using a two piece linear model. */ static void CalcT0(T0Finder &finder, std::vector &trace, std::vector ×tamps, - T0Prior &prior, float &t0) { + T0Prior &prior, float &t0, FILE* fdbg=0) { /* Convert the t0 prior in time into frames. */ std::vector::iterator tstart; int frameEnd = trace.size(); @@ -118,7 +118,7 @@ class T0Calc { frameStart = frame - 1; } finder.SetSearchRange(frameStart, frameEnd); - bool ok = finder.FindT0Time(&trace[0], ×tamps[0], trace.size()); + bool ok = finder.FindT0Time(&trace[0], ×tamps[0], trace.size(), fdbg); if (ok) { t0 = finder.GetT0Est(); t0 = (prior.mT0Weight * prior.mT0Prior) + t0; @@ -174,18 +174,27 @@ class T0Calc { int tooFew = 0; int enough = 0; for (size_t bIx = 0; bIx < numBin; bIx++) { - std::pair > &bin = mRegionSum.GetItem(bIx); - if (bin.first < 20) { - tooFew++; - continue; - } - enough++; - for (size_t fIx = 0; fIx < bin.second.size(); fIx++) { - bin.second[fIx] = bin.second[fIx] / bin.first; - } - T0Prior &prior = mT0Prior.GetItem(bIx); - float &t0 = mT0.GetItem(bIx); - T0Calc::CalcT0(mFinder, bin.second, mTimeStamps, prior, t0); + FILE* fdbg = 0; + if( outputDebug>4){ + char fname[1024]; + sprintf(fname,"bin_%d.dbg",(int)bIx); + FILE* fdbg = fopen(fname,"w"); + } + std::pair > &bin = mRegionSum.GetItem(bIx); + if (bin.first < 20) { + tooFew++; + continue; + } + enough++; + for (size_t fIx = 0; fIx < bin.second.size(); fIx++) { + bin.second[fIx] = bin.second[fIx] / bin.first; + } + T0Prior &prior = mT0Prior.GetItem(bIx); + float &t0 = mT0.GetItem(bIx); + T0Calc::CalcT0(mFinder, bin.second, mTimeStamps, prior, t0, fdbg); + + if(fdbg) + fclose(fdbg); } // std::cout << "T0 Fitting: " << enough << " enough " << tooFew << " too few good wells." << std::endl; } @@ -677,6 +686,7 @@ class T0Calc { float mMinSdThresh; int mPixHigh; int mPixLow; + int outputDebug; }; diff --git a/Analysis/TMAP/CMakeLists.txt b/Analysis/TMAP/CMakeLists.txt index c98e5dc5..8bd6b509 100644 --- a/Analysis/TMAP/CMakeLists.txt +++ b/Analysis/TMAP/CMakeLists.txt @@ -172,9 +172,19 @@ configure_file ( "${PROJECT_BINARY_DIR}/config.h" @ONLY ) - add_executable(tmap ${tmap_SOURCES}) set_target_properties(tmap PROPERTIES COMPILE_FLAGS "-D_TMAP_BWT_RUN_TYPE=0 -D_USE_KNETFILE -DSAMTOOLS_MAIN -D__STDC_LIMIT_MACROS -DTMAP_MMAP=1") + +include(CheckCCompilerFlag) +check_c_compiler_flag(-fdiagnostics-color=never HAS_DIAGNOSTICS_COLOR) +if (HAS_DIAGNOSTICS_COLOR) + target_compile_options(tmap PUBLIC -fdiagnostics-color=never) +endif() + + + +# + target_link_libraries (tmap m z pthread bz2) install(TARGETS tmap DESTINATION bin) diff --git a/Analysis/TMAP/src/index/tmap_refseq.c b/Analysis/TMAP/src/index/tmap_refseq.c index 7ce210bc..881c8c47 100644 --- a/Analysis/TMAP/src/index/tmap_refseq.c +++ b/Analysis/TMAP/src/index/tmap_refseq.c @@ -1235,7 +1235,7 @@ tmap_refseq_read_bed_core(tmap_refseq_t *refseq, char *bedfile, int flag, char * // ZZ:current ampl is contained in the previous one continue; } else if (b[num-1] == beg) { - // ZZ:current one containing the previous one with the same begin, and larger ending, use the current to replace the previous + // ZZ:current one has same beg, but larger end, replace previous one num--; } } @@ -1265,11 +1265,11 @@ find_next_bed(tmap_refseq_t *refseq, int cur_ind, int cur_pos, int *b, int *e, i { int i = *n; *b = -1; - while (i < refseq->bednum[cur_ind] && refseq->bedend[cur_ind][i] < cur_pos) i++; + while (i < refseq->bednum[cur_ind] && refseq->bedend[cur_ind][i] <= cur_pos) i++; *n = i; if (i < refseq->bednum[cur_ind]) { - *b = refseq->bedstart[cur_ind][i]-1; - *e = refseq->bedend[cur_ind][i]-1; + *b = refseq->bedstart[cur_ind][i]; + *e = refseq->bedend[cur_ind][i]; } } @@ -1364,7 +1364,7 @@ int tmap_refseq_fasta2maskedfasta_main(int argc, char *argv[]) // now cur_chr == chr char *s = line; while (*s != '\n' && *s != 0) { - if(cur_pos > e) { + if(cur_pos >= e) { find_next_bed(refseq, chr_ind, cur_pos, &b, &e, &n); if (b == -1) { all_N(s); diff --git a/Analysis/TMAP/src/io/tmap_file.c b/Analysis/TMAP/src/io/tmap_file.c index e4a73422..d33e3c7c 100644 --- a/Analysis/TMAP/src/io/tmap_file.c +++ b/Analysis/TMAP/src/io/tmap_file.c @@ -495,55 +495,64 @@ int32_t tmap_log_enabled () return logging_enabled; } -int32_t tmap_log (const char* format, ...) +// waits for a logging mutex to get released, then acquires it +void tmap_log_record_begin () { - int32_t n = 0; - va_list ap; - if (!logging_enabled) - return n; #ifdef HAVE_LIBPTHREAD - pthread_mutex_lock (&log_mutex); + pthread_mutex_lock (&log_mutex); #endif - va_start(ap, format); - if (!log_file_pointer) - { - n = vprintf(format, ap); - fflush (stdout); - } - else - { - n = vfprintf(log_file_pointer, format, ap); - fflush (log_file_pointer); - } - va_end(ap); +} + +// releases logging mutex +void tmap_log_record_end () +{ #ifdef HAVE_LIBPTHREAD - pthread_mutex_unlock (&log_mutex); + pthread_mutex_unlock (&log_mutex); #endif - return n; +} + +int32_t tmap_log (const char* format, ...) +{ + int32_t n = 0; + va_list ap; + va_start (ap, format); + n = tmap_vlog (format, ap); + va_end (ap); + return n; } int32_t tmap_vlog (const char* format, va_list ap) { - int32_t n = 0; - if (!logging_enabled) + int32_t n = 0; + if (!logging_enabled) + return n; + if (!log_file_pointer) + { + n = vprintf(format, ap); + fflush (stdout); + } + else + { + n = vfprintf(log_file_pointer, format, ap); + fflush (log_file_pointer); + } return n; -#ifdef HAVE_LIBPTHREAD - pthread_mutex_lock (&log_mutex); -#endif - if (!log_file_pointer) - { - n = vprintf(format, ap); - fflush (stdout); - } - else - { - n = vfprintf(log_file_pointer, format, ap); - fflush (log_file_pointer); - } +} -#ifdef HAVE_LIBPTHREAD - pthread_mutex_unlock (&log_mutex); -#endif - return n; +int32_t tmap_log_s (const char* format, ...) +{ + int32_t n = 0; + va_list ap; + va_start (ap, format); + n = tmap_vlog_s (format, ap); + va_end (ap); + return n; } +int32_t tmap_vlog_s (const char* format, va_list ap) +{ + tmap_log_record_begin (); + int32_t n = tmap_vlog (format, ap); + tmap_log_record_end (); + return n; +} diff --git a/Analysis/TMAP/src/io/tmap_file.h b/Analysis/TMAP/src/io/tmap_file.h index 760098ce..ad3e8d11 100644 --- a/Analysis/TMAP/src/io/tmap_file.h +++ b/Analysis/TMAP/src/io/tmap_file.h @@ -189,8 +189,12 @@ tmap_file_fflush(tmap_file_t *fp, int32_t gz_flush); void tmap_log_enable (FILE* fp); void tmap_log_disable (); int32_t tmap_log_enabled (); +void tmap_log_record_begin (); +void tmap_log_record_end (); int32_t tmap_log (const char* format, ...); int32_t tmap_vlog (const char* format, va_list ap); +int32_t tmap_log_s (const char* format, ...); +int32_t tmap_vlog_s (const char* format, va_list ap); #endif // TMAP_FILE_H diff --git a/Analysis/TMAP/src/map/tmap_map_driver.c b/Analysis/TMAP/src/map/tmap_map_driver.c index 84c3a2b7..271ba543 100644 --- a/Analysis/TMAP/src/map/tmap_map_driver.c +++ b/Analysis/TMAP/src/map/tmap_map_driver.c @@ -8,7 +8,7 @@ #ifdef HAVE_LIBPTHREAD #include #endif -#include +#include #include "../util/tmap_error.h" #include "../util/tmap_alloc.h" #include "../util/tmap_definitions.h" @@ -195,7 +195,7 @@ unsigned tailrep_cont (unsigned pattlen, const char* pattern, unsigned textlen, return rep_beg; } -/* this function finds maximal length of the peridicity suffix in text +/* this function finds maximal length of the periodicity suffix in text returns first position in a text that belongs to such periodicity position */ unsigned tailrep (unsigned textlen, const char* text, unsigned max_period) { @@ -491,169 +491,481 @@ unsigned trim_cigar (uint32_t** cigar_buff, unsigned orig_cigar_sz, unsigned qry return trim_cigar_left (cigar_buff, orig_cigar_sz, qry_len - trim_at, ref_rep_off_p); } -static void cigar_log (const uint32_t* cigar, unsigned cigar_sz) +static uint8_t fully_clipped (uint32_t* cigar, int32_t n_cigar) { - const uint32_t* sent; - for (sent = cigar+cigar_sz; cigar != sent; ++cigar) + // check if there are any matches in cigar + // (less conservative would be a check for any reference consuming op, but since mapping that consumes reference with no match is senseless, we'll consider it no-map as well.) + int32_t idx; + for (idx = 0; idx != n_cigar; ++idx) + if (TMAP_SW_CIGAR_OP (cigar [idx]) == BAM_CMATCH) + return 0; + return 1; +} + +void realign_read (tmap_seq_t* qryseq, tmap_refseq_t* refseq, tmap_map_sams_t* sams, struct RealignProxy* realigner, tmap_map_stats_t *stat, uint32_t log_text_als) +{ + // extract query + const char* qryname = tmap_seq_get_name (qryseq)->s; + tmap_string_t* qrybases = tmap_seq_get_bases (qryseq); + const char* qry = qrybases->s; + unsigned matchidx; + for (matchidx = 0; matchidx < sams->n; ++matchidx) { - uint32_t curop = bam_cigar_op (*cigar); - uint32_t count = bam_cigar_oplen (*cigar); - char schar; - switch (curop) + // extract packed cigar and it's length + tmap_map_sam_t* match = sams->sams + matchidx; + unsigned orig_cigar_sz = match->n_cigar; + uint32_t* orig_cigar = tmap_calloc (orig_cigar_sz, sizeof (uint32_t), "orig_cigar"); + memcpy (orig_cigar, match->cigar, match->n_cigar * sizeof (uint32_t)); + // extract seqid + unsigned ref_id = match->seqid; + // extract offset + unsigned ref_off = match->pos; + // extract read direction in alignment + uint8_t forward = (match->strand == 0) ? 1 : 0; + // make inverse/complement copy of the query if direction is reverse + if (!forward) { - case BAM_CHARD_CLIP: - schar = 'H'; - break; - case BAM_CSOFT_CLIP: // skip - schar = 'S'; - break; - case BAM_CMATCH: - schar = 'M'; - break; - case BAM_CEQUAL: - schar = '='; - break; - case BAM_CDIFF: - schar = '#'; - break; - case BAM_CINS: - schar = 'I'; - break; - case BAM_CDEL: - schar = 'I'; - break; - default: - schar = '?'; + char* qry_rev = qry_mem (realigner, qrybases->l+1); + memcpy (qry_rev, qry, qrybases->l); + qry_rev [qrybases->l] = 0; + tmap_reverse_compliment (qry_rev, (int32_t) qrybases->l); + qry = qry_rev; } - tmap_log ("%c%d", schar, count); + // compute alignment and subject lengths + unsigned q_len_cigar, r_len_cigar; + seq_lens_from_bin_cigar (orig_cigar, orig_cigar_sz, &q_len_cigar, &r_len_cigar); + uint8_t* ref = (uint8_t*) ref_mem (realigner, r_len_cigar); + int32_t converted_cnt; + // extract reference. This returns reference sequence in UNPACKED but BINARY CONVERTED form - values of 0-4, one byte per base! + tmap_refseq_subseq2 (refseq, ref_id+1, ref_off+1, ref_off + r_len_cigar, ref, 0, &converted_cnt); + // convert reference to ascii format + { + int ii; + for (ii = 0; ii < r_len_cigar; ++ii) + { + if (ref [ii] >= sizeof (tmap_iupac_int_to_char)) + tmap_bug (); + ref [ii] = tmap_iupac_int_to_char [ref [ii]]; + } + ref [r_len_cigar] = 0; + } + // get name for reporting + const char* ref_name = refseq->annos [ref_id].name->s; + // prepare variables to hold results + uint32_t* cigar_dest; + unsigned cigar_dest_sz; + unsigned new_ref_offset, new_qry_offset, new_ref_len, new_qry_len; + ++(stat->num_realign_invocations); + uint64_t apo = stat->num_realign_already_perfect; + uint64_t nco = stat->num_realign_not_clipped; + uint64_t swfo = stat->num_realign_sw_failures; + uint64_t ucfo = stat->num_realign_unclip_failures; + const char* al_proc_class = "UNKNOWN"; + // compute the alignment + uint8_t al_mod = 0; + if (realigner_compute_alignment (realigner, + qry, + qrybases->l, + (const char*) ref, + r_len_cigar, + ref_off, + forward, + orig_cigar, + orig_cigar_sz, + &cigar_dest, + &cigar_dest_sz, + &new_ref_offset, + &new_qry_offset, + &new_ref_len, + &new_qry_len, + &(stat->num_realign_already_perfect), + &(stat->num_realign_not_clipped), + &(stat->num_realign_sw_failures), + &(stat->num_realign_unclip_failures))) + { + // fix alignment end softclip - the realignment may skip some of it + int32_t sc_adj = qrybases->l - new_qry_offset - new_qry_len; + if (sc_adj) do + { + if ((cigar_dest_sz != 0) && (TMAP_SW_CIGAR_OP (cigar_dest [cigar_dest_sz - 1]) == BAM_CSOFT_CLIP)) + { + sc_adj += TMAP_SW_CIGAR_LENGTH (cigar_dest [cigar_dest_sz - 1]); + TMAP_SW_CIGAR_STORE (cigar_dest [cigar_dest_sz - 1], BAM_CSOFT_CLIP, sc_adj); + break; + } + ++cigar_dest_sz; + cigar_dest = tmap_realloc (cigar_dest, cigar_dest_sz * sizeof (*(match->cigar)), "cigar_dest"); + TMAP_SW_CIGAR_STORE (cigar_dest [cigar_dest_sz - 1], BAM_CSOFT_CLIP, sc_adj); + } + while (0); + // check if changes were introduced + if (new_ref_offset != ref_off || orig_cigar_sz != cigar_dest_sz || memcmp (orig_cigar, cigar_dest, orig_cigar_sz * sizeof (*orig_cigar))) + { + ++(stat->num_realign_changed); + if (new_ref_offset != ref_off) + { + // log shifted alignment + ++(stat->num_realign_shifted); + if (nco == stat->num_realign_not_clipped) + al_proc_class = "MOD-SHIFT"; + else + al_proc_class = "MOD-SHIFT NOCLIP"; + } + else + { + // log changed alignment + if (nco == stat->num_realign_not_clipped) + al_proc_class = "MOD"; + else + al_proc_class = "MOD NOCLIP"; + } + al_mod = 1; + // pack the alignment back into the originating structure (unallocate memory taken by the old one) + // TODO implement more intelligent alignment memory management, avoid heap operations when possible + free (match->cigar); + match->cigar = cigar_dest; + match->n_cigar = cigar_dest_sz; + match->pos = new_ref_offset; + // we need to update match->result since edge indel salvage relies on it + match->result.query_start = new_qry_offset; + match->result.query_end = new_qry_offset + new_qry_len - 1; + match->result.target_start = 0; // adjusted!new_ref_offset - ref_off; + match->result.target_end = new_ref_len - 1; + match->target_len = new_ref_len; + } + else + { + // log unchanged alignment + free (cigar_dest); + if (nco == stat->num_realign_not_clipped) + al_proc_class = "UNMOD"; + else + al_proc_class = "UNMOD NOCLIP"; + } + } + else + { + // log reason why alignment was not processed + if (swfo != stat->num_realign_sw_failures) + al_proc_class = "SWERR"; + else if (ucfo != stat->num_realign_unclip_failures) + al_proc_class = "UNCLIPERR"; + else if (apo != stat->num_realign_already_perfect) + al_proc_class = "PERFECT"; + } + // log + tmap_log_record_begin (); + tmap_log ("REALIGN: %s(%s) vs %s:%d %s. ", qryname, (forward ? "FWD":"REV"), ref_name, ref_off, al_proc_class); + if (al_mod) + { + tmap_log ("orig (%d op) at %d: ", orig_cigar_sz, ref_off); + cigar_log (orig_cigar, orig_cigar_sz); + tmap_log ("; new (%d op) at %d: ", cigar_dest_sz, new_ref_offset); + cigar_log (cigar_dest, cigar_dest_sz); + + if (log_text_als) + { + tmap_map_log_text_align (" Before realignment:\n", orig_cigar, orig_cigar_sz, qry, qrybases->l, forward, (const char*) ref, ref_off); + tmap_map_log_text_align (" After realignment:\n", match->cigar, match->n_cigar, qry, qrybases->l, forward, (const char*) (ref + (new_ref_offset - ref_off)), new_ref_offset); + } + } + tmap_log ("\n"); + tmap_log_record_end (); + free (orig_cigar); } } -typedef struct -{ - unsigned xpos; - unsigned ypos; - unsigned len; -} AlBatch; - -static uint32_t cigar_to_batches (const uint32_t* cigar, uint32_t cigar_sz, uint32_t* x_clip, AlBatch* batches, uint32_t max_batches) +void context_align_read (tmap_seq_t* qryseq, tmap_refseq_t* refseq, tmap_map_sams_t* sams, struct RealignProxy* context, tmap_map_stats_t *stat, int32_t log_text_als) { - uint32_t xpos = 0, ypos = 0; // x is query, y is reference - AlBatch* curb = batches; - const uint32_t* sent = cigar + cigar_sz; - for (; cigar != sent && curb - batches != max_batches; ++cigar) + // context_align_read (qryseq, refseq, sams) + // extract query + const char* qryname = tmap_seq_get_name (qryseq)->s; + + tmap_string_t* qrybases = tmap_seq_get_bases (qryseq); + const char* qry = qrybases->s; + uint32_t qry_len = qrybases->l; + unsigned matchidx; + for (matchidx = 0; matchidx < sams->n; ++matchidx) { - uint32_t curop = bam_cigar_op (*cigar); - uint32_t count = bam_cigar_oplen (*cigar); - switch (curop) + // extract packed cigar and it's length + tmap_map_sam_t* match = sams->sams + matchidx; + uint32_t* orig_cigar = match->cigar; + unsigned orig_cigar_sz = match->n_cigar; + // extract seqid + unsigned ref_id = match->seqid; + // extract offset + unsigned ref_off = match->pos; + // extract read direction in alignment + uint8_t forward = (match->strand == 0) ? 1 : 0; + // make inverse/complement copy of the query if direction is reverse + if (!forward) { - case BAM_CHARD_CLIP: // skip - case BAM_CSOFT_CLIP: // skip - if (x_clip) *x_clip += count; - break; - case BAM_CMATCH: - case BAM_CEQUAL: - case BAM_CDIFF: - curb->xpos = xpos; - curb->ypos = ypos; - curb->len = count; - xpos += count; - ypos += count; - ++curb; - x_clip = NULL; - break; - case BAM_CINS: - xpos += count; - x_clip = NULL; - break; - case BAM_CDEL: - ypos += count; - x_clip = NULL; - break; - default: - ; + char* qry_rev = qry_mem (context, qrybases->l+1); + memcpy (qry_rev, qry, qrybases->l); + qry_rev [qrybases->l] = 0; + tmap_reverse_compliment (qry_rev, (int32_t) qrybases->l); + qry = qry_rev; } - } - return curb - batches; -} - -#define MAXSTRLEN 256 -#define NUMSTRLEN 11 + // compute alignment and subject lengths + unsigned q_len_cigar, r_len_cigar; + seq_lens_from_bin_cigar (orig_cigar, orig_cigar_sz, &q_len_cigar, &r_len_cigar); + uint8_t* ref = (uint8_t*) ref_mem (context, r_len_cigar+1); + int32_t converted_cnt; + // extract reference. This returns reference sequence in UNPACKED but BINARY CONVERTED form - values of 0-4, one byte per base! + if (ref != tmap_refseq_subseq2 (refseq, ref_id+1, ref_off+1, ref_off + r_len_cigar, ref, 0, &converted_cnt)) + tmap_bug (); + // convert reference to ascii format + { + int ii; + for(ii = 0; ii < r_len_cigar; ++ii) + { + if (ref [ii] >= sizeof (tmap_iupac_int_to_char)) + tmap_bug (); + ref [ii] = tmap_iupac_int_to_char [ref [ii]]; + } + ref [r_len_cigar] = 0; + } + const char* ref_name = refseq->annos [ref_id].name->s; + // prepare variables to hold results + uint32_t* cigar_dest; + unsigned cigar_dest_sz; + unsigned new_ref_offset, new_qry_offset, new_ref_len, new_qry_len; + ++(stat->num_hpcost_invocations); + // compute the alignment + + if (realigner_compute_alignment (context, + qry, + qry_len, + (const char*) ref, + r_len_cigar, + ref_off, + forward, + orig_cigar, + orig_cigar_sz, + &cigar_dest, + &cigar_dest_sz, + &new_ref_offset, + &new_qry_offset, + &new_ref_len, + &new_qry_len, + NULL, + NULL, + NULL, + NULL)) + { + // check if changes were introduced + if (new_ref_offset != ref_off || orig_cigar_sz != cigar_dest_sz || memcmp (orig_cigar, cigar_dest, orig_cigar_sz*sizeof (*orig_cigar))) + { + if (tmap_log_enabled ()) + { + tmap_log_record_begin (); + tmap_log ("CONTEXT-GAP: %s(%s) vs %s:%d MODIFIED: ", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); + tmap_log ("orig (%d op) at %d: ", orig_cigar_sz, ref_off); + cigar_log (orig_cigar, orig_cigar_sz); + tmap_log ("; new (%d op) at %d: ", cigar_dest_sz, new_ref_offset); + cigar_log (cigar_dest, cigar_dest_sz); + tmap_log ("\n"); + + if (log_text_als) + { + tmap_map_log_text_align (" Before context realignment:\n", orig_cigar, orig_cigar_sz, qry, qrybases->l, forward, (const char*) ref, ref_off); + tmap_map_log_text_align (" After context realignment:\n", cigar_dest, cigar_dest_sz, qry, qrybases->l, forward, (const char*) (ref + (new_ref_offset - ref_off)), new_ref_offset); + } + tmap_log_record_end (); + } -void log_batches (const char* xseq, unsigned xlen, uint8_t xrev, const char* yseq, unsigned ylen, uint8_t yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff) -{ - const unsigned margin = 0, width = 120, zero_based = 1; + ++(stat->num_hpcost_modified); + if (new_ref_offset != ref_off) + // log shifted alignment + ++(stat->num_hpcost_shifted); + // pack the alignment back into the originating structure (unallocate memory taken by the old one) + if (orig_cigar_sz < cigar_dest_sz) + { + free (match->cigar); + match->cigar = (uint32_t*) tmap_malloc (sizeof (uint32_t) * cigar_dest_sz, "cigar_dest"); + } + match->n_cigar = cigar_dest_sz; + memcpy (match->cigar, cigar_dest, sizeof (uint32_t) * cigar_dest_sz); + sams->sams [matchidx].pos = new_ref_offset; + // we need to update match->result since edge indel salvage relies on it + match->result.query_start = new_qry_offset; + match->result.query_end = new_qry_offset + new_qry_len - 1; + match->result.target_start = 0; // adjusted!new_ref_offset - ref_off; + match->result.target_end = new_ref_len - 1; + match->target_len = new_ref_len; - unsigned slen = 0; - unsigned blen = 0; - unsigned x = b_ptr->xpos; - unsigned y = b_ptr->ypos; - unsigned xstart = x; - unsigned ystart = y; - unsigned char xc, yc; - char s[3][MAXSTRLEN]; + } + else + tmap_log_s ("CONTEXT-GAP: %s(%s) vs %s:%d UNCHANGED\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); + } + else + { + ++(stat->num_hpcost_skipped); + tmap_log_s ("CONTEXT-GAP: %s(%s) vs %s:%d SKIPPED\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); + } + } - assert (width < MAXSTRLEN); +} +const unsigned MAX_PERIOD = 10; - while (b_cnt > 0) +void tail_repeats_clip_read (tmap_seq_t* qryseq, tmap_refseq_t* refseq, tmap_map_sams_t* sams, struct RealignProxy* realigner, tmap_map_stats_t *stat, int32_t repclip_continuation) +{ + // extract query + const char* qryname = tmap_seq_get_name (qryseq)->s; + tmap_string_t* qrybases = tmap_seq_get_bases (qryseq); + const char* qry = qrybases->s; + unsigned filtered = 0; // some of the alignments could be completely skipped + + unsigned matchidx; + for (matchidx = 0; matchidx < sams->n; ++matchidx) { - xc = xseq [x]; - yc = yseq [y]; - - // special handling for (x < b_ptr->xpos && y < b_ptr->ypos) - // treating as batch with special match symbols - - if (x < b_ptr->xpos && y < b_ptr->ypos) + // if necessary, prepare slot for filling in the sam + if (filtered != matchidx) + { + // if we had rejected some of matches, the tail should shrink. (avoid reallocation, just swap all internal pointers. Otherwise it'll be too heavy, and it'll be better to skip them during BAM writing, which is logically more cumbersome) + tmap_map_sam_t tmp = sams->sams [filtered]; + sams->sams [filtered] = sams->sams [matchidx]; + sams->sams [matchidx] = tmp; + } + // extract packed cigar and it's length + uint32_t* orig_cigar = sams->sams [filtered].cigar; + unsigned orig_cigar_sz = sams->sams [filtered].n_cigar; + // extract seqid + unsigned ref_id = sams->sams [filtered].seqid; + // extract offset + unsigned ref_off = sams->sams [filtered].pos; + // extract read direction in alignment + uint8_t forward = (sams->sams [filtered].strand == 0) ? 1 : 0; + // compute alignment and subject lengths + unsigned q_al_beg, q_al_end, r_al_beg, r_al_end; + alignment_bounds_from_bin_cigar (orig_cigar, orig_cigar_sz, forward, qrybases->l, &q_al_beg, &q_al_end, &r_al_beg, &r_al_end); + // check if this read had hit the adapter (even one-base hit makes the repeat clipping invalid) + if (tmap_sam_get_zb (qryseq->data.sam) > 0 && tmap_sam_get_za (qryseq->data.sam) <= q_al_end) { - s[0][slen] = xc; - s[2][slen] = yc; - s[1][slen] = '#'; - x++, y++, slen++; + filtered ++; + continue; } - // X insert - else if (x < b_ptr->xpos) + const char* ref_name = refseq->annos [ref_id].name->s; + unsigned qry_rep_off; + if (repclip_continuation) { - s[0][slen] = xc; - s[1][slen] = ' '; - s[2][slen] = '-'; - x++, slen++; + // extract reference 'tail' (continuation). This returns reference sequence in UNPACKED but BINARY CONVERTED form - values of 0-4, one byte per base! + int32_t ref_tail_beg = forward ? (ref_off + r_al_end) : ((MAX_PERIOD < ref_off + r_al_beg) ? (ref_off + r_al_beg - MAX_PERIOD) : 0); + int32_t ref_tail_end = forward ? mymin (refseq->annos [ref_id].len, ref_tail_beg + MAX_PERIOD) : (ref_off + r_al_beg); //inclusive 1-based - same as exclusive 0-based + int32_t ref_tail_len = ref_tail_end - ref_tail_beg; + if (!ref_tail_len) + { + filtered ++; + continue; + } + uint8_t* ref = (uint8_t*) ref_mem (realigner, ref_tail_len); + int32_t converted_cnt; + tmap_refseq_subseq2 (refseq, ref_id+1, ref_tail_beg+1, ref_tail_end, ref, 0, &converted_cnt); + // convert reference to ascii format + { + int ii; + for(ii = 0; ii < ref_tail_len; ++ii) + { + if (ref [ii] >= sizeof (tmap_iupac_int_to_char)) + { + fprintf (stderr, "Invalid base: %d at position %d in reference %s:%d-%d\n", ref [ii], ii, ref_name, ref_tail_beg+1, ref_tail_end); + tmap_bug (); + } + ref [ii] = tmap_iupac_int_to_char [ref [ii]]; + } + ref [ref_tail_len] = 0; + } + // inverse/complement for reverse match + if (!forward) + tmap_reverse_compliment ((char*) ref, ref_tail_len); + // check if there is a tail periodicity extending by at least one period into reference overhang; + // tailrep returns 0 if entire (unclipped) alignment gets trimmed + qry_rep_off = tailrep_cont (ref_tail_len, (char*) ref, q_al_end, qry); } - // Y insert - else if (y < b_ptr->ypos) + else { - s[0][slen] = '-'; - s[1][slen] = ' '; - s[2][slen] = yc; - y++, slen++; + qry_rep_off = tailrep (q_al_end, qry, MAX_PERIOD); } - // emit text batch - else if (blen < b_ptr->len) + // qry_rep_off is where ON THE READ (in read's forward direction) the clip should start. + ++ stat->num_seen_tailclipped; + stat->bases_seen_tailclipped += q_al_end; + // soft-clip longest one, if any + if (qry_rep_off != q_al_end) { - s[0][slen] = xc; - s[2][slen] = yc; - s[1][slen] = (toupper (xc) == toupper (yc) || toupper (xc) == 'N' || toupper (yc) == 'N') ? '*' : ' '; - x++, y++, slen++, blen++; + // save original cigar for reporting + uint32_t* orig_cigar = alloca (sizeof (uint32_t) * orig_cigar_sz); + memcpy (orig_cigar, sams->sams [filtered].cigar, sizeof (uint32_t) * orig_cigar_sz); + unsigned ref_rep_off = 0; + unsigned trimmed_cigar_sz = trim_cigar (&(sams->sams [filtered].cigar), orig_cigar_sz, qrybases->l, qry_rep_off, forward, &ref_rep_off); + // if (trimmed_cigar_sz) + if (trimmed_cigar_sz && !fully_clipped (sams->sams [filtered].cigar, trimmed_cigar_sz)) + { + sams->sams [filtered].n_cigar = trimmed_cigar_sz; + if (!forward) + sams->sams [filtered].pos += ref_rep_off; + + tmap_log_record_begin (); + tmap_log ("TAIL_REP_TRIM: %s(%s) vs %s:%d TRIMMED %d bases. ", qryname, (forward ? "FWD":"REV"), ref_name, ref_off, q_al_end - qry_rep_off); + tmap_log ("orig (%d op) at %d: ", orig_cigar_sz, ref_off); + cigar_log (orig_cigar, orig_cigar_sz); + tmap_log ("; new (%d op) at %d: ", trimmed_cigar_sz, sams->sams [filtered].pos); + cigar_log (sams->sams [filtered].cigar, trimmed_cigar_sz); + tmap_log ("\n"); + tmap_log_record_end (); + + stat->bases_tailclipped += q_al_end - qry_rep_off; // with respect to the read + filtered ++; + } + else + { + tmap_log_s ("TAIL_REP_TRIM: %s(%s) vs %s:%d REMOVED (FULLY TRIMMED)\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); + stat->bases_tailclipped += q_al_end; + stat->bases_fully_tailclipped += q_al_end; + ++ stat->num_fully_tailclipped; + // do not increment filtered; skip this + } + ++ stat->num_tailclipped; } else - blen = 0, b_cnt--, b_ptr++; - - //print accumulated lines - if ((slen + NUMSTRLEN > width) || b_cnt <= 0) { - //null terminate all strings - for (int i = 0; i < 3; i++) - s[i][slen] = 0; + tmap_log_s ("TAIL_REP_TRIM: %s(%s) vs %s:%d UNCHANGED\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); + ++ filtered; + } + } // end alignment processing + sams->n = filtered; +} - unsigned xdisp = (xrev ? xlen - xstart - 1 : xstart) + xoff + (zero_based ? 0 : 1); - unsigned ydisp = (yrev ? ylen - ystart - 1 : ystart) + yoff + (zero_based ? 0 : 1); +void realign_reads (tmap_seqs_t *seqs_buffer, tmap_refseq_t* refseq, tmap_map_record_t* record, struct RealignProxy* realigner, tmap_map_stats_t *stat, int32_t log_text_als) +{ + unsigned seqidx; + for (seqidx = 0; seqidx < seqs_buffer->n; ++seqidx) + { + tmap_map_sams_t* sams = record->sams [seqidx]; + tmap_seq_t* qryseq = seqs_buffer->seqs [seqidx]; + realign_read (qryseq, refseq, sams, realigner, stat, log_text_als); + } +} - tmap_log ("\n%*s%*d %s", margin, "", NUMSTRLEN, xdisp, s[0]); - tmap_log ("\n%*s%*s %s", margin, "", NUMSTRLEN, "", s[1]); - tmap_log ("\n%*s%*d %s\n", margin, "", NUMSTRLEN, ydisp, s[2]); +void context_align_reads (tmap_seqs_t *seqs_buffer, tmap_refseq_t* refseq, tmap_map_record_t* record, struct RealignProxy* realigner, tmap_map_stats_t *stat, int32_t log_text_als) +{ + unsigned seqidx; + for (seqidx = 0; seqidx < seqs_buffer->n; ++seqidx) + { + tmap_map_sams_t* sams = record->sams [seqidx]; + tmap_seq_t* qryseq = seqs_buffer->seqs [seqidx]; + context_align_read (qryseq, refseq, sams, realigner, stat, log_text_als); + } +} - xstart = x, ystart = y, slen = 0; - } +void tail_repeat_clip_reads (tmap_seqs_t *seqs_buffer, tmap_refseq_t* refseq, tmap_map_record_t* record, struct RealignProxy* realigner, tmap_map_stats_t *stat, int32_t repclip_continuation) +{ + unsigned seqidx; + for (seqidx = 0; seqidx < seqs_buffer->n; ++seqidx) + { + tmap_map_sams_t* sams = record->sams [seqidx]; + tmap_seq_t* qryseq = seqs_buffer->seqs [seqidx]; + tail_repeats_clip_read (qryseq, refseq, sams, realigner, stat, repclip_continuation); } } @@ -674,755 +986,451 @@ tmap_map_driver_core_worker(sam_header_t *sam_header, int32_t do_pairing, int32_t tid) { - int32_t i, j, k, low = 0; - int32_t found; - tmap_seq_t ***seqs = NULL; - tmap_bwt_match_hash_t *hash=NULL; - int32_t max_num_ends = 0; - -#ifdef TMAP_DRIVER_USE_HASH - // init the occurence hash - hash = tmap_bwt_match_hash_init(); -#endif - - // init memory - max_num_ends = 2; - seqs = tmap_malloc(sizeof(tmap_seq_t**)*max_num_ends, "seqs"); - for(i=0;iopt->num_threads)) { - tmap_map_stats_t *stage_stat = NULL; - tmap_map_record_t *record_prev = NULL; - int32_t num_ends; - -#ifdef TMAP_DRIVER_USE_HASH -#ifdef TMAP_DRIVER_CLEAR_HASH_PER_READ - // TODO: should we hash each read, or across the thread? - tmap_bwt_match_hash_clear(hash); -#endif -#endif + int32_t i, j, k, low = 0; + int32_t found; + tmap_seq_t*** seqs = NULL; + tmap_bwt_match_hash_t* hash=NULL; + int32_t max_num_ends = 0; + + // common memory resource for all target fragments + // common memory resource for WS traceback paths + tmap_sw_path_t *path_buf = NULL; // buffer for traceback path + int32_t path_buf_sz = 0; // used portion and allocated size of traceback path. + + #ifdef TMAP_DRIVER_USE_HASH + // init the occurence hash + hash = tmap_bwt_match_hash_init (); + #endif + + // init memory + max_num_ends = 2; + seqs = tmap_malloc (sizeof (tmap_seq_t**) * max_num_ends, "seqs"); + for (i = 0; i < max_num_ends; i++) + seqs [i] = tmap_calloc (4, sizeof (tmap_seq_t*), "seqs[i]"); + + // init target sequence cache + ref_buf_t target; + target_cache_init (&target); + + // initialize thread data + tmap_map_driver_do_threads_init (driver, tid); + + // Go through the buffer + while (low < seqs_buffer_length) + { + if (tid == (low % driver->opt->num_threads)) + { + tmap_map_stats_t *stage_stat = NULL; + tmap_map_record_t *record_prev = NULL; + int32_t num_ends; + + #ifdef TMAP_DRIVER_USE_HASH + #ifdef TMAP_DRIVER_CLEAR_HASH_PER_READ + // TODO: should we hash each read, or across the thread? + tmap_bwt_match_hash_clear (hash); + #endif + #endif + + num_ends = seqs_buffer [low]->n; + if(max_num_ends < num_ends) + { + seqs = tmap_realloc (seqs, sizeof (tmap_seq_t**) * num_ends, "seqs"); + while(max_num_ends < num_ends) + { + seqs [max_num_ends] = tmap_calloc(4, sizeof(tmap_seq_t*), "seqs[max_num_ends]"); + max_num_ends++; + } + max_num_ends = num_ends; + } + // re-initialize the random seed + if(driver->opt->rand_read_name) + tmap_rand_reinit(rand, tmap_hash_str_hash_func_exc(tmap_seq_get_name(seqs_buffer[low]->seqs[0])->s, driver->opt->prefix_exclude, driver->opt->suffix_exclude)); - num_ends = seqs_buffer[low]->n; - if(max_num_ends < num_ends) { - seqs = tmap_realloc(seqs, sizeof(tmap_seq_t**)*num_ends, "seqs"); - while(max_num_ends < num_ends) { - seqs[max_num_ends] = tmap_calloc(4, sizeof(tmap_seq_t*), "seqs[max_num_ends]"); - max_num_ends++; - } - max_num_ends = num_ends; - } - // re-initialize the random seed - if(driver->opt->rand_read_name) { - tmap_rand_reinit(rand, tmap_hash_str_hash_func_exc(tmap_seq_get_name(seqs_buffer[low]->seqs[0])->s, driver->opt->prefix_exclude, driver->opt->suffix_exclude)); - } + // init + for(i = 0; i < num_ends; i++) + { + tmap_map_driver_init_seqs (seqs [i], seqs_buffer [low]->seqs [i], -1); + if (NULL != stat) stat->num_reads++; + } - // init - for(i=0;iseqs[i], -1); - if(NULL != stat) stat->num_reads++; - } + // init records + records [low] = tmap_map_record_init (num_ends); - // init records - records[low] = tmap_map_record_init(num_ends); + // go through each stage + for (i = 0; i < driver->num_stages; i++) + { // for each stage - // go through each stage - for(i=0;inum_stages;i++) { // for each stage - tmap_map_driver_stage_t *stage = driver->stages[i]; + tmap_map_driver_stage_t *stage = driver->stages [i]; + tmap_sw_param_t sw_par; + // stage may have special sw parameters + tmap_map_util_populate_sw_par (&sw_par, stage->opt); - // stage stats - stage_stat = tmap_map_stats_init(); + // stage stats + stage_stat = tmap_map_stats_init (); - // seed - for(j=0;jopt->stage_seed_max_length && stage->opt->stage_seed_max_length < tmap_seq_get_bases_length(seqs[j][0])) { - stage_seqs = tmap_calloc(4, sizeof(tmap_seq_t*), "seqs[i]"); - tmap_map_driver_init_seqs(stage_seqs, seqs_buffer[low]->seqs[i], stage->opt->stage_seed_max_length); - } - else { - stage_seqs = seqs[j]; - } - for(k=0;knum_algorithms;k++) { // for each algorithm - tmap_map_driver_algorithm_t *algorithm = stage->algorithms[k]; - tmap_map_sams_t *sams = NULL; - if(i+1 != algorithm->opt->algo_stage) { - tmap_bug(); - } - // map - sams = algorithm->func_thread_map(&algorithm->thread_data[tid], stage_seqs, index, hash, rand, algorithm->opt); - if(NULL == sams) { - tmap_error("the thread function did not return a mapping", Exit, OutOfRange); - } - // append - tmap_map_sams_merge(records[low]->sams[j], sams); - // destroy - tmap_map_sams_destroy(sams); - } - stage_stat->num_after_seeding += records[low]->sams[j]->n; - if(0 < stage->opt->stage_seed_max_length && stage->opt->stage_seed_max_length < tmap_seq_get_bases_length(seqs[j][0])) { - // free - for(j=0;j<4;j++) { - tmap_seq_destroy(stage_seqs[j]); - stage_seqs[j] = NULL; - } - } - stage_seqs = NULL; // do not use - } + // seed + for (j = 0; j < num_ends; j++) + { // for each end + tmap_seq_t** stage_seqs = NULL; + // should we seed using the whole read? + if(0 < stage->opt->stage_seed_max_length && stage->opt->stage_seed_max_length < tmap_seq_get_bases_length (seqs [j][0])) + { + stage_seqs = tmap_calloc (4, sizeof (tmap_seq_t*), "seqs[i]"); + tmap_map_driver_init_seqs (stage_seqs, seqs_buffer [low]->seqs [i], stage->opt->stage_seed_max_length); + } + else + stage_seqs = seqs [j]; + for (k = 0; k < stage->num_algorithms; k++) + { // for each algorithm + tmap_map_driver_algorithm_t *algorithm = stage->algorithms [k]; + tmap_map_sams_t *sams = NULL; + if (i + 1 != algorithm->opt->algo_stage) + tmap_bug(); + // map + sams = algorithm->func_thread_map (&algorithm->thread_data [tid], stage_seqs, index, hash, rand, algorithm->opt); + if (NULL == sams) + tmap_error ("the thread function did not return a mapping", Exit, OutOfRange); + // append + tmap_map_sams_merge (records [low]->sams [j], sams); + // destroy + tmap_map_sams_destroy (sams); + } + stage_stat->num_after_seeding += records [low]->sams [j]->n; + if (0 < stage->opt->stage_seed_max_length && stage->opt->stage_seed_max_length < tmap_seq_get_bases_length (seqs [j][0])) + { + // free + for (j = 0; j < 4; j++) + { + tmap_seq_destroy (stage_seqs [j]); + stage_seqs [j] = NULL; + } + } + stage_seqs = NULL; // do not use + } - // keep mappings for subsequent stages or restore mappings from - // previous stages - if(1 == stage->opt->stage_keep_all) { - // merge from the previous stage - if(0 < i) { - tmap_map_record_merge(records[low], record_prev); - // destroy the record - tmap_map_record_destroy(record_prev); - record_prev = NULL; - } + // keep mappings for subsequent stages or restore mappings from + // previous stages + if (1 == stage->opt->stage_keep_all) + { + // merge from the previous stage + if (0 < i) + { + tmap_map_record_merge (records [low], record_prev); + // destroy the record + tmap_map_record_destroy (record_prev); + record_prev = NULL; + } - // keep for the next stage - if(i < driver->num_stages-1) { // more stages left - record_prev = tmap_map_record_clone(records[low]); - } - } + // keep for the next stage + if (i < driver->num_stages - 1) // more stages left + record_prev = tmap_map_record_clone (records [low]); + } - // generate scores with smith waterman - for(j=0;jsams[j] = tmap_map_util_sw_gen_score(index->refseq, seqs_buffer[low]->seqs[j], records[low]->sams[j], seqs[j], rand, stage->opt, &k); - stage_stat->num_after_scoring += records[low]->sams[j]->n; - stage_stat->num_after_grouping += k; - } + // generate scores with smith waterman + for (j = 0; j < num_ends; j++) + { // for each end + records [low]->sams [j] = tmap_map_util_sw_gen_score (index->refseq, seqs_buffer [low]->seqs [j], records [low]->sams [j], seqs [j], rand, stage->opt, &k); + stage_stat->num_after_scoring += records [low]->sams [j]->n; + stage_stat->num_after_grouping += k; + } - // remove duplicates - for(j=0;jsams[j], stage->opt->dup_window, rand); - stage_stat->num_after_rmdup += records[low]->sams[j]->n; - } + // remove duplicates + for (j = 0; j < num_ends; j++) + { // for each end + tmap_map_util_remove_duplicates (records [low]->sams [j], stage->opt->dup_window, rand); + stage_stat->num_after_rmdup += records [low]->sams [j]->n; + } - // (single-end) mapping quality - for(j=0;jfunc_mapq(records[low]->sams[j], tmap_seq_get_bases_length(seqs[j][0]), stage->opt, index->refseq); - } + // (single-end) mapping quality + for (j = 0;j < num_ends; j++) // for each end + driver->func_mapq (records [low]->sams [j], tmap_seq_get_bases_length (seqs [j][0]), stage->opt, index->refseq); - // filter if we have more stages - if(i < driver->num_stages-1) { - for(j=0;jsams[j], stage->opt->stage_score_thr, stage->opt->stage_mapq_thr); - } - } - if(0 == do_pairing && 0 <= driver->opt->strandedness && 0 <= driver->opt->positioning - && 2 == num_ends && 0 < records[low]->sams[0]->n && 0 < records[low]->sams[1]->n) { // pairs of reads! + // filter if we have more stages + if (i < driver->num_stages-1) + { + for (j = 0; j < num_ends; j++) // for each end + tmap_map_sams_filter2 (records [low]->sams [j], stage->opt->stage_score_thr, stage->opt->stage_mapq_thr); + } - // read rescue - if(1 == stage->opt->read_rescue) { - int32_t flag = tmap_map_pairing_read_rescue(index->refseq, - seqs_buffer[low]->seqs[0], seqs_buffer[low]->seqs[1], - records[low]->sams[0], records[low]->sams[1], - seqs[0], seqs[1], + if (0 == do_pairing && 0 <= driver->opt->strandedness && 0 <= driver->opt->positioning + && 2 == num_ends && 0 < records [low]->sams [0]->n && 0 < records [low]->sams [1]->n) + { // pairs of reads! + // read rescue + if (1 == stage->opt->read_rescue) + { + int32_t flag = tmap_map_pairing_read_rescue (index->refseq, + seqs_buffer [low]->seqs[0], seqs_buffer [low]->seqs [1], + records [low]->sams [0], records [low]->sams [1], + seqs [0], seqs [1], rand, stage->opt); - // recalculate mapping qualities if necessary - if(0 < (flag & 0x1)) { // first end was rescued - //fprintf(stderr, "re-doing mapq for end #1\n"); - driver->func_mapq(records[low]->sams[0], tmap_seq_get_bases_length(seqs[0][0]), stage->opt, index->refseq); - } - if(0 < (flag & 0x2)) { // second end was rescued - //fprintf(stderr, "re-doing mapq for end #2\n"); - driver->func_mapq(records[low]->sams[1], tmap_seq_get_bases_length(seqs[1][0]), stage->opt, index->refseq); - } - } - // pick pairs - tmap_map_pairing_pick_pairs(records[low]->sams[0], records[low]->sams[1], - seqs[0][0], seqs[1][0], + // recalculate mapping qualities if necessary + if (0 < (flag & 0x1)) // first end was rescued + driver->func_mapq (records [low]->sams [0], tmap_seq_get_bases_length (seqs [0][0]), stage->opt, index->refseq); + if(0 < (flag & 0x2)) // second end was rescued + driver->func_mapq (records [low]->sams [1], tmap_seq_get_bases_length (seqs [1][0]), stage->opt, index->refseq); + } + // pick pairs + tmap_map_pairing_pick_pairs (records [low]->sams [0], records [low]->sams [1], + seqs [0][0], seqs [1][0], rand, stage->opt); - // TODO: if we have one end for a pair, do we go onto the second - // stage? - } - else { - // choose alignments - for(j=0;jsams[j], stage->opt->aln_output_mode, TMAP_MAP_ALGO_NONE, rand); - stage_stat->num_after_filter += records[low]->sams[j]->n; - } - } - - // generate the cigars - found = 0; - for(j=0;jsams[j] = tmap_map_util_sw_gen_cigar(index->refseq, records[low]->sams[j], seqs_buffer[low]->seqs[j], seqs[j], stage->opt); - if(0 < records[low]->sams[j]->n) { - stage_stat->num_with_mapping++; - found = 1; - } - } - - // TODO - // if paired, update pairing score based on target start? - - // did we find any mappings? - if(1 == found) { // yes - if(NULL != stat) { - tmap_map_stats_add(stat, stage_stat); - } - tmap_map_stats_destroy(stage_stat); - break; - } - else { // no - tmap_map_record_destroy(records[low]); - // re-init - records[low] = tmap_map_record_init(num_ends); - } - tmap_map_stats_destroy(stage_stat); - } - - // flowspace re-align and sorting - if(1 == driver->opt->aln_flowspace) { - for(i=0;isams[i]->n) { - // re-align the alignments in flow-space - tmap_seq_t *seq = seqs_buffer[low]->seqs[i]; - // TODO: if this is run, we do not need to run tmap_sw_global_banded_core... - // NB: seqs_buffer should have its key sequence if 0 < key_seq_len - if(1 == tmap_map_util_fsw(seq, records[low]->sams[i], index->refseq, - driver->opt->bw, driver->opt->softclip_type, driver->opt->score_thr, - driver->opt->score_match, driver->opt->pen_mm, driver->opt->pen_gapo, - driver->opt->pen_gape, driver->opt->fscore, 1-driver->opt->ignore_flowgram)) { - // sort by alignment score - if(1 < records[low]->sams[i]->n) { - tmap_sort_introsort(tmap_map_sam_sort_score, - records[low]->sams[i]->n, - records[low]->sams[i]->sams); - } - } - } - if(NULL == seqs_buffer[low]->seqs[i]) { - tmap_error("bug encoutereed", Exit, OutOfRange); - } - } - } - - // only convert to BAM and destroy the records if we are not trying to - // estimate the pairing parameters - if(0 == do_pairing) - { - { // DVK - realignment integration - // run realignment on all matches from records [low]->sams [II] for II from 0 to seqs_buffer[low]->n - // ? should the realignment be run if flowspace realignment is performed ? - yes, if both are requested from command line - if (1 == driver->opt->do_realign) + } + else { - unsigned seqidx, matchidx; - for (seqidx = 0; seqidx < seqs_buffer[low]->n; ++seqidx) + // choose alignments + for (j = 0; j < num_ends; ++j) + { // for each end + tmap_map_sams_filter1 (records [low]->sams [j], stage->opt->aln_output_mode, TMAP_MAP_ALGO_NONE, rand); + stage_stat->num_after_filter += records [low]->sams [j]->n; + } + } + + // generate and post-process alignments + found = 0; + for (j = 0; j < num_ends; ++j) // for each end + { + tmap_map_sams_t* sams = records [low]->sams [j]; + tmap_seq_t* seq = seqs_buffer [low]->seqs [j]; + + //if (0 == strcmp (seq->data.sam->name->s, "K0BK3:04849:06835")) + // tmap_progress_print2 ("here"); + tmap_seq_t** seq_variants = seqs [j]; + // if there are no mappings, continue + if (!sams->n) + continue; + // find alignment starqts + sams = records [low]->sams [j] = tmap_map_util_find_align_starts + ( + index->refseq, // reference server + sams, // initial rough mapping + seq, // read + seq_variants, // array of size 4 that contains pre-computed inverse / complement combinations + stage->opt, // stage parameters + &target, // target cache control structure + stat + ); + if (!sams->n) + continue; + // reference alignment + tmap_map_util_align + ( + index->refseq, // reference server + sams, // mappings to compute alignments for + seq_variants, // array of size 4 that contains pre-computed inverse / complement combinations + &target, // target cache control structure + &path_buf, // buffer for traceback path + &path_buf_sz, // used portion and allocated size of traceback path. + &sw_par, // Smith-Waterman scoring parameters + stat + ); + // realign in flowspace or with context, if requested + if (driver->opt->aln_flowspace) + // NB: seqs_buffer should have its key sequence if 0 < key_seq_len + tmap_map_util_fsw + ( + seq, + sams, + index->refseq, + driver->opt->bw, + driver->opt->softclip_type, + driver->opt->score_thr, + driver->opt->score_match, + driver->opt->pen_mm, + driver->opt->pen_gapo, + driver->opt->pen_gape, + driver->opt->fscore, + 1 - driver->opt->ignore_flowgram, + stat + ); + else if (driver->opt->do_hp_weight) + context_align_read + ( + seq, + index->refseq, + sams, + context, + stat, + driver->opt->log_text_als + ); + // perform anti-dyslexic realignment if enabled + if (driver->opt->do_realign) { - tmap_map_sams_t* sams = records[low]->sams[seqidx]; - tmap_seq_t* qryseq = seqs_buffer[low]->seqs[seqidx]; - tmap_refseq_t* refseq = index->refseq; - - // extract query - const char* qryname = tmap_seq_get_name (qryseq)->s; - tmap_string_t* qrybases = tmap_seq_get_bases (qryseq); - const char* qry = qrybases->s; - for (matchidx = 0; matchidx < sams->n; ++matchidx) + realign_read + ( + seq, + index->refseq, + sams, + realigner, + stat, + driver->opt->log_text_als + ); + } + // salvage ends + if (0 < driver->opt->pen_gapl) + tmap_map_util_salvage_edge_indels + ( + index->refseq, // reference server + sams, // mappings to compute alignments for + seq_variants, // array of size 4 that contains pre-computed inverse / complement combinations + stage->opt, // tmap parameters + &sw_par, // Smith-Waterman scoring parameters + &target, // target cache control structure + &path_buf, // buffer for traceback path + &path_buf_sz, // used portion and allocated size of traceback path. + stat + ); + // trim key + if (1 == stage->opt->softclip_key) + tmap_map_util_trim_key + ( + sams, // mappings to compute alignments for + seq, // read + seq_variants, // array of size 4 that contains pre-computed inverse / complement combinations + index->refseq, // reference server + &target, // target cache control structure + stat + ); + // end repair + if (stage->opt->end_repair) + tmap_map_util_end_repair_bulk + ( + index->refseq, // reference server + sams, // mappings to compute alignments for + seq, // read + seq_variants, // array of size 4 that contains pre-computed inverse / complement combinations + stage->opt, // tmap parameters + &target, // target cache control structure + &path_buf, // buffer for traceback path + &path_buf_sz, // used portion and allocated size of traceback path. + stat + ); + // explicitly fix 5' softclip is required + if (!stage->opt->end_repair_5_prime_softclip && (stage->opt->softclip_type == 2 || stage->opt->softclip_type == 3)) + { + int i; + for (i = 0; i != sams->n; ++i) { - // extract packed cigar and it's length - uint32_t* orig_cigar = sams->sams [matchidx].cigar; - unsigned orig_cigar_sz = sams->sams [matchidx].n_cigar; - // extract seqid - unsigned ref_id = sams->sams [matchidx].seqid; - // extract offset - unsigned ref_off = sams->sams [matchidx].pos; - // extract read direction in alignment - uint8_t forward = (sams->sams [matchidx].strand == 0) ? 1 : 0; - // make inverse/complement copy of the query if direction is reverse - if (!forward) - { - char* qry_rev = qry_mem (realigner, qrybases->l+1); - memcpy (qry_rev, qry, qrybases->l); - qry_rev [qrybases->l] = 0; - tmap_reverse_compliment (qry_rev, (int32_t) qrybases->l); - qry = qry_rev; - } - // compute alignment and subject lengths - unsigned q_len_cigar, r_len_cigar; - seq_lens_from_bin_cigar (orig_cigar, orig_cigar_sz, &q_len_cigar, &r_len_cigar); - uint8_t* ref = (uint8_t*) ref_mem (realigner, r_len_cigar); - int32_t converted_cnt; - // extract reference. This returns reference sequence in UNPACKED but BINARY CONVERTED form - values of 0-4, one byte per base! - tmap_refseq_subseq2 (refseq, ref_id+1, ref_off+1, ref_off + r_len_cigar, ref, 0, &converted_cnt); - // convert reference to ascii format - { - int ii; - for(ii = 0; ii < r_len_cigar; ++ii) - { - if (ref [ii] >= sizeof (tmap_iupac_int_to_char)) - tmap_bug (); - ref [ii] = tmap_iupac_int_to_char [ref [ii]]; - } - ref [r_len_cigar] = 0; - } - // get name for reporting - const char* ref_name = refseq->annos [ref_id].name->s; - // prepare variables to hold results - uint32_t* cigar_dest; - unsigned cigar_dest_sz; - int new_offset; - ++(stat->num_realign_invocations); - uint64_t apo = stat->num_realign_already_perfect; - uint64_t nco = stat->num_realign_not_clipped; - uint64_t swfo = stat->num_realign_sw_failures; - uint64_t ucfo = stat->num_realign_unclip_failures; - const char* al_proc_class = "UNKNOWN"; - // compute the alignment - uint8_t al_mod = 0; - if (realigner_compute_alignment (realigner, - qry, - qrybases->l, - (const char*) ref, - r_len_cigar, - ref_off, - forward, - orig_cigar, - orig_cigar_sz, - &cigar_dest, - &cigar_dest_sz, - &new_offset, - &(stat->num_realign_already_perfect), - &(stat->num_realign_not_clipped), - &(stat->num_realign_sw_failures), - &(stat->num_realign_unclip_failures))) - { - // check if changes were introduced - if (new_offset != ref_off || orig_cigar_sz != cigar_dest_sz || memcmp (orig_cigar, cigar_dest, orig_cigar_sz*sizeof(*orig_cigar))) - { - ++(stat->num_realign_changed); - if (new_offset != ref_off) - { - // log shifted alignment - ++(stat->num_realign_shifted); - if (nco == stat->num_realign_not_clipped) - al_proc_class = "MOD-SHIFT"; - else - al_proc_class = "MOD-SHIFT NOCLIP"; - } - else - { - // log changed alignment - if (nco == stat->num_realign_not_clipped) - al_proc_class = "MOD"; - else - al_proc_class = "MOD NOCLIP"; - } - al_mod = 1; - // pack the alignment back into the originating structure (unallocate memory taken by the old one) - // TODO implement more intelligent alignment memory management, avoid heap operations when possible - free (sams->sams [matchidx].cigar); - sams->sams [matchidx].cigar = cigar_dest; - sams->sams [matchidx].n_cigar = cigar_dest_sz; - sams->sams [matchidx].pos = new_offset; - } - else - { - // log unchanged alignment - free (cigar_dest); - if (nco == stat->num_realign_not_clipped) - al_proc_class = "UNMOD"; - else - al_proc_class = "UNMOD NOCLIP"; - } - } - else - { - // log reason why alignment was not processed - if (swfo != stat->num_realign_sw_failures) - al_proc_class = "SWERR"; - else if (ucfo != stat->num_realign_unclip_failures) - al_proc_class = "UNCLIPERR"; - else if (apo != stat->num_realign_already_perfect) - al_proc_class = "PERFECT"; - } - // log - tmap_log ("REALIGN: %s(%s) vs %s:%d %s. ", qryname, (forward ? "FWD":"REV"), ref_name, ref_off, al_proc_class); - if (al_mod) - { - tmap_log ("orig (%d op) at %d: ", orig_cigar_sz, ref_off); - cigar_log (orig_cigar, orig_cigar_sz); - tmap_log ("; new (%d op) at %d: ", cigar_dest_sz, new_offset); - cigar_log (cigar_dest, cigar_dest_sz); - - if (driver->opt->log_text_als) - { - const unsigned MAX_BATCHNO = 100; - AlBatch batches [MAX_BATCHNO]; - uint32_t q_clip = 0; - int bno = cigar_to_batches (orig_cigar, orig_cigar_sz, &q_clip, batches, MAX_BATCHNO); - // (const char* xseq, unsigned xlen, bool xrev, const char* yseq, unsigned ylen, bool yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff, unsigned margin = 0, unsigned width = 120, bool zero_based = 1) - tmap_log ("\n Before realignment:\n"); - log_batches (qry+q_clip, qrybases->l-q_clip, !forward, (const char*) ref, r_len_cigar, 0, batches, bno, q_clip, ref_off); - - uint32_t q_len_dest, r_len_dest; - seq_lens_from_bin_cigar (cigar_dest, cigar_dest_sz, &q_len_dest, &r_len_dest); - q_clip = 0; - bno = cigar_to_batches (cigar_dest, cigar_dest_sz, &q_clip, batches, MAX_BATCHNO); - // (const char* xseq, unsigned xlen, bool xrev, const char* yseq, unsigned ylen, bool yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff, unsigned margin = 0, unsigned width = 120, bool zero_based = 1) - tmap_log (" After realignment:\n"); - log_batches (qry+q_clip, qrybases->l-q_clip, !forward, (const char*) ref, r_len_dest, 0, batches, bno, q_clip, new_offset); - } - } - tmap_log ("\n"); + tmap_map_util_remove_5_prime_softclip + ( + index->refseq, + sams->sams + i, + seq, + seq_variants, + &target, + &path_buf, + &path_buf_sz, + &sw_par, + stage->opt, + stat + ); } } - } - } // end realignment integration - { // DVK - context-specific realignment integration - // run realignment on all matches from records [low]->sams [II] for II from 0 to seqs_buffer[low]->n - if (1 == driver->opt->do_hp_weight) - { - unsigned seqidx, matchidx; - for (seqidx = 0; seqidx < seqs_buffer[low]->n; ++seqidx) + if (stage->opt->cigar_sanity_check) // do this before tail repeat clipping as the latter does not update alignment box. TODO: update box in tail clip and move this to the very end of alignment post processing { - tmap_map_sams_t* sams = records[low]->sams[seqidx]; - tmap_seq_t* qryseq = seqs_buffer[low]->seqs[seqidx]; - tmap_refseq_t* refseq = index->refseq; - // extract query - const char* qryname = tmap_seq_get_name (qryseq)->s; - - tmap_string_t* qrybases = tmap_seq_get_bases (qryseq); - const char* qry = qrybases->s; - for (matchidx = 0; matchidx < sams->n; ++matchidx) + int i; + for (i = 0; i != sams->n; ++i) { - // extract packed cigar and it's length - uint32_t* orig_cigar = sams->sams [matchidx].cigar; - unsigned orig_cigar_sz = sams->sams [matchidx].n_cigar; - // extract seqid - unsigned ref_id = sams->sams [matchidx].seqid; - // extract offset - unsigned ref_off = sams->sams [matchidx].pos; - // extract read direction in alignment - uint8_t forward = (sams->sams [matchidx].strand == 0) ? 1 : 0; - // make inverse/complement copy of the query if direction is reverse - if (!forward) - { - char* qry_rev = qry_mem (realigner, qrybases->l+1); - memcpy (qry_rev, qry, qrybases->l); - qry_rev [qrybases->l] = 0; - tmap_reverse_compliment (qry_rev, (int32_t) qrybases->l); - qry = qry_rev; - } - // compute alignment and subject lengths - unsigned q_len_cigar, r_len_cigar; - seq_lens_from_bin_cigar (orig_cigar, orig_cigar_sz, &q_len_cigar, &r_len_cigar); - uint8_t* ref = (uint8_t*) ref_mem (realigner, r_len_cigar+1); - int32_t converted_cnt; - // extract reference. This returns reference sequence in UNPACKED but BINARY CONVERTED form - values of 0-4, one byte per base! - tmap_refseq_subseq2 (refseq, ref_id+1, ref_off+1, ref_off + r_len_cigar, ref, 0, &converted_cnt); - // convert reference to ascii format - { - int ii; - for(ii = 0; ii < r_len_cigar; ++ii) - { - if (ref [ii] >= sizeof (tmap_iupac_int_to_char)) - tmap_bug (); - ref [ii] = tmap_iupac_int_to_char [ref [ii]]; - } - ref [r_len_cigar] = 0; - } - const char* ref_name = refseq->annos [ref_id].name->s; - // prepare variables to hold results - uint32_t* cigar_dest; - unsigned cigar_dest_sz; - int new_offset; - ++(stat->num_hpcost_invocations); - // compute the alignment - - if (realigner_compute_alignment (context, - qry, - qrybases->l, - (const char*) ref, - r_len_cigar, - ref_off, - forward, - orig_cigar, - orig_cigar_sz, - &cigar_dest, - &cigar_dest_sz, - &new_offset, - NULL, - NULL, - NULL, - NULL)) - { - // check if changes were introduced - if (new_offset != ref_off || orig_cigar_sz != cigar_dest_sz || memcmp (orig_cigar, cigar_dest, orig_cigar_sz*sizeof (*orig_cigar))) - { - if (tmap_log_enabled ()) - { - tmap_log ("CONTEXT-GAP: %s(%s) vs %s:%d MODIFIED: ", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); - tmap_log ("orig (%d op) at %d: ", orig_cigar_sz, ref_off); - cigar_log (orig_cigar, orig_cigar_sz); - tmap_log ("; new (%d op) at %d: ", cigar_dest_sz, new_offset); - cigar_log (cigar_dest, cigar_dest_sz); - tmap_log ("\n"); - - if (driver->opt->log_text_als) - { - const unsigned MAX_BATCHNO = 100; - AlBatch batches [MAX_BATCHNO]; - uint32_t q_clip = 0; - int bno = cigar_to_batches (orig_cigar, orig_cigar_sz, &q_clip, batches, MAX_BATCHNO); - // (const char* xseq, unsigned xlen, bool xrev, const char* yseq, unsigned ylen, bool yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff, unsigned margin = 0, unsigned width = 120, bool zero_based = 1) - tmap_log (" Before context realignment:\n"); - log_batches (qry+q_clip, qrybases->l-q_clip, !forward, (const char*) ref, r_len_cigar, 0, batches, bno, q_clip, ref_off); - - - uint32_t q_len_dest, r_len_dest; - seq_lens_from_bin_cigar (cigar_dest, cigar_dest_sz, &q_len_dest, &r_len_dest); - q_clip = 0; - bno = cigar_to_batches (cigar_dest, cigar_dest_sz, &q_clip, batches, MAX_BATCHNO); - // (const char* xseq, unsigned xlen, bool xrev, const char* yseq, unsigned ylen, bool yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff, unsigned margin = 0, unsigned width = 120, bool zero_based = 1) - tmap_log (" After context realignment:\n"); - log_batches (qry+q_clip, qrybases->l-q_clip, !forward, (const char*) ref, r_len_dest, 0, batches, bno, q_clip, new_offset); - } - } - - ++(stat->num_hpcost_modified); - if (new_offset != ref_off) - // log shifted alignment - ++(stat->num_hpcost_shifted); - // pack the alignment back into the originating structure (unallocate memory taken by the old one) - if (orig_cigar_sz < cigar_dest_sz) - { - free (sams->sams [matchidx].cigar); - sams->sams [matchidx].cigar = (uint32_t*) tmap_malloc (sizeof (uint32_t) * cigar_dest_sz, "cigar_dest"); - } - sams->sams [matchidx].n_cigar = cigar_dest_sz; - memcpy (sams->sams [matchidx].cigar, cigar_dest, sizeof (uint32_t) * cigar_dest_sz); - sams->sams [matchidx].pos = new_offset; - } - else - tmap_log ("CONTEXT-GAP: %s(%s) vs %s:%d UNCHANGED\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); - } - else - { - ++(stat->num_hpcost_skipped); - tmap_log ("CONTEXT-GAP: %s(%s) vs %s:%d SKIPPED\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); - } + cigar_sanity_check + ( + index->refseq, + sams->sams + i, + seq, + seq_variants, + &target, + stage->opt + ); } } + // clip repeats + if (driver->opt->do_repeat_clip) + tail_repeats_clip_read + ( + seq, + index->refseq, + sams, + realigner, + stat, + driver->opt->repclip_continuation + ); + + stage_stat->num_with_mapping++; + found = 1; } - } // end context-specific realignment integration - { // DVK soft-clipping tail tandem repeats - if (1 == driver->opt->do_repeat_clip) - { - const unsigned MAX_PERIOD = 10; + // TODO + // if paired, update pairing score based on target start? - // detect the repeat (using naive NxM algorithm) - unsigned seqidx, matchidx; - for (seqidx = 0; seqidx < seqs_buffer[low]->n; ++seqidx) - { - tmap_map_sams_t* sams = records[low]->sams[seqidx]; - tmap_seq_t* qryseq = seqs_buffer[low]->seqs[seqidx]; - tmap_refseq_t* refseq = index->refseq; - - // extract query - const char* qryname = tmap_seq_get_name (qryseq)->s; - tmap_string_t* qrybases = tmap_seq_get_bases (qryseq); - const char* qry = qrybases->s; - unsigned filtered = 0; // some of the alignments could be completely skipped + // did we find any mappings? + if (found) + { // yes + if (stat) + tmap_map_stats_add (stat, stage_stat); + tmap_map_stats_destroy (stage_stat); + break; + } + else + { // no + tmap_map_record_destroy (records [low]); + // re-init + records [low] = tmap_map_record_init (num_ends); + } + tmap_map_stats_destroy (stage_stat); + } - for (matchidx = 0; matchidx < sams->n; ++matchidx) - { - // if necessary, prepare slot for filling in the sam - if (filtered != matchidx) - { - // if we had rejected some of matches, the tail should shrink. (avoid reallocation, just swap all internal pointers. Otherwise it'll be too heavy, and it'll be better to skip them during BAM writing, which is logically more cumbersome) - //fprintf (stderr, "filtered == matchidx! This should not happen while bugcheck is in effect!\n"); - //tmap_bug (); - tmap_map_sam_t tmp = sams->sams [filtered]; - sams->sams [filtered] = sams->sams [matchidx]; - sams->sams [matchidx] = tmp; - } - // extract packed cigar and it's length - uint32_t* orig_cigar = sams->sams [filtered].cigar; - unsigned orig_cigar_sz = sams->sams [filtered].n_cigar; - // extract seqid - unsigned ref_id = sams->sams [filtered].seqid; - // extract offset - unsigned ref_off = sams->sams [filtered].pos; - // extract read direction in alignment - uint8_t forward = (sams->sams [filtered].strand == 0) ? 1 : 0; - // make inverse/complement copy of the query if direction is reverse - // FIX: NO REVERSING OF QUERY! this was a wrong logic. The read should stay as is, - /// but the reference should be extracted for the ACTUAL tail of the read, - // which in case of reverse match located at the mapped location + lenght_of_5'_soft_clip - /* - if (!forward) - { - char* qry_rev = qry_mem (realigner, qrybases->l+1); - memcpy (qry_rev, qry, qrybases->l); - qry_rev [qrybases->l] = 0; - tmap_reverse_compliment (qry_rev, (int32_t) qrybases->l); - qry = qry_rev; - } - */ - // compute alignment and subject lengths - unsigned q_al_beg, q_al_end, r_al_beg, r_al_end; - alignment_bounds_from_bin_cigar (orig_cigar, orig_cigar_sz, forward, qrybases->l, &q_al_beg, &q_al_end, &r_al_beg, &r_al_end); - // check if this read had hit the adapter (even one-base hit makes the repeat clipping invalid) - if (tmap_sam_get_zb (qryseq->data.sam) > 0 && tmap_sam_get_za (qryseq->data.sam) <= q_al_end) - { - filtered ++; - continue; - } - const char* ref_name = refseq->annos [ref_id].name->s; - unsigned qry_rep_off; - if (driver->opt->repclip_continuation) - { - // extract reference 'tail' (continuation). This returns reference sequence in UNPACKED but BINARY CONVERTED form - values of 0-4, one byte per base! - int32_t ref_tail_beg = forward ? (ref_off + r_al_end) : ((MAX_PERIOD < ref_off + r_al_beg) ? (ref_off + r_al_beg - MAX_PERIOD) : 0); - int32_t ref_tail_end = forward ? mymin (refseq->annos [ref_id].len, ref_tail_beg + MAX_PERIOD) : (ref_off + r_al_beg); //inclusive 1-based - same as exclusive 0-based - int32_t ref_tail_len = ref_tail_end - ref_tail_beg; - if (!ref_tail_len) - { - filtered ++; - continue; - } - uint8_t* ref = (uint8_t*) ref_mem (realigner, ref_tail_len); - int32_t converted_cnt; - tmap_refseq_subseq2 (refseq, ref_id+1, ref_tail_beg+1, ref_tail_end, ref, 0, &converted_cnt); - // convert reference to ascii format - { - int ii; - for(ii = 0; ii < ref_tail_len; ++ii) - { - if (ref [ii] >= sizeof (tmap_iupac_int_to_char)) - { - fprintf (stderr, "Invalid base: %d at position %d in reference %s:%d-%d; query is %d, match is %u\n", ref [ii], ii, ref_name, ref_tail_beg+1, ref_tail_end, low, matchidx); - tmap_bug (); - } - ref [ii] = tmap_iupac_int_to_char [ref [ii]]; - } - ref [ref_tail_len] = 0; - } - // inverse/complement for reverse match - if (!forward) - tmap_reverse_compliment ((char*) ref, ref_tail_len); - // check if there is a tail periodicity extending by at least one period into reference overhang; - // tailrep returns 0 if entire (unclipped) alignment gets trimmed - qry_rep_off = tailrep_cont (ref_tail_len, (char*) ref, q_al_end, qry); - } - else - { - qry_rep_off = tailrep (q_al_end, qry, MAX_PERIOD); - } - // qry_rep_off is where ON THE READ (in read's forward direction) the clip should start. - ++ stat->num_seen_tailclipped; - stat->bases_seen_tailclipped += q_al_end; - // soft-clip longest one, if any - if (qry_rep_off != q_al_end) - { - // save original cigar for reporting - uint32_t* orig_cigar = alloca (sizeof (uint32_t) * orig_cigar_sz); - memcpy (orig_cigar, sams->sams [filtered].cigar, sizeof (uint32_t) * orig_cigar_sz); - unsigned ref_rep_off = 0; - unsigned trimmed_cigar_sz = trim_cigar (&(sams->sams [filtered].cigar), orig_cigar_sz, qrybases->l, qry_rep_off, forward, &ref_rep_off); - if (trimmed_cigar_sz) - { - sams->sams [filtered].n_cigar = trimmed_cigar_sz; - if (!forward) - // sams->sams [filtered].pos += q_al_end - qry_rep_off; - BUG (TS-11736): query may be imperfectly aligned to ref in repeat zone, causing wrong coords - sams->sams [filtered].pos += ref_rep_off; - - tmap_log ("TAIL_REP_TRIM: %s(%s) vs %s:%d TRIMMED %d bases. ", qryname, (forward ? "FWD":"REV"), ref_name, ref_off, q_al_end - qry_rep_off); - tmap_log ("orig (%d op) at %d: ", orig_cigar_sz, ref_off); - cigar_log (orig_cigar, orig_cigar_sz); - tmap_log ("; new (%d op) at %d: ", trimmed_cigar_sz, sams->sams [filtered].pos); - cigar_log (sams->sams [filtered].cigar, trimmed_cigar_sz); - tmap_log ("\n"); - - stat->bases_tailclipped += q_al_end - qry_rep_off; // with respect to the read - filtered ++; - } - else - { - tmap_log ("TAIL_REP_TRIM: %s(%s) vs %s:%d REMOVED (FULLY TRIMMED)\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); - stat->bases_tailclipped += q_al_end; - stat->bases_fully_tailclipped += q_al_end; - ++ stat->num_fully_tailclipped; - // do not increment filtered; skip this - } - ++ stat->num_tailclipped; - } - else - { - tmap_log ("TAIL_REP_TRIM: %s(%s) vs %s:%d UNCHANGED\n", qryname, (forward ? "FWD":"REV"), ref_name, ref_off); - ++ filtered; - } - } // end alignment processing - sams->n = filtered; - } // end read processing - } // endif driver->opt->do_repeat_clip - } // end tail repeat clipping scope - - // convert the record to bam - if (1 == seqs_buffer [low]->n) - { - bams [low] = tmap_map_bams_init (1); - bams [low]->bams [0] = tmap_map_sams_print (seqs_buffer [low]->seqs [0], index->refseq, records [low]->sams [0], + // only convert to BAM and destroy the records if we are not trying to + // estimate the pairing parameters + if (0 == do_pairing) + { + // convert the record to bam + if (1 == seqs_buffer [low]->n) + { + bams [low] = tmap_map_bams_init (1); + bams [low]->bams [0] = tmap_map_sams_print (seqs_buffer [low]->seqs [0], index->refseq, records [low]->sams [0], 0, NULL, driver->opt->sam_flowspace_tags, driver->opt->bidirectional, driver->opt->seq_eq, driver->opt->min_al_len, driver->opt->min_al_cov, driver->opt->min_identity, driver->opt->score_match, &(stat->num_filtered_als)); - } - else - { - bams [low] = tmap_map_bams_init (seqs_buffer [low]->n); - for(j = 0;j < seqs_buffer [low]->n; j++) - { - bams [low]->bams [j] = tmap_map_sams_print(seqs_buffer [low]->seqs [j], index->refseq, records [low]->sams [j], + } + else + { + bams [low] = tmap_map_bams_init (seqs_buffer [low]->n); + for (j = 0; j < seqs_buffer [low]->n; j++) + bams [low]->bams [j] = tmap_map_sams_print (seqs_buffer [low]->seqs [j], index->refseq, records [low]->sams [j], (0 == j) ? 1 : ((seqs_buffer [low]->n-1 == j) ? 2 : 0), records [low]->sams[(j+1) % seqs_buffer [low]->n], driver->opt->sam_flowspace_tags, driver->opt->bidirectional, driver->opt->seq_eq, driver->opt->min_al_len, driver->opt->min_al_cov, driver->opt->min_identity, driver->opt->score_match, &(stat->num_filtered_als)); - } - } - // free alignments, for space - tmap_map_record_destroy (records [low]); - records [low] = NULL; - } - // free seqs - for (i = 0; i < num_ends; i++) - { - for(j = 0; j < 4; j++) - { - tmap_seq_destroy (seqs [i][j]); - seqs [i][j] = NULL; - } - } - tmap_map_record_destroy (record_prev); - } - // next - (*buffer_idx) = low; - low++; - } - (*buffer_idx) = seqs_buffer_length; - - // free thread variables - for(i=0;iseqs_buffer_length = tmap_seqs_io_read_buffer(d->io_in, d->seqs_buffer, d->reads_queue_size, d->io_out->fp->header->header); + tmap_map_driver_thread_io_data_t *d = (tmap_map_driver_thread_io_data_t*) arg; + d->seqs_buffer_length = tmap_seqs_io_read_buffer (d->io_in, d->seqs_buffer, d->reads_queue_size, d->io_out->fp->header->header); return d; } #endif + + void -tmap_map_driver_core(tmap_map_driver_t *driver) +tmap_map_driver_core (tmap_map_driver_t *driver) { - uint32_t i, j, k, n_reads_processed=0; // # of reads processed - int32_t seqs_buffer_length=0; // # of reads read in - int32_t seqs_loaded=0; // 1 if the seq_buffer is loaded, 0 otherwse + uint32_t i, j, k, n_reads_processed = 0; // # of reads processed + int32_t seqs_buffer_length = 0; // # of reads read in + int32_t seqs_loaded = 0; // 1 if the seq_buffer is loaded, 0 otherwse tmap_seqs_io_t *io_in = NULL; // input file(s) tmap_sam_io_t *io_out = NULL; // output file tmap_seqs_t **seqs_buffer = NULL; // buffer for the reads @@ -1781,11 +1791,10 @@ tmap_map_driver_core(tmap_map_driver_t *driver) #endif time_t start_time = time (NULL); - + if (driver->opt->report_stats) tmap_file_stdout = tmap_file_fdopen(fileno(stdout), "wb", TMAP_FILE_NO_COMPRESSION); - // DVK - realignment #ifdef HAVE_LIBPTHREAD struct RealignProxy** realigner = NULL; @@ -2074,129 +2083,221 @@ tmap_map_driver_core(tmap_map_driver_t *driver) #endif // TODO: should we flush when writing SAM and processing one read at a time? - // print statistics - n_reads_processed += seqs_buffer_length; - if(-1 != driver->opt->reads_queue_size) { - tmap_progress_print2("processed %d reads", n_reads_processed); - tmap_progress_print2("stats [%.2lf,%.2lf,%.2lf,%.2lf,%.2lf,%.2lf]", - stat->num_with_mapping * 100.0 / (double)stat->num_reads, - stat->num_after_seeding/(double)stat->num_with_mapping, - stat->num_after_grouping/(double)stat->num_with_mapping, - stat->num_after_scoring/(double)stat->num_with_mapping, - stat->num_after_rmdup/(double)stat->num_with_mapping, - stat->num_after_filter/(double)stat->num_with_mapping); - if (driver->opt->do_repeat_clip) - { - tmap_progress_print2("total %llu, mapped %llu, clipped %llu, rejected %llu, %.2f%% bases]", + // print statistics + n_reads_processed += seqs_buffer_length; + if (-1 != driver->opt->reads_queue_size) + { + tmap_progress_print2("processed %d reads", n_reads_processed); + tmap_progress_print2("stats [%.2lf,%.2lf,%.2lf,%.2lf,%.2lf,%.2lf]", + stat->num_with_mapping * 100.0 / (double)stat->num_reads, + stat->num_after_seeding/(double)stat->num_with_mapping, + stat->num_after_grouping/(double)stat->num_with_mapping, + stat->num_after_scoring/(double)stat->num_with_mapping, + stat->num_after_rmdup/(double)stat->num_with_mapping, + stat->num_after_filter/(double)stat->num_with_mapping); + if (driver->opt->do_repeat_clip) + { + tmap_progress_print2("total %llu, mapped %llu, clipped %llu, rejected %llu, %.2f%% bases]", stat->num_reads, stat->num_with_mapping, stat->num_tailclipped, stat->num_fully_tailclipped, - ((double) stat->bases_tailclipped) * 100 / stat->bases_seen_tailclipped - ); - } - } - seqs_loaded = 0; - } - if(-1 == driver->opt->reads_queue_size) { - tmap_progress_print2("processed %d reads", n_reads_processed); - tmap_progress_print2("stats [%.2lf,%.2lf,%.2lf,%.2lf,%.2lf,%.2lf]", - stat->num_with_mapping * 100.0 / (double)stat->num_reads, - stat->num_after_seeding/(double)stat->num_with_mapping, - stat->num_after_grouping/(double)stat->num_with_mapping, - stat->num_after_scoring/(double)stat->num_with_mapping, - stat->num_after_rmdup/(double)stat->num_with_mapping, - stat->num_after_filter/(double)stat->num_with_mapping); - } + ((double) stat->bases_tailclipped) * 100 / stat->bases_seen_tailclipped); + } + } + seqs_loaded = 0; + } - // OUTPUT STATS - if (driver->opt->report_stats) - { - time_t end_time = time (NULL); - tmap_file_printf ( "\nMapping completed in %d seconds.\n", end_time-start_time); - tmap_file_printf ( " Total reads: %llu\n", stat->num_reads); - tmap_file_printf ( " Mapped reads: %llu (%.2f%%)\n", stat->num_with_mapping, stat->num_with_mapping * 100.0 / (double)stat->num_reads); - tmap_file_printf ( " After seeding: %llu\n", stat->num_after_seeding); - tmap_file_printf ( " After grouping: %llu\n", stat->num_after_grouping); - tmap_file_printf ( " After scoring: %llu\n", stat->num_after_scoring); - tmap_file_printf ( " After dups removal: %llu\n", stat->num_after_rmdup); - tmap_file_printf ( " After filtering: %llu\n", stat->num_after_filter); - if (!driver->opt->do_realign) - tmap_file_printf ( "No realignment perormed\n"); - else - { - tmap_file_printf ( "Realignment statistics:\n"); - tmap_file_printf ( " Realignment invocations: %llu\n", stat->num_realign_invocations); - tmap_file_printf ( " Not realigned (good): %llu\n"); - if (stat->num_realign_sw_failures) - tmap_file_printf (" Algorithm failures: %llu\n", stat->num_realign_sw_failures); - if (stat->num_realign_unclip_failures) - tmap_file_printf (" Un-clipping failures: %llu\n", stat->num_realign_sw_failures); - tmap_file_printf ( " Altered alignments: %llu", stat->num_realign_changed); - if (stat->num_realign_invocations) - tmap_file_printf ( " (%.2f%% total)", ((double) stat->num_realign_changed) * 100 / stat->num_realign_invocations); - tmap_file_printf ("\n"); - tmap_file_printf ( " Altered positions: %llu", stat->num_realign_shifted); - if (stat->num_realign_shifted) - tmap_file_printf ( " (%.2f%% total, %.2f%% changed)", ((double) stat->num_realign_shifted) * 100 / stat->num_realign_invocations, ((double) stat->num_realign_shifted) * 100 / stat->num_realign_changed); - tmap_file_printf ("\n"); - } - if (!driver->opt->do_hp_weight) - tmap_file_printf ("No realignment with context-dependent gap cost performed\n"); - else - { - tmap_file_printf ( "Context-dependent realignment statistics:\n"); - tmap_file_printf ( " Invocations: %llu\n", stat->num_hpcost_invocations); - tmap_file_printf ( " Skipped (too long): %llu\n", stat->num_hpcost_skipped); - tmap_file_printf ( " Altered alignments: %llu", stat->num_hpcost_modified); - if (stat->num_hpcost_invocations) - tmap_file_printf ( " (%.2f%% total)", ((double) stat->num_hpcost_modified) * 100 / stat->num_hpcost_invocations); - if (stat->num_hpcost_invocations - stat->num_hpcost_skipped) - { - double percent = ((double) stat->num_hpcost_modified * 100) / (stat->num_hpcost_invocations - stat->num_hpcost_skipped); - tmap_file_printf (" (%.2f%% realigned)", percent); - } - tmap_file_printf ("\n"); - tmap_file_printf ( " Altered positions: %llu\n", stat->num_hpcost_shifted); - } - if (!driver->opt->do_repeat_clip) - tmap_file_printf ("No tail repeat clipping performed\n"); - else - { - tmap_file_printf ( " Alignments tail-clipped: %llu", stat->num_tailclipped); - if (stat->num_seen_tailclipped) - tmap_file_printf ( " (%.2f%% seen)", ((double) stat->num_tailclipped) * 100 / stat->num_seen_tailclipped); - tmap_file_printf ("\n"); - tmap_file_printf ( " Tail-clipped bases: %llu", stat->bases_tailclipped); - if (stat->bases_seen_tailclipped) - tmap_file_printf ( " (%.2f%% seen)", ((double) stat->bases_tailclipped) * 100 / stat->bases_seen_tailclipped); - tmap_file_printf ("\n"); - tmap_file_printf ( "Completely clipped reads: %llu", stat->num_fully_tailclipped); - if (stat->num_seen_tailclipped) - tmap_file_printf ( " (%.2f%% clipped)", ((double) stat->num_fully_tailclipped) * 100 / stat->num_tailclipped); - tmap_file_printf (", contain %llu bases", stat->bases_fully_tailclipped); - if (stat->bases_tailclipped) - tmap_file_printf (" (%.2f%% clipped)", ((double) stat->bases_fully_tailclipped) * 100 / stat->bases_tailclipped); - tmap_file_printf ("\n"); - if (stat->num_seen_tailclipped) - { - tmap_file_printf ( " Average bases clipped:\n"); - tmap_file_printf ( " per read: %.1f\n", ((double) stat->bases_tailclipped) / stat->num_seen_tailclipped); - if (stat->num_tailclipped) - tmap_file_printf (" per clipped read: %.1f\n", ((double) stat->bases_tailclipped) / stat->num_tailclipped); - if (stat->num_fully_tailclipped) - tmap_file_printf (" per fully clipped read: %.1f\n", ((double) stat->bases_fully_tailclipped) / stat->num_fully_tailclipped); - } - } - if (!driver->opt->min_al_len && !driver->opt->min_al_cov && !driver->opt->min_identity) - tmap_file_printf ("No alignment filtering performed\n"); - else - { - tmap_file_printf ( " Filtered alignments: %llu\n", stat->num_filtered_als); - } + if(-1 == driver->opt->reads_queue_size) + { + tmap_progress_print2("processed %d reads", n_reads_processed); + tmap_progress_print2("stats [%.2lf,%.2lf,%.2lf,%.2lf,%.2lf,%.2lf]", + stat->num_with_mapping * 100.0 / (double)stat->num_reads, + stat->num_after_seeding/(double)stat->num_with_mapping, + stat->num_after_grouping/(double)stat->num_with_mapping, + stat->num_after_scoring/(double)stat->num_with_mapping, + stat->num_after_rmdup/(double)stat->num_with_mapping, + stat->num_after_filter/(double)stat->num_with_mapping); + } + + // OUTPUT STATS + if (driver->opt->report_stats) + { + time_t end_time = time (NULL); + tmap_file_printf ("\nMapping completed in %d seconds.\n", end_time-start_time); + tmap_file_printf (" Total reads: %llu\n", stat->num_reads); + tmap_file_printf (" Mapped reads: %llu (%.2f%%)\n", stat->num_with_mapping, stat->num_with_mapping * 100.0 / (double)stat->num_reads); + tmap_file_printf (" After seeding: %llu\n", stat->num_after_seeding); + tmap_file_printf (" After grouping: %llu\n", stat->num_after_grouping); + tmap_file_printf (" After scoring: %llu\n", stat->num_after_scoring); + tmap_file_printf (" After dups removal: %llu\n", stat->num_after_rmdup); + tmap_file_printf (" After filtering: %llu\n", stat->num_after_filter); + + if (!driver->opt->do_realign) + tmap_file_printf ( "No realignment perormed\n"); + else + { + tmap_file_printf ("Realignment statistics:\n"); + tmap_file_printf (" Realignment invocations: %llu\n", stat->num_realign_invocations); + tmap_file_printf (" Not realigned (good): %llu\n"); + if (stat->num_realign_sw_failures) + tmap_file_printf (" Algorithm failures: %llu\n", stat->num_realign_sw_failures); + if (stat->num_realign_unclip_failures) + tmap_file_printf (" Un-clipping failures: %llu\n", stat->num_realign_sw_failures); + tmap_file_printf (" Altered alignments: %llu", stat->num_realign_changed); + if (stat->num_realign_invocations) + tmap_file_printf (" (%.2f%% total)", ((double) stat->num_realign_changed) * 100 / stat->num_realign_invocations); + tmap_file_printf ("\n"); + tmap_file_printf (" Altered positions: %llu", stat->num_realign_shifted); + if (stat->num_realign_shifted) + tmap_file_printf (" (%.2f%% total, %.2f%% changed)", ((double) stat->num_realign_shifted) * 100 / stat->num_realign_invocations, ((double) stat->num_realign_shifted) * 100 / stat->num_realign_changed); + tmap_file_printf ("\n"); + } + + if (!driver->opt->do_hp_weight) + tmap_file_printf ("No realignment with context-dependent gap cost performed\n"); + else + { + tmap_file_printf ("Context-dependent realignment statistics:\n"); + tmap_file_printf (" Invocations: %llu\n", stat->num_hpcost_invocations); + tmap_file_printf (" Skipped (too long): %llu\n", stat->num_hpcost_skipped); + tmap_file_printf (" Altered alignments: %llu", stat->num_hpcost_modified); + if (stat->num_hpcost_invocations) + tmap_file_printf (" (%.2f%% total)", ((double) stat->num_hpcost_modified) * 100 / stat->num_hpcost_invocations); + if (stat->num_hpcost_invocations - stat->num_hpcost_skipped) + { + double percent = ((double) stat->num_hpcost_modified * 100) / (stat->num_hpcost_invocations - stat->num_hpcost_skipped); + tmap_file_printf (" (%.2f%% realigned)", percent); + } + tmap_file_printf ("\n"); + tmap_file_printf (" Altered positions: %llu\n", stat->num_hpcost_shifted); + } + + if (!(0 < driver->opt->pen_gapl)) + tmap_file_printf ("No edge long indel salvage performed\n"); + else + { + tmap_file_printf ("Long tail indels salvage : %llu reads\n", stat->reads_salvaged); + tmap_file_printf (" %8s %8s %8s %8s", "5'fwd", "3'fwd", "5'rev", "3'rev\n"); + tmap_file_printf (" Salvaged read ends: %8llu %8llu %8llu %8llu\n", + stat->num_salvaged [F5P], + stat->num_salvaged [F3P], + stat->num_salvaged [R5P], + stat->num_salvaged [R3P]); + tmap_file_printf (" Average query bases: %8.1f %8.1f %8.1f %8.1f\n", + stat->num_salvaged [F5P]?(((double) stat->bases_salvaged_qry [F5P]) / stat->num_salvaged [F5P]):0., + stat->num_salvaged [F3P]?(((double) stat->bases_salvaged_qry [F3P]) / stat->num_salvaged [F3P]):0., + stat->num_salvaged [R5P]?(((double) stat->bases_salvaged_qry [R5P]) / stat->num_salvaged [R5P]):0., + stat->num_salvaged [R3P]?(((double) stat->bases_salvaged_qry [R3P]) / stat->num_salvaged [R3P]):0.); + tmap_file_printf (" Average reference bases: %8.1f %8.1f %8.1f %8.1f\n", + stat->num_salvaged [F5P]?(((double) stat->bases_salvaged_ref [F5P]) / stat->num_salvaged [F5P]):0., + stat->num_salvaged [F3P]?(((double) stat->bases_salvaged_ref [F3P]) / stat->num_salvaged [F3P]):0., + stat->num_salvaged [R5P]?(((double) stat->bases_salvaged_ref [R5P]) / stat->num_salvaged [R5P]):0., + stat->num_salvaged [R3P]?(((double) stat->bases_salvaged_ref [R3P]) / stat->num_salvaged [R3P]):0.); + tmap_file_printf (" Average score change: %8.1f %8.1f %8.1f %8.1f\n", + stat->num_salvaged [F5P]?(((double) stat->score_salvaged_total [F5P]) / stat->num_salvaged [F5P]):0., + stat->num_salvaged [F3P]?(((double) stat->score_salvaged_total [F3P]) / stat->num_salvaged [F3P]):0., + stat->num_salvaged [R5P]?(((double) stat->score_salvaged_total [R5P]) / stat->num_salvaged [R5P]):0., + stat->num_salvaged [R3P]?(((double) stat->score_salvaged_total [R3P]) / stat->num_salvaged [R3P]):0.); + } + + if (!driver->opt->end_repair) + tmap_file_printf ("No end repair performed\n"); + else if (driver->opt->end_repair <= 2) + tmap_file_printf ("\"Old-style\" end repair performed, no statistics collected\n"); + else + { + tmap_file_printf ("End repair : %d reads softclipped, %d extended.\n", stat->reads_end_repair_clipped, stat->reads_end_repair_extended); + tmap_file_printf (" %8s %8s %8s %8s\n", "5'fwd", "3'fwd", "5'rev", "3'rev"); + tmap_file_printf (" Clipped read ends: %8llu %8llu %8llu %8llu\n", + stat->num_end_repair_clipped [F5P], + stat->num_end_repair_clipped [F3P], + stat->num_end_repair_clipped [R5P], + stat->num_end_repair_clipped [R3P]); + tmap_file_printf (" Average bases clipped: %8.1f %8.1f %8.1f %8.1f\n", + stat->num_end_repair_clipped [F5P]?(((double) stat->bases_end_repair_clipped [F5P]) / stat->num_end_repair_clipped [F5P]):0., + stat->num_end_repair_clipped [F3P]?(((double) stat->bases_end_repair_clipped [F3P]) / stat->num_end_repair_clipped [F3P]):0., + stat->num_end_repair_clipped [R5P]?(((double) stat->bases_end_repair_clipped [R5P]) / stat->num_end_repair_clipped [R5P]):0., + stat->num_end_repair_clipped [R3P]?(((double) stat->bases_end_repair_clipped [R3P]) / stat->num_end_repair_clipped [R3P]):0.); + tmap_file_printf (" Extended read ends: %8llu %8llu %8llu %8llu\n", + stat->num_end_repair_extended [F5P], + stat->num_end_repair_extended [F3P], + stat->num_end_repair_extended [R5P], + stat->num_end_repair_extended [R3P]); + tmap_file_printf (" Average bases extended: %8.1f %8.1f %8.1f %8.1f\n", + stat->num_end_repair_extended [F5P]?(((double) stat->bases_end_repair_extended [F5P]) / stat->num_end_repair_extended [F5P]):0., + stat->num_end_repair_extended [F3P]?(((double) stat->bases_end_repair_extended [F3P]) / stat->num_end_repair_extended [F3P]):0., + stat->num_end_repair_extended [R5P]?(((double) stat->bases_end_repair_extended [R5P]) / stat->num_end_repair_extended [R5P]):0., + stat->num_end_repair_extended [R3P]?(((double) stat->bases_end_repair_extended [R3P]) / stat->num_end_repair_extended [R3P]):0.); + tmap_file_printf (" Average indels inserted: %8.1f %8.1f %8.1f %8.1f\n", + stat->num_end_repair_extended [F5P]?(((double) stat->total_end_repair_indel [F5P]) / stat->num_end_repair_extended [F5P]):0., + stat->num_end_repair_extended [F3P]?(((double) stat->total_end_repair_indel [F3P]) / stat->num_end_repair_extended [F3P]):0., + stat->num_end_repair_extended [R5P]?(((double) stat->total_end_repair_indel [R5P]) / stat->num_end_repair_extended [R5P]):0., + stat->num_end_repair_extended [R3P]?(((double) stat->total_end_repair_indel [R3P]) / stat->num_end_repair_extended [R3P]):0.); + } + if (driver->opt->end_repair) + { + if (driver->opt->end_repair_5_prime_softclip) + tmap_file_printf ("5' soft-clipping is allowed for end repair, no softclip removal performed\n"); + else if (driver->opt->softclip_type != 2 && driver->opt->softclip_type != 3) + tmap_file_printf ("5' soft-clipping is explicitly allowed through option \"-g %d\" on command line, no softclip removal performed\n", driver->opt->softclip_type); + else + { + tmap_file_printf ("5' softclip removed on %llu reads\n", stat->num_5_softclips [0] + stat->num_5_softclips [1]); + tmap_file_printf (" %8s %8s\n", "fwd", "rev"); + tmap_file_printf (" Recovered read ends: %8llu %8llu\n", + stat->num_5_softclips [0], + stat->num_5_softclips [1]); + tmap_file_printf (" Average query bases recovered: %8.1f %8.1f\n", + stat->num_5_softclips [0]?(((double) stat->bases_5_softclips_qry [0]) / stat->num_5_softclips [0]):0., + stat->num_5_softclips [1]?(((double) stat->bases_5_softclips_qry [1]) / stat->num_5_softclips [1]):0.); + tmap_file_printf (" Average ref bases recovered: %8.1f %8.1f\n", + stat->num_5_softclips [0]?(((double) stat->bases_5_softclips_ref [0]) / stat->num_5_softclips [0]):0., + stat->num_5_softclips [1]?(((double) stat->bases_5_softclips_ref [1]) / stat->num_5_softclips [1]):0.); + tmap_file_printf (" Average recovered score: %8.1f %8.1f\n", + stat->num_5_softclips [0]?(((double) stat->score_5_softclips_total [0]) / stat->num_5_softclips [0]):0., + stat->num_5_softclips [1]?(((double) stat->score_5_softclips_total [1]) / stat->num_5_softclips [1]):0.); + } + } + + if (!driver->opt->do_repeat_clip) + tmap_file_printf ("No tail repeat clipping performed\n"); + else + { + tmap_file_printf ( " Alignments tail-clipped: %llu", stat->num_tailclipped); + if (stat->num_seen_tailclipped) + tmap_file_printf ( " (%.2f%% seen)", ((double) stat->num_tailclipped) * 100 / stat->num_seen_tailclipped); + tmap_file_printf ("\n"); + tmap_file_printf ( " Tail-clipped bases: %llu", stat->bases_tailclipped); + if (stat->bases_seen_tailclipped) + tmap_file_printf ( " (%.2f%% seen)", ((double) stat->bases_tailclipped) * 100 / stat->bases_seen_tailclipped); + tmap_file_printf ("\n"); + tmap_file_printf ( "Completely clipped reads: %llu", stat->num_fully_tailclipped); + if (stat->num_seen_tailclipped) + tmap_file_printf ( " (%.2f%% clipped)", ((double) stat->num_fully_tailclipped) * 100 / stat->num_tailclipped); + tmap_file_printf (", contain %llu bases", stat->bases_fully_tailclipped); + if (stat->bases_tailclipped) + tmap_file_printf (" (%.2f%% clipped)", ((double) stat->bases_fully_tailclipped) * 100 / stat->bases_tailclipped); + tmap_file_printf ("\n"); + if (stat->num_seen_tailclipped) + { + tmap_file_printf ( " Average bases clipped:\n"); + tmap_file_printf ( " per read: %.1f\n", ((double) stat->bases_tailclipped) / stat->num_seen_tailclipped); + if (stat->num_tailclipped) + tmap_file_printf (" per clipped read: %.1f\n", ((double) stat->bases_tailclipped) / stat->num_tailclipped); + if (stat->num_fully_tailclipped) + tmap_file_printf (" per fully clipped read: %.1f\n", ((double) stat->bases_fully_tailclipped) / stat->num_fully_tailclipped); + } + } + if (!driver->opt->min_al_len && !driver->opt->min_al_cov && !driver->opt->min_identity) + tmap_file_printf ("No alignment filtering performed\n"); + else + { + tmap_file_printf ( " Filtered alignments: %llu\n", stat->num_filtered_als); + } } - - + + tmap_progress_print2("cleaning up"); // cleanup the algorithm persistent data @@ -2237,7 +2338,7 @@ tmap_map_driver_core(tmap_map_driver_t *driver) tmap_log_disable (); if (logfile) fclose (logfile); - + free(records); free(bams); tmap_map_stats_destroy(stat); @@ -2255,7 +2356,7 @@ tmap_map_driver_core(tmap_map_driver_t *driver) #ifdef ENABLE_TMAP_DEBUG_FUNCTIONS tmap_rand_destroy(rand_core); #endif - + // if (driver->opt->report_stats) // tmap_file_fclose (tmap_file_stdout); } diff --git a/Analysis/TMAP/src/map/tmap_map_driver.h b/Analysis/TMAP/src/map/tmap_map_driver.h index 1f34ca20..815d8b30 100644 --- a/Analysis/TMAP/src/map/tmap_map_driver.h +++ b/Analysis/TMAP/src/map/tmap_map_driver.h @@ -31,7 +31,6 @@ typedef int32_t (*tmap_map_driver_func_init)(void **data, tmap_refseq_t *refseq, */ typedef int32_t (*tmap_map_driver_func_thread_init)(void **data, tmap_map_opt_t *opt); - /*! This function will be invoked to map a sequence. @param data the thread persistent data diff --git a/Analysis/TMAP/src/map/util/tmap_map_opt.c b/Analysis/TMAP/src/map/util/tmap_map_opt.c index 95895cec..b50aae88 100644 --- a/Analysis/TMAP/src/map/util/tmap_map_opt.c +++ b/Analysis/TMAP/src/map/util/tmap_map_opt.c @@ -187,6 +187,7 @@ __tmap_map_opt_option_print_func_int_init(max_one_large_indel_rescue) __tmap_map_opt_option_print_func_int_init(min_anchor_large_indel_rescue) __tmap_map_opt_option_print_func_int_init(amplicon_overrun) __tmap_map_opt_option_print_func_int_init(max_adapter_bases_for_soft_clipping) +__tmap_map_opt_option_print_func_tf_init(end_repair_5_prime_softclip) __tmap_map_opt_option_print_func_int_init(shm_key) #ifdef ENABLE_TMAP_DEBUG_FUNCTIONS @@ -212,6 +213,9 @@ __tmap_map_opt_option_print_func_tf_init(do_repeat_clip) // __tmap_map_opt_option_print_func_int_init(repclip_overlap) __tmap_map_opt_option_print_func_tf_init(repclip_continuation) +__tmap_map_opt_option_print_func_int_init(cigar_sanity_check) + + // context-dependent gaps __tmap_map_opt_option_print_func_tf_init(do_hp_weight) __tmap_map_opt_option_print_func_int_init(gap_scale_mode) @@ -454,7 +458,8 @@ tmap_map_opt_options_destroy(tmap_map_opt_options_t *options) static void tmap_map_opt_init_helper(tmap_map_opt_t *opt) { - static char *softclipping_type[] = {"0 - allow on the left and right portion of the read", + static char *softclipping_type[] = { + "0 - allow on the left and right portion of the read", "1 - allow on the left portion of the read", "2 - allow on the right portion of the read", "3 - do not allow soft-clipping", @@ -477,7 +482,8 @@ tmap_map_opt_init_helper(tmap_map_opt_t *opt) "9 - Bladze (Top Coder #6)", "10 - ngthuydiem (Top Coder #7) [Farrar cut-and-paste]", NULL}; - static char *realignment_clip_type[] = {"0 - global realignment", + static char *realignment_clip_type[] = { + "0 - global realignment", "1 - semiglobal (can start/end anywhere in reference)", "2 - semiglobal with soft clip on bead side of a read", "3 - semiglobal with soft clip on key side of a read", @@ -492,6 +498,18 @@ tmap_map_opt_init_helper(tmap_map_opt_t *opt) static char *strandedness[] = {"0 - same strand", "1 - opposite strand", NULL}; static char *positioning[] = {"0 - read one before read two", "1 - read two before read one", NULL}; static char *end_repair[] = {"0 - disable", "1 - prefer mismatches", "2 - prefer indels", ">2 - specify %% Mismatch above which to trim end alignment", NULL}; + static char *sanity_check_outcome [] = { + "0 - do not perform sanity check", + "1 - perform content checks, print warning to stderr; do not check alignment compatibility or scores", + "2 - perform content and alignment compatibility checks, print warnings to stderr; do not check alignment scores", + "3 - perform all checks, print warnings to stderr", + "4 - perform content checks, exit on error", + "5 - perform content checks, exit on error; warn if processed alignment is incompatible with raw one", + "6 - perform content and alignment compatibility checks, exit on error", + "7 - perform content checks, exit on error; warn if processed alignment is incompatible with raw one or if score is suspicious", + "8 - perform content and alignment compatibility checks, exit on error; warn if score is suspicious", + "9 - perform all checks, exit if any of them fails", + NULL}; opt->options = tmap_map_opt_options_init(); @@ -704,7 +722,7 @@ tmap_map_opt_init_helper(tmap_map_opt_t *opt) TMAP_MAP_ALGO_GLOBAL); tmap_map_opt_options_add(opt->options, "end-repair", required_argument, 0, 0 /* no short flag */, TMAP_MAP_OPT_TYPE_INT, - "specifies to perform 5' end repair", + "specifies to perform end repair", end_repair, tmap_map_opt_option_print_func_end_repair, TMAP_MAP_ALGO_GLOBAL); @@ -722,7 +740,7 @@ tmap_map_opt_init_helper(tmap_map_opt_t *opt) TMAP_MAP_ALGO_GLOBAL); tmap_map_opt_options_add(opt->options, "max-amplicon-overrun-large-indel-rescue", required_argument, 0, 0 /* no short flag */, TMAP_MAP_OPT_TYPE_INT, - "the minimum anchor size to rescue with one large indel algorithm", + "the maximum number of bases allowed for a read to overrun the end of amplicon", NULL, tmap_map_opt_option_print_func_amplicon_overrun, TMAP_MAP_ALGO_GLOBAL); @@ -732,6 +750,13 @@ tmap_map_opt_init_helper(tmap_map_opt_t *opt) NULL, tmap_map_opt_option_print_func_max_adapter_bases_for_soft_clipping, TMAP_MAP_ALGO_GLOBAL); + tmap_map_opt_options_add(opt->options, "er-5clip", no_argument, 0, 0, + TMAP_MAP_OPT_TYPE_NONE, + "allow end repair to introduce 5' soft clips", + NULL, + tmap_map_opt_option_print_func_end_repair_5_prime_softclip, + TMAP_MAP_ALGO_GLOBAL); + tmap_map_opt_options_add(opt->options, "shared-memory-key", required_argument, 0, 'k', TMAP_MAP_OPT_TYPE_INT, "use shared memory with the following key", @@ -845,6 +870,16 @@ tmap_map_opt_init_helper(tmap_map_opt_t *opt) tmap_map_opt_option_print_func_repclip_continuation, TMAP_MAP_ALGO_GLOBAL); + + + tmap_map_opt_options_add(opt->options, "cigar-sanity-check", required_argument, 0, 0, + TMAP_MAP_OPT_TYPE_INT, + "perform diagnostics sanity check on all generated alignments", + sanity_check_outcome, + tmap_map_opt_option_print_func_cigar_sanity_check, + TMAP_MAP_ALGO_GLOBAL); + + // context-dependent indel weights tmap_map_opt_options_add(opt->options, "context", no_argument, 0, 0, TMAP_MAP_OPT_TYPE_NONE, @@ -1376,6 +1411,7 @@ tmap_map_opt_init(int32_t algo_id) opt->min_anchor_large_indel_rescue = 6; opt->amplicon_overrun = 6; opt->max_adapter_bases_for_soft_clipping = INT32_MAX; + opt->end_repair_5_prime_softclip = 0; opt->shm_key = 0; opt->min_seq_len = -1; opt->max_seq_len = -1; @@ -1396,6 +1432,9 @@ tmap_map_opt_init(int32_t algo_id) // tail repeat clipping opt->do_repeat_clip = 0; opt->repclip_continuation = 0; + + opt->cigar_sanity_check = TMAP_MAP_SANITY_NONE; + // context dependent gap scores opt->do_hp_weight = 0; opt->gap_scale_mode = TMAP_CONTEXT_GAP_SCALE_GEP; @@ -1904,8 +1943,9 @@ tmap_map_opt_parse(int argc, char *argv[], tmap_map_opt_t *opt) } else if(0 == c && 0 == strcmp("max-amplicon-overrun-large-indel-rescue", options[option_index].name)) { opt->amplicon_overrun = atoi(optarg); } - - + else if (0 == c && 0 == strcmp ("er-5clip", options [option_index].name)) { + opt->end_repair_5_prime_softclip = 1; + } // End of global options // realignment options @@ -1947,6 +1987,9 @@ tmap_map_opt_parse(int argc, char *argv[], tmap_map_opt_t *opt) else if (0 == c && 0 == strcmp ("repclip-cont", options [option_index].name)) { opt->repclip_continuation = 1; } + else if (0 == c && 0 == strcmp ("cigar-sanity-check", options [option_index].name)) { + opt->cigar_sanity_check = atoi (optarg); + } // context-dependent gap scoring else if (0 == c && 0 == strcmp ("context", options [option_index].name)) { opt->do_hp_weight = 1; @@ -2508,6 +2551,7 @@ tmap_map_opt_check(tmap_map_opt_t *opt) tmap_error_cmd_check_int(opt->output_type, 0, 2, "-o"); tmap_error_cmd_check_int(opt->end_repair, 0, 100, "--end-repair"); tmap_error_cmd_check_int(opt->max_adapter_bases_for_soft_clipping, 0, INT32_MAX, "max-adapter-bases-for-soft-clipping"); + tmap_error_cmd_check_int(opt->end_repair_5_prime_softclip, 0, 1, "--er-5clip"); #ifdef ENABLE_TMAP_DEBUG_FUNCTIONS tmap_error_cmd_check_int(opt->sample_reads, 0, 1, "-x"); #endif @@ -2559,6 +2603,8 @@ tmap_map_opt_check(tmap_map_opt_t *opt) tmap_error_cmd_check_int (opt->do_repeat_clip, 0, 1, "--do-repeat-clip"); tmap_error_cmd_check_int (opt->repclip_continuation, 0, 1, "--repclip_cont"); + tmap_error_cmd_check_int (opt->cigar_sanity_check, 0, TMAP_MAP_SANITY_LASTVAL, "--cigar-sanity-check"); + // context dependent gap scores tmap_error_cmd_check_int (opt->do_hp_weight, 0, 1, "--context"); tmap_error_cmd_check_int (opt->gap_scale_mode, 0, 2, "--hpscale"); @@ -2727,12 +2773,15 @@ tmap_map_opt_copy_global(tmap_map_opt_t *opt_dest, tmap_map_opt_t *opt_src) opt_dest->min_anchor_large_indel_rescue = opt_src->min_anchor_large_indel_rescue; opt_dest->amplicon_overrun = opt_src->amplicon_overrun; opt_dest->max_adapter_bases_for_soft_clipping = opt_src->max_adapter_bases_for_soft_clipping; + opt_dest->end_repair_5_prime_softclip = opt_src->end_repair_5_prime_softclip; opt_dest->shm_key = opt_src->shm_key; #ifdef ENABLE_TMAP_DEBUG_FUNCTIONS opt_dest->sample_reads = opt_src->sample_reads; #endif opt_dest->vsw_type = opt_src->vsw_type; - + + opt_dest->cigar_sanity_check = opt_src->cigar_sanity_check; + // flowspace options opt_dest->fscore = opt_src->fscore; opt_dest->softclip_key = opt_src->softclip_key; @@ -2818,6 +2867,7 @@ tmap_map_opt_print(tmap_map_opt_t *opt) fprintf(stderr, "min-anchor-large-indel-rescue=%d\n",opt->min_anchor_large_indel_rescue); fprintf(stderr, "max-amplicon-overrun-indel-rescue=%d", opt->amplicon_overrun); fprintf(stderr, "max_adapter_bases_for_soft_clipping=%d\n", opt->max_adapter_bases_for_soft_clipping); + fprintf(stderr, "end_repair_5_prime_softclip=%d\n", opt->end_repair_5_prime_softclip); fprintf(stderr, "shm_key=%d\n", (int)opt->shm_key); #ifdef ENABLE_TMAP_DEBUG_FUNCTIONS fprintf(stderr, "sample_reads=%lf\n", opt->sample_reads); diff --git a/Analysis/TMAP/src/map/util/tmap_map_opt.h b/Analysis/TMAP/src/map/util/tmap_map_opt.h index cf7fd836..db08b2d6 100644 --- a/Analysis/TMAP/src/map/util/tmap_map_opt.h +++ b/Analysis/TMAP/src/map/util/tmap_map_opt.h @@ -112,6 +112,23 @@ enum { TMAP_CONTEXT_GAP_SCALE_BOTH = 2 }; +/*! + The alignment sanity check modes + */ +enum { + TMAP_MAP_SANITY_NONE = 0, /*!< do not perform sanity check */ + TMAP_MAP_SANITY_WARN_CONTENT = 1, /*!< perform content checks, print warning to stdout; do not check alignment consistency or scores */ + TMAP_MAP_SANITY_WARN_CONTENT_ALIGN = 2, /*!< perform content and alignment compatibility checks, print warning to stdout; do not check alignment scores */ + TMAP_MAP_SANITY_WARN_ALL = 3, /*!< perform all checks, print warnings to stdout */ + TMAP_MAP_SANITY_ERR_CONTENT = 4, /*!< perform content checks, exit on error */ + TMAP_MAP_SANITY_ERR_CONTENT_WARN_ALIGN = 5, /*!< perform content checks, exit on error; warn if processed alignment is incompatible with raw one */ + TMAP_MAP_SANITY_ERR_CONTENT_WARN_ALIGN_SCORE = 6, /*!< perform content checks, exit on error; warn if processed alignment is incompatible with raw one or if score is suspicious */ + TMAP_MAP_SANITY_ERR_CONTENT_ALIGN = 7, /*!< perform content and alignment compatibility checks, exit on error */ + TMAP_MAP_SANITY_ERR_CONTENT_ALIGN_WARN_SCORE = 8, /*!< perform content and alignment compatibility checks, exit on error; warn if score is suspicious */ + TMAP_MAP_SANITY_ERR_ALL = 9, /*!< perform all checks, exit if any of them fails */ + TMAP_MAP_SANITY_LASTVAL = 9 +}; + /*! The various option types */ @@ -218,6 +235,8 @@ typedef struct __tmap_map_opt_t { int32_t min_anchor_large_indel_rescue; /*!< minimum size of anchor needed to open one large gap*/ int32_t amplicon_overrun; /*!< maximum allowed alignment to overrun amplicon edge in one large indel alignment*/ int32_t max_adapter_bases_for_soft_clipping; /*!< specifies to perform 3' soft-clipping (via -g) if at most this # of adapter bases were found (ZB tag) (--max-adapter-bases-for-soft-clipping) */ + int32_t end_repair_5_prime_softclip; /*!< end-repair is allowed to introduce 5' softclip */ + key_t shm_key; /*!< the shared memory key (-k,--shared-memory-key) */ #ifdef ENABLE_TMAP_DEBUG_FUNCTIONS double sample_reads; /*!< sample the reads at this fraction (-x,--sample-reads) */ @@ -241,13 +260,15 @@ typedef struct __tmap_map_opt_t { double context_gip_score; /*!< context realignment gap opening score */ double context_gep_score; /*!< context realignment gap extension score */ int32_t context_extra_bandwidth; /*!< context realignment DP matrix extra band width */ - int32_t context_debug_log; /*!< output detailed log of alignment operation into log */ + int32_t context_debug_log; /*!< output detailed log of context alignment (scoring matrix) into a log file (designated by realign_log)*/ // DVK: tandem repeat end-clipping int32_t do_repeat_clip; /*!< clip tandem repeats at the alignment ends */ // int32_t repclip_overlap; /*! repeat clipping is not performed if read overlaps amplicon end by this number of bases (or more) */ int32_t repclip_continuation; /*! repeat clipping performed only if repeat continues past the end of the read into the reference by at least 1 period */ + int32_t cigar_sanity_check; + // DVK: alignment length filtering int32_t min_al_len; /*!< minimal alignment length to report, -1 to disable */ double min_al_cov; /*!< minimal aligned fraction of the read */ diff --git a/Analysis/TMAP/src/map/util/tmap_map_stats.c b/Analysis/TMAP/src/map/util/tmap_map_stats.c index 4dfdb0a1..4094798b 100644 --- a/Analysis/TMAP/src/map/util/tmap_map_stats.c +++ b/Analysis/TMAP/src/map/util/tmap_map_stats.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "../../util/tmap_error.h" #include "../../util/tmap_alloc.h" #include "../../util/tmap_definitions.h" @@ -13,7 +14,7 @@ tmap_map_stats_t* tmap_map_stats_init() { - tmap_map_stats_t* r = tmap_calloc(1, sizeof(tmap_map_stats_t), "return"); + tmap_map_stats_t* r = tmap_calloc (1, sizeof(tmap_map_stats_t), "stats"); // DVK - guarantee initialization to zero of all members (actually calloc does this anyway) tmap_map_stats_zero (r); @@ -24,7 +25,7 @@ tmap_map_stats_init() void tmap_map_stats_destroy(tmap_map_stats_t *s) { - free(s); + free (s); } void @@ -37,6 +38,7 @@ tmap_map_stats_zero(tmap_map_stats_t *s) void tmap_map_stats_add(tmap_map_stats_t *dest, tmap_map_stats_t *src) { + int i; dest->num_reads += src->num_reads; dest->num_with_mapping += src->num_with_mapping; dest->num_after_seeding += src->num_after_seeding; @@ -45,6 +47,11 @@ tmap_map_stats_add(tmap_map_stats_t *dest, tmap_map_stats_t *src) dest->num_after_rmdup += src->num_after_rmdup; dest->num_after_filter += src->num_after_filter; + dest->num_hpcost_invocations += src->num_hpcost_invocations; + dest->num_hpcost_skipped += src->num_hpcost_skipped; + dest->num_hpcost_modified += src->num_hpcost_modified; + dest->num_hpcost_shifted += src->num_hpcost_shifted; + dest->num_realign_invocations += src->num_realign_invocations; dest->num_realign_already_perfect += src->num_realign_already_perfect; dest->num_realign_not_clipped += src->num_realign_not_clipped; @@ -53,6 +60,25 @@ tmap_map_stats_add(tmap_map_stats_t *dest, tmap_map_stats_t *src) dest->num_realign_changed += src->num_realign_changed; dest->num_realign_shifted += src->num_realign_shifted; + dest->reads_salvaged += src->reads_salvaged; + for (i = 0; i != 4; ++i) dest->num_salvaged [i] += src->num_salvaged [i]; + for (i = 0; i != 4; ++i) dest->bases_salvaged_qry [i] += src->bases_salvaged_qry [i]; + for (i = 0; i != 4; ++i) dest->bases_salvaged_ref [i] += src->bases_salvaged_ref [i]; + for (i = 0; i != 4; ++i) dest->score_salvaged_total [i] += src->score_salvaged_total [i]; + + dest->reads_end_repair_clipped += src->reads_end_repair_clipped; + for (i = 0; i != 4; ++i) dest->num_end_repair_clipped [i] += src->num_end_repair_clipped [i]; + for (i = 0; i != 4; ++i) dest->bases_end_repair_clipped [i] += src->bases_end_repair_clipped [i]; + dest->reads_end_repair_extended += src->reads_end_repair_extended; + for (i = 0; i != 4; ++i) dest->num_end_repair_extended [i] += src->num_end_repair_extended [i]; + for (i = 0; i != 4; ++i) dest->bases_end_repair_extended [i] += src->bases_end_repair_extended [i]; + for (i = 0; i != 4; ++i) dest->total_end_repair_indel [i] += src->total_end_repair_indel [i]; + + for (i = 0; i != 2; ++i) dest->num_5_softclips [i] += src->num_5_softclips [i]; + for (i = 0; i != 2; ++i) dest->bases_5_softclips_qry [i] += src->bases_5_softclips_qry [i]; + for (i = 0; i != 2; ++i) dest->bases_5_softclips_ref [i] += src->bases_5_softclips_ref [i]; + for (i = 0; i != 2; ++i) dest->score_5_softclips_total [i] += src->score_5_softclips_total [i]; + dest->num_seen_tailclipped += src->num_seen_tailclipped; dest->bases_seen_tailclipped += src->bases_seen_tailclipped; dest->num_tailclipped += src->num_tailclipped; @@ -60,45 +86,119 @@ tmap_map_stats_add(tmap_map_stats_t *dest, tmap_map_stats_t *src) dest->num_fully_tailclipped += src->num_fully_tailclipped; dest->bases_fully_tailclipped += src->bases_fully_tailclipped; - dest->num_hpcost_invocations += src->num_hpcost_invocations; - dest->num_hpcost_skipped += src->num_hpcost_skipped; - dest->num_hpcost_modified += src->num_hpcost_modified; - dest->num_hpcost_shifted += src->num_hpcost_shifted; - dest->num_filtered_als += src->num_filtered_als; } void tmap_map_stats_print(tmap_map_stats_t *s) { - fprintf(stderr, "num_reads=%llu\n", (unsigned long long int)s->num_reads); - fprintf(stderr, "num_with_mapping=%llu\n", (unsigned long long int)s->num_with_mapping); - fprintf(stderr, "num_after_seeding=%llu\n", (unsigned long long int)s->num_after_seeding); - fprintf(stderr, "num_after_grouping=%llu\n", (unsigned long long int)s->num_after_grouping); - fprintf(stderr, "num_after_scoring=%llu\n", (unsigned long long int)s->num_after_scoring); - fprintf(stderr, "num_after_rmdup=%llu\n", (unsigned long long int)s->num_after_rmdup); - fprintf(stderr, "num_after_filter=%llu\n", (unsigned long long int)s->num_after_filter); + fprintf (stderr, "num_reads=%llu\n", (unsigned long long int) s->num_reads); + fprintf (stderr, "num_with_mapping=%llu\n", (unsigned long long int) s->num_with_mapping); + fprintf (stderr, "num_after_seeding=%llu\n", (unsigned long long int) s->num_after_seeding); + fprintf (stderr, "num_after_grouping=%llu\n", (unsigned long long int) s->num_after_grouping); + fprintf (stderr, "num_after_scoring=%llu\n", (unsigned long long int) s->num_after_scoring); + fprintf (stderr, "num_after_rmdup=%llu\n", (unsigned long long int) s->num_after_rmdup); + fprintf (stderr, "num_after_filter=%llu\n", (unsigned long long int) s->num_after_filter); + + fprintf (stderr, "num_hpcost_invocations=%llu\n", (unsigned long long int) s->num_hpcost_invocations); + fprintf (stderr, "num_hpcost_skipped=%llu\n", (unsigned long long int) s->num_hpcost_skipped); + fprintf (stderr, "num_hpcost_modified=%llu\n", (unsigned long long int) s->num_hpcost_modified); + fprintf (stderr, "num_hpcost_shifted=%llu\n", (unsigned long long int) s->num_hpcost_shifted); + + fprintf (stderr, "num_realign_invocations=%llu\n", (unsigned long long int) s->num_realign_invocations); + fprintf (stderr, "num_realign_already_perfect=%llu\n", (unsigned long long int) s->num_realign_already_perfect); + fprintf (stderr, "num_realign_not_clipped=%llu\n", (unsigned long long int) s->num_realign_not_clipped); + fprintf (stderr, "num_realign_sw_failures=%llu\n", (unsigned long long int) s->num_realign_sw_failures); + fprintf (stderr, "num_realign_unclip_failures=%llu\n", (unsigned long long int) s->num_realign_unclip_failures); + fprintf (stderr, "num_realign_changed=%llu\n", (unsigned long long int) s->num_realign_changed); + fprintf (stderr, "num_realign_shifted=%llu\n", (unsigned long long int) s->num_realign_shifted); + + + fprintf (stderr, "num_salvaged [reads, fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->reads_salvaged, + (unsigned long long int) s->num_salvaged [F5P], + (unsigned long long int) s->num_salvaged [F3P], + (unsigned long long int) s->num_salvaged [R5P], + (unsigned long long int) s->num_salvaged [R3P]); + + fprintf (stderr, "bases_salvaged_qry [fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->bases_salvaged_qry [F5P], + (unsigned long long int) s->bases_salvaged_qry [F3P], + (unsigned long long int) s->bases_salvaged_qry [R5P], + (unsigned long long int) s->bases_salvaged_qry [R3P]); + + fprintf (stderr, "bases_salvaged_ref [fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->bases_salvaged_ref [F5P], + (unsigned long long int) s->bases_salvaged_ref [F3P], + (unsigned long long int) s->bases_salvaged_ref [R5P], + (unsigned long long int) s->bases_salvaged_ref [R3P]); + + fprintf (stderr, "score_salvaged_total [fwd5', fwd3', rev5', rev3']=[%lld, %lld, %lld, %lld]\n", + (long long int) s->score_salvaged_total [F5P], + (long long int) s->score_salvaged_total [F3P], + (long long int) s->score_salvaged_total [R5P], + (long long int) s->score_salvaged_total [R3P]); + + fprintf (stderr, "num_end_repair_clipped [reads, fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->reads_end_repair_clipped, + (unsigned long long int) s->num_end_repair_clipped [F5P], + (unsigned long long int) s->num_end_repair_clipped [F3P], + (unsigned long long int) s->num_end_repair_clipped [R5P], + (unsigned long long int) s->num_end_repair_clipped [R3P]); + + fprintf (stderr, "bases_end_repair_clipped [fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->bases_end_repair_clipped [F5P], + (unsigned long long int) s->bases_end_repair_clipped [F3P], + (unsigned long long int) s->bases_end_repair_clipped [R5P], + (unsigned long long int) s->bases_end_repair_clipped [R3P]); + + fprintf (stderr, "num_end_repair_extended [reads, fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->reads_end_repair_extended, + (unsigned long long int) s->num_end_repair_extended [F5P], + (unsigned long long int) s->num_end_repair_extended [F3P], + (unsigned long long int) s->num_end_repair_extended [R5P], + (unsigned long long int) s->num_end_repair_extended [R3P]); + + fprintf (stderr, "bases_end_repair_extended [fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->bases_end_repair_extended [F5P], + (unsigned long long int) s->bases_end_repair_extended [F3P], + (unsigned long long int) s->bases_end_repair_extended [R5P], + (unsigned long long int) s->bases_end_repair_extended [R3P]); + + fprintf (stderr, "total_end_repair_indel [fwd5', fwd3', rev5', rev3']=[%llu, %llu, %llu, %llu]\n", + (unsigned long long int) s->total_end_repair_indel [F5P], + (unsigned long long int) s->total_end_repair_indel [F3P], + (unsigned long long int) s->total_end_repair_indel [R5P], + (unsigned long long int) s->total_end_repair_indel [R3P]); + + fprintf (stderr, "num_5_softclips [fwd, rev]=[%llu, %llu]\n", + (unsigned long long int) s->num_5_softclips [0], + (unsigned long long int) s->num_5_softclips [1]); + + fprintf (stderr, "bases_5_softclips_qry [fwd, rev]=[%llu, %llu]\n", + (unsigned long long int) s->bases_5_softclips_qry [0], + (unsigned long long int) s->bases_5_softclips_qry [1]); + + fprintf (stderr, "bases_5_softclips_ref [fwd, rev]=[%llu, %llu]\n", + (unsigned long long int) s->bases_5_softclips_ref [0], + (unsigned long long int) s->bases_5_softclips_ref [1]); - fprintf(stderr, "num_realign_invocations=%llu\n", (unsigned long long int)s->num_realign_invocations); - fprintf(stderr, "num_realign_already_perfect=%llu\n", (unsigned long long int)s->num_realign_already_perfect); - fprintf(stderr, "num_realign_not_clipped=%llu\n", (unsigned long long int)s->num_realign_not_clipped); - fprintf(stderr, "num_realign_sw_failures=%llu\n", (unsigned long long int)s->num_realign_sw_failures); - fprintf(stderr, "num_realign_unclip_failures=%llu\n", (unsigned long long int)s->num_realign_unclip_failures); - fprintf(stderr, "num_realign_changed=%llu\n", (unsigned long long int)s->num_realign_changed); - fprintf(stderr, "num_realign_shifted=%llu\n", (unsigned long long int)s->num_realign_shifted); - - fprintf(stderr, "num_seen_tailclipped=%llu\n", (unsigned long long int)s->num_seen_tailclipped); - fprintf(stderr, "bases_seen_tailclipped=%llu\n", (unsigned long long int)s->bases_seen_tailclipped); - fprintf(stderr, "num_tailclipped=%llu\n", (unsigned long long int)s->num_tailclipped); - fprintf(stderr, "bases_tailclipped=%llu\n", (unsigned long long int)s->bases_tailclipped); - fprintf(stderr, "num_fully_tailclipped=%llu\n", (unsigned long long int)s->num_fully_tailclipped); - fprintf(stderr, "bases_fully_tailclipped=%llu\n", (unsigned long long int)s->bases_fully_tailclipped); + fprintf (stderr, "bases_5_softclips_ref [fwd, rev]=[%llu, %llu]\n", + (unsigned long long int) s->bases_5_softclips_ref [0], + (unsigned long long int) s->bases_5_softclips_ref [1]); + + fprintf (stderr, "score_5_softclips_total [fwd, rev]=[%lld, %lld]\n", + (long long int) s->score_5_softclips_total [0], + (long long int) s->score_5_softclips_total [1]); + + fprintf (stderr, "num_seen_tailclipped=%llu\n", (unsigned long long int)s->num_seen_tailclipped); + fprintf (stderr, "bases_seen_tailclipped=%llu\n", (unsigned long long int)s->bases_seen_tailclipped); + fprintf (stderr, "num_tailclipped=%llu\n", (unsigned long long int)s->num_tailclipped); + fprintf (stderr, "bases_tailclipped=%llu\n", (unsigned long long int)s->bases_tailclipped); + fprintf (stderr, "num_fully_tailclipped=%llu\n", (unsigned long long int)s->num_fully_tailclipped); + fprintf (stderr, "bases_fully_tailclipped=%llu\n", (unsigned long long int)s->bases_fully_tailclipped); - fprintf(stderr, "num_hpcost_invocations=%llu\n", (unsigned long long int)s->num_hpcost_invocations); - fprintf(stderr, "num_hpcost_skipped=%llu\n", (unsigned long long int)s->num_hpcost_skipped); - fprintf(stderr, "num_hpcost_modified=%llu\n", (unsigned long long int)s->num_hpcost_modified); - fprintf(stderr, "num_hpcost_shifted=%llu\n", (unsigned long long int)s->num_hpcost_shifted); - fprintf(stderr, "num_filtered_als=%llu\n", (unsigned long long int)s->num_filtered_als); + fprintf (stderr, "num_filtered_als=%llu\n", (unsigned long long int)s->num_filtered_als); } diff --git a/Analysis/TMAP/src/map/util/tmap_map_stats.h b/Analysis/TMAP/src/map/util/tmap_map_stats.h index b8f8ceb6..2971d1c2 100644 --- a/Analysis/TMAP/src/map/util/tmap_map_stats.h +++ b/Analysis/TMAP/src/map/util/tmap_map_stats.h @@ -5,7 +5,16 @@ /*! The mapping statistics structure. */ -typedef struct { +enum // index into stats arrays +{ + F5P = 0, + F3P = 1, + R5P = 2, + R3P = 3 +}; + +typedef struct +{ uint64_t num_reads; /*!< the number of reads with at least one mapping */ uint64_t num_with_mapping; /*!< the number of reads with at least one mapping */ uint64_t num_after_seeding; /*!< the number of hits after seeding */ @@ -13,6 +22,11 @@ typedef struct { uint64_t num_after_scoring; /*!< the number of hits after scoring */ uint64_t num_after_rmdup; /*!< the number of hits after duplicate removal */ uint64_t num_after_filter; /*!< the number of hits after filtering */ + // statistics for realignment with context-dependent gap cost + uint64_t num_hpcost_invocations; + uint64_t num_hpcost_skipped; + uint64_t num_hpcost_modified; + uint64_t num_hpcost_shifted; // realigner statistics uint64_t num_realign_invocations; /*!< the number of alignments ran through realignment procedure*/ uint64_t num_realign_already_perfect; /*!< the number of alignments that considered already perfect and thus were not re-processed*/ @@ -21,6 +35,25 @@ typedef struct { uint64_t num_realign_unclip_failures; /*!< the number of realigner un-clipping procedure failures*/ uint64_t num_realign_changed; /*!< the number of alignments that were modified adjusted by realigner*/ uint64_t num_realign_shifted; /*!< the number of alignments for which the position on the reference was altered by realigner*/ + // long gap salvage stats + uint64_t reads_salvaged; + uint64_t num_salvaged [4]; + uint64_t bases_salvaged_qry [4]; + uint64_t bases_salvaged_ref [4]; + int64_t score_salvaged_total [4]; + // end repair stats + uint64_t reads_end_repair_clipped; + uint64_t num_end_repair_clipped [4]; + uint64_t bases_end_repair_clipped [4]; + uint64_t reads_end_repair_extended; + uint64_t num_end_repair_extended [4]; + uint64_t bases_end_repair_extended [4]; + uint64_t total_end_repair_indel [4]; + // 5' softclip removal stats + uint64_t num_5_softclips [2]; + uint64_t bases_5_softclips_qry [2]; + uint64_t bases_5_softclips_ref [2]; + int64_t score_5_softclips_total [2]; // tail repeat clipping stats uint64_t num_seen_tailclipped; uint64_t bases_seen_tailclipped; @@ -28,11 +61,6 @@ typedef struct { uint64_t bases_tailclipped; uint64_t num_fully_tailclipped; uint64_t bases_fully_tailclipped; - // statistics for realignment with context-dependent gap cost - uint64_t num_hpcost_invocations; - uint64_t num_hpcost_skipped; - uint64_t num_hpcost_modified; - uint64_t num_hpcost_shifted; // number of filtered alignments uint64_t num_filtered_als; } tmap_map_stats_t; @@ -41,13 +69,13 @@ typedef struct { @return a new stats structure */ tmap_map_stats_t* -tmap_map_stats_init(); +tmap_map_stats_init (); /*! @param s the mapping driver stats to destroy */ void -tmap_map_stats_destroy(tmap_map_stats_t *s); +tmap_map_stats_destroy (tmap_map_stats_t *s); /*! Adds the src stats to the dest stats @@ -55,13 +83,13 @@ tmap_map_stats_destroy(tmap_map_stats_t *s); @param src the source */ void -tmap_map_stats_add(tmap_map_stats_t *dest, tmap_map_stats_t *src); +tmap_map_stats_add (tmap_map_stats_t *dest, tmap_map_stats_t *src); /*! Zeroes all counters @param s the pointer to stats holding object */ void -tmap_map_stats_zero(tmap_map_stats_t *s); +tmap_map_stats_zero (tmap_map_stats_t *s); #endif // TMAP_MAP_STATS_H diff --git a/Analysis/TMAP/src/map/util/tmap_map_util.c b/Analysis/TMAP/src/map/util/tmap_map_util.c index 58ae2fdf..c86c4961 100644 --- a/Analysis/TMAP/src/map/util/tmap_map_util.c +++ b/Analysis/TMAP/src/map/util/tmap_map_util.c @@ -1,7 +1,9 @@ /* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ + #include #include #include +#include #include "../../util/tmap_alloc.h" #include "../../util/tmap_error.h" #include "../../util/tmap_sam_convert.h" @@ -29,6 +31,9 @@ _query[_ql-_i-1] = _tmp; \ } + +// #define WARN_END_REPAIR_FAILURE 1 + // sort by strand, min-seqid, min-position #define __tmap_map_sam_sort_coord_lt(a, b) ( ((a).strand < (b).strand) \ || ( (a).strand == (b).strand && (a).seqid < (b).seqid) \ @@ -59,6 +64,23 @@ TMAP_SORT_INIT(tmap_map_sam_sort_coord_end, tmap_map_sam_t, __tmap_map_sam_sort_ TMAP_SORT_INIT(tmap_map_sam_sort_coord_score, tmap_map_sam_t, __tmap_map_sam_sort_coord_score_lt) TMAP_SORT_INIT(tmap_map_sam_sort_score_coord, tmap_map_sam_t, __tmap_map_sam_sort_score_coord_lt) +static void tmap_map_util_set_target_len( tmap_map_sam_t *s) +{ + s->target_len = 0; + int j; + for (j = 0; j < s->n_cigar; j++) + { + switch (TMAP_SW_CIGAR_OP (s->cigar [j])) + { + case BAM_CMATCH: + case BAM_CDEL: + s->target_len += TMAP_SW_CIGAR_LENGTH(s->cigar[j]); + break; + default: + break; + } + } +} static int tmap_map_get_amplicon(tmap_refseq_t *refseq, @@ -108,21 +130,30 @@ tmap_map_get_amplicon(tmap_refseq_t *refseq, } +// use softclip settings from tmap parameters +// disallow 3' softclip, if +// - adapter bases are present at 3' (sequencing reached the adapter) +// - and there are more of them than max_adapter_bases_for_soft_clipping, +// - and there is an insert (sequence between key abd barcode and before 3' adapter +// - and sequence is more than 5 bases longer than the insert static void -tmap_map_util_set_softclip(tmap_map_opt_t *opt, tmap_seq_t *seq, int32_t *softclip_start, int32_t *softclip_end) -{ - (*softclip_start) = (TMAP_MAP_OPT_SOFT_CLIP_LEFT == opt->softclip_type || TMAP_MAP_OPT_SOFT_CLIP_ALL == opt->softclip_type) ? 1 : 0; - (*softclip_end) = (TMAP_MAP_OPT_SOFT_CLIP_RIGHT == opt->softclip_type || TMAP_MAP_OPT_SOFT_CLIP_ALL == opt->softclip_type) ? 1 : 0; - // check if the ZB tag is present... - if(TMAP_SEQ_TYPE_SAM == seq->type || TMAP_SEQ_TYPE_BAM == seq->type) { // SAM/BAM - tmap_sam_t *sam = seq->data.sam; - int32_t zb = tmap_sam_get_zb(sam); - int32_t za = tmap_sam_get_za(sam); - // keep 3' soft clipping on if zb tag does not exists or there are too few adapter bases. - if(-1 != zb && opt->max_adapter_bases_for_soft_clipping < zb && -1 != za && sam->seq->l >= za-5) { - (*softclip_end) = 0; - } - } +tmap_map_util_set_softclip (tmap_map_opt_t *opt, tmap_seq_t *seq, int32_t *softclip_start, int32_t *softclip_end) +{ + (*softclip_start) = (TMAP_MAP_OPT_SOFT_CLIP_LEFT == opt->softclip_type || TMAP_MAP_OPT_SOFT_CLIP_ALL == opt->softclip_type) ? 1 : 0; + (*softclip_end) = (TMAP_MAP_OPT_SOFT_CLIP_RIGHT == opt->softclip_type || TMAP_MAP_OPT_SOFT_CLIP_ALL == opt->softclip_type) ? 1 : 0; + // check if the ZB tag is present... + if (TMAP_SEQ_TYPE_SAM == seq->type || TMAP_SEQ_TYPE_BAM == seq->type) // SAM/BAM + { + tmap_sam_t *sam = seq->data.sam; + int32_t zb = tmap_sam_get_zb (sam); + int32_t za = tmap_sam_get_za (sam); + // keep 3' soft clipping on if zb tag does not exists or there are too few adapter bases. + if (-1 != zb + && opt->max_adapter_bases_for_soft_clipping < zb + && -1 != za + && sam->seq->l >= za - 5) + (*softclip_end) = 0; + } } void @@ -133,6 +164,7 @@ tmap_map_sam_init(tmap_map_sam_t *s) // nullify s->algo_id = TMAP_MAP_ALGO_NONE; s->ascore = s->score = INT32_MIN; + //s->fivep_offset = 0; } @@ -196,6 +228,10 @@ tmap_map_sam_destroy(tmap_map_sam_t *s) free(s->cigar); s->cigar = NULL; s->n_cigar = 0; + free (s->orig_cigar); + s->orig_cigar = NULL; + s->n_orig_cigar = 0; + } tmap_map_sams_t * @@ -528,8 +564,9 @@ tmap_map_sam_print(tmap_seq_t *seq, tmap_refseq_t *refseq, tmap_map_sam_t *sam, mate_strand, mate_seqid, mate_pos, mate_tlen, sam->mapq, sam->cigar, sam->n_cigar, sam->score, sam->ascore, sam->pscore, nh, sam->algo_id, sam->algo_stage, - "\tXS:i:%d", - sam->score_subo); + "\tXS:i:%d\tXZ:i:%d", + sam->score_subo, + sam->fivep_offset); break; case TMAP_MAP_ALGO_MAPVSW: return tmap_sam_convert_mapped(seq, sam_flowspace_tags, bidirectional, seq_eq, refseq, @@ -1076,7 +1113,7 @@ tmap_map_util_mapq(tmap_map_sams_t *sams, int32_t seq_len, tmap_map_opt_t *opt, // ACGTNBDHKMRSVWYN // This gives a mask for match iupac characters versus A/C/G/T/N // A C G T N B D H K M R S V W Y N -static int32_t matrix_iupac_mask[89] = { +static int32_t matrix_iupac_mask [89] = { 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, // A 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, // C 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, // G @@ -1096,6 +1133,13 @@ static int32_t matrix_iupac_mask[89] = { (par).band_width = (opt)->bw; \ } while(0) +int32_t matrix_iupac [80]; // this does not change, can be global +void tmap_map_util_populate_sw_par (tmap_sw_param_t* par, tmap_map_opt_t* opt) +{ + par->matrix = matrix_iupac; + __map_util_gen_ap_iupac (*par, opt); +} + static inline void tmap_map_util_sw_get_start_and_end_pos(tmap_map_sam_t *sam, int32_t seq_len, uint8_t strand, uint32_t *start_pos, uint32_t *end_pos) @@ -1111,19 +1155,22 @@ tmap_map_util_sw_get_start_and_end_pos(tmap_map_sam_t *sam, int32_t seq_len, uin // NB: this function unrolls banding in some cases static int32_t -tmap_map_util_sw_gen_score_helper(tmap_refseq_t *refseq, tmap_map_sams_t *sams, - tmap_seq_t *seq, tmap_map_sams_t *sams_tmp, - int32_t *idx, int32_t start, int32_t end, - uint8_t strand, tmap_vsw_t *vsw, - int32_t seq_len, uint32_t start_pos, uint32_t end_pos, - int32_t *target_mem, uint8_t **target, - int32_t softclip_start, int32_t softclip_end, - int32_t prev_n_best, - int32_t max_seed_band, // NB: this may be modified as banding is unrolled - int32_t prev_score, // NB: must be greater than or equal to the scoring threshold - tmap_vsw_opt_t *vsw_opt, - tmap_rand_t *rand, - tmap_map_opt_t *opt) +tmap_map_util_sw_gen_score_helper +( + tmap_refseq_t *refseq, tmap_map_sams_t *sams, + tmap_seq_t *seq, tmap_map_sams_t *sams_tmp, + int32_t *idx, int32_t start, int32_t end, + uint8_t strand, tmap_vsw_t *vsw, + int32_t seq_len, uint32_t start_pos, uint32_t end_pos, + int32_t *target_mem, uint8_t **target, + int32_t softclip_start, int32_t softclip_end, + int32_t prev_n_best, + int32_t max_seed_band, // NB: this may be modified as banding is unrolled + int32_t prev_score, // NB: must be greater than or equal to the scoring threshold + tmap_vsw_opt_t *vsw_opt, + tmap_rand_t *rand, + tmap_map_opt_t *opt +) { tmap_map_sam_t tmp_sam; uint8_t *query; @@ -1223,31 +1270,101 @@ tmap_map_util_sw_gen_score_helper(tmap_refseq_t *refseq, tmap_map_sams_t *sams, */ // NB: this aligns in the sequencing direction - tmp_sam.score = tmap_vsw_process_fwd(vsw, query, qlen, (*target), tlen, - &tmp_sam.result, &overflow, opt->score_thr, 1); - - if(1 < tmp_sam.result.n_best) { - // What happens if soft-clipping or not soft-clipping causes two - // alignments to be equally likely? So while we could set the scores to be - // similar, we can also down-weight the score a little bit. - tmp_sam.score_subo = tmp_sam.score - opt->pen_gapo - opt->pen_gape; - //tmp_sam.score_subo = tmp_sam.score - 1; + // DVK: bug #11386 + { + const int16_t TARGET_EDGE_ALLOWANCE = 4; + const int16_t MAX_TARGET_LEN_FOR_VSW = 32000; + int8_t target_causes_read_clipping; + int8_t first_run = 1; + int32_t score = 0, prev_score = 0; + uint32_t new_end_pos, new_start_pos; + tmap_vsw_result_t result; + do + { + // initial assumption + target_causes_read_clipping = 0; + + // initialize the bounds - why? + result.query_start = result.query_end = 0; + result.target_start = result.target_end = 0; + score = tmap_vsw_process_fwd(vsw, query, qlen, (*target), tlen, + &result, &overflow, opt->score_thr, 1); + + if (1 == overflow) + return INT32_MIN; + + if (!first_run && score <= prev_score) // check for zone edge crossing on first run always, then only if score is improved + break; // if score did not improve, get out and keep last result/score in tmp_sam + + // query clip may be recoverable if it has 3' clip and alignment goes to target zone end + new_end_pos = end_pos; + new_start_pos = start_pos; + if (tlen - result.target_end < TARGET_EDGE_ALLOWANCE && qlen > result.query_end) + { + // extend target if possible + new_end_pos += qlen - result.query_end + TARGET_EDGE_ALLOWANCE; + if(refseq->annos[sams->sams[end].seqid].len < new_end_pos) + { + new_end_pos = refseq->annos[sams->sams[end].seqid].len; // one-based + } + } + // similarly process 5' clips + if (result.target_start < TARGET_EDGE_ALLOWANCE && result.query_start > 0) + { + if (result.query_start + TARGET_EDGE_ALLOWANCE > new_start_pos) + new_start_pos -= result.query_start + TARGET_EDGE_ALLOWANCE; + else + new_start_pos = 1; + } + if (new_end_pos > end_pos || new_start_pos < start_pos ) + { + end_pos = new_end_pos; + start_pos = new_start_pos; + // get the target sequence + tlen = end_pos - start_pos + 1; + if((*target_mem) < tlen) + { // more memory? + (*target_mem) = tlen; + tmap_roundup32((*target_mem)); + (*target) = tmap_realloc((*target), sizeof(uint8_t)*(*target_mem), "target"); + } + // NB: IUPAC codes are turned into mismatches + if(NULL == tmap_refseq_subseq2(refseq, sams->sams[end].seqid+1, start_pos, end_pos, (*target), 1, NULL)) + tmap_error("bug encountered", Exit, OutOfRange); + + // reverse compliment the target + if (1 == strand) + tmap_reverse_compliment_int((*target), tlen); + + target_causes_read_clipping = 1; + } + tmp_sam.score = score; + tmp_sam.result = result; + prev_score = score; + first_run = 0; + if (start_pos + MAX_TARGET_LEN_FOR_VSW < end_pos) + break; } - if(1 == is_long_hit) { - // if we have many seeds that span a large tandem repeat, then we should - // downweight the sub-optimal score - tmp_sam.score_subo = tmp_sam.score - opt->pen_gapo - opt->pen_gape; + while (target_causes_read_clipping); + } - /* - fprintf(stderr, "start_pos=%d end_pos=%d start=%d end=%d score=%d n_best=%d\n", - start_pos, end_pos, start, end, tmp_sam.score, tmp_sam.result.n_best); - */ - if(1 == overflow) { - return INT32_MIN; - //tmap_error("bug encountered", Exit, OutOfRange); + if (1 < tmp_sam.result.n_best) + { + // What happens if soft-clipping or not soft-clipping causes two + // alignments to be equally likely? So while we could set the scores to be + // similar, we can also down-weight the score a little bit. + tmp_sam.score_subo = tmp_sam.score - opt->pen_gapo - opt->pen_gape; + //tmp_sam.score_subo = tmp_sam.score - 1; + } + if (1 == is_long_hit) + { + // if we have many seeds that span a large tandem repeat, then we should + // downweight the sub-optimal score + tmp_sam.score_subo = tmp_sam.score - opt->pen_gapo - opt->pen_gape; } + if(opt->score_thr <= tmp_sam.score) { // NB: we could also specify 'prev_score <= tmp_sam.score' int32_t add_current = 1; // yes, by default @@ -1257,7 +1374,7 @@ tmap_map_util_sw_gen_score_helper(tmap_refseq_t *refseq, tmap_map_sams_t *sams, int32_t t_start = start; int32_t t_end_pos = end_pos; uint32_t t_start_pos = start_pos; - + if(1 == opt->unroll_banding // unrolling is turned on && 0 <= max_seed_band // do we have enough bases to unrooll && 1 < end - start + 1 // are there enough seeds in this group @@ -1335,7 +1452,7 @@ tmap_map_util_sw_gen_score_helper(tmap_refseq_t *refseq, tmap_map_sams_t *sams, if(1 == add_current) { tmap_map_sam_t *s = NULL; - + if(sams_tmp->n <= (*idx)) { tmap_map_sams_realloc(sams_tmp, (*idx)+1); //tmap_error("bug encountered", Exit, OutOfRange); @@ -1402,7 +1519,8 @@ tmap_map_util_sw_gen_score_helper(tmap_refseq_t *refseq, tmap_map_sams_t *sams, return tmp_sam.score; } -typedef struct { +typedef struct +{ uint32_t seqid; int8_t strand; int32_t start; @@ -1414,13 +1532,16 @@ typedef struct { } tmap_map_util_gen_score_t; tmap_map_sams_t * -tmap_map_util_sw_gen_score(tmap_refseq_t *refseq, - tmap_seq_t *seq, - tmap_map_sams_t *sams, - tmap_seq_t **seqs, - tmap_rand_t *rand, - tmap_map_opt_t *opt, - int32_t *num_after_grouping) +tmap_map_util_sw_gen_score +( + tmap_refseq_t *refseq, + tmap_seq_t *seq, + tmap_map_sams_t *sams, + tmap_seq_t **seqs, + tmap_rand_t *rand, + tmap_map_opt_t *opt, + int32_t *num_after_grouping +) { int32_t i, j; int32_t start, end; @@ -1814,11 +1935,17 @@ tmap_map_util_sw_gen_score(tmap_refseq_t *refseq, return sams_tmp; } + static void -tmap_map_util_keytrim(uint8_t *query, int32_t qlen, - uint8_t *target, int32_t tlen, - int8_t strand, uint8_t key_base, - tmap_map_sam_t *s) +tmap_map_util_keytrim +( + uint8_t *query, int32_t qlen, + uint8_t *target, int32_t tlen, + int8_t strand, + uint8_t key_base, + tmap_map_sam_t *s, + tmap_map_stats_t* stat +) { int32_t j, k, l; int32_t op, op_len; @@ -1943,1951 +2070,3360 @@ tmap_map_util_keytrim(uint8_t *query, int32_t qlen, } } +// DK note: implementation is very inefficient, could be done in linear time static void -tmap_map_util_merge_adjacent_cigar_operations(tmap_map_sam_t *s) +tmap_map_util_merge_adjacent_cigar_operations (tmap_map_sam_t *s) { - int32_t i, j; - // merge adjacent cigar operations that have the same value - for(i=s->n_cigar-2;0<=i;i--) { - if(TMAP_SW_CIGAR_OP(s->cigar[i]) == TMAP_SW_CIGAR_OP(s->cigar[i+1])) { - TMAP_SW_CIGAR_STORE(s->cigar[i], TMAP_SW_CIGAR_OP(s->cigar[i]), TMAP_SW_CIGAR_LENGTH(s->cigar[i]) + TMAP_SW_CIGAR_LENGTH(s->cigar[i+1])); - // shift down - for(j=i+1;jn_cigar-1;j++) { - s->cigar[j] = s->cigar[j+1]; - } - s->n_cigar--; - } - } + int32_t i, j; + // merge adjacent cigar operations that have the same value + for (i = s->n_cigar-2; 0 <= i; i--) + { + if (TMAP_SW_CIGAR_OP (s->cigar [i]) == TMAP_SW_CIGAR_OP (s->cigar [i+1])) + { + TMAP_SW_CIGAR_STORE (s->cigar[i], TMAP_SW_CIGAR_OP(s->cigar[i]), TMAP_SW_CIGAR_LENGTH(s->cigar[i]) + TMAP_SW_CIGAR_LENGTH(s->cigar[i+1])); + // shift down + for (j = i + 1; j < s->n_cigar - 1; j++) + s->cigar [j] = s->cigar [j + 1]; + s->n_cigar--; + } + } } -//ZZ: finding #S#M#I or #S#M#D alignment for the two sequences allowing at most M mismatch. +// ZZ: finding #S#M#I or #S#M#D alignment for the two sequences allowing at most M mismatch. +// DK: returns 1 if found, 0 if not +// DK: above cigars are for reverse orientation (at the 3' of reverse - oriented read), appearing at the beginning of the whole alignment +// DK: for forward - oriented reads, that would be #I#M#S and #D#M#S, appearing on the end of the whole alignment + static int -tmap_map_util_one_gap(uint8_t *query, int32_t qlen, uint8_t *target, int32_t tlen, int32_t min_size, int max_q, int max_t, int max_MM, int max_indel, - int8_t strand, int *n_mismatch, int *indel_size, int *is_ins, int *softclip) +tmap_map_util_one_gap ( + uint8_t *query, // query sequence (numeric-encoded as [ACGT], 1 byte per base) + int32_t qlen, // query length + uint8_t *target, // target sequence (numeric-encoded as [ACGT], 1 byte per base) + int32_t tlen, // target length + int32_t min_size, // min anchor size (min length of M segment) + int max_q, // max softclip in query + int max_t, // max clip (skipped bases, or shift) in target. This + int max_MM, // maximum allowed mismatches in the searched structure + int max_indel, // maximal number of indels (length of I or D) + int8_t strand, // strand (== orientation): 0 - forward, 1 - reverse + int *n_mismatch, // found number of mismatches + int *indel_size, // number of bases in Indel segment + int *is_ins, // 1 == Ins, 0 == Del + int *softclip, // length of prefix softclip + int pad, // the extension on either size of target buffer that is safe to access. This function checks this many bases as continuation of M segment as if no indel was present + int *extra_match) // number of consecutive matches after M segment end (check relation with pad. Are these bases in the pad??) { int i, j, q, qq, t, tt, inc; int qini, tini; - if (strand == 0) { - qini = qlen-1; tini = tlen-1; - inc = -1; - } else { - qini = tini = 0; inc = 1; - } - if (qlen < min_size || tlen < min_size) return 0; - if (qlen-min_size < max_q) max_q= qlen-min_size; - if (tlen-min_size < max_t) max_t = tlen-min_size; - if (qlen*3 < max_indel) max_indel = qlen*3; - if (/*max_MM > 1 &&*/ qlen-max_q+1 < 8*max_MM) max_MM = (qlen-max_q+1)/8; // 7-14 bp allow 1 MM, 15 allow 2 etc. - if (qlen-tlen> max_indel) return 0; - if (tlen-max_t-qlen > max_indel) return 0; + if (strand == 0) + { + qini = qlen-1; + tini = tlen-1; + inc = -1; + } + else + { + qini = tini = 0; + inc = 1; + } + if (qlen < min_size || tlen < min_size) + return 0; + if (qlen - min_size < max_q) + max_q = qlen - min_size; + if (tlen - min_size < max_t) + max_t = tlen - min_size; + //if (qlen*3 < max_indel) max_indel = qlen*3; + if (/*max_MM > 1 &&*/ qlen - max_q + 1 < 8 * max_MM) + max_MM = (qlen - max_q + 1) / 8; // 7-14 bp allow 1 MM, 15 allow 2 etc. + if (qlen - tlen > max_indel) + return 0; + if (tlen - max_t - qlen > max_indel) + return 0; //debug /* char code[] = "ACGT"; fprintf(stderr, "tlen=%d qlen=%d\n", tlen, qlen); - for (i = 0; i < tlen; i++) { - fprintf(stderr, "%c", code[target[i]]); - } + for (i = 0; i < tlen; i++) + fprintf(stderr, "%c", code[target[i]]); fprintf(stderr, "\n"); - for (i = 0; i < qlen; i++) { - fprintf(stderr,"%c", code[query[i]]); - } + for (i = 0; i < qlen; i++) + fprintf(stderr,"%c", code[query[i]]); fprintf(stderr, "\n"); */ - for (i = 0, t = tini; i < max_t; i++, t+= inc) { - int last_M = -1; - int nM = 0; - for (j = i, q = 0, qq = qini, tt = t; j < tlen && q < qlen; j++, q++, qq += inc, tt+= inc) { - if (query[qq] == target[tt]) continue; - if (q < max_q) last_M = q; - else { - nM++; - if (nM > max_MM) break; - } - } - if (j == tlen || q == qlen) { - // find one - *softclip = last_M+1; - *n_mismatch = nM; - if (j == tlen) { - *indel_size = qlen-q; - *is_ins = 1; - } else { - *indel_size = tlen-j; - *is_ins = 0; - } - if (*indel_size > max_indel) return 0; - if (*indel_size == 0) return 0; - return 1; - } + for (i = 0, t = tini; i < max_t; i++, t+= inc) + { + int last_M = -1; // last_mismatch + int nM = 0; // number_of_mismatches + for (j = i, q = 0, qq = qini, tt = t; j < tlen && q < qlen; j++, q++, qq += inc, tt+= inc) + { + if (query [qq] == target [tt]) + continue; + if (q < max_q) + last_M = q; + else + { + nM++; + if (nM > max_MM) + break; + } + } + if (j == tlen || q == qlen) + { + // find one + *softclip = last_M + 1; + *n_mismatch = nM; + *extra_match = 0; + if (j == tlen) + { + *indel_size = qlen-q; + *is_ins = 1; + } + else + { + *indel_size = tlen - j; + *is_ins = 0; + } + if (*indel_size > max_indel) + return 0; + if (*indel_size == 0) + return 0; + // here for strand == 0 we may go into negative index, intentionally. careful! + // extend beyond the parallel + // DK: for this to work correctly, passed pad should always be confined within the passed query and target buffers, with corrections for clips and indels. Is this the case - need to check calling code.!? + //fprintf(stderr, "find gap strand %d pad %d %d %d %d\n", strand, pad, j, tlen, qlen); + for (j = 0; j < pad; j++, qq += inc, tt += inc) + { + //fprintf(stderr, "%d %d %d %d\n", qq, tt, query[qq], target[tt]); + if (query [qq] != target [tt]) + break; + (*extra_match) += 1; + } + if (*indel_size > 3 * (qlen + (*extra_match))) // reject indels 3 or more times longer than query + matching 'pad' + return 0; + return 1; + } } return 0; } +// swaps two integers (passed by pointers) +static void exchange(int32_t *s, int32_t *t) +{ + int32_t temp = *s; *s = *t; *t = temp; +} -static void -tmap_map_util_end_repair(tmap_seq_t *seq, uint8_t *query, int32_t qlen, - uint8_t *target_prev, int32_t tlen, - int8_t strand, - tmap_sw_path_t *path, - tmap_refseq_t *refseq, - tmap_map_sam_t *s, - tmap_map_opt_t *opt, int repair5p) -{ - int32_t i, cigar_i; - int32_t op, op_len, cur_len; - int32_t cur_op, cur_op_len, cur_cigar_i, cur_cur_len, target_adj; - int32_t softclip_start, softclip_end; - int32_t old_score, new_score; - int32_t start_pos, end_pos; - // scoring matrix - int32_t matrix[25], matrix_iupac[80]; - tmap_sw_param_t par, par_iupac; - int32_t path_len = 0; - uint32_t *cigar = NULL; - int32_t n_cigar = 0; - int32_t found = 0; - - // TODO: use outside memory - uint8_t *target = NULL; - int32_t target_mem = 0; - int32_t conv = 0; - - // PARAMETERS - int32_t max_match_len = 5; // the maximum # of query bases to consider for the sub-alignment - int32_t num_prev_bases = 2; // the maximum number of bases prior to the alignment to include in the target - - // check if we allow soft-clipping on the 5' end - tmap_map_util_set_softclip(opt, seq, &softclip_start, &softclip_end); - if (opt->end_repair > 2) { - if (repair5p == 0 && 1 == softclip_end) return; // allow softclip, then no need to repair. - if (repair5p == 1 && 1 == softclip_start) return; - if (repair5p) strand = (!strand); - old_score = 0; - found = 0; - int worst = 0; - int ind = 0, nMM = 0, /*nc = 0,*/ nqb = 0, ntb = 0, nCC, target_red = 0; - int tb, qb, inc; - int i_c; - int i, nn_cigar=0, total_scl = 0; - int max_gap = 0, num_gap = 0; // ZZ, for TS-10540 - if (0 != strand) { tb = qb = 0; inc = 1;} - else {tb = tlen-1; qb = qlen-1; inc = -1;} - for (i_c = 0; i_c < s->n_cigar; i_c++) { - cigar_i = (0 != strand) ? i_c : s->n_cigar-1-i_c; - op = TMAP_SW_CIGAR_OP(s->cigar[cigar_i]); - op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cigar_i]); - if (op == BAM_CMATCH) { - for(i=0;iscore_match; - else old_score -= opt->pen_mm, found++; - if (old_score < worst) { worst = old_score; ind = i+1; nMM = found; target_red =ntb+1; nn_cigar = i_c; total_scl = nqb+1;} - } - } else { - if (op == BAM_CDEL) { - ntb += op_len; - tb += inc*op_len; - } else if (op == BAM_CINS) { - nqb += op_len; - qb += inc*op_len; - } else break; - old_score -= opt->pen_gapo+opt->pen_gape*op_len; - found += op_len; - if (old_score < worst) { worst = old_score; ind = 0; nMM = found; target_red =ntb; nn_cigar = i_c; total_scl = nqb; - num_gap++; if (op_len > max_gap) max_gap = op_len; // ZZ, for TS-10540 - } - } - if (old_score > 6*opt->score_match) break; // no need go further. - } - if (worst >= 0) return; - nCC = target_red+total_scl+1; - if (num_gap < 3 && max_gap >= 3) { - int del = max_gap/15; //increase the error by 1 for each 15 bp of the gap. - int adj = max_gap-1-del; - /* int adj = max_gap-1;*/ - nMM -= adj; nCC-= adj; - } - nCC /= 2; - int maxMM = (nCC-2)*opt->end_repair/100+2; - //if (repair5p) maxMM = (nCC-2)*50/100+2; // temp, try symmetric - if (maxMM < 2) maxMM = 2; // no need? always at least 2 - if (repair5p && total_scl <= 5) maxMM = nMM+1; // for 5', do not repair short stretch - if (nCC < maxMM) maxMM = nCC; - if (nMM >= maxMM) { - s->score -= worst; - cigar_i = (0 != strand) ? nn_cigar : s->n_cigar-1-nn_cigar; - op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cigar_i]); - if (ind == op_len || ind == 0) { - if (nn_cigar == s->n_cigar-1) return; // all are removed, something not quite right. Give up - if (nn_cigar == s->n_cigar-2) { - if (TMAP_SW_CIGAR_OP(s->cigar[cigar_i+inc]) == BAM_CSOFT_CLIP) return; // something wrong, will become XSYS?? - } - TMAP_SW_CIGAR_STORE(s->cigar[cigar_i], BAM_CSOFT_CLIP, total_scl); - s->n_cigar -= nn_cigar; - } else { - TMAP_SW_CIGAR_STORE(s->cigar[cigar_i], BAM_CMATCH, op_len-ind); - if (nn_cigar == 0) { - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(1 + s->n_cigar), "s->cigar"); - if (0 != strand) { - for (i = s->n_cigar-1; i >= 0; i--) s->cigar[i+1] = s->cigar[i]; - TMAP_SW_CIGAR_STORE(s->cigar[0], BAM_CSOFT_CLIP, total_scl); - } else { - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar], BAM_CSOFT_CLIP, total_scl); - } - s->n_cigar++; - } else { - cigar_i -= inc; // move back one - TMAP_SW_CIGAR_STORE(s->cigar[cigar_i], BAM_CSOFT_CLIP, total_scl); - s->n_cigar -= nn_cigar-1; - } - } - if (0 != strand && cigar_i > 0) { - for (i = 0; i < s->n_cigar; i++) { - s->cigar[i] = s->cigar[i+cigar_i]; - } - } - if (0 != strand) s->pos += target_red; - s->target_len -= target_red; - // do one large gap label:oneGapZZ - uint32_t ampl_start, ampl_end; - if (refseq->bed_exist && total_scl > 5 && - tmap_map_get_amplicon(refseq, s->seqid, s->pos, s->pos+s->target_len-1, &l_start, &l_end, strand)) { - //fprintf(stderr, "%d %d \n", ampl_start, ampl_end); - int need_do = 1; - int half_buffer = /*6*/ opt->amplicon_overrun; // outside freedom. - int buffer = half_buffer+3; - uint32_t start, end; - if (strand == 0) { - start = s->pos + s->target_len + 1; - end = ampl_end+half_buffer; - if (end > refseq->annos[s->seqid].len) end = refseq->annos[s->seqid].len; - query += qlen-total_scl; - } else { - if (ampl_start <= half_buffer) start = 1; - else start = ampl_start-half_buffer; - end = s->pos; - } - tlen = end-start+1; - //fprintf(stderr, "%d %d \n", start, end); - if (tlen > 5) { - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - } - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start, end, target, 1, NULL)) { - tmap_bug(); - } - } else need_do = 0; - int softclip, target_adj, n_mismatch, indel_size, is_ins; - if (need_do && tmap_map_util_one_gap(query, total_scl, target, tlen, opt->min_anchor_large_indel_rescue /*anchor size*/, 2/*int max_q*/, buffer /*int max_t*/, 3 /*maxNM*/, opt->max_one_large_indel_rescue, /*int max_indel*/ - strand, &n_mismatch, &indel_size, &is_ins, &softclip)) { - //adjust cigar - int match_size; - int target_adj; // how many additional target bases aligned. - if (is_ins) match_size = total_scl-softclip-indel_size; - else match_size = total_scl-softclip; - target_adj = match_size; - if (!is_ins) target_adj += indel_size; - int s_adj = (match_size-n_mismatch)*opt->score_match-n_mismatch*opt->pen_mm-opt->pen_gapo-opt->pen_gape; - s->score += s_adj; - int inc_cigar = 1; - if (softclip> 0) inc_cigar++; - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(inc_cigar + s->n_cigar), "s->cigar"); - if (strand == 0) { - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar-1], is_ins? BAM_CINS: BAM_CDEL , indel_size); - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar], BAM_CMATCH, match_size); - s->n_cigar++; - if (softclip> 0) { - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar], BAM_CSOFT_CLIP, softclip); - s->n_cigar++; - } - } else { - for (i = s->n_cigar-1; i >= 0; i--) s->cigar[i+inc_cigar] = s->cigar[i]; - i = 0; - if (softclip> 0) TMAP_SW_CIGAR_STORE(s->cigar[i++], BAM_CSOFT_CLIP, softclip); - TMAP_SW_CIGAR_STORE(s->cigar[i++], BAM_CMATCH, match_size); - TMAP_SW_CIGAR_STORE(s->cigar[i], is_ins? BAM_CINS: BAM_CDEL , indel_size); - s->pos -= target_adj; - s->n_cigar+= inc_cigar; - } - s->target_len += target_adj; - free(target); - } - } - } +static int +trim_read_bases ( + tmap_map_sam_t *s, + int extra_match, + int from_head, + int *is_ins, + int *indel_len) +{ + if (extra_match <= 0) + return 0; + int32_t indel_type, other_type; - /* - cigar_i = (0 != strand) ? 0 : (s->n_cigar - 1); - op = TMAP_SW_CIGAR_OP(s->cigar[cigar_i]); - op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cigar_i]); - if(op != BAM_CMATCH) return; // right now just check terminal matches, will extend to other. - old_score = 0; - found = 0; // did we find mismatches? - int worst = 0; - cur_len = op_len; - int ind = 0, nMM = 0; - if(0 != strand) { // reverse - for(i=0;iscore_match; - else old_score -= opt->pen_mm, found++; - if (old_score < worst) { worst = old_score; ind = i+1; nMM = found;} - } + indel_type = BAM_CDEL; other_type = BAM_CINS; + if (!(*is_ins)) + exchange(&indel_type, &other_type); + + int inc, init, i, j; + int dec = 0; + int in_len = *indel_len; + + if (from_head) + { + init = 1; + inc = 1; } - else { // forward - for(i=0;iscore_match; - else old_score -= opt->pen_mm, found++; - if (old_score < worst) { worst = old_score; ind = i+1; nMM = found;} - } + else + { + init = s->n_cigar-2; + inc = -1; } - if (worst >= 0) return; // local alignment is ok - int maxMM = 2; - if (ind > 5) maxMM = (ind-2)*opt->end_repair/100+2; - if (ind < maxMM) maxMM = ind; - if (nMM >= maxMM) { // trim - s->score -= worst; - if (ind == op_len) { // remove the whole cigar - if (s->n_cigar == 1) return; - TMAP_SW_CIGAR_STORE(s->cigar[cigar_i], BAM_CSOFT_CLIP, op_len); - } else { - TMAP_SW_CIGAR_STORE(s->cigar[cigar_i], BAM_CMATCH, op_len-ind); - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(1 + s->n_cigar), "s->cigar"); - if (0 != strand) { - int i; - for (i = s->n_cigar-1; i >= 0; i--) s->cigar[i+1] = s->cigar[i]; - TMAP_SW_CIGAR_STORE(s->cigar[0], BAM_CSOFT_CLIP, ind); - } else { - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar], BAM_CSOFT_CLIP, ind); - } - s->n_cigar++; - } - if (0 != strand) s->pos += ind; - s->target_len -= ind; - //fprintf(stderr, "ZZ tlen%d %d %d %d worst %d MM=%d %d\n", s->target_len, ind, s->score, s->pos, worst, maxMM, nMM); + int32_t op, op_len; + for (i = 0, j = init; extra_match > 0 && i < s->n_cigar-1; i++, j += inc) + { + op = TMAP_SW_CIGAR_OP (s->cigar[j]); + op_len = TMAP_SW_CIGAR_LENGTH (s->cigar[j]); + //fprintf(stderr, "%d %d extra_match=%d j=%d indel=%d other=%d\n", op, op_len, extra_match, j, indel_type, other_type); + if (op == other_type) + { + in_len += op_len; + dec++; + } + else + { + //if (op == BAM_CMATCH || op == indel_type || op == BAM_CSOFT_CLIP) are the only choice + if (op == indel_type) + { + int x = extra_match > op_len ? op_len : extra_match; + if (x > in_len) + { + extra_match -= in_len; + in_len = op_len - in_len; + exchange (&indel_type, &other_type); // switch indel type now, tricky! ZZ + dec++; // put the indel as the ending one. so remove this op. + continue; + } + in_len -= x; + } + if (extra_match >= op_len) + { + extra_match -= op_len; + dec++; + } + else + { + op_len -= extra_match; + TMAP_SW_CIGAR_STORE(s->cigar[j], op, op_len); + break; + } + } } + if (i >= s->n_cigar-1) + tmap_error("extra match exceed cigar length", Exit, OutOfRange); + /* may not need the following + op = TMAP_SW_CIGAR_OP(s->cigar[j]); + op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[j]); + + if (op == other_type) {dec++; in_len += op_len;} */ - return; - } - // do not perform if so - if (repair5p) return; - if(1 == softclip_start) return; + *is_ins = (indel_type == BAM_CINS)? 0:1; + *indel_len = in_len; + return dec; +} - // check the first cigar op - cigar_i = (0 == strand) ? 0 : (s->n_cigar - 1); - op = TMAP_SW_CIGAR_OP(s->cigar[cigar_i]); - op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cigar_i]); - if(op != BAM_CMATCH) return; // ignore, since we care only about turning mismatches into indels +static uint32_t +tmap_map_util_end_repair ( + tmap_seq_t *seq, uint8_t *query, int32_t qlen, + uint8_t *target_prev, int32_t tlen, + int8_t strand, + tmap_sw_path_t **path_buf, + int32_t* path_buf_sz, + tmap_refseq_t *refseq, + tmap_map_sam_t *s, + tmap_map_opt_t *opt, + int repair5p, + tmap_map_stats_t* stat +) +{ + int32_t i, cigar_i; + int32_t op, op_len, cur_len; + int32_t cur_op, cur_op_len, cur_cigar_i, cur_cur_len, target_adj; + int32_t softclip_start, softclip_end; + int32_t old_score, new_score; + int32_t start_pos, end_pos; + // scoring matrix + int32_t matrix [25], matrix_iupac [80]; + tmap_sw_param_t par, par_iupac; + int32_t path_len = 0; + uint32_t *cigar = NULL; + int32_t n_cigar = 0; + int32_t found = 0; + + // TODO: use outside memory + uint8_t *target = NULL; + int32_t target_mem = 0; + int32_t conv = 0; + + // PARAMETERS + int32_t max_match_len = 5; // the maximum # of query bases to consider for the sub-alignment + int32_t num_prev_bases = 2; // the maximum number of bases prior to the alignment to include in the target + + // side and orientation for stats + int32_t sori = repair5p?(s->strand?R5P:F5P):(s->strand?R3P:F3P); + int32_t repaired = 0; // return value: 0 for no change, 1 for softclip only, 2 for indel salvage. + + // check if we allow soft-clipping on the 5' end + tmap_map_util_set_softclip (opt, seq, &softclip_start, &softclip_end); + int8_t o_strand = strand; + if (opt->end_repair > 2) // Modern End-Repair, invoked when --end-repair parameter is above 2 (and denotes percent of mismatches above which to trim the alignment) + { + if (repair5p == 0 && 1 == softclip_end) + return repaired; // allow softclip, then no need to repair. + if (repair5p == 1 && 1 == softclip_start) + return repaired; + //fprintf(stderr, "repair5p=%d %d\n", repair5p, strand); + if (repair5p) + strand = (!strand); + old_score = 0; // score of original (passed in) alignment + found = 0; // number of non-matches (mismatches, inserts, deletes) found in alignment + int worst = 0; // worst alignment score encountered along the alignment + int ind = 0; // index of base within the cigar operation where worst alignment score is achieved + int nMM = 0; // number of mismatches in the part of alignment before worst score position + int nqb = 0; // number of query bases seen + int ntb = 0; // number of target bases seen + int nCC; // (?) NuCleotide Count - 'average' of target and query lengths involved in (old, passed in) alignment + int target_red = 0; // Target Reduction: the offset on target to the position of worst score + int tb, qb, inc; // target position, query position, increment (+1 | -1) for walking along target / query + int i_c; // index of the cigar operation + int i; // index of base within cigar operation + int nn_cigar = 0; // cigar operation index where worst alignment score is achieved + int total_scl = 0; // total soft clip (?) - actually the offset on query to the position of worst score + int max_gap = 0, num_gap = 0; // ZZ, for TS-10540 + + // remove softclip, shall not be necessary, remove later?? ZZ + + if (strand == 0) + { + op = TMAP_SW_CIGAR_OP (s->cigar [s->n_cigar - 1]); + op_len = TMAP_SW_CIGAR_LENGTH (s->cigar [s->n_cigar - 1]); + if (op == BAM_CSOFT_CLIP) + { + op = TMAP_SW_CIGAR_OP (s->cigar [s->n_cigar - 2]); + int32_t op_len1 = TMAP_SW_CIGAR_LENGTH (s->cigar [s->n_cigar - 2]); + if (op == BAM_CMATCH) + TMAP_SW_CIGAR_STORE (s->cigar [s->n_cigar - 2], op, op_len + op_len1); + else + return repaired; + s->n_cigar--; + tmap_map_util_set_target_len (s); + qlen += op_len; + tlen = s->target_len; + repaired = 1; + } + } + else + { + op = TMAP_SW_CIGAR_OP (s->cigar [0]); + op_len = TMAP_SW_CIGAR_LENGTH (s->cigar [0]); + if (op == BAM_CSOFT_CLIP) + { + op = TMAP_SW_CIGAR_OP (s->cigar [1]); + int32_t op_len1 = TMAP_SW_CIGAR_LENGTH (s->cigar [1]); + if (op == BAM_CMATCH) + { + TMAP_SW_CIGAR_STORE (s->cigar[1], op, op_len+op_len1); + s->pos -= op_len; + } + else + return repaired; + for (i = 0; i < s->n_cigar-1; i++) + s->cigar [i] = s->cigar [i+1]; + s->n_cigar--; + tmap_map_util_set_target_len (s); + query -= op_len; + target_prev -= op_len; + qlen += op_len; + tlen = s->target_len; + repaired = 1; + } + } - // get the amount of bases to adjust - cur_len = (max_match_len < op_len) ? max_match_len : op_len; - // NB: must include full HPs - while(cur_len < qlen && query[cur_len-1] == query[cur_len]) { // TODO: what about reference HPs? - cur_len++; - } - if(cur_len < qlen) { - cur_len++; // NB: include one more base to get left-justification correct - } + // evaluate if repairing makes sense + if (0 != strand) + { + tb = qb = 0; + inc = 1; + } + else + { + tb = tlen-1; + qb = qlen-1; + inc = -1; + } + for (i_c = 0; i_c < s->n_cigar; i_c++) + { + cigar_i = (0 != strand) ? i_c : s->n_cigar-1-i_c; + op = TMAP_SW_CIGAR_OP(s->cigar[cigar_i]); + op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cigar_i]); + if (op == BAM_CMATCH) + { + for (i = 0; i < op_len; i++, qb += inc, tb += inc, nqb++, ntb++) + { + if (qb == -1 || tb == -1) + tmap_bug (); + if (query [qb] == target_prev [tb]) + old_score += opt->score_match; + else + { + old_score -= opt->pen_mm; + found++; + } + if (old_score < worst) + { + worst = old_score; + ind = i + 1; + nMM = found; + target_red = ntb + 1; + nn_cigar = i_c; + total_scl = nqb + 1; + } + } + } + else + { + if (op == BAM_CDEL) + { + ntb += op_len; + tb += inc * op_len; + } + else if (op == BAM_CINS) + { + nqb += op_len; + qb += inc*op_len; + } + else + break; + old_score -= opt->pen_gapo + opt->pen_gape * op_len; + found += op_len; + if (old_score < worst) + { + worst = old_score; + ind = 0; + nMM = found; + target_red = ntb; + nn_cigar = i_c; + total_scl = nqb; + num_gap++; + if (op_len > max_gap) + max_gap = op_len; // ZZ, for TS-10540 + } + } + if (old_score > 6 * opt->score_match) + break; // no need go further. + } + if (worst >= 0) + return repaired; + nCC = target_red + total_scl + 1; + if (num_gap < 3 && max_gap >= 3) + { + int del = max_gap / 15; // increase the error by 1 for each 15 bp of the gap. + int adj = max_gap - 1 - del; + /* int adj = max_gap-1;*/ + nMM -= adj; + nCC -= adj; + } + nCC /= 2; + int maxMM = (nCC - 2) * opt->end_repair / 100 + 2; // DK: integer arithmetics ! + if (maxMM < 2) + maxMM = 2; // no need? always at least 2 + if (nCC < maxMM) + maxMM = nCC; + if (nMM >= maxMM) // observed (and adjusted) number of mismatches BEFORE THE WORST SCORE POSITION is over maximal tolerable number of mismatches + { + s->score -= worst; + int start_o = s->pos, end_o = s->pos + s->target_len; + + // introduce softclip from worst alignment score position to the end (proper one, depending on passed in alignment 'end' for repair and on read strand) + cigar_i = (0 != strand) ? nn_cigar : s->n_cigar - 1 - nn_cigar; + op_len = TMAP_SW_CIGAR_LENGTH (s->cigar [cigar_i]); + if (ind == op_len || ind == 0) + { + if (nn_cigar == s->n_cigar - 1) + return repaired; // all are removed, something not quite right. Give up + if (nn_cigar == s->n_cigar - 2) + { + if (TMAP_SW_CIGAR_OP (s->cigar [cigar_i + inc]) == BAM_CSOFT_CLIP) + return repaired; // something wrong, will become XSYS?? + } + TMAP_SW_CIGAR_STORE (s->cigar [cigar_i], BAM_CSOFT_CLIP, total_scl); + s->n_cigar -= nn_cigar; + } + else + { + TMAP_SW_CIGAR_STORE (s->cigar [cigar_i], BAM_CMATCH, op_len - ind); + if (nn_cigar == 0) + { + s->cigar = tmap_realloc (s->cigar, sizeof (uint32_t) * (1 + s->n_cigar), "s->cigar"); + if (0 != strand) + { + for (i = s->n_cigar - 1; i >= 0; i--) + s->cigar [i+1] = s->cigar [i]; + TMAP_SW_CIGAR_STORE (s->cigar [0], BAM_CSOFT_CLIP, total_scl); + } + else + { + TMAP_SW_CIGAR_STORE (s->cigar [s->n_cigar], BAM_CSOFT_CLIP, total_scl); + } + s->n_cigar++; + } + else + { + cigar_i -= inc; // move back one + TMAP_SW_CIGAR_STORE (s->cigar [cigar_i], BAM_CSOFT_CLIP, total_scl); + s->n_cigar -= nn_cigar - 1; + } + } + // record in stat that we clipped the alignment + stat->num_end_repair_clipped [sori] ++; + stat->bases_end_repair_clipped [sori] += total_scl; + if (0 != strand && cigar_i > 0) + { + for (i = 0; i < s->n_cigar; i++) + { + s->cigar [i] = s->cigar [i+cigar_i]; + } + } + // adjust target position for skipped target bases. DK: Is this right for 5' forward (strand replaced to reverse...)? - yes seems Ok. + if (0 != strand) + s->pos += target_red; + s->target_len -= target_red; + + if (repair5p) + s->fivep_offset = target_red; + + // do one large gap label:oneGapZZ + uint32_t ampl_start, ampl_end; + int min_anchor = opt->min_anchor_large_indel_rescue; + int half_anchor = min_anchor / 2; + if (refseq->bed_exist && total_scl >= half_anchor && + tmap_map_get_amplicon (refseq, s->seqid, start_o, end_o, &l_start, &l_end, o_strand)) + { + //total_scl is desired to be larger than 5 + //fprintf(stderr, "%d %d %d qlen=%d\n", ampl_start, ampl_end, total_scl, qlen); + int max_q = 2; + int original_total_scl = total_scl; + int s_pos_adj = 0; + if (total_scl < min_anchor) + { + total_scl = min_anchor; + s_pos_adj = total_scl - original_total_scl; /* Trimming bases from alignment to get 6 in case of repeat ZZ */ + } + int need_do = 1; + int half_buffer = /*6*/ opt->amplicon_overrun; // outside freedom. + int buffer = half_buffer + 3; + uint32_t start, end; + uint32_t pad_s, pad_e, pad; + pad_s = pad_e = pad = 0; + if (strand == 0) + { + start = s->pos + s->target_len + 1 - s_pos_adj; + end = ampl_end + half_buffer; + pad_s = 100; + if (pad_s > qlen - total_scl) + pad_s = qlen - total_scl; + if (end > refseq->annos [s->seqid].len) + end = refseq->annos [s->seqid].len; + query += qlen-total_scl; + pad = pad_s; + } + else + { + if (ampl_start <= half_buffer) + start = 1; + else + start = ampl_start - half_buffer; + end = s->pos + s_pos_adj; + pad_e = 100; + if (pad_e > qlen - total_scl) + pad_e = qlen - total_scl; + if (pad_e + end > refseq->annos [s->seqid].len) + pad_e = refseq->annos [s->seqid].len - end; + pad = pad_e; + } + tlen = end - start + 1; + //fprintf(stderr, "%d %d \n", start, end); + if (tlen >= min_anchor) + { + if (target_mem < tlen + pad) + { // more memory? + target_mem = tlen + pad; + tmap_roundup32 (target_mem); + target = tmap_realloc (target, sizeof (uint8_t) * target_mem, "target"); + } + if (NULL == tmap_refseq_subseq2 (refseq, s->seqid + 1, start - pad_s, end + pad_e, target, 1, NULL)) + tmap_bug (); + } + else + need_do = 0; + int softclip, target_adj, n_mismatch, indel_size, is_ins, extra_match = 0; + if (need_do) + { + if (tmap_map_util_one_gap (query, // query + total_scl, // q_len + target + pad_s, // target + tlen, // t_len + opt->min_anchor_large_indel_rescue /*anchor size*/, // min_size - a + max_q, // max softclip in query + buffer /*int max_t*/, // max shift on target before M segment + 3 /*maxNM*/, // max allowed mismatches in the resulting structure + opt->max_one_large_indel_rescue, /*int max_indel*/ // max ins / del size + strand, // strand (0 forward 1 reverse) + &n_mismatch, // number of mismatches in resulting structure + &indel_size, // size of the indel in resulting structure + &is_ins, // 1==I, 0==D + &softclip, // size of softclip + pad, // size of pad on either side of target // DK: what if assymetric, like at the end of chromosome? + &extra_match)) // + { + free (target); + //adjust cigar + total_scl = original_total_scl; // adjust back + int match_size; + int target_adj; // how many additional target bases aligned. + if (is_ins) + match_size = total_scl - softclip - indel_size; + else + match_size = total_scl - softclip; + target_adj = match_size; + if (!is_ins) + target_adj += indel_size; + int s_adj = (match_size - n_mismatch) * opt->score_match - n_mismatch * opt->pen_mm - opt->pen_gapo - opt->pen_gape; + s->score += s_adj; + // adjust match size + int inc_cigar = 1; + if (softclip > 0) + inc_cigar++; + int dec_cigar = trim_read_bases (s, extra_match, strand, &is_ins, &indel_size); + if (inc_cigar > dec_cigar) + s->cigar = tmap_realloc (s->cigar, sizeof (uint32_t) * (inc_cigar - dec_cigar + s->n_cigar), "s->cigar"); + match_size += extra_match; + if (strand == 0) + { + if (dec_cigar > 0) + { + s->n_cigar -= dec_cigar; + } + i = s->n_cigar - 1; + if (indel_size > 0) + TMAP_SW_CIGAR_STORE (s->cigar [i++], is_ins ? BAM_CINS : BAM_CDEL, indel_size); + TMAP_SW_CIGAR_STORE (s->cigar [i++], BAM_CMATCH, match_size); + if (softclip > 0) + { + TMAP_SW_CIGAR_STORE (s->cigar [i++], BAM_CSOFT_CLIP, softclip); + } + s->n_cigar = i; + } + else + { + int move = inc_cigar - dec_cigar; + if (indel_size == 0) + move--; + if (move > 0) + { + for (i = s->n_cigar-1; i >= dec_cigar; i--) + s->cigar [i + move] = s->cigar [i]; + } + else if (move < 0) + { + for (i = dec_cigar; i < s->n_cigar; i++) + s->cigar [i + move] = s->cigar [i]; + } + s->n_cigar += move; + i = 0; + if (softclip > 0) + TMAP_SW_CIGAR_STORE (s->cigar [i++], BAM_CSOFT_CLIP, softclip); + TMAP_SW_CIGAR_STORE (s->cigar [i++], BAM_CMATCH, match_size); + if (indel_size > 0) + TMAP_SW_CIGAR_STORE (s->cigar [i++], is_ins? BAM_CINS: BAM_CDEL , indel_size); + //fprintf(stderr, "match_size=%d indel_size=%d is_ins=%d extra_match=%d\n", match_size, indel_size, is_ins, extra_match); + s->pos -= target_adj; + } + s->target_len += target_adj; + if (repair5p) + s->fivep_offset = softclip; // assume here are all mismatches ZZ. + + // DK: quick fix. merge adjacent cigar operations that have the same type + tmap_map_util_merge_adjacent_cigar_operations(s); + + // record stats + stat->num_end_repair_extended [sori] ++; + stat->bases_end_repair_extended [sori] += total_scl - softclip; + stat->total_end_repair_indel [sori] += indel_size; + + repaired = 2; + } + } + } + } + return repaired; + } + // do not perform if so + if (repair5p) + return repaired; + if(1 == softclip_start) + return repaired; + + // check the first cigar op + cigar_i = (0 == strand) ? 0 : (s->n_cigar - 1); + op = TMAP_SW_CIGAR_OP(s->cigar[cigar_i]); + op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cigar_i]); + if(op != BAM_CMATCH) + return repaired; // ignore, since we care only about turning mismatches into indels + + // get the amount of bases to adjust + cur_len = (max_match_len < op_len) ? max_match_len : op_len; + // NB: must include full HPs + while(cur_len < qlen && query[cur_len-1] == query[cur_len]) + { // TODO: what about reference HPs? + cur_len++; + } + if(cur_len < qlen) + { + cur_len++; // NB: include one more base to get left-justification correct + } - // compute the old score of this sub-alignment - old_score = 0; - // NB: assumes the op is a match/mismatch - found = 0; // did we find mismatches? - if(0 == strand) { // forward - for(i=0;iscore_match; - else old_score -= opt->pen_mm, found = 1; - } - } - else { // reverse - for(i=0;iscore_match; - else old_score -= opt->pen_mm, found = 1; - } - } - if(0 == found) return; // no mismatches, so what are you worrying about? - - // see how many bases we should include from the reference - target_adj = 0; - cur_cur_len = cur_len; - cur_cigar_i = (0 == strand) ? 0 : (s->n_cigar - 1); - cur_op = TMAP_SW_CIGAR_OP(s->cigar[cur_cigar_i]); - cur_op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cur_cigar_i]); - while(0 < cur_cur_len) { // while read bases - if(0 == cur_op_len) { // update cigar - if(0 == strand) { - cur_cigar_i++; - } - else { - cur_cigar_i--; - } - cur_op = TMAP_SW_CIGAR_OP(s->cigar[cur_cigar_i]); - cur_op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cur_cigar_i]); - } + // compute the old score of this sub-alignment + old_score = 0; + // NB: assumes the op is a match/mismatch + found = 0; // did we find mismatches? + if(0 == strand) + { // forward + for(i=0;iscore_match; + else + old_score -= opt->pen_mm, found = 1; + } + } + else + { // reverse + for(i=0;iscore_match; + else + old_score -= opt->pen_mm, found = 1; + } + } + if(0 == found) + return repaired; // no mismatches, so what are you worrying about? + + // see how many bases we should include from the reference + target_adj = 0; + cur_cur_len = cur_len; + cur_cigar_i = (0 == strand) ? 0 : (s->n_cigar - 1); + cur_op = TMAP_SW_CIGAR_OP(s->cigar[cur_cigar_i]); + cur_op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cur_cigar_i]); + while(0 < cur_cur_len) + { // while read bases + if(0 == cur_op_len) + { // update cigar + if(0 == strand) + { + cur_cigar_i++; + } + else + { + cur_cigar_i--; + } + cur_op = TMAP_SW_CIGAR_OP(s->cigar[cur_cigar_i]); + cur_op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[cur_cigar_i]); + } - switch(cur_op) { - case BAM_CMATCH: - cur_cur_len--; - target_adj++; - break; - case BAM_CINS: - cur_cur_len--; - break; - case BAM_CDEL: - target_adj++; - break; - default: - tmap_bug(); - break; - } - cur_op_len--; - } + switch(cur_op) + { + case BAM_CMATCH: + cur_cur_len--; + target_adj++; + break; + case BAM_CINS: + cur_cur_len--; + break; + case BAM_CDEL: + target_adj++; + break; + default: + tmap_bug(); + break; + } + cur_op_len--; + } - // get more target upstream - if(0 == strand) { // forward - start_pos = s->pos + 1; // one-based - if(start_pos < num_prev_bases) start_pos = 1; - else start_pos -= num_prev_bases; - end_pos = s->pos + target_adj; // one-based - } - else { - start_pos = s->pos + s->target_len; // 1-based - if(start_pos <= target_adj) start_pos = 1; - else start_pos -= target_adj - 1; - end_pos = s->pos + s->target_len + num_prev_bases; - if(refseq->annos[s->seqid].len < end_pos) end_pos = refseq->annos[s->seqid].len; // bound - } - tlen = end_pos - start_pos + 1; - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - } - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start_pos, end_pos, target, 0, &conv)) { - tmap_bug(); - } + // get more target upstream + if(0 == strand) + { // forward + start_pos = s->pos + 1; // one-based + if(start_pos < num_prev_bases) + start_pos = 1; + else + start_pos -= num_prev_bases; + end_pos = s->pos + target_adj; // one-based + } + else + { + start_pos = s->pos + s->target_len; // 1-based + if(start_pos <= target_adj) + start_pos = 1; + else + start_pos -= target_adj - 1; + end_pos = s->pos + s->target_len + num_prev_bases; + if(refseq->annos[s->seqid].len < end_pos) + end_pos = refseq->annos[s->seqid].len; // bound + } + tlen = end_pos - start_pos + 1; + if(target_mem < tlen) + { // more memory? + target_mem = tlen; + tmap_roundup32(target_mem); + target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); + } + // NB: IUPAC codes are turned into mismatches + if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start_pos, end_pos, target, 0, &conv)) + { + tmap_bug(); + } - // set the scoring parameters - if(0 < conv) { - par_iupac.matrix = matrix_iupac; - for(i=0;i<80;i++) { - if(0 < matrix_iupac_mask[i]) (par_iupac).matrix[i] = (opt)->score_match; - else (par_iupac).matrix[i] = -cur_len * (opt)->pen_mm; - } - (par_iupac).row = 16; - (par_iupac).band_width = (opt)->bw; - - (par_iupac).gap_open = 0; - (par_iupac).gap_ext = (opt)->pen_mm; - (par_iupac).gap_end = (opt)->pen_mm; + // set the scoring parameters + if(0 < conv) + { + par_iupac.matrix = matrix_iupac; + for(i=0;i<80;i++) + { + if(0 < matrix_iupac_mask[i]) + (par_iupac).matrix[i] = (opt)->score_match; + else + (par_iupac).matrix[i] = -cur_len * (opt)->pen_mm; + } + (par_iupac).row = 16; + (par_iupac).band_width = (opt)->bw; - } - else { - par.matrix = matrix; - __map_util_gen_ap(par, opt); - for(i=0;i<25;i++) { - (par).matrix[i] = -cur_len * opt->pen_mm; // TODO: is this enough? - } - for(i=0;i<4;i++) { - (par).matrix[i*5+i] = (opt)->score_match; - } - (par).row = 5; - (par).band_width = (opt)->bw; - (par).gap_open = 0; - (par).gap_ext = (opt)->pen_mm; - (par).gap_end = (opt)->pen_mm; - } + (par_iupac).gap_open = 0; + (par_iupac).gap_ext = (opt)->pen_mm; + (par_iupac).gap_end = (opt)->pen_mm; - // adjust query for the reverse strand - if(1 == strand) { // reverse - query = query + qlen - cur_len; // NB: do not use this otherwise - } + } + else + { + par.matrix = matrix; + __map_util_gen_ap(par, opt); + for(i=0;i<25;i++) + { + (par).matrix[i] = -cur_len * opt->pen_mm; // TODO: is this enough? + } + for(i=0;i<4;i++) + { + (par).matrix[i*5+i] = (opt)->score_match; + } + (par).row = 5; + (par).band_width = (opt)->bw; + (par).gap_open = 0; + (par).gap_ext = (opt)->pen_mm; + (par).gap_end = (opt)->pen_mm; + } - /* - for(i=0;iend_repair && old_score == new_score)) { // update - // get the cigar - cigar = tmap_sw_path2cigar(path, path_len, &n_cigar); - if(0 == n_cigar) { - tmap_bug(); - } - // TODO: what if it starts with an indel + /* + for(i=0;i>1;i++) { - int32_t j = cigar[i]; - cigar[i] = cigar[n_cigar-i-1]; - cigar[n_cigar-i-1] = j; - } - } - */ + // map the target against the the query + // path memory + if (*path_buf_sz <= tlen + cur_len) + { // lengthen the path + *path_buf_sz = tlen + cur_len; + tmap_roundup32 (*path_buf_sz); + *path_buf = tmap_realloc (*path_buf, sizeof (tmap_sw_path_t) * *path_buf_sz, "path_buf"); + } - // check if the cigars match - if(1 == n_cigar && TMAP_SW_CIGAR_OP(cigar[0]) == BAM_CMATCH) found = 0; - else found = 1; // do not match, hmmn - } + if(0 == strand) + { + new_score = tmap_sw_clipping_core2(target, tlen, query, cur_len, + (0 < conv) ? &par_iupac : &par, + 1, 0, // do not skip the end of the target + 0, 0, // no soft-clipping + *path_buf, &path_len, 0); + } + else + { + new_score = tmap_sw_clipping_core2(target, tlen, query, cur_len, + (0 < conv) ? &par_iupac : &par, + 0, 1, // do not skip the start of the target + 0, 0, // no soft-clipping + *path_buf, &path_len, 0); + } + //fprintf(stderr, "new_score=%d old_score=%d\n", new_score, old_score); + + found = 0; // reset + if(old_score < new_score || (2 == opt->end_repair && old_score == new_score)) + { // update + // get the cigar + cigar = tmap_sw_path2cigar(*path_buf, path_len, &n_cigar); + if(0 == n_cigar) + { + tmap_bug(); + } + // TODO: what if it starts with an indel + + /* + if(0 == strand) { // reverse the cigar + for(i=0;i>1;i++) { + int32_t j = cigar[i]; + cigar[i] = cigar[n_cigar-i-1]; + cigar[n_cigar-i-1] = j; + } + } + */ - // should we update? - if(1 == found) { - /* - fprintf(stderr, "strand=%d\n", strand); - fprintf(stderr, "cur_len=%d\n", cur_len); - fprintf(stderr, "OLD:\n"); - for(i=0;in_cigar;i++) { // old - fprintf(stderr, "i=%d %d%c\n", i, - bam_cigar_oplen(s->cigar[i]), - bam_cigar_opchr(s->cigar[i])); - } - fprintf(stderr, "ADDITION:\n"); - for(i=0;in_cigar-1; - while((0 == strand && i < s->n_cigar) || (1 == strand && 0 <= i)) { // gets the number to delete, and modifies the last one if necessary in-place - // query - switch(TMAP_SW_CIGAR_OP(s->cigar[i])) { - case BAM_CMATCH: - case BAM_CINS: - sum += bam_cigar_oplen(s->cigar[i]); - default: - break; - } - // target - switch(TMAP_SW_CIGAR_OP(s->cigar[i])) { - case BAM_CMATCH: - case BAM_CDEL: - if(cur_len <= sum) num_ref_removed += (cur_len - (sum - bam_cigar_oplen(s->cigar[i]))); - else num_ref_removed += bam_cigar_oplen(s->cigar[i]); - default: - break; - } + // check if the cigars match + if(1 == n_cigar && TMAP_SW_CIGAR_OP(cigar[0]) == BAM_CMATCH) + found = 0; + else + found = 1; // do not match, hmmn + } - //fprintf(stderr, "i=%d cur_len=%d sum=%d\n", i, cur_len, sum); - if(cur_len <= sum) { - if(cur_len < sum) { // adjust current cigar - TMAP_SW_CIGAR_STORE(s->cigar[i], TMAP_SW_CIGAR_OP(s->cigar[i]), sum - cur_len); // store the difference - i = (0 == strand) ? (i-1) : (i+1); - } - break; - } - i = (0 == strand) ? (i+1) : (i-1); - } - n = (0 == strand) ? (i+1) : (s->n_cigar - i); // the number of cigar operations to delete - //fprintf(stderr, "to delete: %d\n", n); - if(0 < n) { // delete the cigars - if(0 == strand) { // shift down, delete the first cigar operator - for(i=0;in_cigar-n;i++) { - s->cigar[i] = s->cigar[i+n]; - } - } - s->n_cigar -= n; - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*s->n_cigar, "s->cigar"); // reduce the size - } - /* - fprintf(stderr, "CUT DOWN:\n"); - for(i=0;in_cigar;i++) { // old - fprintf(stderr, "i=%d %d%c\n", i, - bam_cigar_oplen(s->cigar[i]), - bam_cigar_opchr(s->cigar[i])); - } - */ - - // get more cigar - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(n_cigar+s->n_cigar), "s->cigar"); - if(0 == strand) { // forward - // shift up - for(i=s->n_cigar-1;0<=i;i--) { - s->cigar[i+n_cigar] = s->cigar[i]; - } - } // otherwise append to the end - - // add new cigar and update the position - for(i=0;icigar[i] = cigar[i]; - } - else { // reverse - s->cigar[i + s->n_cigar] = cigar[i]; - } - switch(TMAP_SW_CIGAR_OP(cigar[i])) { - case BAM_CMATCH: - case BAM_CDEL: - num_ref_added += TMAP_SW_CIGAR_LENGTH(cigar[i]); - default: - break; - } - /* - switch(TMAP_SW_CIGAR_OP(cigar[i])) { + // should we update? + if(1 == found) + { + /* + fprintf(stderr, "strand=%d\n", strand); + fprintf(stderr, "cur_len=%d\n", cur_len); + fprintf(stderr, "OLD:\n"); + for(i=0;in_cigar;i++) { // old + fprintf(stderr, "i=%d %d%c\n", i, + bam_cigar_oplen(s->cigar[i]), + bam_cigar_opchr(s->cigar[i])); + } + fprintf(stderr, "ADDITION:\n"); + for(i=0;in_cigar-1; + while((0 == strand && i < s->n_cigar) || (1 == strand && 0 <= i)) + { // gets the number to delete, and modifies the last one if necessary in-place + // query + switch(TMAP_SW_CIGAR_OP(s->cigar[i])) + { + case BAM_CMATCH: + case BAM_CINS: + sum += bam_cigar_oplen(s->cigar[i]); + default: + break; + } + // target + switch(TMAP_SW_CIGAR_OP(s->cigar[i])) + { + case BAM_CMATCH: + case BAM_CDEL: + if(cur_len <= sum) num_ref_removed += (cur_len - (sum - bam_cigar_oplen(s->cigar[i]))); + else num_ref_removed += bam_cigar_oplen(s->cigar[i]); + default: + break; + } + + //fprintf(stderr, "i=%d cur_len=%d sum=%d\n", i, cur_len, sum); + if(cur_len <= sum) + { + if(cur_len < sum) + { // adjust current cigar + TMAP_SW_CIGAR_STORE(s->cigar[i], TMAP_SW_CIGAR_OP(s->cigar[i]), sum - cur_len); // store the difference + i = (0 == strand) ? (i-1) : (i+1); + } + break; + } + i = (0 == strand) ? (i+1) : (i-1); + } + n = (0 == strand) ? (i+1) : (s->n_cigar - i); // the number of cigar operations to delete + //fprintf(stderr, "to delete: %d\n", n); + if(0 < n) + { // delete the cigars + if(0 == strand) + { // shift down, delete the first cigar operator + for(i=0;in_cigar-n;i++) + { + s->cigar[i] = s->cigar[i+n]; + } + } + s->n_cigar -= n; + s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*s->n_cigar, "s->cigar"); // reduce the size + } + /* + fprintf(stderr, "CUT DOWN:\n"); + for(i=0;in_cigar;i++) { // old + fprintf(stderr, "i=%d %d%c\n", i, + bam_cigar_oplen(s->cigar[i]), + bam_cigar_opchr(s->cigar[i])); + } + */ + + // get more cigar + s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(n_cigar+s->n_cigar), "s->cigar"); + if(0 == strand) + { // forward + // shift up + for(i=s->n_cigar-1;0<=i;i--) + { + s->cigar[i+n_cigar] = s->cigar[i]; + } + } // otherwise append to the end + + // add new cigar and update the position + for(i=0;icigar[i] = cigar[i]; + } + else + { // reverse + s->cigar[i + s->n_cigar] = cigar[i]; + } + switch(TMAP_SW_CIGAR_OP(cigar[i])) + { + case BAM_CMATCH: + case BAM_CDEL: + num_ref_added += TMAP_SW_CIGAR_LENGTH(cigar[i]); + default: + break; + } + /* + switch(TMAP_SW_CIGAR_OP(cigar[i])) { case BAM_CDEL: - s->target_len -= TMAP_SW_CIGAR_LENGTH(cigar[i]); - if(0 == strand) s->pos -= TMAP_SW_CIGAR_LENGTH(cigar[i]); - break; + s->target_len -= TMAP_SW_CIGAR_LENGTH(cigar[i]); + if(0 == strand) s->pos -= TMAP_SW_CIGAR_LENGTH(cigar[i]); + break; case BAM_CINS: - s->target_len += TMAP_SW_CIGAR_LENGTH(cigar[i]); - if(0 == strand) s->pos += TMAP_SW_CIGAR_LENGTH(cigar[i]); - break; + s->target_len += TMAP_SW_CIGAR_LENGTH(cigar[i]); + if(0 == strand) s->pos += TMAP_SW_CIGAR_LENGTH(cigar[i]); + break; default: - break; - } - */ - } - s->n_cigar += n_cigar; - s->target_len += (num_ref_added - num_ref_removed); - if(0 == strand) s->pos += (num_ref_removed - num_ref_added); + break; + } + */ + } + s->n_cigar += n_cigar; + s->target_len += (num_ref_added - num_ref_removed); + if(0 == strand) + s->pos += (num_ref_removed - num_ref_added); + + // merge adjacent cigar operations that have the same value + tmap_map_util_merge_adjacent_cigar_operations(s); + /* + for(i=0;in_cigar;i++) { + fprintf(stderr, "i=%d %d%c\n", i, + bam_cigar_oplen(s->cigar[i]), + bam_cigar_opchr(s->cigar[i])); + } + */ + + // update the score + // TODO: is this correct? + s->score += new_score; + s->score -= old_score; + + /* + fprintf(stderr, "NEW:\n"); + for(i=0;in_cigar;i++) { // old + fprintf(stderr, "i=%d %d%c\n", i, + bam_cigar_oplen(s->cigar[i]), + bam_cigar_opchr(s->cigar[i])); + } + */ + // Check that the cigar is valid + for(i=cur_len=0;in_cigar;i++) + { + switch(TMAP_SW_CIGAR_OP(s->cigar[i])) + { // NB: do not include soft-clip bases + case BAM_CMATCH: + case BAM_CINS: + cur_len += TMAP_SW_CIGAR_LENGTH(s->cigar[i]); + default: + break; + } + } + //fprintf(stderr, "cur_len=%d qlen=%d\n", cur_len, qlen); + if(cur_len != qlen) + tmap_bug(); + } - // merge adjacent cigar operations that have the same value - tmap_map_util_merge_adjacent_cigar_operations(s); - /* - for(i=0;in_cigar;i++) { - fprintf(stderr, "i=%d %d%c\n", i, - bam_cigar_oplen(s->cigar[i]), - bam_cigar_opchr(s->cigar[i])); - } - */ + // free + free(cigar); + free(target); + return repaired; +} - // update the score - // TODO: is this correct? - s->score += new_score; - s->score -= old_score; +void target_cache_init (ref_buf_t* target) +{ + target->buf = NULL; + target->buf_sz = 0; + target->data = NULL; + target->data_len = 0; + target->seqid = 0; // for reference access the sequence ids are 1-based, so 0 is always invalid + target->seq_start = 0xFFFFFFFF; + target->seq_end = 0xFFFFFFFF; +} +void target_cache_free (ref_buf_t* target) +{ + free (target->buf); + target_cache_init (target); +} - /* - fprintf(stderr, "NEW:\n"); - for(i=0;in_cigar;i++) { // old - fprintf(stderr, "i=%d %d%c\n", i, - bam_cigar_oplen(s->cigar[i]), - bam_cigar_opchr(s->cigar[i])); - } - */ - // Check that the cigar is valid - for(i=cur_len=0;in_cigar;i++) { - switch(TMAP_SW_CIGAR_OP(s->cigar[i])) { // NB: do not include soft-clip bases - case BAM_CMATCH: - case BAM_CINS: - cur_len += TMAP_SW_CIGAR_LENGTH(s->cigar[i]); - default: - break; - } - } - //fprintf(stderr, "cur_len=%d qlen=%d\n", cur_len, qlen); - if(cur_len != qlen) tmap_bug(); - } - - // free - free(cigar); - free(target); -} - -void -tmap_map_rmquerybases_cigar(tmap_map_sam_t *s, uint32_t left_rm_bases, uint32_t right_rm_bases) +void cache_target (ref_buf_t* target, tmap_refseq_t *refseq, uint32_t seqid, uint32_t seq_start, uint32_t seq_end) { - - + uint32_t tlen = seq_end - seq_start + 1; + // check if already in buffer + if (seqid == target->seqid && seq_start >= target->seq_start && seq_end <= target->seq_end) + { + target->data = target->buf + (seq_start - target->seq_start); + target->data_len = tlen; + } + else + { + if (target->buf_sz < tlen) + { // more memory? + target->buf_sz = tlen; + tmap_roundup32 (target->buf_sz); + target->buf = tmap_realloc (target->buf, sizeof (uint8_t) * (target->buf_sz), "target->buf"); + } + if (!tmap_refseq_subseq2 (refseq, seqid, seq_start, seq_end, target->buf, 1, NULL)) + { + uint32_t rl = refseq->annos[seqid-1].len; + tmap_progress_print2 ("\n seqid = %d, seq_start = %d, seq_end = %d, Ref seq len = %d", seqid, seq_start, seq_end, rl); + tmap_bug (); + } + target->seqid = seqid; + target->seq_start = seq_start; + target->seq_end = seq_end; + target->data = target->buf; + target->data_len = tlen; + } } -tmap_map_sams_t * -tmap_map_util_sw_gen_cigar(tmap_refseq_t *refseq, - tmap_map_sams_t *sams, - tmap_seq_t *seq, - tmap_seq_t **seqs, - tmap_map_opt_t *opt) -{ - int32_t i, j, matrix[25], matrix_iupac[80]; - // int32_t start, end; - int32_t end; - tmap_map_sams_t *sams_tmp = NULL; - tmap_sw_param_t par, par_iupac; - tmap_sw_path_t *path = NULL; - int32_t path_len, path_mem=0; - int32_t seq_len=0, tlen, target_mem=0; - uint8_t *target=NULL; - tmap_vsw_t *vsw = NULL; - tmap_vsw_opt_t *vsw_opt = NULL; - uint32_t start_pos, end_pos; - int32_t overflow, softclip_start, softclip_end; - uint8_t key_base = 0; - int32_t iupac_init = 0; - - if(0 == sams->n) { - return sams; - } +int find_alignment_start +( + tmap_map_sam_t* src_sam, // source: raw (position-only) mapping + tmap_map_sam_t* dest_sam, // destination: refined (aligned) mapping + tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_refseq_t* refseq, // reference server + int32_t softclip_start, // is 5' softclip allowed + int32_t softclip_end, // is 3' softclip allowed + tmap_map_opt_t* opt, // tmap parameters + ref_buf_t* target, // reference data + tmap_vsw_t *vsw, // vectorized aligner object + tmap_map_stats_t * stat // statistics +) +// returns 1 on success, 0 on failure +{ + // get the target sequence + uint32_t start_pos = src_sam->pos + 1; + uint32_t end_pos = start_pos + src_sam->target_len - 1; + // int32_t tlen = src_sam->result.target_end + 1; // adjust based on the target end + uint8_t* orig_query = (uint8_t*) tmap_seq_get_bases (seqs [src_sam->strand])->s; // forward genomic strand + int32_t orig_query_len = tmap_seq_get_bases_length (seqs [0]); + + // cache data + cache_target (target, refseq, src_sam->seqid + 1, start_pos, end_pos); + + // retrieve the reverse complimented query sequence + uint8_t *query_rc = (uint8_t*) tmap_seq_get_bases (seqs [1])->s; + query_rc += orig_query_len - src_sam->result.query_end - 1; // offset query + + // uint8_t *query_rc = NULL; + int32_t qlen = src_sam->result.query_end + 1; // adjust based on the query end; + // do not band when generating the cigar + tmap_map_sam_t tmp_sam; // TODO: actually, only the .pos, .score and .result members change; it would be cleaner to use only them + tmp_sam = *src_sam; + + // save the position and target len as passed from mapper for end_repair + tmp_sam.mapper_pos = tmp_sam.pos; + tmp_sam.mapper_tlen = tmp_sam.target_len; + + // reverse compliment the target_buf + if(0 == src_sam->strand) + tmap_reverse_compliment_int (target->data, target->data_len); + + // NB: if match/mismatch penalties are on the opposite strands, we may + // have wrong scores + // NB: this aligns in the opposite direction than sequencing (3'->5') + int32_t overflow; + tmp_sam.score = tmap_vsw_process_rev (vsw, query_rc, qlen, target->data, target->data_len, + &tmp_sam.result, &overflow, opt->score_thr, + (1 == softclip_end && 1 == softclip_start) ? 0 : 1); // NB: to guarantee correct soft-clipping if both ends are clipped + + if(1 == overflow) + tmap_bug(); + + // reverse back compliment the target_buf + if(0 == tmp_sam.strand) + tmap_reverse_compliment_int (target->data, target->data_len); + + if (tmp_sam.score < opt->score_thr) // this could happen if VSW fails. Just skip the damned, with no warning ?!? + return 0; // TODO: may beed warning or error report here + + if (0 == tmp_sam.strand) + { + tmp_sam.pos += tmp_sam.result.target_start; // keep it zero based + } + else + { + int32_t query_start = orig_query_len - tmp_sam.result.query_end - 1; + int32_t query_end = orig_query_len - tmp_sam.result.query_start - 1; + tmp_sam.result.query_start = query_start; + tmp_sam.result.query_end = query_end; + } + tmp_sam.result.target_end -= tmp_sam.result.target_start; + tmp_sam.result.target_start = 0; - // the final mappings will go here - sams_tmp = tmap_map_sams_init(sams); - tmap_map_sams_realloc(sams_tmp, sams->n); - // scoring matrix - par.matrix = matrix; - __map_util_gen_ap(par, opt); + *dest_sam = tmp_sam; - // sort by strand/chr/pos/score - tmap_sort_introsort(tmap_map_sam_sort_coord, sams->n, sams->sams); - tmap_map_util_set_softclip(opt, seq, &softclip_start, &softclip_end); - if (0 && softclip_end == 0 && refseq->bed_exist) { - // ZZ: only disable 3' end softclip if the read aligned all the way close to the end of amplicon - end = 0; - while(end < sams->n) { - uint8_t strand; // *query=NULL, *query_rc=NULL, *tmp_target=NULL; - // int32_t qlen; - tmap_map_sam_t tmp_sam; - // int32_t query_start, query_end; - // int32_t conv = 0; - - // do not band when generating the cigar - tmp_sam = sams->sams[end]; - // get the strand/start/end positions - strand = tmp_sam.strand; - start_pos = tmp_sam.pos + 1; - end_pos = start_pos + tmp_sam.target_len - 1; - uint32_t ampl_start, ampl_end; - if (tmap_map_get_amplicon(refseq, tmp_sam.seqid, start_pos, end_pos, &l_start, &l_end, strand)) { - if (strand == 0 && abs(ampl_end-end_pos) < 15) break; - if (strand != 0 && abs(ampl_start-start_pos) < 15) break; - } - end++; + // update aux data + tmap_map_sam_malloc_aux (dest_sam); + switch (dest_sam->algo_id) + { + case TMAP_MAP_ALGO_MAP1: + (*dest_sam->aux.map1_aux) = (*tmp_sam.aux.map1_aux); + break; + case TMAP_MAP_ALGO_MAP2: + (*dest_sam->aux.map2_aux) = (*tmp_sam.aux.map2_aux); + break; + case TMAP_MAP_ALGO_MAP3: + (*dest_sam->aux.map3_aux) = (*tmp_sam.aux.map3_aux); + break; + case TMAP_MAP_ALGO_MAP4: + (*dest_sam->aux.map4_aux) = (*tmp_sam.aux.map4_aux); + break; + case TMAP_MAP_ALGO_MAPVSW: + (*dest_sam->aux.map_vsw_aux) = (*tmp_sam.aux.map_vsw_aux); + break; + default: + tmap_bug (); + break; } - if (end >= sams->n) softclip_end = 1; - } - - // initialize opt - vsw_opt = tmap_vsw_opt_init(opt->score_match, opt->pen_mm, opt->pen_gapo, opt->pen_gape, opt->score_thr); - - // init seqs - seq_len = tmap_seq_get_bases_length(seqs[0]); + return 1; +} - // reverse compliment query - vsw = tmap_vsw_init((uint8_t*)tmap_seq_get_bases(seqs[1])->s, seq_len, softclip_end, softclip_start, opt->vsw_type, vsw_opt); - - if(1 == opt->softclip_key) { - // get first base - if(NULL == seq->ks) { - key_base = INT8_MAX; - } - else { - key_base = tmap_nt_char_to_int[(uint8_t)seq->ks[strlen(seq->ks)-1]]; // NB: last base of the key - } - } - - // i = start = end = 0; - i = end = 0; - start_pos = end_pos = 0; - while(end < sams->n) { - uint8_t strand, *query=NULL, *query_rc=NULL, *tmp_target=NULL; - int32_t qlen; - tmap_map_sam_t tmp_sam; - tmap_map_sam_t *s = NULL; - int32_t query_start, query_end; - int32_t conv = 0; - - // do not band when generating the cigar - tmp_sam = sams->sams[end]; +static void extend_softclips_to_read_edges ( + tmap_map_sam_t* dest_sam, // mapping being adjusted + tmap_seq_t** seqs // 4-element array containing fwd, rec, compl and rev/compl read sequence +) +{ + int32_t orig_query_len = tmap_seq_get_bases_length (seqs [0]); + + // add needed soft-clips (may be adjusted by salvage) + // Already converted in place, in sams.result (by find_alignment_start) + // int32_t query_start = dest_sam->strand?(orig_query_len - dest_sam->result.query_end - 1):dest_sam->result.query_start; + // int32_t query_end = dest_sam->strand?(orig_query_len - dest_sam->result.query_start - 1):dest_sam->result.query_end; + + // add soft clipping + if(0 < dest_sam->result.query_start) + { + int32_t j; + // soft clip the front of the read + dest_sam->cigar = tmap_realloc (dest_sam->cigar, sizeof (uint32_t) * (1 + dest_sam->n_cigar), "dest_sam->cigar"); + for (j = dest_sam->n_cigar - 1; 0 <= j; j--) // shift up + dest_sam->cigar [j + 1] = dest_sam->cigar [j]; + TMAP_SW_CIGAR_STORE (dest_sam->cigar [0], BAM_CSOFT_CLIP, dest_sam->result.query_start); + dest_sam->n_cigar++; + } + if (dest_sam->result.query_end < orig_query_len - 1) + { + // soft clip the end of the read + dest_sam->cigar = tmap_realloc (dest_sam->cigar, sizeof (uint32_t) * (1 + dest_sam->n_cigar), "dest_sam->cigar"); + TMAP_SW_CIGAR_STORE (dest_sam->cigar [dest_sam->n_cigar], BAM_CSOFT_CLIP, orig_query_len - dest_sam->result.query_end - 1); + dest_sam->n_cigar++; + } +} - // get the strand/start/end positions - strand = tmp_sam.strand; - start_pos = tmp_sam.pos + 1; - end_pos = start_pos + tmp_sam.target_len - 1; - uint32_t left_added_bases = 0, right_added_bases = 0; // ZZ to be removed - - /** - * Step 1: find the start of the alignment - */ - - // retrieve the reverse complimented query sequence - query_rc = (uint8_t*)tmap_seq_get_bases(seqs[1])->s; - query_rc += seq_len - tmp_sam.result.query_end - 1; // offset query - qlen = tmp_sam.result.query_end + 1; // adjust based on the query end - - // get the target sequence - tlen = tmp_sam.result.target_end + 1; // adjust based on the target end - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - } - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, sams->sams[end].seqid+1, start_pos, end_pos, target, 1, &conv)) { - tmap_bug(); - } +void compute_alignment ( + tmap_map_sam_t* dest_sam, // mapping to update with cigar + tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_refseq_t* refseq, // reference server + ref_buf_t* target, // reference data + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_sw_param_t* par, // Smith-Waterman scoring parameters + tmap_map_stats_t* stat // statistics +) +{ + uint8_t* orig_query = (uint8_t*) tmap_seq_get_bases (seqs [dest_sam->strand])->s; // forward genomic strand + int32_t orig_query_len = tmap_seq_get_bases_length (seqs [0]); + // uint8_t* adjusted_query = orig_query + (dest_sam->strand?(orig_query_len - dest_sam->result.query_end - 1):(dest_sam->result.query_start)); // offset query + uint8_t* adjusted_query = orig_query + dest_sam->result.query_start; // offset query; the result.query_start is already properly adjusted depending on a strand + + // uint8_t* adjusted_target = (*target_buf) + (dest_sam->strand?0:dest_sam->result.target_start); + uint32_t start_pos = dest_sam->pos + dest_sam->result.target_start + 1; // addition of result.target_start is obsolete, as it always should be reset to 0 by find_alignment_start + uint32_t end_pos = dest_sam->pos + dest_sam->result.target_end + 1; + // cache data + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); + + + int32_t qlen = dest_sam->result.query_end - dest_sam->result.query_start + 1; // update query length + // int32_t tlen = dest_sam->result.target_end - dest_sam->result.target_start + 1; + int32_t path_len; + int32_t new_score; + + // path memory + if (*path_buf_sz <= target->data_len + orig_query_len) + { // lengthen the path + *path_buf_sz = target->data_len + orig_query_len; + tmap_roundup32 (*path_buf_sz); + *path_buf = tmap_realloc (*path_buf, sizeof (tmap_sw_path_t) * *path_buf_sz, "path_buf"); + } - // reverse compliment the target - if(0 == strand) { - tmap_reverse_compliment_int(target, tlen); - } + // Smith Waterman with banding + // NB: we store the score from the banded version, which does not allow ins then del, or del then ins. The vectorized version does. + // NB: left genomic indel justification is facilitated by always using the forward strand target/query combination. + new_score = tmap_sw_global_banded_core (target->data, target->data_len, adjusted_query, qlen, par, + dest_sam->result.score_fwd, *path_buf, &path_len, 0); + + // ZZ: There can be case when the global score is the same as the vectorized version + // whileas the alignments are different. We are not addressing all of them, but the + // cases where the resulting alignment starts or ends with deletion shall be avoided. + if ( (new_score != dest_sam->score && 1 < dest_sam->result.n_best) + || (path_len > 0 && (TMAP_SW_FROM_D == (*path_buf) [0].ctype || TMAP_SW_FROM_D == (*path_buf) [path_len - 1].ctype))) + // explicitly fit the query into the target + new_score = tmap_sw_fitting_core (target->data, target->data_len, adjusted_query, qlen, par, *path_buf, &path_len, 0); + // update score + dest_sam->score = new_score; + // adjust position to forward alignment + int32_t path_ref_off = (*path_buf) [path_len - 1].i - 1; // path_ref_off is zero-based + dest_sam->result.target_start += path_ref_off; // target_start is zero-based, path coords are one-based + dest_sam->pos += dest_sam->result.target_start; // move pos to the (updated) target_start + + if ((*path_buf) [path_len-1].ctype == TMAP_SW_FROM_I) // don't quite understand this ZZ + dest_sam->pos++; // if alignment starts with INS in query, this ins is placed at NEXT base in rev reference - alignment start should be one base to the right DK + + + // actually convert SW path to cigar + dest_sam->cigar = tmap_sw_path2cigar (*path_buf, path_len, &(dest_sam->n_cigar)); + if (0 == dest_sam->n_cigar) + tmap_bug (); + + // compute target (alignment on the reference) len + dest_sam->target_len = 0; + uint32_t* cigar_pos; + int32_t j; + for (j = 0, cigar_pos = dest_sam->cigar; j != dest_sam->n_cigar; ++j, ++cigar_pos) + { + if (bam_cigar_type (bam_cigar_op (*cigar_pos)) & 2) + dest_sam->target_len += bam_cigar_oplen (*cigar_pos); + } - // NB: if match/mismatch penalties are on the opposite strands, we may - // have wrong scores - // NB: this aligns in the opposite direction than sequencing (3'->5') - tmp_sam.score = tmap_vsw_process_rev(vsw, query_rc, qlen, target, tlen, - &tmp_sam.result, &overflow, opt->score_thr, - (1 == softclip_end && 1 == softclip_start) ? 0 : 1); // NB: to guarantee correct soft-clipping if both ends are clipped + // from now on, the box is not used in original code - all positions are based on pos and target_len for target, and softclips for query + // we use it for everything, so we'll update the box here + // The query positions is in the direction of strands aligning with FORWARD target: + // : q_start is from the beginning of REVERSE query for reverse matches + // and from the beginning of FORWARD query for fwd matches + // target always mean same + dest_sam->result.query_end = dest_sam->result.query_start + (*path_buf) [0].j - 1; // TODO: check if adjustment for DEL at the alignment end needed + dest_sam->result.query_start += (*path_buf) [path_len - 1].j - 1; // one-based in path, zero-based in box + if ((*path_buf) [path_len-1].ctype == TMAP_SW_FROM_D) // should this ever happen? + dest_sam->result.query_start++; + + dest_sam->result.target_start = 0; + dest_sam->result.target_end = dest_sam->target_len - 1; // TODO: check if adjustment for INS at the alignment end needed + + // cure softclips + extend_softclips_to_read_edges (dest_sam, seqs); + + // remember original alignment + dest_sam->n_orig_cigar = dest_sam->n_cigar; + dest_sam->orig_cigar = tmap_calloc (dest_sam->n_orig_cigar, sizeof (*(dest_sam->orig_cigar)), "orig_cigar"); + dest_sam->orig_pos = dest_sam->pos; + memcpy (dest_sam->orig_cigar, dest_sam->cigar, dest_sam->n_orig_cigar * sizeof (*(dest_sam->orig_cigar))); +} - if(1 == overflow) { - tmap_bug(); - } - - // reverse back compliment the target - if(0 == strand) { - tmap_reverse_compliment_int(target, tlen); - } +void salvage_long_indel_at_edges ( + tmap_map_sam_t* dest_sam, // destination: refined (aligned) mapping + tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_refseq_t* refseq, // reference server + ref_buf_t* target, // reference data + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_map_opt_t* opt, // tmap options + tmap_sw_param_t* par, // Smith-Waterman scopring parameters + tmap_map_stats_t *stat // statistics +) +{ + // long indel prefixes and suffixes + // needs: + // tmap_map_sam_t* dest_sam, // destination: refined (aligned) mapping + // tmap_map_opt_t* opt, // tmap parameters + // tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + // tmap_refseq_t* refseq, // reference server + // uint8_t** target_buf, // pointer to the address of the memory buffer for unpacked reference sequence + // int32_t* target_buf_sz, // pointer to the variable contining presently allocated size of target_buf + // tmap_sw_path_t *path_buf, // buffer for traceback path + // int32_t path_buf_sz // used portion and allocated size of traceback path. + // tmap_map_opt_t* opt // tmap options + // tmap_sw_param_t* par; // Smith-Waterman scopring parameters + + uint32_t ampl_start = 0, ampl_end = 0; + uint32_t ampl_exist = 0; + + uint8_t* orig_query = (uint8_t*) tmap_seq_get_bases (seqs [dest_sam->strand])->s; // forward genomic strand + int32_t orig_query_len = tmap_seq_get_bases_length (seqs [0]); + + int32_t query_start = dest_sam->result.query_start; + int32_t query_end = dest_sam->result.query_end; + + // DK: The code below is derived from original Niels's edge indel salvage code + // It assumes cigar string is stripped of softclips. + // We need to save them and then put back. + unsigned beg_softclip = 0, end_softclip = 0; + // int8_t cigar_buf_reallocated = 0; + if (dest_sam->n_cigar > 1 && bam_cigar_op (dest_sam->cigar [dest_sam->n_cigar - 1]) == BAM_CSOFT_CLIP) + { + end_softclip = bam_cigar_oplen (dest_sam->cigar [dest_sam->n_cigar - 1]); + if (0 == end_softclip) + tmap_bug (); + dest_sam->n_cigar --; + } + if (dest_sam->n_cigar && bam_cigar_op (dest_sam->cigar [0]) == BAM_CSOFT_CLIP) + { + beg_softclip = bam_cigar_oplen (dest_sam->cigar [0]); + if (0 == beg_softclip) + tmap_bug (); + dest_sam->n_cigar --; + memmove (dest_sam->cigar, dest_sam->cigar + 1, dest_sam->n_cigar * sizeof (uint32_t)); + } - if(tmp_sam.score < opt->score_thr) { // this could happen if VSW fails - end++; - continue; - } - - // ZZ, figure out amplicon condition to add primers or similar - uint32_t ampl_start = 0, ampl_end = 0; - uint32_t start_pos_a = start_pos, end_pos_a = end_pos; - if (strand == 0) { - start_pos_a += tmp_sam.result.target_start; - } else { - end_pos_a = start_pos+ (tmp_sam.result.target_end-tmp_sam.result.target_start); - } - if (0 && softclip_end == 0 && tmap_map_get_amplicon(refseq, tmp_sam.seqid, start_pos_a, end_pos_a, &l_start, &l_end, strand)) { - uint32_t ds = abs(start_pos_a-ampl_start); - uint32_t de = abs(end_pos_a-ampl_end); - if (ds < 10 && de < 10 && (de > 2 || ds > 2)) { - //if ((strand == 0 && ds < 10 && start_pos_a >= ampl_start+2) || (strand != 0 && de < 10 && end_pos_a <= ampl_end-2)) { - //if ((strand == 0 && ds < 10) || (strand != 0 && de < 10)) { - //int left = 1, right = 1; - //if (strand == 0) left = 0; else right = 0; - //if (ds < 10 && de < 10 /*&& start_pos_a >= ampl_start+2 && end_pos_a <= ampl_end-2*/) left = right = 0; - // target mem - int pad = 20; - tlen = ampl_end-ampl_start+1+pad*2; - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - } - query = (uint8_t*)tmap_seq_get_bases(seqs[strand])->s; - // Get the new target - // NB: IUPAC codes are turned into mismatches - s = &sams_tmp->sams[i]; - (*s) = tmp_sam; - - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, ampl_start-pad, ampl_end+pad, target, 0, &conv)) { - tmap_bug(); - } - // finding the digestion sites. - int x; - for (x = tlen-pad; x < tlen; x++) { - if (target[x] == 0) break; - } - x--; - ampl_end = x+(ampl_start-pad); - - for (x = pad-2; x >=0; x--) { - if (target[x] == 3) break; - } - x++; - tmp_target = target; - target += x; - ampl_start = ampl_start-pad+x; - tlen = ampl_end-ampl_start+1; - if(0 < conv && 0 == iupac_init) { - par_iupac.matrix = matrix_iupac; - __map_util_gen_ap_iupac(par_iupac, opt); - iupac_init = 1; - } + /** + * Try to detect large indels present as soft-clipped prefixes of the + * alignment. + */ - // path memory - if(path_mem <= tlen + seq_len) { // lengthen the path - path_mem = tlen + seq_len; - tmap_roundup32(path_mem); - path = tmap_realloc(path, sizeof(tmap_sw_path_t)*path_mem, "path"); - } + uint32_t salvaged = 0; + while(1) // NB: so we can break out at any time + { + if (0 < query_start) // beginning of the alignment (3' for reverse, 5' for forward strand) + { // start of the alignment + // ZZ:We may check amplicon here as well. May try this only when it is near start of an amplicon. + // Here the reference mapping position is known by s->pos to s->pos+s->target_len + // We can check the amplicon with these. + // A. When bed file is not present, current logic holds. + // B.i. If bed file is given, when an amplicon is present, try use all the bases to the begin/end of amplicon, very small or no long gap penalty. + // B.ii.If bed file is given and the hit position is not in an amplicon, normal logic apply. + + int32_t del_score = 0; + int32_t ins_score = 0; + uint32_t op, op_len; + int32_t new_score; + uint32_t *cigar = NULL; + int32_t n_cigar; + int32_t pos_adj = 0; + int32_t start_pos, end_pos; + int32_t path_len; + + // query = (uint8_t*)tmap_seq_get_bases(seqs[tmp_sam.strand])->s; // forward genomic strand + // DK: this is thr orig_query + + // get the target sequence before the start of the alignment + int32_t tglen = query_start + opt->gapl_len; + + if (dest_sam->pos <= tglen) + start_pos = 1; + else + start_pos = dest_sam->pos - tglen + 1; + end_pos = dest_sam->pos; + if (end_pos < start_pos) + break; // exit the loop + int32_t long_gap = opt->pen_gapl; + //ZZ: check amplicon + uint32_t ampl_start = 0, ampl_end = 0; + if (tmap_map_get_amplicon (refseq, dest_sam->seqid, dest_sam->pos, dest_sam->pos + dest_sam->target_len, &l_start, &l_end, dest_sam->strand)) + { + ampl_exist = 1; + if (ampl_start >= dest_sam->pos) + break; + if (start_pos > ampl_start) + start_pos = ampl_start; + long_gap = 0; + } + + // DK - do not reset; use target_buf, it is kept intact + // target mem + // target = tmp_target; // reset target in memory + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); + + // try to add a larger deletion + del_score = tmap_sw_clipping_core2 (target->data, target->data_len, orig_query, query_start, par, + 1, 1, // allow the start and end of the target to be skipped + 1, 0, // deletions + NULL, 0, 0); - s->score = 20*opt->score_match+ tmap_sw_clipping_core2(target, tlen, query, seq_len, - (0 < conv) ? &par_iupac : &par, - 0, 0, //whole amplicon must be aligned ZZ - 1, 1, /*0, 0,*/ // whole global, may use a better algorithm ZZ - path, &path_len, 0); - //fprintf(stderr, "ZZ= score=%d %d seqid%d %d %d %d %d %d %d strand=%d %d %d\n", s->score, tmp_sam.score, s->seqid+1, ampl_start, ampl_end, start_pos_a, end_pos_a, tmp_sam.result.target_start, tmp_sam.result.target_end, strand, start_pos, end_pos); - //if (s->score > tmp_sam.score) { - int tail_clip = seq_len-path[0].j; - int begin_clip = path[path_len-1].j-1; - if (TMAP_SW_FROM_D == path[path_len-1].ctype) begin_clip++; - s->cigar = tmap_sw_path2cigar(path, path_len, &s->n_cigar); - // need to add soft clip, possibly on both sides - if(0 == s->n_cigar) { - tmap_bug(); - } - s->pos = ampl_start-1; - s->target_len = tlen; - int f = (begin_clip >0) ? 1:0; - int e = (tail_clip > 0) ? 1:0; - if (f+e > 0) { - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(f+e+s->n_cigar), "s->cigar"); - if (f > 0) { - int i; - for (i = s->n_cigar-1; i>=0; i--) { - s->cigar[i+1] = s->cigar[i]; - } - TMAP_SW_CIGAR_STORE(s->cigar[0], BAM_CSOFT_CLIP, begin_clip); - } - if (e > 0) TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar+f+e-1], BAM_CSOFT_CLIP, tail_clip); - s->n_cigar += e+f; - } - // check query length and cigar - int tttlen = 0; - for (j = 0; j < s->n_cigar; j++) { - switch(TMAP_SW_CIGAR_OP(s->cigar[j])) { + // try to add a larger insertion + // TODO: how do we enforce that the target starts immediately where + // we want it to? + ins_score = tmap_sw_clipping_core2 (target->data, target->data_len, orig_query, query_start, par, + 1, 0, // only allow the start of the target to be skipped + 1, 1, // insertions, + NULL, 0, 0); + // reset start/end position + start_pos = dest_sam->pos + 1; + end_pos = dest_sam->pos + dest_sam->target_len; + + if (del_score <= 0 && ins_score <= 0) + // do nothing + new_score = -1; + else + { + // path memory + if (*path_buf_sz <= target->data_len + query_start) + { // lengthen the path + *path_buf_sz = target->data_len + query_start; + tmap_roundup32 (*path_buf_sz); + *path_buf = tmap_realloc (*path_buf, sizeof (tmap_sw_path_t) * *path_buf_sz, "path_buf"); + } + if (del_score < ins_score) + { + // get the path + tmap_sw_clipping_core2 (target->data, target->data_len, orig_query, query_start, + par, + 1, 0, // only allow the start of the target to be skipped + 1, 1, // insertions, + *path_buf, &path_len, 0); + op = BAM_CINS; + op_len = query_start - (*path_buf) [0].j; + new_score = ins_score; + pos_adj = 0; + } + else + { + // get the path + tmap_sw_clipping_core2 (target->data, target->data_len, orig_query, query_start, + par, + 1, 1, // allow the start and end of the target to be skipped + 1, 0, // deletions + *path_buf, &path_len, 0); + op = BAM_CDEL; + op_len = target->data_len - (*path_buf) [0].i; + new_score = del_score; + pos_adj = op_len; + } + if ((*path_buf) [path_len - 1].ctype == TMAP_SW_FROM_I) + pos_adj++; // TODO: is this correct? + } + if(0 < new_score && 0 <= new_score - long_gap) // ?? Redundant condition (DK) + { + int32_t n_cigar_op = (0 < op_len) ? 1 : 0; + int32_t j; + // get the cigar + cigar = tmap_sw_path2cigar (*path_buf, path_len, &n_cigar); + if (0 == n_cigar) + tmap_bug (); + // re-allocate the cigar + dest_sam->cigar = tmap_realloc (dest_sam->cigar, sizeof (uint32_t) * (n_cigar_op + n_cigar + dest_sam->n_cigar), "dest_sam->cigar"); + // cigar_buf_reallocated = 1; + // shift up + for (j = dest_sam->n_cigar - 1; 0 <= j; j--) + dest_sam->cigar [j + n_cigar_op + n_cigar] = dest_sam->cigar [j]; + // add the operation + if (1 == n_cigar_op) // 0 < op_len + TMAP_SW_CIGAR_STORE (dest_sam->cigar [n_cigar], op, op_len); + // add the rest of the cigar + for(j = 0; j < n_cigar; j++) + dest_sam->cigar [j] = cigar [j]; + + uint32_t added_ref = 0, added_qry = 0; + dest_sam->n_cigar += n_cigar + n_cigar_op; + if (op == BAM_CDEL) + { + dest_sam->target_len += op_len; + added_ref += op_len; + } + else + added_qry += op_len; + + for (j = 0; j < n_cigar; j++) + { + int32_t oplen = TMAP_SW_CIGAR_LENGTH (cigar [j]); + switch (TMAP_SW_CIGAR_OP (cigar [j])) + { case BAM_CMATCH: + added_qry += oplen; + case BAM_CDEL: + added_ref += oplen; + dest_sam->target_len += oplen; + dest_sam->pos -= oplen; // NB: must adjust the position at the start of the alignment + break; case BAM_CINS: - case BAM_CSOFT_CLIP: - tttlen += TMAP_SW_CIGAR_LENGTH(s->cigar[j]); - break; - } - } - if (tttlen != seq_len) { - fprintf(stderr, "%d seqlen=%d tttlen=%d\t tail_skip=%d begin_clip=%d n_cigar=%d\n", path[path_len-1].ctype, seq_len, tttlen, tail_clip, begin_clip, s->n_cigar); - } - - // May need adjust start?? - target = tmp_target; - end++; i++; - // start = end; - // update aux data - tmap_map_sam_malloc_aux(s); - switch(s->algo_id) { - case TMAP_MAP_ALGO_MAP1: - (*s->aux.map1_aux) = (*tmp_sam.aux.map1_aux); - break; - case TMAP_MAP_ALGO_MAP2: - (*s->aux.map2_aux) = (*tmp_sam.aux.map2_aux); - break; - case TMAP_MAP_ALGO_MAP3: - (*s->aux.map3_aux) = (*tmp_sam.aux.map3_aux); - - case TMAP_MAP_ALGO_MAP4: - (*s->aux.map4_aux) = (*tmp_sam.aux.map4_aux); - break; - case TMAP_MAP_ALGO_MAPVSW: - (*s->aux.map_vsw_aux) = (*tmp_sam.aux.map_vsw_aux); - break; - default: - tmap_bug(); - break; - } - continue; - //} - } - } - /* - fprintf(stderr, "qlen=%d tlen=%d\n", qlen, tlen); - fprintf(stderr, "tmp_sam.result.query_start=%d\n", tmp_sam.result.query_start); - fprintf(stderr, "tmp_sam.result.query_end=%d\n", tmp_sam.result.query_end); - fprintf(stderr, "tmp_sam.result.target_start=%d\n", tmp_sam.result.target_start); - fprintf(stderr, "tmp_sam.result.target_end=%d\n", tmp_sam.result.target_end); - fprintf(stderr, "tmp_sam.result.n_best=%d\n", tmp_sam.result.n_best); - fprintf(stderr, "tmp_sam.pos=%u\n", tmp_sam.pos); - */ - - // NB: target_start and target_end are inverted for the reverse strand but we - // only care about their difference. - - // adjust the query and target based off of the start of the alignment - query = (uint8_t*)tmap_seq_get_bases(seqs[strand])->s; // forward genomic strand - tmp_target = target; - if(0 == strand) { - query_start = tmp_sam.result.query_start; - query_end = tmp_sam.result.query_end; - query += tmp_sam.result.query_start; // offset query - target += tmp_sam.result.target_start; - tmp_sam.pos += tmp_sam.result.target_start; // keep it zero based - } - else { - // <-------< QE <-------< QS <-------< - query_start = seq_len - tmp_sam.result.query_end - 1; - query_end = seq_len - tmp_sam.result.query_start - 1; - query += seq_len - tmp_sam.result.query_end - 1; // offset query - // does not affect target offset - // does not affect tmp_sam.pos; this was updated earlier - } - qlen = tmp_sam.result.query_end - tmp_sam.result.query_start + 1; // update query length - tlen = tmp_sam.result.target_end - tmp_sam.result.target_start + 1; - - /** - * Step 2: generate the cigar - */ - if(0 < conv) { // NB: there were IUPAC bases - // init iupac parameters, if necessary - if(0 == iupac_init) { - par_iupac.matrix = matrix_iupac; - __map_util_gen_ap_iupac(par_iupac, opt); - iupac_init = 1; - } - // update start/end - start_pos = tmp_sam.pos + 1; // one-based - end_pos = start_pos + tlen - 1; - target = tmp_target; // reset target in memory - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - } - // Get the new target - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, sams->sams[end].seqid+1, start_pos, end_pos, target, 0, NULL)) { - tmap_bug(); - } - tmp_target = target; // store for later - } - - // path memory - if(path_mem <= tlen + seq_len) { // lengthen the path - path_mem = tlen + seq_len; - tmap_roundup32(path_mem); - path = tmap_realloc(path, sizeof(tmap_sw_path_t)*path_mem, "path"); - } + case BAM_CSOFT_CLIP: + added_qry += oplen; + default: + break; + } + } + free (cigar); cigar = NULL; + // update the query end + int32_t orig_qry_start = dest_sam->result.query_start; + dest_sam->result.query_start = (*path_buf) [path_len - 1].j - 1; // query_start is one-based | DK: this should read path.j is one-based + if (query_start < 0) + tmap_bug(); + dest_sam->score += new_score - long_gap; + dest_sam->pos -= pos_adj; // adjust the position further if there was a deletion + // merge adjacent cigar operations + tmap_map_util_merge_adjacent_cigar_operations (dest_sam); + + // update alignment box + // query_end does not change + assert (dest_sam->result.query_start + added_qry == orig_qry_start); + dest_sam->result.target_end += added_ref; + uint32_t read_side = (dest_sam->strand==0)?F5P:R3P; + stat->num_salvaged [read_side] ++; + stat->bases_salvaged_qry [read_side] += added_qry; + stat->bases_salvaged_ref [read_side] += added_ref; + stat->score_salvaged_total [read_side] += new_score - long_gap; + salvaged = 1; + } + } + break; + } - /* - // Debugging - for(j=0;jgapl_len; + start_pos = dest_sam->pos + dest_sam->target_len + 1; + end_pos = start_pos + tglen - 1; + if (refseq->annos [dest_sam->seqid].len < end_pos) // bound + end_pos = refseq->annos [dest_sam->seqid].len; // one-based + + if (end_pos < start_pos) + break; // exit the loop + int32_t long_gap = opt->pen_gapl; + + //ZZ: check amplicon + uint32_t ampl_start = 0, ampl_end = 0; + if (ampl_exist || tmap_map_get_amplicon (refseq, dest_sam->seqid, dest_sam->pos, dest_sam->pos + dest_sam->target_len, &l_start, &l_end, dest_sam->strand)) + { + ampl_exist = 1; + if (ampl_end <= dest_sam->pos + dest_sam->target_len + 1) + break; + if (end_pos < ampl_end) + end_pos = ampl_end; + long_gap = 0; + } - s = &sams_tmp->sams[i]; - - // shallow copy previous data - (*s) = tmp_sam; - - // Smith Waterman with banding - // NB: we store the score from the banded version, which does not allow - // ins then del, or del then ins. The vectorized version does. - // NB: left genomic indel justification is facilitated by always using the - // forward strand target/query combination. - // NB: iupac bases may also increase the score - if(0 < conv) { // NB: there were IUPAC bases - s->score = tmap_sw_global_banded_core(target, tlen, query, qlen, &par_iupac, - tmp_sam.result.score_fwd, path, &path_len, 0); - } - else { - s->score = tmap_sw_global_banded_core(target, tlen, query, qlen, &par, - tmp_sam.result.score_fwd, path, &path_len, 0); - } + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); - // ZZ: There can be case when the global score is the same as the vectorized version - // whileas the alignments are different. We are not addressing all of them, but the - // cases where the resulting alignment starts or ends with deletion shall be avoided. - if( (s->score != tmp_sam.score && 1 < tmp_sam.result.n_best) - || (path_len > 0 && (TMAP_SW_FROM_D == path[0].ctype || TMAP_SW_FROM_D == path[path_len-1].ctype))) - { - /* - fprintf(stderr, "s->score=%d tmp_sam.score=%d\n", s->score, tmp_sam.score); - fprintf(stderr, "tmp_sam.result.n_best=%d\n", tmp_sam.result.n_best); - */ + // try to add a larger deletion + // DK: BUG (in original code): this works only if the prefix salvage procedure above did not succeed. Otherwise, the query points to the new start of aligned region, + // so that query + seq_len points beyond the end of valid sequence (and possibly beyond the end of the buffer) - // TS-9081 (DVK): catch the state of the input parameters -#ifdef CHECK_SWCORE_PARAMS - // qlen and query - int32_t saved_query_len = qlen; - uint8_t* saved_query = alloca (qlen+1); - memcpy (saved_query, query, qlen); saved_query [qlen] = 0; - // tlen and target - int32_t saved_target_len = tlen; - unit8_t* saved_target = alloca (tlen+1); - memcpy (saved_target, target, tlen); saved_target [tlen] = 0; - // SW parameters - tmap_sw_param_t saved_params; - memcpy (&saved_params, (0 < conv)? &par_iupac : &par, sizeof (tmap_sw_param_t)); - saved_params.matrix = alloca (saved_params.row * saved_params.row * sizeof (int32_t)); - memcpy (saved_params.matrix, (0 < conv)? par_iupac.matrix : par.matrix, saved_params.row * saved_params.row * sizeof (int32_t)); -#endif - - // explicitly fit the query into the target - if(0 < conv) { // NB: there were IUPAC bases - s->score = tmap_sw_fitting_core(target, tlen, query, qlen, &par_iupac, path, &path_len, 0); - } - else { - s->score = tmap_sw_fitting_core(target, tlen, query, qlen, &par, path, &path_len, 0); - } + del_score = tmap_sw_clipping_core2 (target->data, target->data_len, orig_query + query_end + 1, orig_query_len - query_end - 1, + par, + 1, 1, // allow the start and end of the target to be skipped + 0, 1, // deletions + NULL, 0, 0); -#ifdef CHECK_SWCORE_PARAMS - tmap_sw_param_t* pp = (0 < conv)? &par_iupac : ∥ - if (qlen != saved_query_len || - tlen != saved_target_len || - memcmp (saved_query_len, query, qlen) || - memcmp (saved_target_len, target, tlen) || - pp->gap_open != saved_params.gap_open || - pp->gap_ext != saved_params.gap_ext || - pp->gap_end != saved_params.gap_end || - pp->row != saved_params.row || - pp->band_width != saved_params.gap_width || - memcmp (pp->matrix, saved_params.matrix, saved_params.row * saved_params.row * sizeof (int32_t))) - { - fprintf (stderr, "\nUpper stack data changed during the function call:\n"); - fprintf (stderr, "Read: %s (%ld bp)\n", qry_name, qry_seq->l); - fprintf (stderr, "Full Query (%4ld bp): %s\n", qry_seq->l, qry_seq->s); - fprintf (stderr, "Query (%4d bp): %s\n", qlen, query); - fprintf (stderr, "Subject (%4d bp): ", tlen); - for (iii = 0; iii != tlen; ++iii) - fputc ("ACGTN" [(target [iii] < 4) ? target [iii] : 4], stderr); - fputc ('\n', stderr); - fprintf (stderr, "score is %d; tmp_sam.score is %d\n", s->score, tmp_sam.score); - tmap_bug (); - } -#endif + // try to add a larger insertion + ins_score = tmap_sw_clipping_core2 (target->data, target->data_len, orig_query + query_end + 1, orig_query_len - query_end - 1, + par, + 0, 1, // only allow the end of the target to be skipped + 1, 1, // insertions, + NULL, 0, 0); + + if(del_score <= 0 && ins_score <= 0) + // do nothing + new_score = -1; + else + { + // path memory + if (*path_buf_sz <= target->data_len + orig_query_len - query_end - 1) + { // lengthen the path + *path_buf_sz = target->data_len + orig_query_len - query_end - 1; + tmap_roundup32 (*path_buf_sz); + *path_buf = tmap_realloc (*path_buf, sizeof (tmap_sw_path_t) * *path_buf_sz, "path_buf"); + } + if (del_score < ins_score) + { + // get the path + tmap_sw_clipping_core2 (target->data, target->data_len, orig_query + query_end + 1, orig_query_len - query_end - 1, + par, + 0, 1, // only allow the end of the target to be skipped + 1, 1, // insertions, + *path_buf, &path_len, 0); + op = BAM_CINS; + op_len = (*path_buf) [path_len - 1].j - 1; + new_score = ins_score; + } + else + { + // get the path + tmap_sw_clipping_core2 (target->data, target->data_len, orig_query + query_end + 1, orig_query_len - query_end - 1, + par, + 1, 1, // allow the start and end of the target to be skipped + 0, 1, // deletions + *path_buf, &path_len, 0); + op = BAM_CDEL; + op_len = (*path_buf) [path_len - 1].i - 1; + new_score = del_score; + } + } + if(0 < new_score && 0 <= new_score - long_gap) //?? redundant condition (DK) + { + int32_t n_cigar_op = (0 < op_len) ? 1 : 0; + int32_t j; + // get the cigar + cigar = tmap_sw_path2cigar (*path_buf, path_len, &n_cigar); + if (0 == n_cigar) + tmap_bug (); + // re-allocate the cigar + dest_sam->cigar = tmap_realloc (dest_sam->cigar, sizeof (uint32_t) * (n_cigar_op + n_cigar + dest_sam->n_cigar), "dest_sam->cigar"); + // cigar_buf_reallocated = 1; + // add the operation + uint32_t added_ref = 0, added_qry = 0; + if (1 == n_cigar_op) // 0 < op_len + { + TMAP_SW_CIGAR_STORE (dest_sam->cigar [dest_sam->n_cigar], op, op_len); + if (op == BAM_CDEL) + { + dest_sam->target_len += op_len; + added_ref += op_len; + } + else + added_qry += op_len; - int32_t leading, trailing; - while(1) { - // check to see if there are leading/trailing deletions, then redo - leading = trailing = 0; - // leading - for(j=path_len-1;0<=j;j--) { - if(TMAP_SW_FROM_D == path[j].ctype) leading++; - else break; - } - // trailing - for(j=0;js; - tmap_string_t* qry_seq = tmap_seq_get_bases (seq); - - fprintf (stderr, "\nLead/trail delete on read: %s (%ld bp), leading = %d, trailing = %d\n", qry_name, qry_seq->l, leading, trailing); - fprintf (stderr, "Full Query (%4ld bp): %s\n", qry_seq->l, qry_seq->s); - fprintf (stderr, "Query (%4d bp): %s\n", qlen, query); - // target in alignment - fprintf (stderr, "Subject (%4d bp): ", tlen); - for (iii = 0; iii != tlen; ++iii) - fputc ("ACGTN" [(target [iii] < 4) ? target [iii] : 4], stderr); - fputc ('\n', stderr); - fprintf (stderr, "score is %d; tmp_sam.score is %d\n", s->score, tmp_sam.score); - fprintf (stderr, "Alignment path (%d positions):\n", path_len); - for (iii = 0; iii != path_len; ++iii) - fprintf (stderr, "%3d", path [iii].i); - fputc ('\n', stderr); - for (iii = 0; iii != path_len; ++iii) - fprintf (stderr, "%3c", "MIDSN" [(path [iii].ctype < 4) ? path [iii].ctype : 4]); - fputc ('\n', stderr); - for (iii = 0; iii != path_len; ++iii) - fprintf (stderr, "%3d", path [iii].j); - fputc ('\n', stderr); - fprintf (stderr, "Reference Id: %d, strand: %d, position: %d", sams->sams[end].seqid, sams->sams[end].strand, sams->sams[end].pos); - - fprintf (stderr, "n_best: %d\n", tmp_sam.result.n_best); - fprintf (stderr, "Converted IUPAC bases in reference: %d\n", conv); - - tmap_sw_param_t *pp = (0 == conv)? &par : &par_iupac; - fprintf (stderr, "SW parameters:\n"); - fprintf (stderr, " band width = %d\n", pp->band_width); /*!< for Smith-Waterman banding */ - fprintf (stderr, " gap open = %d\n", pp->gap_open); /*!< gap open penalty (positive) */ - fprintf (stderr, " gap_ext = %d\n", pp->gap_ext); /*!< gap extension penalty (positive) */ - fprintf (stderr, " gap_end = %d\n", pp->gap_end); /*!< gap end penalty (positive */ - fprintf (stderr, " alphabet size = %d\n", pp->row); - unsigned rr, cc; - for (rr = 0; rr != pp->row; ++rr) - { - fprintf (stderr, " %3u ", rr); - for (cc = 0; cc != pp->row; ++cc) - { - fprintf (stderr, "%5d", pp->matrix [pp->row*rr + cc]); - } - fprintf (stderr, "\n"); - } - } - - tmap_bug(); // NB: this should not happen with tmap_sw_fitting_core - } - // TODO: should we skip otherwise? No, since VSW can differfrom GSW - } + } + // add the rest of the cigar + for (j = 0; j < n_cigar; j++) + dest_sam->cigar [j + dest_sam->n_cigar + n_cigar_op] = cigar [j]; + + dest_sam->n_cigar += n_cigar + n_cigar_op; + for (j = 0; j < n_cigar; j++) + { + int32_t oplen = TMAP_SW_CIGAR_LENGTH (cigar [j]); + switch (TMAP_SW_CIGAR_OP (cigar [j])) + { + case BAM_CMATCH: + added_qry += oplen; + case BAM_CDEL: + added_ref += oplen; + dest_sam->target_len += oplen; + break; + case BAM_CINS: + case BAM_CSOFT_CLIP: + added_qry += oplen; + default: + break; + } + } + free (cigar); cigar = NULL; + // update the query end + query_end += (*path_buf) [0].j; // query_end is zero-based + if (orig_query_len <= query_end) + tmap_bug(); + dest_sam->score += new_score - long_gap; + // merge adjacent cigar operations + tmap_map_util_merge_adjacent_cigar_operations (dest_sam); + // update alignment box + + dest_sam->result.query_end += added_qry; + dest_sam->result.target_end += added_ref; + if (dest_sam->result.query_end != query_end) + tmap_bug (); + if (dest_sam->result.target_end != dest_sam->target_len - 1) + tmap_bug (); + + uint32_t read_side = (dest_sam->strand==0)?F3P:R5P; + stat->num_salvaged [read_side] ++; + stat->bases_salvaged_qry [read_side] += added_qry; + stat->bases_salvaged_ref [read_side] += added_ref; + stat->score_salvaged_total [read_side] += new_score - long_gap; + salvaged = 1; } + } + break; + } + extend_softclips_to_read_edges (dest_sam, seqs); + stat->reads_salvaged += salvaged; +} - s->pos = s->pos + (path[path_len-1].i-1); // zero-based - if(path[path_len-1].ctype == TMAP_SW_FROM_I) { // don't quite understand this ZZ - s->pos++; - } - s->cigar = tmap_sw_path2cigar(path, path_len, &s->n_cigar); - if(0 == s->n_cigar) { - tmap_bug(); - } - s->target_len = 0; - for(j=0;jn_cigar;j++) { - switch(TMAP_SW_CIGAR_OP(s->cigar[j])) { +static void update_align_box_and_tlen (tmap_map_sam_t* dest_sam) +{ + // assumes pos and cigar are correct + // update box from cigar + dest_sam->result.target_start = dest_sam->result.target_end = 0; + dest_sam->result.query_start = dest_sam->result.query_end = 0; + uint32_t *cigar_el_p, *cent; + for (cigar_el_p = dest_sam->cigar, cent = dest_sam->cigar + dest_sam->n_cigar; cigar_el_p != cent; ++ cigar_el_p ) + { + uint32_t op = TMAP_SW_CIGAR_OP (*cigar_el_p); + uint32_t oplen = TMAP_SW_CIGAR_LENGTH (*cigar_el_p); + switch (op) + { case BAM_CMATCH: + dest_sam->result.query_end += oplen; + dest_sam->result.target_end += oplen; + break; case BAM_CDEL: - s->target_len += TMAP_SW_CIGAR_LENGTH(s->cigar[j]); - break; + dest_sam->result.target_end += oplen; + break; + case BAM_CINS: + dest_sam->result.query_end += oplen; + break; + case BAM_CSOFT_CLIP: + if (cigar_el_p == dest_sam->cigar) // very first operation + dest_sam->result.query_start = dest_sam->result.query_end = oplen; + break; default: - break; - } - } + tmap_bug (); + } + } + dest_sam->target_len = dest_sam->result.target_end; + // adjust for inclusivity + if (dest_sam->result.query_end != dest_sam->result.query_start) + --dest_sam->result.query_end; + if (dest_sam->result.target_end != dest_sam->result.target_start) + --dest_sam->result.target_end; +} - /** - * Try to detect large indels present as soft-clipped prefixes of the - * alignment. - */ - uint32_t ampl_exist = 0; - while(1) { // NB: so we can break out at any time - if(0 < opt->pen_gapl && 0 < query_start) { // start of the alignment - // ZZ:We may check amplicon here as well. May try this only when it is near start of an amplicon. - // Here the reference mapping position is known by s->pos to s->pos+s->target_len - // We can check the amplicon with these. - // A. When bed file is not present, current logic holds. - // B.i. If bed file is given, when an amplicon is present, try use all the bases to the begin/end of amplicon, very small or no long gap penalty. - // B.ii.If bed file is given and the hit position is not in an amplicon, normal logic apply. - - int32_t del_score = 0; - int32_t ins_score = 0; - uint32_t op, op_len; - int32_t new_score; - uint32_t *cigar = NULL; - int32_t n_cigar; - int32_t pos_adj = 0; - - query = (uint8_t*)tmap_seq_get_bases(seqs[strand])->s; // forward genomic strand - - // get the target sequence before the start of the alignment - tlen = query_start + opt->gapl_len; - if(s->pos <= tlen) start_pos = 1; - else start_pos = s->pos - tlen + 1; - end_pos = s->pos; - if(end_pos < start_pos) break; // exit the loop - tlen = end_pos - start_pos + 1; - int32_t long_gap = opt->pen_gapl; - //ZZ: check amplicon - if (tmap_map_get_amplicon(refseq, s->seqid, s->pos, s->pos+s->target_len, &l_start, &l_end, strand)) { - ampl_exist = 1; - if (ampl_start >= s->pos) break; - if (start_pos > ampl_start) { - start_pos = ampl_start; - tlen = end_pos - start_pos + 1; - } - //Alternative, align only to the begin of the amplicon. - /* - start_pos = ampl_start; - tlen = end_pos - start_pos + 1; - */ - long_gap = 0; - } +void key_trim_alignment ( + tmap_map_sam_t* dest_sam, // mapping being trimmed + tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_seq_t* seq, // read + tmap_refseq_t* refseq, // reference server + ref_buf_t* target, // reference data + tmap_map_stats_t *stat // statistics +) +{ - // target mem - target = tmp_target; // reset target in memory - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - tmp_target = target; // store for later - } - // Get the new target - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start_pos, end_pos, target, 0, &conv)) { - tmap_bug(); - } - if(0 < conv && 0 == iupac_init) { - par_iupac.matrix = matrix_iupac; - __map_util_gen_ap_iupac(par_iupac, opt); - iupac_init = 1; - } + // get first base + uint8_t key_base = INT8_MAX; + if (seq->ks) + key_base = tmap_nt_char_to_int [(uint8_t) seq->ks [strlen (seq->ks) - 1]]; // NB: last base of the key + // get the new target + // cache data + if (INT8_MAX != key_base) + { + int32_t qlen = dest_sam->result.query_end - dest_sam->result.query_start; // update query length + uint8_t* orig_query = (uint8_t*) tmap_seq_get_bases (seqs [dest_sam->strand])->s; // forward genomic strand + uint8_t* adjusted_query = orig_query + dest_sam->result.query_start; + uint32_t start_pos = dest_sam->pos + 1; + uint32_t end_pos = start_pos + dest_sam->target_len - 1; + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); + tmap_map_util_keytrim (adjusted_query, qlen, target->data, target->data_len, dest_sam->strand, key_base, dest_sam, stat); + // only pos, cigar and n_cigar are updated by tmap_map_util_keytrim + // for end-repair, we need target_len and the alignment box also updated + // update them here + update_align_box_and_tlen (dest_sam); + } +} - // try to add a larger deletion - del_score = tmap_sw_clipping_core2(target, tlen, query, query_start, - (0 < conv) ? &par_iupac : &par, - 1, 1, // allow the start and end of the target to be skipped - 1, 0, // deletions - NULL, 0, 0); - - // try to add a larger insertion - // TODO: how do we enforce that the target starts immediately where - // we want it to? - ins_score = tmap_sw_clipping_core2(target, tlen, query, query_start, - (0 < conv) ? &par_iupac : &par, - 1, 0, // only allow the start of the target to be skipped - 1, 1, // insertions, - NULL, 0, 0); - // reset start/end position - start_pos = s->pos + 1; - end_pos = s->pos + s->target_len; - - if(del_score <= 0 && ins_score <= 0) { - // do nothing - new_score = -1; - } - else { - if(del_score < ins_score) { - // get the path - tmap_sw_clipping_core2(target, tlen, query, query_start, - (0 < conv) ? &par_iupac : &par, - 1, 0, // only allow the start of the target to be skipped - 1, 1, // insertions, - path, &path_len, 0); - op = BAM_CINS; - op_len = query_start - path[0].j; - new_score = ins_score; - pos_adj = 0; - } - else { - // get the path - tmap_sw_clipping_core2(target, tlen, query, query_start, - (0 < conv) ? &par_iupac : &par, - 1, 1, // allow the start and end of the target to be skipped - 1, 0, // deletions - path, &path_len, 0); - op = BAM_CDEL; - op_len = tlen - path[0].i; - new_score = del_score; - pos_adj = op_len; - } - if(path[path_len-1].ctype == TMAP_SW_FROM_I) pos_adj++; // TODO: is this correct? - } - if(0 < new_score && 0 <= new_score - long_gap) { - int32_t n_cigar_op = (0 < op_len) ? 1 : 0; - // get the cigar - cigar = tmap_sw_path2cigar(path, path_len, &n_cigar); - if(0 == n_cigar) { - tmap_bug(); - } - // re-allocate the cigar - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(n_cigar_op+n_cigar+s->n_cigar), "s->cigar"); - // shift up - for(j=s->n_cigar-1;0<=j;j--) { - s->cigar[j+n_cigar_op+n_cigar] = s->cigar[j]; - } - // add the operation - if(1 == n_cigar_op) { // 0 < op_len - TMAP_SW_CIGAR_STORE(s->cigar[n_cigar], op, op_len); - } - // add the rest of the cigar - for(j=0;jcigar[j] = cigar[j]; - } - s->n_cigar += n_cigar + n_cigar_op; - for(j=0;jtarget_len += TMAP_SW_CIGAR_LENGTH(cigar[j]); - s->pos -= TMAP_SW_CIGAR_LENGTH(cigar[j]); // NB: must adjust the position at the start of the alignment - break; - default: - break; - } - } - free(cigar); cigar = NULL; - // update the query end - query_start = path[path_len-1].j - 1; // query_start is one-based - if(query_start < 0) tmap_bug(); - s->score += new_score - long_gap; - s->pos -= pos_adj; // adjust the position further if there was a deletion - // merge adjacent cigar operations - tmap_map_util_merge_adjacent_cigar_operations(s); - } +// The 3' end-repair may alter the alignment and then 5' end-repair should get buffers adjusted accordingly. +// The IR-28816 reveals condition where both 3' and 5' are repairable, and 5' repair used unajusted positions, causing buffer overrun. +// This function serves as a wrapper arounf both 3' and 5' end-repair, correcting SCs and adjusting coordinates and buffers independently. +// returns code for stats collection: 0 for no adjustment, 1 for softclip, 2 for extension +// note: A bit redundant function - it could do a little less if softclip to insert conversion is done only for one proper end of the read. +// Not too much extra work though, and the logic is slightly cleaner. + +static int32_t end_repair_helper ( + tmap_map_sam_t* dest_sam, // mapping being trimmed + tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_seq_t* seq, // read + tmap_refseq_t* refseq, // reference server + ref_buf_t* target, // reference data + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + int32_t softclip_start, // softclip allowed at read 5' + int32_t softclip_end, // softclip allowed at read 3' + tmap_map_opt_t* opt, // tmap options + tmap_map_stats_t *stat, // statistics + int32_t five_prime // 1 to repair 5', 0 to repairt 3' +) +{ + uint8_t* orig_query = (uint8_t*) tmap_seq_get_bases (seqs [dest_sam->strand])->s; // forward genomic strand + int32_t orig_qlen = tmap_seq_get_bases_length (seqs [0]); + int32_t qlen = dest_sam->result.query_end - dest_sam->result.query_start + 1; // update query length + uint8_t* adjusted_query = orig_query + dest_sam->result.query_start; + + // tmap_map_util_end_repair expects no softclips if they are not explicitely allowed; + // if there are such, replace them with INSs + if (TMAP_SW_CIGAR_OP (dest_sam->cigar [0]) == BAM_CSOFT_CLIP && + ((softclip_start == 0 && dest_sam->strand == 0) || + (softclip_end == 0 && dest_sam->strand == 1))) + { + int32_t op_len = TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [0]); + TMAP_SW_CIGAR_STORE (dest_sam->cigar [0], BAM_CINS, op_len); + qlen += op_len; + adjusted_query -= op_len; + } + if (TMAP_SW_CIGAR_OP (dest_sam->cigar [dest_sam->n_cigar-1]) == BAM_CSOFT_CLIP && + ((softclip_end == 0 && dest_sam->strand == 0) || + (softclip_start == 0 && dest_sam->strand == 1))) + { + int32_t op_len = TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [dest_sam->n_cigar - 1]); + TMAP_SW_CIGAR_STORE (dest_sam->cigar [dest_sam->n_cigar - 1], BAM_CINS, op_len); + qlen += op_len; + } - // reset start/end position - start_pos = s->pos + 1; - end_pos = s->pos + s->target_len; - if(end_pos < start_pos) tmap_bug(); - - // retrieve the target - //TODO: is this always necessary (keytrim)? - // target mem - tlen = end_pos - start_pos + 1; - if(tlen <= 0) tmap_bug(); - target = tmp_target; // reset target in memory - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - tmp_target = target; // store for later - } - // Get the new target - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start_pos, end_pos, target, 0, &conv)) { - tmap_bug(); - } - if(0 < conv && 0 == iupac_init) { - par_iupac.matrix = matrix_iupac; - __map_util_gen_ap_iupac(par_iupac, opt); - iupac_init = 1; - } + // extend cached reference enough to support convertion of softclips to matches (tmap_map_util_end_repair assumes they are in) + uint32_t sc5 = 0, sc3 = 0; + if (dest_sam->n_cigar && TMAP_SW_CIGAR_OP (dest_sam->cigar [0]) == BAM_CSOFT_CLIP) + sc5 = TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [0]); + if (dest_sam->n_cigar > 1 && TMAP_SW_CIGAR_OP (dest_sam->cigar [dest_sam->n_cigar - 1]) == BAM_CSOFT_CLIP) + sc3 = TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [dest_sam->n_cigar - 1]); + + // the cigar is in the query direction, convert 5' - 3' to target coords + if (dest_sam->strand == 1) + { + uint32_t t = sc5; + sc5 = sc3; + sc3 = t; + } + // do not go below 0 or over contig length + uint32_t start_pos, end_pos; + if (dest_sam->pos + dest_sam->result.target_start < sc5) + start_pos = 1; + else + start_pos = dest_sam->pos + dest_sam->result.target_start - sc5 + 1; + end_pos = dest_sam->pos + dest_sam->result.target_end + sc3 + 1; + if (end_pos > refseq->annos [dest_sam->seqid].len) + end_pos = refseq->annos [dest_sam->seqid].len; + // cache the data + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); + + // massage target cache internals. This would not affect integrity of cache + uint8_t* orig_data = target->data; + target->data = orig_data + (dest_sam->pos + 1 - start_pos); + target->data_len = dest_sam->target_len; + + int32_t result = tmap_map_util_end_repair (seq, adjusted_query, qlen, target->data, target->data_len, dest_sam->strand, path_buf, path_buf_sz, refseq, dest_sam, opt, five_prime, stat); + + assert (dest_sam->n_cigar); + dest_sam->result.query_start = (TMAP_SW_CIGAR_OP (dest_sam->cigar [0]) == BAM_CSOFT_CLIP) ? (TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [0])) : 0; + dest_sam->result.query_end = (TMAP_SW_CIGAR_OP (dest_sam->cigar [dest_sam->n_cigar - 1]) == BAM_CSOFT_CLIP) ? (orig_qlen - TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [dest_sam->n_cigar - 1]) - 1) : orig_qlen - 1; + dest_sam->result.target_start = 0; + dest_sam->result.target_end = dest_sam->target_len - 1; + + return result; +} - // reset query - query = (uint8_t*)tmap_seq_get_bases(seqs[strand])->s; // forward genomic strand - query += query_start; - } - break; - } - - /** - * Try to detect large indels present as soft-clipped suffixes of the - * alignment. - */ - while(1) { // NB: so we can break out at any time - if(0 < opt->pen_gapl && query_end < seq_len-1) { - int32_t del_score = 0; - int32_t ins_score = 0; - uint32_t op, op_len; - int32_t new_score = 0; - uint32_t *cigar = NULL; - int32_t n_cigar; - - // get the target sequence after the end of the alignment. - tlen = seq_len - 1 - query_end + opt->gapl_len; - start_pos = s->pos + s->target_len + 1; - end_pos = start_pos + tlen - 1; - if(refseq->annos[s->seqid].len < end_pos) { // bound - end_pos = refseq->annos[s->seqid].len; // one-based - } - if(end_pos < start_pos) break; // exit the loop - tlen = end_pos - start_pos + 1; - int32_t long_gap = opt->pen_gapl; - - //ZZ: check amplicon - if (ampl_exist || tmap_map_get_amplicon(refseq, s->seqid, s->pos, s->pos+s->target_len, &l_start, &l_end, strand)) { - ampl_exist = 1; - if (ampl_end <= s->pos+s->target_len + 1) break; - if (end_pos < ampl_end) { - end_pos = ampl_end; - tlen = end_pos - start_pos + 1; - } - //Alternative, align only to the end of the amplicon. - /* - end_pos = ampl_end; - tlen = end_pos - start_pos + 1; - */ - long_gap = 0; - } +void end_repair ( + tmap_map_sam_t* dest_sam, // mapping being trimmed + tmap_seq_t** seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_seq_t* seq, // read + tmap_refseq_t* refseq, // reference server + ref_buf_t* target, // reference data + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_map_opt_t* opt, // tmap options + tmap_map_stats_t *stat // statistics +) +{ + int32_t softclip_start, softclip_end; + tmap_map_util_set_softclip (opt, seq, &softclip_start, &softclip_end); - // target mem - target = tmp_target; // reset target in memory - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - tmp_target = target; // store for later - } - // Get the new target - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start_pos, end_pos, target, 0, &conv)) { - tmap_bug(); - } - if(0 < conv && 0 == iupac_init) { - par_iupac.matrix = matrix_iupac; - __map_util_gen_ap_iupac(par_iupac, opt); - iupac_init = 1; - } + int32_t r3 = end_repair_helper (dest_sam, seqs, seq, refseq, target, path_buf, path_buf_sz, softclip_start, softclip_end, opt, stat, 0); - // try to add a larger deletion - del_score = tmap_sw_clipping_core2(target, tlen, query + query_end + 1, seq_len - query_end - 1, - (0 < conv) ? &par_iupac : &par, - 1, 1, // allow the start and end of the target to be skipped - 0, 1, // deletions - NULL, 0, 0); - - // try to add a larger insertion - ins_score = tmap_sw_clipping_core2(target, tlen, query + query_end + 1, seq_len - query_end - 1, - (0 < conv) ? &par_iupac : &par, - 0, 1, // only allow the end of the target to be skipped - 1, 1, // insertions, - NULL, 0, 0); - - if(del_score <= 0 && ins_score <= 0) { - // do nothing - new_score = -1; - } - else { - if(del_score < ins_score) { - // get the path - tmap_sw_clipping_core2(target, tlen, query + query_end + 1, seq_len - query_end - 1, - (0 < conv) ? &par_iupac : &par, - 0, 1, // only allow the end of the target to be skipped - 1, 1, // insertions, - path, &path_len, 0); - op = BAM_CINS; - op_len = path[path_len-1].j - 1; - new_score = ins_score; - } - else { - // get the path - tmap_sw_clipping_core2(target, tlen, query + query_end + 1, seq_len - query_end - 1, - (0 < conv) ? &par_iupac : &par, - 1, 1, // allow the start and end of the target to be skipped - 0, 1, // deletions - path, &path_len, 0); - op = BAM_CDEL; - op_len = path[path_len-1].i - 1; - new_score = del_score; - } - } - if(0 < new_score && 0 <= new_score - long_gap) { - int32_t n_cigar_op = (0 < op_len) ? 1 : 0; - // get the cigar - cigar = tmap_sw_path2cigar(path, path_len, &n_cigar); - if(0 == n_cigar) { - tmap_bug(); - } - // re-allocate the cigar - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(n_cigar_op+n_cigar+s->n_cigar), "s->cigar"); - // add the operation - if(1 == n_cigar_op) { // 0 < op_len - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar], op, op_len); - } - // add the rest of the cigar - for(j=0;jcigar[j+s->n_cigar+n_cigar_op] = cigar[j]; - } - s->n_cigar += n_cigar + n_cigar_op; - for(j=0;jtarget_len += TMAP_SW_CIGAR_LENGTH(cigar[j]); - break; - default: - break; - } - } - free(cigar); cigar = NULL; - // update the query end - query_end += path[0].j; // query_end is zero-based - if(seq_len <= query_end) tmap_bug(); - s->score += new_score - long_gap; - // merge adjacent cigar operations - tmap_map_util_merge_adjacent_cigar_operations(s); - } + int32_t r5 = end_repair_helper (dest_sam, seqs, seq, refseq, target, path_buf, path_buf_sz, softclip_start, softclip_end, opt, stat, 1); - // reset start/end position - start_pos = s->pos + 1; - end_pos = s->pos + s->target_len; - - // retrieve the target - //TODO: is this always necessary (keytrim)? - // target mem - tlen = end_pos - start_pos + 1; - target = tmp_target; // reset target in memory - if(target_mem < tlen) { // more memory? - target_mem = tlen; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - tmp_target = target; // store for later - } - // Get the new target - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, s->seqid+1, start_pos, end_pos, target, 0, &conv)) { - tmap_bug(); - } - if(0 < conv && 0 == iupac_init) { - par_iupac.matrix = matrix_iupac; - __map_util_gen_ap_iupac(par_iupac, opt); - iupac_init = 1; - } - } - break; - } + // update alignment box (dest_sam->result), as it may be used downstream + // assume alignment is correct here - covers entire read and softclips are properly extended + if (r3 != 0 || r5 != 0) + stat->reads_end_repair_clipped++; + if (r3 == 2 || r5 == 2) + stat->reads_end_repair_extended++; +} - // add soft clipping - if(0 < query_start) { - // soft clip the front of the read - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(1+s->n_cigar), "s->cigar"); - for(j=s->n_cigar-1;0<=j;j--) { // shift up - s->cigar[j+1] = s->cigar[j]; - } - TMAP_SW_CIGAR_STORE(s->cigar[0], BAM_CSOFT_CLIP, query_start); - s->n_cigar++; - } - if(query_end < seq_len-1) { - // soft clip the end of the read - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(1+s->n_cigar), "s->cigar"); - TMAP_SW_CIGAR_STORE(s->cigar[s->n_cigar], BAM_CSOFT_CLIP, seq_len - query_end - 1); - s->n_cigar++; - } +tmap_map_sams_t* +tmap_map_util_find_align_starts ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // initial rough mapping + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_map_opt_t *opt, // tmap parameters + ref_buf_t* target, // reference data + tmap_map_stats_t* stat // statistics + +) +{ + int32_t end; // index for the alignment being evaluated + int32_t i; // index into results buffer; + + tmap_map_sams_t *sams_tmp = NULL; // buffer to receive the alignments + int32_t orig_query_len = 0; // current read size + + tmap_vsw_t *vsw = NULL; + tmap_vsw_opt_t *vsw_opt = NULL; + + int32_t softclip_start, softclip_end; + + sams_tmp = tmap_map_sams_init (sams); + tmap_map_sams_realloc (sams_tmp, sams->n); // pre-allocate for same number of mappings + + // set softclip flags, rule-based + tmap_map_util_set_softclip (opt, seq, &softclip_start, &softclip_end); + + // initialize opt + vsw_opt = tmap_vsw_opt_init (opt->score_match, opt->pen_mm, opt->pen_gapo, opt->pen_gape, opt->score_thr); + + // init seqs + orig_query_len = tmap_seq_get_bases_length (seqs [0]); + + // reverse compliment query + vsw = tmap_vsw_init ((uint8_t*) tmap_seq_get_bases(seqs[1])->s, orig_query_len, softclip_end, softclip_start, opt->vsw_type, vsw_opt); + + for (end = 0, i = 0; end < sams->n; ++end) // for each placement + { + tmap_map_sam_t* src_sam = sams->sams + end; // pointer to the receiving sam (last filled element in sams_tmp) + tmap_map_sam_t* dest_sam = sams_tmp->sams + i; + + if (find_alignment_start ( + src_sam, // source: raw (position-only) mapping + dest_sam, // destination: refined (aligned) mapping + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + refseq, // reference server + softclip_start, // is 5' softclip allowed + softclip_end, // is 3' softclip allowed + opt, // tmap parameters + target, // reference data + vsw, // vectorized aligner object + stat + )) + ++i; + } - // update aux data - tmap_map_sam_malloc_aux(s); - switch(s->algo_id) { - case TMAP_MAP_ALGO_MAP1: - (*s->aux.map1_aux) = (*tmp_sam.aux.map1_aux); - break; - case TMAP_MAP_ALGO_MAP2: - (*s->aux.map2_aux) = (*tmp_sam.aux.map2_aux); - break; - case TMAP_MAP_ALGO_MAP3: - (*s->aux.map3_aux) = (*tmp_sam.aux.map3_aux); - break; - case TMAP_MAP_ALGO_MAP4: - (*s->aux.map4_aux) = (*tmp_sam.aux.map4_aux); - break; - case TMAP_MAP_ALGO_MAPVSW: - (*s->aux.map_vsw_aux) = (*tmp_sam.aux.map_vsw_aux); - break; - default: - tmap_bug(); - break; - } + // realloc + tmap_map_sams_realloc (sams_tmp, i); - // key trim the data - if(1 == opt->softclip_key && INT8_MAX != key_base) { - // get the new target - tmap_map_util_keytrim(query, qlen, target, tlen, strand, key_base, s); - } + // free memory + tmap_map_sams_destroy (sams); + tmap_vsw_destroy (vsw); + tmap_vsw_opt_destroy (vsw_opt); - // end repair - if(0 != opt->end_repair) { - tmap_map_util_end_repair(seq, query, qlen, target, tlen, strand, path, refseq, s, opt, 0); - tmap_map_util_end_repair(seq, query, qlen, target, tlen, strand, path, refseq, s, opt, 1); // do 5' end repair?? - } - // TODO: ZZ: If the primer sequences are added, we remove the cigar that corresponds to the sequence, at the same time calculate the score - // of this part of alignment, this will be substracted from the alignment score there. - // to be removed if the other path works. - if (left_added_bases + right_added_bases > 0) { - tmap_map_rmquerybases_cigar(s, left_added_bases, right_added_bases); - } + // sort by max score, then min coordinate - i++; + return sams_tmp; +} - // update start/end - end++; - // start = end; - target = tmp_target; - } +void tmap_map_util_align ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + ref_buf_t* target, // reference data + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_sw_param_t* par, // Smith-Waterman scopring parameters + tmap_map_stats_t *stat // statistics +) +{ + uint32_t i; + for (i = 0; i < sams->n; ++i) // for each placement + { + tmap_map_sam_t* dest_sam = sams->sams + i; + compute_alignment ( + dest_sam, // destination: refined (aligned) mapping + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + refseq, // reference server + target, // pointer to the reference cache + path_buf, // buffer for traceback path + path_buf_sz, // used portion and allocated size of traceback path. + par, // Smith-Waterman scoring parameters + stat // statistics + ); + } +} - // realloc - tmap_map_sams_realloc(sams_tmp, i); - - // free memory - tmap_map_sams_destroy(sams); - free(path); - free(target); - tmap_vsw_destroy(vsw); - tmap_vsw_opt_destroy(vsw_opt); - - // sort by max score, then min coordinate - if(1 < sams_tmp->n) { - tmap_sort_introsort(tmap_map_sam_sort_score_coord, sams_tmp->n, sams_tmp->sams); - } +void tmap_map_util_salvage_edge_indels ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_map_opt_t *opt, // tmap parameters + tmap_sw_param_t* par, // Smith-Waterman scopring parameters + ref_buf_t* target, // reference data cache + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_map_stats_t *stat // statistics +) +{ + uint32_t i; + for (i = 0; i < sams->n; ++i) // for each placement + { + tmap_map_sam_t* dest_sam = sams->sams + i; + salvage_long_indel_at_edges ( + dest_sam, // destination: refined (aligned) mapping + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + refseq, // reference server + target, // reference data cache + path_buf, // buffer for traceback path + path_buf_sz, // used portion and allocated size of traceback path. + opt, // tmap options + par, // Smith-Waterman scopring parameters + stat // statistics + ); + } +} - return sams_tmp; +void tmap_map_util_cure_softclips ( + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t **seqs // array of size 4 that contains pre-computed inverse / complement combinations +) +{ + uint32_t i; + for (i = 0; i < sams->n; ++i) // for each placement + { + tmap_map_sam_t* dest_sam = sams->sams + i; + extend_softclips_to_read_edges ( + dest_sam, // mapping being adjusted + seqs // 4-element array containing fwd, rec, compl and rev/compl read sequence + ); + } } +void tmap_map_util_trim_key ( + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_refseq_t *refseq, // reference server + ref_buf_t* target, // reference data cache + tmap_map_stats_t *stat // statistics + +) +{ + uint32_t i; + for (i = 0; i < sams->n; ++i) // for each placement + { + tmap_map_sam_t* dest_sam = sams->sams + i; + key_trim_alignment ( + dest_sam, // mapping being trimmed + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + seq, // read + refseq, // reference server + target, // reference data cache + stat // statistics + ); + } +} + +void tmap_map_util_end_repair_bulk ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_map_opt_t *opt, // tmap parameters + ref_buf_t* target, // reference data cache + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_map_stats_t *stat // statistics + +) +{ + uint32_t i; + for (i = 0; i < sams->n; ++i) // for each placement + { + tmap_map_sam_t* dest_sam = sams->sams + i; + end_repair ( + dest_sam, // mapping being trimmed + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + seq, // read + refseq, // reference server + target, // pointer to reference cache control structure + path_buf, // address of the pointer to buffer for traceback path, + path_buf_sz, // pointer to the integer holding allocated size of traceback path. + opt, // tmap options + stat // statistics + ); + } +} + + +tmap_map_sams_t * +tmap_map_util_sw_gen_cigar (tmap_refseq_t* refseq, + tmap_map_sams_t* sams, + tmap_seq_t* seq, + tmap_seq_t** seqs, + tmap_map_opt_t* opt, + tmap_map_stats_t *stat +) +{ + // int32_t start, end; + int32_t i; // index into results buffer; + + tmap_map_sams_t *sams_tmp = NULL; // buffer to receive the alignments + + ref_buf_t target; + target_cache_init (&target); + tmap_sw_path_t *path_buf = NULL; // buffer for traceback path + int32_t path_buf_sz = 0; // used portion and allocated size of traceback path. + tmap_sw_param_t sw_par; + tmap_map_util_populate_sw_par (&sw_par, opt); + + // DK: seems nothing in this function depend on the mappings order + // // sort by strand/chr/pos/score + // tmap_sort_introsort (tmap_map_sam_sort_coord, sams->n, sams->sams); + + // Step 1: fing alignment start point + sams_tmp = tmap_map_util_find_align_starts + ( + refseq, // reference server + sams, // initial rough mapping + seq, // read + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + opt, // tmap parameters + &target, // target cache control structure + stat // statistics + ); + + // Step 2: compute alignment + tmap_map_util_align + ( + refseq, // reference server + sams_tmp, // mappings to compute alignments for + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + &target, // target cache control structure + &path_buf, // buffer for traceback path + &path_buf_sz, // used portion and allocated size of traceback path. + &sw_par, // Smith-Waterman scoring parameters + stat // statistics + ); + + // Step 3: salvage long indels close to the read edges + if (opt->pen_gapl) + tmap_map_util_salvage_edge_indels + ( + refseq, // reference server + sams_tmp, // mappings to compute alignments for + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + opt, // tmap parameters + &sw_par, // Smith-Waterman scoring parameters + &target, // target cache control structure + &path_buf, // buffer for traceback path + &path_buf_sz, // used portion and allocated size of traceback path. + stat // statistics + ); + + + // Step 4: extend alignment to the read edges by adding soft clips + tmap_map_util_cure_softclips + ( + sams_tmp, // mappings to compute alignments for + seqs // array of size 4 that contains pre-computed inverse / complement combinations + ); + + // Step 5: key trim the data + if (opt->softclip_key) + tmap_map_util_trim_key + ( + sams_tmp, // mappings to compute alignments for + seq, // read + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + refseq, // reference server + &target, // target cache control structure + stat // statistics + ); + + // Step 6: end repair + if (opt->end_repair) + tmap_map_util_end_repair_bulk + ( + refseq, // reference server + sams_tmp, // mappings to compute alignments for + seq, // read + seqs, // array of size 4 that contains pre-computed inverse / complement combinations + opt, // tmap parameters + &target, // target cache control structure + &path_buf, // buffer for traceback path + &path_buf_sz, // used portion and allocated size of traceback path. + stat // statistics + ); + + // free memory + // tmap_map_sams_destroy (sams); + free (path_buf); + target_cache_free (&target); + + // sort by max score, then min coordinate + if(1 < sams_tmp->n) + tmap_sort_introsort (tmap_map_sam_sort_score_coord, sams_tmp->n, sams_tmp->sams); + + return sams_tmp; +} + + // TODO: make sure the "longest" read alignment is found int32_t -tmap_map_util_fsw(tmap_seq_t *seq, tmap_map_sams_t *sams, tmap_refseq_t *refseq, +tmap_map_util_fsw (tmap_seq_t *seq, tmap_map_sams_t *sams, tmap_refseq_t *refseq, int32_t bw, int32_t softclip_type, int32_t score_thr, int32_t score_match, int32_t pen_mm, int32_t pen_gapo, - int32_t pen_gape, int32_t fscore, int32_t use_flowgram) -{ - int32_t i, j, k, l; - uint8_t *target = NULL; - int32_t target_mem = 0, target_len = 0; - int32_t was_int = 1; - - tmap_fsw_flowseq_t *fseq = NULL; - tmap_fsw_path_t *path = NULL; - int32_t path_mem = 0, path_len = 0; - tmap_fsw_param_t param; - int32_t matrix[25]; - uint8_t *flow_order = NULL; - int32_t flow_order_len = 0; - uint8_t *key_seq = NULL; - int32_t key_seq_len = 0; - - if(0 == sams->n || NULL == seq->fo || NULL == seq->ks) return 0; - - // flow order - flow_order_len = strlen(seq->fo); - flow_order = tmap_malloc(sizeof(uint8_t)*(flow_order_len+1), "flow_order"); - memcpy(flow_order, seq->fo, flow_order_len); - tmap_to_int((char*)flow_order, flow_order_len); - - // key sequence - key_seq_len = strlen(seq->ks); - key_seq = tmap_malloc(sizeof(uint8_t)*(key_seq_len+1), "key_seq"); - memcpy(key_seq, seq->ks, key_seq_len); - tmap_to_int((char*)key_seq, key_seq_len); + int32_t pen_gape, int32_t fscore, int32_t use_flowgram, tmap_map_stats_t* stat) +{ + int32_t i, j, k, l; + uint8_t *target = NULL; + int32_t target_mem = 0, target_len = 0; + int32_t was_int = 1; + + tmap_fsw_flowseq_t *fseq = NULL; + tmap_fsw_path_t *path = NULL; + int32_t path_mem = 0, path_len = 0; + tmap_fsw_param_t param; + int32_t matrix [25]; + uint8_t *flow_order = NULL; + int32_t flow_order_len = 0; + uint8_t *key_seq = NULL; + int32_t key_seq_len = 0; + + if (0 == sams->n || NULL == seq->fo || NULL == seq->ks) + return 0; - // generate the alignment parameters - param.matrix = matrix; - param.band_width = 0; - param.offset = TMAP_MAP_OPT_FSW_OFFSET; // this sets the hp difference - __tmap_fsw_gen_ap1(param, score_match, pen_mm, pen_gapo, pen_gape, fscore); + // flow order + flow_order_len = strlen (seq->fo); + flow_order = tmap_malloc (sizeof (uint8_t) * (flow_order_len + 1), "flow_order"); + memcpy (flow_order, seq->fo, flow_order_len); + tmap_to_int ((char*) flow_order, flow_order_len); + + // key sequence + key_seq_len = strlen (seq->ks); + key_seq = tmap_malloc (sizeof (uint8_t) * (key_seq_len+1), "key_seq"); + memcpy (key_seq, seq->ks, key_seq_len); + tmap_to_int ((char*) key_seq, key_seq_len); + + // generate the alignment parameters + param.matrix = matrix; + param.band_width = 0; + param.offset = TMAP_MAP_OPT_FSW_OFFSET; // this sets the hp difference + __tmap_fsw_gen_ap1 (param, score_match, pen_mm, pen_gapo, pen_gape, fscore); + + was_int = tmap_seq_is_int (seq); + if (0 == tmap_seq_is_int(seq)) + tmap_seq_to_int(seq); + + // get flow sequence + fseq = tmap_fsw_flowseq_from_seq (NULL, seq, flow_order, flow_order_len, key_seq, key_seq_len, use_flowgram); + + if (!fseq) + { + free (flow_order); + free (key_seq); + return 0; + } - was_int = tmap_seq_is_int(seq); - if(0 == tmap_seq_is_int(seq)) { - tmap_seq_to_int(seq); - } + // go through each hit + for (i = 0; i < sams->n; ++i) + { + tmap_map_sam_t *s = &sams->sams [i]; + uint32_t ref_start, ref_end; + // get the reference end position + // NB: soft-clipping at the start may cause ref_start to be moved + param.band_width = 0; + ref_start = ref_end = s->pos + 1; + for (j = 0; j < s->n_cigar; ++j) + { + int32_t op, op_len; + + op = TMAP_SW_CIGAR_OP (s->cigar [j]); + op_len = TMAP_SW_CIGAR_LENGTH (s->cigar [j]); + + switch(op) + { + case BAM_CMATCH: + ref_end += op_len; + break; + case BAM_CDEL: + if (param.band_width < op_len) + param.band_width += op_len; + ref_end += op_len; + break; + case BAM_CINS: + if (param.band_width < op_len) + param.band_width += op_len; + break; + case BAM_CSOFT_CLIP: + if (0 == j) + { + if (ref_start <= op_len) + ref_start = 1; + else + ref_start = ref_start - op_len; + } + else + ref_end += op_len; + break; + default: + // ignore + break; + } + } + ref_end--; // NB: since we want this to be one-based + + // check bounds + if (ref_start < 1) + ref_start = 1; + if (refseq->annos [s->seqid].len < ref_end) + ref_end = refseq->annos [s->seqid].len; + else if (ref_end < 1) + ref_end = 1; + + // get the target sequence + target_len = ref_end - ref_start + 1; + if(target_mem < target_len) + { + target_mem = target_len; + tmap_roundup32 (target_mem); + target = tmap_realloc (target, sizeof (uint8_t) * target_mem, "target"); + } + target_len = tmap_refseq_subseq (refseq, ref_start + refseq->annos [s->seqid].offset, target_len, target); + /* + // NB: IUPAC codes are turned into mismatches + if(NULL == tmap_refseq_subseq2(refseq, sams->sams[end].seqid+1, start_pos, end_pos, target, 1, NULL)) { + tmap_bug(); + } + */ - // get flow sequence - fseq = tmap_fsw_flowseq_from_seq(NULL, seq, flow_order, flow_order_len, key_seq, key_seq_len, use_flowgram); + if (1 == s->strand) // reverse compliment + tmap_reverse_compliment_int(target, target_len); - if(NULL == fseq) { - free(flow_order); - free(key_seq); - return 0; - } + // add to the band width + param.band_width += 2 * bw; - // go through each hit - for(i=0;in;i++) { - tmap_map_sam_t *s = &sams->sams[i]; - uint32_t ref_start, ref_end; + // make sure we have enough memory for the path + while (path_mem <= target_len + fseq->num_flows) // lengthen the path + { + path_mem = target_len + fseq->num_flows + 1; + tmap_roundup32 (path_mem); + path = tmap_realloc (path, sizeof (tmap_fsw_path_t) * path_mem, "path"); + } - // get the reference end position - // NB: soft-clipping at the start may cause ref_start to be moved - param.band_width = 0; - ref_start = ref_end = s->pos + 1; - for(j=0;jn_cigar;j++) { - int32_t op, op_len; + /* + fprintf(stderr, "strand=%d\n", s->strand); + fprintf(stderr, "ref_start=%d ref_end=%d\n", ref_start, ref_end); + fprintf(stderr, "base_calls:\n"); + for(j=0;jnum_flows;j++) { + for(k=0;kbase_calls[j];k++) { + fputc("ACGTN"[fseq->flow_order[j % fseq->flow_order_len]], stderr); + } + } + fputc('\n', stderr); + fprintf(stderr, "target:\n"); + for(j=0;jflow_order_len;j++) { + fputc("ACGTN"[fseq->flow_order[j]], stderr); + } + fputc('\n', stderr); + */ + + // re-align + s->ascore = s->score; + path_len = path_mem; + //fprintf(stderr, "old score=%d\n", s->score); + switch (softclip_type) + { + case TMAP_MAP_OPT_SOFT_CLIP_ALL: + s->score = tmap_fsw_clipping_core (target, target_len, fseq, ¶m, + 1, 1, s->strand, path, &path_len); + break; + case TMAP_MAP_OPT_SOFT_CLIP_LEFT: + s->score = tmap_fsw_clipping_core (target, target_len, fseq, ¶m, + 1, 0, s->strand, path, &path_len); + break; + case TMAP_MAP_OPT_SOFT_CLIP_RIGHT: + s->score = tmap_fsw_clipping_core (target, target_len, fseq, ¶m, + 0, 1, s->strand, path, &path_len); + break; + case TMAP_MAP_OPT_SOFT_CLIP_NONE: + s->score = tmap_fsw_clipping_core (target, target_len, fseq, ¶m, + 0, 0, s->strand, path, &path_len); + break; + default: + tmap_error("soft clipping type was not recognized", Exit, OutOfRange); + break; + } + s->score_subo = INT32_MIN; + //fprintf(stderr, "new score=%d path_len=%d\n", s->score, path_len); + + if (0 < path_len) + { // update + /* + for(j=0;jscore = (int32_t)((s->score + 99.99) / 100.0); + + // position + s->pos = ref_start - 1; // zero-based + // NB: must be careful of leading insertions and strandedness + if (0 == s->strand) + { + if (0 <= path [path_len - 1].j) + s->pos += (path [path_len - 1].j); + } + else + { + if (path [0].j < target_len) + s->pos += target_len - path [0].j - 1; + } + + if (refseq->len < s->pos) + tmap_bug (); + + // new cigar + free (s->cigar); + s->cigar = tmap_fsw_path2cigar (path, path_len, &s->n_cigar, 1); + + // reverse the cigar + if (1 == s->strand) + for(i=0; i < (s->n_cigar >> 1); i++) + { + uint32_t t = s->cigar [i]; + s->cigar [i] = s->cigar [s->n_cigar - i - 1]; + s->cigar [s->n_cigar - i - 1] = t; + } - op = TMAP_SW_CIGAR_OP(s->cigar[j]); - op_len = TMAP_SW_CIGAR_LENGTH(s->cigar[j]); + int32_t skipped_start, skipped_end; + skipped_start = skipped_end = 0; + + // soft-clipping + if (0 < path [path_len - 1].i) // skipped beginning flows + // get the number of bases to clip + for (j = 0; j < path [path_len - 1].i; j++) + skipped_start += fseq->base_calls[j]; + + if (path [0].i + 1 < fseq->num_flows) // skipped ending flows + // get the number of bases to clip + for (j = path [0].i + 1; j < fseq->num_flows; j++) + skipped_end += fseq->base_calls [j]; + + if (1 == s->strand) // swap + { + k = skipped_start; + skipped_start = skipped_end; + skipped_end = k; + } + if (0 < skipped_start) // start soft clip + { + s->cigar = tmap_realloc (s->cigar, sizeof (uint32_t) * (1 + s->n_cigar), "s->cigar"); + for (l = s->n_cigar-1; 0 <= l; l--) + s->cigar [l + 1] = s->cigar [l]; + + TMAP_SW_CIGAR_STORE (s->cigar[0], BAM_CSOFT_CLIP, skipped_start); + s->n_cigar++; + } + if (0 < skipped_end) // end soft clip + { + s->cigar = tmap_realloc (s->cigar, sizeof (uint32_t) * (1 + s->n_cigar), "s->cigar"); + s->cigar [s->n_cigar] = (skipped_end << 4) | 4; + s->n_cigar++; + } + } + } + // free + free (target); + free (path); + free (flow_order); + free (key_seq); + + if (0 == was_int) + tmap_seq_to_char(seq); + + return 1; +} + + +int32_t tmap_map_util_remove_5_prime_softclip +( + tmap_refseq_t *refseq, // reference server + tmap_map_sam_t *dest_sam, // mappings to fix + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + ref_buf_t* target, // reference data cache + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_sw_param_t* par, // Smith-Waterman scopring parameters + tmap_map_opt_t *opt, // tmap parameters + tmap_map_stats_t *stat // statistics +) +{ + + if (dest_sam->strand == 0 && (dest_sam->n_cigar == 0 || TMAP_SW_CIGAR_OP (dest_sam->cigar [0]) != BAM_CSOFT_CLIP)) + return 0; + if (dest_sam->strand == 1 && (dest_sam->n_cigar == 0 || TMAP_SW_CIGAR_OP (dest_sam->cigar [dest_sam->n_cigar - 1]) != BAM_CSOFT_CLIP)) + return 0; + // if we're here, there is a soft clip on 5' + + // if (0 == strcmp (seq->data.sam->name->s, "K0BK3:03674:08750")) + // printf ("Here"); + uint8_t* orig_query = (uint8_t*) tmap_seq_get_bases (seqs [dest_sam->strand])->s; // forward genomic strand + int32_t orig_query_len = tmap_seq_get_bases_length (seqs [0]); + + int32_t sc_len = TMAP_SW_CIGAR_LENGTH ((dest_sam->strand == 0) ? (dest_sam->cigar [0]) : (dest_sam->cigar [dest_sam->n_cigar - 1])); + int32_t max_ins_equiv = (opt->pen_gape != 0) ? ((opt->score_match * sc_len - opt->pen_gapo) / opt->pen_gape) : (opt->score_match * sc_len - opt->pen_gapo); + if (max_ins_equiv < 0) + max_ins_equiv = 0; + int32_t target_len = sc_len + max_ins_equiv; + + int32_t start_pos, end_pos; + + if (dest_sam->strand == 0) + { + // 5' is a beginning of the alignment, align forward ref to forward read in no-clipping mode + // target + start_pos = (dest_sam->pos > target_len) ? (dest_sam->pos - target_len + 1) : 1; + end_pos = dest_sam->pos; + if (start_pos >= end_pos) + { + // use Ins in place of SC + TMAP_SW_CIGAR_STORE (dest_sam->cigar [0], BAM_CINS, sc_len); + dest_sam->score -= opt->pen_gapo + opt->pen_gape*sc_len; + dest_sam->result.query_start = 0; + } + else + { + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); + // path memory + if (*path_buf_sz <= target->data_len + sc_len) + { // lengthen the path + *path_buf_sz = target->data_len + sc_len; + tmap_roundup32 (*path_buf_sz); + *path_buf = tmap_realloc (*path_buf, sizeof (tmap_sw_path_t) * *path_buf_sz, "path_buf"); + } + // align softclipped part of the query + int32_t path_len; + int32_t add_score = tmap_sw_clipping_core2 ( + target->data, + target->data_len, + orig_query, + sc_len, + par, + 1, 0, // only allow the start of the target to be skipped + 0, 0, // do not allow any softclips + *path_buf, + &path_len, + 0); + + // build the cigar + int32_t addition_len; + uint32_t* addition = tmap_sw_path2cigar (*path_buf, path_len, &addition_len); + if (addition_len == 0) // alignment failure - use Ins + { + // use Ins in place of SC + TMAP_SW_CIGAR_STORE (dest_sam->cigar [0], BAM_CINS, sc_len); + dest_sam->score -= opt->pen_gapo + opt->pen_gape*sc_len; + dest_sam->result.query_start = 0; + return 1; + } + assert (addition); + assert (dest_sam->n_cigar > 1); + // compute and apply pos, target_len amd alignment box adjustment + int32_t cp, addition_ref_len = 0; + for (cp = 0; cp != addition_len; ++cp) + { + int32_t op = TMAP_SW_CIGAR_OP (addition [cp]); + int32_t oplen = TMAP_SW_CIGAR_LENGTH (addition [cp]); + if (op == BAM_CMATCH || op == BAM_CDEL) + addition_ref_len += oplen; + } + + //int32_t pos_adj = 0; // DK: I do not quite understand this. Modeled after position computing in 'raw' alignment. + // DK: end-repair does not do this adjustment + //if ((*path_buf) [0].ctype == TMAP_SW_FROM_I) + // pos_adj++; // TODO: is this correct? + + stat->num_5_softclips [dest_sam->strand] ++; + stat->bases_5_softclips_qry [dest_sam->strand] += dest_sam->result.query_start; + stat->bases_5_softclips_ref [dest_sam->strand] += addition_ref_len; + stat->score_5_softclips_total [dest_sam->strand] += add_score; + + assert (addition_ref_len <= dest_sam->pos); + dest_sam->pos -= addition_ref_len; // - pos_adj; + dest_sam->target_len += addition_ref_len; + dest_sam->result.target_end += addition_ref_len; + dest_sam->result.query_start = 0; + + + //tmap_log_record_begin (); + //tmap_map_log_text_align (" After 5' unclip :\n", addition, addition_len, (const char *) orig_query, orig_query_len, !dest_sam->strand, (const char*) (target->data + (target->data_len - addition_ref_len)), dest_sam->pos); + //tmap_log_record_end (); + + // merge addition with the cigar + // check if last op of addition matches first op of orig cigar; merge ops if yes + int32_t old_shift = addition_len - 1; + int32_t addition_last_op = TMAP_SW_CIGAR_OP (addition [addition_len - 1]); + int32_t old_first_nonsc_op = TMAP_SW_CIGAR_OP (dest_sam->cigar [1]); + if (addition_last_op == old_first_nonsc_op) + { + int32_t merged_len = TMAP_SW_CIGAR_LENGTH (addition [addition_len - 1]) + TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [1]); + TMAP_SW_CIGAR_STORE (addition [addition_len - 1], addition_last_op, merged_len); + --old_shift; + } + int32_t new_cigar_len = dest_sam->n_cigar + old_shift; + if (new_cigar_len > dest_sam->n_cigar) + dest_sam->cigar = tmap_realloc (dest_sam->cigar, new_cigar_len * sizeof (*(dest_sam->cigar)), "cigar"); + if (old_shift > 0) + memmove (dest_sam->cigar + old_shift, dest_sam->cigar, dest_sam->n_cigar * sizeof (*(dest_sam->cigar))); + if (old_shift < 0) + memmove (dest_sam->cigar, dest_sam->cigar - old_shift, (dest_sam->n_cigar + old_shift) * sizeof (*(dest_sam->cigar))); + memcpy (dest_sam->cigar, addition, addition_len * sizeof (*(dest_sam->cigar))); + dest_sam->n_cigar = new_cigar_len; + dest_sam->score += add_score; + free (addition); + + } + } + else + { + // 5' is an end of the alignment, align foward ref to reverse read in no-clipping mode + start_pos = dest_sam->pos + dest_sam->target_len + 1; + end_pos = start_pos + target_len - 1; + if (end_pos > refseq->annos [dest_sam->seqid].len) + end_pos = refseq->annos [dest_sam->seqid].len; + if (start_pos >= end_pos) + { + // use Ins in place of SC + TMAP_SW_CIGAR_STORE (dest_sam->cigar [dest_sam->n_cigar - 1], BAM_CINS, sc_len); + dest_sam->score -= opt->pen_gapo + opt->pen_gape*sc_len; + dest_sam->result.query_end = orig_query_len - 1; + } + else + { + assert (start_pos < refseq->annos [dest_sam->seqid].len); + //if (start_pos >= end_pos) + // printf ("here"); + assert (start_pos < end_pos); + cache_target (target, refseq, dest_sam->seqid + 1, start_pos, end_pos); + // path memory + if (*path_buf_sz <= target->data_len + sc_len) + { // lengthen the path + *path_buf_sz = target->data_len + sc_len; + tmap_roundup32 (*path_buf_sz); + *path_buf = tmap_realloc (*path_buf, sizeof (tmap_sw_path_t) * *path_buf_sz, "path_buf"); + } + // align softclipped part of the query + int32_t path_len; + int32_t add_score = tmap_sw_clipping_core2 ( + target->data, + target->data_len, + orig_query, + sc_len, + par, + 0, 1, // only allow the end of the target to be skipped + 0, 0, // do not allow any softclips + *path_buf, + &path_len, + 0); + + // build the cigar + int32_t addition_len; + uint32_t* addition = tmap_sw_path2cigar (*path_buf, path_len, &addition_len); + if (addition_len == 0) // alignment failure - use Ins + { + // use Ins in place of SC + TMAP_SW_CIGAR_STORE (dest_sam->cigar [dest_sam->n_cigar - 1], BAM_CINS, sc_len); + dest_sam->score -= opt->pen_gapo + opt->pen_gape*sc_len; + dest_sam->result.query_end = orig_query_len - 1; + return 1; + } + assert (addition); + + // compute and apply pos, target_len amd alignment box adjustment + int32_t cp, addition_ref_len = 0; + for (cp = 0; cp != addition_len; ++cp) + { + int32_t op = TMAP_SW_CIGAR_OP (addition [cp]); + int32_t oplen = TMAP_SW_CIGAR_LENGTH (addition [cp]); + if (op == BAM_CMATCH || op == BAM_CDEL) + addition_ref_len += oplen; + } + + stat->num_5_softclips [dest_sam->strand] ++; + stat->bases_5_softclips_qry [dest_sam->strand] += dest_sam->result.query_start; + stat->bases_5_softclips_ref [dest_sam->strand] += addition_ref_len; + stat->score_5_softclips_total [dest_sam->strand] += add_score; + + assert (dest_sam->pos + addition_ref_len <= refseq->annos [dest_sam->seqid].len); + dest_sam->target_len += addition_ref_len; + dest_sam->result.target_end += addition_ref_len; + dest_sam->result.query_end = orig_query_len - 1; + // merge addition with the cigar + int32_t addition_pos = dest_sam->n_cigar - 1; + assert (addition_len); + assert (dest_sam->n_cigar > 1); + int32_t addition_first_op = TMAP_SW_CIGAR_OP (addition [0]); + int32_t old_last_nonsc_op = TMAP_SW_CIGAR_OP (dest_sam->cigar [dest_sam->n_cigar - 2]); + if (addition_first_op == old_last_nonsc_op) + { + int32_t merged_len = TMAP_SW_CIGAR_LENGTH (addition [0]) + TMAP_SW_CIGAR_LENGTH (dest_sam->cigar [dest_sam->n_cigar - 2]); + TMAP_SW_CIGAR_STORE (addition [0], addition_first_op, merged_len); + --addition_pos; + if (addition_first_op == BAM_CINS || addition_first_op == BAM_CDEL) + add_score -= opt->pen_gapo; + } + int32_t new_cigar_len = addition_len + addition_pos; + if (new_cigar_len > dest_sam->n_cigar) + dest_sam->cigar = tmap_realloc (dest_sam->cigar, new_cigar_len * sizeof (*(dest_sam->cigar)), "cigar"); + memcpy (dest_sam->cigar + addition_pos, addition, addition_len * sizeof (*(dest_sam->cigar))); + dest_sam->n_cigar = new_cigar_len; + dest_sam->score += add_score; + free (addition); + + } + } + + return 1; +} + +static uint8_t bin_cigar_to_str (uint32_t* cigar, int32_t n_cigar, char* dest, uint32_t dest_sz) +{ + uint32_t* cent = cigar + n_cigar; + char* dp = dest; + uint32_t incr; + for (; cigar != cent; ++cigar, ++dest) + { + incr = snprintf (dp, dest_sz - (dp - dest), "%d", TMAP_SW_CIGAR_LENGTH (*cigar)); + if (dest_sz - (dp - dest) < incr + 1) + return 0; + dp += incr; + if (dest_sz - (dp - dest) < 2) + return 0; + *(dp++) = BAM_CIGAR_STR [TMAP_SW_CIGAR_OP (*cigar)]; + } + if (dp - dest >= dest_sz) + return 0; + *dp = 0; + return 1; +} + +// checks that alignment score is close enough to the recorded one +// checks if cigar consistent with read size and target_len +// checks if the target boundaries are right +// checks if alignment box consistent with cigar +void cigar_sanity_check +( + tmap_refseq_t *refseq, // reference server + tmap_map_sam_t *sam, // mapping to check + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + ref_buf_t* target, // reference data cache + tmap_map_opt_t *opt // tmap options (for this stage) +) +{ - switch(op) { + cache_target (target, refseq, sam->seqid + 1, sam->pos + 1, sam->pos + sam->target_len); + uint8_t* ref = target->data; + int32_t ref_len = target->data_len; + tmap_string_t* seq_str = seqs [sam->strand]->data.sam->seq; + char* qry = seq_str->s; + int32_t qry_len = seq_str ->l; + + uint32_t cigar_str_sz = 4 * sam->n_cigar + 1; + char* cigar_str = alloca (cigar_str_sz); + if (!bin_cigar_to_str (sam->cigar, sam->n_cigar, cigar_str, cigar_str_sz)) + tmap_bug (); + + if (sam->result.target_start != 0) + tmap_failure ("Alignment sanity check error on read %s:\n alignment box start is %d, should be 0", seq->data.sam->name->s, sam->result.target_start); + + uint32_t qry_pos = 0, ref_pos = 0; + int32_t al_score = 0, nogap_score = 0, score_chg; + uint32_t *opptr, *sent; + uint32_t op, prev_op = 0, oplen, p; // prev_op to 0 to alleviate gcc's jealousy. It thinks it may be uninitialized downstream (logic guarantees otherwise, but it can see only syntax) + for (opptr = sam->cigar, sent = opptr + sam->n_cigar; opptr != sent; ++opptr) + { + op = TMAP_SW_CIGAR_OP (*opptr); + oplen = TMAP_SW_CIGAR_LENGTH (*opptr); + if (opptr == sam->cigar && op != BAM_CSOFT_CLIP && sam->result.query_start != 0) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n query offset is %d while cigar (%s) does not start with softclip", seq->data.sam->name->s, sam->result.query_start, cigar_str); + switch (op) + { case BAM_CMATCH: - ref_end += op_len; - break; - case BAM_CDEL: - if(param.band_width < op_len) param.band_width += op_len; - ref_end += op_len; - break; + for (p = 0; p != oplen; ++p) + { + if (ref_pos >= ref_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes more reference positions than target length (%d)", seq->data.sam->name->s, cigar_str, ref_len); + if (qry_pos >= qry_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes more query positions than query length (%d)", seq->data.sam->name->s, cigar_str, qry_len); + score_chg = (ref [ref_pos] == qry [qry_pos])?(opt->score_match):(-opt->pen_mm); + al_score += score_chg; + nogap_score += score_chg; + ++qry_pos; + ++ref_pos; + } + break; case BAM_CINS: - if(param.band_width < op_len) param.band_width += op_len; - break; + if (qry_pos + oplen > qry_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes more query positions than query length (%d)", seq->data.sam->name->s, cigar_str, qry_len); + al_score -= (opt->pen_gapo + opt->pen_gape*oplen); + qry_pos += oplen; + break; + case BAM_CDEL: + if (ref_pos + oplen > ref_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes more reference positions than target length (%d)", seq->data.sam->name->s, cigar_str, ref_len); + al_score -= (opt->pen_gapo + opt->pen_gape*oplen); + ref_pos += oplen; + break; case BAM_CSOFT_CLIP: - if(0 == j) { - if(ref_start <= op_len) { - ref_start = 1; - } - else { - ref_start = ref_start - op_len; - } - } - else ref_end += op_len; - break; + if (qry_pos + oplen > qry_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes more query positions than query length (%d)", seq->data.sam->name->s, cigar_str, qry_len); + qry_pos += oplen; + if (opptr == sam->cigar) // first operation + { + if (qry_pos != sam->result.query_start) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) has starting softclip of size %d, while query start is %d", seq->data.sam->name->s, cigar_str, sam->result.query_start, oplen); + } + else if (opptr + 1 == sent) // last operation + { + if (sam->result.query_end + 1 + oplen != qry_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) has ending softclip of size %d, while query alignment box ends %d bases before end", seq->data.sam->name->s, cigar_str, oplen, qry_len - (sam->result.query_end + 1)); + } + else // SOFTCLIP not at the edge + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) has internal softclip at position %d", seq->data.sam->name->s, cigar_str, opptr - sam->cigar); + break; default: - // ignore - break; - } - } - ref_end--; // NB: since we want this to be one-based + tmap_bug (); + } + if (opptr != sam->cigar && prev_op == op) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) have consecutive cigar operations of the same type at position %d", seq->data.sam->name->s, cigar_str, opptr - sam->cigar - 1); + prev_op = op; + } + if (qry_pos != qry_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes %d query bases while query len is %d", seq->data.sam->name->s, cigar_str, qry_pos, qry_len); + if (sam->n_cigar != 0 && prev_op != BAM_CSOFT_CLIP && sam->result.query_end + 1 != qry_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n alignment box's query end (%d) does not match (query_len-1)(%d)", seq->data.sam->name->s, cigar_str, sam->result.query_end, qry_len-1); + if (ref_pos != sam->target_len) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar (%s) takes %d reference bases while target len is %d", seq->data.sam->name->s, cigar_str, ref_pos, ref_len); + if (ref_pos != sam->result.target_end + 1) + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT), "Alignment sanity check error on read %s:\n cigar target size (%d) does not match (alignment box + 1)(%d)", seq->data.sam->name->s, ref_pos, sam->result.target_end + 1); + +#define TMIN(X, Y) (((X) < (Y)) ? (X) : (Y)) +#define TMAX(X, Y) (((Y) < (X)) ? (X) : (Y)) + + // check that original alignment is consistent with post-processed one + // check if alignments differ + if (sam->n_cigar != sam->n_orig_cigar || sam->pos != sam->orig_pos || 0 != memcmp (sam->cigar, sam->orig_cigar, sam->n_cigar * sizeof (*(sam->cigar)))) + { +#define TOTAL_OVERLAP_CHECK +#ifdef TOTAL_OVERLAP_CHECK + { + #define MIN_OVERLAP_FACTOR 2 + // compute total overlap length and total match lengths + // use mergewalk to avoid quadratic runtime + int32_t r1_off = sam->orig_pos, q1_off = 0; // reference and query offset at current match in cigar1 (original) + int32_t r2_off = sam->pos, q2_off = 0; // reference and query offset at current match in cigar2 (post-processed) + uint32_t *opptr1 = sam->orig_cigar, *sent1 = opptr1 + sam->n_orig_cigar; // current operation pointer and sentinel for interating over cigar1 (original) + uint32_t *opptr2, *sent2 = sam->cigar + sam->n_cigar; // current operation pointer and sentinel for interating over cigar2 (post-processed) + uint32_t *tail2 = sam->cigar; // index in cigar2 (new) from which to start looking for overlap with current match in cigar1 (orig) + int32_t tail2_q_off = 0, tail2_r_off = r2_off; // match offsets at the beginning of tail2 match + int32_t op1, oplen1, op2, oplen2; // current cigar operations and operation lengths + int32_t match1_tot = 0, overlap_tot = 0; + int32_t stop, tail2_recorded; // flags for inner loop + for (; opptr1 != sent1; ++opptr1) // iterate over cigar1 (once) + { + op1 = TMAP_SW_CIGAR_OP (*opptr1), oplen1 = TMAP_SW_CIGAR_LENGTH (*opptr1); + switch (op1) + { + case BAM_CINS: + q1_off += oplen1; + break; + case BAM_CDEL: + r1_off += oplen1; + break; + case BAM_CSOFT_CLIP: + q1_off += oplen1; + break; + case BAM_CMATCH: + // advance along cigar 2 from last remembered 'tail2' position until overlap with op1 is possible (until beg2 < end1) + stop = 0, tail2_recorded = 0; + for (opptr2 = tail2, q2_off = tail2_q_off, r2_off = tail2_r_off; opptr2 != sent2 && !stop; ++opptr2) + { + op2 = TMAP_SW_CIGAR_OP (*opptr2), oplen2 = TMAP_SW_CIGAR_LENGTH (*opptr2); + switch (op2) + { + case BAM_CINS: + q2_off += oplen2; + break; + case BAM_CDEL: + r2_off += oplen2; + break; + case BAM_CSOFT_CLIP: + q2_off += oplen2; + break; + case BAM_CMATCH: + // check if overlap still possible (if beg2 < end1), break if not + if (r2_off >= r1_off + oplen1) + { + stop = 1; + break; + } + // remember where end2 overruned end1 as 'tail2' - these reads can potentially overlap next cigar1 match + if (!tail2_recorded && r2_off + oplen2 > r1_off + oplen1) + { + tail2 = opptr2; + tail2_r_off = r2_off; + tail2_q_off = q2_off; + tail2_recorded = 1; + } + // if diagonal is same, compute overlap and add to total + if (r2_off - q2_off == r1_off - q1_off) + { + int32_t largest_beg = TMAX (r2_off, r1_off); + int32_t smallest_end = TMIN (r2_off + oplen2, r1_off + oplen1); + int32_t overlap = smallest_end - largest_beg; + if (overlap > 0) + overlap_tot += overlap; + } + r2_off += oplen2; + q2_off += oplen2; + break; + default: + tmap_bug (); + } + } + q1_off += oplen1; + r1_off += oplen1; + match1_tot += oplen1; + break; + default: + tmap_bug (); + } + } + // now compute post-processed alignment total match length - it is not trivial to do this in above loop as each match in pp cigar may be seen several times + int32_t match2_tot = 0; + for (opptr2 = sam->cigar, sent2 = opptr2 + sam->n_cigar; opptr2 != sent2; ++opptr2) + if (TMAP_SW_CIGAR_OP (*opptr2) == BAM_CMATCH) + match2_tot += TMAP_SW_CIGAR_LENGTH (*opptr2); + { + if (overlap_tot * MIN_OVERLAP_FACTOR < TMIN (match1_tot, match2_tot)) + { + uint32_t orig_cigar_str_sz = 4 * sam->n_orig_cigar + 1; + char* orig_cigar_str = alloca (orig_cigar_str_sz); + if (!bin_cigar_to_str (sam->orig_cigar, sam->n_orig_cigar, orig_cigar_str, orig_cigar_str_sz)) + tmap_bug (); + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT_ALIGN), "Alignment sanity check error on read %s:\n post-processed alignment %s at position %d of %s (%s) has insufficient overlap with the original one %s at position %d: overlap=%d, orig_match_len=%d, pp_match_len=%d", seq->data.sam->name->s, cigar_str, sam->pos, refseq->annos [sam->seqid].name->s, (sam->strand?"rev":"fwd"), orig_cigar_str, sam->orig_pos, overlap_tot, match1_tot, match2_tot); + } +#if DEBUG_ALIGNMENT_OVERLAP + else + { + uint32_t orig_cigar_str_sz = 4 * sam->n_orig_cigar + 1; + char* orig_cigar_str = alloca (orig_cigar_str_sz); + if (!bin_cigar_to_str (sam->orig_cigar, sam->n_orig_cigar, orig_cigar_str, orig_cigar_str_sz)) + tmap_bug (); + tmap_conderr (0, "Alignment sanity check Ok on read %s:\n post-processed alignment %s at position %d has sufficient overlap with the original one %s at position %d: overlap=%d, match_len=%d", seq->data.sam->name->s, cigar_str, sam->pos, orig_cigar_str, sam->orig_pos, overlap_tot, match1_tot); + } +#endif + } + } +#endif +// #define LONG_MATCH_OVERLAP_CHECK +#ifdef LONG_MATCH_OVERLAP_CHECK + { + // at least one long match should overlap significantly + #define MIN_LONG_LEN 30 + #define MIN_CHECKED_MATCH_LEN 16 + #define MIN_OVERLAP_FACTOR 2 + int32_t r1_off = sam->orig_pos, r2_off, q1_off = 0, q2_off; + uint32_t *opptr1, *sent1, *opptr2, *sent2; + int32_t op1, oplen1, op2, oplen2; + int32_t has_overlap = 0, has_long = 0; + for (opptr1 = sam->orig_cigar, sent1 = opptr1 + sam->n_orig_cigar; opptr1 != sent1 && !has_overlap; ++opptr1) + { + op1 = TMAP_SW_CIGAR_OP (*opptr1); + oplen1 = TMAP_SW_CIGAR_LENGTH (*opptr1); + switch (op1) + { + case BAM_CINS: + q1_off += oplen1; + break; + case BAM_CDEL: + r1_off += oplen1; + break; + case BAM_CSOFT_CLIP: + q1_off += oplen1; + break; + case BAM_CMATCH: + { + if (oplen1 >= MIN_LONG_LEN) + has_long = 1; + if (oplen1 > MIN_CHECKED_MATCH_LEN) + { + r2_off = sam->pos, q2_off = 0, has_overlap = 0; + for (opptr2 = sam->cigar, sent2 = opptr2 + sam->n_cigar ; opptr2 != sent2 && !has_overlap; ++opptr2) + { + op2 = TMAP_SW_CIGAR_OP (*opptr2); + oplen2 = TMAP_SW_CIGAR_LENGTH (*opptr2); + switch (op2) + { + case BAM_CINS: + q2_off += oplen2; + break; + case BAM_CDEL: + r2_off += oplen2; + break; + case BAM_CSOFT_CLIP: + q2_off += oplen2; + break; + case BAM_CMATCH: + // check if match long enough and overlaps + if (r1_off - q1_off == r2_off - q2_off) // same diagonal && r1_off < r2_off + oplen2 && r2_off < r1_off + oplen1) // ref intervals intersect + { + // find the overlap size + int32_t largest_beg = TMAX (r1_off, r2_off); + int32_t smallest_end = TMIN (r1_off + oplen1, r2_off + oplen2); + int32_t overlap = smallest_end - largest_beg; + int32_t to_cover = oplen1; + if (oplen2 * MIN_OVERLAP_FACTOR >= MIN_CHECKED_MATCH_LEN && oplen2 < oplen1) + to_cover = oplen2; + if (overlap * MIN_OVERLAP_FACTOR >= to_cover) + has_overlap = 1; + } + q2_off += oplen2; + r2_off += oplen2; + break; + default: + tmap_bug (); + } + } + } + } + q1_off += oplen1; + r1_off += oplen1; + break; + default: + tmap_bug (); + } + } + if (has_long && !has_overlap) // some of long original alignment batches found to have no good overlapping match in final alignment + { + uint32_t orig_cigar_str_sz = 4 * sam->n_orig_cigar + 1; + char* orig_cigar_str = alloca (orig_cigar_str_sz); + if (!bin_cigar_to_str (sam->orig_cigar, sam->n_orig_cigar, orig_cigar_str, orig_cigar_str_sz)) + tmap_bug (); + tmap_conderr ((opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT_ALIGN), "Alignment sanity check error on read %s:\n post-processed alignment %s at position %d has insufficient overlap with the original one %s at position %d", seq->data.sam->name->s, cigar_str, sam->pos, orig_cigar_str, sam->orig_pos); + } + } +#endif + } - // check bounds - if(ref_start < 1) ref_start = 1; - if(refseq->annos[s->seqid].len < ref_end) { - ref_end = refseq->annos[s->seqid].len; - } - else if(ref_end < 1) ref_end = 1; - - // get the target sequence - target_len = ref_end - ref_start + 1; - if(target_mem < target_len) { - target_mem = target_len; - tmap_roundup32(target_mem); - target = tmap_realloc(target, sizeof(uint8_t)*target_mem, "target"); - } - target_len = tmap_refseq_subseq(refseq, ref_start + refseq->annos[s->seqid].offset, target_len, target); - /* - // NB: IUPAC codes are turned into mismatches - if(NULL == tmap_refseq_subseq2(refseq, sams->sams[end].seqid+1, start_pos, end_pos, target, 1, NULL)) { - tmap_bug(); - } - */ + if (opt->cigar_sanity_check == TMAP_MAP_SANITY_WARN_ALL || + opt->cigar_sanity_check == TMAP_MAP_SANITY_ERR_CONTENT_WARN_ALIGN_SCORE || + opt->cigar_sanity_check >= TMAP_MAP_SANITY_ERR_CONTENT_ALIGN_WARN_SCORE) + { + if (nogap_score < 0) + tmap_conderr ((opt->cigar_sanity_check == TMAP_MAP_SANITY_ERR_ALL), "Alignment sanity check error on read %s:\n Normal alignment score less gaps (%d) is negative (original score %d, cigar %s): possible misalignment", seq->data.sam->name->s, nogap_score, sam->result.score_fwd, cigar_str); + else if (nogap_score < (sam->result.score_fwd / 2)) + tmap_conderr ((opt->cigar_sanity_check == TMAP_MAP_SANITY_ERR_ALL), "Alignment sanity check error on read %s:\n Normal alignment score less gaps (%d) is below 1/2 of unadjusted alignment score (%d), cigar %s: possible misalignment", seq->data.sam->name->s, nogap_score, sam->result.score_fwd, cigar_str); + } +} - if(1 == s->strand) { // reverse compliment - tmap_reverse_compliment_int(target, target_len); - } - // add to the band width - param.band_width += 2 * bw; +// updates alignment box (result) from cigar, pos and target_len +// assumes cigar covers entire query, does not check with actual query +void tmap_map_update_alignment_box (tmap_map_sam_t* sam) +{ + sam->result.target_start = 0; + sam->result.target_end = sam->target_len - 1; + sam->result.query_start = 0; + uint32_t *cp, *sent, op, oplen; + for (cp = sam->cigar, sent = cp + sam->n_cigar; cp != sent; ++cp) + { + op = TMAP_SW_CIGAR_OP (*cp); + oplen = TMAP_SW_CIGAR_LENGTH (*cp); + switch (op) + { + case BAM_CMATCH: + case BAM_CDEL: + sam->result.query_end += oplen; + break; + case BAM_CSOFT_CLIP: + if (cp == sam->cigar) // first SC + sam->result.query_start = sam->result.query_end = oplen; + default: + break; + } + } +} - // make sure we have enough memory for the path - while(path_mem <= target_len + fseq->num_flows) { // lengthen the path - path_mem = target_len + fseq->num_flows + 1; - tmap_roundup32(path_mem); - path = tmap_realloc(path, sizeof(tmap_fsw_path_t)*path_mem, "path"); - } - /* - fprintf(stderr, "strand=%d\n", s->strand); - fprintf(stderr, "ref_start=%d ref_end=%d\n", ref_start, ref_end); - fprintf(stderr, "base_calls:\n"); - for(j=0;jnum_flows;j++) { - for(k=0;kbase_calls[j];k++) { - fputc("ACGTN"[fseq->flow_order[j % fseq->flow_order_len]], stderr); - } - } - fputc('\n', stderr); - fprintf(stderr, "target:\n"); - for(j=0;jflow_order_len;j++) { - fputc("ACGTN"[fseq->flow_order[j]], stderr); - } - fputc('\n', stderr); - */ - - // re-align - s->ascore = s->score; - path_len = path_mem; - //fprintf(stderr, "old score=%d\n", s->score); - switch(softclip_type) { - case TMAP_MAP_OPT_SOFT_CLIP_ALL: - s->score = tmap_fsw_clipping_core(target, target_len, fseq, ¶m, - 1, 1, s->strand, path, &path_len); - break; - case TMAP_MAP_OPT_SOFT_CLIP_LEFT: - s->score = tmap_fsw_clipping_core(target, target_len, fseq, ¶m, - 1, 0, s->strand, path, &path_len); - break; - case TMAP_MAP_OPT_SOFT_CLIP_RIGHT: - s->score = tmap_fsw_clipping_core(target, target_len, fseq, ¶m, - 0, 1, s->strand, path, &path_len); - break; - case TMAP_MAP_OPT_SOFT_CLIP_NONE: - s->score = tmap_fsw_clipping_core(target, target_len, fseq, ¶m, - 0, 0, s->strand, path, &path_len); - break; - default: - tmap_error("soft clipping type was not recognized", Exit, OutOfRange); - break; - } - s->score_subo = INT32_MIN; - //fprintf(stderr, "new score=%d path_len=%d\n", s->score, path_len); +void cigar_log (const uint32_t* cigar, unsigned cigar_sz) +{ + const uint32_t* sent; + for (sent = cigar+cigar_sz; cigar != sent; ++cigar) + { + uint32_t curop = bam_cigar_op (*cigar); + uint32_t count = bam_cigar_oplen (*cigar); + char schar; + switch (curop) + { + case BAM_CHARD_CLIP: + schar = 'H'; + break; + case BAM_CSOFT_CLIP: // skip + schar = 'S'; + break; + case BAM_CMATCH: + schar = 'M'; + break; + case BAM_CEQUAL: + schar = '='; + break; + case BAM_CDIFF: + schar = '#'; + break; + case BAM_CINS: + schar = 'I'; + break; + case BAM_CDEL: + schar = 'I'; + break; + default: + schar = '?'; + } + tmap_log ("%c%d", schar, count); + } +} - if(0 < path_len) { // update +uint32_t cigar_to_batches (const uint32_t* cigar, uint32_t cigar_sz, uint32_t* x_clip, AlBatch* batches, uint32_t max_batches) +{ + uint32_t xpos = 0, ypos = 0; // x is query, y is reference + AlBatch* curb = batches; + const uint32_t* sent = cigar + cigar_sz; + for (; cigar != sent && curb - batches != max_batches; ++cigar) + { + uint32_t curop = bam_cigar_op (*cigar); + uint32_t count = bam_cigar_oplen (*cigar); + switch (curop) + { + case BAM_CHARD_CLIP: // skip + case BAM_CSOFT_CLIP: // skip + if (x_clip) *x_clip += count; + break; + case BAM_CMATCH: + case BAM_CEQUAL: + case BAM_CDIFF: + curb->xpos = xpos; + curb->ypos = ypos; + curb->len = count; + xpos += count; + ypos += count; + ++curb; + x_clip = NULL; + break; + case BAM_CINS: + xpos += count; + x_clip = NULL; + break; + case BAM_CDEL: + ypos += count; + x_clip = NULL; + break; + default: + ; + } + } + return curb - batches; +} - /* - for(j=0;jscore = (int32_t)((s->score + 99.99)/100.0); +static const char* IB = "ACGTNBDHKMRSVWYN"; - // position - s->pos = (ref_start-1); // zero-based - // NB: must be careful of leading insertions and strandedness - if(0 == s->strand) { - if(0 <= path[path_len-1].j) { - s->pos += (path[path_len-1].j); - } - } - else { - if(path[0].j < target_len) { - s->pos += target_len - path[0].j - 1; - } - } - if(refseq->len < s->pos) { - tmap_bug(); - } +void log_batches (const char* xseq, unsigned xlen, uint8_t xrev, const char* yseq, unsigned ylen, uint8_t yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff) +{ + const unsigned margin = 0, width = 120, zero_based = 1; + + unsigned slen = 0; + unsigned blen = 0; + unsigned x = b_ptr->xpos; + unsigned y = b_ptr->ypos; + unsigned xstart = x; + unsigned ystart = y; + unsigned char xc, yc; + char s[3][MAXSTRLEN]; + + assert (width < MAXSTRLEN); + + + while (b_cnt > 0) + { + xc = xseq [x]; + yc = yseq [y]; + + // special handling for (x < b_ptr->xpos && y < b_ptr->ypos) + // treating as batch with special match symbols + + if (x < b_ptr->xpos && y < b_ptr->ypos) + { + s[0][slen] = (xc>='A')?xc:IB[xc]; + s[2][slen] = (yc>='A')?yc:IB[yc]; + s[1][slen] = '#'; + x++, y++, slen++; + } + // X insert + else if (x < b_ptr->xpos) + { + s[0][slen] = (xc>='A')?xc:IB[xc]; + s[1][slen] = ' '; + s[2][slen] = '-'; + x++, slen++; + } + // Y insert + else if (y < b_ptr->ypos) + { + s[0][slen] = '-'; + s[1][slen] = ' '; + s[2][slen] = (yc>='A')?yc:IB[yc]; + y++, slen++; + } + // emit text batch + else if (blen < b_ptr->len) + { + s[0][slen] = (xc>='A')?xc:IB[xc]; + s[2][slen] = (yc>='A')?yc:IB[yc]; + s[1][slen] = (toupper (xc) == toupper (yc) || toupper (xc) == 'N' || toupper (yc) == 'N') ? '*' : ' '; + x++, y++, slen++, blen++; + } + else + blen = 0, b_cnt--, b_ptr++; + //print accumulated lines + if ((slen + NUMSTRLEN > width) || b_cnt <= 0) + { + //null terminate all strings + for (int i = 0; i < 3; i++) + s[i][slen] = 0; - // new cigar - free(s->cigar); - s->cigar = tmap_fsw_path2cigar(path, path_len, &s->n_cigar, 1); + unsigned xdisp = (xrev ? xlen - xstart - 1 : xstart) + xoff + (zero_based ? 0 : 1); + unsigned ydisp = (yrev ? ylen - ystart - 1 : ystart) + yoff + (zero_based ? 0 : 1); - // reverse the cigar - if(1 == s->strand) { - for(i=0;in_cigar>>1;i++) { - uint32_t t = s->cigar[i]; - s->cigar[i] = s->cigar[s->n_cigar-i-1]; - s->cigar[s->n_cigar-i-1] = t; - } - } + tmap_log ("\n%*s%*d %s", margin, "", NUMSTRLEN, xdisp, s[0]); + tmap_log ("\n%*s%*s %s", margin, "", NUMSTRLEN, "", s[1]); + tmap_log ("\n%*s%*d %s\n", margin, "", NUMSTRLEN, ydisp, s[2]); - int32_t skipped_start, skipped_end; - skipped_start = skipped_end = 0; - - // soft-clipping - if(0 < path[path_len-1].i) { // skipped beginning flows - // get the number of bases to clip - for(j=0;jbase_calls[j]; - } - } - if(path[0].i+1 < fseq->num_flows) { // skipped ending flows - // get the number of bases to clip - for(j=path[0].i+1;jnum_flows;j++) { - skipped_end += fseq->base_calls[j]; - } - } - if(1 == s->strand) { // swap - k = skipped_start; - skipped_start = skipped_end; - skipped_end = k; - } - if(0 < skipped_start) { // start soft clip - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(1 + s->n_cigar), "s->cigar"); - for(l=s->n_cigar-1;0<=l;l--) { - s->cigar[l+1] = s->cigar[l]; - } - TMAP_SW_CIGAR_STORE(s->cigar[0], BAM_CSOFT_CLIP, skipped_start); - s->n_cigar++; - } - if(0 < skipped_end) { // end soft clip - s->cigar = tmap_realloc(s->cigar, sizeof(uint32_t)*(1 + s->n_cigar), "s->cigar"); - s->cigar[s->n_cigar] = (skipped_end << 4) | 4; - s->n_cigar++; - } - } - } - // free - free(target); - free(path); - free(flow_order); - free(key_seq); + xstart = x, ystart = y, slen = 0; + } + } +} - if(0 == was_int) { - tmap_seq_to_char(seq); - } - return 1; + +void tmap_map_log_text_align (const char* preceed, uint32_t* cigar, uint32_t n_cigar, const char* query, uint32_t query_len, uint32_t forward, const char* ref, uint32_t ref_off) +{ + const unsigned MAX_BATCHNO = 100; + AlBatch batches [MAX_BATCHNO]; + uint32_t q_clip = 0; + int bno = cigar_to_batches (cigar, n_cigar, &q_clip, batches, MAX_BATCHNO); + + uint32_t q_len_cigar, r_len_cigar; + seq_lens_from_bin_cigar (cigar, n_cigar, &q_len_cigar, &r_len_cigar); + + if (preceed) + tmap_log (preceed); + log_batches (query+q_clip, query_len - q_clip, !forward, (const char*) ref, r_len_cigar, 0, batches, bno, q_clip, ref_off); } diff --git a/Analysis/TMAP/src/map/util/tmap_map_util.h b/Analysis/TMAP/src/map/util/tmap_map_util.h index da61be7c..c0371538 100644 --- a/Analysis/TMAP/src/map/util/tmap_map_util.h +++ b/Analysis/TMAP/src/map/util/tmap_map_util.h @@ -4,9 +4,12 @@ #include #include "../../util/tmap_rand.h" +#include "../../sw/tmap_sw.h" #include "../../sw/tmap_fsw.h" #include "../../sw/tmap_vsw.h" #include "tmap_map_opt.h" +#include "tmap_map_stats.h" + #define __map_util_gen_ap(par, opt) do { \ int32_t i; \ @@ -90,8 +93,14 @@ typedef struct { int32_t score_subo; /*!< the alignment score of the sub-optimal hit */ int32_t n_cigar; /*!< the number of cigar operators */ uint32_t *cigar; /*!< the cigar operator array */ + int32_t n_orig_cigar; + uint32_t *orig_cigar; + uint32_t orig_pos; uint16_t target_len; /*!< internal variable, the target length estimated by the seeding step */ uint16_t n_seeds; /*!< the number seeds in this hit */ + uint16_t fivep_offset; /*!< number of additional ref bases aligned if 5' not softclipped */ + uint32_t mapper_pos; + uint32_t mapper_tlen; union { tmap_map_map1_aux_t *map1_aux; /*!< auxiliary data for map1 */ tmap_map_map2_aux_t *map2_aux; /*!< auxiliary data for map2 */ @@ -127,7 +136,7 @@ typedef struct { bam1_t **bams; /*!< the bam records */ int32_t n; /*!< the number of hits */ } tmap_map_bam_t; - + /*! The multi-end BAM record structure */ @@ -136,6 +145,33 @@ typedef struct { int32_t n; /*!< the number of records (multi-end) */ } tmap_map_bams_t; +/*! + Control structure for reference sequence buffer + */ +typedef struct { + uint8_t* buf; /*!< pointer to the address of the memory buffer for unpacked reference sequence */ + uint32_t buf_sz; /*!< pointer to the variable contining presently allocated size of target_buf */ + uint8_t* data; /*!< pointer into buffer where actual requested fragment starts */ + uint32_t data_len; /*!< length of last requested data */ + uint32_t seqid; /*!< sequence Id for the fragment stored in data member, 1-based */ + uint32_t seq_start; /*!< offset of the first base of fragment stored in buf, 1-based */ + uint32_t seq_end; /*!< offset of the last base stored in buf, 1-based */ +} ref_buf_t; + +/*! + populates the ref_buf_t so that the data + @param dest (ref_buf_t*) the reference cache control structure + @param refseq (tmap_refseq_t *) pointer to the the reference server control structure + @param seqid (uint32_t) sequence id to cache, 1-based, + @param seq_start (uint32_t) position of first base in a fragment to cache, 1-based + @param seq_end (uint32_t) poition of last base of a fragment to cache, 1-based + */ + +void target_cache_init (ref_buf_t* target); +void target_cache_free (ref_buf_t* target); +void cache_target (ref_buf_t* target, tmap_refseq_t *refseq, uint32_t seqid, uint32_t seq_start, uint32_t seq_end); + + /*! initializes @param s the mapping structure @@ -369,7 +405,8 @@ tmap_map_util_mapq(tmap_map_sams_t *sams, int32_t seq_len, tmap_map_opt_t *opt, @param num_after_grouping used to return the number seeds after grouping @return the locally aligned sams */ -tmap_map_sams_t * + +tmap_map_sams_t* tmap_map_util_sw_gen_score(tmap_refseq_t *refseq, tmap_seq_t *seq, tmap_map_sams_t *sams, @@ -378,6 +415,84 @@ tmap_map_util_sw_gen_score(tmap_refseq_t *refseq, tmap_map_opt_t *opt, int32_t *num_after_grouping); +void tmap_map_util_populate_sw_par ( + tmap_sw_param_t* par, + tmap_map_opt_t* opt +); + +/*! + find alignment starts for raw mappings + @details generates the cigar after tmap_map_util_sw_gen_score has been called + @param refseq the reference sequence + @param sams the seeded sams + @param seq the original query sequence + @param seqs the query sequence (forward, reverse compliment, reverse, and compliment) + @param opt the program parameters + @param target reference data + @param stat statistics + @return the locally aligned sams + */ + +tmap_map_sams_t* +tmap_map_util_find_align_starts ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // initial rough mapping + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_map_opt_t *opt, // tmap parameters + ref_buf_t* target, // reference data + tmap_map_stats_t *stat // statistics +); + +void tmap_map_util_align ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + ref_buf_t* target, // reference data + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_sw_param_t* par, // Smith-Waterman scopring parameters + tmap_map_stats_t *stat // statistics +); + +void tmap_map_util_salvage_edge_indels ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_map_opt_t *opt, // tmap parameters + tmap_sw_param_t* par, // Smith-Waterman scopring parameters + ref_buf_t* target, // reference data cache + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_map_stats_t *stat // statistics +); + +void tmap_map_util_cure_softclips ( + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t **seqs // array of size 4 that contains pre-computed inverse / complement combinations +); + +void tmap_map_util_trim_key ( + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_refseq_t *refseq, // reference server + ref_buf_t* target, // reference data cache + tmap_map_stats_t *stat // statistics +); + +void tmap_map_util_end_repair_bulk ( + tmap_refseq_t *refseq, // reference server + tmap_map_sams_t *sams, // mappings to compute alignments for + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + tmap_map_opt_t *opt, // tmap parameters + ref_buf_t* target, // reference data cache + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_map_stats_t *stat // statistics +); + /*! perform local alignment @details generates the cigar after tmap_map_util_sw_gen_score has been called @@ -386,14 +501,18 @@ tmap_map_util_sw_gen_score(tmap_refseq_t *refseq, @param seq the original query sequence @param seqs the query sequence (forward, reverse compliment, reverse, and compliment) @param opt the program parameters + @param stat statistics @return the locally aligned sams */ tmap_map_sams_t * -tmap_map_util_sw_gen_cigar(tmap_refseq_t *refseq, - tmap_map_sams_t *sams, - tmap_seq_t *seq, - tmap_seq_t **seqs, - tmap_map_opt_t *opt); +tmap_map_util_sw_gen_cigar ( + tmap_refseq_t *refseq, + tmap_map_sams_t *sams, + tmap_seq_t *seq, + tmap_seq_t **seqs, + tmap_map_opt_t *opt, + tmap_map_stats_t *stat +); /*! re-aligns mappings in flow space @@ -409,11 +528,80 @@ tmap_map_util_sw_gen_cigar(tmap_refseq_t *refseq, @param pen_gape the gap extension penalty @param fscore the flow penalty @param use_flowgram 1 to use the flowgram if available, 0 otherwise + @param stat tmap statistics @return 1 if successful, 0 otherwise */ int32_t -tmap_map_util_fsw(tmap_seq_t *seq, tmap_map_sams_t *sams, tmap_refseq_t *refseq, +tmap_map_util_fsw (tmap_seq_t *seq, tmap_map_sams_t *sams, tmap_refseq_t *refseq, int32_t bw, int32_t softclip_type, int32_t score_thr, int32_t score_match, int32_t pen_mm, int32_t pen_gapo, - int32_t pen_gape, int32_t fscore, int32_t use_flowgram); + int32_t pen_gape, int32_t fscore, int32_t use_flowgram, tmap_map_stats_t* stat); + + +// updates alignment box (result) from cigar, pos and target_len +void tmap_map_update_alignment_box (tmap_map_sam_t* sam); + + +/*! + turns softclip on 5' into non-clipped alignment + @param refseq the reference sequence + @param sams the mappings to adjust + @param seq the read + @param seqs the 'alternate forms' of query sequences (forward, reverse compliment, reverse, and compliment) + @param target reference data + @param path_buf buffer for traceback path + @param path_buf_sz used portion and allocated size of traceback path. + @param par Smith-Waterman scoring parameters + @param opt the program parameters + @param stat tmap statistics + @return 1 if 5-prime was found and replaced, 0 if there was no 5' softclip + */ + +int32_t +tmap_map_util_remove_5_prime_softclip +( + tmap_refseq_t *refseq, // reference server + tmap_map_sam_t *dest_sam, // mapping to fix + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + ref_buf_t* target, // reference data cache + tmap_sw_path_t** path_buf, // buffer for traceback path + int32_t* path_buf_sz, // used portion and allocated size of traceback path. + tmap_sw_param_t* par, // Smith-Waterman scoring parameters + tmap_map_opt_t *opt, // tmap options (for this stage) + tmap_map_stats_t *stat // statistics +); + +void +cigar_sanity_check +( + tmap_refseq_t *refseq, // reference server + tmap_map_sam_t *dest_sam, // mapping to fix + tmap_seq_t *seq, // read + tmap_seq_t **seqs, // array of size 4 that contains pre-computed inverse / complement combinations + ref_buf_t* target, // reference data cache + tmap_map_opt_t *opt // tmap options (for this stage) +); + +typedef struct +{ + unsigned xpos; + unsigned ypos; + unsigned len; +} +AlBatch; + +void cigar_log (const uint32_t* cigar, unsigned cigar_sz); +uint32_t cigar_to_batches (const uint32_t* cigar, uint32_t cigar_sz, uint32_t* x_clip, AlBatch* batches, uint32_t max_batches); +void log_batches (const char* xseq, unsigned xlen, uint8_t xrev, const char* yseq, unsigned ylen, uint8_t yrev, const AlBatch *b_ptr, int b_cnt, unsigned xoff, unsigned yoff); +void tmap_map_log_text_align ( + const char* preceed, + uint32_t* cigar, + uint32_t n_cigar, + const char* query, + uint32_t query_len, + uint32_t forward, + const char* ref, + uint32_t ref_off); + #endif // TMAP_MAP_UTIL_H diff --git a/Analysis/TMAP/src/realign/Realign.cpp b/Analysis/TMAP/src/realign/Realign.cpp index 1ea726d1..334cdd91 100644 --- a/Analysis/TMAP/src/realign/Realign.cpp +++ b/Analysis/TMAP/src/realign/Realign.cpp @@ -323,7 +323,7 @@ bool Realigner::computeSWalignment(vector& CigarData, vector unsigned int& start_pos_update) { // string dummy_string; - + // Compute boundaries for tubed alignment around previously found alignment if (!ComputeTubedAlignmentBoundaries()) return false; @@ -492,6 +492,8 @@ void Realigner::backtrackAlignment(unsigned int t_idx, unsigned int q_idx, int next_move = FROM_NOWHERE; int last_move = -1; + // DVK: softclip first / last deletes; + while (current_move != FROM_NOWHERE) { switch (current_move) { diff --git a/Analysis/TMAP/src/realign/cigar_op.h b/Analysis/TMAP/src/realign/cigar_op.h index 016ca1ac..be11dc52 100644 --- a/Analysis/TMAP/src/realign/cigar_op.h +++ b/Analysis/TMAP/src/realign/cigar_op.h @@ -2,17 +2,20 @@ #ifndef CIGAR_OP_H #define CIGAR_OP_H -struct CigarOp { - +struct CigarOp +{ + char Type; //!< CIGAR operation type (MIDNSHPX=) unsigned Length; //!< CIGAR operation length (number of bases) - + //! constructor - CigarOp(const char type = '\0', + CigarOp (const char type = '\0', const unsigned& length = 0) - : Type(type) - , Length(length) - { } + : + Type(type), + Length(length) + { + } }; diff --git a/Analysis/TMAP/src/realign/cigar_utils.cpp b/Analysis/TMAP/src/realign/cigar_utils.cpp index 4c78024e..7ecf453a 100644 --- a/Analysis/TMAP/src/realign/cigar_utils.cpp +++ b/Analysis/TMAP/src/realign/cigar_utils.cpp @@ -5,6 +5,7 @@ extern "C" { #include "../samtools/bam.h" #include "../util/tmap_error.h" +#include "../io/tmap_file.h" } #include @@ -56,7 +57,7 @@ const char* clip_seq (const char* qry, const uint32_t* cigar, unsigned cigar_sz, return qry + noclip_beg; } -unsigned roll_cigar (uint32_t* cigar, unsigned max_cigar_len, unsigned& cigar_len, const BATCH* batches, unsigned bno, unsigned clean_qry_len, EndClips& clip_store, unsigned& x_off, unsigned& y_off) +unsigned roll_cigar (uint32_t* cigar, unsigned max_cigar_len, unsigned& cigar_len, const BATCH* batches, unsigned bno, unsigned clean_qry_len, EndClips& clip_store, unsigned& x_off, unsigned& y_off, unsigned& xlen, unsigned& ylen) { // add beginning clips if any // x is 1st is query, y is 2nd is reference @@ -114,10 +115,16 @@ unsigned roll_cigar (uint32_t* cigar, unsigned max_cigar_len, unsigned& cigar_le } if (cur_x > clean_qry_len) { - std::cerr << "Aligned query portion extends over clipped query length\n"; + tmap_file_fprintf (tmap_file_stderr, "Aligned query portion extends over clipped query length\n"); tmap_bug (); } + xlen = cur_x - x_off; + ylen = cur_y - y_off; + + // shift x_off by the 5' soft clip + x_off += clip_store.soft_beg_; + // add ending clips if any if (clip_store.soft_end_ + (clean_qry_len - cur_x)) { diff --git a/Analysis/TMAP/src/realign/cigar_utils.h b/Analysis/TMAP/src/realign/cigar_utils.h index b5bf219b..3aa62a7b 100644 --- a/Analysis/TMAP/src/realign/cigar_utils.h +++ b/Analysis/TMAP/src/realign/cigar_utils.h @@ -38,7 +38,7 @@ const char* clip_seq (const char* qry, const uint32_t* cigar, unsigned cigar_sz, // starting insertion is added to soft clip // starting deletion is subtracted from starting insertion and result is returned as reference shift -unsigned roll_cigar (uint32_t* cigar, unsigned max_cigar_len, unsigned& cigar_len, const BATCH* batches, unsigned bno, unsigned clean_len, EndClips& clip_store, unsigned& x_off, unsigned& y_off); +unsigned roll_cigar (uint32_t* cigar, unsigned max_cigar_len, unsigned& cigar_len, const BATCH* batches, unsigned bno, unsigned clean_len, EndClips& clip_store, unsigned& x_off, unsigned& y_off, unsigned& xlen, unsigned& ylen); // conversion from cigar to batch format unsigned cigar_to_batches (const uint32_t* cigar, unsigned cigar_sz, BATCH* batches, unsigned max_batches); diff --git a/Analysis/TMAP/src/realign/contalign.cpp b/Analysis/TMAP/src/realign/contalign.cpp index 876f775c..f987d5a3 100644 --- a/Analysis/TMAP/src/realign/contalign.cpp +++ b/Analysis/TMAP/src/realign/contalign.cpp @@ -191,6 +191,7 @@ ap (NULL), xhomo (NULL), yhomo (NULL), logbuf_ (NULL), +own_log_ (NULL), logp_ (NULL) { } @@ -202,6 +203,8 @@ btrmx (NULL), ap (NULL), xhomo (NULL), yhomo (NULL), +logbuf_ (NULL), +own_log_ (NULL), logp_ (NULL) { init (max_ylen, max_xlen, max_size, gip, gep, mat, mis); @@ -413,7 +416,7 @@ double ContAlign::align_band (const char* xseq, int xlen, const char* yseq, int int y_start, y_end, y_len; //, y; char *bp_start; - double cur_w; + // double cur_w; bool bottom_in = false; double add_score; @@ -445,11 +448,12 @@ double ContAlign::align_band (const char* xseq, int xlen, const char* yseq, int if (len <= 0 || xpos + 1 == xlen || std::max (yref, ypos+1) >= std::min (ylen, ypos + 1 + bstep)) last_col = true; - cur_w = align_y_loop (ap + y_start, xseq [xpos], bp_start, xpos, xhomo [xpos - xref + 1], y_start, y_len, bottom_in, add_score, last_col); + align_y_loop (ap + y_start, xseq [xpos], bp_start, xpos, xhomo [xpos - xref + 1], y_start, y_len, bottom_in, add_score, last_col); // record max score at the top of those columns that end at the band edge - if (y_end == ylen && last_w <= cur_w) - last_w = cur_w, last_bp = bp_start + y_len - 1, last_x = xpos, last_y = y_end-1, last_reached = true; + // 02/28/2017: with the change of semantic of firstx / lastx, the lastx means alignment should terminate at the edge of X sequence. The code below terminates at Y edge => commented + // if (y_end == ylen && last_w <= cur_w) + // last_w = cur_w, last_bp = bp_start + y_len - 1, last_x = xpos, last_y = y_end-1, last_reached = true; xpos++, ypos++, bp += bstep; } @@ -578,6 +582,8 @@ unsigned ContAlign::backtrace (BATCH *b_ptr, int max_cnt, unsigned width) ++b_ptr; } // in to_first mode, if y is not fully covered, add pseudo segment and a gap + // 02/28/2017: seem to be not needed as only X coverage is now required for to_first + /* if (to_first && y >= yref) { assert (b_cnt < max_cnt); @@ -586,6 +592,7 @@ unsigned ContAlign::backtrace (BATCH *b_ptr, int max_cnt, unsigned width) b_ptr->len = 0; ++b_cnt; } + */ reverse_inplace (b_start, b_cnt); return b_cnt; } diff --git a/Analysis/TMAP/src/realign/contalign.h b/Analysis/TMAP/src/realign/contalign.h index b5c779d5..e736baee 100644 --- a/Analysis/TMAP/src/realign/contalign.h +++ b/Analysis/TMAP/src/realign/contalign.h @@ -83,6 +83,7 @@ class ContAlign // on a diagonal band (len, diag-width:diag+width_right) // returns maximum local alignment score // NOTE: batch xpos, ypos, len should be inside X and Y sequences, width > 0 + // 02/28/2017 : Changing semantics of tobeg / toend to only apply to X sequence (read in TMAP) double align_band (const char* xseq, int xlen, const char* yseq, int ylen, int xpos, int ypos, int len, int width, int width_right = -1, bool tobeg = false, bool toend = false); // checks if enough matrix space is present for alignment diff --git a/Analysis/TMAP/src/realign/realign_proxy.h b/Analysis/TMAP/src/realign/realign_proxy.h index 7f4574c8..a1a30628 100644 --- a/Analysis/TMAP/src/realign/realign_proxy.h +++ b/Analysis/TMAP/src/realign/realign_proxy.h @@ -37,7 +37,10 @@ class RealignProxy unsigned cigar_sz, uint32_t*& cigar_dest, unsigned& cigar_dest_sz, - int& new_r_pos, + unsigned& new_r_pos, + unsigned& new_q_pos, + unsigned& new_r_len, + unsigned& new_q_len, bool& already_perfect, bool& clip_failed, bool& alignment_failed, diff --git a/Analysis/TMAP/src/realign/realign_wrapper.cpp b/Analysis/TMAP/src/realign/realign_wrapper.cpp index 058ae70a..1d54763b 100644 --- a/Analysis/TMAP/src/realign/realign_wrapper.cpp +++ b/Analysis/TMAP/src/realign/realign_wrapper.cpp @@ -79,7 +79,10 @@ uint8_t realigner_compute_alignment (RealignProxy* r, unsigned cigar_sz, uint32_t** cigar_dest, unsigned* cigar_dest_sz, - int* new_r_pos, + unsigned* new_r_pos, + unsigned* new_q_pos, + unsigned* new_r_len, + unsigned* new_q_len, uint64_t* num_realign_already_perfect, uint64_t* num_realign_not_clipped, uint64_t* num_realign_sw_failures, @@ -90,7 +93,7 @@ uint8_t realigner_compute_alignment (RealignProxy* r, bool alignment_failed = false; bool unclip_failed = false; - bool result = r->compute_alignment (q_seq, q_len, r_seq, r_len, r_pos, (bool) forward, cigar, cigar_sz, *cigar_dest, *cigar_dest_sz, *new_r_pos, already_perfect, clip_failed, alignment_failed, unclip_failed); + bool result = r->compute_alignment (q_seq, q_len, r_seq, r_len, r_pos, (bool) forward, cigar, cigar_sz, *cigar_dest, *cigar_dest_sz, *new_r_pos, *new_q_pos, *new_r_len, *new_q_len, already_perfect, clip_failed, alignment_failed, unclip_failed); if (num_realign_already_perfect && already_perfect) ++(*num_realign_already_perfect); if (num_realign_not_clipped && clip_failed) ++(*num_realign_not_clipped); diff --git a/Analysis/TMAP/src/realign/realign_wrapper.h b/Analysis/TMAP/src/realign/realign_wrapper.h index df793dfb..22723877 100644 --- a/Analysis/TMAP/src/realign/realign_wrapper.h +++ b/Analysis/TMAP/src/realign/realign_wrapper.h @@ -45,7 +45,10 @@ uint8_t realigner_compute_alignment ( unsigned cigar_sz, uint32_t** cigar_dest, unsigned* cigar_dest_sz, - int* new_r_pos, + unsigned* new_r_pos, + unsigned* new_q_pos, + unsigned* new_r_len, + unsigned* new_q_len, uint64_t* num_realign_already_perfect, uint64_t* num_realign_not_clipped, uint64_t* num_realign_sw_failures, diff --git a/Analysis/TMAP/src/realign/realign_wrapper_context_imp.cpp b/Analysis/TMAP/src/realign/realign_wrapper_context_imp.cpp index 94c00099..cd65e4cd 100644 --- a/Analysis/TMAP/src/realign/realign_wrapper_context_imp.cpp +++ b/Analysis/TMAP/src/realign/realign_wrapper_context_imp.cpp @@ -50,7 +50,7 @@ char* ContAlignImp::qry_buf (unsigned len) return qry_buf_; qry_buf_len_ = len_roundup (len); if (qry_buf_) - delete qry_buf_; + delete [] qry_buf_; // TODO: memory allocation error handling qry_buf_ = new char [qry_buf_len_]; return qry_buf_; @@ -64,7 +64,7 @@ char* ContAlignImp::ref_buf (unsigned len) return ref_buf_; ref_buf_len_ = len_roundup (len); if (ref_buf_) - delete ref_buf_; + delete [] ref_buf_; // TODO: memory allocation error handling ref_buf_ = new char [ref_buf_len_]; return ref_buf_; @@ -126,7 +126,10 @@ bool ContAlignImp::compute_alignment ( unsigned cigar_sz, uint32_t*& cigar_dest, unsigned& cigar_dest_sz, - int& new_pos, + unsigned& new_ref_pos, + unsigned& new_qry_pos, + unsigned& new_ref_len, + unsigned& new_qry_len, bool& already_perfect, bool& clip_failed, bool& alignment_failed, @@ -192,10 +195,10 @@ bool ContAlignImp::compute_alignment ( ref_ins + extra_bandwidth_ // width ); // convert alignment to cigar - unsigned qry_off, ref_off; + unsigned ref_off; // int ref_shift = - roll_cigar (new_cigar_, MAX_CIGAR_SZ, cigar_dest_sz, batches_, bno, clean_len, clips, qry_off, ref_off); - new_pos = r_pos + ref_off; + roll_cigar (new_cigar_, MAX_CIGAR_SZ, cigar_dest_sz, batches_, bno, clean_len, clips, new_qry_pos, ref_off, new_qry_len, new_ref_len); + new_ref_pos = r_pos + ref_off; cigar_dest = new_cigar_; return true; diff --git a/Analysis/TMAP/src/realign/realign_wrapper_context_imp.h b/Analysis/TMAP/src/realign/realign_wrapper_context_imp.h index 23d3c666..c82978fd 100644 --- a/Analysis/TMAP/src/realign/realign_wrapper_context_imp.h +++ b/Analysis/TMAP/src/realign/realign_wrapper_context_imp.h @@ -52,7 +52,10 @@ class ContAlignImp : public RealignProxy, protected ContAlign unsigned cigar_sz, uint32_t*& cigar_dest, unsigned& cigar_dest_sz, - int& new_pos, + unsigned& new_ref_pos, + unsigned& new_qry_pos, + unsigned& new_ref_len, + unsigned& new_qry_len, bool& already_perfect, bool& clip_failed, bool& alignment_failed, diff --git a/Analysis/TMAP/src/realign/realign_wrapper_imp.cpp b/Analysis/TMAP/src/realign/realign_wrapper_imp.cpp index 3c6de09e..30b1ccb9 100644 --- a/Analysis/TMAP/src/realign/realign_wrapper_imp.cpp +++ b/Analysis/TMAP/src/realign/realign_wrapper_imp.cpp @@ -128,7 +128,10 @@ bool RealignImp::compute_alignment ( unsigned cigar_sz, uint32_t*& cigar_dest, unsigned& cigar_dest_sz, - int& new_pos, + unsigned& new_ref_pos, + unsigned& new_qry_pos, + unsigned& new_ref_len, + unsigned& new_qry_len, bool& already_perfect, bool& clip_failed, bool& alignment_failed, @@ -193,13 +196,13 @@ bool RealignImp::compute_alignment ( vector new_cigar_vec; unsigned int start_pos_shift; - if (!computeSWalignment(new_cigar_vec, new_md_vec, start_pos_shift)) + if (!computeSWalignment (new_cigar_vec, new_md_vec, start_pos_shift)) { alignment_failed = true; return false; } - if (!addClippedBasesToTags(new_cigar_vec, new_md_vec, q_len)) + if (!addClippedBasesToTags (new_cigar_vec, new_md_vec, q_len)) { unclip_failed = true; return false; // error adding back clipped out zones @@ -209,12 +212,23 @@ bool RealignImp::compute_alignment ( { // build cigar data only if it is needed // TODO avoid automatic vectors to prevent unneeded heap usage - std::vector cigar_vec; + CigarVec cigar_vec; cigar_vector_from_bin (cigar, cigar_sz, cigar_vec); - new_pos = updateReadPosition (cigar_vec, start_pos_shift, r_pos); + new_ref_pos = updateReadPosition (cigar_vec, start_pos_shift, r_pos); } else - new_pos = r_pos; + new_ref_pos = r_pos; + + // here we need to adjust for the possible weirdness of Realigner: + // it may produce alignments starting / ending with indels. + // They are to be converted to softclips or to ref shifts. + // Namely: + // insertions at the start and at the end should be added to softclips + // deletions at the starts should be added to new_ref_pos + // deletions at the end should be subtracted from new_ref_len + // we do processing here in order to avoid touching code inherited from Bamrealignment (can be re-considered later) + // the + adjust_cigar (new_cigar_vec, new_ref_pos, new_ref_pos, new_qry_pos, new_ref_len, new_qry_len); // free (cigar_dest); // TODO: switch to better alignment memory management, avoid heap operations @@ -226,5 +240,101 @@ bool RealignImp::compute_alignment ( } +void adjust_cigar (CigarVec& src, unsigned start_pos_shift, unsigned& new_ref_pos, unsigned& new_qry_pos, unsigned& new_ref_len, unsigned& new_qry_len) +{ + // walk from front to first aligned pair of bases. Count softclips and Is as softclip, Ds as reference shifts + // it is safe to reset new_ref_pos since even if same var is passed as start_pos_shif and new_ref_pos, the former is copied on stack. + new_ref_pos = new_qry_pos = new_ref_len = new_qry_len = 0; + if (!src.size ()) + { + new_ref_pos = start_pos_shift; + return; + } + // walks toward the end to compute ref and lengths + bool in_prefix = true; + unsigned last_prefix_idx = 0; + for (CigarVec::iterator ci = src.begin (); ci != src.end (); ++ci) + { + switch (ci->Type) + { + case 'M': // aligned + case 'X': // mismatch + case '=': // match + // both ref and qry consumed + new_ref_len += ci->Length, new_qry_len += ci->Length; + in_prefix = false; // stops accumulating left flank + break; + case 'I': // insert + case 'S': // softclip + // qry consumed, ref not + if (in_prefix) + new_qry_pos += ci->Length, ++last_prefix_idx; + else + new_qry_len += ci->Length; + break; + case 'D': // delete + case 'N': // refskip + // ref consumed, qry not + if (in_prefix) + new_ref_pos += ci->Length, ++last_prefix_idx; + else + new_ref_len += ci->Length; + break; + default: // hardclip and pad + // none consumed + if (in_prefix) + ++last_prefix_idx; + break; + } + } + // now figure out the suffix - walk backward + bool in_suffix = true; + unsigned first_suffix_idx = src.size (); + for (CigarVec::reverse_iterator ri = src.rbegin (); in_suffix && ri != src.rend (); ++ri) + { + switch (ri->Type) + { + case 'M': // aligned + case 'X': // mismatch + case '=': // match + // both ref and qry consumed + in_suffix = false; // stops accumulating left flank + break; + case 'I': // insert + case 'S': // softclip + // qry consumed, ref not + assert (new_qry_len >= ri->Length); + assert (first_suffix_idx); + new_qry_len -= ri->Length; + --first_suffix_idx; + break; + case 'D': // delete + case 'N': // refskip + // ref consumed, qry not + assert (new_ref_len >= ri->Length); + assert (first_suffix_idx); + new_ref_len -= ri->Length; + --first_suffix_idx; + break; + default: // hardclip and pad + // none consumed + assert (first_suffix_idx); + --first_suffix_idx; + break; + } + } + assert (last_prefix_idx <= first_suffix_idx); + // take the suffix out + src.resize (first_suffix_idx); + // replace prefix with just one softclip (assuming no hard clip encountered - TMAP does not produce one + if (last_prefix_idx) + { + src.erase (src.begin (), src.begin () + (last_prefix_idx - 1)); // leave one position + src.front ().Type = 'S'; + src.front ().Length = new_qry_pos; + } + // add prior start offset + new_ref_pos += start_pos_shift; +} diff --git a/Analysis/TMAP/src/realign/realign_wrapper_imp.h b/Analysis/TMAP/src/realign/realign_wrapper_imp.h index f42e37d6..76bbbe01 100644 --- a/Analysis/TMAP/src/realign/realign_wrapper_imp.h +++ b/Analysis/TMAP/src/realign/realign_wrapper_imp.h @@ -5,8 +5,15 @@ #include "realign_proxy.h" #include "Realign.h" +typedef std::vector CigarVec; +// compensate for cigar discrepancies (in/dels at the alignment edges) +// that is possible result of realignment +// (kind of a hack, implemented in outer layer to avoid meessing with original Realign code from bamrealignment +void adjust_cigar (CigarVec& src, unsigned start_pos_shift, unsigned& new_ref_pos, unsigned& new_qry_pos, unsigned& new_ref_len, unsigned& new_qry_len); + class RealignImp : public RealignProxy, protected Realigner { +private: CLIPTYPE cliptype_; // buffers for reference and inverse query sequences @@ -21,6 +28,9 @@ class RealignImp : public RealignProxy, protected Realigner unsigned len_roundup (unsigned len); + + + protected: RealignImp (); RealignImp (unsigned reserve_size, unsigned clipping_size); @@ -50,7 +60,10 @@ class RealignImp : public RealignProxy, protected Realigner unsigned cigar_sz, uint32_t*& cigar_dest, unsigned& cigar_dest_sz, - int& new_pos, + unsigned& new_ref_pos, + unsigned& new_qry_pos, + unsigned& new_ref_len, + unsigned& new_qry_len, bool& already_perfect, bool& clip_failed, bool& alignment_failed, diff --git a/Analysis/TMAP/src/sw/tmap_fsw.c b/Analysis/TMAP/src/sw/tmap_fsw.c index 60cc3228..7170cddd 100644 --- a/Analysis/TMAP/src/sw/tmap_fsw.c +++ b/Analysis/TMAP/src/sw/tmap_fsw.c @@ -942,7 +942,7 @@ tmap_fsw_path2cigar(const tmap_fsw_path_t *path, int32_t path_len, int32_t *n_ci } *n_cigar = n; cigar = tmap_malloc(*n_cigar * 4, "cigar"); - + // get the last type dpscore_last_type = tmap_fsw_path2cigar_get_type(path[path_len-1].ctype); cigar[0] = 1u << 4 | dpscore_last_type; diff --git a/Analysis/TMAP/src/sw/tmap_sw.h b/Analysis/TMAP/src/sw/tmap_sw.h index 4a604a84..832901f2 100644 --- a/Analysis/TMAP/src/sw/tmap_sw.h +++ b/Analysis/TMAP/src/sw/tmap_sw.h @@ -110,7 +110,7 @@ typedef struct int32_t i; /*!< the seq1 index (1-based) */ int32_t j; /*!< the seq2 index (1-based) */ uint8_t ctype; /*!< the edit operator applied */ -} tmap_sw_path_t; +} tmap_sw_path_t; /*! Stores a Smith Waterman alignment. @@ -313,6 +313,7 @@ uint32_t * tmap_sw_path2cigar(const tmap_sw_path_t *path, int32_t path_len, int32_t *n_cigar); + /******************** * global variables * ********************/ diff --git a/Analysis/TMAP/src/util/tmap_definitions.c b/Analysis/TMAP/src/util/tmap_definitions.c index ab0ad06d..6d43e212 100644 --- a/Analysis/TMAP/src/util/tmap_definitions.c +++ b/Analysis/TMAP/src/util/tmap_definitions.c @@ -615,10 +615,10 @@ tmap_version(int argc, char *argv[]) #ifdef GIT_REV fprintf(stderr, "%sVersion: %s%s%s %sgit:%s%s%s\n", KRED, KWHT, PACKAGE_VERSION, KNRM, KRED, KWHT, GIT_REV, KNRM); #else - fprintf(stderr, "%sVersion: %s%s%s\n", KRED, KWHT, PACKAGE_VERSION, KNRM); + fprintf(stderr, "%sVersion: %s%s%s\n\n", KRED, KWHT, PACKAGE_VERSION, KNRM); #endif //fprintf(stderr, "%sWeb Site: %s%s%s\n", KRED, KWHT, "http://github.com/iontorrent/tmap", KNRM); - fprintf(stderr, "%sContact: %s%s%s\n\n", KRED, KWHT, PACKAGE_BUGREPORT, KNRM); + //fprintf(stderr, "%sContact: %s%s%s\n\n", KRED, KWHT, PACKAGE_BUGREPORT, KNRM); return 0; } diff --git a/Analysis/TMAP/src/util/tmap_error.c b/Analysis/TMAP/src/util/tmap_error.c index bf995d19..2ee25689 100644 --- a/Analysis/TMAP/src/util/tmap_error.c +++ b/Analysis/TMAP/src/util/tmap_error.c @@ -4,6 +4,25 @@ #include #include #include "tmap_error.h" +#include + +#ifdef HAVE_LIBPTHREAD +#include +static pthread_mutex_t err_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static void elock () +{ +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock (&err_mutex); +#endif +} +static void eunlock () +{ +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock (&err_mutex); +#endif +} static char error_string[][64] = { @@ -110,3 +129,33 @@ tmap_error_full(const char *file, const unsigned int line, const char *function_ break; } } + + +void +tmap_warn (const char* src_fname, const char* src_func, int src_lno, const char *fmt, ...) +{ + elock (); + va_list ap; + va_start (ap, fmt); + fprintf (stderr, "Warning: "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n (In file: %s, function: %s, line %d)\n", src_fname, src_func, src_lno); + va_end (ap); + eunlock (); +} + +void +tmap_fail (int quit, const char* src_fname, const char* src_func, int src_lno, const char *fmt, ...) +{ + elock (); + va_list ap; + va_start(ap, fmt); + fprintf (stderr, "Error: "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n (In file: %s, function: %s, line %d)\n", src_fname, src_func, src_lno); + va_end(ap); + eunlock (); + if (quit) + exit(EXIT_FAILURE); +} + diff --git a/Analysis/TMAP/src/util/tmap_error.h b/Analysis/TMAP/src/util/tmap_error.h index b33fbba9..44812d1b 100644 --- a/Analysis/TMAP/src/util/tmap_error.h +++ b/Analysis/TMAP/src/util/tmap_error.h @@ -102,6 +102,24 @@ tmap_error_cmd_check_int64(int64_t val, int64_t lower, int64_t upper, char *opti @param error_type the error type */ void -tmap_error_full(const char *file, const unsigned int line, const char *function_name, const char *variable_name, int action_type, int error_type); +tmap_error_full (const char *file, const unsigned int line, const char *function_name, const char *variable_name, int action_type, int error_type); + + + +void +tmap_fail (int fail, const char* fname, const char* func_name, int lineno, const char *fmt, ...); + +void +tmap_warn (const char* fname, const char* func_name, int lineno, const char *fmt, ...); + + +#define tmap_conderr(quit, fmt, args...) \ + tmap_fail (quit, __FILE__, __func__, __LINE__, fmt, args) + +#define tmap_failure(fmt, args...) \ + tmap_fail (1, __FILE__, __func__, __LINE__, fmt, args) + +#define tmap_warning(fmt, args...) \ + tmap_warn (__FILE__, __func__, __LINE__, fmt, args) #endif // TMAP_ERROR_H diff --git a/Analysis/TsInputUtil/perChipJsonEditor.cpp b/Analysis/TsInputUtil/perChipJsonEditor.cpp new file mode 100644 index 00000000..2895feec --- /dev/null +++ b/Analysis/TsInputUtil/perChipJsonEditor.cpp @@ -0,0 +1,1098 @@ +/* Copyright (C) 2012 Ion Torrent Systems, Inc. All Rights Reserved */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json/json.h" + + +#define PER_CHIP_JSON_EDITOR_VERSION "1.0.0" +#define WAIT_TIME 60 + +using namespace std; +using namespace Json; + +enum OptType { + OT_BOOL = 0, + OT_INT, + OT_DOUBLE, + OT_STRING, + OT_VECTOR_INT, + OT_VECTOR_DOUBLE, + OT_UNKOWN +}; + +void saveJson(const Json::Value & json, const string& filename_json) +{ + ofstream out(filename_json.c_str(), ios::out); + if (out.good()) + { + out << json.toStyledString(); + } + else + { + cout << "tsinpututil ERROR: unable to write JSON file " << filename_json << endl; + } + + out.close(); +} + +void usageMain() +{ + cerr << "perchipjsoneditor - edit per chip json files" << endl; + cerr << "Usage: " << endl + << " perchipjsoneditor [perChipJsonDirectory]" << endl + << " operation:" << endl + << " create" << endl + << " edit" << endl + << " validate" << endl + << " diff" < mapOptType; + mapOptType["acqPrefix"] = OT_STRING; + mapOptType["always-start-slow"] = OT_BOOL; + mapOptType["analysis-region"] = OT_VECTOR_INT; + mapOptType["barcode-debug"] = OT_BOOL; + mapOptType["barcode-flag"] = OT_BOOL; + mapOptType["barcode-penalty"] = OT_DOUBLE; + mapOptType["barcode-radius"] = OT_DOUBLE; + mapOptType["barcode-spec-file"] = OT_STRING; + mapOptType["barcode-tie"] = OT_DOUBLE; + mapOptType["beadfind-basis"] = OT_STRING; + mapOptType["beadfind-bfmult"] = OT_DOUBLE; + mapOptType["beadfind-bgdat"] = OT_STRING; + mapOptType["beadfind-blob-filter"] = OT_BOOL; + mapOptType["beadfind-dat"] = OT_STRING; + mapOptType["beadfind-diagnostics"] = OT_INT; + mapOptType["beadfind-filt-noisy-col"] = OT_STRING; + mapOptType["beadfind-gain-correction"] = OT_BOOL; + mapOptType["beadfind-lib-filt"] = OT_DOUBLE; + mapOptType["beadfind-lib-min-peak"] = OT_DOUBLE; + mapOptType["beadfind-mesh-step"] = OT_VECTOR_INT; + mapOptType["beadfind-min-tf-snr"] = OT_DOUBLE; + mapOptType["beadfind-minlive"] = OT_DOUBLE; + mapOptType["beadfind-minlivesnr"] = OT_DOUBLE; + mapOptType["beadfind-num-threads"] = OT_INT; + mapOptType["beadfind-predict-end"] = OT_INT; + mapOptType["beadfind-predict-start"] = OT_INT; + mapOptType["beadfind-sdasbf"] = OT_BOOL; + mapOptType["beadfind-sep-ref"] = OT_BOOL; + mapOptType["beadfind-sig-ref-type"] = OT_INT; + mapOptType["beadfind-skip-sd-recover"] = OT_INT; + mapOptType["beadfind-smooth-trace"] = OT_BOOL; + mapOptType["beadfind-tf-filt"] = OT_DOUBLE; + mapOptType["beadfind-tf-min-peak"] = OT_DOUBLE; + mapOptType["beadfind-type"] = OT_STRING; + mapOptType["beadfind-zero-flows"] = OT_STRING; + mapOptType["beadfindfile"] = OT_STRING; + mapOptType["beadmask-categorized"] = OT_BOOL; + mapOptType["bfold"] = OT_BOOL; + mapOptType["bfonly"] = OT_BOOL; + mapOptType["bkg-ampl-lower-limit"] = OT_DOUBLE; + mapOptType["bkg-bfmask-update"] = OT_BOOL; + mapOptType["bkg-copy-stringency"] = OT_DOUBLE; + mapOptType["bkg-dbg-trace"] = OT_VECTOR_INT; + mapOptType["bkg-debug-files"] = OT_BOOL; + mapOptType["bkg-debug-nsamples"] = OT_INT; + mapOptType["bkg-debug-param"] = OT_INT; + mapOptType["bkg-debug-region"] = OT_VECTOR_INT; + mapOptType["bkg-debug-trace-rcflow"] = OT_STRING; + mapOptType["bkg-debug-trace-sse"] = OT_STRING; + mapOptType["bkg-debug-trace-xyflow"] = OT_STRING; + mapOptType["bkg-dont-emphasize-by-compression"] = OT_BOOL; + mapOptType["bkg-empty-well-normalization"] = OT_BOOL; + mapOptType["bkg-exp-tail-bkg-adj"] = OT_BOOL; + mapOptType["bkg-exp-tail-bkg-limit"] = OT_DOUBLE; + mapOptType["bkg-exp-tail-bkg-lower"] = OT_DOUBLE; + mapOptType["bkg-exp-tail-fit"] = OT_BOOL; + mapOptType["bkg-exp-tail-tau-adj"] = OT_BOOL; + mapOptType["bkg-kmult-adj-low-hi"] = OT_DOUBLE; + mapOptType["bkg-max-rank-beads"] = OT_INT; + mapOptType["bkg-min-sampled-beads"] = OT_INT; + mapOptType["bkg-pca-dark-matter"] = OT_BOOL; + mapOptType["bkg-per-flow-time-tracking"] = OT_BOOL; + mapOptType["bkg-post-key-step"] = OT_INT; + mapOptType["bkg-post-key-train"] = OT_INT; + mapOptType["bkg-prefilter-beads"] = OT_BOOL; + mapOptType["bkg-recompress-tail-raw-trace"] = OT_BOOL; + mapOptType["bkg-single-gauss-newton"] = OT_BOOL; + mapOptType["bkg-use-duds"] = OT_BOOL; + mapOptType["bkg-use-proton-well-correction"] = OT_BOOL; + mapOptType["bkg-washout-flow-detection"] = OT_INT; + mapOptType["bkg-washout-threshold"] = OT_DOUBLE; + mapOptType["bkg-well-xtalk-name"] = OT_STRING; + mapOptType["clonal-filter-bkgmodel"] = OT_BOOL; + mapOptType["clonal-filter-debug"] = OT_BOOL; + mapOptType["clonal-filter-use-last-iter-params"] = OT_BOOL; + mapOptType["col-doubles-xtalk-correct"] = OT_BOOL; + mapOptType["col-flicker-correct"] = OT_BOOL; + mapOptType["col-flicker-correct-aggressive"] = OT_BOOL; + mapOptType["col-flicker-correct-verbose"] = OT_BOOL; + mapOptType["corr-noise-correct"] = OT_BOOL; + mapOptType["cropped"] = OT_VECTOR_INT; + mapOptType["cropped-region-origin"] = OT_VECTOR_INT; + mapOptType["dark-matter-correction"] = OT_BOOL; + mapOptType["dat-postfix"] = OT_STRING; + mapOptType["dat-source-directory"] = OT_STRING; + mapOptType["datacollect-gain-correction"] = OT_BOOL; + mapOptType["debug-bead-only"] = OT_BOOL; + mapOptType["double-tap-means-zero"] = OT_BOOL; + mapOptType["explog-path"] = OT_STRING; + mapOptType["filter-extreme-ppf-only"] = OT_BOOL; + mapOptType["fit-region-kmult"] = OT_BOOL; + mapOptType["fitting-taue"] = OT_BOOL; + mapOptType["flow-order"] = OT_STRING; + mapOptType["flowlimit"] = OT_INT; + mapOptType["flowtimeoffset"] = OT_INT; + mapOptType["fluid-potential-correct"] = OT_BOOL; + mapOptType["fluid-potential-threshold"] = OT_DOUBLE; + mapOptType["forcenn"] = OT_INT; + mapOptType["frames"] = OT_INT; + mapOptType["from-beadfind"] = OT_BOOL; + mapOptType["gopt"] = OT_STRING; + mapOptType["gpu-amp-guess"] = OT_INT; + mapOptType["gpu-device-ids"] = OT_INT; + mapOptType["gpu-fitting-only"] = OT_BOOL; + mapOptType["gpu-flow-by-flow"] = OT_BOOL; + mapOptType["gpu-force-multi-flow-fit"] = OT_BOOL; + mapOptType["gpu-hybrid-fit-iter"] = OT_INT; + mapOptType["gpu-memory-per-proc"] = OT_INT; + mapOptType["gpu-multi-flow-fit"] = OT_INT; + mapOptType["gpu-multi-flow-fit-blocksize"] = OT_INT; + mapOptType["gpu-multi-flow-fit-l1config"] = OT_INT; + mapOptType["gpu-num-history-flows"] = OT_INT; + mapOptType["gpu-num-streams"] = OT_INT; + mapOptType["gpu-partial-deriv-blocksize"] = OT_INT; + mapOptType["gpu-partial-deriv-l1config"] = OT_INT; + mapOptType["gpu-single-flow-fit"] = OT_INT; + mapOptType["gpu-single-flow-fit-blocksize"] = OT_INT; + mapOptType["gpu-single-flow-fit-l1config"] = OT_INT; + mapOptType["gpu-single-flow-fit-type"] = OT_INT; + mapOptType["gpu-switch-to-flow-by-flow-at"] = OT_INT; + mapOptType["gpu-use-all-devices"] = OT_BOOL; + mapOptType["gpu-verbose"] = OT_BOOL; + mapOptType["gpuworkload"] = OT_DOUBLE; + mapOptType["hilowfilter"] = OT_BOOL; + mapOptType["ignore-checksum-errors"] = OT_BOOL; + mapOptType["ignore-checksum-errors-1frame"] = OT_BOOL; + mapOptType["img-gain-correct"] = OT_BOOL; + mapOptType["incorporation-type"] = OT_INT; + mapOptType["kmult-hi-limit"] = OT_DOUBLE; + mapOptType["kmult-low-limit"] = OT_DOUBLE; + mapOptType["kmult-penalty"] = OT_DOUBLE; + mapOptType["librarykey"] = OT_STRING; + mapOptType["limit-rdr-fit"] = OT_BOOL; + mapOptType["local-wells-file"] = OT_BOOL; + mapOptType["mask-datacollect-exclude-regions"] = OT_BOOL; + mapOptType["max-iterations"] = OT_INT; + mapOptType["mixed-first-flow"] = OT_INT; + mapOptType["mixed-last-flow"] = OT_INT; + mapOptType["mixed-model-option"] = OT_INT; + mapOptType["mixed-stringency"] = OT_DOUBLE; + mapOptType["n-unfiltered-lib"] = OT_INT; + mapOptType["nn-subtract-empties"] = OT_BOOL; + mapOptType["nnmask"] = OT_VECTOR_INT; + mapOptType["nnmaskwh"] = OT_VECTOR_INT; + mapOptType["no-subdir"] = OT_BOOL; + mapOptType["no-threaded-file-access"] = OT_BOOL; + mapOptType["noduds"] = OT_BOOL; + mapOptType["nokey"] = OT_BOOL; + mapOptType["nuc-correct"] = OT_INT; + mapOptType["num-regional-samples"] = OT_INT; + mapOptType["numcputhreads"] = OT_INT; + mapOptType["output-dir"] = OT_STRING; + mapOptType["output-pinned-wells"] = OT_BOOL; + mapOptType["pair-xtalk-coeff"] = OT_DOUBLE; + mapOptType["pass-tau"] = OT_BOOL; + mapOptType["pca-test"] = OT_STRING; + mapOptType["post-fit-handshake-worker"] = OT_BOOL; + mapOptType["readaheaddat"] = OT_INT; + mapOptType["region-list"] = OT_VECTOR_INT; + mapOptType["region-size"] = OT_VECTOR_INT; + mapOptType["region-vfrc-debug"] = OT_BOOL; + mapOptType["regional-sampling"] = OT_BOOL; + mapOptType["regional-sampling-type"] = OT_INT; + mapOptType["restart-check"] = OT_BOOL; + mapOptType["restart-from"] = OT_STRING; + mapOptType["restart-next"] = OT_STRING; + mapOptType["restart-region-params-file"] = OT_STRING; + mapOptType["revert-regional-sampling"] = OT_BOOL; + mapOptType["sigproc-compute-flow"] = OT_STRING; + mapOptType["sigproc-regional-smoothing-alpha"] = OT_DOUBLE; + mapOptType["sigproc-regional-smoothing-gamma"] = OT_DOUBLE; + mapOptType["skip-first-flow-block-regional-fitting"] = OT_BOOL; + mapOptType["smoothing"] = OT_STRING; + mapOptType["smoothing-file"] = OT_STRING; + mapOptType["stack-dump-file"] = OT_STRING; + mapOptType["start-flow-plus-interval"] = OT_VECTOR_INT; + mapOptType["stop-beads"] = OT_BOOL; + mapOptType["suppress-copydrift"] = OT_BOOL; + mapOptType["tfkey"] = OT_STRING; + mapOptType["total-timeout"] = OT_INT; + mapOptType["trim-ref-trace"] = OT_STRING; + mapOptType["use-alternative-etbr-equation"] = OT_BOOL; + mapOptType["use-beadmask"] = OT_STRING; + mapOptType["exclusion-mask"] = OT_STRING; + mapOptType["use-pinned"] = OT_BOOL; + mapOptType["use-safe-buffer-model"] = OT_BOOL; + mapOptType["vectorize"] = OT_BOOL; + mapOptType["well-stat-file"] = OT_STRING; + mapOptType["wells-compression"] = OT_INT; + mapOptType["wells-convert-high"] = OT_DOUBLE; + mapOptType["wells-convert-low"] = OT_DOUBLE; + mapOptType["wells-convert-with-copies"] = OT_BOOL; + mapOptType["wells-format"] = OT_STRING; + mapOptType["wells-save-as-ushort"] = OT_BOOL; + mapOptType["wells-save-flow"] = OT_INT; + mapOptType["wells-save-freq"] = OT_INT; + mapOptType["wells-save-number-copies"] = OT_BOOL; + mapOptType["wells-save-queue-size"] = OT_INT; + mapOptType["xtalk"] = OT_STRING; + mapOptType["xtalk-correction"] = OT_BOOL; + + if(argc == 2) + { + string option = argv[1]; + if("-h" == option) + { + usageMain(); + } + else if("-v" == option) + { + cerr << "perchipjsoneditor version: " << PER_CHIP_JSON_EDITOR_VERSION << endl; + usageMain(); + } + } + + string jsonDir("../config"); + if(argc > 1) + { + jsonDir = argv[1]; + } + + struct stat sb; + if(!(stat(jsonDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))) + { + cerr << "ERROR: " << jsonDir << " does not exist or it is not a directory. Please provide the correct directory where per chip json files locate." << endl; + cout << "Please provide correct per chip json file directory (or type q to quit): "; + cin >> jsonDir; + + if(jsonDir == "q") + { + cout << "Bye." << endl; + exit(0); + } + + if(!(stat(jsonDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))) + { + cerr << "ERROR: " << jsonDir << " does not exist or it is not a directory. Please re-run the program with correct directory where per chip json files locate." << endl; + exit ( EXIT_FAILURE ); + } + } + + vector vecChipType; + map mapChipType; + + string cmdline("ls "); + cmdline += jsonDir; + cmdline += "/args_*.json > ./tmpFileList.txt"; + int ret = system(cmdline.c_str()); + if(ret != 0) + { + cerr << "ERROR: There is no per chip json file at " << jsonDir << endl; + exit ( EXIT_FAILURE ); + } + + char buf[2048]; + ifstream ifs("./tmpFileList.txt"); + while(ifs.getline(buf, 2048)) + { + string chipType = buf; + int index = chipType.rfind("/"); + if(index >= 0) + { + chipType = chipType.substr(index + 1, chipType.length() - index - 1); + } + + chipType = chipType.substr(5, chipType.length() - 5); + index = chipType.find("_"); + if(index > 0) + { + chipType = chipType.substr(0, index); + } + + map::iterator iter = mapChipType.find(chipType); + if(iter == mapChipType.end()) + { + mapChipType[chipType] = 1; + vecChipType.push_back(chipType); + } + else + { + mapChipType[chipType]++; + } + } + + ifs.close(); + cmdline = "rm ./tmpFileList.txt"; + system(cmdline.c_str()); + + if(vecChipType.empty()) + { + cerr << "ERROR: There is no per chip json file at " << jsonDir << endl; + exit ( EXIT_FAILURE ); + } + + sort(vecChipType.begin(), vecChipType.end()); + cout << "Available chip types are:" << endl; + for(vector::iterator iter1 = vecChipType.begin(); iter1 != vecChipType.end() - 1; ++iter1) + { + cout << *iter1 << ", "; + } + + cout << vecChipType.back() << endl; + + vector editList; + string chipTypes; + cout << "Please select chip type to edit ('q' to quit; 'n' for new; 'a' for all; separate multiple chip types with ','):" << endl; + cin >> chipTypes; + + if(chipTypes == "q") + { + cout << "Bye." << endl; + exit(0); + } + else if(chipTypes == "n") + { + cout << "New chip type name: "; + cin >> chipTypes; + string chipBase; + cout << "Base chip type (data will be copied to new chip type): "; + cin >> chipBase; + if(chipBase.length() == 0) + { + chipBase = "530"; + } + + string srcName(jsonDir); + srcName += "/args_"; + srcName += chipBase; + srcName += "_analysis.json"; + string desName(jsonDir); + desName += "/args_"; + desName += chipTypes; + desName += "_analysis.json"; + cmdline = "cp "; + cmdline += srcName; + cmdline += " "; + cmdline += desName; + cout << cmdline; + system(cmdline.c_str()); + + srcName = jsonDir; + srcName += "/args_"; + srcName += chipBase; + srcName += "_beadfind.json"; + desName = jsonDir; + desName += "/args_"; + desName += chipTypes; + desName += "_beadfind.json"; + cmdline = "cp "; + cmdline += srcName; + cmdline += " "; + cmdline += desName; + cout << cmdline; + system(cmdline.c_str()); + + editList.push_back(chipTypes); + } + else if(chipTypes == "a") + { + chipTypes = "all chip types"; + editList = vecChipType; + } + else + { + int index = chipTypes.find(","); + while(index > 0) + { + string chipType = chipTypes.substr(0, index); + editList.push_back(chipType); + chipTypes = chipTypes.substr(index + 1, chipTypes.length() - index - 1); + index = chipTypes.find(","); + } + editList.push_back(chipTypes); + } + + string chipType = editList[0]; + + string analysisName(jsonDir); + analysisName += "/args_"; + analysisName += chipType; + analysisName += "_analysis.json"; + string beadfindName(jsonDir); + beadfindName += "/args_"; + beadfindName += chipType; + beadfindName += "_beadfind.json"; + + ifstream ifsa(analysisName.c_str()); + Value jsona; + Reader readera; + readera.parse(ifsa, jsona, false); + ifsa.close(); + + ifstream ifsb(beadfindName.c_str()); + Value jsonb; + Reader readerb; + readerb.parse(ifsb, jsonb, false); + ifsb.close(); + + map modules; + map opts; + vector tnOpts; + Value::Members groupsa = jsona.getMemberNames(); + for(Value::Members::iterator ita1 = groupsa.begin(); ita1 != groupsa.end(); ++ita1) + { + if(*ita1 == "chipType") + { + string chipType0 = jsona["chipType"].asString(); + if(chipType != chipType0) // for new chip type + { + jsona["chipType"] = chipType; + jsonb["chipType"] = chipType; + } + } + else if(*ita1 == "ThumbnailControl") + { + Value::Members itemstn = jsona["ThumbnailControl"].getMemberNames(); + for(Value::Members::iterator itn = itemstn.begin(); itn != itemstn.end(); ++itn) + { + tnOpts.push_back(*itn); + } + } + else + { + modules[*ita1] = 1; + Value::Members itemsa = jsona[*ita1].getMemberNames(); + for(Value::Members::iterator ita2 = itemsa.begin(); ita2 != itemsa.end(); ++ita2) + { + opts[*ita2] = (*ita1); + } + } + } + + Value::Members groupsb = jsonb.getMemberNames(); + for(Value::Members::iterator itb1 = groupsb.begin(); itb1 != groupsb.end(); ++itb1) + { + if(*itb1 != "chipType") + { + if(*itb1 == "ThumbnailControl") + { + Value::Members itemstn = jsonb["ThumbnailControl"].getMemberNames(); + for(Value::Members::iterator itn = itemstn.begin(); itn != itemstn.end(); ++itn) + { + size_t k = 0; + for(; k < tnOpts.size(); ++k) + { + if(tnOpts[k] == (*itn)) + { + break; + } + } + if(k == tnOpts.size()) + { + tnOpts.push_back(*itn); + } + } + + } + else + { + map::iterator iter1 = modules.find(*itb1); + if(iter1 == modules.end()) + { + modules[*itb1] = 2; + Value::Members itemsb = jsonb[*itb1].getMemberNames(); + for(Value::Members::iterator itb2 = itemsb.begin(); itb2 != itemsb.end(); ++itb2) + { + opts[*itb2] = (*itb1); + } + } + else + { + modules[*itb1] = 3; + } + } + } + } + + cout << "Working on chip type " << chipType << endl; + cout << "Which option you want to work on: " << endl + << "\tfor searching: search" << endl + << "\tfor adding new option: new" << endl + << "\tfor listing all options: list" << endl + << "\tfor thumbnail options: thumbnail" << endl; + + int dtype = -1; + string optin; + cin >> optin; + + bool tn = false; + + if(optin == "thumbnail") + { + tn = true; + + cout << "Existing thumbnail options:" << endl; + size_t k = 0; + for(; k < tnOpts.size(); ++k) + { + cout << k + 1 << " - " << tnOpts[k] << endl; + } + + cout << "Please select an option you want to work on (type 0 for adding new option):" << endl; + int n = 0; + cin >> n; + if(n > 0) + { + optin = tnOpts[n - 1]; + } + else + { + cout << "Please type new option name you want to work on (list for listing all options):" << endl; + cin >> optin; + + if(optin == "list") + { + for(map::iterator iter = opts.begin(); iter != opts.end(); ++iter) + { + cout << iter->first << endl; + } + + cout << "Please type the option you want to work on:" << endl; + cin >> optin; + } + } + } + else if(optin == "list") + { + for(map::iterator iter = opts.begin(); iter != opts.end(); ++iter) + { + cout << iter->first << endl; + } + + cout << "Please type the option you want to work on:" << endl; + cin >> optin; + } + else if(optin == "search") + { + cout << "Please type partial of the option word:" << endl; + cin >> optin; + vector names; + for(map::iterator iter = opts.begin(); iter != opts.end(); ++iter) + { + int idxsearch = iter->first.find(optin); + if(idxsearch >= 0) + { + names.push_back(iter->first); + } + } + + if(names.empty()) + { + cout << "There is no option containing " << optin << endl; + cout << "Here is the full list of options:" << endl; + for(map::iterator iter = opts.begin(); iter != opts.end(); ++iter) + { + cout << iter->first << endl; + } + + cout << "Please type the option you want to work on:" << endl; + cin >> optin; + } + else if(names.size() == 1) + { + optin = names[0]; + cout << "Working on option " << optin << endl; + } + else + { + cout << "Here is the list of options containing " << optin << endl; + for(size_t i = 0; i < names.size(); ++i) + { + cout << i << " - " << names[i] << endl; + } + + cout << "Please type the option you want to work on:" << endl; + int k; + cin >> k; + optin = names[k]; + } + } + if(optin == "new") + { + cout << "Please type new option name:" << endl; + cin >> optin; + cout << "Which module you want to add " << optin << " to:" << endl; + for(map::iterator iter = modules.begin(); iter != modules.end(); ++ iter) + { + cout << iter->first << endl; + } + + string mod; + cin >> mod; + opts[optin] = mod; + + cout << "option data type: 0 - bool; 1 - integer; 2 - double; 3 - string; 4 - vector of integers; 5 - vector 0f doubles." << endl; + cin >> dtype; + } + + int idx = -1; + bool val0; + int val1, vali; + double val2, valf; + string val3, val4, val5; + + string mod = opts[optin]; + string mod2 = mod; + if(tn) + { + mod = "ThumbnailControl"; + } + + cout << "option's value:" << endl; + if(dtype < 0) + { + dtype = mapOptType[optin]; + } + + switch (dtype) + { + case OT_BOOL: + cin >> val0; + + if(modules[mod2] == 1) + { + jsona[mod][optin] = val0; + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin] = val0; + } + else if(modules[mod2] == 3) + { + jsona[mod][optin] = val0; + jsonb[mod][optin] = val0; + } + + break; + + case OT_INT: + cin >> val1; + + if(modules[mod2] == 1) + { + jsona[mod][optin] = val1; + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin] = val1; + } + else if(modules[mod2] == 3) + { + jsona[mod][optin] = val1; + jsonb[mod][optin] = val1; + } + + break; + + case OT_DOUBLE: + cin >> val2; + + if(modules[mod2] == 1) + { + jsona[mod][optin] = val2; + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin] = val2; + } + else if(modules[mod2] == 3) + { + jsona[mod][optin] = val2; + jsonb[mod][optin] = val2; + } + + break; + + case OT_STRING: + cin >> val3; + + if(modules[mod2] == 1) + { + jsona[mod][optin] = val3; + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin] = val3; + } + else if(modules[mod2] == 3) + { + jsona[mod][optin] = val3; + jsonb[mod][optin] = val3; + } + + break; + + case OT_VECTOR_INT: + if(modules[mod2] == 1) + { + jsona[mod][optin].clear(); + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin].clear(); + } + else if(modules[mod2] == 3) + { + jsona[mod][optin].clear(); + jsonb[mod][optin].clear(); + } + + cin >> val3; + val4 = val3; + idx = val4.find(","); + while(idx > 0) + { + string s2 = val4.substr(0, idx); + vali = atoi(s2.c_str()); + if(modules[mod2] == 1) + { + jsona[mod][optin].append(vali); + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin].append(vali); + } + else if(modules[mod2] == 3) + { + jsona[mod][optin].append(vali); + jsonb[mod][optin].append(vali); + } + + val4 = val4.substr(idx + 1, val4.length() - idx - 1); + idx = val4.find(","); + } + + vali = atoi(val4.c_str()); + if(modules[mod2] == 1) + { + jsona[mod][optin].append(vali); + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin].append(vali); + } + else if(modules[mod2] == 3) + { + jsona[mod][optin].append(vali); + jsonb[mod][optin].append(vali); + } + + break; + + case OT_VECTOR_DOUBLE: + if(modules[mod2] == 1) + { + jsona[mod][optin].clear(); + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin].clear(); + } + else if(modules[mod2] == 3) + { + jsona[mod][optin].clear(); + jsonb[mod][optin].clear(); + } + + cin >> val3; + val5 = val3; + idx = val5.find(","); + while(idx > 0) + { + string s2 = val5.substr(0, idx); + valf = atof(s2.c_str()); + if(modules[mod2] == 1) + { + jsona[mod][optin].append(valf); + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin].append(valf); + } + else if(modules[mod2] == 3) + { + jsona[mod][optin].append(valf); + jsonb[mod][optin].append(valf); + } + + val5 = val5.substr(idx + 1, val5.length() - idx - 1); + idx = val5.find(","); + } + + valf = atof(val5.c_str()); + if(modules[mod2] == 1) + { + jsona[mod][optin].append(valf); + } + else if(modules[mod2] == 2) + { + jsonb[mod][optin].append(valf); + } + else if(modules[mod2] == 3) + { + jsona[mod][optin].append(valf); + jsonb[mod][optin].append(valf); + } + + break; + + default: + cout << "ERROR: Invalid option data type!" << endl; + return(1); + } + + saveJson(jsona, analysisName); + saveJson(jsonb, beadfindName); + + for(size_t i = 1; i < editList.size(); ++i) + { + chipType = editList[i]; + cout << "Working on chip type " << chipType << endl; + + analysisName = jsonDir; + analysisName += "/args_"; + analysisName += chipType; + analysisName += "_analysis.json"; + beadfindName = jsonDir; + beadfindName += "/args_"; + beadfindName += chipType; + beadfindName += "_beadfind.json"; + + ifstream ifsa2(analysisName.c_str()); + Value jsona2; + Reader readera2; + readera2.parse(ifsa2, jsona2, false); + ifsa2.close(); + + ifstream ifsb2(beadfindName.c_str()); + Value jsonb2; + Reader readerb2; + readerb2.parse(ifsb2, jsonb2, false); + ifsb2.close(); + + switch (dtype) + { + case OT_BOOL: + if(modules[mod2] == 1) + { + jsona2[mod][optin] = val0; + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin] = val0; + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin] = val0; + jsonb2[mod][optin] = val0; + } + + break; + + case OT_INT: + if(modules[mod2] == 1) + { + jsona2[mod][optin] = val1; + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin] = val1; + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin] = val1; + jsonb2[mod][optin] = val1; + } + + break; + + case OT_DOUBLE: + if(modules[mod2] == 1) + { + jsona2[mod][optin] = val2; + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin] = val2; + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin] = val2; + jsonb2[mod][optin] = val2; + } + + break; + + case OT_STRING: + if(modules[mod2] == 1) + { + jsona2[mod][optin] = val3; + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin] = val3; + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin] = val3; + jsonb2[mod][optin] = val3; + } + + break; + + case OT_VECTOR_INT: + if(modules[mod2] == 1) + { + jsona2[mod][optin].clear(); + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin].clear(); + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin].clear(); + jsonb2[mod][optin].clear(); + } + + val4 = val3; + idx = val4.find(","); + while(idx > 0) + { + string s2 = val4.substr(0, idx); + vali = atoi(s2.c_str()); + if(modules[mod2] == 1) + { + jsona2[mod][optin].append(vali); + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin].append(vali); + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin].append(vali); + jsonb2[mod][optin].append(vali); + } + + val4 = val4.substr(idx + 1, val4.length() - idx - 1); + idx = val4.find(","); + } + + vali = atoi(val4.c_str()); + if(modules[mod2] == 1) + { + jsona2[mod][optin].append(vali); + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin].append(vali); + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin].append(vali); + jsonb2[mod][optin].append(vali); + } + + break; + + case OT_VECTOR_DOUBLE: + if(modules[mod2] == 1) + { + jsona2[mod][optin].clear(); + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin].clear(); + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin].clear(); + jsonb2[mod][optin].clear(); + } + + val5 = val3; + idx = val5.find(","); + while(idx > 0) + { + string s2 = val5.substr(0, idx); + valf = atof(s2.c_str()); + if(modules[mod2] == 1) + { + jsona2[mod][optin].append(valf); + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin].append(valf); + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin].append(valf); + jsonb2[mod][optin].append(valf); + } + + val5 = val5.substr(idx + 1, val5.length() - idx - 1); + idx = val5.find(","); + } + + valf = atof(val5.c_str()); + if(modules[mod2] == 1) + { + jsona2[mod][optin].append(valf); + } + else if(modules[mod2] == 2) + { + jsonb2[mod][optin].append(valf); + } + else if(modules[mod2] == 3) + { + jsona2[mod][optin].append(valf); + jsonb2[mod][optin].append(valf); + } + + break; + + default: + break; + } + + saveJson(jsona2, analysisName); + saveJson(jsonb2, beadfindName); + } + + return (0); +} diff --git a/Analysis/TsInputUtil/tsInputUtil.cpp b/Analysis/TsInputUtil/tsInputUtil.cpp index 622e9781..87aa4609 100644 --- a/Analysis/TsInputUtil/tsInputUtil.cpp +++ b/Analysis/TsInputUtil/tsInputUtil.cpp @@ -10,7 +10,7 @@ #include #include "json/json.h" -#include "Utils.h"" +#include "Utils.h" #include "OptArgs.h" #define TS_INPUT_UTIL_VERSION "1.0.0" @@ -165,11 +165,11 @@ int main(int argc, const char *argv[]) jsonBase["BkgModelControlOpts"]["mixed-stringency"]["value"] = 0.5; jsonBase["BkgModelControlOpts"]["mixed-stringency"]["min"] = ""; jsonBase["BkgModelControlOpts"]["mixed-stringency"]["max"] = ""; - jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-alpha"]["type"] = OT_FLOAT; + jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-alpha"]["type"] = OT_DOUBLE; jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-alpha"]["value"] = 1.0; jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-alpha"]["min"] = ""; jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-alpha"]["max"] = ""; - jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-gamma"]["type"] = OT_FLOAT; + jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-gamma"]["type"] = OT_DOUBLE; jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-gamma"]["value"] = 1.0; jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-gamma"]["min"] = ""; jsonBase["BkgModelControlOpts"]["sigproc-regional-smoothing-gamma"]["max"] = ""; @@ -177,7 +177,7 @@ int main(int argc, const char *argv[]) jsonBase["BkgModelControlOpts"]["restart-region-params-file"]["value"] = ""; jsonBase["BkgModelControlOpts"]["restart-region-params-file"]["min"] = ""; jsonBase["BkgModelControlOpts"]["restart-region-params-file"]["max"] = ""; - jsonBase["BkgModelControlOpts"]["bkg-washout-threshold"]["type"] = OT_FLOAT; + jsonBase["BkgModelControlOpts"]["bkg-washout-threshold"]["type"] = OT_DOUBLE; jsonBase["BkgModelControlOpts"]["bkg-washout-threshold"]["value"] = 2.0; jsonBase["BkgModelControlOpts"]["bkg-washout-threshold"]["min"] = ""; jsonBase["BkgModelControlOpts"]["bkg-washout-threshold"]["max"] = ""; @@ -185,7 +185,7 @@ int main(int argc, const char *argv[]) jsonBase["BkgModelControlOpts"]["bkg-washout-flow-detection"]["value"] = 6; jsonBase["BkgModelControlOpts"]["bkg-washout-flow-detection"]["min"] = ""; jsonBase["BkgModelControlOpts"]["bkg-washout-flow-detection"]["max"] = ""; - jsonBase["GpuControlOpts"]["gpuworkload"]["type"] = OT_FLOAT; + jsonBase["GpuControlOpts"]["gpuworkload"]["type"] = OT_DOUBLE; jsonBase["GpuControlOpts"]["gpuworkload"]["value"] = 1.0; jsonBase["GpuControlOpts"]["gpuworkload"]["min"] = 0.0; jsonBase["GpuControlOpts"]["gpuworkload"]["max"] = 1.0; @@ -269,6 +269,14 @@ int main(int argc, const char *argv[]) jsonBase["GpuControlOpts"]["gpu-num-history-flows"]["value"] = 10; jsonBase["GpuControlOpts"]["gpu-num-history-flows"]["min"] = ""; jsonBase["GpuControlOpts"]["gpu-num-history-flows"]["max"] = ""; + jsonBase["GpuControlOpts"]["gpu-force-multi-flow-fit"]["type"] = OT_BOOL; + jsonBase["GpuControlOpts"]["gpu-force-multi-flow-fit"]["value"] = false; + jsonBase["GpuControlOpts"]["gpu-force-multi-flow-fit"]["min"] = ""; + jsonBase["GpuControlOpts"]["gpu-force-multi-flow-fit"]["max"] = ""; + jsonBase["GpuControlOpts"]["gpu-memory-per-proc"]["type"] = OT_INT; + jsonBase["GpuControlOpts"]["gpu-memory-per-proc"]["value"] = 0; + jsonBase["GpuControlOpts"]["gpu-memory-per-proc"]["min"] = ""; + jsonBase["GpuControlOpts"]["gpu-memory-per-proc"]["max"] = ""; jsonBase["SignalProcessingBlockControl"]["wells-compression"]["type"] = OT_INT; jsonBase["SignalProcessingBlockControl"]["wells-compression"]["value"] = 0; jsonBase["SignalProcessingBlockControl"]["wells-compression"]["min"] = 0; @@ -367,6 +375,10 @@ int main(int argc, const char *argv[]) jsonBase["BeadfindControlOpts"]["use-beadmask"]["value"] = ""; jsonBase["BeadfindControlOpts"]["use-beadmask"]["min"] = ""; jsonBase["BeadfindControlOpts"]["use-beadmask"]["max"] = ""; + jsonBase["BeadfindControlOpts"]["exclusion-mask"]["type"] = OT_STRING; + jsonBase["BeadfindControlOpts"]["exclusion-mask"]["value"] = ""; + jsonBase["BeadfindControlOpts"]["exclusion-mask"]["min"] = ""; + jsonBase["BeadfindControlOpts"]["exclusion-mask"]["max"] = ""; jsonBase["BeadfindControlOpts"]["beadmask-categorized"]["type"] = OT_BOOL; jsonBase["BeadfindControlOpts"]["beadmask-categorized"]["value"] = false; jsonBase["BeadfindControlOpts"]["beadmask-categorized"]["min"] = ""; @@ -387,7 +399,7 @@ int main(int argc, const char *argv[]) jsonBase["BeadfindControlOpts"]["beadfind-sdasbf"]["value"] = true; jsonBase["BeadfindControlOpts"]["beadfind-sdasbf"]["min"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-sdasbf"]["max"] = ""; - jsonBase["BeadfindControlOpts"]["beadfind-bfmult"]["type"] = OT_FLOAT; + jsonBase["BeadfindControlOpts"]["beadfind-bfmult"]["type"] = OT_DOUBLE; jsonBase["BeadfindControlOpts"]["beadfind-bfmult"]["value"] = 1.0; jsonBase["BeadfindControlOpts"]["beadfind-bfmult"]["min"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-bfmult"]["max"] = ""; @@ -407,11 +419,11 @@ int main(int argc, const char *argv[]) jsonBase["BeadfindControlOpts"]["beadfind-min-tf-snr"]["value"] = 7.0; jsonBase["BeadfindControlOpts"]["beadfind-min-tf-snr"]["min"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-min-tf-snr"]["max"] = ""; - jsonBase["BeadfindControlOpts"]["beadfind-tf-min-peak"]["type"] = OT_FLOAT; + jsonBase["BeadfindControlOpts"]["beadfind-tf-min-peak"]["type"] = OT_DOUBLE; jsonBase["BeadfindControlOpts"]["beadfind-tf-min-peak"]["value"] = 40.0; jsonBase["BeadfindControlOpts"]["beadfind-tf-min-peak"]["min"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-tf-min-peak"]["max"] = ""; - jsonBase["BeadfindControlOpts"]["beadfind-lib-min-peak"]["type"] = OT_FLOAT; + jsonBase["BeadfindControlOpts"]["beadfind-lib-min-peak"]["type"] = OT_DOUBLE; jsonBase["BeadfindControlOpts"]["beadfind-lib-min-peak"]["value"] = 10.0; jsonBase["BeadfindControlOpts"]["beadfind-lib-min-peak"]["min"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-lib-min-peak"]["max"] = ""; @@ -427,10 +439,6 @@ int main(int argc, const char *argv[]) jsonBase["BeadfindControlOpts"]["beadfind-skip-sd-recover"]["value"] = 1; jsonBase["BeadfindControlOpts"]["beadfind-skip-sd-recover"]["min"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-skip-sd-recover"]["max"] = ""; - jsonBase["BeadfindControlOpts"]["beadfind-thumbnail"]["type"] = OT_INT; - jsonBase["BeadfindControlOpts"]["beadfind-thumbnail"]["value"] = 0; - jsonBase["BeadfindControlOpts"]["beadfind-thumbnail"]["min"] = ""; - jsonBase["BeadfindControlOpts"]["beadfind-thumbnail"]["max"] = ""; jsonBase["BeadfindControlOpts"]["beadfind-sep-ref"]["type"] = OT_BOOL; jsonBase["BeadfindControlOpts"]["beadfind-sep-ref"]["value"] = false; jsonBase["BeadfindControlOpts"]["beadfind-sep-ref"]["min"] = ""; @@ -583,7 +591,7 @@ int main(int argc, const char *argv[]) jsonBase["ImageControlOpts"]["col-doubles-xtalk-correct"]["value"] = false; jsonBase["ImageControlOpts"]["col-doubles-xtalk-correct"]["min"] = ""; jsonBase["ImageControlOpts"]["col-doubles-xtalk-correct"]["max"] = ""; - jsonBase["ImageControlOpts"]["pair-xtalk-coeff"]["type"] = OT_FLOAT; + jsonBase["ImageControlOpts"]["pair-xtalk-coeff"]["type"] = OT_DOUBLE; jsonBase["ImageControlOpts"]["pair-xtalk-coeff"]["value"] = 0.0; jsonBase["ImageControlOpts"]["pair-xtalk-coeff"]["min"] = ""; jsonBase["ImageControlOpts"]["pair-xtalk-coeff"]["max"] = ""; @@ -591,7 +599,7 @@ int main(int argc, const char *argv[]) jsonBase["ImageControlOpts"]["fluid-potential-correct"]["value"] = false; jsonBase["ImageControlOpts"]["fluid-potential-correct"]["min"] = ""; jsonBase["ImageControlOpts"]["fluid-potential-correct"]["max"] = ""; - jsonBase["ImageControlOpts"]["fluid-potential-threshold"]["type"] = OT_FLOAT; + jsonBase["ImageControlOpts"]["fluid-potential-threshold"]["type"] = OT_DOUBLE; jsonBase["ImageControlOpts"]["fluid-potential-threshold"]["value"] = 1.0; jsonBase["ImageControlOpts"]["fluid-potential-threshold"]["min"] = ""; jsonBase["ImageControlOpts"]["fluid-potential-threshold"]["max"] = ""; @@ -599,6 +607,10 @@ int main(int argc, const char *argv[]) jsonBase["ImageControlOpts"]["corr-noise-correct"]["value"] = true; jsonBase["ImageControlOpts"]["corr-noise-correct"]["min"] = ""; jsonBase["ImageControlOpts"]["corr-noise-correct"]["max"] = ""; + jsonBase["ImageControlOpts"]["mask-datacollect-exclude-regions"]["type"] = OT_BOOL; + jsonBase["ImageControlOpts"]["mask-datacollect-exclude-regions"]["value"] = false; + jsonBase["ImageControlOpts"]["mask-datacollect-exclude-regions"]["min"] = ""; + jsonBase["ImageControlOpts"]["mask-datacollect-exclude-regions"]["max"] = ""; jsonBase["ModuleControlOpts"]["bfonly"]["type"] = OT_BOOL; jsonBase["ModuleControlOpts"]["bfonly"]["value"] = false; jsonBase["ModuleControlOpts"]["bfonly"]["min"] = ""; @@ -705,11 +717,11 @@ int main(int argc, const char *argv[]) jsonBase["SystemContext"]["wells-save-as-ushort"]["value"] = true; jsonBase["SystemContext"]["wells-save-as-ushort"]["min"] = ""; jsonBase["SystemContext"]["wells-save-as-ushort"]["max"] = ""; - jsonBase["SystemContext"]["wells-convert-low"]["type"] = OT_FLOAT; + jsonBase["SystemContext"]["wells-convert-low"]["type"] = OT_DOUBLE; jsonBase["SystemContext"]["wells-convert-low"]["value"] = -5.0; jsonBase["SystemContext"]["wells-convert-low"]["min"] = ""; jsonBase["SystemContext"]["wells-convert-low"]["max"] = ""; - jsonBase["SystemContext"]["wells-convert-high"]["type"] = OT_FLOAT; + jsonBase["SystemContext"]["wells-convert-high"]["type"] = OT_DOUBLE; jsonBase["SystemContext"]["wells-convert-high"]["value"] = 28.0; jsonBase["SystemContext"]["wells-convert-high"]["min"] = ""; jsonBase["SystemContext"]["wells-convert-high"]["max"] = ""; @@ -741,19 +753,19 @@ int main(int argc, const char *argv[]) jsonBase["GlobalDefaultsForBkgModel"]["barcode-spec-file"]["value"] = ""; jsonBase["GlobalDefaultsForBkgModel"]["barcode-spec-file"]["min"] = ""; jsonBase["GlobalDefaultsForBkgModel"]["barcode-spec-file"]["max"] = ""; - jsonBase["LocalSigProcControl"]["bkg-kmult-adj-low-hi"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["bkg-kmult-adj-low-hi"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["bkg-kmult-adj-low-hi"]["value"] = 2.0; jsonBase["LocalSigProcControl"]["bkg-kmult-adj-low-hi"]["min"] = ""; jsonBase["LocalSigProcControl"]["bkg-kmult-adj-low-hi"]["max"] = ""; - jsonBase["LocalSigProcControl"]["kmult-low-limit"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["kmult-low-limit"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["kmult-low-limit"]["value"] = 0.65; jsonBase["LocalSigProcControl"]["kmult-low-limit"]["min"] = ""; jsonBase["LocalSigProcControl"]["kmult-low-limit"]["max"] = ""; - jsonBase["LocalSigProcControl"]["kmult-hi-limit"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["kmult-hi-limit"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["kmult-hi-limit"]["value"] = 1.75; jsonBase["LocalSigProcControl"]["kmult-hi-limit"]["min"] = ""; jsonBase["LocalSigProcControl"]["kmult-hi-limit"]["max"] = ""; - jsonBase["LocalSigProcControl"]["bkg-copy-stringency"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["bkg-copy-stringency"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["bkg-copy-stringency"]["value"] = 1.0; jsonBase["LocalSigProcControl"]["bkg-copy-stringency"]["min"] = ""; jsonBase["LocalSigProcControl"]["bkg-copy-stringency"]["max"] = ""; @@ -789,19 +801,19 @@ int main(int argc, const char *argv[]) jsonBase["LocalSigProcControl"]["barcode-debug"]["value"] = false; jsonBase["LocalSigProcControl"]["barcode-debug"]["min"] = ""; jsonBase["LocalSigProcControl"]["barcode-debug"]["max"] = ""; - jsonBase["LocalSigProcControl"]["barcode-radius"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["barcode-radius"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["barcode-radius"]["value"] = 0.75; jsonBase["LocalSigProcControl"]["barcode-radius"]["min"] = ""; jsonBase["LocalSigProcControl"]["barcode-radius"]["max"] = ""; - jsonBase["LocalSigProcControl"]["barcode-tie"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["barcode-tie"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["barcode-tie"]["value"] = 0.5; jsonBase["LocalSigProcControl"]["barcode-tie"]["min"] = ""; jsonBase["LocalSigProcControl"]["barcode-tie"]["max"] = ""; - jsonBase["LocalSigProcControl"]["barcode-penalty"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["barcode-penalty"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["barcode-penalty"]["value"] = 2000.0; jsonBase["LocalSigProcControl"]["barcode-penalty"]["min"] = ""; jsonBase["LocalSigProcControl"]["barcode-penalty"]["max"] = ""; - jsonBase["LocalSigProcControl"]["kmult-penalty"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["kmult-penalty"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["kmult-penalty"]["value"] = 100.0; jsonBase["LocalSigProcControl"]["kmult-penalty"]["min"] = ""; jsonBase["LocalSigProcControl"]["kmult-penalty"]["max"] = ""; @@ -825,11 +837,11 @@ int main(int argc, const char *argv[]) jsonBase["LocalSigProcControl"]["bkg-exp-tail-tau-adj"]["value"] = true; jsonBase["LocalSigProcControl"]["bkg-exp-tail-tau-adj"]["min"] = ""; jsonBase["LocalSigProcControl"]["bkg-exp-tail-tau-adj"]["max"] = ""; - jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-limit"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-limit"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-limit"]["value"] = 0.2; jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-limit"]["min"] = ""; jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-limit"]["max"] = ""; - jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-lower"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-lower"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-lower"]["value"] = 10.0; jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-lower"]["min"] = ""; jsonBase["LocalSigProcControl"]["bkg-exp-tail-bkg-lower"]["max"] = ""; @@ -861,7 +873,7 @@ int main(int argc, const char *argv[]) jsonBase["LocalSigProcControl"]["vectorize"]["value"] = true; jsonBase["LocalSigProcControl"]["vectorize"]["min"] = ""; jsonBase["LocalSigProcControl"]["vectorize"]["max"] = ""; - jsonBase["LocalSigProcControl"]["bkg-ampl-lower-limit"]["type"] = OT_FLOAT; + jsonBase["LocalSigProcControl"]["bkg-ampl-lower-limit"]["type"] = OT_DOUBLE; jsonBase["LocalSigProcControl"]["bkg-ampl-lower-limit"]["value"] = 0.001; jsonBase["LocalSigProcControl"]["bkg-ampl-lower-limit"]["min"] = ""; jsonBase["LocalSigProcControl"]["bkg-ampl-lower-limit"]["max"] = ""; diff --git a/Analysis/Util/NumericalComparison.h b/Analysis/Util/NumericalComparison.h index b7d8d13a..8fed0ec1 100644 --- a/Analysis/Util/NumericalComparison.h +++ b/Analysis/Util/NumericalComparison.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "SampleStats.h" #define NC_CORRELATION_EPSILON .00001 @@ -48,10 +49,10 @@ class NumericalComparison { * particiular x & y are closer than epsilon, false otherwise. */ bool AddPair(const T &x, const T &y) { - if (isnan(x) && isnan(y)) { + if (std::isnan(x) && std::isnan(y)) { return true; } - if (isinf(x) && isinf(y)) { + if (std::isinf(x) && std::isinf(y)) { return true; } bool closeEnough = true; diff --git a/Analysis/Util/Utils.cpp b/Analysis/Util/Utils.cpp index 2dc13dda..f6c08772 100644 --- a/Analysis/Util/Utils.cpp +++ b/Analysis/Util/Utils.cpp @@ -970,4 +970,129 @@ int GetAbsoluteFreeSystemMemoryInKB() return freeMem; } +// ----------------- + + +void expandBaseSymbol(char nuc, std::vector& nuc_ensemble) +{ + nuc_ensemble.assign(4, false); + nuc = toupper(nuc); + + switch(nuc) { + case 'A': nuc_ensemble[0] = true; break; + case 'C': nuc_ensemble[1] = true; break; + case 'G': nuc_ensemble[2] = true; break; + case 'T': nuc_ensemble[3] = true; break; + case 'U': nuc_ensemble[3] = true; break; + + case 'W': nuc_ensemble[0] = true; nuc_ensemble[3] = true; break; + case 'S': nuc_ensemble[1] = true; nuc_ensemble[2] = true; break; + case 'M': nuc_ensemble[0] = true; nuc_ensemble[1] = true; break; + case 'K': nuc_ensemble[2] = true; nuc_ensemble[3] = true; break; + case 'R': nuc_ensemble[0] = true; nuc_ensemble[2] = true; break; + case 'Y': nuc_ensemble[1] = true; nuc_ensemble[3] = true; break; + + case 'B': nuc_ensemble[1] = true; nuc_ensemble[2] = true; nuc_ensemble[3] = true; break; + case 'D': nuc_ensemble[0] = true; nuc_ensemble[2] = true; nuc_ensemble[3] = true; break; + case 'H': nuc_ensemble[0] = true; nuc_ensemble[1] = true; nuc_ensemble[3] = true; break; + case 'I': nuc_ensemble[0] = true; nuc_ensemble[1] = true; nuc_ensemble[3] = true; break; + case 'V': nuc_ensemble[0] = true; nuc_ensemble[1] = true; nuc_ensemble[2] = true; break; + + case 'N': nuc_ensemble.assign(4, true); break; + } +} + +char contractNucSymbol(const std::vector& nuc_ensemble) +{ + if (nuc_ensemble.size() != 4) + return 'Z'; + + unsigned int weight = 0; + for (unsigned int i=0; i<4; ++i) + if (nuc_ensemble[i]) + ++weight; + + if (weight == 0) + return 'Z'; + + else if (weight==4) + return 'N'; + + else if (weight == 3){ + if (not nuc_ensemble[0]) + return 'B'; + else if (not nuc_ensemble[1]) + return 'D'; + else if (not nuc_ensemble[2]) + return 'H'; + else // if (not nuc_ensemble[3]) + return 'V'; + } + + else if (weight == 2){ + if (nuc_ensemble[0] and nuc_ensemble[3]) + return 'W'; + else if (nuc_ensemble[1] and nuc_ensemble[2]) + return 'S'; + else if (nuc_ensemble[0] and nuc_ensemble[1]) + return 'M'; + else if (nuc_ensemble[2] and nuc_ensemble[3]) + return 'K'; + else if (nuc_ensemble[0] and nuc_ensemble[2]) + return 'R'; + else //if (nuc_ensemble[1] and nuc_ensemble[3]) + return 'Y'; + } + + else if (nuc_ensemble[0]) + return 'A'; + else if (nuc_ensemble[1]) + return 'C'; + else if (nuc_ensemble[2]) + return 'G'; + else // if (nuc_ensemble[3]) + return 'T'; +} + + +bool isBaseMatch(char nuc1, char nuc2) +{ + std::vector ensemble1, ensemble2; + expandBaseSymbol(nuc1, ensemble1); + expandBaseSymbol(nuc2, ensemble2); + + /* / ---- XXX + cerr << "["; + for (unsigned int i=0; i ensemble1, ensemble2; + expandBaseSymbol(nuc1, ensemble1); + expandBaseSymbol(nuc2, ensemble2); + + for (unsigned int i=0; i #include #include + #ifndef ALIGNSTATS_IGNORE #include "SpecialDataTypes.h" #include "LinuxCompat.h" #endif + // Transition states used in Analysis main function for progress tracking #define WELL_TO_IMAGE 1 #define IMAGE_TO_SIGNAL 2 @@ -72,6 +74,12 @@ int GetFreeSystemMem(); int GetCachedSystemMem(); int GetSystemMemInBuffers(); +// --- Mapping of base ambiguity symbols + +void expandBaseSymbol(char nuc, std::vector& nuc_ensemble); +char contractNucSymbol(const std::vector& nuc_ensemble); +bool isBaseMatch(char nuc1, char nuc2); +char getMatchSymbol(char nuc1, char nuc2); //string utils int count_char (std::string s, char c); @@ -283,5 +291,4 @@ T fast_median(T* start, size_t num_elements) { return (*middle + *(middle -1)) /2.0f; } - #endif // UTILS_H diff --git a/Analysis/VariantCaller/BAMWalkerEngine.cpp b/Analysis/VariantCaller/BAMWalkerEngine.cpp index 66ee1aad..6f77943d 100644 --- a/Analysis/VariantCaller/BAMWalkerEngine.cpp +++ b/Analysis/VariantCaller/BAMWalkerEngine.cpp @@ -199,16 +199,30 @@ void BAMWalkerEngine::InitializeBAMs(const ReferenceReader& ref_reader, const ve tmap_version_ += ", "; tmap_version_ += *I; } - } - - bool BAMWalkerEngine::EligibleForReadRemoval() { return alignments_first_ and alignments_first_->read_number+100 < first_useful_read_; } +// Determine the eligibility for read removal for target-pileup +//@TODO: Should depend on first_useful_read_ only. +bool ConsensusBAMWalkerEngine::EligibleForTargetBasedReadRemoval() +{ + if (EligibleForReadRemoval()){ + if (positions_in_progress_.empty()){ + return (((int) alignments_first_->alignment.RefID < next_target_->chr) + or (((int) alignments_first_->alignment.RefID == next_target_->chr) + and (alignments_first_->end < (long int) next_target_->begin))); + }else{ + return (((int) alignments_first_->alignment.RefID < positions_in_progress_.begin()->chr) + or (((int) alignments_first_->alignment.RefID == positions_in_progress_.begin()->chr) + and (alignments_first_->end < (long int) positions_in_progress_.begin()->target_begin))); + } + } + return false; +} void BAMWalkerEngine::RequestReadRemovalTask(Alignment*& removal_list) { @@ -226,6 +240,44 @@ void BAMWalkerEngine::RequestReadRemovalTask(Alignment*& removal_list) list_end->next = NULL; } +// Request read removal for target-pileup. +//@TODO: Should depend on first_useful_read_ only. +void ConsensusBAMWalkerEngine::RequestTargetBasedReadRemovalTask(Alignment*& removal_list) +{ + removal_list = alignments_first_; + + Alignment *list_end = removal_list; + + if (positions_in_progress_.empty()){ + while (alignments_first_ and (alignments_first_->read_number < first_useful_read_)){ + if (((int) alignments_first_->alignment.RefID > next_target_->chr) + or (((int) alignments_first_->alignment.RefID == next_target_->chr) + and (alignments_first_->end >= (long int) next_target_->begin))){ + break; + } + list_end = alignments_first_; + alignments_first_ = alignments_first_->next; + } + } + else{ + while (alignments_first_ and (alignments_first_->read_number < first_useful_read_)){ + if (((int) alignments_first_->alignment.RefID > positions_in_progress_.begin()->chr) + or (((int) alignments_first_->alignment.RefID == positions_in_progress_.begin()->chr) + and (alignments_first_->end >= (long int) positions_in_progress_.begin()->target_begin))){ + break; + } + list_end = alignments_first_; + alignments_first_ = alignments_first_->next; + } + } + if (list_end == removal_list) + removal_list = NULL; + else + list_end->next = NULL; +} + + + void BAMWalkerEngine::openDepth(const string& depth_file) { pthread_mutex_init(&mutexdepth, NULL); depth_out.open(depth_file.c_str(), ofstream::out); prevRefID = -1; prevEndPos = -1; @@ -275,6 +327,9 @@ void BAMWalkerEngine::processDepth(BamAlignment& alignment, TargetsManager* targ // back up a couple of targets because reads can span multiple targets. if (curr_target > targets_manager->merged.begin()) {--curr_target;} if (curr_target > targets_manager->merged.begin()) {--curr_target;} + int read_count = 1; + alignment.GetTag("ZR", read_count); + for (long int pos = alignment.Position + 1; (pos <= alignment.GetEndPosition()); ++pos) { while (curr_target != targets_manager->merged.end() && (alignment.RefID > curr_target->chr || (alignment.RefID == curr_target->chr && pos - 1 >= curr_target->end))) { ++curr_target; @@ -286,11 +341,11 @@ void BAMWalkerEngine::processDepth(BamAlignment& alignment, TargetsManager* targ std::map ::iterator depth_iter = depth_map.find(pos); if (depth_iter == depth_map.end()) { depth_map[pos] = 0; - if (skip_positions.find(pos) == skip_positions.end()) {depth_map[pos]++;} + if (skip_positions.find(pos) == skip_positions.end()) {depth_map[pos] += read_count;} } else { if (skip_positions.find(pos) == skip_positions.end()) { - depth_iter->second++; + depth_iter->second += read_count; } } pthread_mutex_unlock (&mutexdepth); @@ -330,6 +385,8 @@ bool compare_alignments(Alignment* const A, Alignment* const B) { return result; } + + void BAMWalkerEngine::SaveAlignments(Alignment*& removal_list, VariantCallerContext& vc, vector::iterator& depth_target) { if (removal_list == NULL) {return;} @@ -364,9 +421,13 @@ void BAMWalkerEngine::SaveAlignments(Alignment*& removal_list, VariantCallerCont prevRefID = current_read->alignment.RefID; prevEndPos = current_read->alignment.GetEndPosition(); if (bam_writing_enabled_) { - current_read->alignment.RemoveTag("ZM"); - current_read->alignment.RemoveTag("ZP"); - current_read->alignment.RemoveTag("PG"); + // Remove some tags to make the processed bam file lighter. + const vector unwanted_tags = {"ZM", "ZP", "PG", "ZS"}; + for (vector::const_iterator tag_it = unwanted_tags.begin(); tag_it != unwanted_tags.end(); ++tag_it){ + if (current_read->alignment.HasTag(*tag_it)){ + current_read->alignment.RemoveTag(*tag_it); + } + } bam_writer_.SaveAlignment(current_read->alignment); } } @@ -376,7 +437,6 @@ void BAMWalkerEngine::SaveAlignments(Alignment*& removal_list, VariantCallerCont void BAMWalkerEngine::FinishReadRemovalTask(Alignment* removal_list, int recycle_limit) { pthread_mutex_lock(&recycle_mutex_); - while (removal_list) { Alignment *excess = removal_list; @@ -590,29 +650,28 @@ void BAMWalkerEngine::FinishReadProcessingTask(Alignment* new_read, bool success void BAMWalkerEngine::SetupPositionTicket(list::iterator& position_ticket) const { if (position_ticket->begin == NULL) {return;} +/* Alignment* tmp_begin_ = position_ticket->begin; - while (tmp_begin_ and ( (tmp_begin_->alignment.RefID == next_target_->chr and tmp_begin_->end <= next_position_) or tmp_begin_->alignment.RefID < next_target_->chr) and tmp_begin_->processed) tmp_begin_ = tmp_begin_->next; - Alignment* tmp_end_ = tmp_begin_; - while (tmp_end_ and ( (tmp_end_->alignment.RefID == next_target_->chr and tmp_end_->original_position <= next_position_) or tmp_end_->alignment.RefID < next_target_->chr) and tmp_end_->processed) tmp_end_ = tmp_end_->next; +*/ //positions_in_progress_.push_back(PositionInProgress()); //position_ticket = positions_in_progress_.end(); //--position_ticket; position_ticket->chr = next_target_->chr; position_ticket->pos = next_position_; position_ticket->target_end = next_target_->end; - position_ticket->begin = tmp_begin_; - position_ticket->end = tmp_end_; + //position_ticket->begin = tmp_begin_; + //position_ticket->end = tmp_end_; position_ticket->start_time = time(NULL); //first_excess_read_ = tmp_end_->read_number; } @@ -642,6 +701,7 @@ void BAMWalkerEngine::BeginPositionProcessingTask(list::iter --position_ticket; position_ticket->chr = next_target_->chr; position_ticket->pos = next_position_; + position_ticket->target_begin = next_target_->begin; position_ticket->target_end = next_target_->end; position_ticket->begin = tmp_begin_; position_ticket->end = tmp_end_; @@ -705,7 +765,6 @@ void BAMWalkerEngine::FinishPositionProcessingTask(list::ite } - int BAMWalkerEngine::GetRecentUnmergedTarget() { MergedTarget *my_next_target = next_target_; @@ -716,13 +775,13 @@ int BAMWalkerEngine::GetRecentUnmergedTarget() } -bool BAMWalkerEngine::MemoryContention() +bool BAMWalkerEngine::MemoryContention(int max_num_reads) { if (positions_in_progress_.empty()) return false; if (not alignments_first_) return false; - if (read_counter_ - alignments_first_->read_number < 50000) + if (read_counter_ - alignments_first_->read_number < max_num_reads) return false; return true; } @@ -742,6 +801,148 @@ void BAMWalkerEngine::PrintStatus() << " recycle=" << recycle_size_ << endl; } +void ConsensusBAMWalkerEngine::Initialize(const ReferenceReader& ref_reader, TargetsManager& targets_manager, + const vector& bam_filenames, const string& postprocessed_bam, int px, const string& consensus_bam) +{ + this->BAMWalkerEngine::Initialize(ref_reader, targets_manager, bam_filenames, "", px); // no postprocessed_bam + write_consensus_bam_ = not consensus_bam.empty(); + if (not write_consensus_bam_){ + // It seems that I don't want to output consensus bam. As you wish. + return; + } + pthread_mutex_init(&aln_no_needed_consensus_bam_writer_mutex_, NULL); + pthread_mutex_init(&aln_needed_consensus_bam_writer_mutex_, NULL); + SamHeader tmp_header = bam_header_; + //tmp_header.Comments.clear(); + //tmp_header.Programs.Clear(); + aln_no_needed_consensus_bam_writer_.SetCompressionMode(BamWriter::Compressed); + aln_no_needed_consensus_bam_writer_.SetNumThreads(4); + if (not aln_no_needed_consensus_bam_writer_.Open(consensus_bam + ".aln_not_needed.bam", tmp_header, bam_reader_.GetReferenceData())) { + cerr << "ERROR: Could not open consensus BAM file for writing : " << aln_no_needed_consensus_bam_writer_.GetErrorString(); + exit(1); + } + aln_needed_consensus_bam_writer_.SetCompressionMode(BamWriter::Compressed); + aln_needed_consensus_bam_writer_.SetNumThreads(4); + if (not aln_needed_consensus_bam_writer_.Open(consensus_bam + ".aln_needed.bam", tmp_header, bam_reader_.GetReferenceData())) { + cerr << "ERROR: Could not open consensus BAM file for writing : " << aln_needed_consensus_bam_writer_.GetErrorString(); + exit(1); + } +} + +void ConsensusBAMWalkerEngine::Close() +{ + this->BAMWalkerEngine::Close(); + if (write_consensus_bam_){ + aln_no_needed_consensus_bam_writer_.Close(); + aln_needed_consensus_bam_writer_.Close(); + } + pthread_mutex_destroy(&aln_no_needed_consensus_bam_writer_mutex_); + pthread_mutex_destroy(&aln_needed_consensus_bam_writer_mutex_); +} +void ConsensusBAMWalkerEngine::SaveConsensusAlignments(Alignment* const &read_list, Alignment* const &aln_needed_read_list) +{ + if (not write_consensus_bam_){ + return; + } + if (read_list != NULL){ + // Process linked list + pthread_mutex_lock (&aln_no_needed_consensus_bam_writer_mutex_); + for (Alignment *current_read = read_list; current_read; current_read = current_read->next) { + if (current_read->filtered) + continue; + aln_no_needed_consensus_bam_writer_.SaveAlignment(current_read->alignment); + } + pthread_mutex_unlock (&aln_no_needed_consensus_bam_writer_mutex_); + } + if (aln_needed_read_list != NULL){ + pthread_mutex_lock (&aln_needed_consensus_bam_writer_mutex_); + // Process linked list + for (Alignment *current_read = aln_needed_read_list; current_read; current_read = current_read->next) { + if (current_read->filtered) + continue; + aln_needed_consensus_bam_writer_.SaveAlignment(current_read->alignment); + } + pthread_mutex_unlock (&aln_needed_consensus_bam_writer_mutex_); + } +} + +// Generate the list of reads that cover the target +void ConsensusBAMWalkerEngine::BeginTargetProcessingTask(list::iterator& target_ticket){ + if (not tmp_begin_) + tmp_begin_ = alignments_first_; + long int target_start = next_target_->begin; + long int target_end = next_target_->end; + + while (tmp_begin_ and ( + (tmp_begin_->alignment.RefID == next_target_->chr and tmp_begin_->end <= target_start) + or tmp_begin_->alignment.RefID < next_target_->chr) + and tmp_begin_->processed) + tmp_begin_ = tmp_begin_->next; + + if (not tmp_end_) + tmp_end_ = tmp_begin_; + + while (tmp_end_ and ( + (tmp_end_->alignment.RefID == next_target_->chr and tmp_end_->original_position < target_end) + or tmp_end_->alignment.RefID < next_target_->chr) + and tmp_end_->processed) + tmp_end_ = tmp_end_->next; + + positions_in_progress_.push_back(PositionInProgress()); + target_ticket = positions_in_progress_.end(); + --target_ticket; + target_ticket->chr = next_target_->chr; + target_ticket->pos = next_position_; + target_ticket->target_begin = next_target_->begin; + target_ticket->target_end = next_target_->end; + target_ticket->begin = tmp_begin_; + target_ticket->end = tmp_end_; + target_ticket->start_time = time(NULL); + + + first_excess_read_ = tmp_end_->read_number; +} + +// Count how many reads that use the program for alignment. +void BAMWalkerEngine::AddReadToPG(Alignment *rai){ + string pg_of_read; + if (not rai->alignment.GetTag("PG", pg_of_read)) { + cerr << "ERROR: The PG tag is not present in read " << rai->alignment.Name << endl; + exit(1); + } + pair< map::iterator, bool> pg_finder; + pg_finder = read_counts_of_pg_.insert(pair(pg_of_read, 0)); + ++pg_finder.first->second; +} + +// return true if success, else false. +// Get the most popular PG that aligns the bam file, and it must be tmap. +bool BAMWalkerEngine::GetMostPopularTmap(SamProgram& most_popular_tmap){ + string most_popular_pg; + int current_popular_count = -1; + for (map::iterator it = read_counts_of_pg_.begin(); it != read_counts_of_pg_.end(); ++it){ + if ((int) it->second > current_popular_count){ + most_popular_pg = it->first; + } + } + if (most_popular_pg.substr(0, 4) != "tmap"){ + cerr << "ERROR: The most popular program (PG) that aligns the bam file is not tmap! See the header for more details." << endl; + for (map::iterator it = read_counts_of_pg_.begin(); it != read_counts_of_pg_.end(); ++it){ + cerr << " PG: "<< it->first << " , read count: "<< it->second << endl; + } + return false; + } + + for (SamProgramIterator I = bam_header_.Programs.Begin(); I != bam_header_.Programs.End(); ++I) { + if (I->HasID() and I->HasCommandLine()){ + if (I->ID == most_popular_pg){ + most_popular_tmap = *I; + return true; + } + } + } + return false; +} diff --git a/Analysis/VariantCaller/BAMWalkerEngine.h b/Analysis/VariantCaller/BAMWalkerEngine.h index cb7c6db4..5e7f30b7 100644 --- a/Analysis/VariantCaller/BAMWalkerEngine.h +++ b/Analysis/VariantCaller/BAMWalkerEngine.h @@ -49,7 +49,7 @@ struct Allele { // structure to encapsulate registered reads and alleles struct Alignment { - Alignment() { Reset(); } + Alignment() { Reset(); read_count = 1;} void Reset() { next = NULL; read_number = 0; @@ -67,10 +67,12 @@ struct Alignment { refmap_code.clear(); refmap_has_allele.clear(); refmap_allele.clear(); + target_coverage_indices.clear(); is_reverse_strand = false; measurements.clear(); measurements_length = 0; phase_params.clear(); + measurements_sd.clear(); runid.clear(); well_rowcol.clear(); read_bases.clear(); @@ -86,8 +88,9 @@ struct Alignment { flow_order_index = -1; read_group.clear(); prefix_bases.clear(); + suffix_bases.clear(); tag_info.Clear(); - read_count = 1; +// read_count = 1; } BamAlignment alignment; //! Raw BamTools alignment @@ -114,12 +117,14 @@ struct Alignment { vector refmap_code; vector refmap_has_allele; vector refmap_allele; + vector target_coverage_indices; // Candidate evaluator information bool is_reverse_strand; //! Indicates whether read is from the forward or reverse strand vector measurements; //! The measurement values for this read blown up to the length of the flow order int measurements_length;//! Original trimmed length of the ZM measurements vector vector phase_params; //! cf, ie, droop parameters of this read + vector measurements_sd; //! The standard deviation of measurements for consensus reads string runid; //! Identify the run from which this read came: used to find run-specific parameters vector well_rowcol; //! 2 element int vector 0-based row, col in that order mapping to row,col in chip string read_bases; //! Read sequence as base called (minus hard but including soft clips) @@ -135,6 +140,7 @@ struct Alignment { short flow_order_index; //! Index of the flow order belonging to this read string read_group; //! Read group of this read string prefix_bases; //! hard clipped start of the read + string suffix_bases; //! hard clipped end of the read // Post-processing information vector old_cigar; //! Cigar information before primer trimming @@ -146,6 +152,7 @@ struct PositionInProgress { int chr; //! Chromosome index of this variant position long pos; //! Position within chromosome + long target_begin; //! Begin of current target region long target_end; //! End of current target region Alignment * begin; //! First read covering this position Alignment * end; //! Last read coverint this position @@ -164,7 +171,7 @@ class BAMWalkerEngine { BAMWalkerEngine(); ~BAMWalkerEngine(); void Initialize(const ReferenceReader& ref_reader, TargetsManager& targets_manager, - const vector& bam_filenames, const string& postprocessed_bami, int px); + const vector& bam_filenames, const string& postprocessed_bam, int px); void Close(); const SamHeader& GetBamHeader() { return bam_header_; } @@ -174,7 +181,7 @@ class BAMWalkerEngine { bool ReadyForNextPosition(); // Memory contention prevention - bool MemoryContention(); + bool MemoryContention(int max_num_reads = 50000); bool IsEarlierstPositionProcessingTask(list::iterator& position_ticket); // Loading new reads @@ -203,47 +210,30 @@ class BAMWalkerEngine { basecaller_version = basecaller_version_; tmap_version = tmap_version_; } + void AddReadToPG(Alignment *rai); + bool GetMostPopularTmap(SamProgram& most_popular_tmap); + void processDepth(BamAlignment& alignment, TargetsManager* targets_manager, vector::iterator& curr_target); void openDepth(const string& filename); void writeDepth(ReferenceReader* reference_reader, std::map::size_type offset = 0); void closeDepth(ReferenceReader* reference_reader); + int getChrIndex() const {return next_target_->chr;} + long int getStartPosition() const {return next_target_->begin;} + long int getEndPosition() const {return next_target_->end;} + long int getPosition() const {return next_position_;} + Alignment * alignments_first_; //! First in a list of all alignments in memory private: void InitializeBAMs(const ReferenceReader& ref_reader, const vector& bam_filenames); TargetsManager * targets_manager_; //! Manages targets loaded from BED file - BamMultiReader bam_reader_; //! BamTools mulit-bam reader - SamHeader bam_header_; //! Bam header string basecaller_version_; //! BaseCaller version retrieved from BAM header string tmap_version_; //! TMAP version retrieved from BAM header - MergedTarget * next_target_; //! Target containing next position - long int next_position_; //! Next position (chr in the target) - - int last_processed_chr_; //! Reads up to this chr+pos are guaranteed to be processed - long int last_processed_pos_; //! Reads up to this chr+pos are guaranteed to be processed - bool has_more_alignments_; //! Are there still more reads in BAM? - bool has_more_positions_; //! Are there still more positions within the target region to process? -public: - Alignment * alignments_first_; //! First in a list of all alignments in memory -private: - Alignment * alignments_last_; //! Last in a list of all alignments in memory - int read_counter_; //! Total # of reads retrieved so far - Alignment * recycle_; //! Stack of allocated, reusable Alignment objects int recycle_size_; //! Size of the the recycle stack pthread_mutex_t recycle_mutex_; //! Mutex controlling access to the recycle stack - Alignment * tmp_begin_; //! Starts read window of most recent position task - Alignment * tmp_end_; //! Ends read window of most recent position task - - Alignment * processing_first_; //! First in a list of alignments being processed - Alignment * processing_last_; //! Last in a list of alignments being processed - list positions_in_progress_; //! List of positions being processed - int first_excess_read_; //! Index of the earliest read beyond the current position - - int first_useful_read_; //! Index of the earliest read that may still be in use - bool bam_writing_enabled_; BamWriter bam_writer_; @@ -260,8 +250,50 @@ class BAMWalkerEngine { int prevRefID; long int prevEndPos; +protected: + BamMultiReader bam_reader_; //! BamTools mulit-bam reader + SamHeader bam_header_; //! Bam header + + MergedTarget * next_target_; //! Target containing next position + long int next_position_; //! Next position (chr in the target) + + int last_processed_chr_; //! Reads up to this chr+pos are guaranteed to be processed + long int last_processed_pos_; //! Reads up to this chr+pos are guaranteed to be processed + bool has_more_alignments_; //! Are there still more reads in BAM? + bool has_more_positions_; //! Are there still more positions within the target region to process? + Alignment * alignments_last_; //! Last in a list of all alignments in memory + int read_counter_; //! Total # of reads retrieved so far + + Alignment * tmp_begin_; //! Starts read window of most recent position task + Alignment * tmp_end_; //! Ends read window of most recent position task + + Alignment * processing_first_; //! First in a list of alignments being processed + Alignment * processing_last_; //! Last in a list of alignments being processed + list positions_in_progress_; //! List of positions being processed + int first_excess_read_; //! Index of the earliest read beyond the current position + + int first_useful_read_; //! Index of the earliest read that may still be in use + map read_counts_of_pg_; //! Count how many reads that use the program for alignment. }; +class ConsensusBAMWalkerEngine : public BAMWalkerEngine +{ +private: + BamWriter aln_no_needed_consensus_bam_writer_; + BamWriter aln_needed_consensus_bam_writer_; + pthread_mutex_t aln_no_needed_consensus_bam_writer_mutex_; + pthread_mutex_t aln_needed_consensus_bam_writer_mutex_; + bool write_consensus_bam_ = true; +public: + ConsensusBAMWalkerEngine() : BAMWalkerEngine(){}; + void SaveConsensusAlignments(Alignment* const & read_list, Alignment* const & aln_needed_read_list); + void Initialize(const ReferenceReader& ref_reader, TargetsManager& targets_manager, + const vector& bam_filenames, const string& postprocessed_bam, int px, const string& consensus_bam); + void Close(); + void RequestTargetBasedReadRemovalTask(Alignment*& removal_list); + void BeginTargetProcessingTask(list::iterator& position_ticket); + bool EligibleForTargetBasedReadRemoval(); +}; #endif //BAMWALKERENGINE_H diff --git a/Analysis/VariantCaller/Bookkeeping/ExtendParameters.cpp b/Analysis/VariantCaller/Bookkeeping/ExtendParameters.cpp index ab8411fc..d1030656 100644 --- a/Analysis/VariantCaller/Bookkeeping/ExtendParameters.cpp +++ b/Analysis/VariantCaller/Bookkeeping/ExtendParameters.cpp @@ -1,7 +1,6 @@ /* Copyright (C) 2012 Ion Torrent Systems, Inc. All Rights Reserved */ #include "ExtendParameters.h" -#include #include #include #include @@ -18,13 +17,17 @@ void VariantCallerHelp() { printf("Usage: tvc [options]\n"); printf("\n"); + printf("Flow space consensus:\n"); + printf(" consensus generate flow space consensus reads by re-basecalling reads that are flow-synchronized\n"); + printf("\n"); + printf("General options:\n"); printf(" -h,--help print this help message and exit\n"); printf(" -v,--version print version and exit\n"); printf(" -n,--num-threads INT number of worker threads [2]\n"); printf(" -N,--num-variants-per-thread INT worker thread batch size [500]\n"); printf(" --parameters-file FILE json file with algorithm control parameters [optional]\n"); - printf(" --do-indel-assembly on/off do indel assembly to call long indel variants [on]\n"); + printf(" --do-indel-assembly on/off use indel assembler to call long indel variants [on]\n"); printf("\n"); printf("Inputs:\n"); @@ -34,17 +37,21 @@ void VariantCallerHelp() { printf(" --force-sample-name STRING force all read groups to have this sample name [off]\n"); printf(" -t,--target-file FILE only process targets in this bed file [optional]\n"); printf(" --trim-ampliseq-primers on/off match reads to targets and trim the ends that reach outside them [off]\n"); - printf(" -D,--downsample-to-coverage INT ?? [2000]\n"); + printf(" -D,--downsample-to-coverage INT downsample reads to this value for flow-space evaluation [2000]\n"); printf(" --model-file FILE HP recalibration model input file.\n"); printf(" --recal-model-hp-thres INT Lower threshold for HP recalibration.\n"); printf("\n"); printf("Outputs:\n"); printf(" -O,--output-dir DIRECTORY base directory for all output files [current dir]\n"); - printf(" -o,--output-vcf FILE vcf file with variant calling results [required]\n"); + printf(" -o,--output-vcf FILE vcf file with small variant calling results [small_variants.vcf]\n"); + printf(" --assembly-vcf FILE vcf file with indel assembly results [indel_assembly.vcf]\n"); + //printf(" --merged-vcf FILE merged and post processed vcf file [TSVC_variants.vcf]\n"); printf(" --suppress-reference-genotypes on/off write reference calls into the filtered variants vcf [on]\n"); printf(" --suppress-no-calls on/off write filtered variants into the filtered variants vcf [on]\n"); printf(" --suppress-nocall-genotypes on/off do not report a genotype for filtered variants [on]\n"); + printf(" --report-ppa on/off report Possible Polyploidy Alleles (PPA) [off]\n"); + printf(" --candidate-list-file FILE list all the novel candidates generated by freebayes [optional]\n"); printf("\n"); printf("Variant candidate generation (FreeBayes):\n"); @@ -54,17 +61,22 @@ void VariantCallerHelp() { printf(" --allow-complex on/off allow generation of block substitution candidates [off]\n"); printf(" --max-complex-gap INT maximum number of reference alleles between two alternate alleles to allow merging of the alternate alleles [1]\n"); printf(" -m,--use-best-n-alleles INT maximum number of snp alleles [2]\n"); + printf(" --use-best-n-nonsnp-alleles INT maximum number of non-snp alleles [0, no limit]\n"); printf(" -M,--min-mapping-qv INT do not use reads with mapping quality below this [4]\n"); printf(" -U,--read-snp-limit INT do not use reads with number of snps above this [10]\n"); + printf(" --read-mismatch-limit INT do not use reads with number of mismatches (where 1 gap open counts 1) above this value (0 to disable this option) [0]\n"); + printf(" --min-cov-fraction FLOAT do not use reads with fraction of covering the best assigned (unmerged) target region below this [0.0]\n"); printf(" -z,--read-max-mismatch-fraction FLOAT do not use reads with fraction of mismatches above this [1.0]\n"); printf(" --gen-min-alt-allele-freq FLOAT minimum required alt allele frequency to generate a candidate [0.2]\n"); - printf(" --gen-min-indel-alt-allele-freq FLOAT minimum required alt allele frequency to generate a homopolymer indel candidate [0.2]\n"); + printf(" --gen-min-indel-alt-allele-freq FLOAT minimum required alt allele frequency to generate an indel candidate [0.2]\n"); printf(" --gen-min-coverage INT minimum required coverage to generate a candidate [6]\n"); - printf(" --merge-variant-lookahead INT how many bases ahead to merge nearby variant to form correct haplotype [3, 0 if not allow complex]\n"); + printf(" --max-alt-num INT try to break the variant if the number of alt alleles is greater than this value. [20]\n"); + printf(" --merge-variant-lookahead INT how many bases ahead to merge nearby variant to form correct haplotype [-1 use auto-adapt, 0 if not allow complex]\n"); + printf(" --allele-cigar-output on/off output cigar for alt alleles [off]\n"); printf("\n"); printf("External variant candidates:\n"); - printf(" -l,--blacklist-vcf FILE vcf.gz file (+.tbi) with blacklist candidate variant locations and alleles [optional]\n"); + printf(" -l,--sse-vcf FILE vcf.gz file (+.tbi) with sse candidate variant locations and alleles [optional]\n"); printf(" -c,--input-vcf FILE vcf.gz file (+.tbi) with additional candidate variant locations and alleles [optional]\n"); printf(" --process-input-positions-only on/off only generate candidates at locations from input-vcf [off]\n"); printf(" --use-input-allele-only on/off only consider provided alleles for locations in input-vcf [off]\n"); @@ -74,7 +86,8 @@ void VariantCallerHelp() { printf(" --min-delta-for-flow FLOAT minimum prediction delta for scoring flows [0.1]\n"); printf(" --max-flows-to-test INT maximum number of scoring flows [10]\n"); printf(" --outlier-probability FLOAT probability for outlier reads [0.01]\n"); - printf(" --heavy-tailed INT degrees of freedom in t-dist modeling signal residual heavy tail [3]\n"); + printf(" --heavy-tailed INT (2*this value-1) is the Degrees of Freedom (DoF) in t-dist modeling signal residual heavy tail [3]\n"); + printf(" --adjust-sigma on/off It true, use sigma^2=(DoF-2)/Dof*VAR(residual) for calculating the t-dist log-likelihood, else use sigma^2=VAR(residual) [off]\n"); printf(" --suppress-recalibration on/off Suppress homopolymer recalibration [on].\n"); printf(" --do-snp-realignment on/off Realign reads in the vicinity of candidate snp variants [on].\n"); printf(" --do-mnp-realignment on/off Realign reads in the vicinity of candidate mnp variants [do-snp-realignment].\n"); @@ -93,22 +106,75 @@ void VariantCallerHelp() { printf(" --sse-relative-safety-level FLOAT dampen strand bias detection for SSE events for low coverage [0.025]\n"); printf(" --tune-sbias FLOAT dampen strand bias detection for low coverage [0.01]\n"); printf(" --max-detail-level INT number of evaluated frequencies for a given hypothesis, reduce for very high coverage, set to zero to disable this option [0]\n"); - printf(" --min-detail-level-for-fast-scan INT minimum detail level to trigger the fast scan for log-posterior of frequencies [2500]\n"); + printf(" --min-detail-level-for-fast-scan INT minimum detail level to trigger the fast scan for log-posterior of frequencies [0]\n"); printf(" --try-few-restart-freq on/off speed up the evaluator by trying fewer initial guesses of allele frequency [off].\n"); printf("\n"); printf("Variant filtering:\n"); // Filters depending on the variant type - printf(" -k,--snp-min-coverage INT filter out snps with total coverage below this [6]\n"); - printf(" -C,--snp-min-cov-each-strand INT filter out snps with coverage on either strand below this [0]\n"); - printf(" -B,--snp-min-variant-score FLOAT filter out snps with QUAL score below this [10.0]\n"); - printf(" -s,--snp-strand-bias FLOAT filter out snps with strand bias above this [0.95] given strand bias > snp-strand-bias\n"); + printf(" --use-fd-param on/off use flow-disruptiveness to choose parameter set. If on, map inputed (INDEL,SNP,MNP) to (fd-0,fd-5,fd-10) if fd parameters not specified [off]\n"); + printf(" --min-ratio-for-fd FLOAT claim flow-disruption if the portion of reads that are flow-disrupted >= this value [0.1]\n"); + printf(" --fd-nonsnp-min-var-cov INT override min-var-coverage of the flow-disrupted variants that are not snp (0 to disable the override) [0]\n"); + printf(" --hotspots-as-de-novo on/off treat hotspots as de novo during filtering [off]\n"); + // Filters not depending on the variant score + printf(" -L,--hp-max-length INT filter out indels in homopolymers above this [8]\n"); + printf(" --hp-indel-hrun INT VECTOR define the HRUN for filtering HP-INDEL variants [7,8]\n"); + printf(" --hp-ins-len INT VECTOR filter out HP-INS variants whose INS length <= the corresponding entry of this vector if the HRUN is defined in hp-indel-hrun [0,0]\n"); + printf(" --hp-del-len INT VECTOR filter out HP-DEL variants whose DEL length <= the corresponding entry of this vector if the HRUN is defined in hp-indel-hrun [0,0]\n"); + printf(" --error-motifs-dir FILE directory where the error-motifs files can be found [optional]\n"); + printf(" -e,--error-motifs FILE table of systematic error motifs and their error rates [optional]\n"); + printf(" --sse-prob-threshold FLOAT filter out variants in motifs with error rates above this [0.2]\n"); + printf(" --min-ratio-reads-non-sse-strand FLOAT minimum required alt allele frequency for variants with error motifs on opposite strand [0.2]\n"); + printf(" --use-lod-filter on/off enable the Limit Of Detection (LOD) filter [off]\n"); + printf(" --lod-multiplier FLOAT multiplier of LOD for filtering out variants with low alt allele frequency [0.6]\n"); + printf(" --tag-sim-max-cov INT check the similarity of variant molecular tags if the variant molecular coverage <= this value [20]\n"); + printf(" --cleanup-unlikely-candidates on/off remove unlikely variant candidates from the vcf records if there is a highly likely variant presented (similar to heal-snps) [on].\n"); + + printf("\nVariant filtering for hotspot alleles (default X = fd-10 if use-fd-param else X = snp):\n"); + printf(" --hotspot-min-allele-freq FLOAT minimum required alt allele frequency for hotspot variant call [X-min-allele-freq]\n"); + printf(" --hotspot-min-coverage INT filter out hotspot variants with total coverage below this [X-min-coverage]\n"); + printf(" --hotspot-min-cov-each-strand INT filter out hotspot variants with coverage on either strand below this [X-min-cov-each-strand]\n"); + printf(" --hotspot-min-var-coverage INT filter out hotspot variants with hard-classified variant allele coverage below this [X-min-cov-each-strand]\n"); + printf(" --hotspot-min-variant-score FLOAT filter out hotspot variants with QUAL score below this [X-min-variant-score]\n"); + printf(" --hotspot-strand-bias FLOAT filter out hotspot variants with strand bias above this [X-strand-bias] given strand bias > hotspot-strand-bias\n"); + printf(" --hotspot-strand-bias-pval FLOAT filter out hotspot variants with pval below this [X-strand-bias-pval] given pval < hotspot-strand-bias-pval\n"); + + printf("\nVariant filtering for fd-0 (non-flow-disrupted, i.e., HP-INDEL) alleles:\n"); + printf(" --fd-0-min-allele-freq FLOAT minimum required alt allele frequency for fd-0 variant call [0.2]\n"); + printf(" --fd-0-min-coverage INT filter out fd-0 variants with total coverage below this [30]\n"); + printf(" --fd-0-min-cov-each-strand INT filter out fd-0 variants with coverage on either strand below this [1]\n"); + printf(" --fd-0-min-var-coverage INT filter out fd-0 variants with hard-classified variant allele coverage below this [0]\n"); + printf(" --fd-0-min-variant-score FLOAT filter out fd-0 variants with QUAL score below this [10.0]\n"); + printf(" --fd-0-strand-bias FLOAT filter out fd-0 variants with strand bias above this [0.95] given strand bias > fd-0-strand-bias\n"); + printf(" --fd-0-strand-bias-pval FLOAT filter out fd-0 variants with pval below this [1.0] given pval < fd-0-strand-bias-pval\n"); + + printf("\nVariant filtering for fd-5 (moderate flow-disrupted) alleles:\n"); + printf(" --fd-5-min-allele-freq FLOAT minimum required alt allele frequency for fd-5 variant call [fd-0-min-allele-freq]\n"); + printf(" --fd-5-min-coverage INT filter out fd-5 variants with total coverage below this [fd-0-min-coverage]\n"); + printf(" --fd-5-min-cov-each-strand INT filter out fd-5 variants with coverage on either strand below this [fd-0-min-cov-each-strand]\n"); + printf(" --fd-5-min-var-coverage INT filter out fd-5 variants with hard-classified variant allele coverage below this [fd-0-min-var-coverage]\n"); + printf(" --fd-5-min-variant-score FLOAT filter out fd-5 variants with QUAL score below this [fd-0-min-variant-score]\n"); + printf(" --fd-5-strand-bias FLOAT filter out fd-5 variants with strand bias above this [fd-0-strand-bias] given strand bias > fd-5-strand-bias\n"); + printf(" --fd-5-strand-bias-pval FLOAT filter out fd-5 variants with pval below this [fd-0-strand-bias-pval] given pval < fd-5-strand-bias-pval\n"); + + printf("\nVariant filtering for fd-10 (most flow-disrupted) alleles:\n"); + printf(" --fd-10-min-allele-freq FLOAT minimum required alt allele frequency for fd-10 variant call [0.2]\n"); + printf(" --fd-10-min-coverage INT filter out fd-10 variants with total coverage below this [6]\n"); + printf(" --fd-10-min-cov-each-strand INT filter out fd-10 variants with coverage on either strand below this [0]\n"); + printf(" --fd-10-min-var-coverage INT filter out fd-10 variants with hard-classified variant allele coverage below this [0]\n"); + printf(" --fd-10-min-variant-score FLOAT filter out fd-10 variants with QUAL score below this [10.0]\n"); + printf(" --fd-10-strand-bias FLOAT filter out fd-10 variants with strand bias above this [0.95] given strand bias > fd-10-strand-bias\n"); + printf(" --fd-10-strand-bias-pval FLOAT filter out fd-10 variants with pval below this [1.0] given pval < fd-10-strand-bias-pval\n"); + + printf("\nParameters that will be deprecated in the future:\n"); + printf(" --snp-min-coverage INT filter out snps with total coverage below this [6]\n"); + printf(" --snp-min-cov-each-strand INT filter out snps with coverage on either strand below this [0]\n"); + printf(" --snp-min-variant-score FLOAT filter out snps with QUAL score below this [10.0]\n"); + printf(" --snp-strand-bias FLOAT filter out snps with strand bias above this [0.95] given strand bias > snp-strand-bias\n"); printf(" --snp-strand-bias-pval FLOAT filter out snps with pval below this [1.0] given pval < snp-strand-bias-pval\n"); - // printf(" -s,--snp-strand-bias FLOAT filter out snps with strand bias above this [0.95]\n"); - printf(" -A,--snp-min-allele-freq FLOAT minimum required alt allele frequency for non-reference snp calls [0.2]\n"); + printf(" --snp-min-allele-freq FLOAT minimum required alt allele frequency for non-reference snp calls [0.2]\n"); printf(" --snp-min-var-coverage INT filter out snps with variant allele coverage below this [0]\n"); - printf(" --mnp-min-coverage INT filter out mnps with total coverage below this [snp-min-coverage]\n"); printf(" --mnp-min-cov-each-strand INT filter out mnps with coverage on either strand below this, [snp-min-cov-each-strand]\n"); printf(" --mnp-min-variant-score FLOAT filter out mnps with QUAL score below this [snp-min-variant-score]\n"); @@ -116,33 +182,15 @@ void VariantCallerHelp() { printf(" --mnp-strand-bias-pval FLOAT filter out mnps with pval below this [snp-strand-bias-pval] given pval < mnp-strand-bias-pval\n"); printf(" --mnp-min-allele-freq FLOAT minimum required alt allele frequency for non-reference mnp calls [snp-min-allele-freq]\n"); printf(" --mnp-min-var-coverage INT filter out mnps with variant allele coverage below this [snp-min-var-coverage]\n"); - printf(" --indel-min-coverage INT filter out indels with total coverage below this [30]\n"); printf(" --indel-min-cov-each-strand INT filter out indels with coverage on either strand below this [1]\n"); printf(" --indel-min-variant-score FLOAT filter out indels with QUAL score below this [10.0]\n"); - printf(" -S,--indel-strand-bias FLOAT filter out indels with strand bias above this [0.95] given strand bias > indel-strand-bias\n"); + printf(" --indel-strand-bias FLOAT filter out indels with strand bias above this [0.95] given strand bias > indel-strand-bias\n"); printf(" --indel-strand-bias-pval FLOAT filter out indels with pval below this [1.0] given pval < indel-strand-bias-pval\n"); - // printf(" -S,--indel-strand-bias FLOAT filter out indels with strand bias above this [0.85]\n"); printf(" --indel-min-allele-freq FLOAT minimum required alt allele frequency for non-reference indel call [0.2]\n"); printf(" --indel-min-var-coverage INT filter out indels with variant allele coverage below this [snp-min-var-coverage]\n"); - - printf(" --hotspot-min-coverage INT filter out hotspot variants with total coverage below this [6]\n"); - printf(" --hotspot-min-cov-each-strand INT filter out hotspot variants with coverage on either strand below this [snp-min-cov-each-strand]\n"); - printf(" --hotspot-min-variant-score FLOAT filter out hotspot variants with QUAL score below this [snp-min-variant-score]\n"); - printf(" --hotspot-strand-bias FLOAT filter out hotspot variants with strand bias above this [0.95] given strand bias > hotspot-strand-bias\n"); - printf(" --hotspot-strand-bias-pval FLOAT filter out hotspot variants with pval below this [1.0] given pval < hotspot-strand-bias-pval\n"); - // printf(" --hotspot-strand-bias FLOAT filter out hotspot variants with strand bias above this [0.95]\n"); - printf(" -H,--hotspot-min-allele-freq FLOAT minimum required alt allele frequency for non-reference hotspot variant call [0.2]\n"); - printf(" --hotspot-min-var-coverage INT filter out hotspot variants with variant allele coverage below this [snp-min-var-coverage]\n"); - - // Filters not depending on the variant score - printf(" -L,--hp-max-length INT filter out indels in homopolymers above this [8]\n"); - printf(" -e,--error-motifs FILE table of systematic error motifs and their error rates [optional]\n"); - printf(" --sse-prob-threshold FLOAT filter out variants in motifs with error rates above this [0.2]\n"); - printf(" --min-ratio-reads-non-sse-strand FLOAT minimum required alt allele frequency for variants with error motifs on opposite strand [0.2]\n"); printf(" --indel-as-hpindel on/off apply indel filters to non HP indels [off]\n"); - printf(" --use-lod-filter on/off enable the Limit Of Detection (LOD) filter [off]\n"); - printf(" --lod-multiplier FLOAT multiplier of LOD for filtering out variants with low alt allele frequency [0.6]\n"); + printf(" --heal-snps on/off suppress in/dels not participating in diploid variant genotypes if the genotype contains a SNP or MNP [on].\n"); // position-bias filter printf("\nPosition bias variant filtering:\n"); @@ -157,26 +205,51 @@ void VariantCallerHelp() { printf(" --filter-unusual-predictions FLOAT posterior log likelihood threshold for accepting bias estimate [0.3]\n"); printf(" --filter-deletion-predictions FLOAT check post-evaluation systematic bias in deletions; a high value like 100 effectively turns off this filter [100.0]\n"); printf(" --filter-insertion-predictions FLOAT check post-evaluation systematic bias in insertions; a high value like 100 effectively turns off this filter [100.0]\n"); - printf("\n"); - printf(" --heal-snps on/off suppress in/dels not participating in diploid variant genotypes if the genotype contains a SNP or MNP [on].\n"); + // XXX Not applicable in TS 5.4 since merging and post processing is not run in TVC + //printf("\nVCF record filters (applied during merge):\n"); + //printf(" --filter-by-target on/off Filter vcf records by meta information in the target bed file [on]\n"); + //printf(" --hotspot-positions-only on/off Report only hotspot vcf records in final output [off]\n"); + //printf(" --hotspot-variants-only on/off Suppress hotspot records that are no-calls or reference-calls [off]\n"); printf("\n"); MolecularTagTrimmer::PrintHelp(true); + printf(" --indel-func-size-offset INT require family of size >= (min-tag-fam-size + this value) to be functional when calling HP-INDEL [0]\n"); + printf("\n"); + printf("Debugging:\n"); - printf(" -d,--debug INT (0/1/2) display extra debug messages [0]\n"); + printf(" -d,--debug INT (0/1/2/3) display extra debug messages [0]\n"); printf(" --do-json-diagnostic on/off (devel) dump internal state to json file (uses much more time/memory/disk) [off]\n"); printf(" --postprocessed-bam FILE (devel) save tvc-processed reads to an (unsorted) BAM file [optional]\n"); printf(" --do-minimal-diagnostic on/off (devel) provide minimal read information for called variants [off]\n"); printf(" --override-limits on/off (devel) disable limit-check on input parameters [off].\n"); + printf(" --disable-filters on/off (devel) disable most of the filtering steps for the alleles [off].\n"); printf(" --output-multi-min-allele-freq on/off output the inference results for multiple min-allele-freq in vcf [off].\n"); - printf(" --snp-multi-min-allele-freq FLOAT VECTOR multiple min-allele-freq for snp calls [0.05,0.1,0.15,0.2].\n"); - printf(" --mnp-multi-min-allele-freq FLOAT VECTOR multiple min-allele-freq for mnp calls [snp-multi-min-allele-freq].\n"); - printf(" --indel-multi-min-allele-freq FLOAT VECTOR multiple min-allele-freq for indel calls [0.05,0.1,0.15,0.2].\n"); - printf(" --hotspot-multi-min-allele-freq FLOAT VECTOR multiple min-allele-freq for hotspot calls [0.05,0.1,0.15,0.2].\n"); + printf(" --multi-min-allele-freq FLOAT VECTOR output QUAL, GT, GQ using the min-allele-freq specified in the vector [0.05,0.1,0.15,0.2].\n"); printf("\n"); } +int mkpath(std::string s,mode_t mode) +{ + size_t pre=0,pos; + std::string dir; + int mdret = 0; + + if(s[s.size()-1]!='/'){ + // force trailing / so we can handle everything in loop + s+='/'; + } + + while((pos=s.find_first_of('/',pre))!=std::string::npos){ + dir=s.substr(0,pos++); + pre=pos; + if(dir.size()==0) continue; // if leading / first time is 0 length + if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){ + return mdret; + } + } + return mdret; +} ControlCallAndFilters::ControlCallAndFilters() { // all defaults handled by sub-filters @@ -191,6 +264,13 @@ ControlCallAndFilters::ControlCallAndFilters() { use_lod_filter = false; lod_multiplier = 0.6f; + tag_sim_max_cov = 20; + + // VCF record filters (applied during vcf merging) XXX + //filter_by_target = true; // Filter records based on mets information in target bed info field + //hotspot_positions_only = false; // Output only vcf lines with the infoFlag 'HS' + //hotspot_variants_only = false; // Suppress hotspot reference calls and no-calls from the final output vcf + //xbias_tune = 0.005f; sbias_tune = 0.5f; downSampleCoverage = 2000; @@ -199,7 +279,15 @@ ControlCallAndFilters::ControlCallAndFilters() { suppress_reference_genotypes = true; suppress_nocall_genotypes = true; suppress_no_calls = true; - heal_snps = true; + cleanup_unlikely_candidates = true; + report_ppa = false; + hotspots_as_de_novo = false; + disable_filters = false; + + // Flow-disruption parameters + fd_nonsnp_min_var_cov = 0; + use_fd_param = false; + min_ratio_for_fd = 0.1f; } ProgramControlSettings::ProgramControlSettings() { @@ -207,7 +295,6 @@ ProgramControlSettings::ProgramControlSettings() { nThreads = 1; DEBUG = 0; do_indel_assembly = true; - #ifdef __SSE3__ use_SSE_basecaller = true; #else @@ -215,313 +302,14 @@ ProgramControlSettings::ProgramControlSettings() { #endif rich_json_diagnostic = false; minimal_diagnostic = false; - json_plot_dir = "./json_diagnostic/"; inputPositionsOnly = false; suppress_recalibration = true; resolve_clipped_bases = false; is_multi_min_allele_freq = false; - snp_multi_min_allele_freq.clear(); - mnp_multi_min_allele_freq.clear(); - indel_multi_min_allele_freq.clear(); - hotspot_multi_min_allele_freq.clear(); -} - - -int GetParamsInt(Json::Value& json, const string& key, int default_value) { - if (not json.isMember(key)) - return default_value; - if (json[key].isString()) - return atoi(json[key].asCString()); - return json[key].asInt(); -} - -double GetParamsDbl(Json::Value& json, const string& key, double default_value) { - if (not json.isMember(key)) - return default_value; - if (json[key].isString()) - return atof(json[key].asCString()); - return json[key].asDouble(); + multi_min_allele_freq.clear(); } - -int RetrieveParameterInt(OptArgs &opts, Json::Value& json, char short_name, const string& long_name_hyphens, int default_value) -{ - string long_name_underscores = long_name_hyphens; - for (unsigned int i = 0; i < long_name_underscores.size(); ++i) - if (long_name_underscores[i] == '-') - long_name_underscores[i] = '_'; - - int value = default_value; - string source = "builtin default"; - - if (json.isMember(long_name_underscores)) { - if (json[long_name_underscores].isString()) - value = atoi(json[long_name_underscores].asCString()); - else - value = json[long_name_underscores].asInt(); - source = "parameters json file"; - } - - if (opts.HasOption(short_name, long_name_hyphens)) { - value = opts.GetFirstInt(short_name, long_name_hyphens, value); - source = "command line option"; - } - - cout << setw(35) << long_name_hyphens << " = " << setw(10) << value << " (integer, " << source << ")" << endl; - return value; -} - -double RetrieveParameterDouble(OptArgs &opts, Json::Value& json, char short_name, const string& long_name_hyphens, double default_value) -{ - string long_name_underscores = long_name_hyphens; - for (unsigned int i = 0; i < long_name_underscores.size(); ++i) - if (long_name_underscores[i] == '-') - long_name_underscores[i] = '_'; - - double value = default_value; - string source = "builtin default"; - - if (json.isMember(long_name_underscores)) { - if (json[long_name_underscores].isString()) - value = atof(json[long_name_underscores].asCString()); - else - value = json[long_name_underscores].asDouble(); - source = "parameters json file"; - } - - if (opts.HasOption(short_name, long_name_hyphens)) { - value = opts.GetFirstDouble(short_name, long_name_hyphens, value); - source = "command line option"; - } - - cout << setw(35) << long_name_hyphens << " = " << setw(10) << value << " (double, " << source << ")" << endl; - return value; -} - - -bool RetrieveParameterBool(OptArgs &opts, Json::Value& json, char short_name, const string& long_name_hyphens, bool default_value) -{ - string long_name_underscores = long_name_hyphens; - for (unsigned int i = 0; i < long_name_underscores.size(); ++i) - if (long_name_underscores[i] == '-') - long_name_underscores[i] = '_'; - - bool value = default_value; - string source = "builtin default"; - - if (json.isMember(long_name_underscores)) { - if (json[long_name_underscores].isString()) - value = atoi(json[long_name_underscores].asCString()); - else - value = json[long_name_underscores].asInt(); - source = "parameters json file"; - } - - if (opts.HasOption(short_name, long_name_hyphens)) { - value = opts.GetFirstBoolean(short_name, long_name_hyphens, value); - source = "command line option"; - } - - cout << setw(35) << long_name_hyphens << " = " << setw(10) << (value ? "true" : "false") << " (boolean, " << source << ")" << endl; - return value; -} - -void ConvertToUpper(string &s){ - for(unsigned int i = 0; i -1) - { - s = name.substr(index + 2, name.length() - index - 2); - } - - for (unsigned int i = 0; i < s.size(); ++i) - { - if (s[i] == '-') - s[i] = '_'; - } - - return s; -} - -void Split(const string& s, char c, vector& v) -{ - v.clear(); - if (s != "") { - string::size_type i = 0; - string::size_type j = s.find (c); - if (j == string::npos) - { - v.push_back (s); - } - else - { - while (j != string::npos) - { - v.push_back (s.substr (i, j-i)); - i = ++j; - j = s.find (c,j); - - if (j == string::npos) - { - v.push_back (s.substr (i, s.length())); - } - } - } - } -} - -int RetrieveParameterVectorFloat(OptArgs &opts, Json::Value& json, char short_name, const string& long_name_hyphens, const string& default_value, vector& ret_vector) -{ - string long_name_underscores = GetRidOfDomainAndHyphens(long_name_hyphens); - string value = default_value; - - if(value.length() > 0) - { - vector words; - Split(value,',',words); - ret_vector.clear(); - for (size_t i = 0; i < words.size(); i++) { - char *end; - int err = errno; - errno = 0; - ret_vector.push_back(strtod(words[i].c_str(), &end)); - if (errno != 0 || *end != '\0') { - cout << "Error converting: " + words[i] + " to an float for option: " + long_name_hyphens << endl; - return errno; - } - errno = err; - } - } - string source = "builtin default"; - - if (json.isMember(long_name_underscores)) { - ret_vector.clear(); - size_t sz = json[long_name_underscores].size(); - char buf[1000]; - if(sz > 0) - { - if(sz == 1) - { - if(json[long_name_underscores][0].isString()) - { - ret_vector.push_back(atof(json[long_name_underscores][0].asCString())); - value = json[long_name_underscores][0].asCString(); - } - else - { - ret_vector.push_back(json[long_name_underscores][0].asFloat()); - sprintf(buf, "%f", ret_vector[0]); - value = buf; - } - } - else - { - value = ""; - for(int i = 0; i < (int)sz - 1; i++) - { - if(json[long_name_underscores][i].isString()) - { - ret_vector.push_back(atof(json[long_name_underscores][i].asCString())); - value += json[long_name_underscores][i].asCString(); - value += ","; - } - else - { - ret_vector.push_back(json[long_name_underscores][i].asFloat()); - sprintf(buf, "%f,", ret_vector[i]); - string ss = buf; - value += ss; - } - } - - if(json[long_name_underscores][(int)sz - 1].isString()) - { - ret_vector.push_back(atof(json[long_name_underscores][(int)sz - 1].asCString())); - value += json[long_name_underscores][(int)sz - 1].asCString(); - } - else - { - ret_vector.push_back(json[long_name_underscores][(int)sz - 1].asFloat()); - sprintf(buf, "%f", ret_vector[(int)sz - 1]); - string ss = buf; - value += ss; - } - } - source = "parameters json file"; - } - } - - if (opts.HasOption(short_name, long_name_hyphens)) { - ret_vector.clear(); - vector ret_vector2; - opts.GetOption(ret_vector2, default_value, short_name, long_name_hyphens); - for(size_t i = 0; i < ret_vector2.size(); i++) - { - ret_vector.push_back((float)ret_vector2[i]); - } - - char buf[1000]; - if(ret_vector.empty()) - { - cout << "Error setting: there is no value set for option: " + long_name_hyphens << endl; - return 1; - } - else if(ret_vector.size() == 1) - { - sprintf(buf, "%f", ret_vector[0]); - value = buf; - } - else - { - value = ""; - for(size_t i = 0; i < ret_vector.size() - 1; i++) { - sprintf(buf, "%f,", ret_vector[i]); - string ss = buf; - value += ss; - } - sprintf(buf, "%f", ret_vector[ret_vector.size() - 1]); - string ss = buf; - value += ss; - } - source = "command line option"; - } - - cout << setw(35) << long_name_hyphens << " = " << setw(10) << value << " (float, " << source << ")" << endl; - return 0; -} - - // ============================================================================= void EnsembleEvalTuningParameters::SetOpts(OptArgs &opts, Json::Value& tvc_params) { @@ -533,13 +321,14 @@ void EnsembleEvalTuningParameters::SetOpts(OptArgs &opts, Json::Value& tvc_param outlier_prob = RetrieveParameterDouble(opts, tvc_params, '-', "outlier-probability", 0.01); germline_prior_strength = RetrieveParameterDouble(opts, tvc_params, '-', "germline-prior-strength", 0.0f); heavy_tailed = RetrieveParameterInt (opts, tvc_params, '-', "heavy-tailed", 3); - + adjust_sigma = RetrieveParameterBool (opts, tvc_params, '-', "adjust-sigma", false); + filter_unusual_predictions = RetrieveParameterDouble(opts, tvc_params, '-', "filter-unusual-predictions", 0.3f); soft_clip_bias_checker = RetrieveParameterDouble(opts, tvc_params, '-', "soft-clip-bias-checker", 0.1f); filter_deletion_bias = RetrieveParameterDouble(opts, tvc_params, '-', "filter-deletion-predictions", 100.0f); filter_insertion_bias = RetrieveParameterDouble(opts, tvc_params, '-', "filter-insertion-predictions", 100.0f); max_detail_level = RetrieveParameterInt(opts, tvc_params, '-', "max-detail-level", 0); - min_detail_level_for_fast_scan = RetrieveParameterInt(opts, tvc_params, '-', "min-detail-level-for-fast-scan", 2500); + min_detail_level_for_fast_scan = RetrieveParameterInt(opts, tvc_params, '-', "min-detail-level-for-fast-scan", 0); try_few_restart_freq = RetrieveParameterBool(opts, tvc_params, '-', "try-few-restart-freq", false); // shouldn't majorly affect anything, but still expose parameters for completeness @@ -576,6 +365,54 @@ void EnsembleEvalTuningParameters::CheckParameterLimits() { // ============================================================================ +// I really don't want to do this. +void RetrieveParameterStringtifiedVectorIntForIR(Json::Value& json, const string& long_name_hyphens, vector& ret_vector){ + string long_name_underscores = long_name_hyphens; + for (unsigned int idx = 0; idx < long_name_underscores.size(); ++idx){ + if (long_name_underscores[idx] == '-'){ + long_name_underscores[idx] = '_'; + } + } + if (not json.isMember(long_name_underscores)){ + return; + } + if (not json[long_name_underscores].isString()){ + return; + } + string my_value = json[long_name_underscores].toStyledString(); + string my_vector_to_print; + my_vector_to_print.reserve(my_value.size()); + for (string::iterator my_it = my_value.begin(); my_it != my_value.end(); ++my_it){ + // Note that the string returned from toStyledString() starts with "\"" and ends with "\"\n". + if (*my_it != '\n' and *my_it != ' ' and *my_it != '"' and *my_it != '\t'){ + my_vector_to_print.push_back(*my_it); + } + } + + if (not (*(my_vector_to_print.begin()) == '[' and my_vector_to_print.back() == ']')){ + cerr << "WARNING: Skip the invalid value of the json parameter \"" << long_name_underscores << "\": \""<< my_vector_to_print << "\"" << endl; + return; + } + + ret_vector.resize(0); + ret_vector.reserve(my_vector_to_print.size() / 2); + my_value = my_vector_to_print; + + size_t comma_idx = (my_vector_to_print == "[]") ? string::npos : 0; + while (comma_idx != string::npos and my_value.size() > 1){ + my_value.erase(0, comma_idx + 1); + int my_entry = (int) strtol(my_value.c_str(), NULL, 0); + if (my_entry == 0 and my_value[0] != '0'){ + cerr << "WARNING: Skip the invalid value of the json parameter \"" << long_name_underscores << "\": \""<< my_vector_to_print << "\"" << endl; + ret_vector.clear(); + return; + } + ret_vector.push_back(my_entry); + comma_idx = my_value.find(","); + } + cout << setw(35) << long_name_hyphens << " = " << setw(10) << my_vector_to_print << " (vector, parameters json file (specified as a string))" << endl; +} + void ClassifyFilters::SetOpts(OptArgs &opts, Json::Value & tvc_params) { hp_max_length = RetrieveParameterInt (opts, tvc_params, 'L', "hp-max-length", 8); @@ -587,68 +424,198 @@ void ClassifyFilters::SetOpts(OptArgs &opts, Json::Value & tvc_params) { do_mnp_realignment = RetrieveParameterBool (opts, tvc_params, '-', "do-mnp-realignment", do_snp_realignment); realignment_threshold = RetrieveParameterDouble(opts, tvc_params, '-', "realignment-threshold", 1.0); - indel_as_hpindel = RetrieveParameterBool (opts, tvc_params, '-', "indel-as-hpindel", false); + indel_as_hpindel = RetrieveParameterBool (opts, tvc_params, '-', "indel-as-hpindel", false); + + RetrieveParameterVectorInt(opts, tvc_params, '-', "hp-indel-hrun", "-1,-1", filter_hp_indel_hrun); + // Deal with the IR "stringtified" format. + if (filter_hp_indel_hrun == vector({-1, -1})){ + RetrieveParameterStringtifiedVectorIntForIR(tvc_params, "hp-indel-hrun", filter_hp_indel_hrun); + } + if (filter_hp_indel_hrun == vector({-1, -1})){ + filter_hp_indel_hrun = {7, 8}; + cout <<" hp-indel-hrun = 7,8 (int, builtin default)" << endl; + } + + RetrieveParameterVectorInt(opts, tvc_params, '-', "hp-ins-len", "-1,-1", filter_hp_ins_len); + // Deal with the IR "stringtified" format. + if (filter_hp_ins_len == vector({-1, -1})){ + RetrieveParameterStringtifiedVectorIntForIR(tvc_params, "hp-ins-len", filter_hp_ins_len); + } + if (filter_hp_ins_len == vector({-1, -1})){ + filter_hp_ins_len = {0, 0}; + cout << " hp-ins-len = 0,0 (int, builtin default)" << endl; + } + + RetrieveParameterVectorInt(opts, tvc_params, '-', "hp-del-len", "-1,-1", filter_hp_del_len); + // Deal with the IR "stringtified" format. + if (filter_hp_del_len == vector({-1, -1})){ + RetrieveParameterStringtifiedVectorIntForIR(tvc_params, "hp-del-len", filter_hp_del_len); + } + if (filter_hp_del_len == vector({-1, -1})){ + filter_hp_del_len = {0, 0}; + cout << " hp-del-len = 0,0 (int, builtin default)" << endl; + } } void ClassifyFilters::CheckParameterLimits() { - CheckParameterLowerBound ("hp-max-length", hp_max_length, 1); - CheckParameterLowerUpperBound("sse-prob-threshold", sseProbThreshold, 0.0f, 1.0f); + CheckParameterLowerBound ("hp-max-length", hp_max_length, 1); + CheckParameterLowerUpperBound("sse-prob-threshold", sseProbThreshold, 0.0f, 1.0f); CheckParameterLowerUpperBound("min-ratio-reads-non-sse-strand", minRatioReadsOnNonErrorStrand, 0.0f, 1.0f); CheckParameterLowerUpperBound("sse-relative-safety-level", sse_relative_safety_level, 0.0f, 1.0f); CheckParameterLowerUpperBound("realignment-threshold", realignment_threshold, 0.0f, 1.0f); + cout << "Size check parameters hp-indel-hrun, hp-ins-len, hp-del-len: hp_indel_hrun.size()==hp_ins_len.size()==hp_del_len.size()? "; + if (filter_hp_indel_hrun.size() == filter_hp_ins_len.size() and filter_hp_indel_hrun.size() == filter_hp_del_len.size()){ + cout << "OK!" << endl; + }else{ + cout << "Using " << "hp_indel_hrun = {7,8}, filter_hp_ins_len = {0,0}, filter_hp_del_len = {0,0} instead!" << endl; + filter_hp_indel_hrun = {7, 8}; + filter_hp_ins_len = {0, 0}; + filter_hp_del_len = {0, 0}; + } + + for (unsigned int i_hp = 0; i_hp < filter_hp_indel_hrun.size(); ++i_hp){ + string identifier = "hp-indel-hrun[" + convertToString(i_hp) + "]"; + CheckParameterLowerBound (identifier, filter_hp_indel_hrun[i_hp], 0); + } + for (unsigned int i_hp = 0; i_hp < filter_hp_ins_len.size(); ++i_hp){ + string identifier = "hp-ins-len[" + convertToString(i_hp) + "]"; + CheckParameterLowerBound (identifier, filter_hp_ins_len[i_hp], 0); + } + for (unsigned int i_hp = 0; i_hp < filter_hp_del_len.size(); ++i_hp){ + string identifier = "hp-del-len[" + convertToString(i_hp) + "]"; + CheckParameterLowerBound (identifier, filter_hp_del_len[i_hp], 0); + } +} + +BasicFilters::BasicFilters(const string& filter_type) { + my_type = filter_type; + underscored_my_type = my_type; + for (string::iterator s_it = underscored_my_type.begin(); s_it != underscored_my_type.end(); ++s_it){ + if (*s_it == '-'){ + *s_it = '_'; + } + } + if (underscored_my_type == "snp" or underscored_my_type == "fd_10"){ + min_allele_freq = 0.2f; + strand_bias_threshold = 0.95f; + strand_bias_pval_threshold = 1.0f; + min_quality_score = 10.0f; + min_cov = 6; + min_cov_each_strand = 0; + min_var_cov = 0; + } + else if (underscored_my_type == "indel" or underscored_my_type == "fd_0"){ + min_allele_freq = 0.2f; + strand_bias_threshold = 0.95f; + strand_bias_pval_threshold = 1.0f; + min_quality_score = 10.0f; + min_cov = 30; + min_cov_each_strand = 1; + min_var_cov = 0; + } +} +void BasicFilters::SetBasicFilterOpts(OptArgs &opts, Json::Value& tvc_params, BasicFilters* default_basic_filter){ + string opt_prefix; + Json::Value tvc_params_section; + // I will get the X parameters from the kTypeMap[X] parameters in the "torrent_variant_caller" section + const static map kTypeMap = {pair("fd-10", "mnp"), + pair("fd-5", "snp"), + pair("fd-0", "indel"), + pair("hotspot", "hotspot"), + pair("mnp", "mnp"), + pair("snp", "snp"), + pair("indel", "indel")}; + + map::const_iterator map_it = kTypeMap.find(my_type); + if (map_it == kTypeMap.end()){ + cerr << "ERROR: Unkown BasicFilter type: " << my_type << endl; + exit(-1); + } + // First get parameter from the "torrent_variant_caller" section + opt_prefix = map_it->second + "-"; + if (map_it->second != my_type){ + cout << endl << "Retrieving the " << map_it->second << " parameters that will be propagate to the " << my_type << " parameters:" << endl; + } + + if (default_basic_filter == NULL){ + default_basic_filter = this; + } + + min_cov_each_strand = RetrieveParameterInt (opts, tvc_params, '-', opt_prefix + "min-cov-each-strand", default_basic_filter->min_cov_each_strand); + min_quality_score = RetrieveParameterDouble(opts, tvc_params, '-', opt_prefix + "min-variant-score", default_basic_filter->min_quality_score); + min_allele_freq = RetrieveParameterDouble(opts, tvc_params, '-', opt_prefix + "min-allele-freq", default_basic_filter->min_allele_freq); + min_cov = RetrieveParameterInt (opts, tvc_params, '-', opt_prefix + "min-coverage", default_basic_filter->min_cov); + min_var_cov = RetrieveParameterInt (opts, tvc_params, '-', opt_prefix + "min-var-coverage", default_basic_filter->min_var_cov); + strand_bias_threshold = RetrieveParameterDouble(opts, tvc_params, '-', opt_prefix + "strand-bias", default_basic_filter->strand_bias_threshold); + strand_bias_pval_threshold = RetrieveParameterDouble(opts, tvc_params, '-', opt_prefix + "strand-bias-pval", default_basic_filter->strand_bias_pval_threshold); + if (map_it->second != my_type){ + cout << "Translating the above " << map_it->second << " parameters to the " << my_type << " parameters." << endl; + } + + // Then get parameter from the subsection in "torrent_variant_caller" + opt_prefix = my_type + "-"; + if (tvc_params.isMember(underscored_my_type)){ + string underscored_opt_prefix = underscored_my_type + "_"; + tvc_params_section = tvc_params[underscored_my_type]; + vector keys = tvc_params_section.getMemberNames(); + for (vector::iterator key_it = keys.begin(); key_it != keys.end(); ++key_it){ + // In case some one have something like this "fd_0": {"fd_0_min_allele_freq": 0.1,} while I suppose to see something like this "fd_0": {"min_allele_freq": 0.1,} + tvc_params_section.removeMember(underscored_opt_prefix + *key_it); + tvc_params_section[underscored_opt_prefix + *key_it] = tvc_params_section[*key_it]; + } + } + + cout << "Retrieving the " << my_type << " parameters from the command line and parameter_json[\"torrent_variant_caller\"][\""<< underscored_my_type << "\"]:" << endl; + min_cov_each_strand = RetrieveParameterInt (opts, tvc_params_section, '-', opt_prefix + "min-cov-each-strand", min_cov_each_strand); + min_quality_score = RetrieveParameterDouble(opts, tvc_params_section, '-', opt_prefix + "min-variant-score", min_quality_score); + min_allele_freq = RetrieveParameterDouble(opts, tvc_params_section, '-', opt_prefix + "min-allele-freq", min_allele_freq); + min_cov = RetrieveParameterInt (opts, tvc_params_section, '-', opt_prefix + "min-coverage", min_cov); + min_var_cov = RetrieveParameterInt (opts, tvc_params_section, '-', opt_prefix + "min-var-coverage", min_var_cov); + strand_bias_threshold = RetrieveParameterDouble(opts, tvc_params_section, '-', opt_prefix + "strand-bias", strand_bias_threshold); + strand_bias_pval_threshold = RetrieveParameterDouble(opts, tvc_params_section, '-', opt_prefix + "strand-bias-pval", strand_bias_pval_threshold); + cout << "(Note): Here, \"builtin default\" may be referred to as the the parameter has been set previously." << endl + << "Retrieving " << my_type << " parameter set complete." << endl; + +} + + +void BasicFilters::CheckBasicFiltersLimits(){ + string opt_prefix = my_type + "-"; + CheckParameterLowerBound (opt_prefix + "min-cov-each-strand", min_cov_each_strand, 0); + CheckParameterLowerBound (opt_prefix + "min-variant-score", min_quality_score, 0.0f); + CheckParameterLowerUpperBound(opt_prefix + "min-allele-freq", min_allele_freq, 0.0f, 1.0f); + CheckParameterLowerBound (opt_prefix + "min-coverage", min_cov, 0); + CheckParameterLowerUpperBound(opt_prefix + "strand-bias", strand_bias_threshold, 0.5f, 1.0f); + CheckParameterLowerUpperBound(opt_prefix + "strand-bias-pval", strand_bias_pval_threshold, 0.0f, 1.0f); + CheckParameterLowerBound (opt_prefix + "min-var-coverage", min_var_cov, 0); } // =========================================================================== void ControlCallAndFilters::CheckParameterLimits() { - filter_variant.CheckParameterLimits(); - CheckParameterLowerBound ("data-quality-stringency", data_quality_stringency, 0.0f); - CheckParameterLowerUpperBound("read-rejection-threshold", read_rejection_threshold, 0.0f, 1.0f); - CheckParameterLowerUpperBound ("downsample-to-coverage", downSampleCoverage, 20, 100000); - CheckParameterLowerUpperBound("position-bias-ref-fraction",position_bias_ref_fraction, 0.0f, 1.0f); - CheckParameterLowerUpperBound("position-bias", position_bias, 0.0f, 1.0f); - CheckParameterLowerUpperBound("position-bias-pval", position_bias_pval, 0.0f, 1.0f); - // CheckParameterLowerUpperBound("tune-xbias", xbias_tune, 0.001f, 1000.0f); - CheckParameterLowerUpperBound("tune-sbias", sbias_tune, 0.001f, 1000.0f); - CheckParameterLowerBound ("lod-multiplier", lod_multiplier, 0.6f); - - CheckParameterLowerBound ("snp-min-cov-each-strand", filter_snps.min_cov_each_strand, 0); - CheckParameterLowerBound ("snp-min-variant-score", filter_snps.min_quality_score, 0.0f); - CheckParameterLowerUpperBound("snp-min-allele-freq", filter_snps.min_allele_freq, 0.0f, 1.0f); - CheckParameterLowerBound ("snp-min-coverage", filter_snps.min_cov, 0); - CheckParameterLowerUpperBound("snp-strand-bias", filter_snps.strand_bias_threshold, 0.5f, 1.0f); - CheckParameterLowerUpperBound("snp-strand-bias-pval", filter_snps.strand_bias_pval_threshold, 0.0f, 1.0f); - CheckParameterLowerBound ("snp-min-var-coverage", filter_snps.min_var_cov, 0); -// CheckParameterLowerBound ("snp-beta-bias", filter_snps.beta_bias_filter, 0.0f); - - CheckParameterLowerBound ("mnp-min-cov-each-strand", filter_mnp.min_cov_each_strand, 0); - CheckParameterLowerBound ("mnp-min-variant-score", filter_mnp.min_quality_score, 0.0f); - CheckParameterLowerUpperBound("mnp-min-allele-freq", filter_mnp.min_allele_freq, 0.0f, 1.0f); - CheckParameterLowerBound ("mnp-min-coverage", filter_mnp.min_cov, 0); - CheckParameterLowerUpperBound("mnp-strand-bias", filter_mnp.strand_bias_threshold, 0.5f, 1.0f); - CheckParameterLowerUpperBound("mnp-strand-bias-pval", filter_mnp.strand_bias_pval_threshold, 0.0f, 1.0f); - CheckParameterLowerBound ("mnp-min-var-coverage", filter_mnp.min_var_cov, 0); - - CheckParameterLowerBound ("indel-min-cov-each-strand", filter_hp_indel.min_cov_each_strand, 0); - CheckParameterLowerBound ("indel-min-variant-score", filter_hp_indel.min_quality_score, 0.0f); - CheckParameterLowerUpperBound("indel-min-allele-freq", filter_hp_indel.min_allele_freq, 0.0f, 1.0f); - CheckParameterLowerBound ("indel-min-coverage", filter_hp_indel.min_cov, 0); - CheckParameterLowerUpperBound("indel-strand-bias", filter_hp_indel.strand_bias_threshold, 0.5f, 1.0f); - CheckParameterLowerUpperBound("indel-strand-bias-pval", filter_hp_indel.strand_bias_pval_threshold, 0.0f, 1.0f); -// CheckParameterLowerBound ("indel-beta-bias", filter_hp_indel.beta_bias_filter, 0.0f); - CheckParameterLowerBound ("indel-min-var-coverage", filter_hp_indel.min_var_cov, 0); - - CheckParameterLowerBound ("hotspot-min-cov-each-strand", filter_hotspot.min_cov_each_strand, 0); - CheckParameterLowerBound ("hotspot-min-variant-score", filter_hotspot.min_quality_score, 0.0f); - CheckParameterLowerUpperBound("hotspot-min-allele-freq", filter_hotspot.min_allele_freq, 0.0f, 1.0f); - CheckParameterLowerBound ("hotspot-min-coverage", filter_hotspot.min_cov, 0); - CheckParameterLowerUpperBound("hotspot-strand-bias", filter_hotspot.strand_bias_threshold, 0.5f, 1.0f); - CheckParameterLowerUpperBound("hotspot-strand-bias-pval", filter_hotspot.strand_bias_pval_threshold, 0.0f, 1.0f); -// CheckParameterLowerBound ("hotspot-beta-bias", filter_hotspot.beta_bias_filter, 0.0f); - CheckParameterLowerBound ("hotspot-min-var-coverage", filter_hotspot.min_var_cov, 0); + CheckParameterLowerBound ("data-quality-stringency", data_quality_stringency, 0.0f); + CheckParameterLowerUpperBound("read-rejection-threshold", read_rejection_threshold, 0.0f, 1.0f); + CheckParameterLowerUpperBound ("downsample-to-coverage", downSampleCoverage, 20, 200000); + CheckParameterLowerUpperBound("position-bias-ref-fraction",position_bias_ref_fraction, 0.0f, 1.0f); + CheckParameterLowerUpperBound("position-bias", position_bias, 0.0f, 1.0f); + CheckParameterLowerUpperBound("position-bias-pval", position_bias_pval, 0.0f, 1.0f); + CheckParameterLowerUpperBound("tune-sbias", sbias_tune, 0.001f, 1000.0f); + CheckParameterLowerBound ("lod-multiplier", lod_multiplier, 0.0f); + CheckParameterLowerBound ("tag-sim-max-cov", tag_sim_max_cov, 0); + CheckParameterLowerUpperBound("min-read-ratio-for-fd", min_ratio_for_fd, 0.0f, 1.0f); + CheckParameterLowerBound ("fd-nonsnp-min-var-cov", fd_nonsnp_min_var_cov, 0); + + filter_variant.CheckParameterLimits(); + filter_snp.CheckBasicFiltersLimits(); + filter_mnp.CheckBasicFiltersLimits(); + filter_hp_indel.CheckBasicFiltersLimits(); + filter_hotspot.CheckBasicFiltersLimits(); + filter_fd_0.CheckBasicFiltersLimits(); + filter_fd_5.CheckBasicFiltersLimits(); + filter_fd_10.CheckBasicFiltersLimits(); } // ------------------------------------------------------ @@ -671,7 +638,13 @@ void ControlCallAndFilters::SetOpts(OptArgs &opts, Json::Value& tvc_params) { use_lod_filter = RetrieveParameterBool(opts, tvc_params, '-', "use-lod-filter", false); lod_multiplier = RetrieveParameterDouble(opts, tvc_params, '-', "lod-multiplier",0.6f); + tag_sim_max_cov = RetrieveParameterInt (opts, tvc_params, '-', "tag-sim-max-cov", 20); downSampleCoverage = RetrieveParameterInt (opts, tvc_params, '-', "downsample-to-coverage", 2000); + + // VCF record filters XXX + //filter_by_target = RetrieveParameterBool (opts, tvc_params, '-', "filter-by-target", true); + //hotspot_positions_only = RetrieveParameterBool (opts, tvc_params, '-', "hotspot-positions-only", false); + //hotspot_variants_only = RetrieveParameterBool (opts, tvc_params, '-', "hotspot-variants-only", false); //xbias_tune = RetrieveParameterDouble(opts, tvc_params, '-', "tune-xbias", 0.005f); sbias_tune = RetrieveParameterDouble(opts, tvc_params, '-', "tune-sbias", 0.01f); @@ -680,43 +653,34 @@ void ControlCallAndFilters::SetOpts(OptArgs &opts, Json::Value& tvc_params) { suppress_nocall_genotypes = RetrieveParameterBool (opts, tvc_params, '-', "suppress-nocall-genotypes", true); suppress_no_calls = RetrieveParameterBool (opts, tvc_params, '-', "suppress-no-calls", true); - heal_snps = RetrieveParameterBool (opts, tvc_params, '-', "heal-snps", true); - - // SNPS are my usual variants - filter_snps.min_cov_each_strand = RetrieveParameterInt (opts, tvc_params, 'C', "snp-min-cov-each-strand", 0); - filter_snps.min_quality_score = RetrieveParameterDouble(opts, tvc_params, 'B', "snp-min-variant-score", 10.0); - filter_snps.min_allele_freq = RetrieveParameterDouble(opts, tvc_params, 'A', "snp-min-allele-freq", 0.2); - filter_snps.min_cov = RetrieveParameterInt (opts, tvc_params, 'k', "snp-min-coverage", 6); - filter_snps.strand_bias_threshold = RetrieveParameterDouble(opts, tvc_params, 's', "snp-strand-bias", 0.95); - filter_snps.strand_bias_pval_threshold= RetrieveParameterDouble(opts, tvc_params, 's', "snp-strand-bias-pval", 1.0); - filter_snps.min_var_cov = RetrieveParameterInt (opts, tvc_params, '-', "snp-min-var-coverage", 0); - - filter_mnp.min_cov_each_strand = RetrieveParameterInt (opts, tvc_params, '-', "mnp-min-cov-each-strand", filter_snps.min_cov_each_strand); - filter_mnp.min_quality_score = RetrieveParameterDouble(opts, tvc_params, '-', "mnp-min-variant-score", filter_snps.min_quality_score); - filter_mnp.min_allele_freq = RetrieveParameterDouble(opts, tvc_params, 'H', "mnp-min-allele-freq", filter_snps.min_allele_freq); - filter_mnp.min_cov = RetrieveParameterInt (opts, tvc_params, '-', "mnp-min-coverage", filter_snps.min_cov); - filter_mnp.strand_bias_threshold = RetrieveParameterDouble(opts, tvc_params, '-', "mnp-strand-bias", filter_snps.strand_bias_threshold); - filter_mnp.strand_bias_pval_threshold= RetrieveParameterDouble(opts, tvc_params, 's', "mnp-strand-bias-pval", filter_snps.strand_bias_pval_threshold); - filter_mnp.min_var_cov = RetrieveParameterInt (opts, tvc_params, '-', "mnp-min-var-coverage", filter_snps.min_var_cov); - - // hp_indels are more complex - filter_hp_indel.min_cov_each_strand = RetrieveParameterInt (opts, tvc_params, '-', "indel-min-cov-each-strand", 1); - filter_hp_indel.min_quality_score = RetrieveParameterDouble(opts, tvc_params, '-', "indel-min-variant-score", 10.0); - filter_hp_indel.min_allele_freq = RetrieveParameterDouble(opts, tvc_params, '-', "indel-min-allele-freq", 0.2); - filter_hp_indel.min_cov = RetrieveParameterInt (opts, tvc_params, '-', "indel-min-coverage", 15); - filter_hp_indel.strand_bias_threshold = RetrieveParameterDouble(opts, tvc_params, 'S', "indel-strand-bias", 0.85); - filter_hp_indel.strand_bias_pval_threshold= RetrieveParameterDouble(opts, tvc_params, 's', "indel-strand-bias-pval", 1.0); - filter_hp_indel.min_var_cov = RetrieveParameterInt (opts, tvc_params, '-', "indel-min-var-coverage", filter_snps.min_var_cov); - // derive hotspots by default from SNPs - // override from command line or json - filter_hotspot.min_cov_each_strand = RetrieveParameterInt (opts, tvc_params, '-', "hotspot-min-cov-each-strand", filter_snps.min_cov_each_strand); - filter_hotspot.min_quality_score = RetrieveParameterDouble(opts, tvc_params, '-', "hotspot-min-variant-score", filter_snps.min_quality_score); - filter_hotspot.min_allele_freq = RetrieveParameterDouble(opts, tvc_params, 'H', "hotspot-min-allele-freq", filter_snps.min_allele_freq); - filter_hotspot.min_cov = RetrieveParameterInt (opts, tvc_params, '-', "hotspot-min-coverage", filter_snps.min_cov); - filter_hotspot.strand_bias_threshold = RetrieveParameterDouble(opts, tvc_params, '-', "hotspot-strand-bias", filter_snps.strand_bias_threshold); - filter_hotspot.strand_bias_pval_threshold= RetrieveParameterDouble(opts, tvc_params, 's', "hotspot-strand-bias-pval", filter_snps.strand_bias_pval_threshold); - filter_hotspot.min_var_cov = RetrieveParameterInt (opts, tvc_params, '-', "hotspot-min-var-coverage", filter_snps.min_var_cov); - + // Deprecate heal-snps + cleanup_unlikely_candidates = RetrieveParameterBool (opts, tvc_params, '-', "heal-snps", true); + cleanup_unlikely_candidates = RetrieveParameterBool (opts, tvc_params, '-', "cleanup-unlikely-candidates", cleanup_unlikely_candidates); + report_ppa = RetrieveParameterBool (opts, tvc_params, '-', "report-ppa", false); + hotspots_as_de_novo = RetrieveParameterBool (opts, tvc_params, '-', "hotspots-as-de-novo", false); + disable_filters = RetrieveParameterBool (opts, tvc_params, '-', "disable-filters", false); + + // Flow-disruption related + fd_nonsnp_min_var_cov = RetrieveParameterInt (opts, tvc_params, '-', "fd-nonsnp-min-var-cov", 0); + use_fd_param = RetrieveParameterBool (opts, tvc_params, '-', "use-fd-param", false); + min_ratio_for_fd = RetrieveParameterDouble(opts, tvc_params, '-', "min-ratio-for-fd", 0.1); + + filter_snp.SetBasicFilterOpts(opts, tvc_params, NULL); + // Default: mnp behaves like snp + filter_mnp.SetBasicFilterOpts(opts, tvc_params, &filter_snp); + filter_hp_indel.SetBasicFilterOpts(opts, tvc_params, NULL); + + filter_fd_0.SetBasicFilterOpts(opts, tvc_params, NULL); + // Default: fd-5 behaves like fd-0. + filter_fd_5.SetBasicFilterOpts(opts, tvc_params, &filter_fd_0); + filter_fd_10.SetBasicFilterOpts(opts, tvc_params, NULL); + // Default: HS behaves like the most aggresive one. + if (use_fd_param){ + filter_hotspot.SetBasicFilterOpts(opts, tvc_params, &filter_fd_10); + } + else{ + filter_hotspot.SetBasicFilterOpts(opts, tvc_params, &filter_snp); + } } // ============================================================================= @@ -725,21 +689,10 @@ void ProgramControlSettings::CheckParameterLimits() { CheckParameterLowerUpperBound ("num-threads", nThreads, 1, 128); CheckParameterLowerUpperBound ("num-variants-per-thread", nVariantsPerThread, 1, 10000); - for(unsigned int i_freq = 0; i_freq < snp_multi_min_allele_freq.size(); ++i_freq){ + + for(unsigned int i_freq = 0; i_freq < multi_min_allele_freq.size(); ++i_freq){ string identifier = "multi-min-allele-freq[" + convertToString(i_freq) + "]"; - CheckParameterLowerUpperBound (identifier, snp_multi_min_allele_freq[i_freq], 0.0f, 1.0f); - } - for(unsigned int i_freq = 0; i_freq < mnp_multi_min_allele_freq.size(); ++i_freq){ - string identifier = "mnp-multi-min-allele-freq[" + convertToString(i_freq) + "]"; - CheckParameterLowerUpperBound (identifier, mnp_multi_min_allele_freq[i_freq], 0.0f, 1.0f); - } - for(unsigned int i_freq = 0; i_freq < indel_multi_min_allele_freq.size(); ++i_freq){ - string identifier = "indel-multi-min-allele-freq[" + convertToString(i_freq) + "]"; - CheckParameterLowerUpperBound (identifier, indel_multi_min_allele_freq[i_freq], 0.0f, 1.0f); - } - for(unsigned int i_freq = 0; i_freq < hotspot_multi_min_allele_freq.size(); ++i_freq){ - string identifier = "hotspot-multi-min-allele-freq[" + convertToString(i_freq) + "]"; - CheckParameterLowerUpperBound (identifier, hotspot_multi_min_allele_freq[i_freq], 0.0f, 1.0f); + CheckParameterLowerUpperBound (identifier, multi_min_allele_freq[i_freq], 0.0f, 1.0f); } } @@ -764,19 +717,10 @@ void ProgramControlSettings::SetOpts(OptArgs &opts, Json::Value &tvc_params) { use_SSE_basecaller = RetrieveParameterBool (opts, tvc_params, '-', "use-sse-basecaller", true); do_indel_assembly = RetrieveParameterBool (opts, tvc_params, '-', "do-indel-assembly", true); + is_multi_min_allele_freq = RetrieveParameterBool (opts, tvc_params, '-', "output-multi-min-allele-freq", false); - RetrieveParameterVectorFloat(opts, tvc_params, '-', "snp-multi-min-allele-freq", "0.05,0.1,0.15,0.2", snp_multi_min_allele_freq); - string snp_multi_min_allele_freq_str = ""; - for(unsigned i_freq = 0; i_freq < snp_multi_min_allele_freq.size(); ++i_freq){ - snp_multi_min_allele_freq_str += convertToString(snp_multi_min_allele_freq[i_freq]); - if(i_freq < snp_multi_min_allele_freq.size() - 1){ - snp_multi_min_allele_freq_str += ","; - } - } - RetrieveParameterVectorFloat(opts, tvc_params, '-', "mnp-multi-min-allele-freq", snp_multi_min_allele_freq_str, mnp_multi_min_allele_freq); - RetrieveParameterVectorFloat(opts, tvc_params, '-', "indel-multi-min-allele-freq", "0.05,0.1,0.15,0.2", indel_multi_min_allele_freq); - RetrieveParameterVectorFloat(opts, tvc_params, '-', "hotspot-multi-min-allele-freq", "0.05,0.1,0.15,0.2", hotspot_multi_min_allele_freq); + RetrieveParameterVectorFloat(opts, tvc_params, '-', "multi-min-allele-freq", "0.05,0.1,0.15,0.2", multi_min_allele_freq); } // =========================================================================== @@ -793,28 +737,6 @@ bool ExtendParameters::ValidateAndCanonicalizePath(string &path) return true; } -int mkpath(std::string s,mode_t mode) -{ - size_t pre=0,pos; - std::string dir; - int mdret = 0; - - if(s[s.size()-1]!='/'){ - // force trailing / so we can handle everything in loop - s+='/'; - } - - while((pos=s.find_first_of('/',pre))!=std::string::npos){ - dir=s.substr(0,pos++); - pre=pos; - if(dir.size()==0) continue; // if leading / first time is 0 length - if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){ - return mdret; - } - } - return mdret; -} - void ExtendParameters::SetupFileIO(OptArgs &opts, Json::Value& tvc_params) { // freeBayes slot fasta = opts.GetFirstString('r', "reference", ""); @@ -825,9 +747,9 @@ void ExtendParameters::SetupFileIO(OptArgs &opts, Json::Value& tvc_params) { ValidateAndCanonicalizePath(fasta); // freeBayes slot - blacklistFile = opts.GetFirstString('l', "blacklist-vcf", ""); + blacklistFile = opts.GetFirstString('l', "sse-vcf", ""); if (blacklistFile.empty()) { - cerr << "INFO: No blacklist VCF file specified via -l,--blacklist-vcf" << endl; + cerr << "INFO: No sse VCF file specified via -l,--sse-vcf" << endl; } else ValidateAndCanonicalizePath(blacklistFile); @@ -838,7 +760,13 @@ void ExtendParameters::SetupFileIO(OptArgs &opts, Json::Value& tvc_params) { else ValidateAndCanonicalizePath(variantPriorsFile); - sseMotifsFileName = opts.GetFirstString('e', "error-motifs", ""); + sseMotifsDir = opts.GetFirstString('-', "error-motifs-dir", ""); + sseMotifsFileName = RetrieveParameterString (opts, tvc_params, 'e', "error-motifs", ""); + size_t pos = sseMotifsFileName.rfind("/"); + if ((!sseMotifsDir.empty()) && (pos != std::string::npos) && (pos < sseMotifsFileName.length() - 1)) { + sseMotifsFileName = sseMotifsFileName.substr(pos + 1); + } + if (sseMotifsDir != "") {sseMotifsFileName = sseMotifsDir + "/" + sseMotifsFileName;} sseMotifsProvided = true; if (sseMotifsFileName.empty()) { sseMotifsProvided = false; @@ -861,17 +789,45 @@ void ExtendParameters::SetupFileIO(OptArgs &opts, Json::Value& tvc_params) { mkpath(outputDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); ValidateAndCanonicalizePath(outputDir); - outputFile = opts.GetFirstString('o', "output-vcf", ""); - if (outputFile.empty()) { - cerr << "Fatal ERROR: Output VCF filename not specified via -o" << endl; + // Parse output file names + small_variants_vcf = opts.GetFirstString('o', "output-vcf", "small_variants.vcf"); + if (small_variants_vcf.empty()) { + cerr << "Fatal ERROR: Output VCF filename not specified via -o or --output-vcf" << endl; + exit(1); + } + + indel_assembly_vcf = opts.GetFirstString('-', "assembly-vcf", "indel_assembly.vcf"); + if (indel_assembly_vcf.empty()) { + cerr << "Fatal ERROR: Assembly VCF filename not specified via --assembly-vcf" << endl; exit(1); } - // Are those file names? + // XXX VCF merging not being done in TVC 5.4 but in the varriant caller pipeline + //merged_vcf = opts.GetFirstString('o', "merged-vcf", "TSVC_variants.vcf"); + //if (merged_vcf.empty()) { + // cerr << "Fatal ERROR: Merged VCF filename not specified via --merged-vcf" << endl; + // exit(1); + //} + // Gvcf file name is the same as merged vcf file, but with different file ending + //string vcf_file_end(".vcf"); + //merged_genome_vcf = merged_vcf.substr(0, merged_vcf.find(".vcf")) + ".genome.vcf"; + + string tmp = opts.GetFirstString('-', "candidate-list-file", ""); + if (tmp.size() > 0) candidate_list = outputDir+ "/" + tmp; + /* + tmp = opts.GetFirstString('-', "black-listed-file", ""); + if (tmp.size() > 0) black_listed = outputDir + "/" + tmp; + */ + black_listed = outputDir + "/black_listed.vcf"; + + // Are those file names? - Nope, only the first one! postprocessed_bam = opts.GetFirstString('-', "postprocessed-bam", ""); sampleName = opts.GetFirstString('g', "sample-name", ""); force_sample_name = opts.GetFirstString('-', "force-sample-name", ""); - + // TS 5.4: Multisample currently produces core dumps. Deactivated unless override-limits is specified. + //multisample = RetrieveParameterBool (opts, tvc_params, '-', "multisample-analysis", false); + // Allow command line only and do not report in log. + multisample = opts.GetFirstBoolean('-', "multisample-analysis", false); } // ------------------------------------------------------------ @@ -879,9 +835,11 @@ void ExtendParameters::SetupFileIO(OptArgs &opts, Json::Value& tvc_params) { void ExtendParameters::SetFreeBayesParameters(OptArgs &opts, Json::Value& fb_params) { // FreeBayes parameters // primarily used in candidate generation - + // Note: Abuse of fb_params here. The following parameters will get into TargetsManager and won't be the part of Freebayes. targets = opts.GetFirstString('t', "target-file", ""); trim_ampliseq_primers = opts.GetFirstBoolean('-', "trim-ampliseq-primers", false); + min_cov_fraction = RetrieveParameterDouble(opts, fb_params, '-', "min-cov-fraction", 0.0); + if (targets.empty() and trim_ampliseq_primers) { cerr << "ERROR: --trim-ampliseq-primers enabled but no --target-file provided" << endl; exit(1); @@ -891,7 +849,8 @@ void ExtendParameters::SetFreeBayesParameters(OptArgs &opts, Json::Value& fb_par allowSNPs = RetrieveParameterBool (opts, fb_params, '-', "allow-snps", true); allowMNPs = RetrieveParameterBool (opts, fb_params, '-', "allow-mnps", true); allowComplex = RetrieveParameterBool (opts, fb_params, '-', "allow-complex", false); - mergeLookAhead = RetrieveParameterInt (opts, fb_params, '-', "merge-variant-lookahead", 3); + mergeLookAhead = RetrieveParameterInt (opts, fb_params, '-', "merge-variant-lookahead", -1); + output_allele_cigar = RetrieveParameterBool (opts, fb_params, '-', "allele-cigar-output", false); // deprecated: // leftAlignIndels = RetrieveParameterBool (opts, fb_params, '-', "left-align-indels", false); @@ -899,25 +858,24 @@ void ExtendParameters::SetFreeBayesParameters(OptArgs &opts, Json::Value& fb_par //useBestNAlleles = 0; useBestNAlleles = RetrieveParameterInt (opts, fb_params, 'm', "use-best-n-alleles", 2); + useBestNTotalAlleles = RetrieveParameterInt (opts, fb_params, '-', "use-best-n-nonsnp-alleles", 0); + max_alt_num = RetrieveParameterInt (opts, fb_params, '-', "max-alt-num", 20); onlyUseInputAlleles = RetrieveParameterBool (opts, fb_params, '-', "use-input-allele-only", false); min_mapping_qv = RetrieveParameterInt (opts, fb_params, 'M', "min-mapping-qv", 4); read_snp_limit = RetrieveParameterInt (opts, fb_params, 'U', "read-snp-limit", 10); readMaxMismatchFraction = RetrieveParameterDouble(opts, fb_params, 'z', "read-max-mismatch-fraction", 1.0); + read_mismatch_limit = RetrieveParameterInt (opts, fb_params, '-', "read-mismatch-limit", 0); maxComplexGap = RetrieveParameterInt (opts, fb_params, '!', "max-complex-gap", 1); // read from json or command line, otherwise default to snp frequency - minAltFraction = RetrieveParameterDouble(opts, fb_params, '-', "gen-min-alt-allele-freq", my_controls.filter_snps.min_allele_freq); - minCoverage = RetrieveParameterInt (opts, fb_params, '-', "gen-min-coverage", my_controls.filter_snps.min_cov); + minAltFraction = RetrieveParameterDouble(opts, fb_params, '-', "gen-min-alt-allele-freq", my_controls.filter_snp.min_allele_freq); + minCoverage = RetrieveParameterInt (opts, fb_params, '-', "gen-min-coverage", my_controls.filter_snp.min_cov); minIndelAltFraction = RetrieveParameterDouble(opts, fb_params, '-', "gen-min-indel-alt-allele-freq", my_controls.filter_hp_indel.min_allele_freq); //set up debug levels - if (program_flow.DEBUG > 0) - debug = true; - - if (program_flow.inputPositionsOnly) { - processInputPositionsOnly = true; - } + debug = program_flow.DEBUG > 0; + processInputPositionsOnly = program_flow.inputPositionsOnly; - if (variantPriorsFile.empty() && (processInputPositionsOnly || onlyUseInputAlleles) ) { + if (variantPriorsFile.empty() and (processInputPositionsOnly or onlyUseInputAlleles) ) { cerr << "ERROR: Parameter error - Process-input-positions-only: " << processInputPositionsOnly << " use-input-allele-only: " << onlyUseInputAlleles << " : Specified without Input VCF File " << endl; exit(1); } @@ -957,29 +915,65 @@ void ExtendParameters::CheckParameterLimits() { program_flow.CheckParameterLimits(); // Checking FreeBayes parameters + CheckParameterLowerUpperBound("min-cov-fraction", min_cov_fraction, 0.0f, 1.0f); CheckParameterLowerUpperBound ("use-best-n-alleles", useBestNAlleles, 0, 20); + CheckParameterLowerUpperBound ("use-best-n-nonsnp-alleles", useBestNTotalAlleles, 0, 20); CheckParameterLowerBound ("min-mapping-qv", min_mapping_qv, 0); CheckParameterLowerBound ("read-snp-limit", read_snp_limit, 0); CheckParameterLowerUpperBound("read-max-mismatch-fraction", readMaxMismatchFraction, 0.0f, 1.0f); - + CheckParameterLowerBound ("read-mismatch-limit", read_mismatch_limit, 0); CheckParameterLowerUpperBound("gen-min-alt-allele-freq", minAltFraction, 0.0, 1.0); CheckParameterLowerBound ("gen-min-coverage", minCoverage, 0); + CheckParameterLowerBound ("max-alt-num", max_alt_num, 1); CheckParameterLowerUpperBound("gen-min-indel-alt-allele-freq", minIndelAltFraction, 0.0, 1.0); + // Checking TagTrimmerParameters + CheckParameterLowerUpperBound ("tag-trim-method", tag_trimmer_parameters.tag_trim_method, 0, 1); + CheckParameterLowerBound ("min-tag-fam-size", tag_trimmer_parameters.min_family_size, 1); + CheckParameterLowerBound ("indel-func-size-offset", tag_trimmer_parameters.indel_func_size_offset, 0); } +void ExtendParameters::SetMolecularTagTrimmerOpt(Json::Value& tvc_params) +{ + enum TagFilterOptions { + kneed_only_prefix_tag, + kneed_only_suffix_tag, + kneed_all_tags, + }; + + enum TagTrimMethod { + kStrictTrim, + kSloppyTrim, + kUnknownTrim + }; + + tag_trimmer_parameters.suppress_mol_tags = RetrieveParameterBool (opts, tvc_params, '-', "suppress-mol-tags", false); + tag_trimmer_parameters.min_family_size = RetrieveParameterInt (opts, tvc_params, '-', "min-tag-fam-size", 3); + tag_trimmer_parameters.indel_func_size_offset = RetrieveParameterInt (opts, tvc_params, '-', "indel-func-size-offset", 0); + string trim_method = RetrieveParameterString (opts, tvc_params, '-', "tag-trim-method", "sloppy-trim"); + + if (trim_method == "sloppy-trim" or trim_method == "sloppy_trim") + tag_trimmer_parameters.tag_trim_method = kSloppyTrim; + else if (trim_method == "strict-trim" or trim_method == "strict_trim") + tag_trimmer_parameters.tag_trim_method = kStrictTrim; + else { + tag_trimmer_parameters.tag_trim_method = kUnknownTrim; + } + // tvc requires need all tags + tag_trimmer_parameters.tag_filter_method = kneed_all_tags; + +} // ------------------------------------------------------------ ExtendParameters::ExtendParameters(int argc, char** argv) { // i/o parameters: - fasta = ""; // -f --fasta-reference - targets = ""; // -t --targets - outputFile = ""; + // file names use default string constructor = "" // operation parameters trim_ampliseq_primers = false; + min_cov_fraction = 0.0f; useDuplicateReads = false; // -E --use-duplicate-reads useBestNAlleles = 0; // -n --use-best-n-alleles allowIndels = true; // -i --no-indels @@ -990,6 +984,7 @@ ExtendParameters::ExtendParameters(int argc, char** argv) onlyUseInputAlleles = false; min_mapping_qv = 0; // -m --min-mapping-quality readMaxMismatchFraction = 1.0; // -z --read-max-mismatch-fraction + read_mismatch_limit = 0; read_snp_limit = 10000000; // -$ --read-snp-limit minAltFraction = 0.2; // require 20% of reads from sample to be supporting the same alternate to consider minIndelAltFraction = 0.2; @@ -1016,12 +1011,6 @@ ExtendParameters::ExtendParameters(int argc, char** argv) exit(0); } - // enable floating point exceptions during program execution - if (opts.GetFirstBoolean('-', "float-exceptions", true)) { - cout << "TVC: Floating point exceptions enabled." << endl; - feraiseexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); - } //*/ - Json::Value tvc_params(Json::objectValue); Json::Value freebayes_params(Json::objectValue); Json::Value params_meta(Json::objectValue); @@ -1032,22 +1021,28 @@ ExtendParameters::ExtendParameters(int argc, char** argv) my_controls.SetOpts(opts, tvc_params); my_eval_control.SetOpts(opts, tvc_params); program_flow.SetOpts(opts, tvc_params); - tag_trimmer_parameters = MolecularTagTrimmer::ReadOpts(opts); - // Preserve the data for all flows if we want to do rich diagnostic - // Otherwise we only keep the data for test flows - my_eval_control.preserve_full_data = program_flow.rich_json_diagnostic; + // Create output directory for diagnostic if desired + if (program_flow.rich_json_diagnostic || program_flow.minimal_diagnostic) { + json_plot_dir = outputDir + "/json_diagnostic/"; + mkpath(json_plot_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + ValidateAndCanonicalizePath(outputDir); + } + + // tag_trimmer_parameters = MolecularTagTrimmer::ReadOpts(opts); // not read from json! + // Get tag trimmer parameters + SetMolecularTagTrimmerOpt(tvc_params); // Dummy lines for HP recalibration recal_model_file_name = opts.GetFirstString ('-', "model-file", ""); recalModelHPThres = opts.GetFirstInt('-', "recal-model-hp-thres", 4); - prefixExclusion = opts.GetFirstInt('-', "prefix-exclude", 6); - cerr << "prefix-exclude = " << prefixExclusion << endl; - SetFreeBayesParameters(opts, freebayes_params); bool overrideLimits = RetrieveParameterBool (opts, tvc_params, '-', "override-limits", false); + prefixExclusion = opts.GetFirstInt('-', "prefix-exclude", 6); + cerr << "prefix-exclude = " << prefixExclusion << endl; + params_meta_name = params_meta.get("name",string()).asString(); params_meta_details = params_meta.get("configuration_name", string()).asString(); string external_file = params_meta.get("external_file", string()).asString(); @@ -1072,39 +1067,20 @@ ExtendParameters::ExtendParameters(int argc, char** argv) // opts.CheckNoLeftovers(); // Sanity checks on input variables once all are set. - if (!overrideLimits) - CheckParameterLimits(); - -} - -// check whether parameter contains only the characters in context -bool CheckParameterStringContext(string identifier, string ¶meter, const string &contains_only, const string &default_value){ - bool is_ok = true; // for the case where parameter is an empty string. Will return true if parameter is empty. + if (!overrideLimits){ + if (multisample){ + cout << "Limit check parameter multisample-analysis: Using multsample_analysis=false instead." << endl; + multisample = false; + } - cout << "Check parameter " << identifier << ": " - << parameter << " contains only " << contains_only << "? "; + CheckParameterLimits(); + } - for(unsigned int i = 0; i < parameter.size(); ++i){ - is_ok = false; - for(unsigned int j = 0; j < contains_only.size(); ++j){ - if(parameter[i] == contains_only[j]){ - is_ok = true; - break; - } - } - if(not is_ok){ - break; - } - } - if(is_ok){ - cout<< "OK!"; - }else{ - cout<< "The parameter "<< identifier - << " should only contain the characters in " << contains_only <<". " - << "Using the default value " << default_value <<" instead!"; - } - cout << endl; - return is_ok; + if (my_controls.disable_filters){ + cout << "tvc will disable all the post-evaluation filters." << endl; + my_controls.suppress_reference_genotypes = false; + my_controls.suppress_nocall_genotypes = false; + my_controls.cleanup_unlikely_candidates = false; + my_controls.suppress_no_calls = false; + } } - - diff --git a/Analysis/VariantCaller/Bookkeeping/ExtendParameters.h b/Analysis/VariantCaller/Bookkeeping/ExtendParameters.h index d165c0ff..b9fdffd0 100644 --- a/Analysis/VariantCaller/Bookkeeping/ExtendParameters.h +++ b/Analysis/VariantCaller/Bookkeeping/ExtendParameters.h @@ -10,11 +10,18 @@ #include #include #include "OptArgs.h" +#include "OptBase.h" #include "json/json.h" #include "MolecularTagTrimmer.h" +#include +#include +#include using namespace std; +int mkpath(std::string s,mode_t mode); + +//bool RetrieveParameterBool(OptArgs &opts, Json::Value& json, char short_name, const string& long_name_hyphens, bool default_value); // provide an interface to set useful parameters across the objects class EnsembleEvalTuningParameters { @@ -22,6 +29,7 @@ class EnsembleEvalTuningParameters { float germline_prior_strength; // how concentrated are we at 0,0.5,1.0 frequency for germline calls float outlier_prob; // 1-data_reliability int heavy_tailed; // how heavy are the tails of my distribution to resist mis-shapen items = CrossHypotheses + bool adjust_sigma; // If true, sigma^2 = (dof-2) / dof * E[r^2] (where dof = 2*heavy_tail - 1), else sigma^2 = E[r^2] float prediction_precision; // damper_bias = bias_generator - how likely the predictions are to be accurately calibrated float min_delta_for_flow; // select flows to test based on this minimum level @@ -45,8 +53,6 @@ class EnsembleEvalTuningParameters { int min_detail_level_for_fast_scan; bool try_few_restart_freq; - bool preserve_full_data; // Preserve the data for all flows if true, otherwise preserve the data only at test flows - EnsembleEvalTuningParameters() { germline_prior_strength = 0.0f; outlier_prob = 0.01f; @@ -67,17 +73,12 @@ class EnsembleEvalTuningParameters { filter_deletion_bias = 10.0f; filter_insertion_bias = 10.0f; max_detail_level = 0; - min_detail_level_for_fast_scan = 2500; + min_detail_level_for_fast_scan = 0; try_few_restart_freq = false; - preserve_full_data = false; //use_all_compare_for_test_flows = false; }; - float DataReliability() { - return(1.0f - outlier_prob); - }; - void CheckParameterLimits(); void SetOpts(OptArgs &opts, Json::Value& tvc_params); @@ -85,27 +86,20 @@ class EnsembleEvalTuningParameters { class BasicFilters { public: - float min_allele_freq; - - float strand_bias_threshold; - float strand_bias_pval_threshold; - - float min_quality_score; - - int min_cov; - int min_cov_each_strand; - int min_var_cov; - - BasicFilters() { - min_allele_freq = 0.2f; - strand_bias_threshold = 0.8f; - strand_bias_pval_threshold = 1.0f; - - min_cov = 3; - min_cov_each_strand = 3; - min_quality_score = 2.5f; - min_var_cov = 0; - }; + float min_allele_freq = -1.0f; + float strand_bias_threshold = -1.0f; + float strand_bias_pval_threshold = -1.0f; + float min_quality_score = -1.0f; + int min_cov = -1; + int min_cov_each_strand = -1; + int min_var_cov = -1; + string my_type; + string underscored_my_type; + + BasicFilters() {}; + BasicFilters(const string& default_type); + void CheckBasicFiltersLimits(); + void SetBasicFilterOpts(OptArgs &opts, Json::Value& tvc_params, BasicFilters* default_param); }; @@ -130,6 +124,11 @@ class ClassifyFilters { // treat non hp indels as hp indels bool indel_as_hpindel; + // the hp-indel filter + vector filter_hp_indel_hrun; + vector filter_hp_ins_len; + vector filter_hp_del_len; + ClassifyFilters() { hp_max_length = 11; @@ -142,6 +141,10 @@ class ClassifyFilters { realignment_threshold = 1.0; indel_as_hpindel = false; + + filter_hp_indel_hrun = {7, 8}; + filter_hp_ins_len = {0, 0}; + filter_hp_del_len = {0, 0}; }; void SetOpts(OptArgs &opts, Json::Value & tvc_params); void CheckParameterLimits(); @@ -163,29 +166,49 @@ class ControlCallAndFilters { bool suppress_reference_genotypes; bool suppress_nocall_genotypes; - bool heal_snps; // if a snp is the best allele, discard all others + bool cleanup_unlikely_candidates; // if a snp is the best allele, discard all others bool suppress_no_calls; + bool report_ppa; + bool hotspots_as_de_novo; + bool disable_filters; // position bias probably should not be variant specific - bool use_position_bias; + bool use_position_bias; float position_bias_ref_fraction; float position_bias; float position_bias_pval; - // Dima's LOD filter - bool use_lod_filter; + // LOD filter + bool use_lod_filter; float lod_multiplier; + // filter's for mol tags + int tag_sim_max_cov; + + // flow-disruptivenss stuff + bool use_fd_param ; + float min_ratio_for_fd; + int fd_nonsnp_min_var_cov; + ClassifyFilters filter_variant; + // VCF record filters (applied during vcf merging) + //bool filter_by_target; // Filter records based on mets information in target bed info field + //bool hotspot_positions_only; // Output only vcf lines with the infoFlag 'HS' + //bool hotspot_variants_only; // Suppress hotspot reference calls and no-calls from the final output vcf + // tuning parameter for xbias - // float xbias_tune; + //float xbias_tune; float sbias_tune; - BasicFilters filter_snps; - BasicFilters filter_mnp; - BasicFilters filter_hp_indel; - BasicFilters filter_hotspot; + // Filtering parameters that depend on the variant type. + BasicFilters filter_snp = BasicFilters("snp"); + BasicFilters filter_mnp = BasicFilters("mnp"); + BasicFilters filter_hp_indel = BasicFilters("indel"); + BasicFilters filter_hotspot = BasicFilters("hotspot"); + BasicFilters filter_fd_0 = BasicFilters("fd-0"); + BasicFilters filter_fd_5 = BasicFilters("fd-5"); + BasicFilters filter_fd_10 = BasicFilters("fd-10"); ControlCallAndFilters(); void SetOpts(OptArgs &opts, Json::Value& tvc_params); @@ -201,9 +224,10 @@ class ProgramControlSettings { bool do_indel_assembly; + // Directory in ExtendParameters directly bool rich_json_diagnostic; bool minimal_diagnostic; - string json_plot_dir; + bool use_SSE_basecaller; bool suppress_recalibration; @@ -212,25 +236,32 @@ class ProgramControlSettings { bool inputPositionsOnly; bool is_multi_min_allele_freq; - vector snp_multi_min_allele_freq; - vector mnp_multi_min_allele_freq; - vector indel_multi_min_allele_freq; - vector hotspot_multi_min_allele_freq; + vector multi_min_allele_freq; ProgramControlSettings(); void SetOpts(OptArgs &opts, Json::Value & pf_params); void CheckParameterLimits(); }; +struct TvcTagTrimmerParameters : TagTrimmerParameters{ + int indel_func_size_offset = 0; +}; + class ExtendParameters { public: vector bams; string fasta; // -f --fasta-reference string targets; // -t --targets - string outputFile; + + string small_variants_vcf; // small indel output vcf file name + string indel_assembly_vcf; // indel assembly vcf file name + //string merged_vcf; // merged and post processed vcf file name + //string merged_genome_vcf; // merged gvcf file name + string blacklistFile; string variantPriorsFile; string postprocessed_bam; + string json_plot_dir; string basecaller_version; string tmap_version; @@ -239,11 +270,16 @@ class ExtendParameters { bool processInputPositionsOnly; bool trim_ampliseq_primers; + float min_cov_fraction; + int prefixExclusion; // operation parameters + //TODO: Put the Freebayes parameters in a container. bool useDuplicateReads; // -E --use-duplicate-reads int useBestNAlleles; // -n --use-best-n-alleles + int max_alt_num; // Try to break the variant if the number of alt alleles is greater than this value. + int useBestNTotalAlleles; // --use-best-n-total-alleles bool allowIndels; // -I --allow-indels bool allowMNPs; // -X --allow-mnps bool allowComplex; // -X --allow-complex @@ -252,6 +288,7 @@ class ExtendParameters { int min_mapping_qv; // -m --min-mapping-quality float readMaxMismatchFraction; // -z --read-max-mismatch-fraction int read_snp_limit; // -$ --read-snp-limit + int read_mismatch_limit; long double minAltFraction; // -F --min-alternate-fraction long double minIndelAltFraction; // Added by SU to reduce Indel Candidates for Somatic int minAltCount; // -C --min-alternate-count @@ -261,16 +298,17 @@ class ExtendParameters { bool debug; // set if debuglevel >=1 bool multisample; // multisample run - + string candidate_list, black_listed; OptArgs opts; ControlCallAndFilters my_controls; EnsembleEvalTuningParameters my_eval_control; ProgramControlSettings program_flow; - TagTrimmerParameters tag_trimmer_parameters; + TvcTagTrimmerParameters tag_trimmer_parameters; //Input files string outputDir; + string sseMotifsDir; string sseMotifsFileName; bool sseMotifsProvided; @@ -279,11 +317,13 @@ class ExtendParameters { string recal_model_file_name; int recalModelHPThres; + bool output_allele_cigar; string params_meta_name; string params_meta_details; // functions + ExtendParameters() {} ExtendParameters(int argc, char** argv); bool ValidateAndCanonicalizePath(string &path); @@ -291,67 +331,24 @@ class ExtendParameters { void SetFreeBayesParameters(OptArgs &opts, Json::Value& fb_params); void ParametersFromJSON(OptArgs &opts, Json::Value &tvc_params, Json::Value &fb_params, Json::Value ¶ms_meta); void CheckParameterLimits(); + void SetMolecularTagTrimmerOpt(Json::Value& tvc_params); }; -template -bool CheckParameterLowerUpperBound(string identifier ,T ¶meter, T lower_limit, T upper_limit) { - bool is_ok = false; - - //cout << setw(35) << long_name_hyphens << " = " << setw(10) << value << " (integer, " << source << ")" << endl; - cout << "Limit check parameter " << identifier << ": lim. " - << lower_limit << " <= " << parameter << " <= lim. " << upper_limit << "? "; - if (parameter < lower_limit) { - cout << "Using " << identifier << "=" << lower_limit << " instead!"; - parameter = lower_limit; - } - else if (parameter > upper_limit) { - cout << "Using " << identifier << "=" << upper_limit << " instead!"; - parameter = upper_limit; - } - else { - cout << "OK!"; - is_ok = true; - } - cout << endl; - return (is_ok); -} - -template -bool CheckParameterLowerBound(string identifier ,T ¶meter, T lower_limit) { - bool is_ok = false; - cout << "Limit check parameter " << identifier << ": lim. " - << lower_limit << " <= " << parameter << "? "; - if (parameter < lower_limit) { - cout << "Using " << identifier << "=" << lower_limit << " instead!"; - parameter = lower_limit; - } - else { - cout << "OK!"; - is_ok = true; - } - cout << endl; - return (is_ok); +template +string PrintIteratorToString(const MyIter &it_start, const MyIter &it_end, + string left_bracket = "[", string right_bracket = "]", string separation = ", ", string entry_prefix = "") { + string return_str = left_bracket; + MyIter last_it = it_end; + --last_it; + for (MyIter it = it_start; it != it_end; ++it){ + return_str += (entry_prefix + to_string(*it)); + if (it != last_it) + return_str += separation; + } + return_str += right_bracket; + return return_str; } -template -bool CheckParameterUpperBound(string identifier ,T ¶meter, T upper_limit) { - bool is_ok = false; - cout << "Limit check parameter " << identifier << ": " - << parameter << " <= lim. " << upper_limit << "? "; - if (parameter > upper_limit) { - cout << "Using " << identifier << "=" << upper_limit << " instead!"; - parameter = upper_limit; - } - else { - cout << "OK!"; - is_ok = true; - } - cout << endl; - return (is_ok); -} - -bool CheckParameterStringContext(string identifier, string ¶meter, const string &context, const string &default_value); - #endif // EXTENDPARAMETERS_H diff --git a/Analysis/VariantCaller/Bookkeeping/InputStructures.cpp b/Analysis/VariantCaller/Bookkeeping/InputStructures.cpp index 26249fd7..26380c13 100644 --- a/Analysis/VariantCaller/Bookkeeping/InputStructures.cpp +++ b/Analysis/VariantCaller/Bookkeeping/InputStructures.cpp @@ -7,6 +7,7 @@ #include "InputStructures.h" #include "ExtendedReadInfo.h" #include "json/json.h" +#include "MolecularTag.h" InputStructures::InputStructures() { @@ -34,7 +35,7 @@ void InputStructures::Initialize(ExtendParameters ¶meters, const ReferenceRe // now get calibration information, padded to account if nFlows for some bam is large do_recal.ReadRecalibrationFromComments(bam_header,num_flows_by_run_id); // protect against over-flowing nFlows, 0-based - if (parameters.sseMotifsProvided) { + if ((parameters.sseMotifsProvided) && (parameters.my_controls.filter_variant.sseProbThreshold < 1.0)) { cout << "Loading systematic error contexts." << endl; read_error_motifs(parameters.sseMotifsFileName); cout << "Loaded." << endl; @@ -174,7 +175,7 @@ void InputStructures::DetectFlowOrderzAndKeyFromBam(const SamHeader &samHeader){ } // Verbose output - cout << "TVC found a total of " << flow_order_vector.size() << " different flow orders of max flow lengths: "; + cout << "Found a total of " << flow_order_vector.size() << " different flow orders of max flow lengths: "; int iFO=0; for (; iFO<(int)flow_order_vector.size()-1; iFO++) cout << flow_order_vector.at(iFO).num_flows() << ','; @@ -313,7 +314,7 @@ PersistingThreadObjects::PersistingThreadObjects(const InputStructures &global_c #ifdef __SSE3__ if (use_SSE_basecaller) { for (unsigned int iFO=0; iFO < global_context.flow_order_vector.size(); iFO++){ - TreephaserSSE* treephaser_sse = new TreephaserSSE(global_context.flow_order_vector.at(iFO), DPTreephaser::kWindowSizeDefault_); + TreephaserSSE treephaser_sse(global_context.flow_order_vector.at(iFO), DPTreephaser::kWindowSizeDefault_); treephaserSSE_vector.push_back(treephaser_sse); } } diff --git a/Analysis/VariantCaller/Bookkeeping/InputStructures.h b/Analysis/VariantCaller/Bookkeeping/InputStructures.h index 7d7af83b..a8befedb 100644 --- a/Analysis/VariantCaller/Bookkeeping/InputStructures.h +++ b/Analysis/VariantCaller/Bookkeeping/InputStructures.h @@ -39,7 +39,7 @@ class OrderedVCFWriter; class SampleManager; class MetricsManager; class IndelAssembly; - +class MolecularTagManager; // ============================================================================== struct VariantCallerContext { @@ -55,7 +55,7 @@ struct VariantCallerContext { MetricsManager * metrics_manager; //! Keeps track of metrics to output in tvc_metrics.json SampleManager* sample_manager; //! Tracks the sample used in multi-sample analysis IndelAssembly* indel_assembly; //! Outputs the indel_assembly.vcf for long indels - MolecularTagTrimmer* tag_trimmer; //! Manager for molecular tags + MolecularTagManager* mol_tag_manager; //! Manager for molecular tags pthread_mutex_t bam_walker_mutex; //! Mutex for state-altering bam_walker operations pthread_mutex_t read_loading_mutex; //! Mutex for raw read retrieval @@ -79,6 +79,7 @@ struct VariantSpecificParams { min_coverage_override(false), min_coverage(0), min_coverage_each_strand_override(false), min_coverage_each_strand(0), + min_var_coverage_override(false), min_var_coverage(0), min_variant_score_override(false), min_variant_score(0), data_quality_stringency_override(false), data_quality_stringency(0), @@ -90,6 +91,7 @@ struct VariantSpecificParams { filter_insertion_predictions_override(false), filter_insertion_predictions(0), filter_deletion_predictions_override(false), filter_deletion_predictions(0), sse_prob_threshold_override(false), sse_prob_threshold(0), + min_tag_fam_size_override(false), min_tag_fam_size(0), black_strand('.') {} bool min_allele_freq_override; @@ -102,6 +104,8 @@ struct VariantSpecificParams { int min_coverage; bool min_coverage_each_strand_override; int min_coverage_each_strand; + bool min_var_coverage_override; + int min_var_coverage; bool min_variant_score_override; float min_variant_score; bool data_quality_stringency_override; @@ -122,6 +126,8 @@ struct VariantSpecificParams { float filter_deletion_predictions; bool sse_prob_threshold_override; float sse_prob_threshold; + bool min_tag_fam_size_override; + int min_tag_fam_size; char black_strand; }; @@ -197,19 +203,19 @@ class PersistingThreadObjects { PersistingThreadObjects(const InputStructures &global_context); ~PersistingThreadObjects() { -#ifdef __SSE3__ - for (vector::iterator iter = treephaserSSE_vector.begin(); (iter != treephaserSSE_vector.end()); ++iter) { - delete *iter; - *iter = NULL; - } -#endif +//#ifdef __SSE3__ +// for (vector::iterator iter = treephaserSSE_vector.begin(); (iter != treephaserSSE_vector.end()); ++iter) { +// delete *iter; +// *iter = NULL; +// } +//#endif }; //@brief Interface for setting the phasing model parameters void SetModelParameters(const int & flow_order_index, const vector & phase_params) { #ifdef __SSE3__ if (use_SSE_basecaller) - treephaserSSE_vector.at(flow_order_index)->SetModelParameters(phase_params.at(0), phase_params.at(1)); + treephaserSSE_vector.at(flow_order_index).SetModelParameters(phase_params.at(0), phase_params.at(1)); else #endif dpTreephaser_vector.at(flow_order_index).SetModelParameters(phase_params.at(0), phase_params.at(1), phase_params.at(2)); @@ -220,7 +226,7 @@ class PersistingThreadObjects { { #ifdef __SSE3__ if (use_SSE_basecaller) - treephaserSSE_vector.at(flow_order_index)->DisableRecalibration(); + treephaserSSE_vector.at(flow_order_index).DisableRecalibration(); else #endif dpTreephaser_vector.at(flow_order_index).DisableRecalibration(); @@ -231,7 +237,7 @@ class PersistingThreadObjects { { #ifdef __SSE3__ if (use_SSE_basecaller) - return (treephaserSSE_vector.at(flow_order_index)->SetAsBs(As, Bs)); + return (treephaserSSE_vector.at(flow_order_index).SetAsBs(As, Bs)); else #endif return (dpTreephaser_vector.at(flow_order_index).SetAsBs(As, Bs)); @@ -241,7 +247,7 @@ class PersistingThreadObjects { void SolveRead(const int & flow_order_index, BasecallerRead& read, const int & begin_flow, const int & end_flow){ #ifdef __SSE3__ if (use_SSE_basecaller) - treephaserSSE_vector.at(flow_order_index)->SolveRead(read, begin_flow, end_flow); + treephaserSSE_vector.at(flow_order_index).SolveRead(read, begin_flow, end_flow); else #endif dpTreephaser_vector.at(flow_order_index).Solve(read, end_flow, begin_flow); @@ -253,7 +259,7 @@ class PersistingThreadObjects { Realigner realigner; // realignment tool vector dpTreephaser_vector; // c++ treephaser #ifdef __SSE3__ - vector treephaserSSE_vector; // vectorized treephaser + vector treephaserSSE_vector; // vectorized treephaser #endif diff --git a/Analysis/VariantCaller/Bookkeeping/VcfFormat.cpp b/Analysis/VariantCaller/Bookkeeping/VcfFormat.cpp index a2a1e0c3..cc9c60c8 100644 --- a/Analysis/VariantCaller/Bookkeeping/VcfFormat.cpp +++ b/Analysis/VariantCaller/Bookkeeping/VcfFormat.cpp @@ -23,7 +23,7 @@ string dateStr() } -string get_time_iso_string(time_t time) +string tvc_get_time_iso_string(time_t time) { char time_buffer[1024]; strftime(time_buffer, 1024, "%Y-%m-%dT%H:%M:%S", localtime(&time)); @@ -31,13 +31,13 @@ string get_time_iso_string(time_t time) } -string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_reader, const vector& sample_list, int primary_sample) +string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_reader, const vector& sample_list, int primary_sample, bool use_molecular_tag) { stringstream headerss; headerss << "##fileformat=VCFv4.1" << endl << "##fileDate=" << dateStr() << endl - << "##fileUTCtime=" << get_time_iso_string(time(NULL)) << endl + << "##fileUTCtime=" << tvc_get_time_iso_string(time(NULL)) << endl << "##source=\"tvc " << IonVersion::GetVersion() << "-" << IonVersion::GetRelease() << " (" << IonVersion::GetGitHash() << ") - Torrent Variant Caller\"" << endl; if (not parameters->params_meta_name.empty()) @@ -72,6 +72,7 @@ string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_rea string tmp(first+1); headerss << "##maskVersion=" << tmp << endl; } + fclose(fp); } string chr_name; long chr_size; @@ -90,12 +91,6 @@ string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_rea << "##INFO=" << endl << "##INFO=" << endl << "##INFO=" << endl - - << "##INFO=" << endl - << "##INFO=" << endl - << "##INFO=" << endl - << "##INFO=" << endl - << "##INFO=" << endl << "##INFO=" << endl << "##INFO=" << endl @@ -116,7 +111,6 @@ string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_rea << "##INFO=" << endl << "##INFO=" << endl // << "##INFO=" << endl - // << "##INFO=" << endl; // << "##INFO=" << endl @@ -130,18 +124,55 @@ string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_rea << "##INFO=" << endl << "##INFO=" << endl << "##INFO=" << endl - << "##INFO=" << endl + << "##INFO=" << endl << "##INFO=" << endl << "##INFO=" << endl << "##INFO=" << endl << "##INFO=" << endl << "##INFO=X)+0.5P(Y=X)\">" << endl << "##INFO=" << endl - << "##INFO=" << endl; + << "##INFO=" << endl + << "##INFO=" << endl; - if(parameters->my_controls.use_lod_filter){ + // If we want to output multiple min-allele-freq + if (parameters->program_flow.is_multi_min_allele_freq){ + int multi_min_allele_freq_size = (int) parameters->program_flow.multi_min_allele_freq.size(); + headerss + << "##INFO=program_flow.multi_min_allele_freq[i_maf] << (i_maf < (multi_min_allele_freq_size - 1) ? ",": ""); + } + headerss << ").\">" << endl + << "##INFO=" << endl + << "##INFO=" << endl; + } + if (parameters->output_allele_cigar) { + headerss << "##INFO=" << endl; + } + if (parameters->my_controls.use_lod_filter){ headerss << "##INFO=" << endl; } + if (use_molecular_tag){ + headerss << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl; + } + if (parameters->my_controls.report_ppa){ + headerss << "##INFO=" << endl; + } + if (parameters->my_controls.disable_filters){ + headerss << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl + << "##INFO=" << endl; + } + headerss << "##FILTER=" << endl; @@ -149,15 +180,16 @@ string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_rea << "##FORMAT=" << endl << "##FORMAT=" << endl << "##FORMAT=" << endl + << "##FORMAT=" << endl; - << "##FORMAT=" << endl - - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl + if(use_molecular_tag){ + headerss << "##FORMAT=" << endl + << "##FORMAT=" << endl + << "##FORMAT=" << endl + << "##FORMAT=" << endl; + } - << "##FORMAT=" << endl + headerss << "##FORMAT=" << endl << "##FORMAT=" << endl << "##FORMAT=" << endl << "##FORMAT=" << endl @@ -170,43 +202,7 @@ string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_rea << "##FORMAT=" << endl << "##FORMAT=" << endl << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl; - - // If we want to output multiple min-allele-freq - if(parameters->program_flow.is_multi_min_allele_freq){ - string multi_min_allele_freq_size = "?"; - string snp_multi_min_allele_freq_size = convertToString(parameters->program_flow.snp_multi_min_allele_freq.size()); - string mnp_multi_min_allele_freq_size = convertToString(parameters->program_flow.mnp_multi_min_allele_freq.size()); - string indel_multi_min_allele_freq_size = convertToString(parameters->program_flow.indel_multi_min_allele_freq.size()); - string hotspot_multi_min_allele_freq_size = convertToString(parameters->program_flow.hotspot_multi_min_allele_freq.size()); - // the union of all var types - headerss<< "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - // for snp - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - // for mnp - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - // for indel - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - // for hotspot - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl - << "##FORMAT=" << endl; - } - + << "##FORMAT=" << endl; headerss << "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT"; // Ensure primary sample is always in the first column (IR req) @@ -235,177 +231,51 @@ void ClearVal(vcf::Variant &var, const char *clear_me){ //clear all the info tags, in case of a HotSpot VCF react Info tags might contain prior values void clearInfoTags(vcf::Variant &var) { - map >::iterator it; - - it = var.info.find("RO"); - if (it != var.info.end()) - var.info["RO"].clear(); - - it = var.info.find("AO"); - if (it != var.info.end()) - var.info["AO"].clear(); - - it = var.info.find("MDP"); - if (it != var.info.end()) - var.info["MDP"].clear(); - - it = var.info.find("MAO"); - if (it != var.info.end()) - var.info["MAO"].clear(); - - it = var.info.find("MRO"); - if (it != var.info.end()) - var.info["MRO"].clear(); - - it = var.info.find("MAF"); - if (it != var.info.end()) - var.info["MAF"].clear(); - - it = var.info.find("SAF"); - if (it != var.info.end()) - var.info["SAF"].clear(); - - it = var.info.find("SAR"); - if (it != var.info.end()) - var.info["SAR"].clear(); - - it = var.info.find("SRF"); - if (it != var.info.end()) - var.info["SRF"].clear(); - - it = var.info.find("SRR"); - if (it != var.info.end()) - var.info["SRR"].clear(); - - it = var.info.find("DP"); - if (it != var.info.end()) - var.info["DP"].clear(); - - - it = var.info.find("RBI"); - if (it != var.info.end()) - var.info["RBI"].clear(); - - it = var.info.find("HRUN"); - if (it != var.info.end()) - var.info["HRUN"].clear(); - - - it = var.info.find("MLLD"); - if (it != var.info.end()) - var.info["MLLD"].clear(); - - //SSE compute - ClearVal(var,"SSSB"); - ClearVal(var,"SSEN"); - ClearVal(var,"SSEP"); - - it = var.info.find("STB"); - if (it != var.info.end()) - var.info["STB"].clear(); - - it = var.info.find("STBP"); - if (it != var.info.end()) - var.info["STBP"].clear(); - - it = var.info.find("PBP"); - if (it != var.info.end()) - var.info["PBP"].clear(); - - it = var.info.find("LOD"); - if (it != var.info.end()) - var.info["LOD"].clear(); - -/* it = var.info.find("SXB"); - if (it != var.info.end()) - var.info["SXB"].clear();*/ - - ClearVal(var,"FDP"); - ClearVal(var,"FRO"); - ClearVal(var,"FAO"); - //ClearVal(var,"JUNK"); - ClearVal(var,"FSRF"); - ClearVal(var,"FSRR"); - ClearVal(var,"FSAF"); - ClearVal(var,"FSAR"); - ClearVal(var,"FXX"); - ClearVal(var,"QD"); + const vector tag_to_clear = + {"RO", "AO", "MDP", "MAO", "MRO", "MAF", "SAF", "SAR", "SRF", "SRR", + "DP", "RBI", "HRUN", "SSSB", "SSEN", "SSEP", "STB", "STBP", "PBP", "PB", + "FDP", "FRO", "FAO", "FSRF", "FSRR", "FSAF", "FSAR", "FXX", "QD", "TGSM", + "PPA", "VFSH", "MUQUAL", "MUGT", "MUGQ", "MLLD", "LOD"}; + for (vector::const_iterator tag_it = tag_to_clear.begin(); tag_it != tag_to_clear.end(); ++tag_it) + ClearVal(var, tag_it->c_str()); } -void NullInfoFields(vcf::Variant &var, bool use_position_bias){ - clearInfoTags(var); - for (vector::iterator I = var.alt.begin(); I != var.alt.end(); ++I) { - var.info["AO"].push_back(convertToString(0)); - var.info["MAF"].push_back(convertToString(0)); - var.info["MAO"].push_back(convertToString(0)); - var.info["SAF"].push_back(convertToString(0)); - var.info["SAR"].push_back(convertToString(0)); - var.info["FAO"].push_back(convertToString(0)); - var.info["AF"].push_back(convertToString(0)); - var.info["FSAF"].push_back(convertToString(0)); - var.info["FSAR"].push_back(convertToString(0)); - var.info["HRUN"].push_back(convertToString(0)); - var.info["RBI"].push_back(convertToString(0)); - var.info["FWDB"].push_back(convertToString(0)); - var.info["REVB"].push_back(convertToString(0)); - var.info["REFB"].push_back(convertToString(0)); - var.info["VARB"].push_back(convertToString(0)); - var.info["SSSB"].push_back(convertToString(0)); - var.info["SSEN"].push_back(convertToString(0)); - var.info["SSEP"].push_back(convertToString(0)); - var.info["STB"].push_back(convertToString(0)); - var.info["STBP"].push_back(convertToString(0)); - if(use_position_bias) { - var.info["PB"].push_back(convertToString(0.5f)); - var.info["PBP"].push_back(convertToString(1.0f)); - } - var.info["MLLD"].push_back(convertToString(0)); - } - var.info["DP"].push_back(convertToString(0)); - var.info["RO"].push_back(convertToString(0)); - var.info["MDP"].push_back(convertToString(0)); - var.info["MRO"].push_back(convertToString(0)); - var.info["SRF"].push_back(convertToString(0)); - var.info["SRR"].push_back(convertToString(0)); - - var.info["FDP"].push_back(convertToString(0)); - var.info["FRO"].push_back(convertToString(0)); - var.info["FSRF"].push_back(convertToString(0)); - var.info["FSRR"].push_back(convertToString(0)); - var.info["FXX"].push_back(convertToString(0.0)); - var.info["QD"].push_back(convertToString(0.0)); +void NullInfoFields(vcf::Variant &var, bool use_position_bias, bool use_molecular_tag){ + int num_alt = (int) var.alt.size(); + clearInfoTags(var); + const vector num_alt_tags_to_zero = + {"AO", "SAF", "SAR", "AF", "FSAF", "FSAR", "HRUN", "RBI", + "FWDB", "REVB", "REFB", "VARB", "SSSB", "SSEN", "SSEP", "MLLD", "AF"}; + for (vector::const_iterator tag_it = num_alt_tags_to_zero.begin(); tag_it != num_alt_tags_to_zero.end(); ++tag_it){ + var.info[*tag_it] = vector(num_alt, "0"); + } + var.info["STB"] = vector(num_alt, "0.5"); + var.info["STBP"] = vector(num_alt, "1"); + + const vector tags_to_zero = + {"DP", "RO", "SRF", "SRR", "FDP", "FRO", "FSRF", "FSRR", "FXX", "QD"}; + for (vector::const_iterator tag_it = tags_to_zero.begin(); tag_it != tags_to_zero.end(); ++tag_it){ + var.info[*tag_it] = {"0"}; + } + if (use_position_bias) { + var.info["PB"] = vector(num_alt, "0.5"); + var.info["PBP"] = vector(num_alt, "1"); + } + if (use_molecular_tag) { + var.info["MDP"] = {"0"}; + var.info["MRO"] = {"0"}; + var.info["MAO"] = vector(num_alt, "0"); + var.info["MAF"] = vector(num_alt, "0"); + } } // set up format string void SetUpFormatString(vcf::Variant &var) { - var.format.clear(); - var.format.push_back("GT"); - var.format.push_back("GQ"); - // XXX - var.format.push_back("MDP"); - var.format.push_back("MRO"); - var.format.push_back("MAO"); - var.format.push_back("MAF"); - var.format.push_back("DP"); - var.format.push_back("FDP"); - var.format.push_back("RO"); - var.format.push_back("FRO"); - var.format.push_back("AO"); - var.format.push_back("FAO"); - //var.format.push_back("JUNK"); - var.format.push_back("AF"); - var.format.push_back("SAR"); - var.format.push_back("SAF"); - var.format.push_back("SRF"); - var.format.push_back("SRR"); - var.format.push_back("FSAR"); - var.format.push_back("FSAF"); - var.format.push_back("FSRF"); - var.format.push_back("FSRR"); - var.format.push_back("QT"); + var.format = {"GT", "GQ", "MDP", "MRO", "MAO", "MAF", "DP", + "FDP", "RO", "FRO", "AO", "FAO", "AF", "SAR", + "SAF", "SRF", "SRR", "FSAR", "FSAF", "FSRF", "FSRR"}; } - int CalculateWeightOfVariant(vcf::Variant ¤t_variant){ map >::iterator it; @@ -454,18 +324,13 @@ float RetrieveQualityTagValue(vcf::Variant ¤t_variant, const string &tag_w // XXX void NullFilterReason(vcf::Variant &candidate_variant, const string &sample_name){ - ClearVal(candidate_variant, "FR"); - for (vector::iterator I = candidate_variant.alt.begin(); I != candidate_variant.alt.end(); ++I) { - candidate_variant.info["FR"].push_back("."); - } + candidate_variant.info["FR"] = vector(candidate_variant.alt.size(), "."); } void AddFilterReason(vcf::Variant &candidate_variant, string &additional_reason, const string &sample_name){ - int alt_allele_index = 0; - for (vector::iterator I = candidate_variant.alt.begin(); I != candidate_variant.alt.end(); ++I) { - AddFilterReason(candidate_variant, additional_reason, sample_name, alt_allele_index); - alt_allele_index++; - } + for (unsigned int alt_allele_index = 0; alt_allele_index < candidate_variant.alt.size(); ++alt_allele_index){ + AddFilterReason(candidate_variant, additional_reason, sample_name, alt_allele_index); + } } void AddFilterReason(vcf::Variant &candidate_variant, string &additional_reason, const string &sample_name, unsigned int alt_allele_index){ @@ -480,89 +345,44 @@ void AddInfoReason(vcf::Variant &candidate_variant, string &additional_reason, c } // if, for example, missing data -void NullGenotypeAllSamples(vcf::Variant & candidate_variant) +void NullGenotypeAllSamples(vcf::Variant & candidate_variant, bool use_molecular_tag) { vector& sampleNames = candidate_variant.sampleNames; - - for (vector::iterator its = sampleNames.begin(); its != sampleNames.end(); ++its) { - string& sampleName = *its; - map >& sampleOutput = candidate_variant.samples[sampleName]; - sampleOutput["GT"].clear(); - sampleOutput["GQ"].clear(); - sampleOutput["MDP"].clear(); - sampleOutput["MRO"].clear(); - sampleOutput["MAO"].clear(); - sampleOutput["MAF"].clear(); - sampleOutput["FDP"].clear(); - sampleOutput["FRO"].clear(); - sampleOutput["FSRF"].clear(); - sampleOutput["FSRR"].clear(); - sampleOutput["FAO"].clear(); - sampleOutput["AF"].clear(); - sampleOutput["FSAF"].clear(); - sampleOutput["FSAR"].clear(); - sampleOutput["GT"].push_back("./."); - sampleOutput["GQ"].push_back(convertToString(0)); - sampleOutput["MDP"].push_back(convertToString(0)); - sampleOutput["MRO"].push_back(convertToString(0)); - sampleOutput["FDP"].push_back(convertToString(0)); - sampleOutput["FRO"].push_back(convertToString(0)); - sampleOutput["FSRF"].push_back(convertToString(0)); - sampleOutput["FSRR"].push_back(convertToString(0)); - for (vector::iterator I = candidate_variant.alt.begin(); I != candidate_variant.alt.end(); ++I) { - sampleOutput["MAO"].push_back(convertToString(0)); - sampleOutput["MAF"].push_back(convertToString(0)); - sampleOutput["FAO"].push_back(convertToString(0)); - sampleOutput["AF"].push_back(convertToString(0)); - sampleOutput["FSAF"].push_back(convertToString(0)); - sampleOutput["FSAR"].push_back(convertToString(0)); + const vector keys_to_zero = {"GQ", "FDP", "FRO", "FSRF", "FSRR"}; + const vector keys_to_zeros_of_alt = {"FAO", "AF", "FSAF", "FSAR"}; + unsigned int num_alt = candidate_variant.alt.size(); + for (vector::iterator sample_it = sampleNames.begin(); sample_it != sampleNames.end(); ++sample_it) { + map >& sampleOutput = candidate_variant.samples[*sample_it]; + + for (vector::const_iterator key_it = keys_to_zero.begin(); key_it != keys_to_zero.end(); ++key_it){ + sampleOutput[*key_it] = {"0"}; } + for (vector::const_iterator key_it = keys_to_zeros_of_alt.begin(); key_it != keys_to_zeros_of_alt.end(); ++key_it){ + sampleOutput[*key_it] = vector(num_alt, "0"); + } + sampleOutput["GT"] = {"./."}; - sampleOutput["QT"].clear(); - sampleOutput["QT"].push_back("."); - - // for multi-min-allele-freq - vector tags_for_multi_min_allele_freq = {"MUAF", "MUQUAL", "MUGQ", "MUGT", - "SMAF", "SMQUAL", "SMGQ", "SMGT", - "MMAF", "MMQUAL", "MMGQ", "MMGT", - "IMAF", "IMQUAL", "IMGQ", "IMGT", - "HMAF", "HMQUAL", "HMGQ", "HMGT"}; - map >::iterator it; - for(unsigned int i_tag = 0; i_tag < tags_for_multi_min_allele_freq.size(); ++i_tag){ - it = sampleOutput.find(tags_for_multi_min_allele_freq[i_tag]); - if (it != sampleOutput.end()){ - it->second.clear(); - it->second.push_back("."); - } - } + if (use_molecular_tag){ + sampleOutput["MDP"] = {"0"}; + sampleOutput["MRO"] = {"0"}; + sampleOutput["MAO"] = vector(num_alt, "0"); + sampleOutput["MAF"] = vector(num_alt, "0"); + } } } void OverwriteGenotypeForOneSample(vcf::Variant &candidate_variant, const string &my_sample_name, string &my_genotype, float genotype_quality){ - // will create entry if one does not exist - - map >& sampleOutput = candidate_variant.samples[my_sample_name]; - // clear existing values - map >::iterator it; - it = sampleOutput.find("GT"); - if (it != sampleOutput.end()) sampleOutput["GT"].clear(); - it = sampleOutput.find("GQ"); - if (it != sampleOutput.end()) sampleOutput["GQ"].clear(); - - - sampleOutput["GT"].push_back(my_genotype); - // genotype quality should be an "int" - //cout << "Storing Genotype = " << my_genotype << endl; - sampleOutput["GQ"].push_back(convertToString((int)genotype_quality)); - + map >& sampleOutput = candidate_variant.samples[my_sample_name]; + sampleOutput["GT"] = {my_genotype}; + sampleOutput["GQ"] = {convertToString((int) genotype_quality)}; } void DetectAndSetFilteredGenotype(vcf::Variant &candidate_variant, map& variant_quality, const string &sample_name){ -if (candidate_variant.isFiltered){ - string no_call_genotype = "./."; - float original_quality = variant_quality[sample_name]; - OverwriteGenotypeForOneSample(candidate_variant, sample_name, no_call_genotype, original_quality); -} + if (candidate_variant.isFiltered){ + string no_call_genotype = "./."; + float original_quality = variant_quality[sample_name]; + OverwriteGenotypeForOneSample(candidate_variant, sample_name, no_call_genotype, original_quality); + } } @@ -571,37 +391,24 @@ void StoreGenotypeForOneSample(vcf::Variant &candidate_variant, const string &sa if (multisample) { map >& sampleOutput = candidate_variant.samples[sample_name]; - sampleOutput["GT"].clear(); - sampleOutput["GT"].push_back(my_genotype); - //cout << "Storing Genotype = " << my_genotype << endl; - sampleOutput["GQ"].clear(); - sampleOutput["GQ"].push_back(convertToString((int)genotype_quality)); + sampleOutput["GT"] = {my_genotype}; + sampleOutput["GQ"] = {convertToString((int) genotype_quality)}; } else { - for (vector::iterator its = sampleNames.begin(); its != sampleNames.end(); ++its) { - string& sampleName = *its; - //cout << "VariantAssist: SampleName = " << sampleName << " my_sample = " << my_sample_name << endl; - map >& sampleOutput = candidate_variant.samples[sampleName]; - map >::iterator it; - it = sampleOutput.find("GT"); - if (it != sampleOutput.end()) sampleOutput["GT"].clear(); - it = sampleOutput.find("GQ"); - if (it != sampleOutput.end()) sampleOutput["GQ"].clear(); - - if (sampleName.compare(sample_name) == 0) { //sample of interest - //cout << "isNocall " << isNoCall << " genotype = " << my_genotype << endl; - - // if no-call, will reset this entry as a final step, but until then, give me my genotype - sampleOutput["GT"].push_back(my_genotype); - //cout << "Storing Genotype = " << my_genotype << endl; - sampleOutput["GQ"].push_back(convertToString((int)genotype_quality)); - - } else { //for all other samples in BAM file just make a no-call at this point. - sampleOutput["GT"].push_back("./."); - sampleOutput["GQ"].push_back(convertToString(0)); - } - //cout <<"VariantAssist: total genotypes = " << sampleOutput["GT"].size() << endl; + for (vector::iterator its = sampleNames.begin(); its != sampleNames.end(); ++its) { + string& sampleName = *its; + map >& sampleOutput = candidate_variant.samples[sampleName]; + if (sampleName.compare(sample_name) == 0) { //sample of interest + // if no-call, will reset this entry as a final step, but until then, give me my genotype + sampleOutput["GT"] = {my_genotype}; + //cout << "Storing Genotype = " << my_genotype << endl; + sampleOutput["GQ"] = {convertToString((int) genotype_quality)}; } + else{ //for all other samples in BAM file just make a no-call at this point. + sampleOutput["GT"] = {"./."}; + sampleOutput["GQ"] = {"0"}; + } + } } } @@ -616,7 +423,6 @@ void SetFilteredStatus(vcf::Variant &candidate_variant, bool isFiltered) { candidate_variant.filter = "PASS"; candidate_variant.isFiltered = false; } - } @@ -647,6 +453,14 @@ void AdjustFDPForRemovedAlleles(vcf::Variant &candidate_variant, int filtered_al map >& sampleOutput = candidate_variant.samples[sampleName]; sampleOutput["FDP"].clear(); sampleOutput["FDP"].push_back(convertToString(total_depth)); + it = candidate_variant.info.find("MDP"); + if (it != candidate_variant.info.end()){ + candidate_variant.info["MDP"] = candidate_variant.info["FDP"]; + } + it = sampleOutput.find("MDP"); + if (it != sampleOutput.end()){ + sampleOutput["MDP"] = sampleOutput["FDP"]; + } } } diff --git a/Analysis/VariantCaller/Bookkeeping/VcfFormat.h b/Analysis/VariantCaller/Bookkeeping/VcfFormat.h index 8ca0b60a..4daaa70d 100644 --- a/Analysis/VariantCaller/Bookkeeping/VcfFormat.h +++ b/Analysis/VariantCaller/Bookkeeping/VcfFormat.h @@ -33,9 +33,10 @@ using namespace std; // forward declaration class ExtendParameters; -string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_reader, const vector& sample_list, int primary_sample = 0); +string tvc_get_time_iso_string(time_t time); +string getVCFHeader(const ExtendParameters *parameters, ReferenceReader& ref_reader, const vector& sample_list, int primary_sample = 0, bool use_molecular_tag = false); void clearInfoTags(vcf::Variant &var); -void NullInfoFields(vcf::Variant &var, bool use_position_bias); +void NullInfoFields(vcf::Variant &var, bool use_position_bias, bool use_molecular_tag); void SetUpFormatString(vcf::Variant &var); int CalculateWeightOfVariant(vcf::Variant ¤t_variant); void ClearVal(vcf::Variant &var, const char *clear_me); @@ -45,7 +46,7 @@ float RetrieveQualityTagValue(vcf::Variant ¤t_variant, const string &tag_w // double-star pointer here void SetFilteredStatus(vcf::Variant & candidate_variant, bool isFiltered); void StoreGenotypeForOneSample(vcf::Variant & candidate_variant, const string &my_sample_name, string &my_genotype, float genotype_quality, bool multisample); -void NullGenotypeAllSamples(vcf::Variant & candidate_variant); +void NullGenotypeAllSamples(vcf::Variant & candidate_variant, bool use_molecular_tag); void OverwriteGenotypeForOneSample(vcf::Variant & candidate_variant, const string &my_sample_name, string &my_genotype, float genotype_quality); void NullFilterReason(vcf::Variant &candidate_variant, const string &sample_name); void AddFilterReason(vcf::Variant &candidate_variant, string &additional_reason, const string &sample_name); diff --git a/Analysis/VariantCaller/Consensus/Consensus.cpp b/Analysis/VariantCaller/Consensus/Consensus.cpp new file mode 100644 index 00000000..e4eada13 --- /dev/null +++ b/Analysis/VariantCaller/Consensus/Consensus.cpp @@ -0,0 +1,568 @@ +/* Copyright (C) 2016 Thermo Fisher Scientific, All Rights Reserved */ + +//! @file Consensus.cpp +//! @ingroup Consensus +//! @brief Generate flowspace consensus bam file + +#include +#include +#include +#include +#include + +#include "ConsensusParameters.h" +#include "FlowSpaceConsensus.h" +#include "InputStructures.h" +#include "ReferenceReader.h" +#include "OrderedVCFWriter.h" +#include "BAMWalkerEngine.h" +#include "SampleManager.h" +#include "ExtendedReadInfo.h" +#include "TargetsManager.h" +#include "HotspotReader.h" +#include "MetricsManager.h" +#include "IonVersion.h" +#include "MolecularTag.h" +#include "IndelAssembly/IndelAssembly.h" + +#include "json/json.h" +#include "VcfFormat.h" + + +using namespace std; + +// TODO: get rid of global variables! +pthread_mutex_t mutexbam; + +// ---------------------------------------------------------------- +namespace consensus{ +void TheSilenceOfTheArmadillos(ofstream &null_ostream) { + // Disable armadillo warning messages. + arma::set_stream_err1(null_ostream); + arma::set_stream_err2(null_ostream); +} +}; +// ---------------------------------------------------------------- + +struct ConsensusContext : VariantCallerContext { + ConsensusBAMWalkerEngine* consensus_bam_walker; + ConsensusParameters* consensus_parameters; + pthread_mutex_t read_filter_mutex; +}; + +// ---------------------------------------------------------------- +//! @brief Write startup info to json structure. +void DumpStartingStateOfConsensus (int argc, char *argv[], time_t analysis_start_time, Json::Value &json) +{ + char my_host_name[128] = { 0 }; + gethostname (my_host_name, 128); + string command_line = argv[0]; + for (int i = 1; i < argc; i++) { + command_line += " "; + command_line += argv[i]; + } + + json["host_name"] = my_host_name; + json["start_time"] = tvc_get_time_iso_string(analysis_start_time); + json["version"] = IonVersion::GetVersion() + "." + IonVersion::GetRelease(); + json["git_hash"] = IonVersion::GetGitHash(); + json["build_number"] = IonVersion::GetBuildNum(); + json["command_line"] = command_line; +} + +// ---------------------------------------------------------------- + +void SaveJson(const Json::Value & json, const string& filename_json) +{ + ofstream out(filename_json.c_str(), ios::out); + if (out.good()) + out << json.toStyledString(); + else + cerr << "Unable to write JSON file " << filename_json; +} +// ---------------------------------------------------------------- + +void * FlowSpaceConsensusWorker(void *input); + +// ---------------------------------------------------------------- +int ConsensusMain(int argc, char* argv[]) { + pthread_mutex_init(&mutexbam, NULL); + + printf("consensus %s-%s (%s): Generate a consensus bam file by re-basecalling reads that are flow-synchronized\n\n", + IonVersion::GetVersion().c_str(), IonVersion::GetRelease().c_str(), IonVersion::GetGitHash().c_str()); + + // stolen from "Analysis" to silence error messages from Armadillo library + ofstream null_ostream("/dev/null"); // must stay live for entire scope, or crash when writing + consensus::TheSilenceOfTheArmadillos(null_ostream); + + time_t program_start_time = time(NULL); + Json::Value consensus_json(Json::objectValue); + DumpStartingStateOfConsensus (argc, argv, program_start_time, consensus_json["consensus"]); + + // Initialize parameters + ConsensusParameters parameters(argc, argv); + + mkdir(parameters.outputDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + parameters.program_flow.do_indel_assembly = false; + + // Initialize reference reader + ReferenceReader ref_reader; + ref_reader.Initialize(parameters.fasta); + + // Initialize targets manager + TargetsManager targets_manager; + targets_manager.Initialize(ref_reader, parameters.targets, parameters.min_cov_fraction, parameters.trim_ampliseq_primers); + + // Initialize consens bam walker engine + ConsensusBAMWalkerEngine bam_walker; + try { + bam_walker.Initialize(ref_reader, targets_manager, parameters.bams, parameters.postprocessed_bam, parameters.prefixExclusion, parameters.consensus_bam); + bam_walker.GetProgramVersions(parameters.basecaller_version, parameters.tmap_version); + } + catch(...){ + cerr << "Cannot open input-bam. " << endl; + exit(1); + } + + // Initialize sample manager + SampleManager sample_manager; + sample_manager.Initialize(bam_walker.GetBamHeader(), parameters.sampleName, parameters.force_sample_name, parameters.multisample); + // No multi-sample analysis yet. + + // Initialize global_context + InputStructures global_context; + global_context.Initialize(parameters, ref_reader, bam_walker.GetBamHeader()); + + // Initialize tag trimmer + MolecularTagTrimmer tag_trimmer; + tag_trimmer.InitializeFromSamHeader(parameters.tag_trimmer_parameters, bam_walker.GetBamHeader()); + + cout << "consensus: Generating flow-space consensus bam "<< (tag_trimmer.HaveTags()? "with" : "without") << " molecular tags."<< endl; + if (parameters.skip_consensus){ + cout << "consensus: Skip the calculation of consensus. Output targets_depth.txt only." << endl; + } + + // Initialize molecular tag manager + MolecularTagManager mol_tag_manager; + mol_tag_manager.Initialize(&tag_trimmer, &sample_manager); + + + OrderedVCFWriter vcf_writer; + + OrderedBAMWriter bam_writer; + + HotspotReader hotspot_reader; + + // set up producer of variants + AlleleParser candidate_generator(parameters, ref_reader, sample_manager, vcf_writer, hotspot_reader); + + ConsensusContext vc; + vc.ref_reader = &ref_reader; + vc.targets_manager = &targets_manager; + vc.bam_walker = &bam_walker; + vc.consensus_bam_walker = &bam_walker; + vc.parameters = ¶meters; + vc.consensus_parameters = ¶meters; + vc.global_context = &global_context; + vc.candidate_generator = &candidate_generator; + vc.vcf_writer = NULL; + vc.bam_writer = &bam_writer; + vc.metrics_manager = NULL; + vc.sample_manager = &sample_manager; + vc.mol_tag_manager = &mol_tag_manager; + + pthread_mutex_init(&vc.candidate_generation_mutex, NULL); + pthread_mutex_init(&vc.read_loading_mutex, NULL); + pthread_mutex_init(&vc.bam_walker_mutex, NULL); + pthread_mutex_init(&vc.read_removal_mutex, NULL); + pthread_mutex_init(&vc.read_filter_mutex, NULL); + pthread_cond_init(&vc.memory_contention_cond, NULL); + pthread_cond_init(&vc.alignment_tail_cond, NULL); + + //vc.bam_walker->openDepth(parameters.outputDir + "/depth.txt"); + vc.candidate_counter = 0; + vc.dot_time = time(NULL) + 30; + //vc.candidate_dot = 0; + + pthread_t worker_id[parameters.program_flow.nThreads]; + for (int worker = 0; worker < parameters.program_flow.nThreads; worker++){ + if (pthread_create(&worker_id[worker], NULL, FlowSpaceConsensusWorker, &vc)) { + printf("*Error* - problem starting thread\n"); + exit(-1); + } + } + for (int worker = 0; worker < parameters.program_flow.nThreads; worker++) + { pthread_join(worker_id[worker], NULL); } + + pthread_mutex_destroy(&vc.candidate_generation_mutex); + pthread_mutex_destroy(&vc.read_loading_mutex); + pthread_mutex_destroy(&vc.bam_walker_mutex); + pthread_mutex_destroy(&vc.read_removal_mutex); + pthread_mutex_destroy(&vc.read_filter_mutex); + + pthread_cond_destroy(&vc.memory_contention_cond); + pthread_cond_destroy(&vc.alignment_tail_cond); + + vector::iterator depth_target = targets_manager.merged.begin(); + Alignment* save_list = vc.bam_writer->process_new_entries(vc.bam_walker->alignments_first_); + //vc.bam_walker->SaveAlignments(save_list, vc, depth_target); + vc.bam_walker->FinishReadRemovalTask(save_list, -1); + save_list = vc.bam_writer->flush(); + //vc.bam_walker->SaveAlignments(save_list, vc, depth_target); + vc.bam_walker->FinishReadRemovalTask(save_list, -1); + + vcf_writer.Close(); + bam_walker.Close(); + + // write the target coverage txt + vc.targets_manager->WriteTargetsCoverage(parameters.outputDir + "/targets_depth.txt" , *vc.ref_reader); + + pthread_mutex_destroy(&mutexbam); + + // Determine the most frequent tmap program group for alignment + if (not parameters.skip_consensus){ + SamProgram most_popular_tmap_pg; + if (not bam_walker.GetMostPopularTmap(most_popular_tmap_pg)){ + cerr <<"ERROR: Fail to get tmap program group from the input bam header!" << endl; + exit(-1); + } + consensus_json["tmap"]["command_line"] = most_popular_tmap_pg.CommandLine; + } + + time_t program_end_time = time(NULL); + consensus_json["consensus"]["end_time"] = tvc_get_time_iso_string(program_end_time); + consensus_json["consensus"]["total_duration"] = (Json::Int)difftime(program_end_time,program_start_time); + + cerr << endl; + cout << endl; + cout << "[consensus] Processing time: " << (program_end_time-program_start_time) << " seconds." << endl << endl; + + SaveJson(consensus_json, parameters.outputDir + "/consensus.json"); + return 0; +} + +// ---------------------------------------------------------------- + +void * FlowSpaceConsensusWorker(void *input) { + ConsensusContext& vc = *static_cast(input); + + vector::iterator indel_target = vc.targets_manager->merged.begin(); + vector::iterator depth_target = vc.targets_manager->merged.begin(); + + const static int kReadBatchSize = 40; + Alignment * new_read[kReadBatchSize]; + bool success[kReadBatchSize]; + PersistingThreadObjects thread_objects(*vc.global_context); + + list ticket_temp_1(1), ticket_temp_2(1); + + // This is the consensus reads to be saved, no realignment needed. + list::iterator consensus_position_ticket = ticket_temp_1.begin(); + consensus_position_ticket->begin = NULL; + consensus_position_ticket->end = NULL; + // This is the consensus reads to be saved, realignment needed + list::iterator aln_needed_consensus_position_ticket = ticket_temp_2.begin(); + aln_needed_consensus_position_ticket->begin = NULL; + aln_needed_consensus_position_ticket->end = NULL; + + list::iterator target_ticket; + + bool more_positions = true; + const bool use_molecular_tag = vc.mol_tag_manager->tag_trimmer->HaveTags(); + const int effective_min_family_size = use_molecular_tag? vc.parameters->tag_trimmer_parameters.min_family_size : 1; + const int max_read_num_in_memory = use_molecular_tag? 250000 : 50000; + + int sample_num = 1; + if (vc.consensus_parameters->multisample){ + sample_num = vc.sample_manager->num_samples_; + } + + vector< vector< vector > > my_molecular_families_multisample; + MolecularFamilyGenerator my_family_generator; + + // Initialize flowspace_consensus_master + string basecaller_ver, tmap_ver; + vc.bam_walker->GetProgramVersions(basecaller_ver, tmap_ver); + FlowSpaceConsensusMaster flowspace_consensus_master(&thread_objects, vc.ref_reader, vc.global_context, basecaller_ver); + + // Propagate parameters + flowspace_consensus_master.PropagateFlowspaceConsensusParameters(*(vc.consensus_parameters), use_molecular_tag); + if (not vc.consensus_parameters->skip_consensus){ + flowspace_consensus_master.InitializeConsensusCounter(); // If not initialized, then I won't see the stat if no family is generated. + } + while (true /*more_positions*/) { + + // Opportunistic read removal + // Note: The I need to make sure that I am not attempt to remove the reads in progress. + pthread_mutex_lock(&vc.bam_walker_mutex); + if (vc.consensus_bam_walker->EligibleForTargetBasedReadRemoval()) { + if (pthread_mutex_trylock(&vc.read_removal_mutex) == 0) { + Alignment *removal_list = NULL; + vc.consensus_bam_walker->RequestTargetBasedReadRemovalTask(removal_list); + pthread_mutex_unlock(&vc.bam_walker_mutex); + //In rare case, the Eligible check pass, but another thread got to remove reads, then when this thread get the lock, it find there + //is no reads to remove. The unexpected behavior of SaveAlignment() is that when NULL is passed in, it save all the reads and remove + // ZM tags. To prevent that, we need to check for empty. + if (removal_list) { + Alignment* save_list = vc.bam_writer->process_new_entries(removal_list); + //vc.bam_walker->SaveAlignments(save_list, vc, depth_target); + pthread_mutex_lock(&vc.bam_walker_mutex); + vc.bam_walker->FinishReadRemovalTask(save_list, max_read_num_in_memory + 5000); + pthread_mutex_unlock(&vc.bam_walker_mutex); + } + pthread_mutex_unlock(&vc.read_removal_mutex); + pthread_cond_broadcast(&vc.memory_contention_cond); + }else{ + pthread_mutex_unlock(&vc.bam_walker_mutex); + } + } + else{ + pthread_mutex_unlock(&vc.bam_walker_mutex); + } + + // If too many reads in memory and at least one candidate evaluator in progress, pause this thread + // Wake up when the oldest candidate evaluator task is completed. + + pthread_mutex_lock(&vc.bam_walker_mutex); + if (vc.bam_walker->MemoryContention(max_read_num_in_memory)) { + pthread_cond_wait (&vc.memory_contention_cond, &vc.bam_walker_mutex); + pthread_mutex_unlock(&vc.bam_walker_mutex); + continue; + } + pthread_mutex_unlock(&vc.bam_walker_mutex); + + + // + // Task dispatch: Decide whether to load more reads or to generate more variants + // + bool ready_for_next_position = false; + + if (vc.bam_walker->EligibleForGreedyRead()) { + // Greedy reading allowed: if candidate generation in progress, just grab a new read + if (true/*pthread_mutex_trylock(&vc.candidate_generation_mutex) == 0*/) { + pthread_mutex_lock(&vc.bam_walker_mutex); + ready_for_next_position = vc.bam_walker->ReadyForNextPosition(); + if (not ready_for_next_position) { + pthread_mutex_unlock(&vc.bam_walker_mutex); +// pthread_mutex_unlock(&vc.candidate_generation_mutex); + } + } + + } else { + // Greedy reading disallowed: if candidate generation in progress, + // wait for it to finish before deciding what to do. +// pthread_mutex_lock(&vc.candidate_generation_mutex); + pthread_mutex_lock(&vc.bam_walker_mutex); + ready_for_next_position = vc.bam_walker->ReadyForNextPosition(); + if (not ready_for_next_position) { + pthread_mutex_unlock(&vc.bam_walker_mutex); +// pthread_mutex_unlock(&vc.candidate_generation_mutex); + } + } + + + + // + // Dispatch outcome: Load and process more reads + // + + if (not ready_for_next_position) { + + + pthread_mutex_lock(&vc.read_loading_mutex); + if (not vc.bam_walker->HasMoreAlignments()) { + pthread_mutex_unlock(&vc.read_loading_mutex); + pthread_cond_broadcast(&vc.memory_contention_cond); + break; + } + + pthread_mutex_lock(&vc.bam_walker_mutex); + for (int i = 0; i < kReadBatchSize; ++i) { + vc.bam_walker->RequestReadProcessingTask(new_read[i]); + success[i] = false; + } + pthread_mutex_unlock(&vc.bam_walker_mutex); + + for (int i = 0; i < kReadBatchSize; ++i) { + success[i] = vc.bam_walker->GetNextAlignmentCore(new_read[i], vc, indel_target); + if (not success[i]) + { break; } + } + pthread_mutex_unlock(&vc.read_loading_mutex); + + + for (int i = 0; i < kReadBatchSize and success[i]; ++i) { + // 1) Filling in read body information and do initial set of read filters + if (not vc.candidate_generator->BasicFilters(*new_read[i])) + { continue; } + + // 2) Alignment information altering methods (can also filter a read) + if (not vc.mol_tag_manager->tag_trimmer->GetTagsFromBamAlignment(new_read[i]->alignment, new_read[i]->tag_info)) { + new_read[i]->filtered = true; + continue; + } + + // Filter by read mismatch limit (note: NOT the filter for read-max-mismatch-fraction) here. + // Note: Target override did not apply for consensus. + FilterByModifiedMismatches(new_read[i], vc.consensus_parameters->read_mismatch_limit, NULL); + if (new_read[i]->filtered) + continue; + + // 3) Filter by target + // These two variables should be parameters of consensus. + vc.targets_manager->FilterReadByRegion(new_read[i], vc.bam_walker->GetRecentUnmergedTarget()); + if (new_read[i]->filtered) + { continue; } + // 4) Unpacking read meta data for flow-space consensus + UnpackOnLoadLight(new_read[i], *vc.global_context); + // 5) Count how many reads that use the program for alignment. + vc.consensus_bam_walker->AddReadToPG(new_read[i]); + } + + pthread_mutex_lock(&vc.bam_walker_mutex); + for (int i = 0; i < kReadBatchSize; ++i) { + vc.bam_walker->FinishReadProcessingTask(new_read[i], success[i]); + } + pthread_mutex_unlock(&vc.bam_walker_mutex); + + continue; + } + + // + // Dispatch outcome: Generate candidates at next position + // + + if (not vc.bam_walker->HasMoreAlignments() and not more_positions) { + pthread_mutex_unlock(&vc.bam_walker_mutex); +// pthread_mutex_unlock(&vc.candidate_generation_mutex); + pthread_cond_broadcast(&vc.memory_contention_cond); + break; + } + + pthread_mutex_unlock(&vc.bam_walker_mutex); + pthread_mutex_lock(&vc.bam_walker_mutex); + + // I do consensus only if I travel to the position at the end of the target. + bool do_consensus = vc.bam_walker->getPosition() == (vc.bam_walker->getEndPosition() - 1); + + if (not do_consensus){ + more_positions = vc.bam_walker->AdvancePosition(1); + pthread_mutex_unlock(&vc.bam_walker_mutex); + continue; + } + + // Generating the target ticket that consists of the reads that cover the target. + vc.consensus_bam_walker->BeginTargetProcessingTask(target_ticket); + + // The target_ticket has been generated. Now I can move to the next position (in fact, next target) to let other threads keep working. + // But keep in mind, don't let other thread remove the reads that I am processing. + more_positions = vc.bam_walker->AdvancePosition(1); + pthread_mutex_unlock(&vc.bam_walker_mutex); + + // Now I am going to gather the reads to form consensus reads. + my_molecular_families_multisample.resize(sample_num); + + // I need to make sure that every read won't be processed twice. + // i.e., no read can appear in two or more consensus reads. + // So I use read_filter_mutex to prevent any other thread is using the same read to generate another consensus read. + pthread_mutex_lock(&vc.read_filter_mutex); + // Generate families if I am using molecular tags. + if (use_molecular_tag){ + for (int sample_index = 0; sample_index < sample_num; ++sample_index) { + int overloaded_sample_index = vc.consensus_parameters->multisample ? sample_index : -1; + my_family_generator.GenerateMyMolecularFamilies(vc.mol_tag_manager, *target_ticket, overloaded_sample_index, my_molecular_families_multisample[sample_index]); + // Label all the reads being processed here as filtered, so they won't be use again. + for (vector< vector< MolecularFamily> >::iterator strand_it = my_molecular_families_multisample[sample_index].begin(); strand_it != my_molecular_families_multisample[sample_index].end(); ++strand_it) { + for (vector< MolecularFamily>::iterator fam_it = strand_it->begin(); fam_it != strand_it->end(); ++fam_it) { + for (vector< Alignment *>::iterator read_it = (fam_it->all_family_members).begin(); read_it != (fam_it->all_family_members).end(); ++read_it) { + (*read_it)->filtered = true; + } + } + } + } + } + // Generate "families" if I am NOT using molecular tags. + else{ + for (int sample_index = 0; sample_index < sample_num; ++sample_index) { + // If not using molecular tag, all the reads on the strand form a family. + // i.e., I will create 2 huge families, families of reads on the FWD/REV strands, respectively. + my_molecular_families_multisample[sample_index].resize(2); + for (int strand = 0; strand < 2; ++strand){ + if (my_molecular_families_multisample[sample_index][strand].empty()){ + string barcode_name = (strand == 0) ? "All_FWD_READS" : "All_REV_READS"; + my_molecular_families_multisample[sample_index][strand].push_back(MolecularFamily(barcode_name, strand)); + my_molecular_families_multisample[sample_index][strand][0].all_family_members.reserve(4096); + } + // I reuse all_family_members. + my_molecular_families_multisample[sample_index][strand][0].all_family_members.resize(0); + } + } + for (Alignment* rai = target_ticket->begin; rai != target_ticket->end; rai = rai->next) { + if (rai == NULL) { + target_ticket->end = NULL; + break; + } + if (rai->filtered) { + continue; + } + int strand_key = (rai->is_reverse_strand)? 1 : 0; + my_molecular_families_multisample[rai->sample_index][strand_key][0].AddNewMember(rai); + // Label all the reads being processed here as filtered, so they won't be use again. + rai->filtered = true; + } + } + pthread_mutex_unlock(&vc.read_filter_mutex); + + // Now I can generate consensus reads from the families. + GenerateFlowSpaceConsensusPositionTicket(my_molecular_families_multisample, + flowspace_consensus_master, + (unsigned int) effective_min_family_size, + consensus_position_ticket, + aln_needed_consensus_position_ticket, + vc.targets_manager, + vc.consensus_parameters->skip_consensus); + + if (not vc.consensus_parameters->skip_consensus){ + // Save consensus_position_ticket and aln_needed_consensus_position_ticket to the bam files + vc.consensus_bam_walker->SaveConsensusAlignments(consensus_position_ticket->begin, aln_needed_consensus_position_ticket->begin); + + // delete consensus_position_ticket + while ((consensus_position_ticket->begin != NULL) and (consensus_position_ticket->begin != consensus_position_ticket->end)) { + Alignment* p = consensus_position_ticket->begin; + consensus_position_ticket->begin = consensus_position_ticket->begin->next; + delete p; + } + if (consensus_position_ticket->begin != NULL) { + delete consensus_position_ticket->begin; + consensus_position_ticket->begin = NULL; + consensus_position_ticket->end = NULL; + } + // delete aln_needed_consensus_position_ticket + while ((aln_needed_consensus_position_ticket->begin != NULL) and (aln_needed_consensus_position_ticket->begin != aln_needed_consensus_position_ticket->end)) { + Alignment* p = aln_needed_consensus_position_ticket->begin; + aln_needed_consensus_position_ticket->begin = aln_needed_consensus_position_ticket->begin->next; + delete p; + } + if (aln_needed_consensus_position_ticket->begin != NULL) { + delete aln_needed_consensus_position_ticket->begin; + aln_needed_consensus_position_ticket->begin = NULL; + aln_needed_consensus_position_ticket->end = NULL; + } + } +// pthread_mutex_unlock(&vc.candidate_generation_mutex); + pthread_mutex_lock(&vc.bam_walker_mutex); + bool signal_contention_removal = vc.bam_walker->IsEarlierstPositionProcessingTask(target_ticket); + vc.bam_walker->FinishPositionProcessingTask(target_ticket); + pthread_mutex_unlock(&vc.bam_walker_mutex); + + if (signal_contention_removal) + { pthread_cond_broadcast(&vc.memory_contention_cond); } + + if (time(NULL) > vc.dot_time) { + cerr << '.'; + vc.dot_time += 30; + } + } + return NULL; +} + diff --git a/Analysis/VariantCaller/Consensus/Consensus.h b/Analysis/VariantCaller/Consensus/Consensus.h new file mode 100644 index 00000000..633e9dfd --- /dev/null +++ b/Analysis/VariantCaller/Consensus/Consensus.h @@ -0,0 +1,13 @@ +/* + * Consensus.h + * + * Created on: Feb 9, 2017 + * Author: baic1 + */ + +#ifndef CONSENSUS_H +#define CONSENSUS_H + +int ConsensusMain(int argc, char* argv[]); + +#endif // CONSENSUS_H // diff --git a/Analysis/VariantCaller/Consensus/ConsensusParameters.cpp b/Analysis/VariantCaller/Consensus/ConsensusParameters.cpp new file mode 100644 index 00000000..fc3bd70e --- /dev/null +++ b/Analysis/VariantCaller/Consensus/ConsensusParameters.cpp @@ -0,0 +1,213 @@ +/* Copyright (C) 2016 Thermo Fisher Scientific, Inc. All Rights Reserved */ + +#include "ConsensusParameters.h" +#include "MolecularTagTrimmer.h" +#include "MiscUtil.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + + +void ConsensusHelp() { + printf("Usage: consensus [options]\n"); + printf("\n"); + + printf("General options:\n"); + printf(" -h,--help print this help message and exit\n"); + printf(" -v,--version print version and exit\n"); + printf(" -n,--num-threads INT number of worker threads [4]\n"); + printf(" --parameters-file FILE json file with algorithm control parameters [optional]\n"); + printf("\n"); + + printf("Inputs:\n"); + printf(" -r,--reference FILE reference fasta file [required]\n"); + printf(" -b,--input-bam FILE bam file with mapped reads [required]\n"); + printf(" -t,--target-file FILE only process targets in this bed file [required]\n"); + printf("\n"); + + printf("Outputs:\n"); + printf(" -O,--output-dir DIRECTORY base directory for all output files [current dir]\n"); + printf(" --consensus-bam FILE save processed consensus reads to the BAM file [required]\n"); + + printf("\n"); + + MolecularTagTrimmer::PrintHelp(true); + printf("\n"); + + printf("BaseCaller options:\n"); + printf(" --suppress-recalibration on/off suppress homopolymer recalibration [on].\n"); + printf(" --use-sse-basecaller on/off switch to use the vectorized version of the basecaller [on].\n"); + printf("\n"); + + + printf("Read filtering options:\n"); + printf(" -M,--min-mapping-qv INT do not use reads with mapping quality below this [4]\n"); + printf(" -U,--read-snp-limit INT do not use reads with number of snps above this [10]\n"); + printf(" --read-mismatch-limit INT do not use reads with number of mismatches (where 1 gap open counts 1) above this value (0 to disable this option) [0]\n"); + printf(" --min-cov-fraction FLOAT do not use reads with fraction of covering the best assigned (unmerged) target region below this [0.0]\n"); + printf("\n"); + + printf("Consensus read output options:\n"); + printf(" --need-3-end-adapter on/off do not output consensus reads w/o 3\" adapter found [off]\n"); + printf(" --filter-qt-reads on/off do not output quality-trimmed consensus reads [off]\n"); + printf(" --filter-single-read-consensus on/off do not output single-read consensus [off]\n"); + printf("\n"); + + printf("Debug:\n"); + printf(" --skip-consensus on/off skip all calculations for consensus; output targets_depth.txt only [off]\n"); + printf("\n"); + +} + +ConsensusParameters::ConsensusParameters(int argc, char** argv) +{ + // i/o parameters: + fasta = ""; // -f --fasta-reference + targets = ""; // -t --targets + outputDir = ""; + consensus_bam = ""; + + + //OptArgs opts; + opts.ParseCmdLine(argc, (const char**)argv); + + if (argc == 1) { + ConsensusHelp(); + exit(0); + } + if (opts.GetFirstBoolean('v', "version", false)) { + exit(0); + } + if (opts.GetFirstBoolean('h', "help", false)) { + ConsensusHelp(); + exit(0); + } + + // enable floating point exceptions during program execution + if (opts.GetFirstBoolean('-', "float-exceptions", true)) { + cout << "consensus: Floating point exceptions enabled." << endl; + feraiseexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); + } + + Json::Value tvc_params(Json::objectValue); + Json::Value freebayes_params(Json::objectValue); + Json::Value params_meta(Json::objectValue); + ParametersFromJSON(opts, tvc_params, freebayes_params, params_meta); + + prefixExclusion = opts.GetFirstInt('-', "prefix-exclude", 6); + cerr << "prefix-exclude = " << prefixExclusion << endl; + + params_meta_name = params_meta.get("name",string()).asString(); + params_meta_details = params_meta.get("configuration_name", string()).asString(); + string external_file = params_meta.get("external_file", string()).asString(); + if (not external_file.empty()) { + if (not params_meta_details.empty()) {params_meta_details += ", ";} + params_meta_details += external_file; + } + string repository_id = params_meta.get("repository_id",string()).asString(); + string ts_version = params_meta.get("ts_version","").asString(); + if (not repository_id.empty()) { + if (not params_meta_details.empty()) + params_meta_details += ", "; + params_meta_details += repository_id; + } + if (not ts_version.empty()) { + if (not params_meta_details.empty()) + params_meta_details += ", "; + params_meta_details += "TS version: "; + params_meta_details += ts_version; + } + + // Retrieve the parameters that consensus needed + program_flow.nThreads = RetrieveParameterInt (opts, tvc_params, 'n', "num-threads", 4); + program_flow.suppress_recalibration = RetrieveParameterBool (opts, tvc_params, '-', "suppress-recalibration", true); +#ifdef __SSE3__ + program_flow.use_SSE_basecaller = RetrieveParameterBool (opts, tvc_params, '-', "use-sse-basecaller", true); +#else + program_flow.use_SSE_basecaller = RetrieveParameterBool (opts, tvc_params, '-', "use-sse-basecaller", false); +#endif + min_mapping_qv = RetrieveParameterInt (opts, freebayes_params, 'M', "min-mapping-qv", 4); + min_cov_fraction = RetrieveParameterDouble(opts, freebayes_params, '-', "min-cov-fraction", 0.0f); + read_mismatch_limit = RetrieveParameterInt (opts, freebayes_params, '-', "read-mismatch-limit", 0); + read_snp_limit = RetrieveParameterInt (opts, freebayes_params, 'U', "read-snp-limit", 10); + need_3_end_adapter = opts.GetFirstBoolean('-', "need-3-end-adapter", false); + filter_qt_reads = opts.GetFirstBoolean('-', "filter-qt-reads", false); + filter_single_read_consensus = opts.GetFirstBoolean('-', "filter-single-read-consensus", false); + skip_consensus = opts.GetFirstBoolean('-', "skip-consensus", false); + + SetMolecularTagTrimmerOpt(tvc_params); + + cout << endl; + + // Check limit + CheckParameterLowerBound ("num-threads", program_flow.nThreads, 1); + CheckParameterLowerUpperBound("min-cov-fraction", min_cov_fraction, 0.0f, 1.0f); + CheckParameterLowerBound ("read-snp-limit", read_snp_limit, 0); + CheckParameterLowerBound ("min-mapping-qv", min_mapping_qv, 0); + CheckParameterLowerBound ("min-mapping-qv", min_mapping_qv, 0); + CheckParameterLowerBound ("read-mismatch-limit", read_mismatch_limit, 0); + CheckParameterLowerUpperBound ("tag-trim-method", tag_trimmer_parameters.tag_trim_method, 0, 2); + CheckParameterLowerBound ("min-tag-fam-size", tag_trimmer_parameters.min_family_size, 1); + cout << endl; + + SetupFileIO(opts); +} + +void ConsensusParameters::SetupFileIO(OptArgs &opts) +{ + // Reference fasta + fasta = opts.GetFirstString('r', "reference", ""); + if (fasta.empty()) { + cerr << "FATAL ERROR: Reference file not specified via -r" << endl; + exit(-1); + } + ValidateAndCanonicalizePath(fasta); + + // Region bed + targets = opts.GetFirstString('t', "target-file", ""); + if (targets.empty()) { + cerr << "FATAL ERROR: Target file not specified!" << endl; + exit(-1); + } + ValidateAndCanonicalizePath(targets); + + // Input bam + opts.GetOption(bams, "", 'b', "input-bam"); + if (bams.empty()) { + cerr << "FATAL ERROR: BAM file not specified via -b" << endl; + exit(-1); + } + for (unsigned int i_bam = 0; i_bam < bams.size(); ++i_bam) + ValidateAndCanonicalizePath(bams[i_bam]); + + // Output dir + outputDir = opts.GetFirstString('O', "output-dir", "."); + mkpath(outputDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // XXX nope! + ValidateAndCanonicalizePath(outputDir); + + // output consensus bam + string dir = ""; + if (skip_consensus){ + consensus_bam = ""; + dir = outputDir + "/"; + }else{ + consensus_bam = opts.GetFirstString('-', "consensus-bam", ""); + consensus_bam = outputDir + "/" + consensus_bam; + dir = consensus_bam.substr(0, consensus_bam.find_last_of("/\\")); + } + mkpath(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + ValidateAndCanonicalizePath(dir); + + // Options for multisample. Currently not supported. + /* + sampleName = opts.GetFirstString('g', "sample-name", ""); + force_sample_name = opts.GetFirstString('-', "force-sample-name", ""); + */ +} diff --git a/Analysis/VariantCaller/Consensus/ConsensusParameters.h b/Analysis/VariantCaller/Consensus/ConsensusParameters.h new file mode 100644 index 00000000..b0a2225a --- /dev/null +++ b/Analysis/VariantCaller/Consensus/ConsensusParameters.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2016 Thermo Fisher Scientific, Inc. All Rights Reserved */ + +#ifndef CONSENSUSPARAMETERS_H +#define CONSENSUSPARAMETERS_H + +#include +#include +#include "OptArgs.h" +#include "OptBase.h" +#include "json/json.h" +#include "ExtendParameters.h" + +using namespace std; + + +class ConsensusParameters : public ExtendParameters { +public: + string consensus_bam; // Base name of the two consensus bam files. + + bool need_3_end_adapter = false; + bool filter_qt_reads = false; + bool filter_single_read_consensus = false; + bool skip_consensus = false; +// bool use_flowspace_clustering; // generate one consensus read for each flowspace cluster + // functions + ConsensusParameters(int argc, char** argv); + void SetupFileIO(OptArgs &opts); +}; + + +#endif // CONSENSUSPARAMETERS_H + diff --git a/Analysis/VariantCaller/Consensus/FlowSpaceConsensus.cpp b/Analysis/VariantCaller/Consensus/FlowSpaceConsensus.cpp new file mode 100644 index 00000000..b0136951 --- /dev/null +++ b/Analysis/VariantCaller/Consensus/FlowSpaceConsensus.cpp @@ -0,0 +1,1165 @@ +/* Copyright (C) 2016 Thermo Fisher Scientific, Inc. All Rights Reserved */ + +//! @file FlowSpaceConsensus.cpp +//! @ingroup VariantCaller +//! @brief Cluster flow-synchronized reads; generate consensus measurements. + +#include "FlowSpaceConsensus.h" + +#include +#include "MiscUtil.h" +#include "ExtendedReadInfo.h" + +class ConsensusCounter { +private: + pthread_mutex_t mutextConsensusCounter_; + unsigned int num_func_fam_ = 0; + unsigned int num_consensus_reads_ = 0; + unsigned int num_single_read_consensus_ = 0; + unsigned int num_reads_in_func_fam_ = 0; + unsigned int num_reads_in_consensus_bam_ = 0; + unsigned int num_consensus_reads_need_aln_ = 0; + void PrintSummary_() + { + if (use_molecular_tag){ + cout << "[Stat] Summary of the flow-space consensus:" << endl + << " - Number of functional families identified = " << num_func_fam_ << endl + << " - Number of reads in the functional families identified = " << num_reads_in_func_fam_ << endl + << " - Number of consensus reads generated = " << num_consensus_reads_ << endl + << " - Number of consensus reads realigned by tmap = " << num_consensus_reads_need_aln_ << endl + << " - Number of reads that consists of the consensus reads = " << num_reads_in_consensus_bam_ << endl + << " - Number of single-read consensus = " << num_single_read_consensus_ << endl; + cout << " - Compression rate = "<< num_consensus_reads_ <<"/"<< num_reads_in_consensus_bam_ <<" = "; + if (num_reads_in_consensus_bam_ == 0){ + cout << "NaN"<< endl; + }else{ + cout << (double) num_consensus_reads_ / (double) num_reads_in_consensus_bam_ << endl; + } + cout << " - Average number of consensus reads per functional family = "; + if (num_func_fam_ == 0){ + cout << "NaN" << endl << endl << endl; + }else{ + cout << (double) num_consensus_reads_ / (double) num_func_fam_ << endl << endl << endl; + } + } + else{ + cout << "[Stat] Summary of the flow-space consensus:" << endl + << " - Number of reads processed = " << num_reads_in_func_fam_ << endl + << " - Number of reads that consists of the consensus reads = " << num_reads_in_consensus_bam_ << endl + << " - Number of consensus reads generated = " << num_consensus_reads_ << endl + << " - Number of consensus reads realigned by tmap = " << num_consensus_reads_need_aln_ << endl + << " - Number of single-read consensus = " << num_single_read_consensus_ << endl + << " - Compression rate = "<< num_consensus_reads_ <<"/"<< num_reads_in_consensus_bam_ << " = "; + if (num_reads_in_consensus_bam_ == 0){ + cout << "NaN" << endl << endl << endl; + }else{ + cout << (double) num_consensus_reads_ / (double) num_reads_in_consensus_bam_ << endl << endl << endl; + } + } + } +public: + ConsensusCounter(){}; + ConsensusCounter(bool is_mol_tag){ use_molecular_tag = is_mol_tag; }; + bool use_molecular_tag = false; + virtual ~ConsensusCounter() {PrintSummary_();} + void Count(unsigned int num_func_fam, + unsigned int num_consensus_reads, + unsigned int num_single_read_consensus, + unsigned int num_reads_in_func_fam, + unsigned int num_reads_in_consensus_bam, + unsigned int num_consensus_reads_need_aln) + { + pthread_mutex_lock(&mutextConsensusCounter_); + num_func_fam_ += num_func_fam; + num_consensus_reads_ += num_consensus_reads; + num_single_read_consensus_ += num_single_read_consensus; + num_reads_in_func_fam_ += num_reads_in_func_fam; + num_reads_in_consensus_bam_ += num_reads_in_consensus_bam; + num_consensus_reads_need_aln_ += num_consensus_reads_need_aln; + pthread_mutex_unlock(&mutextConsensusCounter_); + } +}; + +// Inputs: base_seq (can be vector or string), flow_order (any type with operator [] defined), num_flows (num_flows is the length of flow_order) +// Outputs: flowgram, base_idx_to_flow_idx +// Convert base_seq to flowgram associated with the flow_order +// base_idx_to_flow_idx[base_idx] is the flow index where base_seq[base_idx] incorporates. +// base_idx_to_flow_idx[base_idx] is set to -1 if incorporated flow of base_seq[base_idx] can not be specified by flow_order. +template +void BaseSeqToFlowgram (const BaseSeqI &base_seq, const FlowOrderI &flow_order, int num_flows, vector &flowgram, vector &base_idx_to_flow_idx) +{ + int flow_idx = 0; + unsigned int num_bases = base_seq.size(); + unsigned int base_idx = 0; + + base_idx_to_flow_idx.assign(base_seq.size(), -1); + flowgram.assign(num_flows, 0); + + while (flow_idx < num_flows and base_idx < num_bases) { + while (base_idx < num_bases and flow_order[flow_idx] == base_seq.at(base_idx)) { + base_idx_to_flow_idx[base_idx] = flow_idx; + ++flowgram[flow_idx]; + ++base_idx; + } + ++flow_idx; + } +} + +// This function is used to fix the bug in the ZC tag where it is possible to get ZC[0] != ZC[1] while flow_order[ZC[0]] = flow_order[ZC[1]] +// The bug is fixed in 5.2.1. +bool FixZcBug(vector &zc, const ion::FlowOrder& flow_order) +{ + bool is_fixed = false; + if (zc[0] != zc[1] and flow_order[zc[0]] == flow_order[zc[1]]){ + if (zc[0] < zc[1]) + zc[1] = zc[0]; + else + zc[0] = zc[1]; + is_fixed = true; + } + return is_fixed; +} + +// A "softer" criterion to determine x_mer == y_mer that allows some hp error. +// I claim x_mer and y_mer are "very different" if max(x_mer, y_mer) - min(x_mer, y_mer) > kHpLenDiffAllowed[max(x_mer, y_mer)] +// return true if x_mer and y_mer are "very different". +// Here kHpLenDiffAllowed is set heuristically. +//@TODO: optimize kHpLenDiffAllowed or come up with a better algorithm (e.g. K-run). +bool SofterHpDisriminator(int x_mer, int y_mer) +{ + const static vector kHpLenDiffAllowed = {0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8}; // I hard code the criterion. + const static int kMaxHpDefined = (int) kHpLenDiffAllowed.size() - 1; + return (abs(x_mer - y_mer) > kHpLenDiffAllowed[min(max(x_mer, y_mer), kMaxHpDefined)]); +} + +// base_seq = (KS + ZT + ZE) + template + (YE + YT) +// Here I do not reconstruct the 3' adapter because I don't have enough information. +void ReconstructBaseAsCalled(const Alignment* rai, string &base_seq) +{ + string read_base = rai->alignment.QueryBases; + if (rai->is_reverse_strand){ + RevComplementInPlace(read_base); + } + base_seq = rai->prefix_bases + read_base + rai->suffix_bases; +} + +// return the first/last hp length of base_seq if is_reverse_mode = true/false, respectively. +int FindFirstHpLen(const string& base_seq, bool is_reverse_mode = false) +{ + int base_seq_size = (int) base_seq.size(); + if (base_seq_size <= 1) + return base_seq_size; + int first_hp_len = 1; + int current_idx = 1; + int increment = 1; + char first_nuc = base_seq.at(0); + if (is_reverse_mode){ + current_idx = base_seq_size - 2; + increment = -1; + first_nuc = base_seq.at(base_seq_size - 1); + } + while (current_idx >= 0 and current_idx < base_seq_size){ + if (base_seq.at(current_idx) == first_nuc) + ++first_hp_len; + else + return first_hp_len; + current_idx += increment; + } + return first_hp_len; +} + +// Find the smallest index i >= start_flow such that flowgram_0[i] = flowgram_1[i] > 0 +// return -1 if not found +int GetNextIncorpFlowInCommon(const vector& flowgram_0, const vector& flowgram_1, unsigned int start_flow) +{ + unsigned int min_len = min(flowgram_0.size(), flowgram_1.size()); + int next_incorp_flow_in_common = -1; + for (unsigned int flow_idx = start_flow; flow_idx < min_len; ++flow_idx){ + if (flowgram_0[flow_idx] == flowgram_1[flow_idx] and flowgram_0[flow_idx] > 0){ + next_incorp_flow_in_common = flow_idx; + return next_incorp_flow_in_common; + } + } + return next_incorp_flow_in_common; +} + +// Find the largest index i <= start_flow such that flowgram_0[i] = flowgram_1[i] > 0 +// return -1 if not found +int GetLastIncorpFlowInCommon(const vector& flowgram_0, const vector& flowgram_1, unsigned int start_flow) +{ + int last_incorp_flow_in_common = -1; + unsigned int min_len = min(flowgram_0.size(), flowgram_1.size()); + if (min_len == 0){ + return last_incorp_flow_in_common; + } + if (start_flow >= min_len){ + start_flow = min_len - 1; + } + for (unsigned int flow_idx = start_flow; flow_idx != 0; --flow_idx){ + if (flowgram_0[flow_idx] == flowgram_1[flow_idx] and flowgram_0[flow_idx] > 0){ + last_incorp_flow_in_common = flow_idx; + return last_incorp_flow_in_common; + } + } + return last_incorp_flow_in_common; +} + +// Find the largest index i <= start_flow such that flowgram_0[i] = flowgram_1[i] > 0 +// return -1 if not found +int GetLastIncorpFlowAlmostInCommon(const vector& flowgram_0, const vector& flowgram_1, unsigned int start_flow) +{ + int last_incorp_flow_in_common = -1; + unsigned int min_len = min(flowgram_0.size(), flowgram_1.size()); + if (min_len == 0){ + return last_incorp_flow_in_common; + } + if (start_flow >= min_len){ + start_flow = min_len - 1; + } + for (unsigned int flow_idx = start_flow; flow_idx != 0; --flow_idx){ + if ((not SofterHpDisriminator(flowgram_0[flow_idx], flowgram_1[flow_idx])) and flowgram_0[flow_idx] > 0 and flowgram_1[flow_idx] > 0){ + last_incorp_flow_in_common = flow_idx; + return last_incorp_flow_in_common; + } + } + return last_incorp_flow_in_common; +} + +// This function determine whether flowgram_0[0:synchronized_len] and flowgram_1[0:synchronized_len] are flow-synchronized or not by simply looking at the discrepancy between the two flowgrams, +// where synchronized_len - 1 is the is last incorporated flow in common (i.e., the largest i such that flowgram_0[i] = flowgram_1[i] > 0). +// Alignment information is not needed! +// I claim the two flowsgrams are flow-synchronized if the following conditions are all satisfied. +// a) There exists no flow s.t. one flowgram is 0-mer while the other one is 2-mer or above. (i.e., 0-mer -> (>1-mer) is claimed to be not a sequencing error.) +// b) number of flows with "discrepancy" <= max_num_diff_flows, where discrepancy is defined by SofterHpDisriminator(...) +// c) number of flows with "discrepancy" in every window <= max_num_local_diff_flows +// where a window is a closed interval [window_start, window_end] where window_start, window_end are the only two incorporated flows in common in the window. +// If strict_flow_sync = true, then I don't allow any (0-mer) <-> (>0-mer) change. +// Consider y = H*x where y, x are the vectors of prediction and flowgram, respectively, H is the phasing matrix. +// strict_flow_sync guarantees the same structure of the phasing matrix. +bool IsFlowSynchronized(const vector& flowgram_0, const vector& flowgram_1, unsigned int& synchronized_len, unsigned int max_num_diff_flows, unsigned int max_num_local_diff_flows, bool strict_flow_sync) +{ + bool is_flow_synchronized = false; + // Get the last incorporated flow where the two flowgrams are in common. + synchronized_len = GetLastIncorpFlowAlmostInCommon(flowgram_0, flowgram_1, flowgram_0.size() - 1) + 1; + + if (synchronized_len == 0){ + return is_flow_synchronized; + } + + vector diff_flow_index; + int max_0_mer_diff = strict_flow_sync? 0 : 1; + diff_flow_index.reserve(max_num_diff_flows); + // Find the diff_flow_index from the back since we can return the function faster if they are not flow-synchronized. + for (unsigned int flow_idx = synchronized_len - 1; flow_idx != 0; --flow_idx){ + // Is there a delta at the flow? + bool is_diff = false; + if (flowgram_0[flow_idx] != flowgram_1[flow_idx]){ + if (flowgram_0[flow_idx] == 0 or flowgram_1[flow_idx] == 0){ + // A non-hp indel delta found. + // I don't allow 0-mer <-> (>1-mer). + // I don't allow 0-mer <-> (>0-mer) if strict_flow_sync = true. + // Claim not flow-synchronized. + if (abs((int) flowgram_0[flow_idx] - (int) flowgram_1[flow_idx]) > max_0_mer_diff){ + synchronized_len = 0; + return is_flow_synchronized; + } + is_diff = true; + } + // hp-indel + else if (SofterHpDisriminator((int) flowgram_0[flow_idx], (int) flowgram_1[flow_idx])){ + // The HP lengths of the two flowgram seem very different. + is_diff = true; + } + + if (is_diff){ + // The two flowgrams seem very different at the flow. + diff_flow_index.push_back(flow_idx); + if (diff_flow_index.size() > max_num_diff_flows){ + // The flows with discrepancy is greater than max_num_diff_flows. + // Claim not flow-synchronized. + synchronized_len = 0; + return is_flow_synchronized; + } + } + } + } + + if (diff_flow_index.size() <= max_num_local_diff_flows){ + // num_local_diff won't be greater than max_num_local_diff_flows + // I claim the two flowgrams are flow-synchronized. + is_flow_synchronized = true; + return is_flow_synchronized; + } + + // Define window = closed interval of [window_start, window_end] + int window_end = GetNextIncorpFlowInCommon(flowgram_0, flowgram_1, diff_flow_index[0]); + int window_start = window_end; + int i_diff = 0; + + // num_local_diff = the number of flows with discrepancy in the window + // I claim the flowgrams are not flow-synchronized if num_local_diff > max_num_local_diff_flows in any window. + // Sliding window approach to check num_local_diff for every window with complexity = Big O(diff_flow_index.size()) + // Note that diff_flow_index "must" be in "decreasing" order! + while (i_diff < (int) diff_flow_index.size() and window_start > -1){ + if ((int) diff_flow_index[i_diff] < window_start){ + window_end = window_start; + window_start = GetLastIncorpFlowInCommon(flowgram_0, flowgram_1, diff_flow_index[i_diff]); + unsigned int num_local_diff = 0; + int j_diff = i_diff; + while ((int) diff_flow_index[j_diff] >= window_start and j_diff < (int) diff_flow_index.size()){ + ++num_local_diff; + if (num_local_diff > max_num_local_diff_flows){ + // I find one window with num_local_diff > max_num_local_diff_flows + synchronized_len = 0; + return is_flow_synchronized; + } + ++j_diff; + } + i_diff = j_diff; + } + } + + // Now I have num_local_diff <= max_num_local_diff_flows in all windows. + // Claim the two flowgrams are flow-synchronized. + is_flow_synchronized = true; + return is_flow_synchronized; +} + +PrecomputeForClustering::PrecomputeForClustering(const Alignment* rai){ + if (not rai->alignment.GetTag("ZC", zc)){ + vector u_zc; + if (rai->alignment.GetTag("ZC", u_zc)){ + zc.assign(u_zc.size(), 0); + for (unsigned int i = 0; i < u_zc.size(); ++i){ + zc[i] = (int) u_zc[i]; + } + } + } + if ((not zc.empty()) and zc.size() < 3){ + cerr << "ERROR: the ZC tag with number of fields < 3 in read " << rai->alignment.Name << endl; + exit(1); + } + ReconstructBaseAsCalled(rai, base_seq); +} + +// ============================================================================== + +FlowSpaceCluster::FlowSpaceCluster(const Alignment* new_rai, const ion::FlowOrder* fo) + : flow_order_(fo), runid(new_rai->runid), flow_order_index(new_rai->flow_order_index), is_reverse_strand(new_rai->is_reverse_strand) +{ + PrecomputeForClustering precomp_for_new_rai(new_rai); + InitializeCluster_(new_rai); + AddNewMember_(new_rai, precomp_for_new_rai); +} + + +FlowSpaceCluster::FlowSpaceCluster(const Alignment* new_rai, const vector& flow_order_vector, PrecomputeForClustering& precomp_for_new_rai) + : flow_order_(&(flow_order_vector[new_rai->flow_order_index])), runid(new_rai->runid), flow_order_index(new_rai->flow_order_index), is_reverse_strand(new_rai->is_reverse_strand) +{ + InitializeCluster_(new_rai); + AddNewMember_(new_rai, precomp_for_new_rai); +}; + + +void FlowSpaceCluster::InitializeCluster_(const Alignment* new_rai) +{ + trim_info.prefix_bases = new_rai->prefix_bases; + trim_info.suffix_bases = new_rai->suffix_bases; + trim_info.prefix_end_flow = new_rai->prefix_flow; + trim_info.prefix_end_hp = FindFirstHpLen(trim_info.prefix_bases, true); + trim_info.suffix_start_hp = FindFirstHpLen(trim_info.suffix_bases, false); +} + +void FlowSpaceCluster::AddNewMember_(const Alignment* new_rai, PrecomputeForClustering& precomp_for_new_rai) +{ + cluster_members.push_back(new_rai); + // I use the cluster member with the highest mapping quality to represent this cluster. + if (new_rai->alignment.MapQuality > best_mapping_quality){ + best_mapping_quality = new_rai->alignment.MapQuality; + best_mapping_quality_index = (int) cluster_members.size() - 1; + template_base_seq_ = precomp_for_new_rai.base_seq; + + // Calculate the flowgram if I didn't do it before. + if (precomp_for_new_rai.flowgram.empty() or precomp_for_new_rai.base_index_to_flow_index.empty()){ + BaseSeqToFlowgram(template_base_seq_, *flow_order_, flow_order_->num_flows(), precomp_for_new_rai.flowgram, precomp_for_new_rai.base_index_to_flow_index); + } + template_flowgram_ = precomp_for_new_rai.flowgram; + + // Calculate suffix_start_flow + if (not trim_info.suffix_bases.empty()){ + trim_info.suffix_start_flow = precomp_for_new_rai.base_index_to_flow_index[template_base_seq_.size() - trim_info.suffix_bases.size()]; + } + else{ + // Set to the end flow of template since there is no suffix_bases. + trim_info.suffix_start_flow = precomp_for_new_rai.base_index_to_flow_index.back(); + } + + if (trim_info.suffix_start_flow < 0){ + cerr << "ERROR: insert_base_end_flow outside of [0, num_flows) range in read " << new_rai->alignment.Name << endl; + exit(1); + } + + if (not precomp_for_new_rai.zc.empty()){ + adapter_start_flow = precomp_for_new_rai.zc[0]; + } + else{ + adapter_start_flow = -1; + } + } +} + + +bool FlowSpaceCluster::AskToJoinMe(const Alignment* query_rai) +{ + PrecomputeForClustering precomp_for_query_rai(query_rai); + return AskToJoinMe(query_rai, precomp_for_query_rai); +} + +// The query_rai can join the cluster if query_rai is flow-synchronized with the cluster +// (Note): Sort the reads by the mapping quality in the decreasing order and then call this function is highly recommended! +// (Note): Don't waste your time inputting query_rai if IsIsolateRead(query_rai) is true, since it won't join me anyway. +bool FlowSpaceCluster::AskToJoinMe(const Alignment* query_rai, PrecomputeForClustering& precomp_for_query_rai) +{ + // I only cluster the reads with 3' adapter found for molecular tagging. + // @TODO: It can be an optional constraint for general purpose. + bool is_join = false; + if (precomp_for_query_rai.zc.empty()){ + return is_join; + } + + // The most common reason that a read can't join the cluster. + if (precomp_for_query_rai.zc[0] != adapter_start_flow) // I require the adapter start flow must be the same. + return is_join; + + if (query_rai->runid != runid // The query read must come from the same run + or query_rai->is_reverse_strand != is_reverse_strand // The query read must on the same strand + or query_rai->prefix_bases != trim_info.prefix_bases // The query read must have exactly the same prefix_bases (hp error not allowed) + or query_rai->suffix_bases != trim_info.suffix_bases // The query read must have exactly the same suffix_bases (hp error not allowed) + or query_rai->prefix_flow != trim_info.prefix_end_flow // Let's check the prefix end flow again, though it has been carried out by checking the prefix_bases + or query_rai->flow_order_index != flow_order_index) // I require the same flow order index (it is redundant since every run_id has a unique flow_order index) + return is_join; + + // Easy task: Claim flow-synchronized if base_seq of the query_rai and this->template_base_seq_ are identical. + is_join = precomp_for_query_rai.base_seq == template_base_seq_; + if (is_join){ + AddNewMember_(query_rai, precomp_for_query_rai); + return is_join; + } + + // Calculate the flowgram for query_rai if we haven't done yet. + if (precomp_for_query_rai.flowgram.empty() or precomp_for_query_rai.base_index_to_flow_index.empty()){ + BaseSeqToFlowgram(precomp_for_query_rai.base_seq, *flow_order_, flow_order_->num_flows(), precomp_for_query_rai.flowgram, precomp_for_query_rai.base_index_to_flow_index); + } + + unsigned int synchronized_len = 0; + // Is precomp_for_query_rai.flowgram flow-synchronized with template_flowgram_? + is_join = IsFlowSynchronized(template_flowgram_, precomp_for_query_rai.flowgram, synchronized_len, kMaxNumDiffFlows_, kMaxNumLocalDiffFlows_, kStrictFlowSync_); + if (is_join){ + // Double check if synchronized_len covers suffix_start_flow + // e.g., a quality trimmed read fails this test. + if ((int) synchronized_len < trim_info.suffix_start_flow + 1){ + is_join = false; + } + else{ + AddNewMember_(query_rai, precomp_for_query_rai); + } + return is_join; + } + return is_join; +} + +// ============================================================================== + +FlowSpaceConsensusMaster::FlowSpaceConsensusMaster(PersistingThreadObjects* thread_objects, + const ReferenceReader* reference_reader, + const InputStructures* global_context, + const string& basecaller_ver): + thread_objects_(thread_objects), reference_reader_(reference_reader), global_context_(global_context) +{ + flow_order_index_ = -1; + flow_order_ = NULL; + + is_suffix_counted_in_za_ = false; + // Note that the format of BaseCaller version is "5.2-x" + if (basecaller_ver.substr(0, 4) == "5.2-"){ + long release_ver = strtol(basecaller_ver.substr(4).c_str(), NULL, 0); + // The change of ZA definition starts from BaseCaller 5.2-21. Before that, suffix tags are counted in ZA. + is_suffix_counted_in_za_ = release_ver < 21; + }else if (basecaller_ver.substr(0, 17) == "5.3-0-week160330-"){ + is_suffix_counted_in_za_ = true; + } +} + + +struct RecalGroup{ + vector group_members; + uint16_t mapq_sum; + RecalGroup(){ mapq_sum = 0;}; + RecalGroup(const Alignment* rai){ + group_members = {rai}; + mapq_sum = rai->alignment.MapQuality; + }; + void AddNew(const Alignment* rai){ + group_members.push_back(rai); + mapq_sum += rai->alignment.MapQuality; + }; + // Compare two recalibration groups. + // A group wins if it has more reads. If ties, the group with larger accumulated mapping quality wins. + bool operator>(const RecalGroup& rhs){ + if (group_members.size() == rhs.group_members.size()){ + return (mapq_sum > rhs.mapq_sum); + } + return (group_members.size() > rhs.group_members.size()); + }; +}; + +void FlowSpaceConsensusMaster::InitializeForBaseCalling(const FlowSpaceCluster& my_cluster, const vector& consensus_phase_params, bool suppress_recal, string &consensus_read_name){ + flow_order_index_ = my_cluster.flow_order_index; + flow_order_ = &(global_context_->flow_order_vector.at(flow_order_index_)); + // Set phasing parameters + thread_objects_->SetModelParameters(flow_order_index_, consensus_phase_params); + // Disable use of a previously loaded recalibration model + thread_objects_->DisableRecalibration(flow_order_index_); + + //@TODO: Need to come up with a more clever way to handle recalibration. + bool recalibration_enabled = false; + + // Let's initialize recalibration + if (not suppress_recal and global_context_->do_recal.recal_is_live()){ + recalibration_enabled = true; + vector > recal_groups_vec; + recal_groups_vec.reserve(my_cluster.cluster_members.size()); + for (vector::const_iterator read_it = my_cluster.cluster_members.begin(); read_it != my_cluster.cluster_members.end(); ++read_it){ + string found_key = global_context_->do_recal.FindKey((*read_it)->runid, (*read_it)->well_rowcol[1], (*read_it)->well_rowcol[0]); + MultiAB multi_ab; + global_context_->do_recal.getAB(multi_ab, found_key, (*read_it)->well_rowcol[1], (*read_it)->well_rowcol[0]); + if (not multi_ab.Valid()){ + recalibration_enabled = false; + cerr << "Warning: The read "<< (*read_it)->alignment.Name << " has invalid recalibrartion MultiAB. Recalibration is disabled in the cluster."<< endl; + break; + } + bool group_exists = false; + for (vector >::iterator rc_group_it = recal_groups_vec.begin(); rc_group_it != recal_groups_vec.end(); ++rc_group_it){ + if (multi_ab.aPtr == rc_group_it->first.aPtr and multi_ab.bPtr == rc_group_it->first.bPtr){ + group_exists = true; + rc_group_it->second.AddNew(*read_it); + break; + } + } + if (not group_exists){ + recal_groups_vec.push_back(pair(multi_ab, RecalGroup(*read_it))); + } + } + if (recalibration_enabled){ + vector >::iterator best_rc_group_it = recal_groups_vec.begin(); + for (vector >::iterator rc_group_it = recal_groups_vec.begin(); rc_group_it != recal_groups_vec.end(); ++rc_group_it){ + // Find the "best" recalibration result, what I mean "best" is defined by the > operator + if (rc_group_it->second > best_rc_group_it->second){ + best_rc_group_it = rc_group_it; + } + } + // I use the best recalibration result as the recalibration for the consensus read. + thread_objects_->SetAsBs(flow_order_index_, best_rc_group_it->first.aPtr, best_rc_group_it->first.bPtr); + // query recalibration structure using row, column, entity. + // The consensus_read_name set here will let tvc use the desired recal results. + consensus_read_name = best_rc_group_it->second.group_members[0]->alignment.Name; + } + } + + // Use the read name with the best mapq as the consensus read name if recalibration is not enabled. + if (not recalibration_enabled){ + consensus_read_name = my_cluster.cluster_members[my_cluster.best_mapping_quality_index]->alignment.Name; + } +} + + +// this->InitializeForBaseCalling(short, const vector&) must be done first. +// Input: flow_synchronized_measurements, trim_info +// Outputs: consensus_measurements, prefix_trimmed_consensus_base, measurements_sd +// Call this function if I use void GetMeasurements(const vector&, vector< vector >&) to get flow_synchronized_measurements +bool FlowSpaceConsensusMaster::CalculateConsensusMeasurements(const vector >& flow_synchronized_measurements, + const TrimInfo& trim_info, + vector& consensus_measurements, + vector& measurements_sd) +{ + vector *> flow_synchronized_measurements_ptr; + flow_synchronized_measurements_ptr.assign(flow_synchronized_measurements.size(), NULL); + for (unsigned int i_read = 0; i_read < flow_synchronized_measurements.size(); ++i_read){ + flow_synchronized_measurements_ptr[i_read] = &flow_synchronized_measurements[i_read]; + } + return CalculateConsensusMeasurements(flow_synchronized_measurements_ptr, trim_info, consensus_measurements, measurements_sd); +} + + +// this->InitializeForBaseCalling(short, const vector&) must be done first. +// Input: flow_synchronized_measurements, trim_info +// Outputs: consensus_measurements, prefix_trimmed_consensus_base, measurements_sd +// Call this function if I use void GetMeasurements(const vector&, vector< vector *>&) to get flow_synchronized_measurements +bool FlowSpaceConsensusMaster::CalculateConsensusMeasurements(const vector *>& flow_synchronized_measurements, + const TrimInfo& trim_info, + vector& consensus_measurements, + vector& measurements_sd) +{ + bool success = false; + const unsigned int num_reads_in_cluster = flow_synchronized_measurements.size(); + const double num_reads_in_cluster_double = (double) num_reads_in_cluster; + const double num_reads_in_cluster_minus_one = (double) ((int) num_reads_in_cluster - 1); + + // The flow space consensus is trivial for single read or no read + // I don't attempt to generate consensus measurement for this guy. + if (num_reads_in_cluster <= 1){ + return success; + } + + int min_measurements_len = (int) flow_synchronized_measurements.at(0)->size(); + for (unsigned int i_read = 1; i_read < num_reads_in_cluster; ++i_read){ + min_measurements_len = min(min_measurements_len, (int) flow_synchronized_measurements.at(i_read)->size()); + } + + // consensus_measurements[flow] = mean(flow_synchronized_measurements[:][flow]) + consensus_measurements.resize(flow_order_->num_flows()); // Note that Treephaser requires the length of measurement = length of flow order + // Taking the mean measurements of reads. Set to zero if the flow >= min_measurements_len + for (int i_flow = 0; i_flow < min_measurements_len; ++i_flow){ + double mean_measuremet = 0.0; // Use double to get better numerical precision for cumulative sum + for (unsigned int i_read = 0; i_read < num_reads_in_cluster; ++i_read){ + mean_measuremet += (double) flow_synchronized_measurements.at(i_read)->at(i_flow); + } + mean_measuremet /= num_reads_in_cluster_double; + consensus_measurements.at(i_flow) = (float) mean_measuremet; + } + + // Set data for base calling + consensus_read_.SetData(consensus_measurements, flow_order_->num_flows()); + consensus_read_.sequence.resize(trim_info.prefix_bases.size()); + for (unsigned int i_base = 0; i_base < trim_info.prefix_bases.size(); ++i_base){ + consensus_read_.sequence[i_base] = trim_info.prefix_bases[i_base]; + } + + // Call Treephaser to solve the consensus_measurements + int safe_start_flow = min(trim_info.prefix_end_flow, (int) consensus_read_.normalized_measurements.size() - 1); + thread_objects_->SolveRead(flow_order_index_, consensus_read_, safe_start_flow, min_measurements_len); + + // Check Treephaser got any solution or not + if (consensus_read_.sequence.size() <= trim_info.prefix_bases.size()) + return success; + + // Shrink the length of consensus_measurements to what we want to preserve + consensus_measurements.resize(min_measurements_len); + + // Calculate the standard deviation of the measurements + measurements_sd.resize(min_measurements_len); + for (int i_flow = 0; i_flow < min_measurements_len; ++i_flow){ + double mean_squared_err = 0.0; + for (unsigned int i_read = 0; i_read < num_reads_in_cluster; ++i_read){ + double err = (double) (flow_synchronized_measurements.at(i_read)->at(i_flow) - consensus_measurements.at(i_flow)); + mean_squared_err += (err * err); + } + mean_squared_err /= num_reads_in_cluster_minus_one; // It should be safe since I don't deal with any single read cluster. + measurements_sd.at(i_flow) = (float) sqrt(mean_squared_err); + } + success = true; + return success; +} + +// Apply prefix and suffix trimming on consensus_read_.sequence according to trim_info +// Input: trim_info (contains the trimming information) +// Output: prefix and suffix trimmed base sequence, start_flow +// Note that I do the trimming in flow space -- the bases actually trimmed may be different from trim_info.prefix_base and trim.suffix_bases. +void FlowSpaceConsensusMaster::TrimPrefixSuffixBases(const TrimInfo& trim_info, string& trimmed_consensus_bases, int& start_flow) +{ + vector bases_index_to_flow_index; + vector consensus_flowgram; + BaseSeqToFlowgram(consensus_read_.sequence, *flow_order_, flow_order_->num_flows(), consensus_flowgram, bases_index_to_flow_index); + int trim_prefix_len = 0; + int trim_suffix_len = 0; + + // I shall trim them all if the last incorporated flow is less than trim_info.prefix_end_flow. + if (bases_index_to_flow_index.back() < trim_info.prefix_end_flow){ + trimmed_consensus_bases.resize(0); + start_flow = -1; + return; + } + + for (int flow_idx = 0; flow_idx < trim_info.prefix_end_flow; ++flow_idx){ + trim_prefix_len += consensus_flowgram[flow_idx]; + } + // I suppose to obtain at least (trim_info.prefix_end_hp)-mer at the (trim_info.prefix_end_flow)-th flow, but I don't. + // So I don't trim more than (consensus_flowgram[trim_info.prefix_end_flow])-mer at this flow. + trim_prefix_len += min(consensus_flowgram[trim_info.prefix_end_flow], trim_info.prefix_end_hp); + // start_flow is the first incorporated flow of the template. + start_flow = bases_index_to_flow_index[trim_prefix_len]; + + if (bases_index_to_flow_index.back() >= trim_info.suffix_start_flow){ + for (int flow_idx = bases_index_to_flow_index.back(); flow_idx > trim_info.suffix_start_flow; --flow_idx){ + trim_suffix_len += consensus_flowgram[flow_idx]; + } + // I suppose to obtain at least (trim_info.suffix_start_hp)-mer at the (trim_info.suffix_start_flow)-th flow, but I don't. + // So I don't trim more than (consensus_flowgram[trim_info.suffix_start_flow])-mer at this flow. + trim_suffix_len += min(consensus_flowgram[trim_info.suffix_start_flow], trim_info.suffix_start_hp); + } + + int trimmed_consensus_bases_len = (int) consensus_read_.sequence.size() - (trim_prefix_len + trim_suffix_len); + trimmed_consensus_bases.resize(trimmed_consensus_bases_len); + int untrimmed_base_idx = trim_prefix_len; + for (int trimmed_base_idx = 0; trimmed_base_idx < trimmed_consensus_bases_len; ++trimmed_base_idx){ + trimmed_consensus_bases[trimmed_base_idx] = consensus_read_.sequence[untrimmed_base_idx]; + ++untrimmed_base_idx; + } +} + +// This function works for the case where the measurements have been written in Alignment +void FlowSpaceConsensusMaster::GetMeasurements(const vector& cluster_members, vector *>& flow_synchronized_measurements_ptr) +{ + flow_synchronized_measurements_ptr.assign(cluster_members.size(), NULL); + for (unsigned int read_idx = 0; read_idx < cluster_members.size(); ++read_idx) + { + if (cluster_members[read_idx]->measurements.empty()){ + cerr << "ERROR: Normalized measurements not initialized in read " << cluster_members[read_idx]->alignment.Name << endl; + exit(1); + } + flow_synchronized_measurements_ptr[read_idx] = &(cluster_members[read_idx]->measurements); + } +} + +// This function works for the case where the measurements were not written in Alignment +void FlowSpaceConsensusMaster::GetMeasurements(const vector& cluster_members, vector< vector >& flow_synchronized_measurements) +{ + flow_synchronized_measurements.reserve(cluster_members.size()); + for (vector::const_iterator read_it = cluster_members.begin(); read_it != cluster_members.end(); ++read_it) + { + vector quantized_measurements; + if (not (*read_it)->alignment.GetTag("ZM", quantized_measurements)) { + cerr << "ERROR: Normalized measurements ZM:tag is not present in read " << (*read_it)->alignment.Name << endl; + exit(1); + } + flow_synchronized_measurements.push_back(vector(quantized_measurements.size())); + for (unsigned int i_flow = 0; i_flow < quantized_measurements.size(); ++i_flow){ + flow_synchronized_measurements.back()[i_flow] = ((float) quantized_measurements[i_flow]) / 256.0f; + } + } +} + +// consensus_phase_params = mean of the phase_params +void FlowSpaceConsensusMaster::CalculateConsensusPhaseParams(const vector& cluster_members, vector& consensus_phase_params, bool is_zero_droop) +{ + consensus_phase_params.assign(3, 0.0f); + float num_reads = (float) cluster_members.size(); + assert(num_reads > 0.0f); + for (vector::const_iterator read_it = cluster_members.begin(); read_it != cluster_members.end(); ++read_it){ + consensus_phase_params[0] += ((*read_it)->phase_params[0]); + consensus_phase_params[1] += ((*read_it)->phase_params[1]); + } + consensus_phase_params[0] /= num_reads; + consensus_phase_params[1] /= num_reads; + + if (not is_zero_droop){ + for (vector::const_iterator read_it = cluster_members.begin(); read_it != cluster_members.end(); ++read_it){ + consensus_phase_params[2] += ((*read_it)->phase_params[2]); + } + consensus_phase_params[2] /= num_reads; + } +} + + +// Get the length of trimmed insert bases using the ZA tag where ZA = number of insert bases. +// Note that insert bases = template + YE + YT in 5.2. +// Note that insert bases = template in 5.2.1 and after. +// Input is_suffix_in_za = true if BaseCaller version <= 5.2; false if BaseCaller version >= 5.2.1 +int LenTrimmedInsertBases(const Alignment* rai, bool is_suffix_in_za) +{ + int num_trimmed_bases = 0; + int za = -1; + string ye = ""; + int ye_len = 0; + int len_QueryBases = (int) rai->alignment.QueryBases.size(); + + if( not rai->alignment.GetTag("ZA", za)){ + uint32_t u_za = 0; + if (not rai->alignment.GetTag("ZA", u_za)) { + cerr << "ERROR: ZA tag not found in read " << rai->alignment.Name << endl; + exit(1); + } + za = (int) u_za; + } + if (za < len_QueryBases){ + cerr << "Error: za < QueryBases.size() in read " << rai->alignment.Name << endl; + exit(1); + } + num_trimmed_bases = is_suffix_in_za? za - (len_QueryBases + rai->suffix_bases.size()) : za - len_QueryBases; + return num_trimmed_bases; +} + + +// Input is_suffix_in_za = true if BaseCaller version <= 5.2; false if BaseCaller version >= 5.2.1 +bool IsQualityTrimmedRead(const Alignment* rai, bool is_suffix_in_za){ + // It should be is_quality_trimmed = ( LenTrimmedInsertBases(rai) > 0) + // But the bases additionally trimmed by with heal-tag-indel-hp as also counted in LenTrimmedInsertBases(rai). + // The number 4 is safe against this issue. + int max_num_trimmed_bases_allowed = is_suffix_in_za? 4 : 0; + bool is_quality_trimmed = abs(LenTrimmedInsertBases(rai, is_suffix_in_za)) > max_num_trimmed_bases_allowed; + return is_quality_trimmed; +} + +// The return value indicates whether the consensus read needs realignment or not. +// Two cases where realignment is not needed +// (Case 1): The consensus query bases is exactly the same as the template read in the cluster. +// (Case 2): The consensus query bases all matches the reference. +// Usually, only a very small portion of consensus reads needs realignment. +bool FlowSpaceConsensusMaster::SaveToBamAlignment(const FlowSpaceCluster& my_cluster, + const vector& consensus_phase_params, + const vector& consensus_measurements, + const vector& measurements_sd, + const string& trimmed_bases, + const string& consensus_read_name, + int start_flow, + BamAlignment& alignment) +{ + // Use the read with the best mapping quality in the cluster as a template read. + // The tags "ZT", "ZE", "YT", "YE", "RG", etc. follow the template read. + // Note that The choice of the read name will affect the recaliration results when running tvc with the consensus bam. + const BamAlignment& template_alignment = my_cluster.cluster_members[my_cluster.best_mapping_quality_index]->alignment; + alignment = template_alignment; + + // Set the query bases + alignment.Name = consensus_read_name; + alignment.QueryBases = trimmed_bases; + alignment.Length = (int32_t) alignment.QueryBases.size(); + if (my_cluster.is_reverse_strand){ + RevComplementInPlace(alignment.QueryBases); + } + + // If the query bases of the consensus read is exactly the same as the template read, then I don't need to realign the consensus read. + bool need_realign = (alignment.QueryBases != template_alignment.QueryBases); + + // If not exactly the same as the template, then let's try another trivial alignment where the consensus query bases all matches the reference + if (need_realign){ + bool all_match_ref = false; + string template_md = ""; + template_alignment.GetTag("MD", template_md); + // If the template query bases all matches the reference, then no need to check again. + if ((template_md != to_string((int) template_alignment.QueryBases.size())) + or (alignment.QueryBases.size() != template_alignment.QueryBases.size())){ + long try_start_position = template_alignment.Position; + if (my_cluster.is_reverse_strand){ + int template_xm = (int) alignment.QueryBases.size(); // XM is the ion tag that specifies the number of ref bases spanned by the alignment. + template_alignment.GetTag("XM", template_xm); + // The actual "start" position of a reverse read of should be the end of the alignment. + try_start_position = template_alignment.Position + (long) template_xm - (long) alignment.QueryBases.size(); + } + + string reference_bases = reference_reader_->substr((int) (template_alignment.RefID), try_start_position, (long) alignment.QueryBases.size()); + all_match_ref = alignment.QueryBases == reference_bases; + + if (all_match_ref){ + need_realign = false; + alignment.Position = try_start_position; + alignment.Qualities = "*"; + // I set MapQ super sloppily by adding 3 (since from not all matches to all matches it means that the mapping quality is getting better). + // Also, according to the spec, no alignments should be assigned mapping quality 255. + alignment.MapQuality = (uint16_t) (max(254 - 3, (int) alignment.MapQuality) + 3); + alignment.CigarData.assign(1, CigarOp('M', (uint32_t) alignment.QueryBases.size())); // All-match cigar + alignment.EditTag("MD", "Z", to_string(alignment.QueryBases.size())); // All matches MD + alignment.EditTag("NM", "i", 0); // no mismatch + alignment.EditTag("XM", "i", (int) alignment.QueryBases.size()); + } + } + } + + if (need_realign){ + alignment.CigarData.assign(1, CigarOp('M', (uint32_t) alignment.QueryBases.size())); // dummay cigar that claim all M + alignment.Qualities = "*"; + alignment.MapQuality = 0; + alignment.RemoveTag("MD"); + alignment.RemoveTag("NM"); + alignment.RemoveTag("XM"); + } + + // Add/Edit/Remove tags + if (is_suffix_counted_in_za_){ + alignment.EditTag("ZA", "i", (int) (trimmed_bases.size() + my_cluster.trim_info.suffix_bases.size())); + }else{ + alignment.EditTag("ZA", "i", (int) trimmed_bases.size()); + } + alignment.EditTag("ZF", "i", start_flow); + alignment.EditTag("ZG", "i", my_cluster.adapter_start_flow); + alignment.EditTag("ZC", vector {my_cluster.adapter_start_flow, -1, -1, -1}); // ZC[1], ZC[2], ZC[3] may vary members from members, so I don't save them for the consensus reads. + alignment.EditTag("ZP", consensus_phase_params); + alignment.RemoveTag("ZB"); // ZB may vary members from members, so I don't save them for the consensus reads. + + // Add the ZM tag + vector vec_temp(consensus_measurements.size()); + for (unsigned int i_flow = 0; i_flow < consensus_measurements.size(); ++i_flow){ + vec_temp[i_flow] = (int16_t) (consensus_measurements[i_flow] * 256.0f); + } + alignment.EditTag("ZM", vec_temp); + + // New tags "ZR", "ZN", "ZS" from consensus + // Check the pre-existence of these tags. + for (vector::const_iterator read_it = my_cluster.cluster_members.begin(); read_it != my_cluster.cluster_members.end(); ++read_it) + { + if ((*read_it)->alignment.HasTag("ZN") or (*read_it)->alignment.HasTag("ZR") or (*read_it)->alignment.HasTag("ZS")){ + cerr << "Warning: The tag ZN, ZR or ZS is found in the read "<< (*read_it)->alignment.Name << ". " + << "The input bam file may contain unrecognized tags or may be a consensus bam. " + << "Some information may be lost or the ZM, ZP, ZS, ZR, ZN tags may be calculated incorrectly." << endl; + } + } + + vec_temp.resize(measurements_sd.size()); + for (unsigned int i_flow = 0; i_flow < measurements_sd.size(); ++i_flow){ + vec_temp[i_flow] = (int16_t) (measurements_sd[i_flow] * 256.0f); + } + alignment.EditTag("ZS", vec_temp); + + alignment.EditTag("ZR", "i", (int) my_cluster.cluster_members.size()); // ZR tag = read count, number of reads that form the consensus read + string read_names = ""; + for (unsigned int i_member = 0; i_member < my_cluster.cluster_members.size(); ++i_member){ + read_names += my_cluster.cluster_members[i_member]->alignment.Name; + if (i_member != my_cluster.cluster_members.size() - 1){ + read_names += ";"; + } + } + for (string::iterator c_it = read_names.begin(); c_it != read_names.end(); ++c_it) + if (*c_it == ':') {*c_it = '.';} // use "." to replace ":" + alignment.EditTag("ZN", "Z", read_names); // ZN tag = query names of the reads that from the consensus read + + return need_realign; +} + +bool CompareMapQ(const Alignment* const rai_1, const Alignment* const rai_2){ + return rai_1->alignment.MapQuality > rai_2->alignment.MapQuality; +} + +void AppendPositionTicket(list::iterator& position_ticket, Alignment* const alignment) +{ + if (position_ticket->begin == NULL) + position_ticket->begin = alignment; + if (position_ticket->end != NULL) + position_ticket->end->next = alignment; + position_ticket->end = alignment; + position_ticket->end->next = NULL; +} + +// Input: family_members +// Output: consensus_position_ticket, aln_needed_consensus_position_ticket +unsigned int FlowSpaceConsensusMaster::FlowSpaceConsensusOneFamily(vector& family_members, + list::iterator& consensus_position_ticket, + list::iterator& aln_needed_consensus_position_ticket) +{ + // For ConsensusCounter + static ConsensusCounter my_counter(consensus_for_molecular_tag); + + if (family_members.empty()){ + return 0; + } + + unsigned int add_to_num_func_fam = family_members.empty()? 0 : 1; + unsigned int add_to_num_consensus_reads = 0; + unsigned int add_to_num_single_read_consensus = 0; + unsigned int add_to_num_reads_in_func_fam = family_members.size(); + unsigned int add_to_num_reads_in_consensus_bam = 0; + unsigned int add_to_num_consensus_reads_need_aln = 0; + + vector flow_space_clusters; + vector isolated_reads; // reads w/o 3' adapter found or quality trimmed are isolate reads. + + // This should be enough most of the time. + if (consensus_for_molecular_tag){ + flow_space_clusters.reserve(4); + if ((not filter_single_read_consensus) and (not (need_3_end_adapter and filter_qt_reads))){ + isolated_reads.reserve(8); + } + }else{ + flow_space_clusters.reserve(32); + if ((not filter_single_read_consensus) and (not (need_3_end_adapter and filter_qt_reads))){ + isolated_reads.reserve(add_to_num_reads_in_func_fam / 8); + } + } + + // (Step 0): sort the reads by mapping quality + sort(family_members.begin(), family_members.end(), CompareMapQ); + + // (Step 1): Flow space clustering + for (vector::const_iterator member_it = family_members.begin(); member_it != family_members.end(); ++member_it) + { + const Alignment* rai = *member_it; // Now I do flowspace clustering for the read rai. + + // Did BaseCaller find 3' adapter in the read? + bool is_adapter_found = (rai->alignment.HasTag("ZC") and rai->alignment.HasTag("ZA")); + if (not is_adapter_found){ + if (not (need_3_end_adapter or filter_single_read_consensus)){ + isolated_reads.push_back(rai); + } + continue; + } + + // Is the read quality trimmed? + bool is_quality_trimmed_read = IsQualityTrimmedRead(rai, is_suffix_counted_in_za_); + if (is_quality_trimmed_read){ + if (not (filter_qt_reads or filter_single_read_consensus)){ + isolated_reads.push_back(rai); + } + continue; + } + + PrecomputeForClustering precomp_for_rai(rai); // The information in precomp_for_rai may be used frequently + bool is_in_existing_cluster = false; // Can I join any existing cluster + + for (vector::iterator cluster_it = flow_space_clusters.begin(); cluster_it != flow_space_clusters.end(); ++cluster_it){ + // Try to join any existing cluster if possible + if (cluster_it->AskToJoinMe(rai, precomp_for_rai)){ + is_in_existing_cluster = true; + break; // The read join the cluster. No need to seek joining others. + } + } + + if (not is_in_existing_cluster){ + // The read can not join any existing cluster. So it forms a new cluster. + flow_space_clusters.push_back(FlowSpaceCluster(rai, global_context_->flow_order_vector, precomp_for_rai)); + } + } + + // How many consensus reads will be generated for the reads in family_members? + add_to_num_consensus_reads = flow_space_clusters.size() + isolated_reads.size(); + add_to_num_reads_in_consensus_bam += isolated_reads.size(); + add_to_num_single_read_consensus += isolated_reads.size(); + + // (Step 2a): BamAlignment for isolate reads: no extra action required + for (vector::iterator read_it = isolated_reads.begin(); read_it != isolated_reads.end(); ++read_it) + { + // Append a new consensus alignment to consensus_position_ticket + Alignment* consensus_alignment = new Alignment; + consensus_alignment->alignment = (*read_it)->alignment; + AppendPositionTicket(consensus_position_ticket, consensus_alignment); + } + + // (Step 2b): BamAlignment for clusters + for (vector::iterator cluster_it = flow_space_clusters.begin(); cluster_it != flow_space_clusters.end(); ++cluster_it) + { + // The cluster consists of single read or no read, no additional action required + if (cluster_it->cluster_members.size() <= 1){ + if (cluster_it->cluster_members.empty() or filter_single_read_consensus){ + continue; + } + add_to_num_reads_in_consensus_bam += cluster_it->cluster_members.size(); + + // Append a new consensus alignment to consensus_position_ticket + Alignment* consensus_alignment = new Alignment; + consensus_alignment->alignment = cluster_it->cluster_members[0]->alignment; + AppendPositionTicket(consensus_position_ticket, consensus_alignment); + ++add_to_num_single_read_consensus; + continue; + } + add_to_num_reads_in_consensus_bam += cluster_it->cluster_members.size(); + + // Now I calculate the consensus_phase_params, consensus_measurements, trimmed_consensus_bases for the cluster + vector > measurements_in_cluster; + vector consensus_phase_params; // consensus phasing parameters for the cluster + vector consensus_measurements; // consensus measurements for the cluster + vector measurements_sd; // measurements deviation from consensus_measurements + string trimmed_consensus_bases; // prefix/suffix trimmed consensus base sequence as called by BaseCaller + int start_flow; // The first incorporated flow of trimmed_consensus_bases + + // Calculate consensus phase parameters and get the measurements of the reads + CalculateConsensusPhaseParams(cluster_it->cluster_members, consensus_phase_params, true); + + // Initialize BaseCaller + // Use the read with the best mapq as the template read of the cluster. + string consensus_read_name; + InitializeForBaseCalling(*cluster_it, consensus_phase_params, suppress_recalibration, consensus_read_name); + + // Calculate consensus measurements + GetMeasurements(cluster_it->cluster_members, measurements_in_cluster); + bool success = CalculateConsensusMeasurements(measurements_in_cluster, cluster_it->trim_info, consensus_measurements, measurements_sd); + + if (not success){ + cerr << "Warning: Fail to calculate the consensus measurements of the cluster ("; + for (vector::const_iterator read_it = cluster_it->cluster_members.begin(); read_it != cluster_it->cluster_members.end(); ++read_it){ + cerr << (*read_it)->alignment.Name <<", "; + } + cerr << "). The cluster will be ignored." << endl; + continue; + } + + // Trim prefix and suffix bases, note that trimmed_consensus_bases is the trimmed bases as called by BaseCaller + TrimPrefixSuffixBases(cluster_it->trim_info, trimmed_consensus_bases, start_flow); + + // Write to the consensus information to a new Alignment + Alignment* consensus_alignment = new Alignment; + bool aln_needed = SaveToBamAlignment(*cluster_it, consensus_phase_params, consensus_measurements, measurements_sd, trimmed_consensus_bases, consensus_read_name, start_flow, consensus_alignment->alignment); + + if (aln_needed){ + ++add_to_num_consensus_reads_need_aln; + // Append the new consensus alignment to consensus_position_ticket + AppendPositionTicket(aln_needed_consensus_position_ticket, consensus_alignment); + } + else{ + AppendPositionTicket(consensus_position_ticket, consensus_alignment); + } + } + my_counter.Count(add_to_num_func_fam, + add_to_num_consensus_reads, + add_to_num_single_read_consensus, + add_to_num_reads_in_func_fam, + add_to_num_reads_in_consensus_bam, + add_to_num_consensus_reads_need_aln); + return add_to_num_reads_in_consensus_bam; +} + +void FlowSpaceConsensusMaster::PropagateFlowspaceConsensusParameters(const ConsensusParameters& my_param, bool use_mol_tag){ + suppress_recalibration = my_param.program_flow.suppress_recalibration; + filter_qt_reads = my_param.filter_qt_reads; + filter_single_read_consensus = my_param.filter_single_read_consensus; + need_3_end_adapter = my_param.need_3_end_adapter; + consensus_for_molecular_tag = use_mol_tag; +} + +void FlowSpaceConsensusMaster::InitializeConsensusCounter(){ + // Initialize the consensus counter using dummy inputs since now I know consensus_for_molecular_tag. + list::iterator dummy_iter; + vector dummy_family(0); + FlowSpaceConsensusOneFamily(dummy_family, dummy_iter, dummy_iter); +} + +void GenerateFlowSpaceConsensusPositionTicket(vector< vector< vector > >& my_molecular_families_multisample, + FlowSpaceConsensusMaster& flow_space_consensus_master, + unsigned int min_family_size, + list::iterator& consensus_position_ticket, + list::iterator& aln_needed_consensus_position_ticket, + TargetsManager* targets_manager, + bool skip_consensus) +{ + // my_molecular_families_multisample is usually the famly pileup that cover one target. + // Typcally, a read just covers one target. + // map is a better container than vector to store the target stat. + // stat_of_targets[i] is the coverage information for the i-th unmerged region generated here. + // TODO: Should I split coverage stat for each sample? + map stat_of_targets; + for (vector< vector< vector< MolecularFamily> > >::iterator sample_it = my_molecular_families_multisample.begin(); sample_it != my_molecular_families_multisample.end(); ++sample_it) { + for (vector< vector< MolecularFamily> >::iterator strand_it = sample_it->begin(); strand_it != sample_it->end(); ++strand_it) { + for (vector< MolecularFamily>::iterator fam_it = strand_it->begin(); fam_it != strand_it->end(); ++fam_it) { + // Is *fam_it functional? + if (fam_it->SetFuncFromAll((unsigned int) min_family_size)) { + unsigned int consensus_fam_size = 0; + if (skip_consensus){ + consensus_fam_size = fam_it->GetFamSize(); + }else{ + // Generate consensus reads + consensus_fam_size = flow_space_consensus_master.FlowSpaceConsensusOneFamily(fam_it->all_family_members, consensus_position_ticket, aln_needed_consensus_position_ticket); + } + // Count the coverage of the target. + if (consensus_fam_size >= min_family_size and (not fam_it->all_family_members.empty())){ + // Important Assumption: is_split_families_by_region_ = true in MolecularFamilyGenerator. + for (vector::iterator target_it = fam_it->all_family_members[0]->target_coverage_indices.begin(); target_it != fam_it->all_family_members[0]->target_coverage_indices.end(); ++target_it){ + TargetStat& my_stat = stat_of_targets[*target_it]; + my_stat.read_coverage += consensus_fam_size; + ++(my_stat.family_coverage); + ++(my_stat.fam_size_hist[consensus_fam_size]); + } + } + } + } + } + } + targets_manager->AddCoverageToRegions(stat_of_targets); + +} diff --git a/Analysis/VariantCaller/Consensus/FlowSpaceConsensus.h b/Analysis/VariantCaller/Consensus/FlowSpaceConsensus.h new file mode 100644 index 00000000..aa5395d8 --- /dev/null +++ b/Analysis/VariantCaller/Consensus/FlowSpaceConsensus.h @@ -0,0 +1,138 @@ +/* Copyright (C) 2016 Thermo Fisher Scientific, Inc. All Rights Reserved */ + +#ifndef FLOWSPACECONSENSUS_H +#define FLOWSPACECONSENSUS_H + +#include +#include +#include +#include +#include +#include +#include +#include "InputStructures.h" +#include "BAMWalkerEngine.h" +#include "MolecularTag.h" +#include "ConsensusParameters.h" + +using namespace std; + +// ============================================================================== +// Pre-computed information for FlowSpaceCluster +struct PrecomputeForClustering +{ + PrecomputeForClustering(const Alignment* rai); + vector zc = {}; //! bug-fixed zc tag of the read + string base_seq = ""; //! base sequence as called by BaseCaller + vector flowgram = {}; //! flowgram of base_seq, will be computed if needed + vector base_index_to_flow_index = {}; //! the index mapping from base_seq to flowgram, will be computed if needed +}; + +// ============================================================================== + +struct TrimInfo +{ + string prefix_bases = ""; //! the 5' hard-clipped prefix bases (KS + ZT + ZE) + string suffix_bases = ""; //! the 3' hard-clipped suffix bases (YE + YT), 3' adapter not included + int prefix_end_flow = -1; //! Flow corresponding to to the last base of the 5' hard clipped prefix + int prefix_end_hp = -1; //! Last hp length of prefix_bases + int suffix_start_flow = -1; //! The first incorporated flow of the suffix_bases. Set to the end flow of template if no suffix_bases. + int suffix_start_hp = -1; //! First hp length of suffix_bases +}; + +// ============================================================================== + +//@TODO: optimize the heuristic assigned parameters kMaxNumDiffFlows_, kMaxNumLocalDiffFlows_, kStrictFlowSync_ +class FlowSpaceCluster +{ +private: + const static int kMaxNumDiffFlows_ = 6; //! Build-in parameter for bool IsFlowSynchronized(...) + const static int kMaxNumLocalDiffFlows_ = 2; //! Build-in parameter for bool IsFlowSynchronized(...) + const static bool kStrictFlowSync_ = false; //! Build-in parameter for bool IsFlowSynchronized(...) + const ion::FlowOrder* const flow_order_; //! the flow order corresponds to run_id + string template_base_seq_ = ""; //! The template base sequence that represents the cluster + vector template_flowgram_ = {}; //! The flowgram of template_base_seq_ + + void InitializeCluster_(const Alignment *new_rai); + void AddNewMember_(const Alignment* new_rai, PrecomputeForClustering& precomp_for_new_rai); + +public: + const string runid; //! Every read must come from the same run + const short flow_order_index; //! flow order index used by of the cluster + const bool is_reverse_strand; //! The reads in the cluster must be on the same strand. + int adapter_start_flow = -1; //! The ZG tag (or ZC[0]) of the read in Ion BAM format + int best_mapping_quality_index = -1; //! The index of the read in cluster_members that has the best mapping quality + uint16_t best_mapping_quality = 0; //! The best mapping quality of the reads in cluster_members + TrimInfo trim_info; //! Information for trimming prefix bases and suffix bases + vector cluster_members = {}; //! the read members in the cluster + + FlowSpaceCluster(const Alignment* new_rai, const ion::FlowOrder* fo); // Call me to construct an object if you want to ignore flow_order_index + FlowSpaceCluster(const Alignment* new_rai, const vector& flow_order_vector, PrecomputeForClustering& precomp_for_new_rai); + + bool AskToJoinMe(const Alignment* query_rai); + bool AskToJoinMe(const Alignment* query_rai, PrecomputeForClustering& precomp_for_query_rai); +}; + +// ============================================================================== + +class FlowSpaceConsensusMaster +{ +private: + short flow_order_index_; //! The flow order index associated with the reads that form the consensus + const ion::FlowOrder* flow_order_; //! Flow order specified by flow_order_index_ + BasecallerRead consensus_read_; //! Consensus flow space information + PersistingThreadObjects* const thread_objects_; //! Used to call Treephaser and the realigner + const ReferenceReader* const reference_reader_; //! Reference reader for realignemnt + const InputStructures* const global_context_; //! Header information of the bam file + bool is_suffix_counted_in_za_; //! true if the ZA tag counts the suffix bases (BaseCaller 5.2-0, ..., 5.2-20), else false. + +public: + bool filter_qt_reads = false; //! filtered out quality trimmed reads + bool need_3_end_adapter = false; //! filtered out the reads w/o 3' end adapter found + bool suppress_recalibration = true; //! suppress recalibration when solving the consensus measurements + bool consensus_for_molecular_tag = false; //! consensus for molecular tags? + bool filter_single_read_consensus = false; //! filtered out single-read consensus + FlowSpaceConsensusMaster(PersistingThreadObjects* thread_objects, + const ReferenceReader* reference_reader, + const InputStructures* global_context, + const string& basecaller_ver); + + void PropagateFlowspaceConsensusParameters(const ConsensusParameters& my_param, bool use_mol_tag); + void CalculateConsensusPhaseParams(const vector& cluster_members, vector& consensus_phase_params, bool is_zero_droop = true); + void GetMeasurements(const vector& cluster_members, vector *>& flow_synchronized_measurements_ptr); + void GetMeasurements(const vector& cluster_members, vector< vector >& flow_synchronized_measurements); + void InitializeForBaseCalling(const FlowSpaceCluster& my_cluster, const vector& consensus_phase_params, bool suppress_recal, string &consensus_read_name); + void InitializeConsensusCounter(); + bool CalculateConsensusMeasurements(const vector >& flow_synchronized_measurements, + const TrimInfo& trim_info, + vector& consensus_measurements, + vector& measurements_sd); + bool CalculateConsensusMeasurements(const vector *>& flow_synchronized_measurements_ptr, + const TrimInfo& trim_info, + vector& consensus_measurements, + vector& measurements_sd); + void TrimPrefixSuffixBases(const TrimInfo& trim_info, string& trimmed_consensus_bases, int& start_flow); + bool SaveToBamAlignment(const FlowSpaceCluster& my_cluster, + const vector& consensus_phase_params, + const vector& consensus_measurements, + const vector& measurements_sd, + const string& trimmed_bases, + const string& consensus_read_name, + int start_flow, + BamAlignment& alignment); + unsigned int FlowSpaceConsensusOneFamily(vector& family_members, + list::iterator& consensus_position_ticket, + list::iterator& aln_needed_consensus_position_ticket); +}; + +// ============================================================================== + +void GenerateFlowSpaceConsensusPositionTicket(vector< vector< vector > >& my_molecular_families_multisample, + FlowSpaceConsensusMaster& flow_space_consensus_master, + unsigned int min_family_size, + list::iterator& consensus_position_ticket, + list::iterator& aln_needed_consensus_position_ticket, + TargetsManager* targets_manager, + bool skip_consensus); + +#endif /* FLOWSPACECONSENSUS_H */ diff --git a/Analysis/VariantCaller/EnsembleEval/BiasGenerator.cpp b/Analysis/VariantCaller/EnsembleEval/BiasGenerator.cpp index a43cda6b..ea513293 100644 --- a/Analysis/VariantCaller/EnsembleEval/BiasGenerator.cpp +++ b/Analysis/VariantCaller/EnsembleEval/BiasGenerator.cpp @@ -2,9 +2,6 @@ #include "BiasGenerator.h" - - - // bias generator handles latent variables representing sources of bias in measurement // the trivial example is by strand void BasicBiasGenerator::GenerateBiasByStrand(int i_hyp, HiddenBasis &delta_state, vector &test_flow, int strand_key, vector &new_residuals, vector &new_predictions){ @@ -53,7 +50,7 @@ void BasicBiasGenerator::AddOneUpdate(HiddenBasis &delta_state, const vector 1){ + cout << " + Latent bias"<< (print_updated? " updated:" : ":") << endl + << " - FWD: latent_bias = " << PrintIteratorToString(latent_bias[0].begin(), latent_bias[0].end()) <0.0f) { - l_sigma = sigma*skew; - } else { - l_sigma = sigma/skew; - } - - float x = res/l_sigma; - float xx = x*x; - float v = 2*half_n-1; // 1,3,5,7,... - float my_likelihood = 1.0f/(3.14159f*sqrt(v)); - float my_factor = 1.0f/(1.0f+xx/v); - - for (int i_prod=0; i_prod 0.0f)? sigma * skew : sigma / skew; + } + float x = res / l_sigma; + float my_log_likelihood = log_factor; + + my_log_likelihood += half_n * (log_v - log(v + x * x)); + my_log_likelihood -= log(l_sigma); + if (skew == 1.0f){ + return my_log_likelihood; + } + // account for skew + float skew_factor = log(2.0f*skew/(skew*skew+1.0f)); + my_log_likelihood += skew_factor; + return my_log_likelihood; } HiddenBasis::HiddenBasis(){ @@ -105,6 +98,7 @@ void HiddenBasis::Allocate(unsigned int num_hyp, unsigned int num_test_flow){ void CrossHypotheses::CleanAllocate(int num_hyp, int num_flow) { // allocate my vectors here responsibility.assign(num_hyp, 0.0f); + weighted_responsibility.assign(num_hyp, 0.0f); log_likelihood.assign(num_hyp, 0.0f); scaled_likelihood.assign(num_hyp, 0.0f); @@ -115,9 +109,10 @@ void CrossHypotheses::CleanAllocate(int num_hyp, int num_flow) { predictions_all_flows.resize(num_hyp); mod_predictions.resize(num_hyp); normalized_all_flows.assign(num_flow, 0.0f); + measurement_sd_all_flows.assign(num_flow, 0.0f); residuals.resize(num_hyp); sigma_estimate.resize(num_hyp); - basic_likelihoods.resize(num_hyp); + basic_log_likelihoods.resize(num_hyp); for (int i_hyp=0; i_hyp 1){ + measurement_sd_all_flows = my_read.measurements_sd; + } + for (unsigned int i_hyp = 1; i_hyp < same_as_null_hypothesis.size(); ++i_hyp){ + at_least_one_same_as_null += same_as_null_hypothesis[i_hyp]; + } } void CrossHypotheses::InitializeTestFlows() { @@ -173,7 +184,7 @@ void CrossHypotheses::InitializeRelevantToTestFlows(){ mod_predictions[i_hyp].assign(test_flow_num, 0.0f); residuals[i_hyp].assign(test_flow_num, 0.0f); sigma_estimate[i_hyp].assign(test_flow_num, 0.0f); - basic_likelihoods[i_hyp].assign(test_flow_num, 0.0f); + basic_log_likelihoods[i_hyp].assign(test_flow_num, 0.0f); } normalized.assign(test_flow_num, 0.0f); for(unsigned int t_flow = 0; t_flow < test_flow_num; ++t_flow){ @@ -185,14 +196,19 @@ void CrossHypotheses::InitializeRelevantToTestFlows(){ } delta_state.Allocate(num_hyp, test_flow_num); - // clear the data of all flows if we don't want to preserve it - if(not preserve_full_data){ - normalized_all_flows.clear(); - predictions_all_flows.clear(); + // If it is a consensus read + if (read_counter > 1){ + // Note that if the ZS tag is not presented, I set measurement_sd to be all zeros. + measurement_var.assign(test_flow_num, 0.0f); + for(unsigned int t_flow = 0; t_flow < test_flow_num; ++t_flow){ + int j_flow = test_flow[t_flow]; + if (j_flow < (int) measurement_sd_all_flows.size()){ + measurement_var[t_flow] = measurement_sd_all_flows[j_flow] * measurement_sd_all_flows[j_flow]; + } + } } } - void CrossHypotheses::InitializeDerivedQualities() { InitializeResponsibility(); // depends on hypotheses @@ -203,16 +219,21 @@ void CrossHypotheses::InitializeDerivedQualities() { InitializeSigma(); // depends on predicted my_t.SetV(heavy_tailed); + // 2*heavy_tailed - 1 = Dof of t-dist + sigma_factor = adjust_sigma? sqrt((2.0f * heavy_tailed - 3.0f) / (2.0f * heavy_tailed - 1.0f)) : 1.0f; - ComputeBasicLikelihoods(); // depends on residuals and sigma + ComputeBasicLogLikelihoods(); // depends on residuals and sigma // compute log-likelihoods ComputeLogLikelihoods(); // depends on test flow(s) } void CrossHypotheses::InitializeResponsibility() { responsibility[0] = 1.0f; // everyone is an outlier until we trust you - for (unsigned int i_hyp=1; i_hyp &hyp_prob, float typical_prob) { - +void CrossHypotheses::UpdateResponsibility(const vector &hyp_prob, float outlier_prob) { + if (at_least_one_same_as_null){ + // In principle, an outlier read means it supports neither alleles. + // In this case, the read can't be an outlier because the sequence as called is the same as an allele. + // Hence I override outlier_prob to be extremely low. + // Otherwise, the reads that supports the allele with AF < outlier prob will be treated as outliers. + // This step can mitigate the bad parameter setting where min-allele-freq < outlier prob or the true AF ~ or < outlier prob + // while it does not affect the outlier handling of the read that is a indeed an outlier. + outlier_prob = min(outlier_prob, MINIMUM_RELATIVE_OUTLIER_PROBABILITY); + } + float typical_prob = 1.0f - outlier_prob; if (!success){ //cout << "alert: fail to splice still called" << endl; InitializeResponsibility(); } else { // vector tmp_prob(3); - tmp_prob_d[0] = (1.0f-typical_prob)*scaled_likelihood[0]; // i'm an outlier + tmp_prob_d[0] = outlier_prob * scaled_likelihood[0]; // i'm an outlier for (unsigned int i_hyp=1; i_hyp &hyp_prob, float ll_denom += tmp_prob_d[i_hyp]; } - for (unsigned int i_hyp=0; i_hyp &hyp_prob, float typical_prob) { - // vector tmp_prob(3); - tmp_prob_f[0] = (1.0f-typical_prob)*scaled_likelihood[0]; // i'm an outlier +float CrossHypotheses::ComputePosteriorLikelihood(const vector &hyp_prob, float outlier_prob) { + if (at_least_one_same_as_null){ + // In principle, an outlier read means it supports neither alleles. + // In this case, the read can't be an outlier because the sequence as called is the same as an allele. + // Hence I override outlier_prob to be extremely low. + // Otherwise, the reads that supports the allele with AF < outlier prob will be treated as outliers. + // This step can mitigate the bad parameter setting where min-allele-freq < outlier prob or the true AF ~ or < outlier prob + // while it does not affect the outlier handling of the read that is a indeed an outlier. + outlier_prob = min(outlier_prob, MINIMUM_RELATIVE_OUTLIER_PROBABILITY); + } + float typical_prob = 1.0f - outlier_prob; + tmp_prob_f[0] = outlier_prob * scaled_likelihood[0]; // i'm an outlier for (unsigned int i_hyp=1; i_hyp 0.0f) ? sqrt(adj_res * adj_res + measurement_var[t_flow]) : -sqrt(adj_res * adj_res + measurement_var[t_flow]); + } + float my_sigma = adjust_sigma? sigma_estimate[i_hyp][t_flow] * sigma_factor : sigma_estimate[i_hyp][t_flow]; + basic_log_likelihoods[i_hyp][t_flow] = my_t.LogTDistOddN(adj_res, my_sigma, skew_estimate); // pure observational likelihood depends on residual + current estimated sigma under each hypothesis + } + } + } } void CrossHypotheses::UpdateRelevantLikelihoods() { - ComputeBasicLikelihoods(); + ComputeBasicLogLikelihoods(); ComputeLogLikelihoods(); // automatically over relevant likelihoods } @@ -423,7 +478,10 @@ void CrossHypotheses::ComputeLogLikelihoodsSum() { for (unsigned int i_hyp=0; i_hyp 1){ + log_likelihood[i_hyp] *= read_counter_f; } } } @@ -451,7 +509,24 @@ void CrossHypotheses::JointLogLikelihood() { float sigma_projection = 0.001f; // always some minimal variance in case we divide for (unsigned int t_flow = 0; t_flow 0)? + sqrt(residuals[i_hyp][t_flow] * residuals[i_hyp][t_flow] + measurement_var[t_flow]) : -sqrt(residuals[i_hyp][t_flow] * residuals[i_hyp][t_flow] + measurement_var[t_flow]); + } + res_component = adj_res * d_val/ delta_scale; + } + + + res_projection += res_component; /*for (unsigned int s_flow=0; s_flow0) with the prior P(hyp_i) = typical_prob. -// I claim the read is "not" an outlier, if I find any binary hypothesis pair hyp_0 vs. hyp_i s.t. log APP(hyp_0) < log APP(hyp_i), -// where APP is the posterior probability. -// Suppose that sigma is the default magic sigma (minimum_sigma_prior = 0.085, slope_sigma_prior = 0.0084). -// Example 1: -// hyp_0: 0-mer, hyp_1: 1-mer, and the measurement = 0-mer -// If outlier_probability < 0.00055 then we claim that it is not an outlier. -// => We need outlier_probability < 5.5E-4 to tolerate a 1-mer error at a single flow. -// Example 2: -// Suppose hyp_0: 0-mer, hyp_1: 2-mer, and the measurement = 0-mer -// With the default value of magic sigma, if outlier_probability < 1.1E-5 then we claim that it is not an outlier. -// => We need outlier_probability < 1.1E-5 to tolerate a 2-mer error at a single flow. -// Example 3: -// Suppose hyp_0: {0-mer, 0-mer}, hyp_1: {1-mer, 1-mer}, and the measurement = {0-mer, 0-mer} -// With the default value of magic sigma, if outlier_probability < 3.1E-7 then we claim that it is not an outlier. -// => We need outlier_probability < 3.1E-7 to tolerate 1-mer, 1-mer errors at two flows. -bool CrossHypotheses::LocalOutlierClassifier(float typical_prob){ - //Use CheckParameterLowerUpperBound(...) to prevent extremely low ol_prob instead - //if(ol_prob < MINIMUM_RELATIVE_OUTLIER_PROBABILITY){ - // ol_prob = MINIMUM_RELATIVE_OUTLIER_PROBABILITY; - // typical_prob = 1.0f - MINIMUM_RELATIVE_OUTLIER_PROBABILITY; - //} - bool is_outlier = true; - - // First check same_as_null_hypothesis - // The read is not an outlier if any hypothesis not null is the same as the null hypothesis - // This can prevent the case where we classify a hypothesis the same as null to be an outlier if typical_prob < 0.5. - for(unsigned int i_hyp = 1; i_hyp < same_as_null_hypothesis.size(); ++i_hyp){ - if(same_as_null_hypothesis[i_hyp]){ - is_outlier = false; - return is_outlier; - } - } - - float log_priori_not_ol = log(typical_prob); - float log_posterior_likelihood_ol = log(1.0f - typical_prob) + log_likelihood[0]; - - for(unsigned int i_hyp = 1; i_hyp < log_likelihood.size(); ++i_hyp){ - float log_posterior_likelihood_not_ol = log_priori_not_ol + log_likelihood[i_hyp]; - // If any hypothesis says "I am not an outlier", then the read is not an outlier read. - if(log_posterior_likelihood_ol < log_posterior_likelihood_not_ol){ - is_outlier = false; - return is_outlier; - } - } - return is_outlier; -} - - - void EvalFamily::InitializeEvalFamily(unsigned int num_hyp){ - ResetFamily(); CleanAllocate(num_hyp); InitializeFamilyResponsibility(); } @@ -664,6 +692,7 @@ void EvalFamily::InitializeEvalFamily(unsigned int num_hyp){ void EvalFamily::CleanAllocate(unsigned int num_hyp){ // I only clean and allocate the ones that I need. my_family_cross_.responsibility.assign(num_hyp, 0.0f); + my_family_cross_.weighted_responsibility.assign(num_hyp, 0.0f); family_responsibility.assign(num_hyp, 0.0f); my_family_cross_.log_likelihood.assign(num_hyp, 0.0f); my_family_cross_.scaled_likelihood.assign(num_hyp, 0.0f); @@ -680,34 +709,394 @@ void EvalFamily::InitializeFamilyResponsibility(){ } } -// Currently, I don't handle the case of "outlier family". -// I force the log-likelihood of the outlier hypothesis of each family to be extremely low. -// So no family will be treated as an outlier. -// Do ShortStack::OutlierFiltering() before barcode classification to make sure that all families consist of no outlier read. -// @TODO: Perhaps need a better way to deal with outlier families? void EvalFamily::ComputeFamilyLogLikelihoods(const vector &my_hypotheses){ - my_family_cross_.log_likelihood[0] = -9999.9f; // no other hypothesis would have log-likelihood less than this value + my_family_cross_.log_likelihood[0] = -999999.9f; // accumulate the log-likelihood from the reads of the family for not null hypotheses - for (unsigned int i_hyp=1; i_hyp < my_family_cross_.log_likelihood.size(); i_hyp++) { + for (unsigned int i_hyp = 1 ; i_hyp < my_family_cross_.log_likelihood.size(); i_hyp++) { my_family_cross_.log_likelihood[i_hyp] = 0.0f; - for (unsigned int i_member = 0; i_member < family_members.size(); ++i_member){ - unsigned int i_read = family_members[i_member]; - my_family_cross_.log_likelihood[i_hyp] += my_hypotheses[i_read].log_likelihood[i_hyp]; + for (unsigned int i_member = 0; i_member < valid_family_members.size(); ++i_member){ + unsigned int i_read = valid_family_members[i_member]; + my_family_cross_.log_likelihood[i_hyp] += ((1.0f - my_hypotheses[i_read].responsibility[0]) * my_hypotheses[i_read].log_likelihood[i_hyp]); } } my_family_cross_.ComputeScaledLikelihood(); } // ComputeFamilyLogLikelihoods(...) must be done first! -void EvalFamily::UpdateFamilyResponsibility(const vector &hyp_prob, float typical_prob){ - my_family_cross_.UpdateResponsibility(hyp_prob, typical_prob); +void EvalFamily::UpdateFamilyResponsibility(const vector &hyp_prob, float outlier_prob){ + my_family_cross_.UpdateResponsibility(hyp_prob, outlier_prob); family_responsibility = my_family_cross_.responsibility; } -float EvalFamily::ComputeFamilyPosteriorLikelihood(const vector &hyp_prob, float typical_prob){ - return my_family_cross_.ComputePosteriorLikelihood(hyp_prob, typical_prob); +// (Note 1): my_family_cross_.log_likelihood[0] is set to be super low +// (Note 2): family_responsibility[0] is obtained using an ad hoc fashion (not derived from family log-likelihood) +// So I can't calculate the family posterior likelihood from scaled_likelihood[0]. +// I kind of reversely engineering from family_responsibility[0] to family posterior likelihood +float EvalFamily::ComputeFamilyPosteriorLikelihood(const vector &hyp_prob){ + float ll_denom = 0.0f; + float safety_zero = 1.0E-12; + + for (unsigned int i_hyp = 1; i_hyp < my_family_cross_.scaled_likelihood.size(); ++i_hyp){ + ll_denom += hyp_prob[i_hyp - 1] * my_family_cross_.scaled_likelihood[i_hyp]; + } + ll_denom *= (1.0f - family_responsibility[0]); + // If family_responsibility[0] is high, FamilyPosteriorLikelihood is dominated by family_responsibility[0], i.e., not a function of hyp_prob. + ll_denom += family_responsibility[0]; + return log(ll_denom + safety_zero) + my_family_cross_.ll_scale; // log-likelihood under current distribution, including common value of log-likelihood-scale } int EvalFamily::MostResponsible(){ return my_family_cross_.MostResponsible(); } + +int EvalFamily::CountFamSizeFromAll() +{ + fam_size_ = 0; + for (vector::iterator read_it = all_family_members.begin(); read_it != all_family_members.end(); ++read_it){ + fam_size_ += (read_stack_->at(*read_it)->read_count); + } + return fam_size_; +} + +int EvalFamily::CountFamSizeFromValid() +{ + valid_fam_size_ = 0; + for (vector::iterator read_it = valid_family_members.begin(); read_it != valid_family_members.end(); ++read_it){ + valid_fam_size_ += (read_stack_->at(*read_it)->read_count); + } + return valid_fam_size_; +} + +// I define Family outlier responsibility = P(# non-outlier read members < min_fam_size) +// In theory, Resp(OL family) is obtained from the cdf of a multinomial distribution. +// Since Resp(OL family) >= min Resp(OL read), I use the lower bound to approximate Resp(OL family) for some extreme cases. +// Otherwise, I calculate Resp(OL family) via Monte-Carlo simulation. +// (Note 1): my_cross_.responsibility is not derived from my_cross_.log_likelihood in this function. +void EvalFamily::ComputeFamilyOutlierResponsibility(const vector &my_hypotheses, unsigned int min_fam_size) +{ + float family_ol_resp = 1.0f; + float safety_zero = 0.000001f; + float min_read_ol_resp = 1.0f; + float max_read_ol_resp = 0.0f; + float weighted_avg_read_ol_resp = 0.0f; + + int semi_hard_fam_size = 0; + + vector potential_outlier_reads; + + for (vector::iterator read_idx_it = valid_family_members.begin(); read_idx_it != valid_family_members.end(); ++read_idx_it){ + min_read_ol_resp = min(min_read_ol_resp, my_hypotheses[*read_idx_it].responsibility[0]); + max_read_ol_resp = max(max_read_ol_resp, my_hypotheses[*read_idx_it].responsibility[0]); + weighted_avg_read_ol_resp += (my_hypotheses[*read_idx_it].responsibility[0] * my_hypotheses[*read_idx_it].read_counter_f); + // The following criterion basically claims that the read is not an outlier. + if (my_hypotheses[*read_idx_it].at_least_one_same_as_null){ + // This condition basically implies that a hypothesis is the same as null. So it can't be an outlier. + semi_hard_fam_size += my_hypotheses[*read_idx_it].read_counter; + } + else{ + potential_outlier_reads.push_back(*read_idx_it); + } + } + int num_fam_needed = (int) min_fam_size - semi_hard_fam_size; + + if (semi_hard_fam_size >= (int) min_fam_size){ + // I am very sure that the the family consists of sufficient number of non-outlier reads (which is the most ubiquitous case). + // Don't waste time doing Monte Carlo simulation. Set family_ol_resp to close to zero. + family_ol_resp = safety_zero; + } + else if (min_read_ol_resp >= 0.8f){ + //@TODO: Come up with something better than the hard-coded threshold. + // It seems that all reads in the family are very likely to be outliers. + // Don't waste time doing Monte Carlo simulation. Use the averaged read_ol_reso as family_ol_resp. + family_ol_resp = weighted_avg_read_ol_resp / (float) GetValidFamSize(); + } + else if (potential_outlier_reads.size() == 1){ + // Another trivial case where I don't need to go through Monte Carlo. + family_ol_resp = my_hypotheses[potential_outlier_reads[0]].responsibility[0]; + } + else{ + // I calculate family outlier responsibility using Monte Carlo simulation. + RandSchrange RandGen(1729); // I choose 1729 as the seed because it is the Hardy–Ramanujan number. + int num_trails = 200; + int num_ol = 0; + // Only deal with those potential outlier reads. + vector min_rand_for_non_ol(potential_outlier_reads.size()); + for (unsigned int read_idx = 0; read_idx < min_rand_for_non_ol.size(); ++read_idx){ + min_rand_for_non_ol[read_idx] = (int) ((double)(RandGen.RandMax) * (double) my_hypotheses[potential_outlier_reads[read_idx]].responsibility[0]); + } + + for (int trail_round = 0; trail_round < num_trails; ++trail_round){ + int trailed_fam_size = 0; + for (unsigned int read_idx = 0; read_idx < min_rand_for_non_ol.size(); ++read_idx){ + // Toss a coin: head = outlier, tail = not outlier + bool is_not_ol = RandGen.Rand() > min_rand_for_non_ol[read_idx]; + if (is_not_ol){ + trailed_fam_size += my_hypotheses[potential_outlier_reads[read_idx]].read_counter; + } + } + num_ol += (trailed_fam_size < num_fam_needed); + } + family_ol_resp = (float) num_ol / (float) num_trails; + } + + // Guard by safety zero in case something crazy happened + family_ol_resp = max(family_ol_resp, safety_zero); + family_ol_resp = min(family_ol_resp, 1.0f - safety_zero); + + // Normalize the family responsibility + float sum_of_resp = family_ol_resp; + float normalization_factor = (1.0f - family_ol_resp) / (1.0f - my_family_cross_.responsibility[0]); + my_family_cross_.responsibility[0] = family_ol_resp; + for (unsigned int i_hyp = 1; i_hyp < my_family_cross_.responsibility.size(); ++i_hyp){ + my_family_cross_.responsibility[i_hyp] *= normalization_factor; + sum_of_resp += my_family_cross_.responsibility[i_hyp]; + } + family_responsibility = my_family_cross_.responsibility; + assert(sum_of_resp > 0.9999f and sum_of_resp < 1.0001f); +} + + +void RemoveHp(const string& base_seq, string& hp_removed_base_seq){ + hp_removed_base_seq.resize(0); + hp_removed_base_seq.reserve(base_seq.size()); + if (base_seq.empty()){ + return; + } + for (string::const_iterator nuc_it = base_seq.begin(); nuc_it != base_seq.end(); ++nuc_it){ + if (*nuc_it != hp_removed_base_seq.back()){ + hp_removed_base_seq.push_back(*nuc_it); + } + } +} + +bool IsHpIndel(const string& seq_1, const string& seq_2) +{ + if (seq_1.empty() or seq_2.empty()){ + return false; + } + + bool hp_indel_found = false; + int hp_len_1 = 0; + int hp_len_2 = 0; + string::const_iterator nuc_it_1 = seq_1.begin(); + string::const_iterator nuc_it_2 = seq_2.begin(); + while (nuc_it_1 != seq_1.end() or nuc_it_2 != seq_2.end()){ + if (*nuc_it_1 != *nuc_it_2){ + return false; + } + ++nuc_it_1; + ++nuc_it_2; + hp_len_1 = 1; + hp_len_2 = 1; + while (*nuc_it_1 == *(nuc_it_1 -1)){ + ++hp_len_1; + ++nuc_it_1; + } + while (*nuc_it_2 == *(nuc_it_2 -1)){ + ++hp_len_2; + ++nuc_it_2; + } + if (hp_len_1 != hp_len_2){ + if (hp_indel_found){ + return false; + } + hp_indel_found = true; + } + } + return true; +} + +// The function is implemented based on the assumption that instance_of_read_by_state is obtained from "splicing", +// I claim instance_of_read_by_state[i_hyp] and instance_of_read_by_state[j_hyp] are non-flow-disruptive if the first common suffix bases of the two hypotheses are mainly incorporated at the same flow. +void CrossHypotheses::FillInFlowDisruptivenessMatrix(const ion::FlowOrder &flow_order, const Alignment &my_read) +{ + // Every hypotheses pair starts with indefinite + local_flow_disruptiveness_matrix.assign(instance_of_read_by_state.size(), vector(instance_of_read_by_state.size(), -1)); + + if (not success){ + return; + } + + int common_prefix_len = -1; // length of common starting bases of all hypotheses. What I mean prefix here is NOT my_read.prefix_bases. It is the common starting bases used in splicing. + int min_instance_of_read_by_state_len = (int) instance_of_read_by_state[0].size(); + for (unsigned int i_hyp = 1; i_hyp < instance_of_read_by_state.size(); ++i_hyp){ + if ((int) instance_of_read_by_state[i_hyp].size() < min_instance_of_read_by_state_len){ + min_instance_of_read_by_state_len = (int) instance_of_read_by_state[i_hyp].size(); + } + } + + // Find the length of common starting bases for all hypotheses + int base_idx = 0; + while (common_prefix_len < 0 and base_idx < min_instance_of_read_by_state_len){ + for (unsigned int i_hyp = 1; i_hyp < instance_of_read_by_state.size(); ++i_hyp){ + if (instance_of_read_by_state[0][base_idx] != instance_of_read_by_state[i_hyp][base_idx]){ + common_prefix_len = base_idx; + break; + } + } + ++base_idx; + } + + // Check if I didn't see any delta, e.g., variant at the end of the read. + if (common_prefix_len <= 0){ + common_prefix_len = min_instance_of_read_by_state_len; + } + + char anchor_base = 0; // anchor_base is the last common prefix base of all hypotheses + int flow_index_of_anchor_base = my_read.start_flow; + if (common_prefix_len == 0){ + anchor_base = my_read.prefix_bases.back(); + // Find the flow index of anchor_base + while (flow_index_of_anchor_base >= 0 and flow_order.nuc_at(flow_index_of_anchor_base) != anchor_base){ + --flow_index_of_anchor_base; + } + } + else{ + anchor_base = instance_of_read_by_state[0][common_prefix_len - 1]; + flow_index_of_anchor_base = my_read.flow_index[common_prefix_len - 1]; + } + vector > flow_order_index_start_from_anchor; // flow_order index for each hypothesis, starting from the anchor base + // i.e., flow_order_index_start_from_anchor[i_hyp][idx] = the index of the main incorporating flow of instance_of_read_by_state[i_hyp][idx + common_prefix_len - 1] + flow_order_index_start_from_anchor.assign(instance_of_read_by_state.size(), {flow_index_of_anchor_base}); + + for (unsigned int i_hyp = 0; i_hyp < instance_of_read_by_state.size(); ++i_hyp){ + local_flow_disruptiveness_matrix[i_hyp][i_hyp] = 0; // identical means INDEL length 0. + for (unsigned int j_hyp = i_hyp + 1; j_hyp < instance_of_read_by_state.size(); ++j_hyp){ + if (i_hyp == 0 and same_as_null_hypothesis[j_hyp]){ + // same_as_null_hypothesis means identical + local_flow_disruptiveness_matrix[0][j_hyp] = 0; + local_flow_disruptiveness_matrix[j_hyp][0] = 0; + continue; + } + // determine the common_prefix_len for i_hyp, j_hyp + int common_prefix_len_i_j_pair = common_prefix_len; + while (instance_of_read_by_state[i_hyp][common_prefix_len_i_j_pair] == instance_of_read_by_state[j_hyp][common_prefix_len_i_j_pair]){ + ++common_prefix_len_i_j_pair; + if (common_prefix_len_i_j_pair >= (int) min(instance_of_read_by_state[i_hyp].size(), instance_of_read_by_state[j_hyp].size())) + break; + } + + // determine the common_suffix_len for i_hyp, j_hyp + // i_idx + 1, j_idx + 1 are the indices of the first common suffix base of i_hyp, j_hyp, respectively. + int i_idx = (int) instance_of_read_by_state[i_hyp].size() - 1; + int j_idx = (int) instance_of_read_by_state[j_hyp].size() - 1; + int common_suffix_len = 0; // The number of common ending bases of instance_of_read_by_state[i_hyp] and instance_of_read_by_state[j_hyp] + while ((min(i_idx, j_idx) > common_prefix_len_i_j_pair) and instance_of_read_by_state[i_hyp][i_idx] == instance_of_read_by_state[j_hyp][j_idx]){ + --i_idx; + --j_idx; + ++common_suffix_len; + } + + if (common_suffix_len == 0){ + // The flow-disruptiveness is indefinite because there is no common suffix bases (or may be hard-clipped). + // For example, the variant position is at the end of the read. + //@TODO: Append or use the suffix bases (if any, e.g., suffix molecular tag) if there is no other hard-clipped base (Usually, it is safe if --trim-ampliseq-primers=on). + continue; + } + + // Check HP-INDEL first because it is the easiest (and probably the most ubiquitous) one. + if (IsHpIndel(instance_of_read_by_state[i_hyp].substr(common_prefix_len_i_j_pair - 1, i_idx + 3 - common_prefix_len_i_j_pair), + instance_of_read_by_state[j_hyp].substr(common_prefix_len_i_j_pair - 1, j_idx + 3 - common_prefix_len_i_j_pair))){ + local_flow_disruptiveness_matrix[i_hyp][j_hyp] = 0; + local_flow_disruptiveness_matrix[j_hyp][i_hyp] = 0; + continue; + } + + // Now fill flow_order_index_start_from_anchor since I am here (not HP-INDEL). + int flow_i_hyp = flow_order_index_start_from_anchor[i_hyp].back(); + for (int base_idx = (int) flow_order_index_start_from_anchor[i_hyp].size() + common_prefix_len - 1; base_idx <= i_idx + 1; ++base_idx){ + IncrementFlow(flow_order, instance_of_read_by_state[i_hyp][base_idx], flow_i_hyp); + flow_order_index_start_from_anchor[i_hyp].push_back(flow_i_hyp); + } + int flow_j_hyp = flow_order_index_start_from_anchor[j_hyp].back(); + for (int base_idx = (int) flow_order_index_start_from_anchor[j_hyp].size() + common_prefix_len - 1; base_idx <= j_idx + 1; ++base_idx){ + IncrementFlow(flow_order, instance_of_read_by_state[j_hyp][base_idx], flow_j_hyp); + flow_order_index_start_from_anchor[j_hyp].push_back(flow_j_hyp); + } + + bool is_i_j_fd = flow_order_index_start_from_anchor[i_hyp][i_idx + 2 - common_prefix_len] != flow_order_index_start_from_anchor[j_hyp][j_idx + 2 - common_prefix_len]; + + if ((not is_i_j_fd) and flow_order_index_start_from_anchor[i_hyp][i_idx + 2 - common_prefix_len] == flow_order.num_flows()){ + // The flow order is not long enough to represent the first common suffix base for both i_hyp and j_hyp. So the flow-disruptiveness is indefinite. + continue; + } + + // Compare the index of the main incorporating flows of the first common suffix bases of the two hypotheses + if (is_i_j_fd){ + local_flow_disruptiveness_matrix[i_hyp][j_hyp] = 2; + local_flow_disruptiveness_matrix[j_hyp][i_hyp] = 2; + } + else{ + // Not FD and not HP-INDEL + local_flow_disruptiveness_matrix[i_hyp][j_hyp] = 1; + local_flow_disruptiveness_matrix[j_hyp][i_hyp] = 1; + } + } + } +} + +// A simple criterion to determine outlier reads +// If I can not find any non-null hypothesis which is not flow-disruptive with the null hypothesis, I claim the read is an outluer and return true. +bool CrossHypotheses::OutlierByFlowDisruptiveness() const { + if (not success or local_flow_disruptiveness_matrix.empty()){ + return true; + } + + for (unsigned int i_hyp = 1; i_hyp < local_flow_disruptiveness_matrix[0].size(); ++i_hyp){ + if (local_flow_disruptiveness_matrix[0][i_hyp] == 0 or local_flow_disruptiveness_matrix[0][i_hyp] == 1){ + return false; + } + } + // indefinite or flow-disruptive means outlier + return true; +} + +void EvalFamily::FillInFlowDisruptivenessMatrix(const vector &my_hypotheses) +{ + unsigned int num_hyp = 0; + for(unsigned int i_member = 0; i_member < all_family_members.size(); ++i_member){ + if (my_hypotheses[all_family_members[i_member]].success){ + num_hyp = my_hypotheses[all_family_members[i_member]].instance_of_read_by_state.size(); + break; + } + } + if (num_hyp == 0){ + my_family_cross_.local_flow_disruptiveness_matrix.clear(); + return; + } + + my_family_cross_.local_flow_disruptiveness_matrix.assign(num_hyp, vector(num_hyp, -1)); + // flow_disruptiveness_matrix[0][i_hyp] and flow_disruptiveness_matrix[i_hyp][0] are indefinite. + for (unsigned int i_hyp = 1; i_hyp < num_hyp; ++i_hyp){ + my_family_cross_.local_flow_disruptiveness_matrix[i_hyp][i_hyp] = 0; + for (unsigned int j_hyp = i_hyp + 1; j_hyp < num_hyp; ++j_hyp){ + // Note that loacal_flow_disruptiveness_matrix[i_hyp][j_hyp] = -1, 0, 1, 2 means indefinite, HP-INDEL, not-FD and not HP-INDEL, FD, respectively. + // Use majority rule to determine flow_disruptiveness_matrix of the family. + vector fd_type_counts(3); + for (unsigned int i_member = 0; i_member < all_family_members.size(); ++i_member){ + // Don't count outliers. + const CrossHypotheses* my_member = &(my_hypotheses[all_family_members[i_member]]); + if (my_member->OutlierByFlowDisruptiveness()){ + continue; + } + int fd_type = my_member->local_flow_disruptiveness_matrix[i_hyp][j_hyp]; + if (fd_type >= 0){ + fd_type_counts[fd_type] += my_member->read_counter; + } + } + int max_type = -1; + int max_count = 1; + for (unsigned int fd_type = 0; fd_type < 3; ++fd_type){ + // Claim higher FD level in the tie cases. + if (fd_type_counts[fd_type] >= max_count){ + max_type = fd_type; + max_count = fd_type_counts[fd_type]; + } + } + my_family_cross_.local_flow_disruptiveness_matrix[i_hyp][j_hyp] = max_type; + my_family_cross_.local_flow_disruptiveness_matrix[j_hyp][i_hyp] = max_type; + } + } +} + + diff --git a/Analysis/VariantCaller/EnsembleEval/CrossHypotheses.h b/Analysis/VariantCaller/EnsembleEval/CrossHypotheses.h index c6e3cd53..de5ef04d 100644 --- a/Analysis/VariantCaller/EnsembleEval/CrossHypotheses.h +++ b/Analysis/VariantCaller/EnsembleEval/CrossHypotheses.h @@ -27,12 +27,16 @@ class PrecomputeTDistOddN{ public: float v; + float log_v; float pi_factor; float v_factor; + float log_factor; int half_n; - PrecomputeTDistOddN(){v=pi_factor=v_factor=1.0f; half_n = 3; SetV(3);}; + PrecomputeTDistOddN(){v=pi_factor=v_factor=log_factor=log_v=1.0f; half_n = 3; SetV(3);}; void SetV(int _half_n); float TDistOddN(float res, float sigma, float skew); + float LogTDistOddN(float res, float sigma, float skew); + }; class HiddenBasis{ @@ -71,11 +75,12 @@ class CrossHypotheses{ vector normalized; // Normalized signal for test flows, it is the same for all hypotheses vector state_spread; vector same_as_null_hypothesis; // indicates whether a ref or alt hypothesis equals the read as called + vector measurement_var; // measurements var for a consensus read // keep the data at all flows here if preserve_full_data == true vector > predictions_all_flows; vector normalized_all_flows; - bool preserve_full_data = false; + vector measurement_sd_all_flows; HiddenBasis delta_state; bool use_correlated_likelihood; @@ -86,7 +91,7 @@ class CrossHypotheses{ vector > residuals; // difference prediction and observed vector > sigma_estimate; // estimate of variability per test flow per hypothesis for this read - vector > basic_likelihoods; // likelihood given residuals at each test flow of the observation at that flow != likelihood of read + vector > basic_log_likelihoods; // log-likelihood given residuals at each test flow of the observation at that flow != likelihood of read float skew_estimate; @@ -95,6 +100,7 @@ class CrossHypotheses{ // size number of hypotheses vector responsibility; // how responsible this read is for a given hypothesis under the MAP: size number of hypotheses (including null=outlier) + vector weighted_responsibility; // responsibility * read_counter_f vector log_likelihood; // sum over our test flows: logged to avoid under-flows vector scaled_likelihood; // actual sum likelihood over test flows, rescaled to null hypothesis (as called), derived from log_likelihood float ll_scale; // local scaling factor for scaled likelihood as can't trust null hypothesis to be near data @@ -103,12 +109,20 @@ class CrossHypotheses{ vector tmp_prob_f; vector tmp_prob_d; + // flow-disruptiveness for all pairs of hypotheses in the read level + // local_flow_disruptiveness_matrix[i][j] indicates the flow-disruptiveness between instance_of_read_by_state[i] and instance_of_read_by_state[j] + // -1: indefinite (e.g. no common suffix bases), 0: HP-INDEL (change of HP at only one flow), 1: Non-FD and not HP-INDEL (e.g., non-FD SNP), 2: FD + // @TODO: Use enumerate to represent the fd-code. + vector > local_flow_disruptiveness_matrix; + PrecomputeTDistOddN my_t; // useful hidden variables int strand_key; int heavy_tailed; + bool adjust_sigma; + float sigma_factor; int max_flows_to_test; float min_delta_for_flow; @@ -118,11 +132,18 @@ class CrossHypotheses{ int splice_start_flow; // Flow just before we start splicing in hypotheses (same for all hypotheses) int splice_end_flow; // Flow of the first base after the variant window (maximum over all hypotheses) int max_last_flow; // Last flow that is being simulated in prediction generation (max over all hypotheses) + + int read_counter; // Indicating how many reads form this read (>1 means it is a consensus read) + float read_counter_f; // float of read_counter bool success; + bool at_least_one_same_as_null; + // functions CrossHypotheses(){ heavy_tailed = 3; // t_5 degrees of freedom + adjust_sigma = false; + sigma_factor = 1.0f; my_t.SetV(3); strand_key = 0; max_last_flow=0; @@ -137,8 +158,13 @@ class CrossHypotheses{ magic_sigma_base = 0.085f; magic_sigma_slope = 0.0084f; use_correlated_likelihood = false; + read_counter = 1; + read_counter_f = 1.0f; + at_least_one_same_as_null = false; + local_flow_disruptiveness_matrix.clear(); }; void CleanAllocate(int num_hyp, int num_flow); + void ClearAllFlowsData(); void SetModPredictions(); void FillInPrediction(PersistingThreadObjects &thread_objects, const Alignment &my_read, const InputStructures &global_context); void InitializeDerivedQualities(); @@ -148,45 +174,52 @@ class CrossHypotheses{ void ResetModPredictions(); void ComputeDeltaCorrelation(); void ResetRelevantResiduals(); - void ComputeBasicLikelihoods(); + void ComputeBasicLogLikelihoods(); void ComputeLogLikelihoods(); void ComputeLogLikelihoodsSum(); void JointLogLikelihood(); void ComputeScaledLikelihood(); - float ComputePosteriorLikelihood(const vector &hyp_prob, float typical_prob); + float ComputePosteriorLikelihood(const vector &hyp_prob, float outlier_prob); void InitializeSigma(); void InitializeResponsibility(); - void UpdateResponsibility(const vector &hyp_prob, float typical_prob); + void UpdateResponsibility(const vector &hyp_prob, float outlier_prob); void UpdateRelevantLikelihoods(); void ComputeDelta(); bool ComputeAllComparisonsTestFlow(float threshold, int max_choice); float ComputeLLDifference(int a_hyp, int b_hyp); - int MostResponsible(); + int MostResponsible() const; bool IsValidTestFlowIndexOld(unsigned int flow,unsigned int max_choice); bool IsValidTestFlowIndexNew(unsigned int flow,unsigned int max_choice); - // HardOutlierClassifier is used to pre-filter out the outliers in a family. - // Then it is reasonable to say that a functional family contains no outliers. - bool LocalOutlierClassifier(float typical_prob); + void FillInFlowDisruptivenessMatrix(const ion::FlowOrder &flow_order, const Alignment &my_alignment); + bool OutlierByFlowDisruptiveness() const; }; // Deal with the inference for a single family -class EvalFamily : public MolecularFamily{ +class EvalFamily : public AbstractMolecularFamily{ public: vector family_responsibility; - EvalFamily(const string &barcode, int strand): MolecularFamily(barcode, strand) {}; + EvalFamily(const string &barcode, int strand, const vector* const read_stack) + : AbstractMolecularFamily(barcode, strand), read_stack_(read_stack) {}; + ~EvalFamily(){}; + int CountFamSizeFromValid(); + int CountFamSizeFromAll(); void InitializeEvalFamily(unsigned int num_hyp); void CleanAllocate(unsigned int num_hyp); void InitializeFamilyResponsibility(); void ComputeFamilyLogLikelihoods(const vector &my_hypotheses); - void UpdateFamilyResponsibility(const vector &hyp_prob, float typical_prob); - float ComputeFamilyPosteriorLikelihood(const vector &hyp_prob, float typical_prob); + void UpdateFamilyResponsibility(const vector &hyp_prob, float outlier_prob); + void ComputeFamilyOutlierResponsibility(const vector &my_hypotheses, unsigned int min_fam_size); + float ComputeFamilyPosteriorLikelihood(const vector &hyp_prob); + float ComputeLLDifference(int a_hyp, int b_hyp) {return my_family_cross_.ComputeLLDifference(a_hyp, b_hyp);}; int MostResponsible(); vector GetFamilyLogLikelihood() const{ return my_family_cross_.log_likelihood; }; vector GetFamilyScaledLikelihood() const{ return my_family_cross_.scaled_likelihood; }; - + void FillInFlowDisruptivenessMatrix(const vector &my_hypotheses); + int GetFlowDisruptiveness(int i_hyp, int j_hyp) const { return my_family_cross_.local_flow_disruptiveness_matrix[i_hyp][j_hyp]; }; private: + const vector* const read_stack_; // Used to calculate family size // The calculation of log-likelihood etc. of a family is pretty much the same as a single read. // my_family_cross_ is used for calculating the "likelihoods" and "responsibility" only. // must be use my_family_cross_ carefully since it has a lot of uninitialized members. @@ -194,4 +227,6 @@ class EvalFamily : public MolecularFamily{ CrossHypotheses my_family_cross_; }; +bool IsHpIndel(const string& seq_1, const string& seq_2); + #endif // CROSSHYPOTHESES_H diff --git a/Analysis/VariantCaller/EnsembleEval/DiagnosticJSON.cpp b/Analysis/VariantCaller/EnsembleEval/DiagnosticJSON.cpp index c0a03db3..5bf0cd40 100644 --- a/Analysis/VariantCaller/EnsembleEval/DiagnosticJSON.cpp +++ b/Analysis/VariantCaller/EnsembleEval/DiagnosticJSON.cpp @@ -68,6 +68,7 @@ void DiagnosticJsonCrossHypotheses(Json::Value &json, const CrossHypotheses &my_ json["strand"] = my_cross.strand_key; json["success"] = my_cross.success ? 1 : 0; json["usecorr"] = my_cross.use_correlated_likelihood ? 1: 0; + json["read_counter"] = my_cross.read_counter; json["heavy"] = my_cross.heavy_tailed; json["lastrelevantflow"] = my_cross.max_last_flow; @@ -100,7 +101,7 @@ void DiagnosticJsonCrossHypotheses(Json::Value &json, const CrossHypotheses &my_ my_cross_temp.mod_predictions[i_hyp][j_flow] = my_cross.mod_predictions[i_hyp][t_flow]; my_cross_temp.residuals[i_hyp][j_flow] = my_cross.residuals[i_hyp][t_flow]; my_cross_temp.sigma_estimate[i_hyp][j_flow] = my_cross.sigma_estimate[i_hyp][t_flow]; - my_cross_temp.basic_likelihoods[i_hyp][j_flow] = my_cross.basic_likelihoods[i_hyp][t_flow]; + my_cross_temp.basic_log_likelihoods[i_hyp][j_flow] = my_cross.basic_log_likelihoods[i_hyp][t_flow]; } } @@ -120,17 +121,23 @@ void DiagnosticJsonCrossHypotheses(Json::Value &json, const CrossHypotheses &my_ json["testflows"][i_test] = my_cross.test_flow[i_test]; // hold some intermediates size data matrix hyp * nFlows (should be active flows) - - for (unsigned int i_hyp = 0; i_hyp < my_cross_temp.predictions.size(); i_hyp++) { - for (unsigned int i_flow = 0; i_flow < my_cross_temp.predictions[0].size(); i_flow++) { + for (unsigned int i_flow = 0; i_flow < my_cross_temp.predictions[0].size(); i_flow++) { + for (unsigned int i_hyp = 0; i_hyp < my_cross_temp.predictions.size(); i_hyp++) { json["predictions"][i_hyp][i_flow] = my_cross_temp.predictions[i_hyp][i_flow]; json["modpred"][i_hyp][i_flow] = my_cross_temp.mod_predictions[i_hyp][i_flow]; json["normalized"][i_hyp][i_flow] = my_cross_temp.normalized[i_flow]; json["residuals"][i_hyp][i_flow] = my_cross_temp.residuals[i_hyp][i_flow]; json["sigma"][i_hyp][i_flow] = my_cross_temp.sigma_estimate[i_hyp][i_flow]; - json["basiclikelihoods"][i_hyp][i_flow] = my_cross_temp.basic_likelihoods[i_hyp][i_flow]; + json["basiclikelihoods"][i_hyp][i_flow] = exp(my_cross_temp.basic_log_likelihoods[i_hyp][i_flow]); // For historical reasons, dump the likelihood w/o taking logarithm } } + + if (not my_cross_temp.measurement_var.empty()){ + for (unsigned int i_flow = 0; i_flow < my_cross_temp.measurement_var.size(); i_flow++) + json["measurement_var"][i_flow] = my_cross_temp.measurement_var[i_flow]; + } + + // sequence, responsibility (after clustering), etc for (unsigned int i_hyp = 0; i_hyp < my_cross.responsibility.size(); i_hyp++) { json["instancebystate"][i_hyp] = my_cross.instance_of_read_by_state[i_hyp]; @@ -143,6 +150,7 @@ void DiagnosticJsonCrossHypotheses(Json::Value &json, const CrossHypotheses &my_ void TinyDiagnosticJsonCrossHypotheses(Json::Value &json, const CrossHypotheses &my_cross) { json["strand"] = my_cross.strand_key; + json["read_counter"] = my_cross.read_counter; json["success"] = my_cross.success ? 1 : 0; // active flows over which we are testing @@ -161,14 +169,14 @@ json["strand"] = my_cross.strand_key; void DiagnosticJsonEvalFamily(Json::Value &json, const EvalFamily &my_family) { json["family_barcaode"] = my_family.family_barcode; json["strand_key"] = my_family.strand_key; - json["is_func_family"] = my_family.GetFunctionality() ? 1 : 0; + json["is_func_family"] = my_family.GetFuncFromValid() ? 1 : 0; // members in the family - for (unsigned int i_ndx = 0; i_ndx < my_family.family_members.size(); i_ndx++) { - json["family_members"][i_ndx] = my_family.family_members[i_ndx]; + for (unsigned int i_ndx = 0; i_ndx < my_family.valid_family_members.size(); i_ndx++) { + json["valid_family_members"][i_ndx] = my_family.valid_family_members[i_ndx]; } - if(my_family.GetFunctionality()){ + if(my_family.GetFuncFromValid()){ vector family_log_likelihood(my_family.GetFamilyLogLikelihood()); vector family_scaled_likelihood(my_family.GetFamilyScaledLikelihood()); // responsibility (after clustering), etc @@ -185,11 +193,11 @@ void DiagnosticJsonEvalFamily(Json::Value &json, const EvalFamily &my_family) { void TinyDiagnosticJsonEvalFamily(Json::Value &json, const EvalFamily &my_family) { json["family_barcaode"] = my_family.family_barcode; json["strand_key"] = my_family.strand_key; - json["is_func_family"] = my_family.GetFunctionality() ? 1 : 0; + json["is_func_family"] = my_family.GetFuncFromValid() ? 1 : 0; // members in the family - for (unsigned int i_ndx = 0; i_ndx < my_family.family_members.size(); i_ndx++) { - json["family_members"][i_ndx] = my_family.family_members[i_ndx]; + for (unsigned int i_ndx = 0; i_ndx < my_family.valid_family_members.size(); i_ndx++) { + json["family_members"][i_ndx] = my_family.valid_family_members[i_ndx]; } } diff --git a/Analysis/VariantCaller/EnsembleEval/PosteriorInference.cpp b/Analysis/VariantCaller/EnsembleEval/PosteriorInference.cpp index 9f289d73..0f0b0004 100644 --- a/Analysis/VariantCaller/EnsembleEval/PosteriorInference.cpp +++ b/Analysis/VariantCaller/EnsembleEval/PosteriorInference.cpp @@ -3,7 +3,8 @@ #include "PosteriorInference.h" ScanSpace::ScanSpace(){ - scan_done = false; + scan_pair_done = false; + scan_ref_done = false; freq_pair.assign(2,0); // reference and hypothesis freq_pair[1]=1; @@ -12,21 +13,19 @@ ScanSpace::ScanSpace(){ max_ll = -999999999.0f; // anything is better than this max_index = 0; min_detail_level_for_fast_scan = 2500; - // member variables for fast scan - + max_detail_level = 0; + // private variables for fast scan is_scanned_.clear(); - coarse_freq_resolution_ = 0.01f; - num_of_fibonacci_blocks = 3; // Assume the maximums for Hom, Het, Hom - fine_log_posterior_cutoff_gap_ = -log(0.00001f); - fine_freq_search_increment_ = 0.02f; - min_fine_log_posterior_gap_ = 0.01f; - fine_scan_penalty_order_ = 1.5f; max_log_posterior_scanned_ = -999999999999.0f; // anything is better than this argmax_log_posterior_scanned_ = 0; + + scan_more_frequencies_ = {0.0001f, 0.0005f, 0.001f, 0.005f}; // prevent singularity at edge frequencies + + DEBUG = 0; } FreqMaster::FreqMaster(){ - data_reliability = 1.0f-0.001f; + outlier_prob = 0.001f; max_hyp_freq.resize(2); prior_frequency_weight.resize(2); prior_frequency_weight.assign(2,0.0f); // additional prior weight here @@ -45,12 +44,11 @@ bool FreqMaster::Compare(vector &original, int numreads, float threshold } PosteriorInference::PosteriorInference() { - params_ll = 0.0f; - vector local_freq_start; local_freq_start.assign(2,0.5f); clustering.SetHypFreq(local_freq_start); + DEBUG = 0; } void FreqMaster::SetHypFreq(const vector &local_freq){ @@ -181,7 +179,7 @@ float ScanSpace::LogDefiniteIntegral(float _alpha, float _beta) { float beta_interpolate = exp(log_posterior_by_frequency[beta_low] - local_max_ll) * (1.0 - delta_beta) + exp(log_posterior_by_frequency[beta_hi] - local_max_ll) * delta_beta; // trapezoidal rule - float integral_sum = 0.0f; + double integral_sum = 0.0; // use double to calculate cumsum to enhance numerical stability. float distance_to_next; float cur_point_val; float cur_area; @@ -193,24 +191,24 @@ float ScanSpace::LogDefiniteIntegral(float _alpha, float _beta) { cur_point_val = exp(log_posterior_by_frequency[alpha_hi] - local_max_ll); cur_area = 0.5f * distance_to_next * (alpha_interpolate + cur_point_val); old_point_val = cur_point_val; - integral_sum += cur_area; // distance to starting integer + integral_sum += (double) cur_area; // distance to starting integer // interior segments for (int i_eval = alpha_hi + 1; i_eval <= beta_low; i_eval++) { cur_point_val = exp(log_posterior_by_frequency[i_eval] - local_max_ll); - cur_area = 0.5 * (old_point_val + cur_point_val); - integral_sum += cur_area; + cur_area = 0.5f * (old_point_val + cur_point_val); + integral_sum += (double) cur_area; old_point_val = cur_point_val; } // final segments distance_to_next = beta_n - beta_low; cur_area = 0.5f * distance_to_next * (old_point_val + beta_interpolate); - integral_sum += cur_area; + integral_sum += (double) cur_area; } else { - integral_sum = 0.5f * (beta_n - alpha_n) * (alpha_interpolate + beta_interpolate); + integral_sum = (double) (0.5f * (beta_n - alpha_n) * (alpha_interpolate + beta_interpolate)); } // now rescale back on log-scale - float integral_log = log(integral_sum) + local_max_ll; + float integral_log = log((float) integral_sum) + local_max_ll; if (isnan(integral_log)) { // trap! @@ -250,16 +248,18 @@ void FibInterval(vector &samples, int eval_start, int detail_level std::sort(samples.begin(), samples.end()); } -unsigned int ScanSpace::ResizeToMatch(ShortStack &total_theory, unsigned max_detail_level ){ - unsigned int detail_level = total_theory.DetailLevel(); - if(max_detail_level>0) detail_level = (detail_level < max_detail_level) ? (detail_level+1) : (max_detail_level+1); - float fdetail_level = (float) detail_level; +unsigned int ScanSpace::ResizeToMatch(ShortStack &total_theory, unsigned int max_detail_level){ + unsigned int detail_level = total_theory.DetailLevel(); + if (max_detail_level > 0 and detail_level > max_detail_level){ + detail_level = max_detail_level; + } + float fdetail_level = (float) detail_level; log_posterior_by_frequency.resize(detail_level + 1); eval_at_frequency.resize(detail_level + 1); for (unsigned int i_eval = 0; i_eval < eval_at_frequency.size(); i_eval++) { - eval_at_frequency[i_eval] = (float)i_eval / fdetail_level; - } - return(detail_level); + eval_at_frequency[i_eval] = (float) i_eval / fdetail_level; + } + return detail_level; } @@ -309,47 +309,96 @@ void PosteriorInference::InterpolateFrequencyScan(ShortStack &total_theory, bool scan_done = true; }*/ -void ScanSpace::DoPosteriorFrequencyScan(ShortStack &total_theory, FreqMaster &base_clustering, bool update_frequency, int strand_key, bool scan_ref, int max_detail_level) { +void ScanSpace::DoFullScan_(){ + if(DEBUG > 1){ + cout << " - Do full scan: scan all " << eval_at_frequency.size() << " frequencies."<< endl; + } + max_log_posterior_scanned_ = -999999999999.0f; // anything is better than this + argmax_log_posterior_scanned_ = 0; + + for (unsigned int i_eval = 0; i_eval < eval_at_frequency.size(); ++i_eval){ + DoPosteriorFrequencyScanOneHypFreq_(i_eval); + UpdateMaxPosteior_(i_eval); + } + max_ll = max_log_posterior_scanned_; + max_index = (int) argmax_log_posterior_scanned_; +} + +void ScanSpace::DoPosteriorFrequencyScan(ShortStack &total_theory, FreqMaster &base_clustering, bool update_frequency, int strand_key, bool scan_ref) { // cout << "ScanningFrequency" << endl; // posterior frequency inference given current data/likelihood pairing - unsigned int detail_level = ResizeToMatch(total_theory, (unsigned) max_detail_level); // now fills in frequency + unsigned long t0 = clock(); + unsigned int detail_level = ResizeToMatch(total_theory, max_detail_level); // now fills in frequency - is_scanned_.clear(); // Very important step! + is_scanned_.resize(0); // (Important): Always make sure is_scanned_ is empty. // Set the pointers for DoPosteriorFrequencyScanOneHypFreq_ ptr_total_theory_ = &total_theory; ptr_base_clustering_ = &base_clustering; ptr_strand_key_ = &strand_key; ptr_scan_ref_ = &scan_ref; - if(detail_level < min_detail_level_for_fast_scan or detail_level < (unsigned int)(1.0f / coarse_freq_resolution_)){ - // Do full scan - for (unsigned int i_eval = 0; i_eval < eval_at_frequency.size(); ++i_eval){ - DoPosteriorFrequencyScanOneHypFreq_(i_eval); + if (DEBUG > 1){ + cout << " + Do posterior frequency scan:" << endl; + cout << " - Baseline allele_freq = " << PrintIteratorToString(base_clustering.max_hyp_freq.begin(), base_clustering.max_hyp_freq.end()) << endl; + if (scan_ref) { + cout << " - Scan type: ref vs. (all alt) => f = allele_freq[0]/allele_freq[1:]" << endl; + } + else { + cout << " - Scan type: (allele "<< freq_pair[0] <<") vs. (allele "<< freq_pair[1] + << ") => f = allele_freq[" << freq_pair[0] << "]/(allele_freq[" << freq_pair[0] << "]+allele_freq[" << freq_pair[1] << "])" << endl; } - FindMaxFrequency(); + } + + if(detail_level < min_detail_level_for_fast_scan or detail_level < (unsigned int)(1.0f / kCoarseFreqResolution_)){ + DoFullScan_(); } else{ DoFastScan_(); - max_ll = max_log_posterior_scanned_; - max_index = (int) argmax_log_posterior_scanned_; } - // if doing monomorphic eval, set frequency to begin with and don't update //FindMaxFrequency(update_frequency); // log_posterior now contains all frequency information inferred from the data - scan_done = true; + if(scan_ref){ + scan_ref_done = true; + }else{ + scan_pair_done = true; + } // clear the pointers for DoPosteriorFrequencyScanOneHypFreq_ ptr_total_theory_ = NULL; ptr_base_clustering_ = NULL; ptr_strand_key_ = NULL; ptr_scan_ref_ = NULL; + + if (DEBUG > 1){ + cout << " + Posterior frequency scan done. Processing time = " << (double) (clock() - t0) / 1E6 << " sec." << endl; + float f_resolution_debug = 0.05f; + unsigned int num_freq_debug = (unsigned int) (1.0f / f_resolution_debug) + 1; + if (num_freq_debug >= log_posterior_by_frequency.size()){ + cout << " - Scan results: (f, log-posterior(f)) = "; + for (unsigned int i_eval = 0; i_eval < log_posterior_by_frequency.size(); ++i_eval){ + cout << "(" << eval_at_frequency[i_eval] << ", " << log_posterior_by_frequency[i_eval] << "), "; + } + } + else{ + cout << " - Sampled scan results: (f, log-posterior(f)) = "; + for(unsigned int i = 0; i < num_freq_debug; ++i){ + unsigned int i_eval = (i == (num_freq_debug - 1))? eval_at_frequency.size() - 1: (unsigned int) (double(i * (log_posterior_by_frequency.size() - 1)) / double(num_freq_debug - 1)); + cout << "(" << eval_at_frequency[i_eval] << ", " << log_posterior_by_frequency[i_eval] << "), "; + } + } + cout << endl; + cout << " - max(log-posterior(f)) = " << max_ll << " @ f = "<< eval_at_frequency[max_index] << endl; + } } void ScanSpace::DoPosteriorFrequencyScanOneHypFreq_(unsigned int i_eval){ if(not is_scanned_.empty()){ - if(is_scanned_[i_eval]) + if(is_scanned_[i_eval]){ return; // scan has been done at i_eval + } + // I am going to scan at i_eval. + is_scanned_[i_eval] = true; } vector hyp_freq = ptr_base_clustering_->max_hyp_freq; @@ -360,12 +409,7 @@ void ScanSpace::DoPosteriorFrequencyScanOneHypFreq_(unsigned int i_eval){ else{ ptr_base_clustering_->UpdateFrequencyAgainstOne(hyp_freq, eval_at_frequency[i_eval], 0); } - log_posterior_by_frequency[i_eval] = ptr_total_theory_->PosteriorFrequencyLogLikelihood(hyp_freq, ptr_base_clustering_->prior_frequency_weight, ptr_base_clustering_->germline_log_prior_normalization, ptr_base_clustering_->data_reliability, *ptr_strand_key_); - - if(not is_scanned_.empty()){ - // mark as scanned - is_scanned_[i_eval] = true; - } + log_posterior_by_frequency[i_eval] = ptr_total_theory_->PosteriorFrequencyLogLikelihood(hyp_freq, ptr_base_clustering_->prior_frequency_weight, ptr_base_clustering_->germline_log_prior_normalization, ptr_base_clustering_->outlier_prob, *ptr_strand_key_); } @@ -374,8 +418,8 @@ void PosteriorInference::UpdateMaxFreqFromResponsibility(ShortStack &total_theor // skip time consuming scan and use responsibilities as cluster entry total_theory.MultiFrequencyFromResponsibility(clustering.max_hyp_freq, clustering.prior_frequency_weight, strand_key); - ref_vs_all.max_ll = total_theory.PosteriorFrequencyLogLikelihood(clustering.max_hyp_freq, clustering.prior_frequency_weight,clustering.germline_log_prior_normalization, clustering.data_reliability, strand_key); - ref_vs_all.scan_done = false; // didn't come from scan + ref_vs_all.max_ll = total_theory.PosteriorFrequencyLogLikelihood(clustering.max_hyp_freq, clustering.prior_frequency_weight,clustering.germline_log_prior_normalization, clustering.outlier_prob, strand_key); + ref_vs_all.scan_ref_done = false; // didn't come from scan } @@ -396,15 +440,21 @@ void PosteriorInference::StartAtHardClassify(ShortStack &total_theory, bool upda if (update_frequency) { clustering.SetHypFreq(start_frequency); clustering.SetPriorStrength(start_frequency); - ref_vs_all.max_ll = total_theory.PosteriorFrequencyLogLikelihood(clustering.max_hyp_freq, clustering.prior_frequency_weight,clustering.germline_log_prior_normalization, clustering.data_reliability, ALL_STRAND_KEY); + ref_vs_all.max_ll = total_theory.PosteriorFrequencyLogLikelihood(clustering.max_hyp_freq, clustering.prior_frequency_weight,clustering.germline_log_prior_normalization, clustering.outlier_prob, ALL_STRAND_KEY); } - total_theory.UpdateResponsibility(clustering.max_hyp_freq, clustering.data_reliability); + total_theory.UpdateResponsibility(clustering.max_hyp_freq, clustering.outlier_prob); } void PosteriorInference::QuickUpdateStep(ShortStack &total_theory){ UpdateMaxFreqFromResponsibility(total_theory, ALL_STRAND_KEY); - total_theory.UpdateResponsibility(clustering.max_hyp_freq, clustering.data_reliability); // update cluster responsibilities + total_theory.UpdateResponsibility(clustering.max_hyp_freq, clustering.outlier_prob); // update cluster responsibilities + + if(DEBUG > 1){ + cout << " + allele_freq updated from responsibility "<< endl + << " - allele_freq = " << PrintIteratorToString(clustering.max_hyp_freq.begin(), clustering.max_hyp_freq.end()) << endl + << " - ref_vs_all.max_ll = " << ReturnJustLL() << endl; + } } /* void PosteriorInference::DetailedUpdateStep(ShortStack &total_theory, bool update_frequency){ @@ -412,39 +462,28 @@ void PosteriorInference::DetailedUpdateStep(ShortStack &total_theory, bool updat total_theory.UpdateResponsibility(max_hyp_freq, data_reliability); // update cluster responsibilities }*/ - -// functions for fast scan - -// Note that i_1 can't equal i_2 -void ScanSpace::LinearInterpolation_(unsigned int i_1, unsigned int i_2, unsigned int i_intp){ - if(i_intp == i_1){ - log_posterior_by_frequency[i_intp] = log_posterior_by_frequency[i_1]; - return; - } - if(i_intp == i_2){ - log_posterior_by_frequency[i_intp] = log_posterior_by_frequency[i_2]; - return; - } - log_posterior_by_frequency[i_intp] = log_posterior_by_frequency[i_1] + (log_posterior_by_frequency[i_2] - log_posterior_by_frequency[i_1]) * float(i_intp - i_1) / float(i_2 - i_1); -} - void ScanSpace::DoInterpolation_(){ unsigned int scanned_idx_left = 0; unsigned int scanned_idx_right = 0; - // make sure we scan the first and last + // Always make sure we scan the first and last DoPosteriorFrequencyScanOneHypFreq_(0); DoPosteriorFrequencyScanOneHypFreq_(eval_at_frequency.size() - 1); - for(unsigned int i_eval = 0; i_eval < eval_at_frequency.size(); ++i_eval){ - if(is_scanned_[i_eval]){ - scanned_idx_left = i_eval; - continue; - } - while(scanned_idx_right <= i_eval or (not is_scanned_[scanned_idx_right])){ + while(scanned_idx_left < eval_at_frequency.size() - 1){ + scanned_idx_right = scanned_idx_left + 1; + while(not is_scanned_[scanned_idx_right]){ ++scanned_idx_right; } - LinearInterpolation_(scanned_idx_right, scanned_idx_left, i_eval); + float delta_idx = (float) (scanned_idx_right - scanned_idx_left); + for (unsigned int i_eval = scanned_idx_left + 1; i_eval < scanned_idx_right; ++i_eval){ + float alpha = (float) (i_eval - scanned_idx_left) / delta_idx; + // Do linear interpolation here. + // My experiments show that doing interpolation in log-domain has better result. + // Although quadratic interpolation (i.e., assume posterior is Gaussian) has lower interpolation error, the linear interpolation is good enough. + log_posterior_by_frequency[i_eval] = alpha * log_posterior_by_frequency[scanned_idx_right] + (1.0f - alpha) * log_posterior_by_frequency[scanned_idx_left]; + } + scanned_idx_left = scanned_idx_right; } } @@ -513,21 +552,23 @@ void ScanSpace::DoFineScan_(unsigned int i_left, unsigned int i_right, unsigned DoPosteriorFrequencyScanOneHypFreq_(i_left); DoPosteriorFrequencyScanOneHypFreq_(i_right); DoPosteriorFrequencyScanOneHypFreq_(i_middle); + UpdateMaxPosteior_(i_left); + UpdateMaxPosteior_(i_right); + UpdateMaxPosteior_(i_middle); left_max = max(log_posterior_by_frequency[i_left], log_posterior_by_frequency[i_middle]); right_max = max(log_posterior_by_frequency[i_right], log_posterior_by_frequency[i_middle]); - max_log_posterior_scanned_ = max(max_log_posterior_scanned_, max(left_max, right_max)); if(i_middle - i_left > 1){ // The penalty is used to obtain finer resolution around max_log_posterior_scanned_ - penalty = exp(- fine_scan_penalty_order_ * (max_log_posterior_scanned_ - left_max)); // 0 < penalty <= 1 + penalty = exp(- kFineScanPenaltyOrder_ * (max_log_posterior_scanned_ - left_max)); // 0 < penalty <= 1 // if (the gap between the two points) * penalty > min_fine_log_posterior_gap_, we scan the middle of the two points - if (abs(log_posterior_by_frequency[i_middle] - log_posterior_by_frequency[i_left]) * penalty > min_fine_log_posterior_gap_) + if (abs(log_posterior_by_frequency[i_middle] - log_posterior_by_frequency[i_left]) * penalty > kMinFineLogPosteriorGap_) DoFineScan_(i_left, i_middle, (i_left + i_middle) / 2); } if(i_right - i_middle > 1){ - penalty = exp(- fine_scan_penalty_order_ * (max_log_posterior_scanned_ - right_max)); - if (abs(log_posterior_by_frequency[i_middle] - log_posterior_by_frequency[i_right]) * penalty > min_fine_log_posterior_gap_) + penalty = exp(- kFineScanPenaltyOrder_ * (max_log_posterior_scanned_ - right_max)); + if (abs(log_posterior_by_frequency[i_middle] - log_posterior_by_frequency[i_right]) * penalty > kMinFineLogPosteriorGap_) DoFineScan_(i_middle, i_right, (i_right + i_middle) / 2); } @@ -542,31 +583,42 @@ void ScanSpace::DoFineScan_(unsigned int i_left, unsigned int i_right, unsigned // (a. For the EM algorithm) The maximum of log_posterior_by_frequency // (b. For PASS/NOCALL, QUAL, GT, GQ) The definite integral of posterior_by_frequency (i.e., convert log_posterior_by_frequency to linear scale and normalize it) // DoFastScan_() computes an approximate log_posterior_by_frequency that provides good results for both (a) and (b) without scanning all frequencies. +// I will scan three neighborhood of frequencies: +// (a): The neighborhood around the peak of posterior_by_frequency +// (b): The neighborhoods around min_allele_freq and 1 - min_allele_freq +// (c): The neighborhoods around f = 0.0 and f = 1.0 (In case singularity in the edge cases) // In particular, the number of frequencies scanned = O(log(N)), which results the complexity of DoFastScan_() = O(N*log(N)). // (Note 1): The underlying assumption of log_posterior_by_frequency for DoFastScan_() is that log_posterior_by_frequency is "unimodal" (at least in large scale). // (Note 2): DoFastScan_() is a "blind" scan algorithm that has no information about log_posterior_by_frequency priorly. -// @TODO: Instead of the "blind" approach, the fast scan algorithm can be improved by taking max_hyp_freq into account. +// (Note 3): The interpolation error should be neglectable in liner scale, while the error may show up in the log-domain (i.e., in "large" QUAL). void ScanSpace::DoFastScan_(){ unsigned int num_scanned_freq = 0; // Step 0): Initialization + unsigned int num_coarse_scan = (unsigned int) (1.0f / kCoarseFreqResolution_) + 1; // also scan the last index + // Is it necessarily to do fast scan? + if( 0.9f * (float) eval_at_frequency.size() <= (float) num_coarse_scan){ + DoFullScan_(); + return; + } + unsigned int detail_level = eval_at_frequency.size() - 1; is_scanned_.assign(detail_level + 1, false); max_log_posterior_scanned_ = -999999999999.0f; // anything is better than this argmax_log_posterior_scanned_ = 0; - if(debug_){ - cout<<"<>"<< endl; - cout<<" detail_level = "<< detail_level<< endl; - cout<<" # of reads = "<my_hypotheses.size()< 1){ + if(ptr_total_theory_->GetIsMolecularTag()){ + cout << " - Do fast scan (detail_level = "<< detail_level << ", number of functional families = "<< ptr_total_theory_->GetNumFuncFamilies() << ")" << endl; + } + else{ + cout << " - Do fast scan (detail_level = "<< detail_level << ", number of valid reads = "<< ptr_total_theory_->valid_indexes.size() << ")" << endl; + } } - // Step 1): Do coarse scan float croase_max_log_posterior = 0.0f; unsigned int croase_argmax_log_posterior = 0; - unsigned int num_coarse_scan = (unsigned int) (1.0f / coarse_freq_resolution_) + 1; // also scan the last index - unsigned int coarse_scan_spacing = detail_level / (num_coarse_scan - 1); vector coarse_scan_indices; coarse_scan_indices.assign(num_coarse_scan, 0); @@ -577,7 +629,7 @@ void ScanSpace::DoFastScan_(){ croase_argmax_log_posterior = coarse_scan_indices.size() - 1; for(unsigned int i = 0; i < num_coarse_scan - 1; ++i){ - unsigned int i_eval = i * coarse_scan_spacing; + unsigned int i_eval = (unsigned int) (double(i * (detail_level)) / double(num_coarse_scan - 1)); coarse_scan_indices[i] = i_eval; DoPosteriorFrequencyScanOneHypFreq_(i_eval); if(log_posterior_by_frequency[i_eval] > croase_max_log_posterior){ @@ -586,26 +638,13 @@ void ScanSpace::DoFastScan_(){ } } - if(debug_){ - unsigned int num_scanned_freq_tmp = 0; - for(unsigned int i_eval = 0; i_eval < is_scanned_.size(); ++i_eval){ - num_scanned_freq_tmp += (unsigned int) is_scanned_[i_eval]; - } - cout<<" # of coarse freq scanned = "<< num_scanned_freq_tmp - num_scanned_freq<<" (coarse_freq_resolution_= "<< coarse_freq_resolution_<< ")"< max_log_posterior_scanned_){ - argmax_log_posterior_scanned_ = i_eval; - max_log_posterior_scanned_ = log_posterior_by_frequency[argmax_log_posterior_scanned_]; - } + UpdateMaxPosteior_(i_eval); } // In case Fibonacci search returns a local maximum which is less than croase_max_log_posterior if(max_log_posterior_scanned_ < croase_max_log_posterior){ @@ -613,18 +652,34 @@ void ScanSpace::DoFastScan_(){ unsigned int i_left = croase_argmax_log_posterior == 0? coarse_scan_indices[0] : coarse_scan_indices[croase_argmax_log_posterior - 1]; unsigned int i_right = croase_argmax_log_posterior == coarse_scan_indices.size() - 1? coarse_scan_indices[coarse_scan_indices.size() - 1] : coarse_scan_indices[croase_argmax_log_posterior + 1]; unsigned int i_eval = FibonacciSearchMax_(i_left, i_right); - if(log_posterior_by_frequency[i_eval] < croase_max_log_posterior){ - // croase_max_log_posterior still beats the search results, although it shouldn't happen. - max_log_posterior_scanned_ = croase_max_log_posterior; - argmax_log_posterior_scanned_ = coarse_scan_indices[croase_argmax_log_posterior]; - } - else{ - argmax_log_posterior_scanned_ = i_eval; - max_log_posterior_scanned_ = log_posterior_by_frequency[argmax_log_posterior_scanned_]; - } + UpdateMaxPosteior_(croase_argmax_log_posterior); // in case croase_max_log_posterior still beats the search results, although it shouldn't happen. + UpdateMaxPosteior_(i_eval); } - // Step 3): Determine the interval for fine scan + if(DEBUG > 1){ + cout << " - Fibonacci search found max(log-posterior(f)) = "<< max_log_posterior_scanned_<< " @ f = eval_at_frequency["<< argmax_log_posterior_scanned_ << "] = " << eval_at_frequency[argmax_log_posterior_scanned_] << endl; + } + + // Step 3): + // Scan the frequencies at scan_more_frequencies to enhance the accuracy. + // The entries in scan_more_frequencies are obtained from: a) edge frequencies, b) min-allele-freq + for (unsigned int i_freq = 0; i_freq < scan_more_frequencies_.size(); ++i_freq){ + unsigned int i_eval = (unsigned int) ( (double) detail_level * (double) scan_more_frequencies_[i_freq]); + DoPosteriorFrequencyScanOneHypFreq_(i_eval); // scan freq + UpdateMaxPosteior_(i_eval); + DoPosteriorFrequencyScanOneHypFreq_(detail_level - i_eval); // scan 1.0 - freq + UpdateMaxPosteior_(detail_level - i_eval); + } + // Scan more edge frequencies to prevent singularity + unsigned int scan_more_edge_index = (eval_at_frequency.size() < 8)? eval_at_frequency.size() : 8; + for (unsigned int i_eval = 0; i_eval < scan_more_edge_index; ++i_eval){ + DoPosteriorFrequencyScanOneHypFreq_(i_eval); + UpdateMaxPosteior_(i_eval); + DoPosteriorFrequencyScanOneHypFreq_(detail_level - i_eval); + UpdateMaxPosteior_(detail_level - i_eval); + } + + // Step 4): Determine the interval for fine scan // For (b. For PASS/NOCALL, QUAL, GT, GQ) The definite integral of posterior_by_frequency (i.e., convert log_posterior_by_frequency to linear scale and normalize it)), // what I really care about is the main mass of the posterior probability function. // The log-posterior(f) that is dominated by max_log_posterior_scanned_ is not important at all. @@ -632,15 +687,12 @@ void ScanSpace::DoFastScan_(){ // I start from the frequency at max_log_posterior_scanned_, and then increase/decrease by fine_freq_search_increment_ until I obtain // log_posterior(f) - max_log_posterior_scanned_ > fine_log_posterior_cutoff_gap_. // Then I do fine scan within the interval [fine_cutoff_i_left, fine_cutoff_i_right] - int index_increament = max((int)((float) detail_level * fine_freq_search_increment_), 1); + int index_increament = max((int)((float) detail_level * kFineFreqSearchIncrement_), 1); int fine_cutoff_i_left = (int) argmax_log_posterior_scanned_; while(fine_cutoff_i_left >= 0){ DoPosteriorFrequencyScanOneHypFreq_((unsigned int) fine_cutoff_i_left); - if(log_posterior_by_frequency[fine_cutoff_i_left] > max_log_posterior_scanned_){ - argmax_log_posterior_scanned_ = (unsigned int) fine_cutoff_i_left; - max_log_posterior_scanned_ = log_posterior_by_frequency[argmax_log_posterior_scanned_]; - } - if(max_log_posterior_scanned_ - log_posterior_by_frequency[fine_cutoff_i_left] > fine_log_posterior_cutoff_gap_) + UpdateMaxPosteior_((unsigned int) fine_cutoff_i_left); + if(max_log_posterior_scanned_ - log_posterior_by_frequency[fine_cutoff_i_left] > kFineLogPosteriorCutoffGap_) break; fine_cutoff_i_left -= index_increament; @@ -650,56 +702,100 @@ void ScanSpace::DoFastScan_(){ unsigned int fine_cutoff_i_right = argmax_log_posterior_scanned_; while(fine_cutoff_i_right <= detail_level){ DoPosteriorFrequencyScanOneHypFreq_(fine_cutoff_i_right); - if(log_posterior_by_frequency[fine_cutoff_i_right] > max_log_posterior_scanned_){ - argmax_log_posterior_scanned_ = fine_cutoff_i_right; - max_log_posterior_scanned_ = log_posterior_by_frequency[argmax_log_posterior_scanned_]; - } - if(max_log_posterior_scanned_ - log_posterior_by_frequency[fine_cutoff_i_right] > fine_log_posterior_cutoff_gap_) + UpdateMaxPosteior_((unsigned int) fine_cutoff_i_right); + if(max_log_posterior_scanned_ - log_posterior_by_frequency[fine_cutoff_i_right] > kFineLogPosteriorCutoffGap_) break; fine_cutoff_i_right += index_increament; } fine_cutoff_i_right = min(fine_cutoff_i_right, detail_level); - if(debug_){ - unsigned int num_scanned_freq_tmp = 0; - for(unsigned int i_eval = 0; i_eval < is_scanned_.size(); ++i_eval){ - num_scanned_freq_tmp += (unsigned int) is_scanned_[i_eval]; - } - cout<<" # of freq scanned for peak finding = "<< num_scanned_freq_tmp - num_scanned_freq< 1){ + cout << " - Do fine scan in the frequency interval ["<< eval_at_frequency[fine_cutoff_i_left] << ", " <>"<< endl; + cout<< " - Fast scan done. Number of frequencies scanned = " << num_scanned << endl; } - is_scanned_.clear(); + if (DEBUG > 2){ + cout << " + Fast scan results:" << endl; + cout << " - eval_at_frequency = linspace(0, 1, "<< eval_at_frequency.size() << ")"<< endl; + cout << " - scanned_index = ["; + for(unsigned int i_eval = 0; i_eval < is_scanned_.size(); ++i_eval) + if(is_scanned_[i_eval]) {cout << i_eval << ", ";} + cout << "]" << endl; + cout << " - scanned_log_posterior_by_frequency = ["; + for(unsigned int i_eval = 0; i_eval < is_scanned_.size(); ++i_eval) + if(is_scanned_[i_eval]) {cout << log_posterior_by_frequency[i_eval] << ", ";} + cout << "]" << endl; + } + is_scanned_.resize(0); + max_ll = max_log_posterior_scanned_; + max_index = (int) argmax_log_posterior_scanned_; +} + +void ScanSpace::UpdateMaxPosteior_(unsigned int i_eval){ + if (log_posterior_by_frequency[i_eval] > max_log_posterior_scanned_){ + argmax_log_posterior_scanned_ = i_eval; + max_log_posterior_scanned_ = log_posterior_by_frequency[argmax_log_posterior_scanned_]; + } +} + + +void ScanSpace::SetTargetMinAlleleFreq(const ExtendParameters& my_param, const vector& variant_specific_params){ + vector all_min_allele_freq; + if (my_param.program_flow.is_multi_min_allele_freq){ + all_min_allele_freq = my_param.program_flow.multi_min_allele_freq; + } + + all_min_allele_freq.reserve(all_min_allele_freq.size() + 4); + if (my_param.my_controls.use_fd_param){ + all_min_allele_freq = {my_param.my_controls.filter_fd_0.min_allele_freq, my_param.my_controls.filter_fd_5.min_allele_freq, my_param.my_controls.filter_fd_10.min_allele_freq}; + }else{ + all_min_allele_freq = {my_param.my_controls.filter_snp.min_allele_freq, my_param.my_controls.filter_mnp.min_allele_freq, my_param.my_controls.filter_hp_indel.min_allele_freq}; + } + if (not my_param.my_controls.hotspots_as_de_novo){ + all_min_allele_freq.push_back(my_param.my_controls.filter_hotspot.min_allele_freq); + } + + for (unsigned int i_allele = 0; i_allele < variant_specific_params.size(); ++i_allele){ + if (variant_specific_params[i_allele].min_allele_freq_override){ + all_min_allele_freq.push_back(variant_specific_params[i_allele].min_allele_freq); + } + } + + int num_steps = 4; + for (vector::iterator f_it = all_min_allele_freq.begin(); f_it != all_min_allele_freq.end(); ++f_it){ + scan_more_frequencies_.push_back(*f_it); + float maf_plus = *f_it + kCoarseFreqResolution_; + maf_plus = (maf_plus > 1.0f) ? 1.0f : maf_plus; + for (int i_step = 0; i_step < num_steps; ++i_step){ + maf_plus = (*f_it + maf_plus) * 0.5f; + scan_more_frequencies_.push_back(min(maf_plus, 1.0f)); // always make sure not scan a frequency > 1.0f + } + float maf_minus = *f_it - kCoarseFreqResolution_; + maf_minus = (maf_minus < 0.0f) ? 0.0f : maf_minus; + for (int i_step = 0; i_step < num_steps; ++i_step){ + maf_minus = (*f_it + maf_minus) * 0.5f; + scan_more_frequencies_.push_back(max(maf_minus, 0.0f)); // always make sure not scan a negative frequency + } + } } + diff --git a/Analysis/VariantCaller/EnsembleEval/PosteriorInference.h b/Analysis/VariantCaller/EnsembleEval/PosteriorInference.h index 9a48ee4e..7e685495 100644 --- a/Analysis/VariantCaller/EnsembleEval/PosteriorInference.h +++ b/Analysis/VariantCaller/EnsembleEval/PosteriorInference.h @@ -35,7 +35,9 @@ class FreqMaster{ float germline_log_prior_normalization; // for comparing LL across different priors vector prior_frequency_weight; - float data_reliability; + // Historically FreqMaster stored and used typical_prob = (1.0f - outlier_prob). + // It can cause singular outlier_prob (i.e. outlier_prob = 0.0f) which is calculated by outlier_prob from outlier_prob = (1.0f - typical_prob) in the downstream process if typical_prob is too close to 1. + float outlier_prob; FreqMaster(); void SetHypFreq(const vector & local_freq); void SetPriorStrength(const vector & local_freq); @@ -58,45 +60,53 @@ class ScanSpace{ // if I am doing one allele vs another for genotyping vector freq_pair; float freq_pair_weight; - bool scan_done; + bool scan_pair_done; + bool scan_ref_done; unsigned int min_detail_level_for_fast_scan; + unsigned int max_detail_level; + int DEBUG; ScanSpace(); float LogDefiniteIntegral(float alpha, float beta); float FindMaxFrequency(); void UpdatePairedFrequency(vector &tmp_freq, FreqMaster &base_clustering, float local_freq); unsigned int ResizeToMatch(ShortStack &total_theory, unsigned max_detail_level = 0); - void DoPosteriorFrequencyScan(ShortStack &total_theory, FreqMaster &base_clustering, bool update_frequency, int strand_key, bool scan_ref, int max_detail_level = 0); - void SetDebug(bool debug){ debug_ = debug;}; + void DoPosteriorFrequencyScan(ShortStack &total_theory, FreqMaster &base_clustering, bool update_frequency, int strand_key, bool scan_ref); + void SetTargetMinAlleleFreq(const ExtendParameters& my_param, const vector& variant_specific_params); private: // Calculate the posterior for just one hyp frequency void DoPosteriorFrequencyScanOneHypFreq_(unsigned int i_eval); + // Scan all frequencies + void DoFullScan_(); + // Update max_log_posterior_scanned_ and argmax_log_posterior_scanned_ + void UpdateMaxPosteior_(unsigned int i_eval); // functions for fast scan void DoFastScan_(); void DoFineScan_(unsigned int i_left, unsigned int i_right, unsigned int i_middle); void DoInterpolation_(); - void LinearInterpolation_(unsigned int i_1, unsigned int i_2, unsigned int i_intp); unsigned int FibonacciSearchMax_(unsigned int i_left, unsigned int i_right); unsigned int FibonacciSearchMax_(unsigned int i_left, unsigned int i_right, unsigned int i_middle); // variables for fast scan - vector is_scanned_; + vector is_scanned_; // is_scanned_[i_eval] = Have I scanned for log_posterior_by_frequency[i_eval]? + vector scan_more_frequencies_; unsigned int argmax_log_posterior_scanned_; float max_log_posterior_scanned_; - float coarse_freq_resolution_; - unsigned int num_of_fibonacci_blocks; - float fine_log_posterior_cutoff_gap_; - float fine_freq_search_increment_; - float min_fine_log_posterior_gap_; - float fine_scan_penalty_order_; + + // hard coded parameters for fast scan + const static unsigned int kNumOfFibonacciBlocks = 3; // The maximums for Hom, Het, Hom + const static constexpr float kCoarseFreqResolution_ = 0.01f; + const static constexpr float kFineLogPosteriorCutoffGap_ = -log(0.00001f); + const static constexpr float kFineFreqSearchIncrement_ = 0.02f; + const static constexpr float kMinFineLogPosteriorGap_ = 0.01f; + const static constexpr float kFineScanPenaltyOrder_ = 1.5f; // pointers for DoPosteriorFrequencyScanOneHypFreq_ ShortStack *ptr_total_theory_ = NULL; FreqMaster *ptr_base_clustering_ = NULL; int *ptr_strand_key_ = NULL; bool *ptr_scan_ref_ = NULL; - bool debug_ = false; }; class PosteriorInference{ @@ -110,6 +120,8 @@ class PosteriorInference{ float params_ll; // likelihood offset for fitted parameters + int DEBUG; + PosteriorInference(); void FindMaxFrequency(bool update_frequency); // from posterior likelihood @@ -125,10 +137,6 @@ class PosteriorInference{ float ReturnMaxLL(){ return(ref_vs_all.max_ll+params_ll);}; float ReturnJustLL(){return(ref_vs_all.max_ll);}; - void SetDebug(bool debug){ - ref_vs_all.SetDebug(debug); - gq_pair.SetDebug(debug); - }; }; diff --git a/Analysis/VariantCaller/EnsembleEval/ShortStack.cpp b/Analysis/VariantCaller/EnsembleEval/ShortStack.cpp index cf69d655..8ec1d204 100644 --- a/Analysis/VariantCaller/EnsembleEval/ShortStack.cpp +++ b/Analysis/VariantCaller/EnsembleEval/ShortStack.cpp @@ -5,11 +5,11 @@ void ShortStack::PropagateTuningParameters(EnsembleEvalTuningParameters &my_params){ - // Set is_molecular_tag_ - for (unsigned int i_read = 0; i_read < my_hypotheses.size(); i_read++) { + for (unsigned int i_read = 0; i_read < my_hypotheses.size(); i_read++) { // basic likelihoods my_hypotheses[i_read].heavy_tailed = my_params.heavy_tailed; + my_hypotheses[i_read].adjust_sigma = my_params.adjust_sigma; // test flow construction my_hypotheses[i_read].max_flows_to_test = my_params.max_flows_to_test; @@ -18,9 +18,6 @@ void ShortStack::PropagateTuningParameters(EnsembleEvalTuningParameters &my_para // used to initialize sigma-estimates my_hypotheses[i_read].magic_sigma_base = my_params.magic_sigma_base; my_hypotheses[i_read].magic_sigma_slope = my_params.magic_sigma_slope; - - // preserve the data for all flows? - my_hypotheses[i_read].preserve_full_data = my_params.preserve_full_data; } } @@ -34,10 +31,13 @@ void ShortStack::FillInPredictionsAndTestFlows(PersistingThreadObjects &thread_o my_hypotheses[i_read].FillInPrediction(thread_objects, *read_stack[i_read], global_context); my_hypotheses[i_read].start_flow = read_stack[i_read]->start_flow; my_hypotheses[i_read].InitializeTestFlows(); + if (not preserve_full_data){ + my_hypotheses[i_read].ClearAllFlowsData(); + } } } -void ShortStack::ResetQualities() { +void ShortStack::ResetQualities(float outlier_probability) { // ! does not reset test flows or delta (correctly) for (unsigned int i_read = 0; i_read < my_hypotheses.size(); i_read++) { my_hypotheses[i_read].InitializeDerivedQualities(); @@ -45,46 +45,50 @@ void ShortStack::ResetQualities() { // reset the derived qualities of my_families if(is_molecular_tag_){ + float uniform_f = (num_hyp_not_null == 0)? 1.0f: 1.0f / (float) num_hyp_not_null; + vector init_hyp_freq(num_hyp_not_null, uniform_f); + // I calculate read responsibility is just for obtaining + UpdateReadResponsibility_(init_hyp_freq, outlier_probability); ResetQualitiesForFamilies(); } } - void ShortStack::ResetQualitiesForFamilies(){ for(unsigned int i_fam = 0; i_fam < my_eval_families.size(); ++i_fam){ // don't count the not functional families - if(my_eval_families[i_fam].GetFunctionality()){ + if(my_eval_families[i_fam].GetFuncFromValid()){ my_eval_families[i_fam].InitializeFamilyResponsibility(); + // Note that I must calculate read responsibility first because now the read log-likeligood propagated to the family is weighted by Resp(read is not an outlier) my_eval_families[i_fam].ComputeFamilyLogLikelihoods(my_hypotheses); } } } -// Call PosteriorFrequencyLogLikelihoodMolTag if is_molecular_tag_ == true -// Call PosteriorFrequencyLogLikelihoodNonMolTag if is_molecular_tag_ == false -float ShortStack::PosteriorFrequencyLogLikelihood(const vector &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float my_reliability, int strand_key) { - return (this->*ptrPosteriorFrequencyLogLikelihood_)(hyp_freq, prior_frequency_weight, prior_log_normalization, my_reliability, strand_key); +// Call PosteriorFrequencyLogLikelihoodFromFamilies_ if is_molecular_tag_ +// Call PosteriorFrequencyLogLikelihoodFromReads_ if not is_molecular_tag_ +float ShortStack::PosteriorFrequencyLogLikelihood(const vector &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float outlier_prob, int strand_key) { + return (this->*ptrPosteriorFrequencyLogLikelihood_)(hyp_freq, prior_frequency_weight, prior_log_normalization, outlier_prob, strand_key); } -float ShortStack::PosteriorFrequencyLogLikelihoodNoMolTags(const vector &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float my_reliability, int strand_key) { +float ShortStack::PosteriorFrequencyLogLikelihoodFromReads_(const vector &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float outlier_prob, int strand_key) { //cout << "eval at freq " << my_freq << endl; - float my_LL = 0.0f; - + double my_LL = 0.0; // use double to calculate cumsum to enjance numerical stability + float my_reliability = 1.0f - outlier_prob; //for (unsigned int i_read=0; i_read &hyp_freq, float data_reliability) { +void ShortStack::UpdateReadResponsibility_(const vector &hyp_freq, float outlier_prob) { //for (unsigned int i_read=0; i_read &hyp_freq, float data_reliability) { - (this->*ptrUpdateResponsibility_)(hyp_freq, data_reliability); +// Call UpdateFamilyAndReadResponsibility_ if is_molecular_tag_ +// Call UpdateReadResponsibility_ if not is_molecular_tag_ +void ShortStack::UpdateResponsibility(const vector &hyp_freq, float outlier_prob) { + (this->*ptrUpdateResponsibility_)(hyp_freq, outlier_prob); } -void ShortStack::MultiFrequencyFromResponsibilityNoMolTags(vector &hyp_freq, vector &prior_frequency_weight, int strand_key){ +void ShortStack::MultiFrequencyFromReadResponsibility_(vector &hyp_freq, vector &prior_frequency_weight, int strand_key){ hyp_freq.assign(hyp_freq.size(), 0.0f); for (unsigned int i_ndx = 0; i_ndx < valid_indexes.size(); i_ndx++) { unsigned int i_read = valid_indexes[i_ndx]; for (unsigned int i_hyp = 0; i_hyp < hyp_freq.size(); i_hyp++) { if ((strand_key < 0) || (my_hypotheses[i_read].strand_key == strand_key)) - hyp_freq[i_hyp] += my_hypotheses[i_read].responsibility[i_hyp+1]; + hyp_freq[i_hyp] += my_hypotheses[i_read].weighted_responsibility[i_hyp+1]; } } // add prior weight to count of cluster @@ -162,158 +166,161 @@ void ShortStack::MultiFrequencyFromResponsibilityNoMolTags(vector &hyp_fr } } -// Call MultiFrequencyFromResponsibilityMolTag if is_molecular_tag_ == true -// Call MultiFrequencyFromResponsibilityNonMolTag if is_molecular_tag_ == false +// Call MultiFrequencyFromFamilyResponsibility_ if is_molecular_tag_ +// Call MultiFrequencyFromReadResponsibility_ if not is_molecular_tag_ void ShortStack::MultiFrequencyFromResponsibility(vector &hyp_freq, vector &prior_frequency_weight, int strand_key){ (this->*ptrMultiFrequencyFromResponsibility_)(hyp_freq, prior_frequency_weight, strand_key); } -void ShortStack::OutlierFiltering(float data_reliability, bool is_update_valid_index){ - vector zeros_vector; - for(unsigned int i_read = 0; i_read < my_hypotheses.size(); ++i_read){ - if(my_hypotheses[i_read].success){ - zeros_vector.assign(my_hypotheses[i_read].responsibility.size(), 0.0f); - // Not yet initialize my_hypotheses. - if(my_hypotheses[i_read].responsibility == zeros_vector){ - my_hypotheses[i_read].InitializeDerivedQualities(); - } - my_hypotheses[i_read].success = (!my_hypotheses[i_read].LocalOutlierClassifier(data_reliability)); - } - } - if(is_update_valid_index){ - FindValidIndexes(); - } -} - -// family stuff // Update Family Responsibility if the Log Likelihoods of reads are updated. -void ShortStack::UpdateFamilyResponsibility_(const vector &hyp_freq, float data_reliability) { - for(unsigned int i_fam = 0; i_fam < my_eval_families.size(); ++i_fam){ - if(my_eval_families[i_fam].GetFunctionality()){ - my_eval_families[i_fam].ComputeFamilyLogLikelihoods(my_hypotheses); - my_eval_families[i_fam].UpdateFamilyResponsibility(hyp_freq, data_reliability); +void ShortStack::UpdateFamilyResponsibility_(const vector &hyp_freq, float outlier_prob) { + for (vector::iterator fam_it = my_eval_families.begin(); fam_it != my_eval_families.end(); ++fam_it){ + if(fam_it->GetFuncFromValid()){ + fam_it->ComputeFamilyLogLikelihoods(my_hypotheses); + fam_it->UpdateFamilyResponsibility(hyp_freq, outlier_prob); + // Note that the outlier responsibility was set to (close to) zero in ComputeFamilyLogLikelihoods and UpdateFamilyResponsibility. + // I am calculating the family outlier responsibility here. + fam_it->ComputeFamilyOutlierResponsibility(my_hypotheses, effective_min_family_size); } } } -// family stuff // Let the family_responsibility be loc_freq // UpdateFamilyResponsibility_() must be done first -void ShortStack::UpdateReadResponsibilityFromFamily_(unsigned int num_hyp_no_null, float data_reliability){ - vector loc_freq; - float safety_zero = 0.00001f; - - loc_freq.assign(num_hyp_no_null, 0.0f); +void ShortStack::UpdateReadResponsibilityFromFamily_(unsigned int num_hyp_no_null, float outlier_prob){ + float safety_zero = 0.00001f; // The close-to-zero outlier prob for the read which is clearly not an outlier. + vector loc_freq(num_hyp_no_null, 0.0f); + // Basically, I let the family responsibility be the prior for the read. + // However, I add a freq offset to the family responsibility to prevent the family bullies a read that has divergent opinion. + // Otherwise the read will be treated as an outlier and won't came back, which may also let the family become an outlier. for(unsigned int i_fam = 0; i_fam < my_eval_families.size(); ++i_fam){ // don't count the non-functional families - if(not my_eval_families[i_fam].GetFunctionality()){ + if(not my_eval_families[i_fam].GetFuncFromValid()){ continue; } - // Let loc_freq be {family_responsibility[1], family_responsibility[2], ...} with a safety zero + float freq_offset = 0.0f; + float freq_offset_gap = 0.01f; // I shall set the parameter outlier_probability << freq_offset_gap. + // Let loc_freq be {family_responsibility[1], family_responsibility[2], ...} with freq_offset float d_normalize = 0.0f; - for (unsigned int i_hyp=0; i_hyp outlier_probability. + freq_offset = (max_loc_freq > outlier_prob) ? outlier_prob + freq_offset_gap * (max_loc_freq - outlier_prob) : outlier_prob; + d_normalize = 1.0f + (freq_offset * (float) num_hyp_no_null); + for (unsigned int i_hyp = 0; i_hyp < num_hyp_no_null; ++i_hyp){ + loc_freq[i_hyp] += freq_offset; + loc_freq[i_hyp] /= d_normalize; } - for(unsigned int i_ndx = 0; i_ndx < my_eval_families[i_fam].family_members.size(); ++i_ndx){ - int i_read = my_eval_families[i_fam].family_members[i_ndx]; - my_hypotheses[i_read].UpdateResponsibility(loc_freq, data_reliability); + for (unsigned int i_ndx = 0; i_ndx < my_eval_families[i_fam].valid_family_members.size(); ++i_ndx){ + int i_read = my_eval_families[i_fam].valid_family_members[i_ndx]; + float my_outlier_prob = my_hypotheses[i_read].at_least_one_same_as_null ? safety_zero: outlier_prob; + my_hypotheses[i_read].UpdateResponsibility(loc_freq, my_outlier_prob); } } } -// family stuff // pretty much the same as void ShortStack::MultiFrequencyFromResponsibility(...) except we use the family responsibility void ShortStack::MultiFrequencyFromFamilyResponsibility_(vector &hyp_freq, vector &prior_frequency_weight, int strand_key){ hyp_freq.assign(hyp_freq.size(), 0.0f); - for(unsigned int i_fam = 0; i_fam < my_eval_families.size(); ++ i_fam){ if(strand_key >= 0 and my_eval_families[i_fam].strand_key != strand_key){ continue; } // don't count the not functional families - if(!(my_eval_families[i_fam].GetFunctionality())){ + if(not my_eval_families[i_fam].GetFuncFromValid()){ continue; } - for (unsigned int i_hyp=0; i_hyp &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float my_reliability, int strand_key) { - float my_LL = 0.0f; - +float ShortStack::PosteriorFrequencyLogLikelihoodFromFamilies_(const vector &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float outlier_prob, int strand_key) { + double my_LL = 0.0; // use double to calculate cumsum to enjance numerical stability + float my_reliability = 1.0f - outlier_prob; for(unsigned int i_fam = 0; i_fam < my_eval_families.size(); ++i_fam){ if(strand_key >= 0 and my_eval_families[i_fam].strand_key != strand_key){ continue; } // don't count the non-functional families - if(!(my_eval_families[i_fam].GetFunctionality())){ + if(!(my_eval_families[i_fam].GetFuncFromValid())){ continue; } - - my_LL += my_eval_families[i_fam].ComputeFamilyPosteriorLikelihood(hyp_freq, my_reliability); + my_LL += (double) my_eval_families[i_fam].ComputeFamilyPosteriorLikelihood(hyp_freq); } // add contribution from prior for (unsigned int i_hyp=0; i_hyp &hyp_freq, const vector &prior_frequency_weight, float prior_log_normalization, float my_reliability, int strand_key){ - // the posterior likelihood of hyp_freq is the posterior likelihood of hyp_freq from families - return PosteriorFrequencyFamilyLogLikelihood_(hyp_freq, prior_frequency_weight, prior_log_normalization, my_reliability, strand_key); -} - -void ShortStack::UpdateResponsibilityMolTags(const vector &hyp_freq, float data_reliability){ - // first update family responsibility - UpdateFamilyResponsibility_(hyp_freq, data_reliability); - // then update read responsibility from family - UpdateReadResponsibilityFromFamily_(hyp_freq.size(), data_reliability); -} - -void ShortStack::MultiFrequencyFromResponsibilityMolTags(vector &hyp_freq, vector &prior_frequency_weight, int strand_key){ - // Update hyp_freq using family responsibility - MultiFrequencyFromFamilyResponsibility_(hyp_freq, prior_frequency_weight, strand_key); +// A belief propagation approach +//@TODO: The outlier handling can be further improved. +void ShortStack::UpdateFamilyAndReadResponsibility_(const vector &hyp_freq, float outlier_prob){ + // 1. update family responsibility + UpdateFamilyResponsibility_(hyp_freq, outlier_prob); + // 2. update read responsibility from family + UpdateReadResponsibilityFromFamily_(hyp_freq.size(), outlier_prob); } void ShortStack::SwitchMolTagsPtr_(void){ if(is_molecular_tag_){ - ptrPosteriorFrequencyLogLikelihood_ = &ShortStack::PosteriorFrequencyLogLikelihoodMolTags; - ptrUpdateResponsibility_ = &ShortStack::UpdateResponsibilityMolTags; - ptrMultiFrequencyFromResponsibility_ = &ShortStack::MultiFrequencyFromResponsibilityMolTags; + // I do inference for the allele frequency from families + ptrPosteriorFrequencyLogLikelihood_ = &ShortStack::PosteriorFrequencyLogLikelihoodFromFamilies_; + ptrUpdateResponsibility_ = &ShortStack::UpdateFamilyAndReadResponsibility_; + ptrMultiFrequencyFromResponsibility_ = &ShortStack::MultiFrequencyFromFamilyResponsibility_; } else{ - ptrPosteriorFrequencyLogLikelihood_ = &ShortStack::PosteriorFrequencyLogLikelihoodNoMolTags; - ptrUpdateResponsibility_ = &ShortStack::UpdateResponsibilityNoMolTags; - ptrMultiFrequencyFromResponsibility_ = &ShortStack::MultiFrequencyFromResponsibilityNoMolTags; + // I do inference for the allele frequency from reads + ptrPosteriorFrequencyLogLikelihood_ = &ShortStack::PosteriorFrequencyLogLikelihoodFromReads_; + ptrUpdateResponsibility_ = &ShortStack::UpdateReadResponsibility_; + ptrMultiFrequencyFromResponsibility_ = &ShortStack::MultiFrequencyFromReadResponsibility_; } } @@ -323,37 +330,69 @@ void ShortStack::SetIsMolecularTag(bool is_mol_tag){ SwitchMolTagsPtr_(); } - unsigned int ShortStack::DetailLevel(void){ - if(is_molecular_tag_) - return num_func_families_; + if (is_molecular_tag_) + // With mol tagging, we often call variants using just a few (<5) variant families. + // If I serve detail_level = num_func_families_, then the singularity of log-posterior at f = 0 may cause big interpolation error. + // Thus I let detail_level finer than num_func_families_to obtain better numerical accuracy. + // Since the number of scanned points doesn't grow a lot in fast scan, the complexity is manageable. + // (Note): Setting detail_level to be too large (e.g. num_valid_read_counts_) may cause floating point error on frequency and cause problem in Fibonacci search dueing fast scan. + return num_func_families_ * 2; // This should be enough. return my_hypotheses.size(); } -// Set the functionality for all families in my_families. -void ShortStack::SetFuncionalityForFamilies(unsigned int min_fam_size){ +// Step 1): Initialize all families +// Step 2): Set the functionality of the families in my_families. +// Step 3): Update valid_index +void ShortStack::InitializeMyEvalFamilies(unsigned int num_hyp){ num_func_families_ = 0; - for(vector::iterator it_fam = my_eval_families.begin(); - it_fam != my_eval_families.end(); ++it_fam){ - vector fam_members_temp(0); - fam_members_temp.reserve(it_fam->family_members.size()); - for(vector::iterator it_read = it_fam->family_members.begin(); - it_read !=it_fam->family_members.end(); ++it_read){ - if(my_hypotheses[*it_read].success){ - fam_members_temp.push_back(*it_read); + for (vector::iterator fam_it = my_eval_families.begin(); fam_it != my_eval_families.end(); ++fam_it){ + fam_it->InitializeEvalFamily(num_hyp); + fam_it->ResetValidFamilyMembers(); + for (vector::iterator read_it = fam_it->all_family_members.begin(); read_it != fam_it->all_family_members.end(); ++read_it){ + if (my_hypotheses[*read_it].success){ + // valid_family_members contains only the reads that are successfully initialized + fam_it->valid_family_members.push_back(*read_it); } } - it_fam->family_members.swap(fam_members_temp); - if(not it_fam->SetFunctionality(min_fam_size)){ + fam_it->CountFamSizeFromValid(); + if (fam_it->SetFuncFromValid(effective_min_family_size)){ + ++num_func_families_; + num_valid_read_counts_ += fam_it->GetValidFamSize(); + } + else{ // disable the reads in a non-functional family - for(vector::iterator it_read = it_fam->family_members.begin(); - it_read !=it_fam->family_members.end(); ++it_read){ - my_hypotheses[*it_read].success = false; + for(vector::iterator read_it = fam_it->valid_family_members.begin(); read_it !=fam_it->valid_family_members.end(); ++read_it){ + my_hypotheses[*read_it].success = false; } - }else{ - ++num_func_families_; } } + + // Some of the reads may be set to not success. Need to update valid_index. FindValidIndexes(); } + +int ShortStack::OutlierCountsByFlowDisruptiveness(){ + int ol_counts = 0; + for (unsigned int i_read = 0; i_read < my_hypotheses.size(); ++i_read){ + ol_counts += my_hypotheses[i_read].OutlierByFlowDisruptiveness(); + } + if (DEBUG > 0){ + cout << endl << "+ Counting outlier reads using flow-disruptiveness:" < &prediction, float responsibility, float skew_estimate, vector &test_flow, vector &residuals); + void AddOneUpdateForHypothesis(vector &prediction, float responsibility, float skew_estimate, vector &test_flow, vector &residuals, vector &measurements_var); void DoLatentUpdate(); float RetrieveApproximateWeight(float x_val); void PushToPrior(); @@ -81,13 +81,17 @@ class StrandedSigmaGenerator{ BasicSigmaGenerator fwd,rev; // if want to revert to old-style bool combine_strands; - - StrandedSigmaGenerator(){combine_strands = false;}; + int DEBUG = 0; + StrandedSigmaGenerator(){ + combine_strands = false; + DEBUG = 0; + }; void UpdateSigmaGenerator(ShortStack &total_theory); void UpdateSigmaEstimates(ShortStack &total_theory); void DoStepForSigma(ShortStack &total_theory); void ResetSigmaGenerator(); + void PrintDebug(bool print_update = true); }; diff --git a/Analysis/VariantCaller/EnsembleEval/SkewGenerator.cpp b/Analysis/VariantCaller/EnsembleEval/SkewGenerator.cpp index 47da4587..1b13ffdd 100644 --- a/Analysis/VariantCaller/EnsembleEval/SkewGenerator.cpp +++ b/Analysis/VariantCaller/EnsembleEval/SkewGenerator.cpp @@ -23,7 +23,7 @@ void BasicSkewGenerator::AddOneUpdateForHypothesis(int strand_key, float respons void BasicSkewGenerator::AddCrossUpdate(CrossHypotheses &my_cross){ for (unsigned int i_hyp=1; i_hyp a, pair b){ @@ -10,12 +12,12 @@ bool compare_best_response(pair a, pair b){ void LatentSlate::PropagateTuningParameters(EnsembleEvalTuningParameters &my_params, int num_hyp_no_null) { // prior reliability for outlier read frequency - cur_posterior.clustering.data_reliability = my_params.DataReliability(); + cur_posterior.clustering.outlier_prob = my_params.outlier_prob; cur_posterior.clustering.germline_prior_strength = my_params.germline_prior_strength; + cur_posterior.gq_pair.max_detail_level = (unsigned int) my_params.max_detail_level; cur_posterior.gq_pair.min_detail_level_for_fast_scan = (unsigned int) my_params.min_detail_level_for_fast_scan; cur_posterior.ref_vs_all.min_detail_level_for_fast_scan = (unsigned int) my_params.min_detail_level_for_fast_scan; - //rev_posterior.data_reliability = my_params.DataReliability(); - //fwd_posterior.data_reliability = my_params.DataReliability(); + cur_posterior.ref_vs_all.max_detail_level = (unsigned int) my_params.max_detail_level; // prior precision and likelihood penalty for moving off-center bias_generator.InitForStrand(num_hyp_no_null-1); // num_alt = num_hyp_no_null-1 @@ -38,6 +40,16 @@ void LatentSlate::PropagateTuningParameters(EnsembleEvalTuningParameters &my_par skew_generator.dampened_skew = my_params.prediction_precision; } +void LatentSlate::SetAndPropagateDebug(int debug) { + DEBUG = debug; + cur_posterior.DEBUG = debug; + cur_posterior.ref_vs_all.DEBUG = debug; + cur_posterior.gq_pair.DEBUG = debug; + bias_generator.DEBUG = debug; + sigma_generator.DEBUG = debug; +} + + // see how quick we can make this void LatentSlate::LocalExecuteInference(ShortStack &total_theory, bool update_frequency, bool update_sigma, vector &start_frequency) { if (detailed_integral) { @@ -51,6 +63,9 @@ void LatentSlate::LocalExecuteInference(ShortStack &total_theory, bool update_fr void LatentSlate::FastStep(ShortStack &total_theory, bool update_frequency, bool update_sigma) { + if(DEBUG > 0){ + cout << " - Starting one iteration with allele_freq = " << PrintIteratorToString(cur_posterior.clustering.max_hyp_freq.begin(), cur_posterior.clustering.max_hyp_freq.end()) << (update_frequency? "." : ", update_frequency = false.") << endl; + } bias_generator.DoStepForBias(total_theory); // update bias estimate-> residuals->likelihoods if (update_frequency) @@ -60,7 +75,9 @@ void LatentSlate::FastStep(ShortStack &total_theory, bool update_frequency, bool sigma_generator.DoStepForSigma(total_theory); // update sigma estimate if (update_frequency) cur_posterior.QuickUpdateStep(total_theory); - + } + if(DEBUG > 0){ + cout << " - One iteration done: ref_vs_all.max_ll = "<< cur_posterior.ReturnJustLL() < fake_hs_alleles; + fake_hs_alleles.reserve(num_hyp_no_null - 1); // try pure frequencies for the alleles if(try_alternatives){ // try pure frequencies for alt alleles for(int i_hyp = 1; i_hyp < num_hyp_no_null; ++i_hyp){ int i_alt = i_hyp - 1; + if (allele_identity_vector[i_alt].status.isFakeHsAllele){ + // I don't want to try the pure freq for a fake hs allele/ + fake_hs_alleles.push_back(i_hyp); + continue; + } if(my_params.try_few_restart_freq and (allele_identity_vector[i_alt].ActAsSNP() or allele_identity_vector[i_alt].ActAsMNP())){ // skip the pure frequency of a snp or mnp alt allele. continue; } + try_me.assign(num_hyp_no_null, safety_zero / float(num_hyp_no_null - 1)); try_me[i_hyp] = 1.0f - safety_zero; try_hyp_freq.push_back(try_me); } + + bool has_ref_pure = false; // try the pure frequency of the ref allele if we try at least one pure frequency of alt allele - if(try_hyp_freq.size() > 1){ + if (try_hyp_freq.size() > 1){ try_me.assign(num_hyp_no_null, safety_zero / float(num_hyp_no_null - 1)); try_me[0] = 1.0f - safety_zero; try_hyp_freq.push_back(try_me); + has_ref_pure = true; + } + + // Finally, try the uniform allele freq precluding all fake HS alleles. + if (not fake_hs_alleles.empty()){ + int num_non_fake_hs_alleles = num_hyp_no_null - (int) fake_hs_alleles.size(); + if (num_non_fake_hs_alleles > 1 or (num_non_fake_hs_alleles == 1 and (not has_ref_pure))){ + try_me.assign(num_hyp_no_null, (1.0f - safety_zero) / (float) num_non_fake_hs_alleles); + float my_safety_zero = safety_zero / (float) fake_hs_alleles.size(); + for (unsigned int idx = 0; idx < fake_hs_alleles.size(); ++idx){ + try_me[fake_hs_alleles[idx]] = my_safety_zero; + } + try_hyp_freq.push_back(try_me); + } } } @@ -187,26 +224,64 @@ float HypothesisStack::ReturnMaxLL() { return cur_state.cur_posterior.ReturnMaxLL(); } -void HypothesisStack::InitForInference(PersistingThreadObjects &thread_objects, vector& read_stack, const InputStructures &global_context, int num_hyp_no_null, vector &allele_identity_vector) { +void HypothesisStack::InitForInference(PersistingThreadObjects &thread_objects, vector& read_stack, const InputStructures &global_context, vector &allele_identity_vector) { + int num_hyp_no_null = (int) allele_identity_vector.size() + 1; // number of alt alleles + 1 for ref + total_theory.num_hyp_not_null = num_hyp_no_null; PropagateTuningParameters(num_hyp_no_null); // sub-objects need to know - cur_state.cur_posterior.SetDebug(global_context.DEBUG > 1); // debug information for fast scan - // predict given hypotheses per read - total_theory.FillInPredictionsAndTestFlows(thread_objects, read_stack, global_context); - total_theory.FindValidIndexes(); // how many alleles? AllocateFrequencyStarts(num_hyp_no_null, allele_identity_vector); + // predict given hypotheses per read + total_theory.FillInPredictionsAndTestFlows(thread_objects, read_stack, global_context); + if(not total_theory.GetIsMolecularTag()){ + // If use mol tag, total_theory.FindValidIndexes() is done in total_theory.InitializeMyEvalFamilies(unsigned int num_hyp) + total_theory.FindValidIndexes(); + } + else{ + total_theory.FlowDisruptiveOutlierFiltering(false); // Filter out obvious outlier reads using flow-disruption. + total_theory.InitializeMyEvalFamilies((unsigned int) num_hyp_no_null + 1); + + if(DEBUG > 0){ + cout << endl << "+ Initialized families on the read stack: "<< endl + <<" - Number of reads on the read stack = " << total_theory.my_hypotheses.size() << endl + <<" - Number of valid reads on the read stack = " << total_theory.valid_indexes.size() << endl + <<" - Number of families on the read stack = "<< total_theory.my_eval_families.size() << endl + <<" - Number of functional families on the read stack = "<< total_theory.GetNumFuncFamilies() << endl; + } + } } void HypothesisStack::ExecuteInference() { + unsigned long t0 = clock(); + + if(DEBUG > 0){ + cout << endl << "+ Execute inference for the variant (" << PrintVariant(*variant) << ")" << endl; + } + // now with unrestrained inference ExecuteExtremeInferences(); // set up our filter cur_state.bias_checker.UpdateBiasChecker(total_theory); // @TODO: Now with fast scan, we can always do it - if(my_params.max_detail_level > 0){ - cur_state.ScanStrandPosterior(total_theory, true, my_params.max_detail_level); + if(cur_state.cur_posterior.ref_vs_all.max_detail_level > 0){ + cur_state.ScanStrandPosterior(total_theory, true); } + + if(DEBUG > 0){ + vector max_allele_freq = cur_state.cur_posterior.clustering.max_hyp_freq; + if(cur_state.cur_posterior.ref_vs_all.scan_ref_done){ + cur_state.cur_posterior.clustering.UpdateFrequencyAgainstOne(max_allele_freq, cur_state.cur_posterior.ref_vs_all.eval_at_frequency[cur_state.cur_posterior.ref_vs_all.max_index], 0); + } + cout << "+ Execute inference for the variant (" << PrintVariant(*variant) << ") done. " + << "Processing time = " << (double) (clock() - t0) / 1E6 << " sec."<< endl + << " - Winner of the initial allele_freq = " << PrintIteratorToString(cur_state.start_freq_of_winner.begin(), cur_state.start_freq_of_winner.end()) << endl; + cur_state.bias_generator.PrintDebug(false); + cur_state.sigma_generator.PrintDebug(false); + cout << " - params_ll = "<< cur_state.cur_posterior.params_ll << endl + << " - ref_vs_all.max_ll + params_ll = "<< cur_state.cur_posterior.ReturnMaxLL() <<" @ allele_freq = " << PrintIteratorToString(max_allele_freq.begin(), max_allele_freq.end()) + << (cur_state.cur_posterior.ref_vs_all.scan_ref_done? " from scan." : "from responsibility.") << endl << endl; + } + } void HypothesisStack::SetAlternateFromMain() { @@ -218,24 +293,42 @@ void HypothesisStack::SetAlternateFromMain() { -float HypothesisStack::ExecuteOneRestart(vector &restart_hyp, int max_detail_level){ +float HypothesisStack::ExecuteOneRestart(vector &restart_hyp){ LatentSlate tmp_state; tmp_state = cur_state; tmp_state.detailed_integral = false; - tmp_state.start_freq_of_winner =restart_hyp; - total_theory.ResetQualities(); // clean slate to begin again + tmp_state.start_freq_of_winner = restart_hyp; + total_theory.ResetQualities(my_params.outlier_prob); // clean slate to begin again tmp_state.ResetToOrigin(); // everyone back to starting places + if (DEBUG > 0){ + cout<< "+ Restart the EM algorithm with initial allele_freq = " << PrintIteratorToString(restart_hyp.begin(), restart_hyp.end()) << endl; + } tmp_state.LocalExecuteInference(total_theory, true, true, restart_hyp); // start at reference - if(max_detail_level < 1){ - tmp_state.ScanStrandPosterior(total_theory, true, 0); + + if( tmp_state.cur_posterior.ref_vs_all.max_detail_level == 0){ + if (DEBUG > 0){ + cout<< " - Scan posterior likelihood for the restart of the EM algorithm."<< endl; + } + tmp_state.ScanStrandPosterior(total_theory, true); } float restart_LL=tmp_state.cur_posterior.ReturnMaxLL(); + if (DEBUG > 0){ + vector max_allele_freq = tmp_state.cur_posterior.clustering.max_hyp_freq; + if(tmp_state.cur_posterior.ref_vs_all.scan_ref_done){ + tmp_state.cur_posterior.clustering.UpdateFrequencyAgainstOne(max_allele_freq, tmp_state.cur_posterior.ref_vs_all.eval_at_frequency[tmp_state.cur_posterior.ref_vs_all.max_index], 0); + } + cout << "+ Restart the EM algorithm with initial allele_freq = " << PrintIteratorToString(restart_hyp.begin(), restart_hyp.end()) << " done. "<< endl + << " - params_ll = "<< tmp_state.cur_posterior.params_ll << endl + << " - ref_vs_all.max_ll + params_ll = "<< restart_LL << " @ allele_freq = " << PrintIteratorToString(max_allele_freq.begin(), max_allele_freq.end()) << (tmp_state.cur_posterior.ref_vs_all.scan_ref_done? " from scan." : "from responsibility.") << endl; + } + if (cur_state.cur_posterior.ReturnMaxLL() 0){ + cout<< " - The EM algorithm will be restarted by trying "<< try_hyp_freq.size() << " different initial allele_freq."<< endl; + } for (unsigned int i_start=0; i_start 0){ + int pair_0 = cur_posterior.gq_pair.freq_pair[0]; + int pair_1 = cur_posterior.gq_pair.freq_pair[1]; + float allele_ratio_cut_off = real_safety / (1.0f - real_safety); + cout << "+ Calling genotype: " << endl; + cout << " - Baseline allele_freq = " << PrintIteratorToString(cur_posterior.clustering.max_hyp_freq.begin(), cur_posterior.clustering.max_hyp_freq.end()) << endl; + cout << " - H(GT="<< pair_0 <<"/"<< pair_0 + << ": allele_freq[" << pair_1 <<"]/(allele_freq["<< pair_0 << "]+allele_freq[" << pair_1 << "])" + << " < " << real_safety << "), log(definite integral of posterior) = " << genotype_interval[0] < " << real_safety << "), log(definite integral of posterior) = " << genotype_interval[2] << endl; + cout << " - Call GT = "; + switch (genotype_call){ + case 0: + cout << pair_0 <<"/"<< pair_0; + break; + case 1: + cout << pair_0 <<"/"<< pair_1; + break; + case 2: + cout << pair_1 <<"/"<< pair_1; + break; + default: + cout << "?/?"; + break; + } + cout << ", GQ = " << quasi_phred_quality_score << endl; + } + } bool RejectionByIntegral(vector dual_interval, float &reject_status_quality_score){ // reject ref = quality of rejection call - bool is_ref = false; - float log_ref = 0.0f; - int top = 0; - int bottom = 1; - - // First normalize dual_interval to enhance the floating point accuracy - float max_dual_interval = max(dual_interval[0], dual_interval[1]); - for (unsigned int i = 0; i < dual_interval.size(); ++i){ - dual_interval[i] -= max_dual_interval;} - - if (dual_interval[1]>dual_interval[0]) { - // if reference, how strongly can we reject the outer interval - log_ref = dual_interval[0]; - is_ref = false; - top=1; - bottom=0; - } - else { - // if var, how strongly can we reject ref - log_ref = dual_interval[1]; - is_ref = true; - top=0; - bottom=1; - } + bool is_ref = dual_interval[0] >= dual_interval[1]; + double dual_interval_diff = abs((double) dual_interval[0] - (double) dual_interval[1]); // Use double to enhance the numerical accuracy + // QUAL = exp(min(dual_interval)) / ( exp(min(dual_interval)) + exp(max(dual_interval) ) ) in phred scale which is equivalent to the following expression. + reject_status_quality_score = (float) (10.0 / log(10.0) * (dual_interval_diff + log(1.0 + exp(-dual_interval_diff)))); + reject_status_quality_score = max(reject_status_quality_score, 3.0103f); // Guard by the minimum possible QUAL score to prevent floating point error. - float log_all = dual_interval[top]+ log(1.0f+exp(dual_interval[bottom]-dual_interval[top])); - - reject_status_quality_score = 10.0f * (log_all - log_ref) / log(10.0f); // how much mass speaks against the pure reference state - reject_status_quality_score = max(reject_status_quality_score, 3.010f); // Prevent floating error that causes QUAL < 10*log10(2) - if (isnan(reject_status_quality_score)) { - cout << "Warning: reject ref score NAN " << endl; - reject_status_quality_score = 0.0f; - } return is_ref; } @@ -424,16 +524,22 @@ bool DoRejectionByIntegral(PosteriorInference &cur_posterior, float real_safety, dual_interval[i_cut] = cur_posterior.ref_vs_all.LogDefiniteIntegral(variant_cuts[i_cut+1], variant_cuts[i_cut]); } - return RejectionByIntegral(dual_interval, reject_status_quality_score); + bool is_ref = RejectionByIntegral(dual_interval, reject_status_quality_score); + if (cur_posterior.DEBUG > 0){ + cout << "+ Calling ref vs. (all alt): "<< endl; + cout << " - Baseline allele_freq = " << PrintIteratorToString(cur_posterior.clustering.max_hyp_freq.begin(), cur_posterior.clustering.max_hyp_freq.end()) << endl; + float min_area = min(dual_interval[0], dual_interval[1]); + cout << " - H(ref: sum(allele_freq[1:])/allele_freq[0] < "<< real_safety << "/" << (1.0f - real_safety) + << " = " << real_safety / (1.0f - real_safety) << "), log(definite integral of posterior) = " << min_area <<" + " << (dual_interval[0] - min_area) << endl; + cout << " - H(all alt: sum(allele_freq[1:])/allele_freq[0] >= "<< real_safety << "/" << (1.0f - real_safety) + << " = " << real_safety / (1.0f - real_safety) << "), log(definite integral of posterior) = " << min_area <<" + " << (dual_interval[1] - min_area) << endl; + cout << " - "<< (is_ref? "Call reference" : "Call variant")<< ", QUAL = " << reject_status_quality_score << endl; + } + return is_ref; } -// must have scan done to be workable -// must have set which pair of hypotheses are being checked -bool CallByIntegral(PosteriorInference &cur_posterior, float hom_safety, int &genotype_call, float &quasi_phred_quality_score, float &reject_status_quality_score) { - // divide MAF into three zones = 0/1/2 variant alleles - +bool CalculateRealSafetyAfCutoff(float af_cutoff, int detail_level, float &real_safety){ // make sure we're safe based on the number of reads - int detail_level = cur_posterior.ref_vs_all.eval_at_frequency.size(); // if we don't have enough reads might be crazy near mid float fine_scale = 0.5f / (detail_level + 1.0f); float mid_cutoff = 0.5f - fine_scale; @@ -441,29 +547,61 @@ bool CallByIntegral(PosteriorInference &cur_posterior, float hom_safety, int &ge // so no sense testing that low float low_cutoff = 1.0f / detail_level; // if we have a small number of reads, these ranges may conflict - float real_safety = min(mid_cutoff, max(low_cutoff, hom_safety)); + real_safety = min(mid_cutoff, max(low_cutoff, af_cutoff)); + bool safety_active_flag = false; + if (fabs(real_safety- af_cutoff) > fine_scale) + safety_active_flag = true; + return safety_active_flag; +} +// must have scan done to be workable +// must have set which pair of hypotheses are being checked +bool HypothesisStack::CallByIntegral(float af_cutoff_rej, float af_cutoff_gt, vector &genotype_component, float &quasi_phred_quality_score, float &reject_status_quality_score, int &qual_type) { + // divide MAF into three zones = 0/1/2 variant alleles bool safety_active_flag = false; - if (fabs(real_safety - hom_safety) > fine_scale) - safety_active_flag = true; + int detail_level = cur_state.cur_posterior.ref_vs_all.eval_at_frequency.size(); - // bool isref = - DoRejectionByIntegral(cur_posterior, real_safety, reject_status_quality_score); + float real_safety_rej = af_cutoff_rej; + safety_active_flag += CalculateRealSafetyAfCutoff(af_cutoff_rej, detail_level, real_safety_rej); - // in dual allele case, do not need to check "is-ref" before making some quality assessment - DoGenotypeByIntegral(cur_posterior, real_safety, genotype_call, quasi_phred_quality_score); - //cout << genotype_call << "\t" << reject_status_quality_score << "\t" << quasi_phred_quality_score << endl; + bool is_ref = DoRejectionByIntegral(cur_state.cur_posterior, real_safety_rej, reject_status_quality_score); + qual_type = is_ref? 0 : 1; - return safety_active_flag; -} + /* Less exception is better, though it is possible to see some non-trivial cases. + // If I reject the variant, I force to make a reference call (i.e., GT=0/0) and output GQ=QUAL. + // In this case, I don't even try to scan the gq pair and call DoGenotypeByIntegral. + if (is_ref){ + quasi_phred_quality_score = reject_status_quality_score; + genotype_component.assign(2, 0); + if (cur_state.cur_posterior.DEBUG > 0){ + cout << "+ Set GT=0/0 and GQ=QUAL for a reference call." << endl; + } + return safety_active_flag; + } + */ -bool HypothesisStack::CallGermline(float hom_safety, int &genotype_call, float &quasi_phred_quality_score, float &reject_status_quality_score){ - bool retval = CallByIntegral(cur_state.cur_posterior, hom_safety, genotype_call, quasi_phred_quality_score, reject_status_quality_score); - return retval; -} + int genotype_call = 0; + assert(not cur_state.cur_posterior.gq_pair.freq_pair.empty()); // gq_pair.freq_pair must be set. + if (not cur_state.cur_posterior.gq_pair.scan_pair_done){ + cur_state.cur_posterior.gq_pair.DoPosteriorFrequencyScan(total_theory, cur_state.cur_posterior.clustering, true, ALL_STRAND_KEY, false); + } + float real_safety_gt = af_cutoff_gt; + safety_active_flag += CalculateRealSafetyAfCutoff(af_cutoff_gt, detail_level, real_safety_gt); + // in dual allele case, do not need to check "is-ref" before making some quality assessment + DoGenotypeByIntegral(cur_state.cur_posterior, real_safety_gt, genotype_call, quasi_phred_quality_score); + + genotype_component = cur_state.cur_posterior.gq_pair.freq_pair; // het diploid_choice[0]/diploid_choice[1] + if(genotype_call == 2){ // hom diploid_choice[1]/diploid_choice[1] + genotype_component[0] = genotype_component[1]; + } + else if(genotype_call == 0){ // hom diploid_choice[0]/diploid_choice[0] + genotype_component[1] = genotype_component[0]; + } + return safety_active_flag; +} // evidence for i_allele vs ref void EnsembleEval::ScanSupportingEvidence(float &mean_ll_delta, int i_allele) { @@ -473,15 +611,24 @@ void EnsembleEval::ScanSupportingEvidence(float &mean_ll_delta, int i_allele) { int ref_hyp = 1; int alt_hyp = i_allele + 2; // alt_alleles = 0->n not counting ref >or< null, therefore alt-allele 0 = 2 - for (unsigned int i_read = 0; i_read < allele_eval.total_theory.my_hypotheses.size(); i_read++) { - if (allele_eval.total_theory.my_hypotheses[i_read].success) { - // measure disruption - - mean_ll_delta += allele_eval.total_theory.my_hypotheses[i_read].ComputeLLDifference(ref_hyp, alt_hyp); - count++; - } + if (allele_eval.total_theory.GetIsMolecularTag()){ + for (vector::iterator fam_it = allele_eval.total_theory.my_eval_families.begin(); fam_it != allele_eval.total_theory.my_eval_families.end(); ++fam_it){ + if (fam_it->GetFuncFromValid()){ + mean_ll_delta += fam_it->ComputeLLDifference(ref_hyp, alt_hyp); + ++count; + } + } } - mean_ll_delta /= (count + 0.01f); + else{ + for (vector::iterator read_it = allele_eval.total_theory.my_hypotheses.begin(); read_it != allele_eval.total_theory.my_hypotheses.end(); ++read_it){ + if (read_it->success){ + mean_ll_delta += read_it->ComputeLLDifference(ref_hyp, alt_hyp); + ++count; + } + } + } + + mean_ll_delta /= ((float) count + 0.01f); mean_ll_delta = 10.0f * mean_ll_delta / log(10.0f); // phred-scaled } @@ -489,23 +636,19 @@ void EnsembleEval::ScanSupportingEvidence(float &mean_ll_delta, int i_allele) { // read_id_[i] = -1 means the i-th read is classified as an outlier. // read_id_[i] = 0 means the i-th read is classified as ref. // read_id_[i] = 1 means the i-th read is classified as the variant allele 1, and so on. -void EnsembleEval::ApproximateHardClassifierForReads(){ - read_id_.clear(); - strand_id_.clear(); - dist_to_left_.clear(); - dist_to_right_.clear(); - +void EnsembleEval::ApproximateHardClassifierForReads() +{ read_id_.assign(read_stack.size(), -1); strand_id_.assign(read_stack.size(), false); dist_to_left_.assign(read_stack.size(), -1); dist_to_right_.assign(read_stack.size(), -1); int position0 = variant->position -1; // variant->position 1-base: vcflib/Variant.h - for (unsigned int i_read = 0; i_read < read_stack.size(); ++i_read) { // compute read_id_ if(allele_eval.total_theory.my_hypotheses[i_read].success){ - read_id_[i_read] = allele_eval.total_theory.my_hypotheses[i_read].MostResponsible() - 1; // -1 = null, 0 = ref , ... + int most_responsibile_hyp = allele_eval.total_theory.my_hypotheses[i_read].MostResponsible(); + read_id_[i_read] = most_responsibile_hyp - 1; // -1 = null, 0 = ref , ... } else{ read_id_[i_read] = -1; // failure = outlier @@ -523,47 +666,178 @@ void EnsembleEval::ApproximateHardClassifierForReads(){ //compute strand_id_ strand_id_[i_read] = not (read_stack[i_read]->is_reverse_strand); } +} - is_hard_classification_for_reads_done_ = true; +struct PositionGroup{ + vector group_members; + uint16_t mapq_sum; + int group_size; + int group_position; + PositionGroup(){ mapq_sum = 0; group_size = 0; group_position = 0;}; + PositionGroup(const Alignment* rai, int p){ + group_members = {rai}; + mapq_sum = rai->alignment.MapQuality; + group_size = rai->read_count; + group_position = p; + }; + void AddNew(const Alignment* rai){ + group_members.push_back(rai); + mapq_sum += rai->alignment.MapQuality; + group_size += rai->read_count; + }; + // Compare two groups. + // A group wins if it has more reads. If ties, the group with larger accumulated mapping quality wins. + bool operator>(const PositionGroup& rhs){ + if (group_size == rhs.group_size){ + return (mapq_sum > rhs.mapq_sum); + } + return (group_size > rhs.group_size); + }; +}; + +int GetConsensusPosition(EnsembleEval* my_ensemble, EvalFamily* my_fam, bool is_to_left){ + if (not my_fam->GetFuncFromValid()){ + return -1; + } + int position0 = my_ensemble->variant->position -1; // variant->position 1-base: vcflib/Variant.h + vector position_groups_vec; + position_groups_vec.reserve(my_fam->valid_family_members.size()); + for (vector::iterator member_it = my_fam->valid_family_members.begin(); member_it != my_fam->valid_family_members.end(); ++member_it) + { + if (not my_ensemble->allele_eval.total_theory.my_hypotheses[*member_it].success){ + continue; + } + const Alignment* rai = my_ensemble->read_stack[*member_it]; + int my_position = is_to_left? position0 - rai->align_start : rai->align_end - position0; + assert(my_position >= 0); + bool group_exists = false; + for (vector::iterator pos_group_it = position_groups_vec.begin(); pos_group_it != position_groups_vec.end(); ++pos_group_it){ + if (my_position == pos_group_it->group_position){ + group_exists = true; + pos_group_it->AddNew(rai); + break; + } + } + if (not group_exists){ + position_groups_vec.push_back(PositionGroup(rai, my_position)); + } + } + if (position_groups_vec.empty()){ + return -1; + } + vector::iterator best_pos_group_it = position_groups_vec.begin(); + for (vector::iterator pos_group_it = position_groups_vec.begin(); pos_group_it != position_groups_vec.end(); ++pos_group_it){ + if (*pos_group_it > *best_pos_group_it){ + best_pos_group_it = pos_group_it; + } + } + return best_pos_group_it->group_position; } // Hard classify each family using its family responsibility -// Compute family_id_ and family_strand_id_, which are similar to read_id_, strand_id_ for MuitiBook -// family_id_ is obtained from the most responsible hypothesis of the family -// family_id_ stores the hard classification results for "families" (can be non-functional) -// family_id_[i] = -1 means the i-th family is classified as an outlier. -// family_id_[i] = 0 means the i-th family is classified as ref. -// family_id_[i] = 1 means the i-th family is classified as the variant allele 1, and so on. +// Compute read_id_ and strand_id_, which are similar to ApproximateHardClassifierForReads +// read_id_ is obtained from the most responsible hypothesis of the family +// read_id_ stores the hard classification results for "families" (can be non-functional) +// read_id_[i] = -1 means the i-th family is classified as an outlier. +// read_id_[i] = 0 means the i-th family is classified as ref. +// read_id_[i] = 1 means the i-th family is classified as the variant allele 1, and so on. void EnsembleEval::ApproximateHardClassifierForFamilies(){ if(not allele_eval.total_theory.GetIsMolecularTag()){ - cout<<"Warning: Skip EnsembleEval::ApproximateHardClassifierForFamilies() because use_mol_tag is off!"<< endl; + cerr<<"Warning: Skip EnsembleEval::ApproximateHardClassifierForFamilies() because use_mol_tag is off!"<< endl; return; } unsigned int num_families = allele_eval.total_theory.my_eval_families.size(); - int fam_count = 0; - family_id_.assign(num_families, -1); // every family starts with outlier - family_strand_id_.assign(num_families, false); - max_family_responsibility_.assign(num_families, 0.0f); + int position0 = variant->position -1; // variant->position 1-base: vcflib/Variant.h + read_id_.assign(num_families, -1); // every family starts with outlier + strand_id_.assign(num_families, false); + dist_to_left_.assign(num_families, -1); + dist_to_right_.assign(num_families, -1); + alt_fam_indices_.resize(allele_identity_vector.size()); + + for(unsigned int fam_idx = 0; fam_idx < allele_eval.total_theory.my_eval_families.size(); ++fam_idx){ + EvalFamily* my_fam = &(allele_eval.total_theory.my_eval_families[fam_idx]); + // Hard classify the family to allele_assigned (-1 = outlier, 0 = ref, 1 = alt1, etc) + int allele_assigned = my_fam->GetFuncFromValid() ? my_fam->MostResponsible() - 1 : -1; // non-func familiy = outlier + read_id_[fam_idx] = allele_assigned; + strand_id_[fam_idx] = (my_fam->strand_key == 0); + + // Classify as an alt + if (allele_assigned > 0){ + alt_fam_indices_[allele_assigned - 1].push_back(fam_idx); + } - for(vector::iterator fam_it = allele_eval.total_theory.my_eval_families.begin(); fam_it != allele_eval.total_theory.my_eval_families.end(); ++fam_it){ - // don't count the non-functional families - if(fam_it->GetFunctionality()){ - family_id_[fam_count] = fam_it->MostResponsible() - 1; + // Assign position + if (allele_assigned > -1){ + dist_to_left_[fam_idx] = GetConsensusPosition(this, my_fam, true); + dist_to_right_[fam_idx] = GetConsensusPosition(this, my_fam, false); + } + } + + if (DEBUG > 0){ + unsigned int max_num_print_per_allele = 100; + vector > print_families_per_allele(allele_identity_vector.size() + 1); + for (unsigned int i_allele = 0; i_allele < print_families_per_allele.size(); ++i_allele){ + print_families_per_allele[i_allele].reserve(max_num_print_per_allele + 1); + } + for(unsigned int fam_idx = 0; fam_idx < read_id_.size(); ++fam_idx){ + if (read_id_[fam_idx] >= 0){ + if (print_families_per_allele[read_id_[fam_idx]].size() <= max_num_print_per_allele){ + print_families_per_allele[read_id_[fam_idx]].push_back(fam_idx); + } + } + } + cout << "+ Hard-classifying "<< num_families << " families on read stack for the variant ("<< PrintVariant(*variant) << "):" << endl; + for (unsigned int i_allele = 0; i_allele < print_families_per_allele.size(); ++i_allele){ + cout << " - Families that are most responsible for allele " << i_allele <<": "; + for (unsigned int i = 0; i < min((unsigned int) print_families_per_allele[i_allele].size(), max_num_print_per_allele); ++i){ + unsigned int i_fam = print_families_per_allele[i_allele][i]; + cout << allele_eval.total_theory.my_eval_families[i_fam].family_barcode << ", "; + } + if ((unsigned int) print_families_per_allele[i_allele].size() > max_num_print_per_allele) + cout << "......"; + cout << endl; + } + } +} + +void EnsembleEval::DetectPossiblePolyploidyAlleles(const vector& allele_freq_est, const ControlCallAndFilters &my_controls, const vector& variant_specific_params){ + assert(allele_freq_est.size() == allele_identity_vector.size() + 1); + float min_of_min_allele_freq = 1.0f; + is_possible_polyploidy_allele.assign(allele_freq_est.size(), false); + + if (DEBUG){ + cout << "+ Detecting Possible Polyploidy Alleles (PPA):" << endl + << " - estimated_allele_freq = " << PrintIteratorToString(allele_freq_est.begin(), allele_freq_est.end()) << endl; + } + + for (int i_allele = (int) allele_freq_est.size() - 1; i_allele >= 0; --i_allele){ + int i_alt = i_allele - 1; + float my_min_allele_freq = (i_allele == 0)? min_of_min_allele_freq : 0.0f; + if (i_allele > 0){ + my_min_allele_freq = FreqThresholdByType(allele_identity_vector[i_alt], my_controls, variant_specific_params[i_alt]); + min_of_min_allele_freq = min(my_min_allele_freq, min_of_min_allele_freq); + } + is_possible_polyploidy_allele[i_allele] = allele_freq_est[i_allele] > my_min_allele_freq; + + if (DEBUG){ + cout << " - Allele "<< i_allele << (is_possible_polyploidy_allele[i_allele]? " may be" : " is not") << " a PPA: estimated_allele_freq[" << i_allele << "] = " << allele_freq_est[i_allele] << (is_possible_polyploidy_allele[i_allele]? " > " : " <= ") << "min-allele-freq = " << my_min_allele_freq << endl; } - max_family_responsibility_[fam_count] = fam_it->family_responsibility[family_id_[fam_count] + 1]; - family_strand_id_[fam_count] = (fam_it->strand_key == 0); - ++fam_count; } - is_hard_classification_for_families_done_ = true; } int EnsembleEval::DetectBestMultiAllelePair(){ + vector allele_freq_estimation; + return DetectBestMultiAllelePair(allele_freq_estimation); +} + + +int EnsembleEval::DetectBestMultiAllelePair(vector& allele_freq_estimation){ int best_alt_ndx = 0; // forced choice with ref //@TODO: just get the plane off the ground //@TODO: do the top pair by responsibility vector< pair > best_allele_test; - int num_hyp_no_null = allele_eval.total_theory.my_hypotheses[0].responsibility.size()-1; + int num_hyp_no_null = (int) allele_identity_vector.size() + 1; best_allele_test.resize(num_hyp_no_null); // null can never be a winner in "best allele" sweepstakes for (unsigned int i_alt=0; i_alt -1){ - best_allele_test[my_alt].second += max_family_responsibility_[i_family]; + best_allele_test[my_alt].second += allele_eval.total_theory.my_eval_families[i_family].family_responsibility[my_alt + 1]; } // otherwise count for nothing } } // not using molecular tagging else{ - if(not is_hard_classification_for_reads_done_){ + if(read_id_.empty()){ ApproximateHardClassifierForReads(); } // take responsibility for(unsigned int i_read = 0; i_read < read_id_.size(); ++i_read){ int my_alt = read_id_[i_read]; if (my_alt > -1){ - best_allele_test[my_alt].second += allele_eval.total_theory.my_hypotheses[i_read].responsibility[my_alt + 1]; + best_allele_test[my_alt].second += allele_eval.total_theory.my_hypotheses[i_read].weighted_responsibility[my_alt + 1]; } // otherwise count for nothing } } + // calculate allele_freq_estimation + float total_weight = 0.0f; + allele_freq_estimation.assign(num_hyp_no_null, 0.0f); + for(int i = 0; i < num_hyp_no_null; ++i){ + total_weight += best_allele_test[i].second; + } + for(int i = 0; i < num_hyp_no_null; ++i){ + allele_freq_estimation[i] = best_allele_test[i].second / total_weight; + } + + if (DEBUG > 0){ + cout << "+ Detecting the best allele pair for the variant ("<< PrintVariant(*variant) << "):" << endl; + cout << " - allele_freq = " << PrintIteratorToString(allele_freq_estimation.begin(), allele_freq_estimation.end()) << " from semi-hard classification result." << endl; + } + // pick my pair of best alleles sort(best_allele_test.begin(), best_allele_test.end(), compare_best_response); // not-null choices + diploid_choice.assign(2, 0); diploid_choice[0] = best_allele_test[0].first; // index of biggest weight diploid_choice[1] = best_allele_test[1].first; // index of second-biggest weight // problematic cases: @@ -619,57 +909,1469 @@ int EnsembleEval::DetectBestMultiAllelePair(){ // sort as final step to avoid best_alt_ndx reflecting a worse allele sort(diploid_choice.begin(),diploid_choice.end()); // now in increasing allele order as needed - is_detect_best_multi_allele_pair_done_ = true; + if (DEBUG > 0){ + cout << " - The best allele pair = ("<< best_allele_test[0].first<<", "<< best_allele_test[1].first << ")"<< endl << endl; + } + return best_alt_ndx; } +void EnsembleEval::MultiAlleleGenotype(float af_cutoff_rej, float af_cutoff_gt, vector &genotype_component, float >_quality_score, float &reject_status_quality_score, int &quality_type){ + if (diploid_choice.empty()){ + // detect best allele pair if not done yet. + DetectBestMultiAllelePair(); + } + // set diploid in gq_pair + allele_eval.cur_state.cur_posterior.gq_pair.freq_pair = diploid_choice; -void EnsembleEval::ComputePosteriorGenotype(int _alt_allele_index,float local_min_allele_freq, - int &genotype_call, float >_quality_score, float &reject_status_quality_score){ - allele_eval.CallGermline(local_min_allele_freq, genotype_call, gt_quality_score, reject_status_quality_score); + // Call by integral + allele_eval.CallByIntegral(af_cutoff_rej, af_cutoff_gt, genotype_component, gt_quality_score, reject_status_quality_score, quality_type); } -void EnsembleEval::MultiAlleleGenotype(float local_min_allele_freq, vector &genotype_component, float >_quality_score, float &reject_status_quality_score, int max_detail_level){ - // detect best allele hard classify - if(not is_detect_best_multi_allele_pair_done_){ // don't need to do it again if we've already done - DetectBestMultiAllelePair(); // diploid_choice set by posterior responsibility +// Rules of setting the effective min fam size: +// If override requested by HS, then effective_min_family_size = the maximum one among all overrides. +// If not override by HS, effective_min_family_size = min_family_size + +void EnsembleEval::SetEffectiveMinFamilySize(const ExtendParameters& parameters, const vector& variant_specific_params){ + assert(allele_identity_vector.size() > 0); + allele_eval.total_theory.effective_min_family_size = (unsigned int) parameters.tag_trimmer_parameters.min_family_size; + + bool is_override = false; + for (unsigned int i_alt = 0; i_alt < variant_specific_params.size(); ++i_alt){ + if (variant_specific_params[i_alt].min_tag_fam_size_override){ + if (variant_specific_params[i_alt].min_tag_fam_size < 1){ + cerr << "WARNING: Fail to override the parameter min_tag_fam_size by " << variant_specific_params[i_alt].min_tag_fam_size << " < 1." << endl; + continue; + } + if (not is_override){ + // This is the first override. + allele_eval.total_theory.effective_min_family_size = (unsigned int) variant_specific_params[i_alt].min_tag_fam_size; + }else{ + allele_eval.total_theory.effective_min_family_size = max(allele_eval.total_theory.effective_min_family_size, (unsigned int) variant_specific_params[i_alt].min_tag_fam_size); + } + is_override = true; + } + } + if (is_override){ + if (DEBUG){ + cout << "+ Override min_fam_size to " << allele_eval.total_theory.effective_min_family_size << endl; + } + return; + } + // Increase min_fam_size if I found an allele is HP-INDEL. + if (parameters.tag_trimmer_parameters.indel_func_size_offset > 0){ + for (unsigned int i_alt = 0; i_alt < allele_identity_vector.size(); ++i_alt){ + if (allele_identity_vector[i_alt].status.isHPIndel){ + allele_eval.total_theory.effective_min_family_size += (unsigned int) parameters.tag_trimmer_parameters.indel_func_size_offset; + if (DEBUG){ + cout << "+ Found allele "<< i_alt + 1 << " is HP-INDEL: increase min_fam_size from " + << parameters.tag_trimmer_parameters.min_family_size + << " to " << allele_eval.total_theory.effective_min_family_size << endl; + } + return; + } + } + } +} + + + +void EnsembleEval::SetAndPropagateParameters(ExtendParameters* parameters, bool use_molecular_tag, const vector& variant_specific_params){ + allele_eval.my_params = parameters->my_eval_control; + // Set debug level + DEBUG = parameters->program_flow.DEBUG; + allele_eval.DEBUG = parameters->program_flow.DEBUG; + allele_eval.cur_state.SetAndPropagateDebug(parameters->program_flow.DEBUG); + allele_eval.total_theory.DEBUG = parameters->program_flow.DEBUG; + // Set target min-allele-freq for fast scan + allele_eval.cur_state.cur_posterior.ref_vs_all.SetTargetMinAlleleFreq(*parameters, variant_specific_params); + allele_eval.cur_state.cur_posterior.gq_pair.SetTargetMinAlleleFreq(*parameters, variant_specific_params); + // Set molecular tag related parameters + allele_eval.total_theory.SetIsMolecularTag(use_molecular_tag); + SetEffectiveMinFamilySize(*parameters, variant_specific_params); + // only rich_json_diagnostic needs full data + allele_eval.total_theory.preserve_full_data = parameters->program_flow.rich_json_diagnostic; +} + +void EnsembleEval::FlowDisruptivenessInReadLevel(const InputStructures &global_context) +{ + for (unsigned int i_read = 0; i_read < read_stack.size(); ++i_read){ + allele_eval.total_theory.my_hypotheses[i_read].FillInFlowDisruptivenessMatrix(global_context.flow_order_vector.at(read_stack[i_read]->flow_order_index), *(read_stack[i_read])); + } + if (allele_eval.total_theory.GetIsMolecularTag()){ + for (vector::iterator fam_it = allele_eval.total_theory.my_eval_families.begin(); fam_it != allele_eval.total_theory.my_eval_families.end(); ++fam_it){ + fam_it->FillInFlowDisruptivenessMatrix(allele_eval.total_theory.my_hypotheses); + } + } +} + + +// The rule for determining FD in the read stack level is the following. +// Suppose I want to determine the FD between i_hyp and j_hyp. Denote the set reads that support i_hyp by Reads(i_hyp). +// (Case 1): Both hypotheses have reads support, i.e., Reads(i_hyp) and Reads(j_hyp) are both non-empty +// (Case 1.a): I find reads in Reads(i_hyp) and reads in Reads(j_hyp) indicate that the pair (i_hyp, j_hyp) is FD. Then the pair (i_hyp, j_hyp) is FD. +// (Case 1.b): I find reads in Reads(i_hyp) shows (i_hyp, j_hyp) is FD, but no (or too few) reads in Reads(j_hyp) says (i_hyp, j_hyp) is FD. Then (i_hyp, j_hyp) is not FD, (i.e. FD bias) +// (Case 1.c): Reads(i_hyp) and Reads(j_hyp) have no read shows (i_hyp, j_hyp) is FD. Then (i_hyp, j_hyp) is not FD. +// (Case 2): i_hyp has reads support but j_hyp doesn't, i.e., Reads(i_hyp) is non-empty but Reads(j_hyp) is empty +// (Case 2.a): Reads(i_hyp) has reads indicate (i_hyp, j_hyp) is FD. Then (i_hyp, j_hyp) is FD. +// (Case 2.b): Reads(i_hyp) has no read indicate (i_hyp, j_hyp) is FD. Then (i_hyp, j_hyp) is not FD. +// (Case 3): Both i_hyp and j_hyp have no read support. Then the FD between the two hypotheses is indefinite. +// Usually, if any of i_hyp and j_hyp is obtained from the best allele pair, then case 3 should not happen. +// @TODO: Use the transitivity of HP-INDEL to determine the FD of the no coverage alleles. +void EnsembleEval::FlowDisruptivenessInReadStackLevel(float min_ratio_for_fd) +{ + unsigned int num_hyp_not_null = allele_identity_vector.size() + 1; // reference + alternatives + global_flow_disruptive_matrix.assign(num_hyp_not_null, vector(num_hyp_not_null, -1)); // Starts with indefinite + float min_posterior_coverage = 0.9f; + // Aggregating too many super low responsibility may exceed min_posterior_coverage. Use min_resp_cutoff to guard against the problem. + float min_resp_cutoff = 0.1f; + + // postrior_coverage[i_hyp] = posterior coverage for i_hyp. + vector postrior_coverage(num_hyp_not_null, 0.0f); + // posterior_fd_counts[i_hyp][j_hyp][i_type] = posterior counts for Reads(i_hyp) that indicates the FD-type of (i_hyp, j_hyp) is i_type. + vector< vector< vector > > posterior_fd_type_counts(num_hyp_not_null, vector< vector >(num_hyp_not_null, {0.0f, 0.0f, 0.0f})); + + if (DEBUG > 0){ + cout << "- Determine Flow-Disruptiveness (FD) of the variant:" << endl + << " - FD codes: 0 = HP-INDEL, 1 = (not HP-INDEL and not flow-disruptive), 2 = flow-disruptive" << endl + << " - min_ratio_for_fd = "<< min_ratio_for_fd << endl; } - // set diploid in gq_pair - allele_eval.cur_state.cur_posterior.gq_pair.freq_pair = diploid_choice; - // scan gq_pair - allele_eval.cur_state.cur_posterior.gq_pair.DoPosteriorFrequencyScan(allele_eval.total_theory, - allele_eval.cur_state.cur_posterior.clustering, - true, ALL_STRAND_KEY, false, max_detail_level); - - //call-germline - int genotype_call; - allele_eval.CallGermline(local_min_allele_freq, genotype_call, gt_quality_score, reject_status_quality_score); - // set the outputs - // start at "het" by choice - genotype_component[0] = diploid_choice[0]; - genotype_component[1] = diploid_choice[1]; - if(genotype_call == 2){ // hom var - genotype_component[0] = diploid_choice[1]; + + + if (allele_eval.total_theory.GetIsMolecularTag()){ + for (vector::iterator fam_it = allele_eval.total_theory.my_eval_families.begin(); fam_it != allele_eval.total_theory.my_eval_families.end(); ++fam_it){ + if (not fam_it->GetFuncFromValid()){ + continue; + } + for (unsigned int i_hyp = 0; i_hyp < num_hyp_not_null; ++i_hyp){ + float resp = fam_it->family_responsibility[i_hyp + 1]; + if (resp < min_resp_cutoff){ + // I don't count low responsible hypotheses. + continue; + } + postrior_coverage[i_hyp] += resp; + for (unsigned int j_hyp = 0; j_hyp < num_hyp_not_null; ++j_hyp){ + // Note that local_flow_disruptiveness_matrix contains the outlier hypothesis + int fd_type = fam_it->GetFlowDisruptiveness(i_hyp + 1, j_hyp + 1); + if (fd_type >= 0){ + posterior_fd_type_counts[i_hyp][j_hyp][fd_type] += resp; + } + } + } + } + } + else{ + for (vector::iterator read_it = allele_eval.total_theory.my_hypotheses.begin(); read_it != allele_eval.total_theory.my_hypotheses.end(); ++read_it){ + if (not read_it->success){ + continue; + } + for (unsigned int i_hyp = 0; i_hyp < num_hyp_not_null; ++i_hyp){ + float resp = read_it->responsibility[i_hyp + 1]; + if (resp < min_resp_cutoff) + continue; + postrior_coverage[i_hyp] += resp; + for (unsigned int j_hyp = 0; j_hyp < num_hyp_not_null; ++j_hyp){ + // Note that local_flow_disruptiveness_matrix contains the outlier hypothesis + int fd_type = read_it->local_flow_disruptiveness_matrix[i_hyp + 1][j_hyp + 1]; + if (fd_type >= 0){ + posterior_fd_type_counts[i_hyp][j_hyp][fd_type] += resp; + } + } + } + } + } + + for (unsigned int i_hyp = 0; i_hyp < num_hyp_not_null; ++i_hyp){ + global_flow_disruptive_matrix[i_hyp][i_hyp] = 0; + for (unsigned int j_hyp = i_hyp + 1; j_hyp < num_hyp_not_null; ++j_hyp){ + // Is it Case 1? + if (postrior_coverage[i_hyp] >= min_posterior_coverage and postrior_coverage[j_hyp] >= min_posterior_coverage){ + // Check in the order of FD->(not FD and not HPINDEL)->(HPINDEL) + for (int type = 2; type >=0; --type){ + if ( posterior_fd_type_counts[i_hyp][j_hyp][type] / postrior_coverage[i_hyp] >= min_ratio_for_fd + and posterior_fd_type_counts[j_hyp][i_hyp][type] / postrior_coverage[j_hyp] >= min_ratio_for_fd){ + global_flow_disruptive_matrix[i_hyp][j_hyp] = type; + break; + } + } + } + // Is it Case 3? + else if (postrior_coverage[i_hyp] < min_posterior_coverage and postrior_coverage[j_hyp] < min_posterior_coverage){ + // global_flow_disruptive_matrix[j_hyp][i_hyp] = -1; // Indefinite, and it was set by default. + } + // Case 2: one hyp has coverage but the other one doesn't + else{ + int hyp_w_cov = i_hyp; + int hyp_wo_cov = j_hyp; + if (postrior_coverage[j_hyp] > postrior_coverage[i_hyp]){ + hyp_w_cov = j_hyp; + hyp_wo_cov = i_hyp; + } + // Check in the order of (HPINDEL)->(not FD and not HPINDEL)->FD + // I check HPINDEL first because I don't believe in those HP-INDEL reads that support the hyp w/ coverage. + for (int type = 0; type < 3; ++type){ + if ( posterior_fd_type_counts[hyp_w_cov][hyp_wo_cov][type] / postrior_coverage[hyp_w_cov] >= min_ratio_for_fd){ + global_flow_disruptive_matrix[i_hyp][j_hyp] = type; + break; + } + } + } + global_flow_disruptive_matrix[j_hyp][i_hyp] = global_flow_disruptive_matrix[i_hyp][j_hyp]; + } + } + + // Propagate the FD to downstream + variant->info["FDVR"].clear(); + for (unsigned int i_alt = 0; i_alt < allele_identity_vector.size(); ++i_alt){ + allele_identity_vector[i_alt].fd_level_vs_ref = global_flow_disruptive_matrix[0][i_alt + 1]; + /* + // I make an exception according to the reference context! + if (allele_identity_vector[i_alt].status.isHPIndel and global_flow_disruptive_matrix[0][i_alt + 1] != 0){ + if (DEBUG > 0){ + cout << " - Overwrite global_flow_disruptive_matrix[0][" << (i_alt + 1) << "] from " + << global_flow_disruptive_matrix[0][i_alt + 1] << " to 0 using the reference context." << endl; + } + allele_identity_vector[i_alt].fd_level_vs_ref = 0; + global_flow_disruptive_matrix[0][i_alt + 1] = 0; + } + */ + string fdvr = "-1"; + if (global_flow_disruptive_matrix[0][i_alt + 1] == 0){ + fdvr = "0"; + }else if (global_flow_disruptive_matrix[0][i_alt + 1] == 1){ + fdvr = "5"; + }else if (global_flow_disruptive_matrix[0][i_alt + 1] == 2){ + fdvr = "10"; + } + variant->info["FDVR"].push_back(fdvr); + } + + if (DEBUG > 0){ + cout << "- Determine Flow-Disruptiveness (FD) of the variant:" << endl + << " - FD codes: 0 = HP-INDEL, 1 = (not HP-INDEL and not flow-disruptive), 2 = flow-disruptive" << endl + << " - min_ratio_for_fd = "<< min_ratio_for_fd << endl; + for (unsigned int i_hyp = 0; i_hyp < posterior_fd_type_counts.size(); ++i_hyp){ + cout << " - For the reads supporting Allele "<< i_hyp << ": " << "posterior coverage = " << postrior_coverage[i_hyp] << endl; + for (unsigned int j_hyp = 0; j_hyp < posterior_fd_type_counts[i_hyp].size(); ++j_hyp){ + if (j_hyp == i_hyp){ + continue; // trivial case + } + cout << " - vs. Allele " << j_hyp << ": posterior coverage indexed by FD codes = ["; + for (unsigned int fd_idx = 0; fd_idx < posterior_fd_type_counts[i_hyp][j_hyp].size(); ++fd_idx){ + cout << posterior_fd_type_counts[i_hyp][j_hyp][fd_idx] << (fd_idx == posterior_fd_type_counts[i_hyp][j_hyp].size() - 1 ? "": ", "); + } + cout << "]" << endl; + } + } + cout << " - FD matrix ('-': trivial, '/': indefinite)" << endl; + for (unsigned int i_hyp = 0; i_hyp < global_flow_disruptive_matrix.size(); ++i_hyp){ + cout << (i_hyp == 0 ? " = [[" : " ["); + for (unsigned int j_hyp = 0; j_hyp < global_flow_disruptive_matrix[i_hyp].size(); ++j_hyp){ + if (i_hyp == j_hyp){ + cout << "-"; + } + else if (global_flow_disruptive_matrix[i_hyp][j_hyp] > -1){ + cout << global_flow_disruptive_matrix[i_hyp][j_hyp]; + } + else{ + cout << "/"; + } + cout << (j_hyp == global_flow_disruptive_matrix[i_hyp].size() - 1? "]": ", "); + } + cout << (i_hyp == global_flow_disruptive_matrix.size() -1 ? "]": ",") << endl; + } + cout<& variant_specific_params, + float& af_cutoff_rej, float& af_cutoff_gt) +{ + af_cutoff_rej = 1.0f; + // The old fashion scheme: choose the minimum one among all alleles + if (not my_controls.use_fd_param ){ + for (unsigned int allele_idx = 0; allele_idx < allele_identity_vector.size(); ++allele_idx){ + af_cutoff_rej = min(af_cutoff_rej, FreqThresholdByType(allele_identity_vector[allele_idx], my_controls, variant_specific_params[allele_idx])); + } + // The allele-freq-cutoff for calling GT is the same as the allele-freq-cutoff for Rej. + af_cutoff_gt = af_cutoff_rej; + return; + } + + // The new approach: + // af_cutoff_rej is the smallest min-allele-freq from the alleles in "diploid_choice" (aka best allele pair). + // The reason of doing this is because I am not going to call any allele other than diploid_choice. + // Therefore, it makes no sense that I use the min-allele-freq from the allele other than diploid_choice. + for (vector::iterator choice_it = diploid_choice.begin(); choice_it != diploid_choice.end(); ++choice_it){ + if (*choice_it == 0){ + // The reference allele is in best allele pair. + continue; + } + af_cutoff_rej = min(af_cutoff_rej, FreqThresholdByType(allele_identity_vector[*choice_it - 1], my_controls, variant_specific_params[*choice_it - 1])); + } + + // Get af_cutoff_gt + if (diploid_choice[0] == 0 or diploid_choice[1] == 0){ + af_cutoff_gt = af_cutoff_rej; + return; + } + + int fd_diploid_choice = global_flow_disruptive_matrix[diploid_choice[0]][diploid_choice[1]]; + + if (fd_diploid_choice == 2){ + af_cutoff_gt = my_controls.filter_fd_10.min_allele_freq; + } + else if (fd_diploid_choice == 1){ + af_cutoff_gt = my_controls.filter_fd_5.min_allele_freq; + } + else if (fd_diploid_choice == 0){ + af_cutoff_gt = my_controls.filter_fd_0.min_allele_freq; + } + else{ + // indefinite case + af_cutoff_gt = my_controls.filter_fd_0.min_allele_freq; + } +} + +void EnsembleEval::VariantFamilySizeHistogram(){ + const int m = 2; + // Basically, the bins will be (< (mode - m + 1)), .... mode - 1, mode, mode + 1, ..., (>(mode + m - 1)) + const int hist_x_num = 2 * m + 1; + // Make sure ApproximateHardClassification is done. + assert(alt_fam_indices_.size() == allele_identity_vector.size()); + for (unsigned int alt_idx = 0; alt_idx < alt_fam_indices_.size(); ++ alt_idx){ + if (alt_fam_indices_[alt_idx].empty()){ + // FAO = 0 means no histogram. + variant->info["VFSH"].push_back("."); + continue; + } + // Calculate the histogram + map fam_size_hist; + for (unsigned int idx = 0; idx < alt_fam_indices_[alt_idx].size(); ++ idx){ + int fam_size = allele_eval.total_theory.my_eval_families[alt_fam_indices_[alt_idx][idx]].GetValidFamSize(); + ++fam_size_hist[fam_size]; + } + + // Get the mode of the histogram + unsigned int mode_counts = fam_size_hist.begin()->second; + int mode_idx = 0; + int hist_len = 0; + for (map::iterator it = fam_size_hist.begin(); it != fam_size_hist.end(); ++it, ++hist_len){ + if (it->second > mode_counts){ + mode_idx = hist_len; + mode_counts = it->second; + } + } + + // See if I need to bin the begin, end of the histogram, respectively + int x_remaining = hist_x_num - 1; // minus the index mode + int x_start_cutoff = mode_idx; + int x_end_cutoff = mode_idx; + while (x_remaining > 0 and (not (x_start_cutoff == 0 and x_end_cutoff == hist_len - 1))){ + if (x_start_cutoff > 0){ + --x_remaining; + --x_start_cutoff; + } + if (x_end_cutoff < hist_len - 1){ + --x_remaining; + ++x_end_cutoff; + } + } + string hist_text = "("; + map::iterator it_start = fam_size_hist.begin(); + if (x_start_cutoff > 0){ + // bin the begin of the histogram + int fam_counts = 0; + for (int idx = 0; idx <= x_start_cutoff; ++idx, ++it_start){ + fam_counts += it_start->second; + } + --it_start; + hist_text += string("(<") + to_string(it_start->first + 1) + string(",") + to_string(fam_counts) +"),"; + ++it_start; + } + string last_hist_text = ""; + map::iterator it_end = fam_size_hist.end(); + if (x_end_cutoff < hist_len - 1){ + // bin the end of the histogram + int fam_counts = 0; + --it_end; + for (int idx = hist_len - 1; idx >= x_end_cutoff; --idx, --it_end){ + fam_counts += it_end->second; + } + ++it_end; + last_hist_text = string("(>") + to_string(it_end->first - 1) + string(",") + to_string(fam_counts) +"))"; + } + + // No need to bin the rest of the family size + for (map::iterator it = it_start; it != it_end; ++it){ + hist_text += string("(") + to_string(it->first) + string(",") + to_string(it->second) +"),"; + } + + if (last_hist_text.empty()){ + hist_text.back() = ')'; + } + else{ + hist_text += last_hist_text; + } + variant->info["VFSH"].push_back(hist_text); + } +} + + +// Check tag similarity if the molecular alternative coverage <= max_alt_cov and compute TGSM for each alternative allele. +// (Step 1): Determine pairwise family similarity +// I claim family A and family B are similar if +// a) Prefix A is partial similar to prefix B "and" suffix A is partial similar to suffix B (partial similar = allow 1 SNP + small HP-INDEL) +// or b) Prefix A is synchronized with prefix B or suffix A is synchronized with suffix B (synchronized = allow small HP-INDEL) +// The criterion a) is used to deal with PCR error occurs on tag. The criterion b) is used to against family cloning during sample prep. +// Note that pairwise family similarity has NO transitivity. +// (Step 2): Calculate TGSM +// I construct a graph by connecting the families using the relation defined by pairwise family similarity. +// TGSM = (number of families in the graph) - (number of isolated sub-graphs) +void EnsembleEval::CalculateTagSimilarity(const MolecularTagManager& mol_tag_manager, int max_alt_cov, int sample_idx) +{ + tag_similar_counts_.assign(allele_identity_vector.size(), 0); // This is TGSM in the vcf record. + unsigned int prefix_tag_len = mol_tag_manager.GetPrefixTagStruct(sample_idx).size(); + unsigned int suffix_tag_len = mol_tag_manager.GetSuffixTagStruct(sample_idx).size(); + + for (unsigned int allele_idx = 0; allele_idx < alt_fam_indices_.size(); ++allele_idx){ + unsigned int allele_fam_cov = alt_fam_indices_[allele_idx].size(); + if ((int) allele_fam_cov > max_alt_cov or allele_fam_cov == 0){ + continue; + } + // Step 1: Determine pairwise tag similarity + bool non_trivial_similar_tag_found = false; // Do I find any pairwise similar families? + vector > pairwise_tag_similar_matrix; + pairwise_tag_similar_matrix.assign(allele_fam_cov, vector(allele_fam_cov, false)); + for (unsigned int fam_idx_1 = 0; fam_idx_1 < allele_fam_cov; ++fam_idx_1){ + pairwise_tag_similar_matrix[fam_idx_1][fam_idx_1] = true; // pairwise tag similarity is a reflexive relation + const string& tag_1 = allele_eval.total_theory.my_eval_families[alt_fam_indices_[allele_idx][fam_idx_1]].family_barcode; + if (prefix_tag_len + suffix_tag_len != tag_1.size()){ + cerr << "ERROR: The molecular tag "<< tag_1 << " doesn't match the tag structure "<< mol_tag_manager.GetPrefixTagStruct(sample_idx) <<" + "< > subgraph_to_family; + int num_isolated_subgraphs = 0; + if (non_trivial_similar_tag_found){ + FindNodesInIsoSubGraph(pairwise_tag_similar_matrix, subgraph_to_family, true); + num_isolated_subgraphs = (int) subgraph_to_family.size(); + }else{ + num_isolated_subgraphs = (int) allele_fam_cov; + } + tag_similar_counts_[allele_idx] = (int) allele_fam_cov - num_isolated_subgraphs; + + if (DEBUG > 0){ + cout <<"+ Checking tag similarity of the "<< allele_fam_cov <<" families that support allele " << allele_idx + 1 << endl; + for (unsigned int fam_idx = 0; fam_idx < allele_fam_cov; ++fam_idx){ + const string& fam_barcode = allele_eval.total_theory.my_eval_families[alt_fam_indices_[allele_idx][fam_idx]].family_barcode; + cout << " - Family #" << alt_fam_indices_[allele_idx][fam_idx] << " \"" << fam_barcode.substr(0, prefix_tag_len) << "\" + \"" << fam_barcode.substr(prefix_tag_len) << "\" is similar to "; + for (unsigned int fam_idx_2 = 0; fam_idx_2 < allele_fam_cov; ++fam_idx_2){ + if (pairwise_tag_similar_matrix[fam_idx][fam_idx_2] and fam_idx != fam_idx_2){ + const string& sim_fam_barcode = allele_eval.total_theory.my_eval_families[alt_fam_indices_[allele_idx][fam_idx_2]].family_barcode; + cout << "Family #" << alt_fam_indices_[allele_idx][fam_idx_2] << " \"" << sim_fam_barcode.substr(0, prefix_tag_len) << "\" + \"" << sim_fam_barcode.substr(prefix_tag_len) << "\", "; + } + } + cout << endl; + } + if (tag_similar_counts_[allele_idx] > 0){ + cout << "+ Found "<< num_isolated_subgraphs << " isolated subgraph(s) of tag-similar families for allele "<< allele_idx + 1 << ": " << endl; + int group_idx = 0; + for (list >::iterator subgraph_it = subgraph_to_family.begin(); subgraph_it != subgraph_to_family.end(); ++subgraph_it, ++group_idx){ + cout << " - Subgraph "<< group_idx << " consists of " << subgraph_it->size() <<" families: "; + for (list::iterator fam_idx_it = subgraph_it->begin(); fam_idx_it != subgraph_it->end(); ++fam_idx_it){ + cout << "#"<< alt_fam_indices_[allele_idx][*fam_idx_it] << ", "; + } + cout << endl; + } + } + else{ + cout << "+ No tag-similar family found for allele " << allele_idx + 1 << endl; + } + cout << "+ Allele "<< allele_idx + 1 << " has TGSM = " << allele_fam_cov << " - " << num_isolated_subgraphs << " = " << tag_similar_counts_[allele_idx] << endl; + } + } + if (DEBUG > 0) { + cout << endl; + } +} + +bool CompareIntList(const list& list_1, const list& list_2){ + if (list_1.empty()){ + return false; } - else if(genotype_call == 0){ // hom ref - genotype_component[1] = diploid_choice[0]; + else if (list_2.empty()){ + return true; } + return (*(list_1.begin()) < *(list_2.begin())); } +// Consider a non-directed graph of N nodes where the connectivity between node i and j is determined by connectivity_matrix[i][j], i.e., true if connected. +// The function groups the nodes that are transitively connected. +// (Note): connectivity_matrix "must be" symmetric, i.e., connectivity_matrix[i][j] = connectivity_matrix[j][i] +// (Example): N = 5, connectivity_matrix[0][1] = 1, connectivity_matrix[1][2] = 1, connectivity_matrix[3][4] = 1, and all other upper triangle elements are 0. +// Then there are two isolated subgraphs 0-1-2 and 3-4 => subgraph_to_nodes = {{0, 1, 2}, {3, 4}}. +void FindNodesInIsoSubGraph(const vector >& connectivity_matrix, list >& subgraph_to_nodes, bool sort_by_index){ + int num_of_nodes = (int) connectivity_matrix.size(); + int num_nodes_in_subgraphs = 0; // just for safety check + list > dummy_list; + const list >::iterator null_it = dummy_list.begin(); + vector >::iterator> node_to_subgraph(num_of_nodes, null_it); // a look-up table to map from nodes to sub-graph + subgraph_to_nodes.resize(0); + + for (int node_idx_1 = 0; node_idx_1 < num_of_nodes; ++node_idx_1){ + if (node_to_subgraph[node_idx_1] == null_it){ + // This is the first time fam_idx_1 shows up. Create a new subgraph for it. + subgraph_to_nodes.push_back(list(1, node_idx_1)); + node_to_subgraph[node_idx_1] = --(subgraph_to_nodes.end()); + } + list >::iterator& master_subgraph = node_to_subgraph[node_idx_1]; + // Now let the nodes connected to node_idx_1 join the subgraph where node_idx_1 belongs to. + // No need to process node_idx_2 if node_idx_2 < node_idx_1, since it has been carried out previously. + for (int node_idx_2 = node_idx_1 + 1; node_idx_2 < num_of_nodes; ++node_idx_2){ + // Safety check: connectivity_matrix must be symmetric + assert(connectivity_matrix[node_idx_1][node_idx_2] == connectivity_matrix[node_idx_2][node_idx_1]); + if (not connectivity_matrix[node_idx_1][node_idx_2]){ + continue; + } + if (node_to_subgraph[node_idx_2] == null_it){ + node_to_subgraph[node_idx_2] = master_subgraph; + master_subgraph->push_back(node_idx_2); + }else if (node_to_subgraph[node_idx_2] != master_subgraph){ + // store the last node in master_subgraph before merging + list::iterator last_node_in_master_before_merge = --(master_subgraph->end()); + // Let all nodes connected to node_idx_2 merge into master_subgraph + master_subgraph->splice(master_subgraph->end(), *(node_to_subgraph[node_idx_2])); + // Destroy the original subgraph where node_idx_2 belongs to. + subgraph_to_nodes.erase(node_to_subgraph[node_idx_2]); + // Update node_to_subgraph for the nodes that are newly merged into master_subgraph + for (list::iterator node_it = ++last_node_in_master_before_merge; node_it != master_subgraph->end(); ++node_it){ + node_to_subgraph[*node_it] = master_subgraph; + } + } + } + } + + // Sort the nodes and sub-graphs + for (list >::iterator graph_it = subgraph_to_nodes.begin(); graph_it != subgraph_to_nodes.end(); ++graph_it){ + if (sort_by_index){ + graph_it->sort(); + } + num_nodes_in_subgraphs += (int) graph_it->size(); + } + if (sort_by_index){ + subgraph_to_nodes.sort(CompareIntList); + } + // Simple safety check. + assert(num_nodes_in_subgraphs == num_of_nodes + and (int) subgraph_to_nodes.size() <= num_of_nodes + and (subgraph_to_nodes.size() > 0 or num_of_nodes == 0)); +} -/* -// do molecular barcode classification -void HypothesisStack::DoMolecularTagClassification(MolecularTag &mol_tag){ - if(not total_theory.GetIsMolecularTag()){ - cout<<"Warning: Skip molecular tag classification since is_molecular_tag == false."<< endl; +string PrintVariant(const vcf::Variant& variant){ + string spacing = "\t"; + string vcf_variant = variant.sequenceName + spacing + + convertToString(variant.position) + spacing + + variant.id + spacing + + variant.ref + spacing; + for(unsigned int i_alt = 0; i_alt < variant.alt.size(); ++i_alt){ + vcf_variant += variant.alt[i_alt]; + if(i_alt != variant.alt.size() - 1){ + vcf_variant += ","; + } + } + return vcf_variant; +} + +class CompareAllelePositions{ +public: + CompareAllelePositions(vector const* allele_identity_vector = NULL){ + allele_identity_vector_ = allele_identity_vector;}; + ~CompareAllelePositions(){}; + bool operator()(int lhs_allele_idx, int rhs_allele_idx) const { + return allele_identity_vector_->at(lhs_allele_idx).start_variant_window < allele_identity_vector_->at(rhs_allele_idx).start_variant_window; + }; +private: + vector const* allele_identity_vector_ = NULL; +}; + +class CompareAlleleGroups{ +public: + CompareAlleleGroups(vector const* allele_identity_vector = NULL){ + allele_identity_vector_ = allele_identity_vector;}; + ~CompareAlleleGroups(){}; + bool operator()(const list& lhs_group, const list& rhs_group) const { + return allele_identity_vector_->at(*(lhs_group.begin())).start_variant_window < allele_identity_vector_->at(*(rhs_group.begin())).start_variant_window; + }; +private: + vector const* allele_identity_vector_; +}; + + +// Finalize the splitting: +// 1) Sort the alleles and groups +// 2) Further split the groups if the group size is too large. +// 2.a) Stage 1: break the connectivity between long Fake HS and others; break the connectivity between Fake HS and HP-INDEL +// 2.b) Stage 2: break the connectivity between all Fake HS and others +// 2.c) Stage 3: break all connectivity +// 2.d) Stage 4: repeat stage 3 again (in case the alleles from the big group move to a small group and let the small group too big.). +void FinalizeSplitting(vector >& allele_connectivity_matrix, const vector& allele_identity_vector, int max_group_size_allowed, list > &allele_groups){ + int debug = allele_identity_vector[0].DEBUG; + // Used to sort the groups + CompareAlleleGroups compare_groups(&allele_identity_vector); + // Used to sort the alleles in a group + CompareAllelePositions compare_positions(&allele_identity_vector); + unsigned int max_iteration = 4; + unsigned int num_iter = 0; + bool keep_itertate = true; + while (keep_itertate and num_iter < max_iteration){ + if (debug > 0){ + cout <<"+ Final variant splitting. Round "<< (num_iter + 1) << ":" << endl; + } + bool max_group_size_achieved = false; + bool connectivity_changed = false; + for (list >::iterator group_it = allele_groups.begin(); group_it != allele_groups.end(); ++group_it){ + // See if I have a group that contains too many alleles. + if ((int) group_it->size() > max_group_size_allowed){ + max_group_size_achieved = true; + if (debug > 0){ + cout << " - The group "<< PrintIteratorToString(group_it->begin(), group_it->end(), "{", "}", ", ", "alt") + << " contains "<< group_it->size() << " alleles (> max_alt_num = " << max_group_size_allowed <<"): splitting needed." << endl; + } + // I let the alleles in this group be isolated unless the other allele has the same start position. + for (list::iterator idx_it = group_it->begin(); idx_it != group_it->end(); ++idx_it){ + // At the first two rounds (num_iter < 2), I only want to break Fake HS. + if (num_iter < 2 and allele_identity_vector[*idx_it].status.isFakeHsAllele){ + for (unsigned int idx = 0; idx < allele_connectivity_matrix.size(); ++idx){ + if (allele_connectivity_matrix[*idx_it][idx] and allele_identity_vector[*idx_it].start_variant_window != allele_identity_vector[idx].start_variant_window){ + if ( (num_iter == 0 and (allele_identity_vector[idx].status.isHPIndel or allele_identity_vector[*idx_it].ref_length > 10)) + or (num_iter == 1)){ + allele_connectivity_matrix[*idx_it][idx] = false; + allele_connectivity_matrix[idx][*idx_it] = false; + connectivity_changed = true; + if (debug > 0){ + cout << " - Breaking the connectivity between alt"<< *idx_it << ": "<< allele_identity_vector[*idx_it].altAllele << "@[" << allele_identity_vector[*idx_it].position0 << ", " << allele_identity_vector[*idx_it].position0 + allele_identity_vector[*idx_it].ref_length + << ") and alt" << idx << ": " << allele_identity_vector[idx].altAllele << "@[" << allele_identity_vector[idx].position0 << ", " << allele_identity_vector[idx].position0 + allele_identity_vector[idx].ref_length << ")." << endl; + } + } + } + } + } + // Then, I will break all alleles in the group. + else if (num_iter >= 2){ + for (unsigned int idx = 0; idx < allele_connectivity_matrix.size(); ++idx){ + if (allele_connectivity_matrix[*idx_it][idx] and (allele_identity_vector[*idx_it].start_variant_window != allele_identity_vector[idx].start_variant_window)){ + allele_connectivity_matrix[*idx_it][idx] = false; + allele_connectivity_matrix[idx][*idx_it] = false; + connectivity_changed = true; + if (debug > 0){ + cout << " - Breaking the connectivity between alt"<< *idx_it << ": "<< allele_identity_vector[*idx_it].altAllele << "@[" << allele_identity_vector[*idx_it].position0 << ", " << allele_identity_vector[*idx_it].position0 + allele_identity_vector[*idx_it].ref_length + << ") and alt" << idx << ": " << allele_identity_vector[idx].altAllele << "@[" << allele_identity_vector[idx].position0 << ", " << allele_identity_vector[idx].position0 + allele_identity_vector[idx].ref_length << "), "<< endl; + } + } + } + } + } + } + } + + if (max_group_size_achieved){ + // Split the variant using the modified allele_connectivity_matrix. + if (connectivity_changed){ + FindNodesInIsoSubGraph(allele_connectivity_matrix, allele_groups, false); + } + }else{ + keep_itertate = false; + if (debug > 0){ + cout <<" - No splitting needed." << endl; + } + } + ++num_iter; + } + // Must sort the alleles in each group first (because compare_groups will use the pos of the first allele for sorting). + for (list >::iterator group_it = allele_groups.begin(); group_it != allele_groups.end(); ++group_it){ + group_it->sort(compare_positions); + } + // Then sort the groups of alleles. + allele_groups.sort(compare_groups); +} + + +// This function helps the candidate to split the multi-allele variant into smaller variants +void SplitAlleleIdentityVector(const vector& allele_identity_vector, list >& allele_groups, const ReferenceReader &ref_reader, int max_group_size_allowed) +{ + int num_alt = (int) allele_identity_vector.size(); + map, LocalReferenceContext> contex_dict; + vector > allele_connectivity_matrix(num_alt, vector(num_alt)); + + if (num_alt == 1){ + // Don't waste my time on the trivial splitting. + allele_groups = {{0}}; return; } - // Do outlier pre-filtering because we will assume no functional family is an outlier. - float data_reliability = my_params.DataReliability(); - total_theory.OutlierFiltering(data_reliability); - // Do classification - total_theory.MolecularTagClassifier(mol_tag); + // (Step 1): Get the padding removed allele_identity_vector + // Note that allele connectivity should be determined using the "padding removed version" of the alt alleles. + // In particular, I need the splicing window of padding removed allele. + vector padding_removed_allele_identity_vector; + padding_removed_allele_identity_vector.resize(num_alt); + for (int i_alt = 0; i_alt < num_alt; ++i_alt){ + int num_padding = allele_identity_vector[i_alt].num_padding_added.first + allele_identity_vector[i_alt].num_padding_added.second; + if (num_padding == 0 or allele_identity_vector[i_alt].status.isProblematicAllele){ + // Usually, calling this function means that there are padding bases added. Copying the original allele_identity would be rare. + padding_removed_allele_identity_vector[i_alt] = allele_identity_vector[i_alt]; + continue; + } + padding_removed_allele_identity_vector[i_alt].DEBUG = allele_identity_vector[i_alt].DEBUG; + pair< map, LocalReferenceContext>::iterator, bool> context_finder; + // context_finder.first->second is the context for the padding pair allele_identity_vector[i_alt].num_padding_added. + context_finder = contex_dict.insert(pair, LocalReferenceContext> (allele_identity_vector[i_alt].num_padding_added, LocalReferenceContext())); + // context_finder.second indicates did I see this context before? + if (context_finder.second){ + // Didn't see this context before. Need to detect context. + context_finder.first->second.DetectContextAtPosition(ref_reader, + allele_identity_vector[i_alt].chr_idx, allele_identity_vector[i_alt].start_variant_window, + allele_identity_vector[i_alt].ref_length - num_padding); + } + if (not context_finder.first->second.context_detected){ + padding_removed_allele_identity_vector[i_alt].status.isProblematicAllele = true; + continue; + } + padding_removed_allele_identity_vector[i_alt].altAllele = allele_identity_vector[i_alt].altAllele.substr(allele_identity_vector[i_alt].num_padding_added.first, (int) allele_identity_vector[i_alt].altAllele.size() - num_padding); + padding_removed_allele_identity_vector[i_alt].position0 = allele_identity_vector[i_alt].start_variant_window; + padding_removed_allele_identity_vector[i_alt].ref_length = allele_identity_vector[i_alt].ref_length - num_padding; + padding_removed_allele_identity_vector[i_alt].chr_idx = allele_identity_vector[i_alt].chr_idx; + padding_removed_allele_identity_vector[i_alt].status.isFakeHsAllele = allele_identity_vector[i_alt].status.isFakeHsAllele; + if (not padding_removed_allele_identity_vector[i_alt].CharacterizeVariantStatus(context_finder.first->second, ref_reader)){ + padding_removed_allele_identity_vector[i_alt].status.isProblematicAllele = true; + continue; + } + padding_removed_allele_identity_vector[i_alt].CalculateWindowForVariant(context_finder.first->second, ref_reader); + } + + // (Step 2): Determine allele connectivity + for (int i_alt = 0; i_alt < num_alt; ++i_alt){ + allele_connectivity_matrix[i_alt][i_alt] = true; + for (int j_alt = i_alt + 1; j_alt < num_alt; ++j_alt){ + allele_connectivity_matrix[i_alt][j_alt] = IsAllelePairConnected(padding_removed_allele_identity_vector[i_alt], padding_removed_allele_identity_vector[j_alt]); + allele_connectivity_matrix[j_alt][i_alt] = allele_connectivity_matrix[i_alt][j_alt]; + } + } + + // (Step 3): Split the alleles into groups + FindNodesInIsoSubGraph(allele_connectivity_matrix, allele_groups, false); + + // (Step 4): Finalize the groups and alleles: + // (4.a) Sort the alleles and groups + // (4.b) Further split the groups that contain too many alleles. + FinalizeSplitting(allele_connectivity_matrix, padding_removed_allele_identity_vector, max_group_size_allowed, allele_groups); +} + +// Given variant candidates, calculate the end of the look ahead window for candidate generator, +// where look ahead window = [variant_window_end, look_ahead_end). +// I.e., the candidate generator should make sure that there is NO other variant till the (0-based) position at (look_ahead_end_0 - 1), while a variant @ look_ahead_end_0 is fine. +int EnsembleEval::CalculateLookAheadEnd0(const ReferenceReader &ref_reader){ + int chr_size = ref_reader.chr_size(seq_context.chr_idx); // The size of the chromosome. + // (Step 1): At least lookahead to the end of the splicing window to make sure there is no splicing hazard of the current variant interfered the variant at the future position. + int look_ahead_end_0 = multiallele_window_end; + // Can't lookahead beyond the chromosome! + if (look_ahead_end_0 >= chr_size) + return chr_size; + + // (Step 2): Keep looking ahead until this variant will not interfere any variant at the future position. + // I.e., the splicing lower bound at look_ahead_end_0 should >= variant_window_end. + // Note that all windows defined here are left-closed, right-open. + LocalReferenceContext future_context; // The context at the future position + bool keep_lookahead = true; + // I assume that Freebayes doesn't add right anchors to an alt allele for no reason. There must be another alt allele pushes the variant window to the right. (Of course it is not true for HS allele). + // If the assumption doesn't hold, then the look ahead will be more conservative, not hurt. + const int variant_window_end = (int) seq_context.position0 + (int) seq_context.reference_allele.size(); + int splicing_lower_bound_at_look_ahead_end = -1; + bool keep_look_ahead = true; + + if (DEBUG){ + cout << "+ Investigating lookahead window (0-based) for the variant (1-based) (" << PrintVariant(*variant) <<"): "<< endl + << " - Current splicing window end = " << multiallele_window_end << ", which is the lower bound of lookahead window end." << endl + << " - Current variant window end = " << variant_window_end << endl + << " + Finding the smallest future position that won't be interfered by this variant (i.e., splicing lower bound@pos >= current variant window end):" << endl; + } + + while (keep_look_ahead){ + future_context.DetectContextAtPosition(ref_reader, seq_context.chr_idx, look_ahead_end_0, 1); + // Calculate the splicing lower bound at look_ahead_end_0 + splicing_lower_bound_at_look_ahead_end = future_context.SplicingLeftBound(ref_reader); + + if (DEBUG){ + cout << " - pos = " << look_ahead_end_0 << ", Splicing lower bound@pos = " << splicing_lower_bound_at_look_ahead_end << endl; + } + + if (splicing_lower_bound_at_look_ahead_end >= variant_window_end) { + keep_look_ahead = false; + } + else{ + keep_look_ahead = (++look_ahead_end_0 < chr_size); + } + } + + if (DEBUG){ + cout << " - Lookahead window end = " << look_ahead_end_0 + << ", splicing lower bound @(lookahead end) = " << splicing_lower_bound_at_look_ahead_end << endl; + } + return look_ahead_end_0; +} + +// Given the candidate alleles, determine the maximally possible split of the variant (or group of the alternative alleles) that can be correctly (i.e., w/o high FXX) evaluated by the evaluator. +// e.g. output: allele_groups = {{0,1,2}, {3, 4}, {5}}. Then alt[0], alt[1], alt[2] must be evaluated jointly; alt[3], alt[4] must be evaluated jointly; alt[5] can be evaluated individually. +void EnsembleEval::SplitMyAlleleIdentityVector(list >& allele_groups, const ReferenceReader &ref_reader, int max_group_size_allowed){ + SplitAlleleIdentityVector(allele_identity_vector, allele_groups, ref_reader, max_group_size_allowed); + + if (DEBUG){ + cout << "+ Investigating variant splitting for the variant (" << PrintVariant(*variant) <<"): "<< endl + << " - There are " << allele_groups.size() << " groups of alternative alleles identified." << endl; + int group_idx = 0; + for (list >::iterator g_it = allele_groups.begin(); g_it != allele_groups.end(); ++g_it, ++group_idx){ + cout << " - Group #" << group_idx << " consists of "<< g_it->size() << " alternative alleles: " + << PrintIteratorToString(g_it->begin(), g_it->end(), "{", "}", ", ", "alt ") << endl; + } + } +} + +void EnsembleEval::SetupAllAlleles(const ExtendParameters ¶meters, + const InputStructures &global_context, + const ReferenceReader &ref_reader) +{ + seq_context.DetectContext(*variant, global_context.DEBUG, ref_reader); + allele_identity_vector.resize(variant->alt.size()); + + if (global_context.DEBUG > 0 and variant->alt.size() > 0) { + cout << "Investigating variant candidate " << seq_context.reference_allele << " -> " << variant->alt[0]; + for (unsigned int i_allele = 1; i_allele < allele_identity_vector.size(); i_allele++) + cout << ',' << variant->alt[i_allele]; + cout << endl; + } + + // Make sure the vectors are initialized. + assert(variant->alt.size() == variant->alt_orig_padding.size()); + assert(variant->alt.size() == variant->isAltHotspot.size()); + assert(variant->alt.size() == variant->isAltFakeHotspot.size()); + + //now calculate the allele type (SNP/Indel/MNV/HPIndel etc.) and window for hypothesis calculation for each alt allele. + for (unsigned int i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { + allele_identity_vector[i_allele].status.isHotSpot = variant->isHotSpot; + allele_identity_vector[i_allele].status.isHotSpotAllele = variant->isAltHotspot[i_allele]; + allele_identity_vector[i_allele].status.isFakeHsAllele = variant->isAltFakeHotspot[i_allele]; + allele_identity_vector[i_allele].filterReasons.clear(); + allele_identity_vector[i_allele].DEBUG = global_context.DEBUG; + + allele_identity_vector[i_allele].indelActAsHPIndel = parameters.my_controls.filter_variant.indel_as_hpindel; + allele_identity_vector[i_allele].getVariantType(variant->alt[i_allele], seq_context, + global_context.ErrorMotifs, parameters.my_controls.filter_variant, ref_reader, variant->alt_orig_padding[i_allele]); + allele_identity_vector[i_allele].CalculateWindowForVariant(seq_context, ref_reader); + } + + //GetMultiAlleleVariantWindow(); + multiallele_window_start = -1; + multiallele_window_end = -1; + + + // Mark Ensemble for realignment if any of the possible variants should be realigned + // TODO: Should we exclude already filtered alleles? + for (unsigned int i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { + //if (!allele_identity_vector[i_allele].status.isNoCallVariant) { + if (allele_identity_vector[i_allele].start_splicing_window < multiallele_window_start or multiallele_window_start == -1) + multiallele_window_start = allele_identity_vector[i_allele].start_splicing_window; + if (allele_identity_vector[i_allele].end_splicing_window > multiallele_window_end or multiallele_window_end == -1) + multiallele_window_end = allele_identity_vector[i_allele].end_splicing_window; + + if (allele_identity_vector[i_allele].ActAsSNP() && parameters.my_controls.filter_variant.do_snp_realignment) { + doRealignment = doRealignment or allele_identity_vector[i_allele].status.doRealignment; + } + if (allele_identity_vector[i_allele].ActAsMNP() && parameters.my_controls.filter_variant.do_mnp_realignment) { + doRealignment = doRealignment or allele_identity_vector[i_allele].status.doRealignment; + } + } + // pass allele windows back down the object + for (unsigned int i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { + allele_identity_vector[i_allele].multiallele_window_start = multiallele_window_start; + allele_identity_vector[i_allele].multiallele_window_end = multiallele_window_end; + } + + + if (global_context.DEBUG > 0) { + cout << "Realignment for this candidate is turned " << (doRealignment ? "on" : "off") << endl; + cout << "Final window for multi-allele: " << ": (" << multiallele_window_start << ") "; + for (int p_idx = multiallele_window_start; p_idx < multiallele_window_end; p_idx++) + cout << ref_reader.base(seq_context.chr_idx,p_idx); + cout << " (" << multiallele_window_end << ") " << endl; + } +} + + +void EnsembleEval::SpliceAllelesIntoReads(PersistingThreadObjects &thread_objects, const InputStructures &global_context, + const ExtendParameters ¶meters, const ReferenceReader &ref_reader) +{ + bool changed_alignment; + unsigned int num_valid_reads = 0; + unsigned int num_realigned = 0; + int num_hyp_no_null = allele_identity_vector.size()+1; // num alleles +1 for ref + + // generate null+ref+nr.alt hypotheses per read in the case of do_multiallele_eval + allele_eval.total_theory.my_hypotheses.resize(read_stack.size()); + + for (unsigned int i_read = 0; i_read < allele_eval.total_theory.my_hypotheses.size(); i_read++) { + // --- New splicing function --- + allele_eval.total_theory.my_hypotheses[i_read].success = + SpliceVariantHypotheses(*read_stack[i_read], + *this, + seq_context, + thread_objects, + allele_eval.total_theory.my_hypotheses[i_read].splice_start_flow, + allele_eval.total_theory.my_hypotheses[i_read].splice_end_flow, + allele_eval.total_theory.my_hypotheses[i_read].instance_of_read_by_state, + allele_eval.total_theory.my_hypotheses[i_read].same_as_null_hypothesis, + changed_alignment, + global_context, + ref_reader); + + if (allele_eval.total_theory.my_hypotheses[i_read].success){ + num_valid_reads++; + if (changed_alignment) + num_realigned++; + } + + // if we need to compare likelihoods across multiple possibilities + if (num_hyp_no_null > 2) + allele_eval.total_theory.my_hypotheses[i_read].use_correlated_likelihood = false; + } + + // Check how many reads had their alignment modified + std::ostringstream my_info; + my_info.precision(4); + if (doRealignment and num_valid_reads>0){ + float frac_realigned = (float)num_realigned / (float)num_valid_reads; + // And re-do splicing without realignment if we exceed the threshold + if (frac_realigned > parameters.my_controls.filter_variant.realignment_threshold){ + my_info << "SKIPREALIGNx" << frac_realigned; + doRealignment = false; + for (unsigned int i_read = 0; i_read < allele_eval.total_theory.my_hypotheses.size(); i_read++) { + allele_eval.total_theory.my_hypotheses[i_read].success = + SpliceVariantHypotheses(*read_stack[i_read], + *this, + seq_context, + thread_objects, + allele_eval.total_theory.my_hypotheses[i_read].splice_start_flow, + allele_eval.total_theory.my_hypotheses[i_read].splice_end_flow, + allele_eval.total_theory.my_hypotheses[i_read].instance_of_read_by_state, + allele_eval.total_theory.my_hypotheses[i_read].same_as_null_hypothesis, + changed_alignment, + global_context, + ref_reader); + } + } + else { + my_info << "REALIGNEDx" << frac_realigned; + } + info_fields.push_back(my_info.str()); + } + +} + +// Read and process records appropriate for this variant; positions are zero based +void EnsembleEval::StackUpOneVariant(const ExtendParameters ¶meters, const PositionInProgress& bam_position, int sample_index) +{ + + // Initialize random number generator for each stack -> ensure reproducibility + RandSchrange RandGen(parameters.my_controls.RandSeed); + + read_stack.clear(); // reset the stack + read_stack.reserve(parameters.my_controls.downSampleCoverage); + int read_counter = 0; + + for (Alignment* rai = bam_position.begin; rai != bam_position.end; rai = rai->next) { + + // Check global conditions to stop reading in more alignments + if (rai->original_position > multiallele_window_start) + break; + + // filter reads belonging to other samples + if (rai->sample_index != sample_index) + continue; + + if (rai->alignment.Position > multiallele_window_start) + continue; + + if (rai->filtered) + continue; + + if (rai->alignment.GetEndPosition() < multiallele_window_end) + continue; + + // Reservoir Sampling + if (read_stack.size() < (unsigned int)parameters.my_controls.downSampleCoverage) { + read_counter++; + read_stack.push_back(rai); + } else { + read_counter++; + // produces a uniformly distributed test_position between [0, read_counter-1] + unsigned int test_position = ((double)RandGen.Rand() / ((double)RandGen.RandMax + 1.0)) * (double)read_counter; + if (test_position < (unsigned int)parameters.my_controls.downSampleCoverage) + read_stack[test_position] = rai; + } + } +} + +// The class is simply used for random_shuffle +class MyRandSchrange : private RandSchrange{ +public: + MyRandSchrange(int seed = 1) {SetSeed(seed);} ; + int operator()(int upper_lim) {return Rand() % upper_lim;}; // return a random number between 0 and upper_lim-1 +}; + + +// Contains the information I need for downsampling with mol tagging +struct FamInfoForDownSample +{ + MolecularFamily* ptr_fam; // The pointer of the molecular family. + unsigned int num_reads_remaining; // How many reads that are not picked up after down sampling? + FamInfoForDownSample(MolecularFamily* const fam){ + ptr_fam = fam; + num_reads_remaining = ptr_fam->valid_family_members.size(); // Initially, none of the reads is picked up. + }; + bool operator<(const FamInfoForDownSample &rhs) const { return num_reads_remaining > rhs.num_reads_remaining; }; +}; + +// Compare two func families for sorting. +// The family has a read with larger read count wins. +// valid_family_members_sorted must be sorted! +bool CompareFuncFamilies(const FamInfoForDownSample& fam_0, const FamInfoForDownSample& fam_1) +{ + if (fam_0.ptr_fam->valid_family_members[0]->read_count == 1 and fam_1.ptr_fam->valid_family_members[0]->read_count == 1){ + return fam_0.ptr_fam->valid_family_members.size() > fam_1.ptr_fam->valid_family_members.size(); + } + + unsigned int min_size = min(fam_0.ptr_fam->valid_family_members.size(), fam_1.ptr_fam->valid_family_members.size()); + for (unsigned int i_read = 0; i_read < min_size; ++i_read){ + if (fam_0.ptr_fam->valid_family_members[i_read]->read_count > fam_1.ptr_fam->valid_family_members[i_read]->read_count){ + return true; + } + else if (fam_0.ptr_fam->valid_family_members[i_read]->read_count < fam_1.ptr_fam->valid_family_members[i_read]->read_count){ + return false; + } + } + return fam_0.ptr_fam->GetValidFamSize() > fam_1.ptr_fam->GetValidFamSize(); +} + +// I apply a strategic downsampling algorithm for molecular tagging using the following rules. +// Rule 0: Only reads in functional families will be evaluated. +// Rule 1: Get as many functional families as possible after down sampling +// Rule 2: If no consensus read, prefer to pick up "rich" families. +// Rule 3: For consensus reads, maximize the family size after downsampling. +// I only pick up the reads from my_molecular_families[strand_key] +// num_reads_available: total number of reads in the functional families (from valid_family_members) on the strand specified by strand_key +// num_func_fam: total number of functional families (from valid_family_members) on the strand specified by strand_key +void EnsembleEval::DoDownSamplingMolTag(const ExtendParameters ¶meters, unsigned int effective_min_fam_size, vector< vector > &my_molecular_families, + unsigned int num_reads_available, unsigned int num_func_fam, int strand_key) +{ + MyRandSchrange my_rand_schrange(parameters.my_controls.RandSeed); // The random number generator that we use to guarantee reproducibility. + unsigned int read_counter = 0; // Number of reads on read stack + unsigned int downSampleCoverage = (unsigned int) parameters.my_controls.downSampleCoverage; + + read_stack.clear(); // reset the stack + allele_eval.total_theory.my_eval_families.clear(); + + // (Case 1): I can keep all the reads in all functional families :D + if (num_reads_available <= downSampleCoverage){ + allele_eval.total_theory.my_eval_families.reserve(num_func_fam); + read_stack.reserve(num_reads_available); + + for (vector::iterator family_it = my_molecular_families[strand_key].begin(); + family_it != my_molecular_families[strand_key].end(); ++family_it){ + if (family_it->SetFuncFromValid(effective_min_fam_size)){ + allele_eval.total_theory.my_eval_families.push_back(EvalFamily(family_it->family_barcode, family_it->strand_key, &read_stack)); + for (vector::iterator read_it = family_it->valid_family_members.begin(); read_it != family_it->valid_family_members.end(); ++read_it){ + read_stack.push_back(*read_it); + allele_eval.total_theory.my_eval_families.back().AddNewMember(read_counter); + ++read_counter; + } + } + } + + if (DEBUG > 0){ + cout << endl <<"+ Down sample for molecular tags: "<< endl; + cout << " - Down sample " << num_reads_available << " reads to " << downSampleCoverage << ". "; + cout << "Number of families after down sampling = "<< num_func_fam << "." << endl; + cout << " - Total reads after down sampling = " << read_counter << endl; + } + return; + } + + // (Case 2): I can't preserve all reads but I can preserve all functional families. + // Step 1: Find and sort all available func families + unsigned int reads_remaining = downSampleCoverage; + unsigned int num_of_func_fam_after_down_sampling = 0; + vector func_families; + func_families.reserve(num_func_fam); + for (vector::iterator family_it = my_molecular_families[strand_key].begin(); + family_it != my_molecular_families[strand_key].end(); ++family_it){ + if (family_it->SetFuncFromValid(effective_min_fam_size)){ + func_families.push_back(FamInfoForDownSample(&(*family_it))); + // Always make sure valid_family_members is sorted + if (not family_it->is_valid_family_members_sorted){ + family_it->SortValidFamilyMembers(); + } + } + } + // Random shuffle func_families to get randomness for the tie situation during sorting. + random_shuffle(func_families.begin(), func_families.end(), my_rand_schrange); + // The most important step. Sort func_families according to CompareFuncFamilies + sort(func_families.begin(), func_families.end(), CompareFuncFamilies); + + // Step 2: + // In each family, pick valid_family_members[0] if the read can make the family functional. I call such a read "super read". + // Again, valid_family_members must be sorted, so is func_families. + vector::iterator first_poor_fam_it = func_families.end(); // A family is "poor" if it doesn't have such a "super read". first_poor_fam_it is the first poor family. + for (vector::iterator func_fam_it = func_families.begin(); func_fam_it != func_families.end(); ++func_fam_it){ + if (reads_remaining == 0){ + break; + } + if (func_fam_it->ptr_fam->valid_family_members[0]->read_count >= (int) effective_min_fam_size){ + ++num_of_func_fam_after_down_sampling; + --(func_fam_it->num_reads_remaining); + --reads_remaining; + } + else{ + first_poor_fam_it = func_fam_it; + break; + } + } + + // Step 3: + // Try to let as many poor families be functional as possible, if I still have reads remaining. + vector::iterator last_poor_fam = func_families.end(); + if (reads_remaining > 0){ + for (vector::iterator func_fam_it = first_poor_fam_it; func_fam_it != func_families.end(); ++func_fam_it){ + if (reads_remaining < effective_min_fam_size){ // The simple criterion is not the best implementation because I may lose some func fam. But it should be acceptable. + last_poor_fam = func_fam_it; + break; + } + int down_sampled_fam_size = 0; + for (vector::iterator read_it = func_fam_it->ptr_fam->valid_family_members.begin(); read_it != func_fam_it->ptr_fam->valid_family_members.end(); ++read_it){ + if (reads_remaining == 0 or down_sampled_fam_size >= (int) effective_min_fam_size){ + break; + } + down_sampled_fam_size += ((*read_it)->read_count); + --(func_fam_it->num_reads_remaining); + --reads_remaining; + } + if (down_sampled_fam_size >= (int) effective_min_fam_size){ + ++num_of_func_fam_after_down_sampling; + } + else{ // shouldn't happen + last_poor_fam = func_fam_it; + break; + } + } + } + + // Step 4: + // Pick one read in one family until I don't have read remaining. + // This step should not affect the number of functional families after down sampling. + if (reads_remaining > 0){ + sort(func_families.begin(), last_poor_fam); // Sort by the num_reads_remaining (more num_reads_remaining first). Otherwise it can be super slow in the worst case. + } + while (reads_remaining > 0 and func_families[0].num_reads_remaining > 0){ // Although the criterion (func_families[0].num_reads_remaining > 0) should be redundant, it's always safe to check this here. + for (vector::iterator func_fam_it = func_families.begin(); func_fam_it != last_poor_fam; ++func_fam_it){ + if (reads_remaining == 0 or func_fam_it->num_reads_remaining == 0){ + break; + } + if (func_fam_it->num_reads_remaining > 0){ + --(func_fam_it->num_reads_remaining); + --reads_remaining; + } + } + } + + // Step 5: + // Fill the reads into read_stack + allele_eval.total_theory.my_eval_families.reserve(num_of_func_fam_after_down_sampling); + read_stack.reserve(downSampleCoverage); + + for (vector::iterator func_fam_it = func_families.begin(); func_fam_it != func_families.end(); ++func_fam_it){ + int num_reads_picked = (int) (func_fam_it->ptr_fam->valid_family_members.size()) - func_fam_it->num_reads_remaining; + if (num_reads_picked == 0){ + continue; + } + allele_eval.total_theory.my_eval_families.push_back(EvalFamily(func_fam_it->ptr_fam->family_barcode, func_fam_it->ptr_fam->strand_key, &read_stack)); + allele_eval.total_theory.my_eval_families.back().all_family_members.reserve(num_reads_picked); + + // For fairness, I need to randomly pick up the reads with the same read_count if needed. + if (func_fam_it->num_reads_remaining > 0){ + vector::iterator first_read_it_with_smallest_read_count; + vector::iterator next_read_it_not_with_smallest_read_count; + + if (func_fam_it->ptr_fam->valid_family_members[0]->read_count == func_fam_it->ptr_fam->valid_family_members.back()->read_count){ + // All reads have equal read_count. Of course I need to randomly pick up num_reads_picked reads. + first_read_it_with_smallest_read_count = func_fam_it->ptr_fam->valid_family_members.begin(); + next_read_it_not_with_smallest_read_count = func_fam_it->ptr_fam->valid_family_members.end(); + } + else{ + // The reads in the family have variable read_count. + // If more than one read have smallest_read_count, I need to random shuffle these reads and then do down sampling. + vector::iterator read_it_with_smallest_read_count = func_fam_it->ptr_fam->valid_family_members.begin() + (num_reads_picked - 1); + int smallest_read_count = (*read_it_with_smallest_read_count)->read_count; + // Find the first read after read_it_with_smallest_read_count whose read_count != smallest_read_count + for (next_read_it_not_with_smallest_read_count = read_it_with_smallest_read_count + 1; next_read_it_not_with_smallest_read_count != func_fam_it->ptr_fam->valid_family_members.end(); ++next_read_it_not_with_smallest_read_count){ + if ((*next_read_it_not_with_smallest_read_count)->read_count != smallest_read_count) + break; + } + // Find the last read before read_it_with_smallest_read_count whose read_count != smallest_read_count + if ( (*(func_fam_it->ptr_fam->valid_family_members.begin()))->read_count == smallest_read_count){ + first_read_it_with_smallest_read_count = func_fam_it->ptr_fam->valid_family_members.begin(); + } + else{ + for (first_read_it_with_smallest_read_count = read_it_with_smallest_read_count - 1; first_read_it_with_smallest_read_count != func_fam_it->ptr_fam->valid_family_members.begin(); --first_read_it_with_smallest_read_count){ + if ((*first_read_it_with_smallest_read_count)->read_count != smallest_read_count){ + break; + } + } + ++first_read_it_with_smallest_read_count; + } + } + if (first_read_it_with_smallest_read_count != (next_read_it_not_with_smallest_read_count - 1)){ + // Partially random shuffle the reads in the family. + random_shuffle(first_read_it_with_smallest_read_count, next_read_it_not_with_smallest_read_count, my_rand_schrange); + } + } + // Pickup the first num_reads_picked reads from valid_family_members. + for (int i_read = 0; i_read < num_reads_picked; ++i_read){ + // Add the read into read_stack + read_stack.push_back(func_fam_it->ptr_fam->valid_family_members[i_read]); + // Add the read in to the family + allele_eval.total_theory.my_eval_families.back().AddNewMember(read_counter); + ++read_counter; + } + } + + if (DEBUG > 0){ + cout << "+ Down sample with molecular tagging: "<< endl; + cout << " - Down sample " << num_reads_available << " reads to " << downSampleCoverage << ". " << endl; + cout << " - Number of functional families after down sampling = "<< num_of_func_fam_after_down_sampling<< "." << endl; + cout << " - Total reads after down sampling = " << read_counter << endl; + } +} + +// Currently only take the reads on one strand +void EnsembleEval::StackUpOneVariantMolTag(const ExtendParameters ¶meters, vector< vector > &my_molecular_families, int sample_index) +{ + int strand_key = -1; + unsigned int effective_min_fam_size = allele_eval.total_theory.effective_min_family_size; + vector num_func_fam_by_strand = {0, 0}; + vector num_reads_available_by_strand = {0, 0}; // Here, one consensus read counts one read! + vector num_reads_conuts_available_by_strand = {0, 0}; // Here, one consensus read counts by its read counts! + + assert(allele_eval.total_theory.effective_min_family_size > 0); + + for (unsigned int i_strand = 0; i_strand < my_molecular_families.size(); ++i_strand){ + for (vector< MolecularFamily>::iterator family_it = my_molecular_families[i_strand].begin(); + family_it != my_molecular_families[i_strand].end(); ++family_it){ + // Skip the family if it is not functional + if (not family_it->SetFuncFromAll(effective_min_fam_size)){ + continue; + } + family_it->ResetValidFamilyMembers(); + if (not family_it->is_all_family_members_sorted){ + family_it->SortAllFamilyMembers(); // In case I haven't done this before. + } + + // Apply more filtering criteria to filter out some reads in all_family_members + // The reads in valid_family_members are available for downsampling and get into read stack + for (vector::iterator member_it = family_it->all_family_members.begin(); member_it != family_it->all_family_members.end(); ++member_it){ + // Although it has been done previously, do it again to make sure everything is right. + if ((*member_it)->filtered){ + continue; + } + // Although it has been done previously, do it again to make sure everything is right. + if ((*member_it)->sample_index != sample_index) { + continue; + } + + // Check global conditions to stop reading in more alignments + if ((*member_it)->original_position > multiallele_window_start + or (*member_it)->alignment.Position > multiallele_window_start + or (*member_it)->alignment.GetEndPosition() < multiallele_window_end){ + continue; + } + + // family_members_temp stores the reads which are not filtered out here + family_it->valid_family_members.push_back((*member_it)); + } + family_it->is_valid_family_members_sorted = true; // valid_family_members is of course sorted as well since all_family_members is sorted. + family_it->CountFamSizeFromValid(); + // Determine the functionality from valid_family_members + if (family_it->SetFuncFromValid(effective_min_fam_size)){ + // Count how many reads and functional families available for down sampling + num_reads_available_by_strand[i_strand] += family_it->valid_family_members.size(); + num_reads_conuts_available_by_strand[i_strand] += family_it->GetValidFamSize(); + ++num_func_fam_by_strand[i_strand]; + } + } + } + + // For the current molecular barcoding scheme (bcprimer), the reads in each amplicom should be on on strand only. + // However, we sometimes get families on bo th strands, primarily due to false priming. + // Here I pick the strand that has more functional families + strand_key = num_func_fam_by_strand[0] > num_func_fam_by_strand[1] ? 0 : 1; + + if (DEBUG > 0){ + string which_strand = (strand_key == 0)? "FWD" : "REV"; + cout << endl << "+ Stack up one variant with molecular tagging" << endl + << " - Effective-min-fam-size = " << effective_min_fam_size < fam_size_hist; + for (vector< MolecularFamily>::iterator family_it = my_molecular_families[i_strand].begin(); family_it != my_molecular_families[i_strand].end(); ++family_it){ + int fam_size = family_it->GetFuncFromAll()? family_it->GetValidFamSize() : family_it->GetFamSize(); + ++fam_size_hist[fam_size]; + } + cout << " - "<< (strand_key == 0 ? "FWD" : "REV") << ": fam_size_hist = ["; + for (map::iterator hist_it = fam_size_hist.begin(); hist_it != fam_size_hist.end(); ++hist_it){ + cout << "(" << hist_it->first << ", " << hist_it->second <<"), "; + } + cout << "]" << endl; + } + } + + // Do down-sampling + DoDownSamplingMolTag(parameters, effective_min_fam_size, my_molecular_families, num_reads_available_by_strand[strand_key], num_func_fam_by_strand[strand_key], strand_key); +} + +// ------------------------------------------------------------ + +void EnsembleEval::FilterAllAlleles(const ControlCallAndFilters& my_controls, const vector& variant_specific_params) { + if (seq_context.context_detected) { + for (unsigned int i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { + allele_identity_vector[i_allele].DetectCasesToForceNoCall(seq_context, my_controls, variant_specific_params[i_allele]); + } + } +} + + +BasicFilters const * ServeBasicFilterByType(const AlleleIdentity &variant_identity, const ControlCallAndFilters &my_controls){ + // The old logic for serving min-allele-freq based on allele type. Maybe deprecated in the future. + if (not my_controls.use_fd_param){ + if (variant_identity.status.isHotSpot and (not my_controls.hotspots_as_de_novo)) + return &(my_controls.filter_hotspot); + if (variant_identity.ActAsSNP()) + return &(my_controls.filter_snp); + if (variant_identity.ActAsMNP()) + return &(my_controls.filter_mnp); + if (variant_identity.ActAsHPIndel()) + return &(my_controls.filter_hp_indel); + return &(my_controls.filter_snp); + } + + // The new logic for serving min-allele-freq based on fd + if (variant_identity.status.isHotSpotAllele and (not my_controls.hotspots_as_de_novo)) + return &(my_controls.filter_hotspot); + if (variant_identity.fd_level_vs_ref == 0) + return &(my_controls.filter_fd_0); + if (variant_identity.fd_level_vs_ref == 1) + return &(my_controls.filter_fd_5); + if (variant_identity.fd_level_vs_ref == 2) + return &(my_controls.filter_fd_10); + // If indefinate, use the fd-0-min-allele-freq (usually the most conservative one) + return &(my_controls.filter_fd_0); +} + +float FreqThresholdByType(const AlleleIdentity& variant_identity, + const ControlCallAndFilters& my_controls, + const VariantSpecificParams& variant_specific_params) +{ + // Override has the top prioirty + if (variant_specific_params.min_allele_freq_override) + return variant_specific_params.min_allele_freq; + + return ServeBasicFilterByType(variant_identity, my_controls)->min_allele_freq; +} + + +void EnsembleEval::GatherInfoForOfflineFiltering(const ControlCallAndFilters &my_controls, int best_allele_index){ + variant->info["PARAM"].clear(); + variant->info["FDPARAM"].clear(); + variant->info["BAP"].clear(); + variant->info["BAI"].clear(); + variant->info["FDBAP"].clear(); + variant->info["AAHPINDEL"].clear(); + variant->info["ISHPINDEL"].clear(); + + + for (unsigned int i_alt = 0; i_alt < allele_identity_vector.size(); ++i_alt){ + variant->info["AAHPINDEL"].push_back((allele_identity_vector[i_alt].ActAsHPIndel()? "1" : "0")); + variant->info["ISHPINDEL"].push_back((allele_identity_vector[i_alt].status.isHPIndel? "1" : "0")); + } + ControlCallAndFilters my_controls_temp = my_controls; + for (int use_fd = 0; use_fd < 2; ++use_fd){ + my_controls_temp.use_fd_param = (use_fd == 1); + for (unsigned int i_alt = 0; i_alt < allele_identity_vector.size(); ++i_alt){ + BasicFilters const *my_basic_filter = ServeBasicFilterByType(allele_identity_vector[i_alt], my_controls_temp); + string param_type; + if (my_basic_filter == &(my_controls_temp.filter_fd_0)){ + param_type = "fd_0"; + }else if (my_basic_filter == &(my_controls_temp.filter_fd_5)){ + param_type = "fd_5"; + }else if (my_basic_filter == &(my_controls_temp.filter_fd_10)){ + param_type = "fd_10"; + }else if (my_basic_filter == &(my_controls_temp.filter_snp)){ + param_type = "snp"; + }else if (my_basic_filter == &(my_controls_temp.filter_mnp)){ + param_type = "mnp"; + }else if (my_basic_filter == &(my_controls_temp.filter_hp_indel)){ + param_type = "indel"; + }else if (my_basic_filter == &(my_controls_temp.filter_hotspot)){ + param_type = "hotspot"; + }else{ + param_type = "."; + } + variant->info[(my_controls_temp.use_fd_param? "FDPARAM" : "PARAM")].push_back(param_type); + } + } + + // The Best Alt allele Index. Allele X = Alt X - 1 => (X - 1) + variant->info["BAI"].push_back(convertToString(best_allele_index)); + // Best Allele Pair + variant->info["BAP"].push_back(convertToString(diploid_choice[0])); + variant->info["BAP"].push_back(convertToString(diploid_choice[1])); + // FD between Best Allele Pair + string fd_bpa_string; + int fd_bpa = global_flow_disruptive_matrix[diploid_choice[0]][diploid_choice[1]]; + if (fd_bpa == 0){ + fd_bpa_string = "0"; + }else if (fd_bpa == 1){ + fd_bpa_string = "5"; + }else if (fd_bpa == 2){ + fd_bpa_string = "10"; + }else{ + fd_bpa_string = "-1"; + } + variant->info["FDBAP"].push_back(fd_bpa_string); } -*/ diff --git a/Analysis/VariantCaller/EnsembleEval/StackEngine.h b/Analysis/VariantCaller/EnsembleEval/StackEngine.h index 078f8467..53cf7c17 100644 --- a/Analysis/VariantCaller/EnsembleEval/StackEngine.h +++ b/Analysis/VariantCaller/EnsembleEval/StackEngine.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "ExtendedReadInfo.h" #include "CrossHypotheses.h" @@ -47,17 +48,22 @@ class LatentSlate{ vector ll_at_stage; vector start_freq_of_winner; + int DEBUG; + void FastExecuteInference(ShortStack &total_theory, bool update_frequency, bool update_sigma, vector &start_frequency); void LocalExecuteInference(ShortStack &total_theory, bool update_frequency, bool update_sigma, vector &start_frequency); void FastStep(ShortStack &total_theory, bool update_frequency, bool update_sigma); void DetailedStep(ShortStack &total_theory, bool update_frequency, bool update_sigma); - void ScanStrandPosterior(ShortStack &total_theory, bool vs_ref, int max_detail_level); + void ScanStrandPosterior(ShortStack &total_theory, bool vs_ref); void ResetToOrigin(); void PropagateTuningParameters(EnsembleEvalTuningParameters &my_params, int num_hyp_no_null); - LatentSlate(){ + void SetAndPropagateDebug(int debug); + LatentSlate(int debug = 0){ max_iterations = 10; detailed_integral = true; iter_done = 0; + DEBUG = debug; + SetAndPropagateDebug(DEBUG); }; }; @@ -68,6 +74,8 @@ class HypothesisStack{ ShortStack total_theory; EnsembleEvalTuningParameters my_params; bool try_alternatives; + int DEBUG; + vcf::Variant* variant; // just for debug message. vector ll_record; vector > try_hyp_freq; @@ -84,12 +92,12 @@ class HypothesisStack{ void SetAlternateFromMain(); void ExecuteExtremeInferences(); void TriangulateRestart(); - float ExecuteOneRestart(vector &restart_hyp, int max_detail_level = 0); + float ExecuteOneRestart(vector &restart_hyp); void ExecuteInference(); - void InitForInference(PersistingThreadObjects &thread_objects, vector& read_stack, const InputStructures &global_context, int num_hyp_no_null, vector &allele_identity_vector); + void InitForInference(PersistingThreadObjects &thread_objects, vector& read_stack, const InputStructures &global_context, vector &allele_identity_vector); // tool for posterior density estimation - bool CallGermline(float hom_safety, int &genotype_call, float &quasi_phred_quality_score, float &reject_status_quality_score); + bool CallByIntegral(float af_cutoff_rej, float af_cutoff_gt, vector &genotype_component, float &quasi_phred_quality_score, float &reject_status_quality_score, int &qual_type); float ReturnMaxLL(); }; @@ -108,67 +116,117 @@ class EnsembleEval { int multiallele_window_end; vector info_fields; bool doRealignment; + int DEBUG; // Allele evaluation information HypothesisStack allele_eval; vector diploid_choice; + vector is_possible_polyploidy_allele; + //@TODO: Use enumerate to represent the fd-code + vector > global_flow_disruptive_matrix; EnsembleEval(vcf::Variant &candidate_variant) { - diploid_choice.assign(2,0); - diploid_choice[1]=1; // ref = 0, alt = 1 + diploid_choice.clear(); variant = &candidate_variant; + allele_eval.variant = variant; info_fields.clear(); multiallele_window_start = -1; multiallele_window_end = -1; doRealignment = false; - is_detect_best_multi_allele_pair_done_ = false; - is_hard_classification_for_reads_done_ = false; - is_hard_classification_for_families_done_ = false; + DEBUG = 0; + read_id_.clear(); + strand_id_.clear(); + dist_to_left_.clear(); + dist_to_right_.clear(); }; - //! @brief Create a detailed picture about this variant and all its alleles + //! @brief Set the parameters of the evaluator + void SetAndPropagateParameters(ExtendParameters* parameters, bool use_molecular_tag, const vector& variant_specific_params); + //! @brief Generate the base space hypotheses for each read + void SpliceAllelesIntoReads(PersistingThreadObjects &thread_objects, const InputStructures &global_context, + const ExtendParameters ¶meters, const ReferenceReader &ref_reader); + //! @brief Get MLLD from the evaluator + void ScanSupportingEvidence(float &mean_ll_delta, int i_allele); + //! @brief Calculate multi-min-allele-freq + void MultiMinAlleleFreq(const vector& multi_min_allele_freq); + //! @brief Output additional information in the INFO field to make off-line filter possible + void GatherInfoForOfflineFiltering(const ControlCallAndFilters &my_controls, int best_allele_index); + + //------------------------------------------------------------------ + // Functions for molecular tagging + //! @brief Set the min family size that will be used in the evaluator. + void SetEffectiveMinFamilySize(const ExtendParameters& parameters, const vector& variant_specific_params); + //! @brief Calculate the tag similarity for molecular tagging + void CalculateTagSimilarity(const MolecularTagManager& mol_tag_manager, int max_alt_cov, int sample_idx); + //! @brief Calculate the variant family histogram + void VariantFamilySizeHistogram(); + //------------------------------------------------------------------ + // Functions of allele related (not go to the reads) are defined here + //! @brief Setup the alleles, i.e., context investigation, allele classification, etc. void SetupAllAlleles(const ExtendParameters ¶meters, const InputStructures &global_context, - const ReferenceReader &ref_reader, int chr_idx); - void FilterAllAlleles(const ClassifyFilters &filter_variant, const vector& variant_specific_params); + const ReferenceReader &ref_reader); + //! @brief Filter out undesired alleles + void FilterAllAlleles(const ControlCallAndFilters& my_controls, const vector& variant_specific_params); + //! @brief Calculate the end of the look ahead window (primarily for candidate generator) + int CalculateLookAheadEnd0(const ReferenceReader &ref_reader); + //! @brief Split the current variant into as many callable smaller variants as possible (primarily for candidate generator)) + void SplitMyAlleleIdentityVector(list >& allele_group, const ReferenceReader &ref_reader, int max_group_size_allowed); + //------------------------------------------------------------------ + // Functions for filling read stack are defined here + //! @brief Fill the read stack w/o molecular tagging void StackUpOneVariant(const ExtendParameters ¶meters, const PositionInProgress& bam_position, int sample_index); - void StackUpOneVariantMolTag(const ExtendParameters ¶meters, vector< vector< MolecularFamily > > &my_molecular_families_one_strand, int sample_index); - void DoDownSamplingMolTag(const ExtendParameters ¶meters, vector< vector< MolecularFamily > > &my_molecular_families, + //! @brief Fill the read stack w/ molecular tagging + void StackUpOneVariantMolTag(const ExtendParameters ¶meters, vector< vector< MolecularFamily> > &my_molecular_families_one_strand, int sample_index); + //! @brief Strategic downsampling algorithm for molecular tagging + void DoDownSamplingMolTag(const ExtendParameters ¶meters, unsigned int effective_min_fam_size, vector< vector< MolecularFamily> > &my_molecular_families, unsigned int num_reads_available, unsigned int num_func_fam, int strand_key); - void SpliceAllelesIntoReads(PersistingThreadObjects &thread_objects, const InputStructures &global_context, - const ExtendParameters ¶meters, const ReferenceReader &ref_reader, int chr_idx); + //------------------------------------------------------------------ + // Functions of hard classification of reads/families are defined here + //! @brief Hard classification of reads void ApproximateHardClassifierForReads(); - void ApproximateHardClassifierForFamilies(); // calculate the family id for cfDNA - void ScanSupportingEvidence(float &mean_ll_delta, int i_allele); + //!@ brief Hard classification of families for molecular tagging + void ApproximateHardClassifierForFamilies(); // calculate the family id for mol taging + //------------------------------------------------------------------ + // Functions for high level evaluation results (e.g. QUAL, GT, GQ) are defined here + //! @brief Determine the best two alleles int DetectBestMultiAllelePair(); - void ComputePosteriorGenotype(int _alt_allele_index,float local_min_allele_freq, int &genotype_call, - float >_quality_score, float &reject_status_quality_score); - void MultiAlleleGenotype(float local_min_allele_freq, vector &genotype_component, - float >_quality_score, float &reject_status_quality_score, - int max_detail_level = 0); + //! @brief Determine the best two alleles and output the allele_freq_estimation in the detection of best allele pair + int DetectBestMultiAllelePair(vector& allele_freq_estimation); + //! @brief Determine possible polyploidy alleles + void DetectPossiblePolyploidyAlleles(const vector& allele_freq, const ControlCallAndFilters &my_controls, const vector& variant_specific_params); + //! @brief Get allele frequency cutoff according to the level of FD or allele type + void ServeAfCutoff(const ControlCallAndFilters &my_controls, const vector& variant_specific_params, + float& af_cutoff_rej, float& af_cutoff_gt); + //! @brief Calculate QUAL, GT, GQ + void MultiAlleleGenotype(float af_cutoff_rej, float af_cutoff_gt, vector &genotype_component, + float >_quality_score, float &reject_status_quality_score, int &qualiy_type); + //------------------------------------------------------------------ + // Functions of flow-disruption related are defined here + //! @brief Determine the flow-disruptiveness between hypotheses pairs for each read + void FlowDisruptivenessInReadLevel(const InputStructures &global_context); + //! @brief Determine the flow-disruptiveness between hypotheses pairs by looking at all reads + void FlowDisruptivenessInReadStackLevel(float min_ratio_for_fd); friend void GlueOutputVariant(EnsembleEval &my_ensemble, VariantCandidate &candidate_variant, const ExtendParameters ¶meters, int _best_allele_index, int sample_index); // I want to access your private members -// The following private members are used only in the internal steps at -// a) int DetectBestMultiAllelePair() -// b) void ApproximateHardClassifierForReads() -// c) void ApproximateHardClassifierForFamilies() private: - bool is_detect_best_multi_allele_pair_done_ = false; - // Tvc used to compute read_id etc. twice. This is not computationally efficient. - // Now we do it just once. // The following private members are the results of approximate hard classification for reads - bool is_hard_classification_for_reads_done_ = false; vector read_id_; // vector of allele ids per read, -1 = outlier, 0 = ref, >0 real allele vector strand_id_; // vector of forward (true) or reverse (false) per read // for each variant, calculate its' position within the soft clipped read distance to left and distance to right vector dist_to_left_; // vector of distances from allele position to left soft clip per read vector dist_to_right_; // vector of distances from allele position to left soft clip per read - // The following private members are the results of approximate hard classification for families - bool is_hard_classification_for_families_done_ = false; - vector family_id_; // similar to read_id, but it is for family - vector family_strand_id_; // similar to strand_id, but it is for family - vector max_family_responsibility_; // maximum family responsibility each the family in family_id + // The followings are for tag similarity + vector > alt_fam_indices_; // alt_fam_indices_[i_allele] stores the indices of families (of allele_eval.total_theory.my_eval_families) that support i_allele + vector tag_similar_counts_; }; +string PrintVariant(const vcf::Variant& variant); // just for debug message + +void FindNodesInIsoSubGraph(const vector >& connectivity_matrix, list >& subgraph_to_nodes, bool sort_by_index); +void SplitAlleleIdentityVector(const vector& allele_identity_vector, list >& allele_groups, const ReferenceReader& ref_reader, int max_group_size_allowed); +BasicFilters const * ServeBasicFilterByType(const AlleleIdentity& variant_identity, const ControlCallAndFilters& my_controls); +float FreqThresholdByType(const AlleleIdentity& variant_identity, const ControlCallAndFilters &my_controls, const VariantSpecificParams& variant_specific_params); + #endif // STACKENGINE_H diff --git a/Analysis/VariantCaller/Filter/DecisionTreeData.cpp b/Analysis/VariantCaller/Filter/DecisionTreeData.cpp index 2f5bc8f3..7b82712c 100644 --- a/Analysis/VariantCaller/Filter/DecisionTreeData.cpp +++ b/Analysis/VariantCaller/Filter/DecisionTreeData.cpp @@ -2,51 +2,29 @@ #include "DecisionTreeData.h" -void RemoveVcfInfo(vcf::Variant &variant, const vector &info_fields, const string &sample_name, int sample_index) +void RemoveVcfTagAndInfo(vcf::Variant &variant, const vector &keys, const string &sample_name, int sample_index) { - for(unsigned int i_info = 0; i_info < info_fields.size(); ++i_info){ - variant.info.erase(info_fields[i_info]); - variant.format.erase(std::remove(variant.format.begin(), variant.format.end(), info_fields[i_info]), variant.format.end()); + for(vector::const_iterator key_it = keys.begin(); key_it != keys.end(); ++key_it){ + variant.info.erase(*key_it); + variant.format.erase(std::remove(variant.format.begin(), variant.format.end(), *key_it), variant.format.end()); if (sample_index >= 0) { - variant.samples[sample_name].erase(info_fields[i_info]); + variant.samples[sample_name].erase(*key_it); } } } -void AutoFailTheCandidate(vcf::Variant &candidate_variant, bool use_position_bias, const string &sample_name, bool use_molecular_tag = false) { +void AutoFailTheCandidate(vcf::Variant &candidate_variant, bool use_position_bias, const string &sample_name, bool use_molecular_tag = false, string my_reason = "") { candidate_variant.quality = 0.0f; - NullInfoFields(candidate_variant, use_position_bias); // no information, destroy any spurious entries, add all needed tags - NullGenotypeAllSamples(candidate_variant); + NullInfoFields(candidate_variant, use_position_bias, use_molecular_tag); // no information, destroy any spurious entries, add all needed tags + NullGenotypeAllSamples(candidate_variant, use_molecular_tag); NullFilterReason(candidate_variant, sample_name); - string my_reason = "NODATA"; + if (my_reason.empty()){ + my_reason = "NODATA"; + } AddFilterReason(candidate_variant, my_reason, sample_name); SetFilteredStatus(candidate_variant, true); - - if(not use_molecular_tag){ - RemoveVcfInfo(candidate_variant, vector({"MDP", "MRO", "MAO", "MAF"}), sample_name, 0); - } } - - -float FreqThresholdByType(AlleleIdentity &variant_identity, const ControlCallAndFilters &my_controls, - const VariantSpecificParams& variant_specific_params) -{ - if (variant_specific_params.min_allele_freq_override) - return variant_specific_params.min_allele_freq; - if (variant_identity.status.isHotSpot) - return my_controls.filter_hotspot.min_allele_freq; - if (variant_identity.ActAsSNP()) - return my_controls.filter_snps.min_allele_freq; - if (variant_identity.ActAsMNP()) - return my_controls.filter_mnp.min_allele_freq; - if (variant_identity.ActAsHPIndel()) - return my_controls.filter_hp_indel.min_allele_freq; - - return my_controls.filter_snps.min_allele_freq; -} - - string EvaluatedGenotype::GenotypeAsString(){ stringstream tmp_g; tmp_g << genotype_component[0] << "/" << genotype_component[1]; @@ -54,10 +32,7 @@ string EvaluatedGenotype::GenotypeAsString(){ } bool EvaluatedGenotype::IsReference(){ - if ((genotype_component[0]==0) & (genotype_component[1]==0)) - return(true); - else - return(false); + return (genotype_component[0] == 0) and (genotype_component[1] == 0); } void PushValueOntoStringMaps(vector &tag, int index, double value) { @@ -67,30 +42,35 @@ void PushValueOntoStringMaps(vector &tag, int index, double value) { tag[index] = convertToString(d); } -void PushAlleleCountsOntoStringMaps(map >& my_map, MultiBook &all_summary_stats, bool info){ - unsigned mdp = atoi(my_map["MDP"][0].c_str()); - PushValueOntoStringMaps(my_map["FDP"], 0, all_summary_stats.TotalCount(-1)); - PushValueOntoStringMaps(my_map["FRO"], 0, all_summary_stats.GetAlleleCount(-1,0)); - PushValueOntoStringMaps(my_map["FSRF"], 0, all_summary_stats.GetAlleleCount(0,0)); - PushValueOntoStringMaps(my_map["FSRR"], 0, all_summary_stats.GetAlleleCount(1,0)); +void PushAlleleCountsOntoStringMaps(map >& my_map, MultiBook &all_summary_stats, bool use_molecular_tag){ + int fdp = all_summary_stats.TotalCount(-1); + int fro = all_summary_stats.GetAlleleCount(-1,0); + + PushValueOntoStringMaps(my_map["FDP"], 0, fdp); + PushValueOntoStringMaps(my_map["FRO"], 0, fro); + PushValueOntoStringMaps(my_map["FSRF"], 0, all_summary_stats.GetAlleleCount(0,0)); + PushValueOntoStringMaps(my_map["FSRR"], 0, all_summary_stats.GetAlleleCount(1,0)); + if (use_molecular_tag){ + PushValueOntoStringMaps(my_map["MDP"], 0, fdp); // MRO = FRO + PushValueOntoStringMaps(my_map["MRO"], 0, fro); // MRO = FRO + } // alternate allele count varies by allele - for ( int i_alt=0; i_alt< all_summary_stats.NumAltAlleles(); i_alt++){ - unsigned mao = atoi(my_map["MAO"][i_alt].c_str()); - PushValueOntoStringMaps(my_map["FAO"], i_alt, all_summary_stats.GetAlleleCount(-1,i_alt+1)); - PushValueOntoStringMaps(my_map["FSAF"], i_alt, all_summary_stats.GetAlleleCount(0,i_alt+1)); - PushValueOntoStringMaps(my_map["FSAR"], i_alt, all_summary_stats.GetAlleleCount(1,i_alt+1)); - //if (!info) { - if (all_summary_stats.TotalCount(-1) > 0) - my_map["AF"].push_back(convertToString((double)all_summary_stats.GetAlleleCount(-1,i_alt+1) / (double)all_summary_stats.TotalCount(-1))); - else - my_map["AF"].push_back(convertToString(0)); - if (mdp > 0) - my_map["MAF"].push_back(convertToString((double)mao / (double)mdp)); - else - my_map["MAF"].push_back(convertToString(0)); - //} - } + for (int i_alt=0; i_alt< all_summary_stats.NumAltAlleles(); i_alt++){ + int fao = all_summary_stats.GetAlleleCount(-1,i_alt+1); + PushValueOntoStringMaps(my_map["FAO"], i_alt, fao); + PushValueOntoStringMaps(my_map["FSAF"], i_alt, all_summary_stats.GetAlleleCount(0,i_alt+1)); + PushValueOntoStringMaps(my_map["FSAR"], i_alt, all_summary_stats.GetAlleleCount(1,i_alt+1)); + + string af_str = fdp > 0? convertToString((double) fao / (double) fdp) : "0"; + // czb: why didn't call PushValueOntoStringMaps here? + my_map["AF"].push_back(af_str); + if (use_molecular_tag){ + my_map["MAF"].push_back(af_str); // MAF = AF + PushValueOntoStringMaps(my_map["MAO"], i_alt, fao); // MAO = FAO + } + + } } float ComputeBaseStrandBiasForSSE(float relative_safety_level, vcf::Variant & candidate_variant, unsigned _altAlleleIndex, const string &sample_name){ @@ -121,14 +101,11 @@ void SuppressReferenceCalls(vcf::Variant &candidate_variant, const ExtendParamet //*** Below here is actual decision tree data ***/ - -void DecisionTreeData::SetupFromMultiAllele(const EnsembleEval &my_ensemble) { - //multi_allele = _multi_allele; - allele_identity_vector = my_ensemble.allele_identity_vector; - info_fields = my_ensemble.info_fields; -// summary_stats_vector.resize(multi_allele.allele_identity_vector.size()); - all_summary_stats.Allocate(allele_identity_vector.size()+1); // ref plus num alternate alleles - summary_info_vector.resize(allele_identity_vector.size()); +void DecisionTreeData::SetupFromMultiAllele(vector* const allele_identity_vect_ptr, vector* const info_fields_ptr){ + allele_identity_vector = allele_identity_vect_ptr; + info_fields = info_fields_ptr; + all_summary_stats.Allocate(allele_identity_vector->size() + 1); // ref plus num alternate alleles + summary_info_vector.resize(allele_identity_vector->size()); } void DecisionTreeData::OverrideFilter(vcf::Variant &candidate_variant, string & _filter_reason, int _allele, const string &sample_name) { @@ -138,26 +115,24 @@ void DecisionTreeData::OverrideFilter(vcf::Variant &candidate_variant, string & } // warning: no white-space allowed in filter reason -void FilterByBasicThresholds(const string& allele, int i_alt, MultiBook &m_summary_stats, +void DecisionTreeData::FilterByBasicThresholds(int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const BasicFilters &basic_filter, float tune_xbias, float tune_sbias, - const VariantSpecificParams& variant_specific_params, - bool is_reference_call) + const VariantSpecificParams& variant_specific_params) { - int effective_min_quality_score = basic_filter.min_quality_score; - if (variant_specific_params.min_variant_score_override) - effective_min_quality_score = variant_specific_params.min_variant_score; - if (l_summary_info.variant_qual_score < effective_min_quality_score) { + // Quality Score filter + int effective_min_quality_score = variant_specific_params.min_variant_score_override ? + variant_specific_params.min_variant_score : basic_filter.min_quality_score; + if (l_summary_info.variant_qual_score < effective_min_quality_score){ string my_reason = "QualityScore<"; my_reason += convertToString(effective_min_quality_score); l_summary_info.filterReason.push_back(my_reason); l_summary_info.isFiltered = true; } - int effective_min_cov = basic_filter.min_cov; - if (variant_specific_params.min_coverage_override) - effective_min_cov = variant_specific_params.min_coverage; - + // Min Coverage filter + int effective_min_cov = variant_specific_params.min_coverage_override ? + variant_specific_params.min_coverage : basic_filter.min_cov; if (m_summary_stats.GetDepth(-1, i_alt) < effective_min_cov) { l_summary_info.isFiltered = true; string my_reason = "MINCOV<"; @@ -165,20 +140,34 @@ void FilterByBasicThresholds(const string& allele, int i_alt, MultiBook &m_summa l_summary_info.filterReason.push_back(my_reason); } - int effective_min_var_cov = basic_filter.min_var_cov; // Filter out a variant allele if the variant coverage (FAO) is too low. // Don't apply the variant coverage filter if we are making a reference call. // This is because, by applying this filter, a reference call will be filtered out if any of the variant allele fails to pass the filter, which does not make sense. - if ((not is_reference_call) and m_summary_stats.GetAlleleCount(-1, i_alt + 1) < effective_min_var_cov) { - l_summary_info.isFiltered = true; - string my_reason = "VARCOV<"; - my_reason += convertToString(effective_min_var_cov); - l_summary_info.filterReason.push_back(my_reason); + if (not eval_genotype.IsReference()){ + // Min Variant Coverage filter + int effective_min_var_cov = variant_specific_params.min_var_coverage_override ? + variant_specific_params.min_var_coverage : basic_filter.min_var_cov; + if (m_summary_stats.GetAlleleCount(-1, i_alt + 1) < effective_min_var_cov) { + l_summary_info.isFiltered = true; + string my_reason = "VARCOV<"; + my_reason += convertToString(effective_min_var_cov); + l_summary_info.filterReason.push_back(my_reason); + } + + // Min Variant Coverage filter with TGSM adjustment + if (not m_summary_stats.tag_similar_counts.empty()){ + if (m_summary_stats.GetAlleleCount(-1, i_alt + 1) - m_summary_stats.tag_similar_counts[i_alt] < effective_min_var_cov){ + l_summary_info.isFiltered = true; + string my_reason = "VARCOV-TGSM<"; + my_reason += convertToString(effective_min_var_cov); + l_summary_info.filterReason.push_back(my_reason); + } + } } - int effective_min_cov_each_strand = basic_filter.min_cov_each_strand; - if (variant_specific_params.min_coverage_each_strand_override) - effective_min_cov_each_strand = variant_specific_params.min_coverage_each_strand; + // Min Strand Coverage filter + int effective_min_cov_each_strand = variant_specific_params.min_coverage_each_strand_override ? + variant_specific_params.min_coverage_each_strand : basic_filter.min_cov_each_strand; bool pos_cov = m_summary_stats.GetDepth(0, i_alt) < effective_min_cov_each_strand; if (pos_cov){ l_summary_info.isFiltered = true; @@ -194,18 +183,18 @@ void FilterByBasicThresholds(const string& allele, int i_alt, MultiBook &m_summa l_summary_info.filterReason.push_back(my_reason); } - float effective_strand_bias_thr = basic_filter.strand_bias_threshold; - if (variant_specific_params.strand_bias_override) - effective_strand_bias_thr = variant_specific_params.strand_bias; + // STB filter + float effective_strand_bias_thr = variant_specific_params.strand_bias_override ? + variant_specific_params.strand_bias : basic_filter.strand_bias_threshold; - float effective_strand_bias_pval_thr = basic_filter.strand_bias_pval_threshold; - if (variant_specific_params.strand_bias_pval_override) - effective_strand_bias_thr = variant_specific_params.strand_bias_pval; + float effective_strand_bias_pval_thr = variant_specific_params.strand_bias_pval_override ? + variant_specific_params.strand_bias_pval : basic_filter.strand_bias_pval_threshold; float strand_bias = m_summary_stats.OldStrandBias(i_alt, tune_sbias); float strand_bias_pval = m_summary_stats.StrandBiasPval(i_alt, tune_sbias); - if (strand_bias > effective_strand_bias_thr && - strand_bias_pval <= effective_strand_bias_pval_thr) { + if ((not eval_genotype.IsReference()) + and (strand_bias > effective_strand_bias_thr) + and (strand_bias_pval <= effective_strand_bias_pval_thr)) { string my_reason = "STDBIAS"; my_reason += convertToString(strand_bias); my_reason += ">"; @@ -217,7 +206,6 @@ void FilterByBasicThresholds(const string& allele, int i_alt, MultiBook &m_summa my_reason1 += "<"; my_reason1 += convertToString(effective_strand_bias_pval_thr); l_summary_info.filterReason.push_back(my_reason1); - l_summary_info.isFiltered = true; } @@ -235,6 +223,7 @@ void FilterByBasicThresholds(const string& allele, int i_alt, MultiBook &m_summa float CalculateLod(int total_count, int min_var_cov){ float lod = 1.0f; + min_var_cov = max(min_var_cov, 1); if(total_count > 0) lod = ((float) min_var_cov - 0.5f)/((float) total_count); return lod; @@ -258,50 +247,41 @@ float CalculateOutputLod(float lod){ return output_lod; } -void DecisionTreeData::AddLodTags(vcf::Variant &candidate_variant, const string &sample_name, const ExtendParameters ¶meters){ +void DecisionTreeData::AddLodTags(vcf::Variant &candidate_variant, const string &sample_name, const ControlCallAndFilters &my_controls){ // Currently don't output LOD if use_lod_filter = false - if(not parameters.my_controls.use_lod_filter){ + if(not my_controls.use_lod_filter){ return; } int total_count = all_summary_stats.TotalCount(-1); candidate_variant.info["LOD"].clear(); - for (int i_allele = 0; i_allele < all_summary_stats.NumAltAlleles(); ++i_allele){ - int min_var_cov = 0; - //filter values specific to SNPs, MNVs and Non Homopolymer Indels - if (allele_identity_vector[i_allele].status.isHotSpot) { - min_var_cov = parameters.my_controls.filter_hotspot.min_var_cov; - } - else if (allele_identity_vector[i_allele].ActAsSNP()) { - min_var_cov = parameters.my_controls.filter_snps.min_var_cov; - } - else if (allele_identity_vector[i_allele].ActAsMNP()) { - min_var_cov = parameters.my_controls.filter_mnp.min_var_cov; - - } // end if MNV - else if (allele_identity_vector[i_allele].ActAsHPIndel()) { - min_var_cov = parameters.my_controls.filter_hp_indel.min_var_cov; - } - - float lod = CalculateLod(total_count, min_var_cov); - float output_lod = CalculateOutputLod(lod); - string val = convertToString(output_lod); - candidate_variant.info["LOD"].push_back(val); + for (unsigned int i_alt = 0; i_alt < allele_identity_vector->size(); ++i_alt){ + float output_lod = CalculateOutputLod(all_summary_stats.lod[i_alt]); + candidate_variant.info["LOD"].push_back(convertToString(output_lod)); } } -void DecisionTreeData::FilterOnLod(const string& allele, int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const BasicFilters &basic_filter, bool is_reference_call) +void DecisionTreeData::FilterOnLod(int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const BasicFilters &basic_filter) { - if((not my_filters.use_lod_filter) or is_reference_call){ + if (not my_filters.use_lod_filter){ return; } int min_var_cov = basic_filter.min_var_cov; int total_count = m_summary_stats.TotalCount(-1); - float lod = CalculateLod(total_count, min_var_cov); float min_allele_freq = basic_filter.min_allele_freq; - float af = (float) m_summary_stats.GetAlleleCount(-1, i_alt + 1) / (float)(total_count); + float lod = CalculateLod(total_count, min_var_cov); + float af = total_count > 0 ? + (float) m_summary_stats.GetAlleleCount(-1, i_alt + 1) / (float)(total_count) : 0.0f; + + if (all_summary_stats.lod.size() != allele_identity_vector->size()){ + all_summary_stats.lod.resize(allele_identity_vector->size()); + } + all_summary_stats.lod[i_alt] = lod; + + if (eval_genotype.IsReference()) + return; // The LOD filter first filter out the alt allele if af < min_allele_freq - if(af < min_allele_freq){ + if (af < min_allele_freq){ string my_reason = ""; l_summary_info.isFiltered = true; my_reason += "AF"; @@ -311,7 +291,7 @@ void DecisionTreeData::FilterOnLod(const string& allele, int i_alt, MultiBook &m l_summary_info.filterReason.push_back(my_reason); } // The LOD filter then filter out the alt allele if af < lod_multiplier * lod - if(af < my_filters.lod_multiplier * lod){ + if (af < my_filters.lod_multiplier * lod){ string my_reason = ""; l_summary_info.isFiltered = true; my_reason += "AF"; @@ -325,39 +305,35 @@ void DecisionTreeData::FilterOnLod(const string& allele, int i_alt, MultiBook &m } -void DecisionTreeData::FilterOnPositionBias(const string& allele, int i_alt, MultiBook &m_summary_stats, - VariantOutputInfo &l_summary_info, - const ControlCallAndFilters &my_filters, +void DecisionTreeData::FilterOnPositionBias(int i_alt, MultiBook &m_summary_stats, + VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const VariantSpecificParams& variant_specific_params) { - if (!my_filters.use_position_bias) { + if ((not my_filters.use_position_bias) or eval_genotype.IsReference()) { return; } - float effective_position_bias_pval_thr = my_filters.position_bias_pval; - if (variant_specific_params.position_bias_pval_override) - effective_position_bias_pval_thr = variant_specific_params.position_bias_pval; + float effective_position_bias_pval_thr = variant_specific_params.position_bias_pval_override ? + variant_specific_params.position_bias_pval : my_filters.position_bias_pval; - float effective_position_bias_thr = my_filters.position_bias; - if (variant_specific_params.position_bias_override) - effective_position_bias_thr = variant_specific_params.position_bias; + float effective_position_bias_thr = variant_specific_params.position_bias_override ? + variant_specific_params.position_bias : my_filters.position_bias; - i_alt = i_alt + 1; // confusing, ref is 0, but iterating from 0 excluding ref + int i_allele = i_alt + 1; // confusing, ref is 0, but iterating from 0 excluding ref - float position_bias = m_summary_stats.PositionBias(i_alt); - float position_bias_pval = m_summary_stats.GetPositionBiasPval(i_alt); + float position_bias = m_summary_stats.PositionBias(i_allele); + float position_bias_pval = m_summary_stats.GetPositionBiasPval(i_allele); // low fraction of ref reads is not associated with real position bias unsigned int ref_count = m_summary_stats.GetAlleleCount(-1,0); //FRO - unsigned int var_count = m_summary_stats.GetAlleleCount(-1,i_alt); //FAO - float ref_fraction = ((float)ref_count) / ((float)ref_count + (float)var_count + 0.1); + unsigned int var_count = m_summary_stats.GetAlleleCount(-1,i_allele); //FAO + float ref_fraction = ((float)ref_count) / ((float)ref_count + (float)var_count + 0.1f); - if ( ref_fraction <= my_filters.position_bias_ref_fraction ) { + if ( ref_fraction <= my_filters.position_bias_ref_fraction or my_filters.disable_filters) { return; } - if ((position_bias_pval < effective_position_bias_pval_thr) && - (position_bias > effective_position_bias_thr)){ + if ((position_bias_pval < effective_position_bias_pval_thr) and (position_bias > effective_position_bias_thr)){ string my_reason = "POSBIAS"; my_reason += convertToString(position_bias); my_reason += ">"; @@ -374,52 +350,35 @@ void DecisionTreeData::FilterOnPositionBias(const string& allele, int i_alt, Mul } } - void DecisionTreeData::FilterOneAllele(int i_alt, VariantOutputInfo &l_summary_info, AlleleIdentity &l_variant_identity, const ControlCallAndFilters &my_filters, const VariantSpecificParams& variant_specific_params) { - bool is_reference_call = eval_genotype.IsReference(); - - // if some reason from the identity to filter it - if (l_variant_identity.status.isProblematicAllele){ - l_summary_info.isFiltered = true; - for (unsigned int i_reason=0; i_reason& variant_specific_params) { for (int i_alt = 0; i_alt < all_summary_stats.NumAltAlleles(); i_alt++) { - FilterOneAllele(i_alt, summary_info_vector[i_alt], allele_identity_vector[i_alt], my_filters, variant_specific_params[i_alt]); + FilterOneAllele(i_alt, summary_info_vector[i_alt], allele_identity_vector->at(i_alt), my_filters, variant_specific_params[i_alt]); } //end loop thru alleles } @@ -427,84 +386,86 @@ void DecisionTreeData::FilterAlleles(const ControlCallAndFilters &my_filters, co ///***** heal snps here *****// void DecisionTreeData::AccumulateFilteredAlleles(){ - int numAlleles = summary_info_vector.size(); - VariantOutputInfo _summary_info; - for (int i=0; iA SNP is true then we want to move the allele representation to REF = C, Alt = A which is a more standard representation. - if (isBestAlleleSNP & heal_snps) { - //loop thru all the alleles and filter all Indel alleles which will be later removed from alts. - int numAlleles = summary_info_vector.size(); - - VariantOutputInfo _summary_info; - AlleleIdentity _variant_identity; - for (int counter = 0; counter < numAlleles; counter++) { - _summary_info = summary_info_vector[counter]; - _variant_identity = allele_identity_vector[counter]; - - - if (_variant_identity.status.isIndel && !_summary_info.isFiltered) { //if it is Indel allele and not already filtered - summary_info_vector[counter].isFiltered = true; - filteredAllelesIndex.push_back(counter); - } + if (not (isBestAlleleVeryReal and cleanup_unlikely_candidates)){ + return; + } - } - } + //loop thru all the alleles and filter all Indel alleles which will be later removed from alts. + for (unsigned int i_alt = 0; i_alt < allele_identity_vector->size(); ++i_alt){ + if (allele_identity_vector->at(i_alt).status.isHPIndel and (not summary_info_vector[i_alt].isFiltered)) { + // if it is Indel allele and not already filtered + // Note: The following step is based on the high level logic of filtering that, for X > 0 and Y > 0, X/Y PASS if either X or Y passes all filters. + // i.e., If the best allele passes, then I don't care the second best is filtered or not. + // It can be buggy if someone changes the logic. + //TODO: Make the code safe, clear and robust. + summary_info_vector[i_alt].isFiltered = true; + filteredAllelesIndex.push_back(i_alt); + } + } } -void DecisionTreeData::FindBestAlleleIdentity(){ - - AlleleIdentity _variant_identity = allele_identity_vector[best_allele_index]; - if (_variant_identity.status.isSNP || _variant_identity.status.isMNV) - isBestAlleleSNP = true; - else - isBestAlleleSNP = false; +void DecisionTreeData::FindBestAlleleIdentity(bool use_fd_param){ + if (use_fd_param){ + isBestAlleleVeryReal = allele_identity_vector->at(best_allele_index).fd_level_vs_ref == 2; + } + else{ + isBestAlleleVeryReal = allele_identity_vector->at(best_allele_index).status.isSNP or allele_identity_vector->at(best_allele_index).status.isMNV; + } } -void DecisionTreeData::SimplifySNPsIfNeeded(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, const string &sample_name){ - - FindBestAlleleIdentity(); - AccumulateFilteredAlleles(); - - if (!best_variant_filtered && (isBestAlleleSNP & parameters.my_controls.heal_snps)) { //currently we are removing other filtered alleles if the best allele is a SNP +void DecisionTreeData::CleanupCandidatesIfNeeded(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, const string &sample_name){ + FindBestAlleleIdentity(parameters.my_controls.use_fd_param); + AccumulateFilteredAlleles(); + // Note: CleanupCandidates only if PPA is the same as GT. + if (best_variant_filtered + or (not is_ppa_same_as_gt) + or (not isBestAlleleVeryReal) + or (not parameters.my_controls.cleanup_unlikely_candidates) + or (not eval_genotype.genotype_already_set)){ + return; + } - BestSNPsSuppressInDels(parameters.my_controls.heal_snps); + // Currently we are removing other filtered alleles if the best allele is (flow-disrupted and use_fd_param) or (SNP and not use_fd_param). + BestVariantSuppressInDels(parameters.my_controls.cleanup_unlikely_candidates); // see if any genotype-components are needed here // if so, cannot remove them // unwilling to assume "deletions" or "insertions" are really reference if they are noticeable - bool cannot_adjust = false; - if (eval_genotype.genotype_already_set){ - for (unsigned int i_ndx=0; i_ndx0){ - if (summary_info_vector[i_comp-1].isFiltered) - cannot_adjust = true; // because it is part of the best diploid genotype - } - } + for (vector::iterator gt_it = eval_genotype.genotype_component.begin(); gt_it != eval_genotype.genotype_component.end(); ++gt_it){ + if (*gt_it > 0){ + if (summary_info_vector[*gt_it - 1].isFiltered){ + return; // because it is part of the best diploid genotype + } + } } + + RemoveFilteredAlleles(candidate_variant.variant, filteredAllelesIndex, sample_name); // and therefore will give a nonsense genotype if we do adjust - if (!cannot_adjust){ - RemoveFilteredAlleles(candidate_variant.variant, filteredAllelesIndex, sample_name); - AdjustAlleles(candidate_variant.variant, candidate_variant.position_upper_bound); + // Note: The only possible GT for heal snps is 0/X and X/X where X is the best allele and it is SNP or MNP. + if (parameters.my_controls.report_ppa){ + // Update PPA to be the new GT + candidate_variant.variant.info["PPA"] = candidate_variant.variant.samples[sample_name]["GT"]; + } + if (not parameters.output_allele_cigar){ + AdjustAlleles(candidate_variant.variant, candidate_variant.position_upper_bound); } - } - } -//********* heal snps done ****//// - void DetectSSEForNoCall(VariantOutputInfo &l_summary_info, AlleleIdentity &var_identity, float sseProbThreshold, float minRatioReadsOnNonErrorStrand, float base_strand_bias,vcf::Variant & candidate_variant, unsigned _altAlleleIndex) { if (var_identity.sse_prob_positive_strand > sseProbThreshold && var_identity.sse_prob_negative_strand > sseProbThreshold) { @@ -545,7 +506,7 @@ void DecisionTreeData::FilterBlackList(const vector& vari return; // disable pending algo changes, revert to 4.2 behavior - for (unsigned int i_allele=0; i_allelesize(); i_allele++) { VariantOutputInfo &l_summary_info = summary_info_vector[i_allele]; @@ -577,25 +538,27 @@ void DecisionTreeData::FilterBlackList(const vector& vari } -void DecisionTreeData::FilterSSE(vcf::Variant &candidate_variant, const ClassifyFilters &filter_variant, const vector& variant_specific_params, const string &sample_name) +void DecisionTreeData::FilterSSE(vcf::Variant &candidate_variant, const ControlCallAndFilters &my_control, const vector& variant_specific_params, const string &sample_name) { - for (unsigned int i_allele=0; i_allelesize(); i_allele++) { // change for 4.0: store all allele information for multiallele clean filter application after VCF int _alt_allele_index = i_allele; - float base_strand_bias = ComputeBaseStrandBiasForSSE(filter_variant.sse_relative_safety_level, candidate_variant, _alt_allele_index, sample_name); + float base_strand_bias = ComputeBaseStrandBiasForSSE(my_control.filter_variant.sse_relative_safety_level, candidate_variant, _alt_allele_index, sample_name); candidate_variant.info["SSSB"].push_back(convertToString(base_strand_bias)); - candidate_variant.info["SSEP"].push_back(convertToString(allele_identity_vector[_alt_allele_index].sse_prob_positive_strand)); - candidate_variant.info["SSEN"].push_back(convertToString(allele_identity_vector[_alt_allele_index].sse_prob_negative_strand)); + candidate_variant.info["SSEP"].push_back(convertToString(allele_identity_vector->at(_alt_allele_index).sse_prob_positive_strand)); + candidate_variant.info["SSEN"].push_back(convertToString(allele_identity_vector->at(_alt_allele_index).sse_prob_negative_strand)); + if (not my_control.disable_filters){ //@TODO: make sure this takes information from the tags in candidate variant and nowhere else // which forces us to be honest and only use information in the output DetectSSEForNoCall(summary_info_vector[i_allele], - allele_identity_vector[i_allele], + allele_identity_vector->at(i_allele), variant_specific_params[_alt_allele_index].sse_prob_threshold_override ? - variant_specific_params[_alt_allele_index].sse_prob_threshold : filter_variant.sseProbThreshold, - filter_variant.minRatioReadsOnNonErrorStrand, base_strand_bias, candidate_variant, i_allele); -} + variant_specific_params[_alt_allele_index].sse_prob_threshold : my_control.filter_variant.sseProbThreshold, + my_control.filter_variant.minRatioReadsOnNonErrorStrand, base_strand_bias, candidate_variant, i_allele); + } + } } void DecisionTreeData::AddStrandBiasTags(vcf::Variant &candidate_variant, const string &sample_name){ @@ -645,22 +608,22 @@ void DecisionTreeData::AddCountInformationTags(vcf::Variant & candidate_variant, AddPositionBiasTags(candidate_variant, sample_name); // Add Dima's LOD tag - AddLodTags(candidate_variant, sample_name, parameters); + AddLodTags(candidate_variant, sample_name, parameters.my_controls); map >& infoOutput = candidate_variant.info; - PushAlleleCountsOntoStringMaps(infoOutput,all_summary_stats, true); + PushAlleleCountsOntoStringMaps(infoOutput,all_summary_stats, use_molecular_tag); infoOutput["FXX"].push_back(convertToString(all_summary_stats.GetFailedReadRatio())); if (!sample_name.empty()) { map >& sampleOutput = candidate_variant.samples[sample_name]; - PushAlleleCountsOntoStringMaps(sampleOutput, all_summary_stats, false); + PushAlleleCountsOntoStringMaps(sampleOutput, all_summary_stats, use_molecular_tag); } // hrun fill in ClearVal(candidate_variant, "HRUN"); - for (unsigned int ia=0; iasize(); ia++){ + candidate_variant.info["HRUN"].push_back(convertToString(allele_identity_vector->at(ia).ref_hp_length)); } } @@ -704,6 +667,9 @@ void DecisionTreeData::FilterOnStringency(vcf::Variant &candidate_variant, const void DecisionTreeData::FilterOnSpecialTags(vcf::Variant & candidate_variant, const ExtendParameters ¶meters, const vector& variant_specific_params, const string &sample_name) { + if (parameters.my_controls.disable_filters){ + return; + } // separate my control here /* float max_bias = 0.0f; @@ -712,7 +678,7 @@ void DecisionTreeData::FilterOnSpecialTags(vcf::Variant & candidate_variant, con if (abs(bias) > max_bias) max_bias = abs(bias); } */ - for (unsigned int _alt_allele_index = 0; _alt_allele_index < allele_identity_vector.size(); _alt_allele_index++) { + for (unsigned int _alt_allele_index = 0; _alt_allele_index < allele_identity_vector->size(); _alt_allele_index++) { // if something is strange here /* Not to do this on allele, revert to 4.6*/ SpecializedFilterFromLatentVariables(*(variant), variant_specific_params[_alt_allele_index].filter_unusual_predictions_override ? @@ -730,12 +696,12 @@ void DecisionTreeData::FilterOnSpecialTags(vcf::Variant & candidate_variant, con } */ - SpecializedFilterFromHypothesisBias(*(variant), allele_identity_vector[_alt_allele_index], + SpecializedFilterFromHypothesisBias(*(variant), allele_identity_vector->at(_alt_allele_index), variant_specific_params[_alt_allele_index].filter_deletion_predictions_override ? variant_specific_params[_alt_allele_index].filter_deletion_predictions : parameters.my_eval_control.filter_deletion_bias, variant_specific_params[_alt_allele_index].filter_insertion_predictions_override ? variant_specific_params[_alt_allele_index].filter_insertion_predictions : parameters.my_eval_control.filter_insertion_bias, - _alt_allele_index, sample_name); + _alt_allele_index, sample_name, parameters.my_controls.use_fd_param); float effective_data_quality_stringency = parameters.my_controls.data_quality_stringency; if (variant_specific_params[_alt_allele_index].data_quality_stringency_override) @@ -791,23 +757,35 @@ void DecisionTreeData::FilterAlleleHypothesisBias(vcf::Variant & candidate_varia } - -void DecisionTreeData::SpecializedFilterFromHypothesisBias(vcf::Variant & candidate_variant, AlleleIdentity allele_identity, const float deletion_bias, const float insertion_bias, int _allele, const string &sample_name) +void DecisionTreeData::SpecializedFilterFromHypothesisBias(vcf::Variant & candidate_variant, AlleleIdentity allele_identity, const float deletion_bias, const float insertion_bias, int _allele, const string &sample_name, bool use_fd_param) { - -// float ref_bias = hypothesis_stack.cur_state.bias_generator.latent_bias_v[0]; -// float var_bias = hypothesis_stack.cur_state.bias_generator.latent_bias_v[1]; - float ref_bias = RetrieveQualityTagValue(candidate_variant, "REFB", _allele); - float var_bias = RetrieveQualityTagValue(candidate_variant, "VARB", _allele); - - if (allele_identity.ActAsHPIndel()) { - if (allele_identity.status.isDeletion) { - FilterAlleleHypothesisBias(candidate_variant, ref_bias, var_bias, deletion_bias, _allele, sample_name); + float ref_bias = RetrieveQualityTagValue(candidate_variant, "REFB", _allele); + float var_bias = RetrieveQualityTagValue(candidate_variant, "VARB", _allele); + float bias_threshold = -1.0f; + if (use_fd_param){ + if (allele_identity.fd_level_vs_ref < 2){ + // Now the ref bias and var bias filters can be applied to non-fd snp. + // Non-fd snp is a combo of INS + DEL, so I will use the minimum bias threshold. + if (allele_identity.status.isDeletion){ + bias_threshold = deletion_bias; + }else if (allele_identity.status.isInsertion){ + bias_threshold = insertion_bias; + }else{ + bias_threshold = min(deletion_bias, insertion_bias); + } + } + }else if (allele_identity.ActAsHPIndel()){ + // The old way that we do bias filtering. + if (allele_identity.status.isDeletion){ + bias_threshold = deletion_bias; + }else if (allele_identity.status.isInsertion){ + bias_threshold = insertion_bias; + } } - else if (allele_identity.status.isInsertion) { - FilterAlleleHypothesisBias(candidate_variant, ref_bias, var_bias, insertion_bias, _allele, sample_name); + + if (bias_threshold > 0.0f){ + FilterAlleleHypothesisBias(candidate_variant, ref_bias, var_bias, bias_threshold, _allele, sample_name); } - } } void FilterOnReadRejectionRate(vcf::Variant &candidate_variant, float read_rejection_threshold, const string &sample_name){ @@ -828,14 +806,13 @@ void DecisionTreeData::AggregateFilterInformation(vcf::Variant & candidate_varia // adds tags to the file FilterBlackList(variant_specific_params); - FilterSSE(candidate_variant, parameters.my_controls.filter_variant, variant_specific_params, sample_name); + FilterSSE(candidate_variant, parameters.my_controls, variant_specific_params, sample_name); FilterOnSpecialTags(candidate_variant, parameters, variant_specific_params, sample_name); FilterAlleles(parameters.my_controls, variant_specific_params); AddCountInformationTags(candidate_variant, sample_name, parameters); - } @@ -880,8 +857,8 @@ void DecisionTreeData::GenotypeAlleleFilterMyCandidate(vcf::Variant &candidate_ } // Abuse FR tag to write out info fields - for (unsigned int i_info=0; i_infosize(); i_info++) + AddInfoReason(candidate_variant, info_fields->at(i_info), sample_name); // if no-one escaped if (!no_filter){ @@ -909,10 +886,61 @@ void DecisionTreeData::FilterMyCandidate(vcf::Variant &candidate_variant, const GenotypeAlleleFilterMyCandidate(candidate_variant, parameters, sample_name); // whole candidate reason - FilterOnReadRejectionRate(candidate_variant, parameters.my_controls.read_rejection_threshold, sample_name); + if (not parameters.my_controls.disable_filters){ + FilterOnReadRejectionRate(candidate_variant, parameters.my_controls.read_rejection_threshold, sample_name); + } } +// Determine Possible Polyploidy Alleles (PPA) +// An allele is a PPA if the conditions a) and b) are both satisfied. +// a) Called in GT or its estimated allele frequency > min-allele-freq +// b) Pass all the filters (e.g., STB, RBI, QUAL, etc.) +// (Note): Must be done before heal SNPs +// (Note): Report "." if none +void DecisionTreeData::DeterminePossiblePolyploidyAlleles(vcf::Variant &candidate_variant, const ExtendParameters ¶meters) { + unsigned int num_alleles = allele_identity_vector->size() + 1; + if (is_possible_polyploidy_allele.size() != num_alleles){ + is_possible_polyploidy_allele.assign(num_alleles, false); + } + // The alleles in GT are PPA + is_possible_polyploidy_allele[eval_genotype.genotype_component[0]] = true; + is_possible_polyploidy_allele[eval_genotype.genotype_component[1]] = true; + + if (not parameters.my_controls.report_ppa){ + is_ppa_same_as_gt = true; + return; + } + + + string ppa_string = ""; + ppa_string.reserve(2 * num_alleles); + int num_ppa_alleles = 0; // number of alleles in PPA + for (unsigned int i_allele = 0; i_allele < num_alleles; ++i_allele){ + if (i_allele > 0){ + is_possible_polyploidy_allele[i_allele] = is_possible_polyploidy_allele[i_allele] and (not summary_info_vector[i_allele - 1].isFiltered); + } + if (is_possible_polyploidy_allele[i_allele]){ + ppa_string += (to_string(i_allele) + "/"); + ++num_ppa_alleles; + } + } + int num_gt_alleles = (eval_genotype.genotype_component[0] == eval_genotype.genotype_component[1])? 1:2; + + is_ppa_same_as_gt = is_possible_polyploidy_allele[eval_genotype.genotype_component[0]] // Does the first allele of GT get filtered? + and is_possible_polyploidy_allele[eval_genotype.genotype_component[1]] // Does the second allele in GT get filtered? + and (num_ppa_alleles == num_gt_alleles); // Does GT and PPA have the same number of alleles? + + if (is_ppa_same_as_gt){ + ppa_string = eval_genotype.GenotypeAsString(); + }else if (ppa_string.empty()){ + ppa_string = "."; + }else{ + ppa_string.resize(ppa_string.size() - 1); // remove the last "/" + } + candidate_variant.info["PPA"] = {ppa_string}; +} + void DecisionTreeData::FillInFiltersAtEnd(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, const string &sample_name) { @@ -924,161 +952,15 @@ void DecisionTreeData::FillInFiltersAtEnd(VariantCandidate &candidate_variant, c // candidate alleles contribute to possible filtration FilterMyCandidate(candidate_variant.variant, parameters, sample_name); - SimplifySNPsIfNeeded(candidate_variant, parameters, sample_name); + CleanupCandidatesIfNeeded(candidate_variant, parameters, sample_name); // if the genotype is a reference call, and we want to suppress it, make sure it will be in the filtered file SuppressReferenceCalls(candidate_variant.variant, parameters, reference_genotype, sample_name); if (parameters.my_controls.suppress_nocall_genotypes) DetectAndSetFilteredGenotype(candidate_variant.variant, variant_quality, sample_name); -} - - -// once we have standard data format across all alleles, the common filters can execute. -// this is just horrible at the moment -// need to clean this code up badly/* Copyright (C) 2013 Ion Torrent Systems, Inc. All Rights Reserved */ - - -/* Copyright (C) 2013 Ion Torrent Systems, Inc. All Rights Reserved */ -#ifndef DECISIONTREEDATA_H -#define DECISIONTREEDATA_H - - -#include "api/BamReader.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ClassifyVariant.h" -#include "StackEngine.h" -#include "VcfFormat.h" - -class EvaluatedGenotype{ -public: - bool genotype_already_set; - float evaluated_genotype_quality; - float evaluated_variant_quality; - vector genotype_component; - - EvaluatedGenotype(){ - genotype_already_set = false; - evaluated_genotype_quality = 0.0f; - evaluated_variant_quality = 0.0f; - genotype_component.assign(2,0); // 0/0 = reference call - - }; - string GenotypeAsString(); - bool IsReference(); -}; - -// all the data needed to make a decision for filtration -// characterize the variant, and the outcome from whatever evaluator we use -class DecisionTreeData { - public: - - vcf::Variant * variant; //!< VCF record of this variant position - vector allele_identity_vector; //!< Detailed information for each candidate allele - vector info_fields; //!< Additional information to be printed out in vcf FR tag - - MultiBook all_summary_stats; - - vector summary_info_vector; - - vector filteredAllelesIndex; - - map variant_quality; - - - bool best_variant_filtered; - - bool best_allele_set; - int best_allele_index; - bool isBestAlleleSNP; - bool reference_genotype; - - EvaluatedGenotype eval_genotype; - - float tune_xbias; // not tuned, removed from filters - float tune_sbias; - - DecisionTreeData(vcf::Variant &candidate_variant) /*: multi_allele(candidate_variant)*/ { - variant = &candidate_variant; - best_allele_set = false; - best_allele_index = 0; - best_variant_filtered=false; - isBestAlleleSNP = false; - reference_genotype = false; - - - tune_xbias = 0.005f; // tune calculation of chi-square bias = proportioinal variance by frequency - tune_sbias = 0.5f; // safety factor for small allele counts for transformed strand bias - }; - - void OverrideFilter(vcf::Variant &candidate_variant, string & _filter_reason, int _allele, const string &sample_name); - void FilterOneAllele(int i_alt,VariantOutputInfo &l_summary_info, - AlleleIdentity &l_variant_identity, const ControlCallAndFilters &my_filters, - const VariantSpecificParams& variant_specific_params); - void FilterAlleles(const ControlCallAndFilters &my_filters, const vector& variant_specific_params); - - void AccumulateFilteredAlleles(); - - void BestSNPsSuppressInDels(bool heal_snps); - void FindBestAlleleIdentity(); - void FindBestAlleleByScore(); - - void GenotypeFromBestAlleleIndex(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); - void GenotypeFromEvaluator(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); - - void FilterMyCandidate(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); - void BestAlleleFilterMyCandidate(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); - void GenotypeAlleleFilterMyCandidate(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); - - void SimplifySNPsIfNeeded(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); - - - bool SetGenotype(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, float gt_quality); - void DecisionTreeOutputToVariant(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, int sample_index); - - void AggregateFilterInformation(vcf::Variant &candidate_variant, const vector& variant_specific_params, const ExtendParameters ¶meters, const string &sample_name); - void FillInFiltersAtEnd(VariantCandidate &candidate_variant,const ExtendParameters ¶meters, const string &sample_name); - - - - void SetupFromMultiAllele(const EnsembleEval &my_ensemble); - void AddStrandBiasTags(vcf::Variant &candidate_variant, const string &sample_name); - void AddPositionBiasTags(vcf::Variant &candidate_variant, const string &sample_name); - void AddLodTags(vcf::Variant &candidate_variant, const string &sample_name, const ExtendParameters ¶meters); - void AddCountInformationTags(vcf::Variant &candidate_variant, const string &sampleName, const ExtendParameters ¶meters); - string GenotypeStringFromAlleles(std::vector &allowedGenotypes, bool refAlleleFound); - bool AllowedGenotypesFromSummary(std::vector &allowedGenotypes); - string GenotypeFromStatus(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); - void SpecializedFilterFromLatentVariables(vcf::Variant &candidate_variant, const float bias_radius, int _allele, const string &sample_name); - void SpecializedFilterFromHypothesisBias(vcf::Variant &candidate_variant, AlleleIdentity allele_identity, const float deletion_bias, const float insertion_bias, int _allele, const string &sample_name); - void FilterAlleleHypothesisBias(vcf::Variant &candidate_variant, float ref_bias, float var_bias, float threshold_bias, int _allele, const string &sample_name); - void FilterOnSpecialTags(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, const vector& variant_specific_params, const string &sample_name); - void FilterOnStringency(vcf::Variant &candidate_variant, const float data_quality_stringency, int _check_allele_index, const string &sample_name); - void FilterOnPositionBias(const string& allele, int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const VariantSpecificParams& variant_specific_params); - void FilterOnLod(const string& allele, int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const BasicFilters &basic_filter, bool is_reference_call); - void FilterBlackList(const vector& variant_specific_params); - void FilterSSE(vcf::Variant &candidate_variant, const ClassifyFilters &filter_variant, const vector& variant_specific_params, const string &sample_name); -}; -void FilterByBasicThresholds(stringstream &s, int i_alt, MultiBook &m_summary_stats, - VariantOutputInfo &l_summary_info, - const BasicFilters &basic_filter, float tune_xbias, float tune_bias, bool is_reference_call); - -void AutoFailTheCandidate(vcf::Variant &candidate_variant, bool use_position_bias, const string &sample_name); -float FreqThresholdByType(AlleleIdentity &variant_identity, const ControlCallAndFilters &my_controls, const VariantSpecificParams& variant_specific_params); -void DetectSSEForNoCall(AlleleIdentity &var_identity, float sseProbThreshold, float minRatioReadsOnNonErrorStrand, float relative_safety_level, vcf::Variant &candidate_variant, unsigned _altAlleIndex); -void SetQualityByDepth(vcf::Variant &candidate_variant, map& variant_quality, const string &sample_name); - -#endif // DECISIONTREEDATA_H - +} /* 0) A variant entry accumulates all relevant data before being filtered: @@ -1105,7 +987,7 @@ void SetQualityByDepth(vcf::Variant &candidate_variant, map& vari 3y) the QUAL score is below the min-variant-score 3a) if coverage (ref+allele) is too low in total (low quality of data) 3b) if coverage (ref+allele) is insufficient on either strand (low quality of data) -3c) if strand bias is triggered for this allele vs reference (model violation) +3c) if not a ref call and strand bias is triggered for this allele vs reference (model violation) 3cc) if the strand-beta-bias is triggered for this allele vs reference (model violation) 3d) The allele fails STRINGENCY against reference (general signal/noise) 3e) The allele has a PREDICTIONSHIFT (systematic mismatch between predictions and measurements in basecalling) @@ -1115,14 +997,13 @@ void SetQualityByDepth(vcf::Variant &candidate_variant, map& vari */ void DecisionTreeData::DecisionTreeOutputToVariant(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, int sample_index) { - string sample_name = ""; - if (sample_index >= 0) {sample_name = candidate_variant.variant.sampleNames[sample_index];} + string sample_name = sample_index >= 0? candidate_variant.variant.sampleNames[sample_index] : ""; AggregateFilterInformation(candidate_variant.variant, candidate_variant.variant_specific_params, parameters, sample_name); // step 0 above - GenotypeFromEvaluator(candidate_variant.variant, parameters, sample_name); // step 0 // add a derived tag from the QUAL field and the depth counts SetQualityByDepth(candidate_variant.variant, variant_quality, sample_name); - + DeterminePossiblePolyploidyAlleles(candidate_variant.variant, parameters); // no actual filters should be filled in yet, just all the information needed for filtering FillInFiltersAtEnd(candidate_variant, parameters, sample_name); + } diff --git a/Analysis/VariantCaller/Filter/DecisionTreeData.h b/Analysis/VariantCaller/Filter/DecisionTreeData.h index 03843e9d..8b30b4e7 100644 --- a/Analysis/VariantCaller/Filter/DecisionTreeData.h +++ b/Analysis/VariantCaller/Filter/DecisionTreeData.h @@ -43,15 +43,15 @@ class DecisionTreeData { public: vcf::Variant * variant; //!< VCF record of this variant position - vector allele_identity_vector; //!< Detailed information for each candidate allele - vector info_fields; //!< Additional information to be printed out in vcf FR tag + vector* allele_identity_vector; //!< Detailed information for each candidate allele + vector* info_fields; //!< Additional information to be printed out in vcf FR tag MultiBook all_summary_stats; vector summary_info_vector; vector filteredAllelesIndex; - + vector is_possible_polyploidy_allele; map variant_quality; @@ -59,9 +59,10 @@ class DecisionTreeData { bool best_allele_set; int best_allele_index; - bool isBestAlleleSNP; + bool isBestAlleleVeryReal; bool reference_genotype; - + bool is_ppa_same_as_gt; + bool use_molecular_tag; EvaluatedGenotype eval_genotype; float tune_xbias; // not tuned, removed from filters @@ -69,12 +70,15 @@ class DecisionTreeData { DecisionTreeData(vcf::Variant &candidate_variant) /*: multi_allele(candidate_variant)*/ { variant = &candidate_variant; + allele_identity_vector = NULL; + info_fields = NULL; best_allele_set = false; best_allele_index = 0; best_variant_filtered=false; - isBestAlleleSNP = false; + isBestAlleleVeryReal = false; reference_genotype = false; - + is_ppa_same_as_gt = true; + use_molecular_tag = false; tune_xbias = 0.005f; // tune calculation of chi-square bias = proportioinal variance by frequency tune_sbias = 0.5f; // safety factor for small allele counts for transformed strand bias @@ -88,8 +92,10 @@ class DecisionTreeData { void AccumulateFilteredAlleles(); - void BestSNPsSuppressInDels(bool heal_snps); + void BestVariantSuppressInDels(bool heal_snps); void FindBestAlleleIdentity(); + void FindBestAlleleIdentity(bool use_fd_param); + void FindBestAlleleByScore(); void GenotypeFromBestAlleleIndex(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); @@ -99,8 +105,7 @@ class DecisionTreeData { void BestAlleleFilterMyCandidate(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); void GenotypeAlleleFilterMyCandidate(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); - void SimplifySNPsIfNeeded(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); - + void CleanupCandidatesIfNeeded(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, const string &sample_name); bool SetGenotype(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, float gt_quality); void DecisionTreeOutputToVariant(VariantCandidate &candidate_variant, const ExtendParameters ¶meters, int sample_index); @@ -108,34 +113,35 @@ class DecisionTreeData { void AggregateFilterInformation(vcf::Variant &candidate_variant, const vector& variant_specific_params, const ExtendParameters ¶meters, const string &sample_name); void FillInFiltersAtEnd(VariantCandidate &candidate_variant,const ExtendParameters ¶meters, const string &sample_name); + void SetupFromMultiAllele(vector* const allele_identity_vect_ptr, vector* const info_fields_ptr); - - void SetupFromMultiAllele(const EnsembleEval &my_ensemble); void AddStrandBiasTags(vcf::Variant &candidate_variant, const string &sample_name); void AddPositionBiasTags(vcf::Variant &candidate_variant, const string &sample_name); - void AddLodTags(vcf::Variant &candidate_variant, const string &sample_name, const ExtendParameters ¶meters); + void AddLodTags(vcf::Variant &candidate_variant, const string &sample_name, const ControlCallAndFilters &my_control); void AddCountInformationTags(vcf::Variant &candidate_variant, const string &sampleName, const ExtendParameters ¶meters); string GenotypeStringFromAlleles(std::vector &allowedGenotypes, bool refAlleleFound); bool AllowedGenotypesFromSummary(std::vector &allowedGenotypes); string GenotypeFromStatus(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); void SpecializedFilterFromLatentVariables(vcf::Variant &candidate_variant, const float bias_radius, int _allele, const string &sample_name); - void SpecializedFilterFromHypothesisBias(vcf::Variant &candidate_variant, AlleleIdentity allele_identity, const float deletion_bias, const float insertion_bias, int _allele, const string &sample_name); + void SpecializedFilterFromHypothesisBias(vcf::Variant &candidate_variant, AlleleIdentity allele_identity, const float deletion_bias, const float insertion_bias, int _allele, const string &sample_name, bool use_fd_param); + void FilterByBasicThresholds(int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, + const BasicFilters &basic_filter, float tune_xbias, float tune_bias, const VariantSpecificParams& variant_specific_params); + void FilterAlleleHypothesisBias(vcf::Variant &candidate_variant, float ref_bias, float var_bias, float threshold_bias, int _allele, const string &sample_name); void FilterOnSpecialTags(vcf::Variant &candidate_variant, const ExtendParameters ¶meters, const vector& variant_specific_params, const string &sample_name); void FilterOnStringency(vcf::Variant &candidate_variant, const float data_quality_stringency, int _check_allele_index, const string &sample_name); - void FilterOnPositionBias(const string& allele, int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const VariantSpecificParams& variant_specific_params); - void FilterOnLod(const string& allele, int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const BasicFilters &basic_filter, bool is_reference_call); + void FilterOnPositionBias(int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const VariantSpecificParams& variant_specific_params); + void FilterOnLod(int i_alt, MultiBook &m_summary_stats, VariantOutputInfo &l_summary_info, const ControlCallAndFilters &my_filters, const BasicFilters &basic_filter); + void FilterBlackList(const vector& variant_specific_params); - void FilterSSE(vcf::Variant &candidate_variant, const ClassifyFilters &filter_variant, const vector& variant_specific_params, const string &sample_name); + void FilterSSE(vcf::Variant &candidate_variant, const ControlCallAndFilters &my_control, const vector& variant_specific_params, const string &sample_name); + void DeterminePossiblePolyploidyAlleles(vcf::Variant &candidate_variant, const ExtendParameters ¶meters); }; -void FilterByBasicThresholds(stringstream &s, int i_alt, MultiBook &m_summary_stats, - VariantOutputInfo &l_summary_info, - const BasicFilters &basic_filter, float tune_xbias, float tune_bias, bool is_reference_call); -void AutoFailTheCandidate(vcf::Variant &candidate_variant, bool use_position_bias, const string &sample_name, bool use_molecular_tag); -float FreqThresholdByType(AlleleIdentity &variant_identity, const ControlCallAndFilters &my_controls, const VariantSpecificParams& variant_specific_params); + +void AutoFailTheCandidate(vcf::Variant &candidate_variant, bool use_position_bias, const string &sample_name, bool use_molecular_tag, string my_reason); void DetectSSEForNoCall(AlleleIdentity &var_identity, float sseProbThreshold, float minRatioReadsOnNonErrorStrand, float relative_safety_level, vcf::Variant &candidate_variant, unsigned _altAlleIndex); void SetQualityByDepth(vcf::Variant &candidate_variant, map& variant_quality, const string &sample_name); -void RemoveVcfInfo(vcf::Variant &variant, const vector &info_fields, const string &sample_name, int sample_index); +void RemoveVcfTagAndInfo(vcf::Variant &variant, const vector &info_fields, const string &sample_name, int sample_index); #endif // DECISIONTREEDATA_H diff --git a/Analysis/VariantCaller/Filter/VariantAssist.h b/Analysis/VariantCaller/Filter/VariantAssist.h index 501d7b6b..5bc961e2 100644 --- a/Analysis/VariantCaller/Filter/VariantAssist.h +++ b/Analysis/VariantCaller/Filter/VariantAssist.h @@ -49,6 +49,8 @@ class MultiBook { // alleles = 0 (ref), 1,2,... alts vector< vector > my_book; int invalid_reads; + vector tag_similar_counts; // counts for similar molecular tags + vector lod; // limit of detection for tag seq MultiBook(); void Allocate(int num_hyp); diff --git a/Analysis/VariantCaller/HandleVariant.cpp b/Analysis/VariantCaller/HandleVariant.cpp index 6cb48a4e..4ddb38ea 100644 --- a/Analysis/VariantCaller/HandleVariant.cpp +++ b/Analysis/VariantCaller/HandleVariant.cpp @@ -6,79 +6,13 @@ #include "HandleVariant.h" #include "DecisionTreeData.h" - - -void EnsembleEval::SpliceAllelesIntoReads(PersistingThreadObjects &thread_objects, const InputStructures &global_context, - const ExtendParameters ¶meters, const ReferenceReader &ref_reader, int chr_idx) -{ - bool changed_alignment; - unsigned int num_valid_reads = 0; - unsigned int num_realigned = 0; - int num_hyp_no_null = allele_identity_vector.size()+1; // num alleles +1 for ref - - // generate null+ref+nr.alt hypotheses per read in the case of do_multiallele_eval - allele_eval.total_theory.my_hypotheses.resize(read_stack.size()); - - for (unsigned int i_read = 0; i_read < allele_eval.total_theory.my_hypotheses.size(); i_read++) { - // --- New splicing function --- - allele_eval.total_theory.my_hypotheses[i_read].success = - SpliceVariantHypotheses(*read_stack[i_read], - *this, - seq_context, - thread_objects, - allele_eval.total_theory.my_hypotheses[i_read].splice_start_flow, - allele_eval.total_theory.my_hypotheses[i_read].splice_end_flow, - allele_eval.total_theory.my_hypotheses[i_read].instance_of_read_by_state, - allele_eval.total_theory.my_hypotheses[i_read].same_as_null_hypothesis, - changed_alignment, - global_context, - ref_reader, chr_idx); - - if (allele_eval.total_theory.my_hypotheses[i_read].success){ - num_valid_reads++; - if (changed_alignment) - num_realigned++; - } - - // if we need to compare likelihoods across multiple possibilities - if (num_hyp_no_null > 2) - allele_eval.total_theory.my_hypotheses[i_read].use_correlated_likelihood = false; - } - - // Check how many reads had their alignment modified - std::ostringstream my_info; - my_info.precision(4); - if (doRealignment and num_valid_reads>0){ - float frac_realigned = (float)num_realigned / (float)num_valid_reads; - // And re-do splicing without realignment if we exceed the threshold - if (frac_realigned > parameters.my_controls.filter_variant.realignment_threshold){ - my_info << "SKIPREALIGNx" << frac_realigned; - doRealignment = false; - for (unsigned int i_read = 0; i_read < allele_eval.total_theory.my_hypotheses.size(); i_read++) { - allele_eval.total_theory.my_hypotheses[i_read].success = - SpliceVariantHypotheses(*read_stack[i_read], - *this, - seq_context, - thread_objects, - allele_eval.total_theory.my_hypotheses[i_read].splice_start_flow, - allele_eval.total_theory.my_hypotheses[i_read].splice_end_flow, - allele_eval.total_theory.my_hypotheses[i_read].instance_of_read_by_state, - allele_eval.total_theory.my_hypotheses[i_read].same_as_null_hypothesis, - changed_alignment, - global_context, - ref_reader, chr_idx); - } - } - else { - my_info << "REALIGNEDx" << frac_realigned; - } - info_fields.push_back(my_info.str()); - } - -} - - - +#include "StackEngine.h" +#include "ShortStack.h" +#include "MiscUtil.h" +#include "ExtendedReadInfo.h" +#include "ClassifyVariant.h" +#include "DiagnosticJson.h" +#include "ExtendParameters.h" void SummarizeInfoFieldsFromEnsemble(EnsembleEval &my_ensemble, vcf::Variant &candidate_variant, int _cur_allele_index, const string &sample_name) { @@ -112,601 +46,370 @@ void SummarizeInfoFieldsFromEnsemble(EnsembleEval &my_ensemble, vcf::Variant &ca candidate_variant.info["VARB"].push_back(convertToString(var_bias)); } -// The structure InferenceResult is used for void MultiMinAlleleFreq(...) only. -// qual, gt, gq are the QUAL, GT, GQ computed for min_allele_freq, respectively -struct InferenceResult{ - float min_allele_freq; - float qual; - string gt; - int gq; - bool operator<(const InferenceResult &rhs) const { return min_allele_freq < rhs.min_allele_freq; } // use the operator "<" for "std::sort" - bool operator==(const float &rhs) const { return min_allele_freq == rhs; } // use the operator "==" for "std::find" -}; - -void MultiMinAlleleFreq(EnsembleEval &my_ensemble, VariantCandidate &candidate_variant, int sample_index, ProgramControlSettings &program_flow, int max_detail_level){ - string sample_name = ""; - if(sample_index >= 0) { - sample_name = candidate_variant.variant.sampleNames[sample_index]; - } - - // info tags needed for multi-min-allele-freq - vector tags_for_multi_min_allele_freq = {"MUAF", "MUQUAL", "MUGQ", "MUGT", - "SMAF", "SMQUAL", "SMGQ", "SMGT", - "MMAF", "MMQUAL", "MMGQ", "MMGT", - "IMAF", "IMQUAL", "IMGQ", "IMGT", - "HMAF", "HMQUAL", "HMGQ", "HMGT"}; - // Add the info tags for multi-min-allele-freq if we have not added yet. - for(unsigned int i_tag = 0; i_tag < tags_for_multi_min_allele_freq.size(); ++i_tag){ - vector::iterator it_format = find(candidate_variant.variant.format.begin(), - candidate_variant.variant.format.end(), - tags_for_multi_min_allele_freq[i_tag]); - if(it_format == candidate_variant.variant.format.end()){ - candidate_variant.variant.format.push_back(tags_for_multi_min_allele_freq[i_tag]); - } - } - - // inference_results_union stores the inference results for the union of the min-allele-freq of all available variant types - vector inference_results_union; - bool is_snp_done = false; - bool is_mnp_done = false; - bool is_hotspot_done = false; - bool is_indel_done = false; - - for(unsigned int alt_allele_index = 0; alt_allele_index < my_ensemble.allele_identity_vector.size(); ++alt_allele_index){ - // ptr_maf_vec = the pointer to the multi_min_allele_freq vector of which type of variant for this allele - vector *ptr_maf_vec = &(program_flow.snp_multi_min_allele_freq); - string type_prefix = "S"; - - // Note that no override here! - if(my_ensemble.allele_identity_vector[alt_allele_index].status.isHotSpot){ - if(is_hotspot_done){ - continue; - } - ptr_maf_vec = &(program_flow.hotspot_multi_min_allele_freq); - type_prefix = "H"; - } - else if(my_ensemble.allele_identity_vector[alt_allele_index].ActAsSNP()){ - if(is_snp_done){ - continue; - } - ptr_maf_vec = &(program_flow.snp_multi_min_allele_freq); - type_prefix = "S"; - } - else if(my_ensemble.allele_identity_vector[alt_allele_index].ActAsMNP()){ - if(is_mnp_done){ - continue; - } - ptr_maf_vec = &(program_flow.mnp_multi_min_allele_freq); - type_prefix = "M"; - } - else if(my_ensemble.allele_identity_vector[alt_allele_index].ActAsHPIndel()){ - if(is_indel_done){ - continue; - } - ptr_maf_vec = &(program_flow.indel_multi_min_allele_freq); - type_prefix = "I"; - }else{ - if(is_snp_done){ - continue; - } - ptr_maf_vec = &(program_flow.snp_multi_min_allele_freq); - string type_prefix = "S"; - } - - for(unsigned int i_freq = 0; i_freq < ptr_maf_vec->size(); ++i_freq){ - float loc_min_allele_freq = ptr_maf_vec->at(i_freq); - vector::iterator it = find(inference_results_union.begin(), inference_results_union.end(), loc_min_allele_freq); - float loc_qual; - int loc_gq; - string loc_gt; - - if(it == inference_results_union.end()){ // This the first time we get loc_min_allele_freq - int genotype_call; - float evaluated_genotype_quality; - // Let's do the inference for the given loc_min_allele_freq - my_ensemble.allele_eval.CallGermline(loc_min_allele_freq, genotype_call, evaluated_genotype_quality, loc_qual); - - vector genotype_component = {my_ensemble.diploid_choice[0], my_ensemble.diploid_choice[1]}; // starts with het var - - if(genotype_call == 2){ //hom var - genotype_component[0] = my_ensemble.diploid_choice[1]; - } - else if(genotype_call == 0){ //hom ref - genotype_component[1] = my_ensemble.diploid_choice[0]; - } - - loc_gt = convertToString(genotype_component[0]) + "/" + convertToString(genotype_component[1]); - loc_gq = int(round(evaluated_genotype_quality)); // genotype quality is rounded as an integer. - // append inference_results_union - inference_results_union.push_back({loc_min_allele_freq, loc_qual, loc_gt, loc_gq}); - } - else{ // We've seen loc_min_allele_freq before. Don't need to call CallGermline(...) again. - loc_qual = it->qual; - loc_gq = it->gq; - loc_gt = it->gt; - } - - // write the info tag for the corresponding var type - candidate_variant.variant.samples[sample_name][type_prefix + "MUAF"].push_back(convertToString(loc_min_allele_freq)); - candidate_variant.variant.samples[sample_name][type_prefix + "MUQUAL"].push_back(convertToString(loc_qual)); - candidate_variant.variant.samples[sample_name][type_prefix + "MUGT"].push_back(loc_gt); - candidate_variant.variant.samples[sample_name][type_prefix + "MUGQ"].push_back(convertToString(loc_gq)); - - switch(type_prefix[0]){ - case 'S': - is_snp_done = true; - break; - case 'M': - is_mnp_done = true; - break; - case 'I': - is_indel_done = true; - break; - case 'H': - is_hotspot_done = true; - break; - } - } - } - // sort inference_results_union according to min_allele_freq in the ascending order - sort(inference_results_union.begin(), inference_results_union.end()); - // write the info tag for the union of min_allele_freq of the var types - for(unsigned int i_freq = 0; i_freq < inference_results_union.size(); ++i_freq){ - candidate_variant.variant.samples[sample_name]["MUAF"].push_back(convertToString(inference_results_union[i_freq].min_allele_freq)); - candidate_variant.variant.samples[sample_name]["MUQUAL"].push_back(convertToString(inference_results_union[i_freq].qual)); - candidate_variant.variant.samples[sample_name]["MUGT"].push_back(inference_results_union[i_freq].gt); - candidate_variant.variant.samples[sample_name]["MUGQ"].push_back(convertToString(inference_results_union[i_freq].gq)); +void EnsembleEval::MultiMinAlleleFreq(const vector& multi_min_allele_freq){ + variant->info["MUQUAL"].clear(); + variant->info["MUGT"].clear(); + variant->info["MUGQ"].clear(); + for (vector::const_iterator maf_it = multi_min_allele_freq.begin(); maf_it != multi_min_allele_freq.end(); ++maf_it){ + float loc_qual = 0.0f; + int loc_gq = 0; + float evaluated_genotype_quality = 0.0f; + int quality_type = 0; + string loc_gt; + // Let's do the inference for min-allele-freq = *maf_it + vector genotype_component; + allele_eval.CallByIntegral(*maf_it, *maf_it, genotype_component, evaluated_genotype_quality, loc_qual, quality_type); + loc_gt = convertToString(genotype_component[0]) + "/" + convertToString(genotype_component[1]); + loc_gq = int(round(evaluated_genotype_quality)); // genotype quality is rounded as an integer. + variant->info["MUQUAL"].push_back(convertToString(loc_qual)); + variant->info["MUGT"].push_back(loc_gt); + variant->info["MUGQ"].push_back(convertToString(loc_gq)); } } +// Override fd-nonsnp-min-var-coverage +void OverrideMinVarCov(int fd_nonsnp_min_var_cov, + const vector& allele_identity_vector, + const vector >& global_flow_disruptive_matrix, + vector& variant_specific_params) +{ + assert(allele_identity_vector.size() + 1 == global_flow_disruptive_matrix.size()); + if (fd_nonsnp_min_var_cov < 1) + return; + + for (unsigned int i_alt = 0; i_alt < allele_identity_vector.size(); ++i_alt){ + // Override min_var_coverage of the allele if all the following are satisfied + // a) Not override by hotspot + // b) The allele is flow-disrupted + // c) It is not a SNP and it is not a padding SNP + if ((not variant_specific_params[i_alt].min_var_coverage_override) + and (global_flow_disruptive_matrix[0][i_alt + 1] == 2) + and (not (allele_identity_vector[i_alt].status.isSNP or allele_identity_vector[i_alt].status.isPaddedSNP))){ + variant_specific_params[i_alt].min_var_coverage_override = true; + variant_specific_params[i_alt].min_var_coverage = fd_nonsnp_min_var_cov; + } + } +} void GlueOutputVariant(EnsembleEval &my_ensemble, VariantCandidate &candidate_variant, const ExtendParameters ¶meters, int _best_allele_index, int sample_index){ - string sample_name = ""; - if(sample_index >= 0) { - sample_name = candidate_variant.variant.sampleNames[sample_index]; - } - + string sample_name = (sample_index >= 0) ? candidate_variant.variant.sampleNames[sample_index] : ""; DecisionTreeData my_decision(*(my_ensemble.variant)); + my_decision.use_molecular_tag = my_ensemble.allele_eval.total_theory.GetIsMolecularTag(); my_decision.tune_sbias = parameters.my_controls.sbias_tune; - my_decision.SetupFromMultiAllele(my_ensemble); + my_decision.SetupFromMultiAllele(&(my_ensemble.allele_identity_vector), &(my_ensemble.info_fields)); - // pretend we can classify reads across multiple alleles - if(not my_ensemble.is_hard_classification_for_reads_done_){ - my_ensemble.ApproximateHardClassifierForReads(); - } - - if(my_ensemble.allele_eval.total_theory.GetIsMolecularTag()){ - if(not my_ensemble.is_hard_classification_for_families_done_){ - my_ensemble.ApproximateHardClassifierForFamilies(); - } - // I count the strand coverage by families, not reads. - // Note that family_id_ is the hard classification results for functional and non-functional families. - // And we classify non-functional families as outliers. - // This gives FXX = 1.0 - (# of functional families) / (# of families). - my_decision.all_summary_stats.AssignStrandToHardClassifiedReads(my_ensemble.family_strand_id_, my_ensemble.family_id_); - } - else{ - // non-mol-tag - my_decision.all_summary_stats.AssignStrandToHardClassifiedReads(my_ensemble.strand_id_, my_ensemble.read_id_); + if (my_ensemble.read_id_.empty()){ + cerr << "ERROR: Can't GlueOutputVariant with empty read id." << endl; + exit(-1); } - + my_decision.all_summary_stats.AssignStrandToHardClassifiedReads(my_ensemble.strand_id_, my_ensemble.read_id_); my_decision.all_summary_stats.AssignPositionFromEndToHardClassifiedReads(my_ensemble.read_id_, my_ensemble.dist_to_left_, my_ensemble.dist_to_right_); - float smallest_allele_freq = 1.0f; - for (unsigned int _alt_allele_index = 0; _alt_allele_index < my_decision.allele_identity_vector.size(); _alt_allele_index++) { - // for each alt allele, do my best - // thresholds here can vary by >type< of allele - float local_min_allele_freq = FreqThresholdByType(my_ensemble.allele_identity_vector[_alt_allele_index], parameters.my_controls, - candidate_variant.variant_specific_params[_alt_allele_index]); - - if (local_min_allele_freq < smallest_allele_freq){ - smallest_allele_freq = local_min_allele_freq; // choose least-restrictive amongst multialleles - } - - /* The following piece of code seems redundant. Perhaps due to historical reasons? - my_ensemble.ComputePosteriorGenotype(_alt_allele_index, local_min_allele_freq, - my_decision.summary_info_vector[_alt_allele_index].genotype_call, - my_decision.summary_info_vector[_alt_allele_index].gt_quality_score, - my_decision.summary_info_vector[_alt_allele_index].variant_qual_score); - */ - SummarizeInfoFieldsFromEnsemble(my_ensemble, *(my_ensemble.variant), _alt_allele_index, sample_name); + for (unsigned int _alt_allele_index = 0; _alt_allele_index < my_decision.allele_identity_vector->size(); _alt_allele_index++) { + SummarizeInfoFieldsFromEnsemble(my_ensemble, *(my_ensemble.variant), _alt_allele_index, sample_name); } + if (my_ensemble.allele_eval.total_theory.GetIsMolecularTag()){ + my_ensemble.variant->info["TGSM"].clear(); + for (unsigned int _alt_allele_index = 0; _alt_allele_index < my_decision.allele_identity_vector->size(); _alt_allele_index++) { + if (my_ensemble.tag_similar_counts_.empty()){ + my_ensemble.variant->info["TGSM"].push_back("."); + }else{ + my_ensemble.variant->info["TGSM"].push_back(convertToString(my_ensemble.tag_similar_counts_[_alt_allele_index])); + } + } + my_decision.all_summary_stats.tag_similar_counts = my_ensemble.tag_similar_counts_; + } + my_decision.best_allele_index = _best_allele_index; my_decision.best_allele_set = true; + my_decision.is_possible_polyploidy_allele = my_ensemble.is_possible_polyploidy_allele; - // tell the evaluator to do a genotype - // choose a diploid genotype - // return it and set it so that decision tree cannot override + float af_cutoff_rej = 1.0f; + float af_cutoff_gt = 1.0f; + my_ensemble.ServeAfCutoff(parameters.my_controls, candidate_variant.variant_specific_params, af_cutoff_rej, af_cutoff_gt); - //@TODO: fix this frequency to be sensible - float local_min_allele_freq = smallest_allele_freq; // must choose a qual score relative to some frequency + if (my_ensemble.DEBUG > 0){ + cout << "+ Evaluating the variant (" << PrintVariant(*my_ensemble.variant) << "):" <size(); ++i_allele){ my_decision.summary_info_vector[i_allele].variant_qual_score = my_decision.eval_genotype.evaluated_variant_quality; my_decision.summary_info_vector[i_allele].gt_quality_score = my_decision.eval_genotype.evaluated_genotype_quality; } - // now that all the data has been gathered describing the variant, combine to produce the output - my_decision.DecisionTreeOutputToVariant(candidate_variant, parameters, sample_index); -} - -// Read and process records appropriate for this variant; positions are zero based -void EnsembleEval::StackUpOneVariant(const ExtendParameters ¶meters, const PositionInProgress& bam_position, int sample_index) -{ - - // Initialize random number generator for each stack -> ensure reproducibility - RandSchrange RandGen(parameters.my_controls.RandSeed); - - read_stack.clear(); // reset the stack - read_stack.reserve(parameters.my_controls.downSampleCoverage); - int read_counter = 0; - - for (Alignment* rai = bam_position.begin; rai != bam_position.end; rai = rai->next) { - - // Check global conditions to stop reading in more alignments - if (rai->original_position > multiallele_window_start) - break; - - if (rai->alignment.Position > multiallele_window_start) - continue; - - if (rai->filtered) - continue; - - if (rai->alignment.GetEndPosition() < multiallele_window_end) - continue; + // Override fd-nonsnp-min-var-coverage here + OverrideMinVarCov(parameters.my_controls.fd_nonsnp_min_var_cov, + my_ensemble.allele_identity_vector, + my_ensemble.global_flow_disruptive_matrix, + candidate_variant.variant_specific_params); - if (parameters.multisample) { - if (rai->sample_index != sample_index) { - continue; - } + if (parameters.my_controls.disable_filters){ + my_ensemble.GatherInfoForOfflineFiltering(parameters.my_controls, _best_allele_index); } - // Reservoir Sampling - if (read_stack.size() < (unsigned int)parameters.my_controls.downSampleCoverage) { - read_counter++; - read_stack.push_back(rai); - } else { - read_counter++; - // produces a uniformly distributed test_position between [0, read_counter-1] - unsigned int test_position = ((double)RandGen.Rand() / ((double)RandGen.RandMax + 1.0)) * (double)read_counter; - if (test_position < (unsigned int)parameters.my_controls.downSampleCoverage) - read_stack[test_position] = rai; - } - } + // now that all the data has been gathered describing the variant, combine to produce the output + my_decision.DecisionTreeOutputToVariant(candidate_variant, parameters, sample_index); } -// The class is simply used for random_shuffle -class MyRandSchrange : private RandSchrange{ -public: - MyRandSchrange(int seed = 1){SetSeed(seed);} ; - int operator()(int upper_lim){return Rand() % upper_lim;}; // return a random number between 0 and upper_lim-1 -}; - -// We do down sampling based on the following rules when we have molecular tag. -// Rule 1: Try to get as many functional families as possible after down sampling -// Rule 2: Try to equalize the sizes of functional families after down sampling. -// Rule 3: If we need to drop a functional family, drop the functional family with smaller family size. -void EnsembleEval::DoDownSamplingMolTag(const ExtendParameters ¶meters, vector< vector< MolecularFamily > > &my_molecular_families, - unsigned int num_reads_available, unsigned int num_func_fam, int strand_key) -{ - MyRandSchrange my_rand_schrange(parameters.my_controls.RandSeed); // The random number generator that we use to guarantee reproducibility. - unsigned int read_counter = 0; - unsigned int min_fam_size = (unsigned int) parameters.tag_trimmer_parameters.min_family_size; - unsigned int downSampleCoverage = (unsigned int) parameters.my_controls.downSampleCoverage; - unsigned int num_hyp = allele_identity_vector.size() + 2; - read_stack.clear(); // reset the stack - read_stack.reserve(downSampleCoverage); - allele_eval.total_theory.my_eval_families.clear(); - - // We can keep all the reads in all functional families :D - if(num_reads_available <= downSampleCoverage){ - allele_eval.total_theory.my_eval_families.reserve(num_reads_available); - for(vector< MolecularFamily >::iterator family_it = my_molecular_families[strand_key].begin(); - family_it != my_molecular_families[strand_key].end(); ++family_it){ - if(family_it->is_func_family_temp){ - allele_eval.total_theory.my_eval_families.push_back(EvalFamily(family_it->family_barcode, family_it->strand_key)); - allele_eval.total_theory.my_eval_families.back().InitializeEvalFamily(num_hyp); - for(unsigned int i_read = 0; i_read < family_it->family_members_temp.size(); ++i_read){ - read_stack.push_back(family_it->family_members_temp[i_read]); - allele_eval.total_theory.my_eval_families.back().AddNewMember(read_counter); - ++read_counter; - } - } - } - return; - } - - unsigned int num_of_func_fam_after_down_sampling = 0; - unsigned int min_fam_size_after_down_sampling = 0; - // The size of this vector is < 3, i.e., we only need to do random shuffle for the at most two different sizes of families. - // There are two cases that we need to randomly pick families. - // (random_shuffle case 1): We can not preserve the functional families of size = min_fam_size_after_down_sampling. - // (random_shuffle case 2): Some families of the same size have one more read than others. - vector size_of_fam_need_random_shuffle(0); - - // sort the families by the family size in the ascending order - sort(my_molecular_families[strand_key].begin(), my_molecular_families[strand_key].end()); - - // We need to give up some reads but we can keep all functional families :) - if(downSampleCoverage >= min_fam_size * num_func_fam){ - num_of_func_fam_after_down_sampling = num_func_fam; - unsigned int i_fam = my_molecular_families[strand_key].size() - num_of_func_fam_after_down_sampling; - min_fam_size_after_down_sampling = my_molecular_families[strand_key][i_fam].family_members_temp.size(); - } - // We need to give up some functional families... :( - else{ - num_of_func_fam_after_down_sampling = downSampleCoverage / min_fam_size; - unsigned int i_fam = my_molecular_families[strand_key].size() - num_of_func_fam_after_down_sampling; - min_fam_size_after_down_sampling = my_molecular_families[strand_key][i_fam].family_members_temp.size(); - if(i_fam < my_molecular_families[strand_key].size() - 1){ - if(min_fam_size_after_down_sampling == my_molecular_families[strand_key][i_fam + 1].family_members_temp.size()){ - // (random_shuffle case 1): - // We can't preserve all the families of size my_molecular_families[strand_key][i_fam].family_members_temp.size(). - // For fairness, we will to randomly pick some of families with this size. - size_of_fam_need_random_shuffle.push_back(my_molecular_families[strand_key][i_fam].family_members_temp.size()); - } - } - } - allele_eval.total_theory.my_eval_families.reserve(num_of_func_fam_after_down_sampling); - - // Count how many reads we want to pick in each family. - unsigned int reads_remaining = downSampleCoverage; - vector num_reads_picked_in_fam; - // num_reads_picked_in_fam[i] is the number of reads we picked for the family my_molecular_families[strand_key][i_fam] - // where i_fam = my_molecular_families[strand_key].size() - i - 1 - num_reads_picked_in_fam.assign(num_of_func_fam_after_down_sampling, 0); - - unsigned int break_at_i_fam = 0; - while(reads_remaining > 0){ - for(unsigned int i = 0; i < num_of_func_fam_after_down_sampling; ++i){ - unsigned int i_fam = my_molecular_families[strand_key].size() - i - 1; - // Note that my_molecular_families[strand_key][i_fam] should be functional since we sort my_molecular_families[strand_key] - // All the reads in the family are picked up, and of course for the next family since we sort my_molecular_families[strand_key] - // So let's break the for loop and starts with the family with the largest size. - if(num_reads_picked_in_fam[i] >= my_molecular_families[strand_key][i_fam].family_members_temp.size()){ - break; - } - - // We take one more read from the family - ++num_reads_picked_in_fam[i]; - --reads_remaining; - - // Sorry, we can't pick up more reads. We have reached the down sampling limit. - if(reads_remaining <= 0){ - break_at_i_fam = i_fam; - break; - } - } - } - - if(break_at_i_fam < my_molecular_families[strand_key].size() - 1){ - if(my_molecular_families[strand_key][break_at_i_fam].family_members_temp.size() == my_molecular_families[strand_key][break_at_i_fam + 1].family_members_temp.size()){ - // (random_shuffle case 2): - // Some of the families of size my_molecular_families[strand_key][break_at_i_fam].family_members_temp.size() get one more read after down sampling. - // For fairness, we will do random shuffle for the families of this size. - size_of_fam_need_random_shuffle.push_back(my_molecular_families[strand_key][break_at_i_fam].family_members_temp.size()); - } - } - - // sort size_of_fam_need_random_shuffle in increasing order and remove repeats - if(size_of_fam_need_random_shuffle.size() == 2){ - if(size_of_fam_need_random_shuffle[0] > size_of_fam_need_random_shuffle[1]){ - swap(size_of_fam_need_random_shuffle[0], size_of_fam_need_random_shuffle[1]); - } - else if(size_of_fam_need_random_shuffle[0] == size_of_fam_need_random_shuffle[1]){ - size_of_fam_need_random_shuffle.resize(1); - } +void RemoveVcfFormat(vcf::Variant &variant, const vector &keys){ + for(vector::const_iterator key_it = keys.begin(); key_it != keys.end(); ++key_it){ + variant.format.erase(std::remove(variant.format.begin(), variant.format.end(), *key_it), variant.format.end()); } +} - if(size_of_fam_need_random_shuffle.size() > 0){ - // Now we random shuffle the orders of the families of the same size to give more randomness if we can't pick all among them. - // Note that my_molecular_families[strand_key] will remain sorted after random_shuffle. - vector< MolecularFamily >::iterator current_fam_size_begin_it = my_molecular_families[strand_key].begin(); - for(vector< MolecularFamily >::iterator family_it = my_molecular_families[strand_key].begin(); - family_it != my_molecular_families[strand_key].end(); ++family_it){ - if(family_it->family_members_temp.size() != current_fam_size_begin_it->family_members_temp.size()){ - // we've got all families of size family_it->family_members_temp.size() - if(current_fam_size_begin_it->family_members_temp.size() == size_of_fam_need_random_shuffle[0]){ - // random shuffle the families of the size size_of_fam_need_random_shuffle[0] - random_shuffle(current_fam_size_begin_it, family_it, my_rand_schrange); - if(size_of_fam_need_random_shuffle.size() == 1){ - // We've done random shuffle for all families of the sizes needed - size_of_fam_need_random_shuffle.clear(); - break; - } - else{ - // size_of_fam_need_random_shuffle[0] is done. Will do random_shuffle for size_of_fam_need_random_shuffle[1] - // Let's do it like a sliding window. Note that size_of_fam_need_random_shuffle must be sorted as well!!! - size_of_fam_need_random_shuffle.assign(1, size_of_fam_need_random_shuffle[1]); - } - } - current_fam_size_begin_it = family_it; - } - } - // Don't forget to random shuffle the families if size_of_fam_need_random_shuffle[0] equals the maximum family size - if(size_of_fam_need_random_shuffle.size() > 0){ - random_shuffle(current_fam_size_begin_it, my_molecular_families[strand_key].end(), my_rand_schrange); - } - } - for(unsigned int i = 0; i < num_of_func_fam_after_down_sampling; ++i){ - unsigned int i_fam = my_molecular_families[strand_key].size() - i - 1; - MolecularFamily *ptr_fam = &(my_molecular_families[strand_key][i_fam]); - // Add one more family - allele_eval.total_theory.my_eval_families.push_back(EvalFamily(ptr_fam->family_barcode, ptr_fam->strand_key)); - allele_eval.total_theory.my_eval_families.back().InitializeEvalFamily(num_hyp); - allele_eval.total_theory.my_eval_families.back().family_members.reserve(num_reads_picked_in_fam[i]); - random_shuffle(ptr_fam->family_members_temp.begin(), ptr_fam->family_members_temp.end(), my_rand_schrange); - // Pick up num_reads_picked_in_fam[i] reads from family i_fam. - for(unsigned int i_read = 0; i_read < num_reads_picked_in_fam[i]; ++i_read){ - // Add the read into read_stack - read_stack.push_back(ptr_fam->family_members_temp[i_read]); - // Add the read in to the family - allele_eval.total_theory.my_eval_families.back().AddNewMember(read_counter); - ++read_counter; - } - } +void DoStepsForNoData(VariantCandidate& candidate_variant, const string& sample_name, int sample_index, bool use_molecular_tag, string my_reason){ + //cerr << "Nonfatal: No reads found for " << candidate_variant.variant.sequenceName << "\t" << my_ensemble.multiallele_window_start << endl; + NullFilterReason(candidate_variant.variant, sample_name); + if (my_reason.empty()){ + my_reason = "NODATA"; + } + AddFilterReason(candidate_variant.variant, my_reason, sample_name); + SetFilteredStatus(candidate_variant.variant, true); + candidate_variant.variant.samples[sample_name]["GT"] = {"./."}; } -// Currently only take the reads on one strand -void EnsembleEval::StackUpOneVariantMolTag(const ExtendParameters ¶meters, vector< vector< MolecularFamily > > &my_molecular_families, int sample_index) + +// return 0: normal termination +// return 1: no data (empty read stack) +// return 2: no data (no valid functional families on read stack) +int EnsembleProcessOneVariant(PersistingThreadObjects &thread_objects, VariantCallerContext& vc, + VariantCandidate &candidate_variant, const PositionInProgress& bam_position, + vector< vector > &molecular_families, int sample_index) { - int strand_key = -1; - unsigned int min_fam_size = (unsigned int) parameters.tag_trimmer_parameters.min_family_size; - vector num_func_fam_by_strand = {0, 0}; - vector num_reads_available_by_strand = {0, 0}; - - for(unsigned int i_strand = 0; i_strand < my_molecular_families.size(); ++i_strand){ - for(vector< MolecularFamily >::iterator family_it = my_molecular_families[i_strand].begin(); - family_it != my_molecular_families[i_strand].end(); ++family_it){ - if(not family_it->SetFunctionality(min_fam_size)){ - continue; - } - family_it->family_members_temp.reserve(family_it->family_members.size()); - for(vector::iterator member_it = family_it->family_members.begin(); member_it != family_it->family_members.end(); ++member_it){ - - // Although we have done this in the function GenerateMyMolecularFamilies, do it again to make sure everything is right. - if ((*member_it)->filtered) - continue; - // Although we have done this in the function GenerateMyMolecularFamilies, do it again to make sure everything is right. - if(parameters.multisample) { - if ((*member_it)->sample_index != sample_index) { - continue; - } - } - - // Check global conditions to stop reading in more alignments - if ((*member_it)->original_position > multiallele_window_start - or (*member_it)->alignment.Position > multiallele_window_start - or (*member_it)->alignment.GetEndPosition() < multiallele_window_end) - continue; - - - // family_members_temp stores the reads which are not filtered out here - family_it->family_members_temp.push_back((*member_it)); - } + unsigned long t0 = clock(); + string sample_name = (sample_index >= 0)? candidate_variant.variant.sampleNames[sample_index] : ""; + const bool use_molecular_tag = vc.mol_tag_manager->tag_trimmer->HaveTags(); - // We may change the family size since some reads may be filtered out. - // Need to determine the functionality again! - if(family_it->family_members_temp.size() >= min_fam_size){ - family_it->is_func_family_temp = true; - // Count how many reads and functional families available for down sampling - num_reads_available_by_strand[i_strand] += family_it->family_members_temp.size(); - ++num_func_fam_by_strand[i_strand]; - } - else{ - family_it->is_func_family_temp = false; - } - } - } + if(vc.parameters->program_flow.DEBUG > 0 ){ + cout<< endl << "[tvc] Start EnsembleProcessOneVariant for (" << PrintVariant(candidate_variant.variant) << ")"<< endl << endl; + } - // For the current molecular barcoding scheme (bcprimer), the reads in each amplicom should be on on strand only. - // However, we sometimes get families on both strands, primarily due to false priming. - // Here I pick the strand that has more functional families - strand_key = num_func_fam_by_strand[0] > num_func_fam_by_strand[1] ? 0 : 1; + if (not use_molecular_tag){ + RemoveVcfFormat(candidate_variant.variant, {"MDP", "MAO", "MRO", "MAF"}); + } - // Do down-sampling - DoDownSamplingMolTag(parameters, my_molecular_families, num_reads_available_by_strand[strand_key], num_func_fam_by_strand[strand_key], strand_key); -} + EnsembleEval my_ensemble(candidate_variant.variant); -bool EnsembleProcessOneVariant(PersistingThreadObjects &thread_objects, VariantCallerContext& vc, - VariantCandidate &candidate_variant, const PositionInProgress& bam_position, - vector< vector< MolecularFamily > > &molecular_families, int sample_index) -{ - string sample_name = ""; - bool use_molecular_tag = vc.tag_trimmer->HaveTags(); - if (sample_index >= 0) {sample_name = candidate_variant.variant.sampleNames[sample_index];} + // Allele preparation + my_ensemble.SetupAllAlleles(*vc.parameters, *vc.global_context, *vc.ref_reader); + my_ensemble.FilterAllAlleles(vc.parameters->my_controls, candidate_variant.variant_specific_params); // put filtering here in case we want to skip below entries - int chr_idx = vc.ref_reader->chr_idx(candidate_variant.variant.sequenceName.c_str()); + // set parameters for the evaluator + my_ensemble.SetAndPropagateParameters(vc.parameters, use_molecular_tag, candidate_variant.variant_specific_params); - EnsembleEval my_ensemble(candidate_variant.variant); - my_ensemble.allele_eval.total_theory.SetIsMolecularTag(use_molecular_tag); - my_ensemble.SetupAllAlleles(*vc.parameters, *vc.global_context, *vc.ref_reader, chr_idx); - my_ensemble.FilterAllAlleles(vc.parameters->my_controls.filter_variant, candidate_variant.variant_specific_params); // put filtering here in case we want to skip below entries + if (vc.parameters->program_flow.DEBUG > 0){ + list >allele_groups; + CandidateExaminer my_examiner(&thread_objects, &vc); + my_examiner.SetupVariantCandidate(candidate_variant); + my_examiner.FindLookAheadEnd0(); + my_examiner.SplitCandidateVariant(allele_groups); + } // We read in one stack per multi-allele variant - if(use_molecular_tag){ + if (use_molecular_tag){ my_ensemble.StackUpOneVariantMolTag(*vc.parameters, molecular_families, sample_index); } else{ my_ensemble.StackUpOneVariant(*vc.parameters, bam_position, sample_index); } + + // No data if (my_ensemble.read_stack.empty()) { - //cerr << "Nonfatal: No reads found for " << candidate_variant.variant.sequenceName << "\t" << my_ensemble.multiallele_window_start << endl; - NullFilterReason(candidate_variant.variant, sample_name); - if (not use_molecular_tag) { - RemoveVcfInfo(candidate_variant.variant, vector({"MDP", "MRO", "MAO", "MAF"}), sample_name, sample_index); - } - string my_reason = "NODATA"; - AddFilterReason(candidate_variant.variant, my_reason, sample_name); - SetFilteredStatus(candidate_variant.variant, true); - candidate_variant.variant.samples[sample_name]["GT"].clear(); - candidate_variant.variant.samples[sample_name]["GT"].push_back("./."); - return false; + DoStepsForNoData(candidate_variant, sample_name, sample_index, use_molecular_tag, "NODATA"); + if(vc.parameters->program_flow.DEBUG > 0 ){ + cout<< "+ No data: empty read stack!" << endl << endl + << "[tvc] Complete EnsembleProcessOneVariant for ("<< PrintVariant(candidate_variant.variant) << "). Processing time = " << (double) (clock() - t0) / 1E6 << " sec." << endl << endl; + } + return 1; } - - // handle the unfortunate case in which we must try multiple alleles to be happy - // try only ref vs alt allele here - // leave ensemble in ref vs alt state - // glue in variants - my_ensemble.SpliceAllelesIntoReads(thread_objects, *vc.global_context, *vc.parameters, *vc.ref_reader, chr_idx); + my_ensemble.SpliceAllelesIntoReads(thread_objects, *vc.global_context, *vc.parameters, *vc.ref_reader); - my_ensemble.allele_eval.my_params = vc.parameters->my_eval_control; + // Calculate flow-disruptiveness in the read level + my_ensemble.FlowDisruptivenessInReadLevel(*vc.global_context); // fill in quantities derived from predictions - int num_hyp_no_null = my_ensemble.allele_identity_vector.size()+1; // num alleles +1 for ref - my_ensemble.allele_eval.InitForInference(thread_objects, my_ensemble.read_stack, *vc.global_context, num_hyp_no_null, my_ensemble.allele_identity_vector); - - if(use_molecular_tag){ - // Filter out outlier reads to make sure there is no outlier family! - my_ensemble.allele_eval.total_theory.OutlierFiltering(vc.parameters->my_eval_control.DataReliability(), false); - my_ensemble.allele_eval.total_theory.SetFuncionalityForFamilies(vc.parameters->tag_trimmer_parameters.min_family_size); + my_ensemble.allele_eval.InitForInference(thread_objects, my_ensemble.read_stack, *vc.global_context, my_ensemble.allele_identity_vector); + + // No valid function family + if (use_molecular_tag){ + if (my_ensemble.allele_eval.total_theory.GetNumFuncFamilies() == 0) { + DoStepsForNoData(candidate_variant, sample_name, sample_index, use_molecular_tag, "NOVALIDFUNCFAM"); + if (vc.parameters->program_flow.DEBUG > 0){ + cout << "+ No valid functional families on read stack!" << endl << endl + << "[tvc] Complete EnsembleProcessOneVariant for ("<< PrintVariant(candidate_variant.variant) << "). Processing time = " << (double) (clock() - t0) / 1E6 << " sec." << endl << endl; + } + return 2; + } } + // do inference my_ensemble.allele_eval.ExecuteInference(); + + // set fd in the read_stack level. + my_ensemble.FlowDisruptivenessInReadStackLevel(vc.parameters->my_controls.min_ratio_for_fd); + // now we're in the guaranteed state of best index - int best_allele = my_ensemble.DetectBestMultiAllelePair(); + vector semi_soft_allele_freq_est; + int best_allele = my_ensemble.DetectBestMultiAllelePair(semi_soft_allele_freq_est); + if (vc.parameters->my_controls.report_ppa){ + my_ensemble.DetectPossiblePolyploidyAlleles(semi_soft_allele_freq_est, vc.parameters->my_controls, candidate_variant.variant_specific_params); + } + + if (use_molecular_tag){ + my_ensemble.CalculateTagSimilarity(*vc.mol_tag_manager, vc.parameters->my_controls.tag_sim_max_cov, sample_index); + my_ensemble.VariantFamilySizeHistogram(); + } // output to variant GlueOutputVariant(my_ensemble, candidate_variant, *vc.parameters, best_allele, sample_index); - if (not use_molecular_tag) { - RemoveVcfInfo(candidate_variant.variant, vector({"MDP", "MRO", "MAO", "MAF"}), sample_name, sample_index); + // output the inference results (MUQUAL, MUGT, MUGQ) if I turn on multi_min_allele_freq + if (vc.parameters->program_flow.is_multi_min_allele_freq){ + my_ensemble.MultiMinAlleleFreq(vc.parameters->program_flow.multi_min_allele_freq); } - // output the inference results (MUQUAL, MUGT, MUGQ, etc.) if I turn on multi_min_allele_freq - if(vc.parameters->program_flow.is_multi_min_allele_freq){ - MultiMinAlleleFreq(my_ensemble, candidate_variant, sample_index, vc.parameters->program_flow, vc.parameters->my_eval_control.max_detail_level); - } // test diagnostic output for this ensemble - if (vc.parameters->program_flow.rich_json_diagnostic & (!(my_ensemble.variant->isFiltered) | my_ensemble.variant->isHotSpot)) // look at everything that came through - JustOneDiagnosis(my_ensemble, *vc.global_context, vc.parameters->program_flow.json_plot_dir, true); - if (vc.parameters->program_flow.minimal_diagnostic & (!(my_ensemble.variant->isFiltered) | my_ensemble.variant->isHotSpot)) // look at everything that came through - JustOneDiagnosis(my_ensemble, *vc.global_context, vc.parameters->program_flow.json_plot_dir, false); + if (vc.parameters->program_flow.rich_json_diagnostic & (!(my_ensemble.variant->isFiltered) | my_ensemble.variant->isHotSpot)){ // look at everything that came through + cout << "+ Dumping rich json diagnostic for (" << PrintVariant(candidate_variant.variant) << ")" << endl; + JustOneDiagnosis(my_ensemble, *vc.global_context, vc.parameters->json_plot_dir, true); + } + if (vc.parameters->program_flow.minimal_diagnostic & (!(my_ensemble.variant->isFiltered) | my_ensemble.variant->isHotSpot)){ // look at everything that came through + cout << "+ Dumping minimal json diagnostic for (" << PrintVariant(candidate_variant.variant) << ")" << endl; + JustOneDiagnosis(my_ensemble, *vc.global_context, vc.parameters->json_plot_dir, false); + } - return true; + if(vc.parameters->program_flow.DEBUG > 0){ + cout << endl << "[tvc] Complete EnsembleProcessOneVariant for (" << PrintVariant(candidate_variant.variant) << "). Processing time = " << (double) (clock() - t0) / 1E6 << " sec." << endl << endl; + } + return 0; } +CandidateExaminer::CandidateExaminer(){ + thread_objects_ = NULL; + vc_ = NULL; + my_ensemble_ = NULL; + max_group_size_allowed_ = 32; +} +CandidateExaminer::CandidateExaminer(PersistingThreadObjects* thread_objects, VariantCallerContext* vc){ + thread_objects_ = NULL; + vc_ = NULL; + my_ensemble_ = NULL; + max_group_size_allowed_ = 32; + Initialize(thread_objects, vc); +} +CandidateExaminer::~CandidateExaminer(){ + ClearVariantCandidate(); +} +void CandidateExaminer::Initialize(PersistingThreadObjects* thread_objects, VariantCallerContext* vc){ + thread_objects_ = thread_objects; + vc_ = vc; + max_group_size_allowed_ = vc->parameters->max_alt_num; +} +// flow_disruptive_code[i][j] indicates the flow-disruptiveness between the j-th hyp (j=0: ref, j=1: allele 1, etc) and the query sequence of the read in test_read_stack[i] +// flow_disruptive_code[i][j] = -1: indefinite (fail to splicing, not cover the position, etc.) +// flow_disruptive_code[i][j] = 0: the j-th hyp is exactly the same as the query sequence +// flow_disruptive_code[i][j] = 1: the j-th hyp is an HP-INDEL of the query sequence +// flow_disruptive_code[i][j] = 2: the j-th hyp is neither HP-INDEL nor flow-disruptive of the query sequence +// flow_disruptive_code[i][j] = 3: the j-th hyp disrupts the flow of query sequence (i.e., the read is very unlikely to support the j-th hyp). +void CandidateExaminer::QuickExamFD(vector& test_read_stack, vector >& flow_disruptive_code) +{ + PersistingThreadObjects& thread_objects = *thread_objects_; + VariantCallerContext& vc = *vc_; + my_ensemble_->read_stack.swap(test_read_stack); + my_ensemble_->SpliceAllelesIntoReads(thread_objects, *vc.global_context, *vc.parameters, *vc.ref_reader); + my_ensemble_->allele_eval.total_theory.SetIsMolecularTag(false); + // Calculate flow-disruptiveness in the read level + my_ensemble_->FlowDisruptivenessInReadLevel(*vc.global_context); + flow_disruptive_code.resize(my_ensemble_->read_stack.size()); + vector >::iterator flow_disruptive_code_it = flow_disruptive_code.begin(); + int num_hyp_not_null = my_ensemble_->allele_identity_vector.size() + 1; + for (vector::iterator read_it = my_ensemble_->allele_eval.total_theory.my_hypotheses.begin(); read_it != my_ensemble_->allele_eval.total_theory.my_hypotheses.end(); ++read_it, ++flow_disruptive_code_it){ + flow_disruptive_code_it->assign(num_hyp_not_null, -1); + if (not read_it->success){ + continue; + } + for (int i_hyp = 0; i_hyp < num_hyp_not_null; ++i_hyp){ + if (read_it->same_as_null_hypothesis[i_hyp + 1]){ + flow_disruptive_code_it->at(i_hyp) = 0; + } + else if (read_it->local_flow_disruptiveness_matrix[0][i_hyp + 1] >= 0){ + flow_disruptive_code_it->at(i_hyp) = read_it->local_flow_disruptiveness_matrix[0][i_hyp + 1] + 1; + } + } + } + my_ensemble_->read_stack.swap(test_read_stack); +} + +// Setup the candidate variants for examination +// Minimal requirements of candidate_variant: +// a) candidate_variant.variant.sequenceName +// b) candidate_variant.variant.position +// c) candidate_variant.variant.ref +// d) candidate_variant.variant.alt +// e) candidate_variant.variant.isAltHotspot.size() == candidate_variant.variant.alt.size() +// f) candidate_variant.variant_specific_params.size() == candidate_variant.variant.alt.size() +void CandidateExaminer::SetupVariantCandidate(VariantCandidate& candidate_variant){ + if (my_ensemble_ == NULL){ + my_ensemble_ = new EnsembleEval(candidate_variant.variant); + } + else{ + *my_ensemble_ = EnsembleEval(candidate_variant.variant); + } + my_ensemble_->DEBUG = vc_->parameters->program_flow.DEBUG; + PrepareAlleles_(candidate_variant); +} + +void CandidateExaminer::ClearVariantCandidate(){ + if (my_ensemble_ != NULL){ + delete my_ensemble_; + } + my_ensemble_ = NULL; +} + +// Allele preparing/filtering steps +void CandidateExaminer::PrepareAlleles_(VariantCandidate& candidate_variant){ + my_ensemble_->SetupAllAlleles(*(vc_->parameters), *(vc_->global_context), *(vc_->ref_reader)); + my_ensemble_->FilterAllAlleles(vc_->parameters->my_controls, candidate_variant.variant_specific_params); // put filtering here in case we want to skip below entries +} + +// Given the candidate alleles, determine the maximally possible split of the variant (or group of the alternative alleles) that can be correctly (i.e., w/o high FXX) evaluated by the evaluator. +// e.g. output: allele_groups = {{0, 1, 2}, {3, 4}, {5}}. Then alt[0], alt[1], alt[2] must be evaluated jointly; alt[3], alt[4] must be evaluated jointly; alt[5] can be evaluated individually. +void CandidateExaminer::SplitCandidateVariant(list >& allele_groups){ + my_ensemble_->SplitMyAlleleIdentityVector(allele_groups, *(vc_->ref_reader), max_group_size_allowed_); +} + +// Given the variant candidates, calculate the end of the look ahead window for candidate generator, +// where (0-based) look ahead window = [last seen position + 1, look_ahead_end_0) +// I.e., the candidate generator should make sure that there is NO other de novo variant till the (0-based) position @ (look_ahead_end_0 - 1), while a variant @ look_ahead_end_0 is fine. +int CandidateExaminer::FindLookAheadEnd0(){ + int look_ahead_end_0 = my_ensemble_->CalculateLookAheadEnd0(*(vc_->ref_reader)); + return look_ahead_end_0; +} + +// return 1-based look ahead end +int CandidateExaminer::FindLookAheadEnd1(){ + return FindLookAheadEnd0() + 1; +} diff --git a/Analysis/VariantCaller/HandleVariant.h b/Analysis/VariantCaller/HandleVariant.h index d2358ec4..1c6cea1a 100644 --- a/Analysis/VariantCaller/HandleVariant.h +++ b/Analysis/VariantCaller/HandleVariant.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -29,35 +28,58 @@ #include "sys/types.h" #include "sys/stat.h" -#include #include #include #include #include - +#include +#include #include #include "InputStructures.h" -#include "MiscUtil.h" -#include "ExtendedReadInfo.h" -#include "ClassifyVariant.h" -#include "ExtendParameters.h" - -#include "StackEngine.h" -#include "ShortStack.h" -#include "DiagnosticJson.h" - using namespace std; using namespace BamTools; using namespace ion; +class MolecularFamily; +class EnsembleEval; // ---------------------------------------------------------------------- +// The function of doing flow-space evaluation for the variant +int EnsembleProcessOneVariant(PersistingThreadObjects &thread_objects, VariantCallerContext& vc, + VariantCandidate ¤t_variant, const PositionInProgress& bam_position, + vector< vector< MolecularFamily> > &molecular_families_one_sample, int sample_index = -1); +// ---------------------------------------------------------------------- +// The interface for the candidate generator to exam the candidate variants +class CandidateExaminer{ +private: + PersistingThreadObjects* thread_objects_; + VariantCallerContext* vc_; + EnsembleEval* my_ensemble_; + int max_group_size_allowed_; + //! @brief The allele preparation steps: setup alleles and filter alleles + void PrepareAlleles_(VariantCandidate& candidate_variant); -bool EnsembleProcessOneVariant(PersistingThreadObjects &thread_objects, VariantCallerContext& vc, - VariantCandidate ¤t_variant, const PositionInProgress& bam_position, - vector< vector< MolecularFamily > > &molecular_families_one_strand, int sample_index = -1); +public: + CandidateExaminer(); + CandidateExaminer(PersistingThreadObjects* thread_objects, VariantCallerContext* vc); + ~CandidateExaminer(); + //! @brief Initialize the object + void Initialize(PersistingThreadObjects* thread_objects, VariantCallerContext* vc); + //! @brief Setup variant candidates for examination + void SetupVariantCandidate(VariantCandidate& candidate_variant); + //! @brief Clear variant candidates + void ClearVariantCandidate(); + //! @brief Calculate the 0-based end position of the look ahead window + int FindLookAheadEnd0(); + //! @brief Calculate the 1-based end position of the look ahead window + int FindLookAheadEnd1(); + //! @brief Split the candidate variant into smaller callable variants + void SplitCandidateVariant(list >& allele_groups); + //! @brief Calculate the FD of the (reference, alternatives) alleles vs. reads as called on test_read_stack + void QuickExamFD(vector& test_read_stack, vector >& flow_disruptive_code); +}; #endif //HANDLEVARIANT_H diff --git a/Analysis/VariantCaller/HotspotReader.cpp b/Analysis/VariantCaller/HotspotReader.cpp index 172da20b..65326b9d 100644 --- a/Analysis/VariantCaller/HotspotReader.cpp +++ b/Analysis/VariantCaller/HotspotReader.cpp @@ -87,6 +87,7 @@ void HotspotReader::MakeHintQueue(const string& hotspot_vcf_filename) size_t r_pos = line.find("R", found+bstrand.size()); // look for the code F size_t f_pos = line.find("F", found+bstrand.size()); + size_t s_pos = line.find("S", found+bstrand.size()); bool blacklist = false; if (f_pos < semicolon_pos) { hint = FWD_BAD_HINT; @@ -98,8 +99,7 @@ void HotspotReader::MakeHintQueue(const string& hotspot_vcf_filename) hint = BOTH_BAD_HINT; } if ( (f_pos < semicolon_pos) && (r_pos < semicolon_pos)){ - hint = BOTH_BAD_HINT; - } + hint = BOTH_BAD_HINT; } // blacklist this position if (hint != NO_HINT) { @@ -110,11 +110,44 @@ void HotspotReader::MakeHintQueue(const string& hotspot_vcf_filename) char* end; // dummy variable for strtoll long int pos = strtoll(fields.at(1).c_str(), &end, 10); long int chrom_idx = ref_reader_->chr_idx(sequenceName.c_str()); - vector hint_entry (3, NO_HINT); - hint_entry[0] = chrom_idx; - hint_entry[1] = pos-1; - hint_entry[2] = hint; + hint_item hint_entry; + hint_entry.chr_ind = chrom_idx; + hint_entry.pos = pos-1; + hint_entry.value = hint; + hint_entry.rlen = 0; hint_vec.push_back(hint_entry); + } else if (s_pos < semicolon_pos) { + vector fields = split(line, '\t'); + string sequenceName = fields.at(0); + char* end; // dummy variable for strtoll + long int pos = strtoll(fields.at(1).c_str(), &end, 10); + long int chrom_idx = ref_reader_->chr_idx(sequenceName.c_str()); + vector alts = split(fields.at(4), ','); + vector bstr = split(line.substr(found+bstrand.size()+1, semicolon_pos-(found+bstrand.size()+1)), ','); + int len = fields.at(3).size(); + for (unsigned int i = 0; i < alts.size(); i++) { + if (bstr.at(i)[0] == 'S') { + hint = BOTH_BAD_HINT; + switch(bstr.at(i)[1]) { + case 'b': + hint = BOTH_BAD_HINT; + break; + case 'f': + hint = FWD_BAD_HINT; + break; + case 'r': + hint = REV_BAD_HINT; + break; + } + hint_item hint_entry; + hint_entry.chr_ind = chrom_idx; + hint_entry.pos = pos-1; + hint_entry.value = hint; + hint_entry.rlen = len; + hint_entry.alt = alts.at(i); + hint_vec.push_back(hint_entry); + } + } } } } @@ -156,6 +189,7 @@ void HotspotReader::FetchNextVariant() vector& strand_bias = current_hotspot.info["strand_bias"]; vector& min_coverage = current_hotspot.info["min_coverage"]; vector& min_coverage_each_strand = current_hotspot.info["min_coverage_each_strand"]; + vector& min_var_coverage = current_hotspot.info["min_var_coverage"]; vector& min_variant_score = current_hotspot.info["min_variant_score"]; vector& data_quality_stringency = current_hotspot.info["data_quality_stringency"]; vector& hp_max_length = current_hotspot.info["hp_max_length"]; @@ -163,6 +197,7 @@ void HotspotReader::FetchNextVariant() vector& filter_unusual_predictions = current_hotspot.info["filter_unusual_predictions"]; vector& filter_insertion_predictions = current_hotspot.info["filter_insertion_predictions"]; vector& filter_deletion_predictions = current_hotspot.info["filter_deletion_predictions"]; + vector& min_tag_fam_size = current_hotspot.info["min_tag_fam_size"]; vector& sse_prob_threshold = current_hotspot.info["sse_prob_threshold"]; // collect bad-strand info @@ -222,6 +257,11 @@ void HotspotReader::FetchNextVariant() hotspot.params.min_coverage_each_strand = atoi(min_coverage_each_strand[alt_idx].c_str()); } + if (alt_idx < min_var_coverage.size() and min_var_coverage[alt_idx] != ".") { + hotspot.params.min_var_coverage_override = true; + hotspot.params.min_var_coverage = atoi(min_var_coverage[alt_idx].c_str()); + } + if (alt_idx < min_variant_score.size() and min_variant_score[alt_idx] != ".") { hotspot.params.min_variant_score_override = true; hotspot.params.min_variant_score = atof(min_variant_score[alt_idx].c_str()); @@ -252,6 +292,11 @@ void HotspotReader::FetchNextVariant() hotspot.params.filter_deletion_predictions = atof(filter_deletion_predictions[alt_idx].c_str()); } + if (alt_idx < min_tag_fam_size.size() and min_tag_fam_size[alt_idx] != ".") { + hotspot.params.min_tag_fam_size_override = true; + hotspot.params.min_tag_fam_size = atof(min_tag_fam_size[alt_idx].c_str()); + } + if (alt_idx < sse_prob_threshold.size() and sse_prob_threshold[alt_idx] != ".") { hotspot.params.sse_prob_threshold_override = true; hotspot.params.sse_prob_threshold = atof(sse_prob_threshold[alt_idx].c_str()); diff --git a/Analysis/VariantCaller/HotspotReader.h b/Analysis/VariantCaller/HotspotReader.h index 5df3b2ad..3b3149d3 100644 --- a/Analysis/VariantCaller/HotspotReader.h +++ b/Analysis/VariantCaller/HotspotReader.h @@ -38,7 +38,16 @@ enum AlleleHint { REV_BAD_HINT = 2, BOTH_BAD_HINT = 3 }; - + +class hint_item { + public: + int chr_ind; + long int pos; + long int value; + long int rlen; + string alt; +}; + class HotspotReader { public: HotspotReader(); @@ -54,10 +63,12 @@ class HotspotReader { int next_chr() const { return next_chr_; } int next_pos() const { return next_pos_; } - vector< vector > hint_vec; - int hint_chr_index() const { return (int)hint_vec[hint_cur_][0]; } - long int hint_position() const { return hint_vec[hint_cur_][1]; } - long int hint_value() const { return hint_vec[hint_cur_][2]; } + vector< hint_item > hint_vec; + int hint_chr_index() const { return (int)hint_vec[hint_cur_].chr_ind; } + long int hint_position() const { return hint_vec[hint_cur_].pos; } + long int hint_value() const { return hint_vec[hint_cur_].value; } + long int hint_rlen() const { return hint_vec[hint_cur_].rlen; } + string hint_alt() const { return hint_vec[hint_cur_].alt; } bool hint_empty() const { return hint_vec.empty() || hint_header_ >= hint_vec.size();} void hint_pop() { hint_header_++;} void hint_next() { hint_cur_++;} diff --git a/Analysis/VariantCaller/IndelAssembly/IndelAssembly.cpp b/Analysis/VariantCaller/IndelAssembly/IndelAssembly.cpp index 9dfad168..6cc64ebb 100644 --- a/Analysis/VariantCaller/IndelAssembly/IndelAssembly.cpp +++ b/Analysis/VariantCaller/IndelAssembly/IndelAssembly.cpp @@ -1295,9 +1295,13 @@ void IndelAssemblyArgs::processParameters(OptArgs& opts) { } } - void IndelAssembly::onTraversalDone() { +// ----------------------------------------------------------------------------- +// The function takes a boolean signalling whether assembly output is desired + +void IndelAssembly::onTraversalDone(bool do_assembly) { + if (do_assembly) DetectCandidateRegions(WINDOW_SIZE); - out.close(); - } - \ No newline at end of file + out.close(); +} + diff --git a/Analysis/VariantCaller/IndelAssembly/IndelAssembly.h b/Analysis/VariantCaller/IndelAssembly/IndelAssembly.h index 1ded5374..33518485 100644 --- a/Analysis/VariantCaller/IndelAssembly/IndelAssembly.h +++ b/Analysis/VariantCaller/IndelAssembly/IndelAssembly.h @@ -249,7 +249,7 @@ class IndelAssembly { int getSoftStart(BamAlignment& alignment); void SetReferencePoint(BamAlignment& read); void map(BamAlignment& read); - void onTraversalDone(); + void onTraversalDone(bool do_assembly); void cleanCounts(); void shiftCounts(int delta); void DetectCandidateRegions(int wsize); diff --git a/Analysis/VariantCaller/IndelAssembly/IndelAssemblyMain.cpp b/Analysis/VariantCaller/IndelAssembly/IndelAssemblyMain.cpp index f095a79b..5b46381e 100644 --- a/Analysis/VariantCaller/IndelAssembly/IndelAssemblyMain.cpp +++ b/Analysis/VariantCaller/IndelAssembly/IndelAssemblyMain.cpp @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) } SampleManager sample_manager; - sample_manager.Initialize(bam_reader.GetHeader(), parsed_opts.sample_name, parsed_opts.force_sample_name); + sample_manager.Initialize(bam_reader.GetHeader(), parsed_opts.sample_name, parsed_opts.force_sample_name, parsed_opts.multisample); IndelAssembly indel_assembly(&parsed_opts, &reference_reader, &sample_manager, &targets_manager); @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) while (bam_reader.GetNextAlignment(alignment)) { if (!indel_assembly.processRead(alignment, indel_target)) {break;} } - indel_assembly.onTraversalDone(); + indel_assembly.onTraversalDone(true); bam_reader.Close(); diff --git a/Analysis/VariantCaller/MolecularTag.cpp b/Analysis/VariantCaller/MolecularTag.cpp index b7a3974c..a259f21b 100644 --- a/Analysis/VariantCaller/MolecularTag.cpp +++ b/Analysis/VariantCaller/MolecularTag.cpp @@ -3,10 +3,71 @@ #include "MolecularTag.h" #include "MiscUtil.h" +float median(vector& v) { + sort(v.begin(), v.end()); + int n = v.size(); + if (n == 0) {return 0;} + if ((n % 2) == 1) {return v[n/2];} + else {int i = n/2; return (v[i-1] + v[i])/2;} +} + +float mean(vector& v) { + unsigned int size = v.size(); + if (size == 0) {return 0;} + else { + float sum = 0; + for (vector::iterator iter = v.begin(); (iter != v.end()); ++iter) { + sum += *iter; + } + return (sum / size); + } +} + +int MolecularFamily::CountFamSizeFromAll() +{ + fam_size_ = 0; + for (vector::iterator read_it = all_family_members.begin(); read_it != all_family_members.end(); ++read_it){ + fam_size_ += ((*read_it)->read_count); + } + return fam_size_; +} + +int MolecularFamily::CountFamSizeFromValid() +{ + valid_fam_size_ = 0; + for (vector::iterator read_it = valid_family_members.begin(); read_it != valid_family_members.end(); ++read_it){ + valid_fam_size_ += ((*read_it)->read_count); + } + return valid_fam_size_; +} + +bool CompareByReadCounts(const Alignment* const rai_1, const Alignment* const rai_2){ + return rai_1->read_count > rai_2->read_count; +} + +void MolecularFamily::SortAllFamilyMembers(){ + sort(all_family_members.begin(), all_family_members.end(), CompareByReadCounts); + is_all_family_members_sorted = true; +} + +void MolecularFamily::SortValidFamilyMembers(){ + sort(valid_family_members.begin(), valid_family_members.end(), CompareByReadCounts); + is_valid_family_members_sorted = true; +} + +void MolecularFamily::ResetValidFamilyMembers(){ + AbstractMolecularFamily::ResetValidFamilyMembers(); + is_valid_family_members_sorted = false; +} + Consensus::Consensus() { min_start_position_ = 0; max_read_length_ = 0; debug_ = false; + flow_consensus_ = true; + error_ = false; + stitch_ = false; + iupac_cutoff_ = 1; } Consensus::~Consensus() { @@ -14,35 +75,57 @@ Consensus::~Consensus() { unsigned int Consensus::GetAlignedFamily(vector& family_members) { min_start_position_ = 999999999; + start_flow_ = 99999999; insertions_.clear(); aligned_bases_.clear(); insertion_bases_.clear(); + flow_indexes_.clear(); + measurement_vector_.clear(); + phase_params_.clear(); + flow_index_.clear(); + measurements_.clear(); + soft_clip_offset_.clear(); max_read_length_ = 0; - + error_ = false; + unsigned int map_quality = 0; for (vector::iterator iter = family_members.begin(); (iter != family_members.end()); ++iter) { + if (iter == family_members.begin()) {name_ = (*iter)->alignment.Name;} if (debug_) { + cerr << "Name " << (*iter)->alignment.Name << endl; cerr << "RefID:Position " << (*iter)->alignment.RefID << ":" << (*iter)->alignment.Position << endl; + cerr << "align_start " << (*iter)->align_start << endl; cerr << "start " << (*iter)->start << endl; cerr << "end " << (*iter)->end << endl; cerr << "QueryBases " << (*iter)->alignment.QueryBases << endl; cerr << "AlignedBases " << (*iter)->alignment.AlignedBases << endl; } + phase_params_.push_back((*iter)->phase_params); map_quality += (*iter)->alignment.MapQuality; min_start_position_ = min(min_start_position_, (unsigned int)((*iter)->alignment.Position)); unsigned int offset = 0; for (unsigned int position = min_start_position_; (position < (unsigned int)((*iter)->alignment.Position)); ++position) {offset++;} max_read_length_ = max(max_read_length_, (std::basic_string::size_type)(((*iter)->alignment.AlignedBases.length() + offset))); unsigned int pos = 0; + int soft_clip = 0; + unsigned int cigar_index = 0; for (vector::iterator iterCigar = (*iter)->alignment.CigarData.begin(); (iterCigar != (*iter)->alignment.CigarData.end()); ++iterCigar) { if (debug_) {cerr << iterCigar->Type << " " << iterCigar->Length << endl;} + if (iterCigar == (*iter)->alignment.CigarData.begin()) { + if (iterCigar->Type == 'S') { + soft_clip = iterCigar->Length; + } + } if (iterCigar->Type == 'I') { std::map::iterator iterInsertions = insertions_.find(pos + offset); if (iterInsertions == insertions_.end()) {insertions_[pos + offset] = iterCigar->Length;} else {insertions_[pos + offset] = max(iterInsertions->second, iterCigar->Length);} } if (iterCigar->Type == 'M') {pos += iterCigar->Length;} + if (iterCigar->Type == 'D') {pos += iterCigar->Length;} + cigar_index++; } + soft_clip_offset_.push_back(soft_clip); } map_quality /= family_members.size(); unsigned int count = 0; @@ -51,21 +134,63 @@ unsigned int Consensus::GetAlignedFamily(vector& family_members) { } max_read_length_ += count; insertion_bases_.assign(max_read_length_, '-'); + int read_index = -1; for (vector::iterator iter = family_members.begin(); (iter != family_members.end()); ++iter) { + read_index++; string str = (*iter)->alignment.AlignedBases; - str.reserve(max_read_length_); + if (flow_consensus_) { + flow_indexes_.push_back((*iter)->flow_index); + if (iter == family_members.begin()) { + flow_order_index_ = (*iter)->flow_order_index; + measurements_ = (*iter)->measurements; + for (vector::iterator init = measurements_.begin(); (init != measurements_.end()); ++init) {*init = 0;} + } + int start_flow = 0; + (*iter)->alignment.GetTag("ZF", start_flow); + start_flow_ = min(start_flow_, start_flow); + measurement_vector_.push_back((*iter)->measurements); + vector hp_counts = (*iter)->flow_index; + for (unsigned int index = 0; (index < (*iter)->flow_index.size()); ++index) { + if (index == (*iter)->flow_index.size() - 1) {hp_counts[index] = 1;} + else { + unsigned int index2 = index + 1; + for (; (index2 < (*iter)->flow_index.size()); ++index2) { + if ((*iter)->flow_index[index2] != (*iter)->flow_index[index]) {break;} + } + for (unsigned int index3 = index; (index3 < index2); ++index3) { + if (index3 == index) { + hp_counts[index3] = index2 - index; + } else { + hp_counts[index3] = 1; + } + } + index += index2 - index - 1; + } + } + int index = 0; + for (vector::iterator iter2 = (*iter)->flow_index.begin(); (iter2 != (*iter)->flow_index.end()); ++iter2) { + measurement_vector_[measurement_vector_.size() - 1][*iter2] /= hp_counts[index]; + index++; + } + } + str.reserve(max_read_length_); unsigned int insertion_count = 0; unsigned int offset = 0; - for (unsigned int position = min_start_position_; (position < (unsigned int)((*iter)->alignment.Position)); ++position) {str = '-' + str; insertion_count++; offset++;} - while (str.length() < max_read_length_) {str += "-";} + unsigned int length = 0; + str.reserve(max_read_length_); + char padding = '-'; + if (stitch_) {padding = ' ';} + for (unsigned int position = min_start_position_; (position < (unsigned int)((*iter)->alignment.Position)); ++position) {str = padding + str; insertion_count++; offset++; length++;} std::map read_insertions; unsigned int pos = 0; for (vector::iterator iterCigar = (*iter)->alignment.CigarData.begin(); (iterCigar != (*iter)->alignment.CigarData.end()); ++iterCigar) { if (iterCigar->Type == 'I') { - read_insertions[pos + offset] = iterCigar->Length; + read_insertions[pos + offset] = iterCigar->Length; length += iterCigar->Length; } - if (iterCigar->Type == 'M') {pos += iterCigar->Length;} + if (iterCigar->Type == 'M') {pos += iterCigar->Length; length += iterCigar->Length;} + if (iterCigar->Type == 'D') {pos += iterCigar->Length; length += iterCigar->Length;} } + while (str.length() < max_read_length_) {str += padding;} for (std::map::iterator iterInsertions = insertions_.begin(); (iterInsertions != insertions_.end()); ++iterInsertions) { for (unsigned int pos = iterInsertions->first + insertion_count - offset; (pos < (iterInsertions->first + insertion_count - offset + iterInsertions->second)); ++pos) { if (pos < insertion_bases_.length()) {insertion_bases_[pos] = '*';} @@ -83,8 +208,7 @@ unsigned int Consensus::GetAlignedFamily(vector& family_members) { insertion_count++; } str += part2; - } - else { + } else { string part1 = ""; if (iterInsertions->first + iterReadInsertions->second + insertion_count - offset < str.length()) {part1 = str.substr(0, iterInsertions->first + iterReadInsertions->second + insertion_count - offset);} string part2 = ""; @@ -96,12 +220,13 @@ unsigned int Consensus::GetAlignedFamily(vector& family_members) { } insertion_count += iterInsertions->second; str += part2; - + } } aligned_bases_.push_back(str); + read_counts_.push_back((*iter)->read_count); } - + max_read_length_ = 0; for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) { max_read_length_ = max(max_read_length_, (std::basic_string::size_type)(iter->length())); @@ -112,16 +237,25 @@ unsigned int Consensus::GetAlignedFamily(vector& family_members) { } void Consensus::GetConsensus(ReferenceReader& ref_reader, unsigned int RefID) { + // base space consensus consensus_ = ""; - consensus_.reserve(max_read_length_); + consensus_.reserve(max_read_length_); int A_count = 0; int C_count = 0; int G_count = 0; int T_count = 0; int N_count = 0; int D_count = 0; - int I_count = 0; + int I_count = 0; + int R_count = 0; + int Y_count = 0; + int S_count = 0; + int W_count = 0; + int K_count = 0; + int M_count = 0; + int total_count = 0; unsigned int position = min_start_position_; + while (insertion_bases_.length() < max_read_length_) {insertion_bases_ += '-';} for (unsigned int index = 0; (index < max_read_length_); ++index) { A_count = 0; C_count = 0; @@ -129,44 +263,167 @@ void Consensus::GetConsensus(ReferenceReader& ref_reader, unsigned int RefID) { T_count = 0; N_count = 0; D_count = 0; - I_count = 0; + I_count = 0; + R_count = 0; + Y_count = 0; + S_count = 0; + W_count = 0; + K_count = 0; + M_count = 0; + total_count = 0; + vector::iterator read_counts_iter = read_counts_.begin(); for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) { - if (index >= iter->length()) {D_count++;} + if (index >= iter->length()) {D_count+=(*read_counts_iter);} else { switch (iter->at(index)) { - case 'A': A_count++; break; - case 'C': C_count++; break; - case 'G': G_count++; break; - case 'T': T_count++; break; - case '-': D_count++; break; - case '*': I_count++; break; - default: N_count++; + case ' ': break; + case 'A': total_count+=(*read_counts_iter); A_count+=(*read_counts_iter); break; + case 'C': total_count+=(*read_counts_iter); C_count+=(*read_counts_iter); break; + case 'G': total_count+=(*read_counts_iter); G_count+=(*read_counts_iter); break; + case 'T': total_count+=(*read_counts_iter); T_count+=(*read_counts_iter); break; + case 'R': total_count+=(*read_counts_iter); R_count+=(*read_counts_iter); break; + case 'Y': total_count+=(*read_counts_iter); Y_count+=(*read_counts_iter); break; + case 'S': total_count+=(*read_counts_iter); S_count+=(*read_counts_iter); break; + case 'W': total_count+=(*read_counts_iter); W_count+=(*read_counts_iter); break; + case 'K': total_count+=(*read_counts_iter); K_count+=(*read_counts_iter); break; + case 'M': total_count+=(*read_counts_iter); M_count+=(*read_counts_iter); break; + case '-': total_count+=(*read_counts_iter); D_count+=(*read_counts_iter); break; + case '*': total_count+=(*read_counts_iter); I_count+=(*read_counts_iter); break; + default: total_count+=(*read_counts_iter); N_count+=(*read_counts_iter); } } + ++read_counts_iter; } + if (total_count == 0) {D_count = 1; total_count = 1;} char ref_base = 0; - if (insertion_bases_[index] != '*') {ref_base = (char)(*(ref_reader.iter(RefID, position)));} - int max_count = max(max(max(max(max(max(A_count, C_count), G_count), T_count), N_count), D_count), I_count); - if ((ref_base == 'G') and (max_count == G_count)) {C_count = 0; T_count = 0; A_count = 0; D_count = 0;} - else if ((ref_base == 'C') and (max_count == C_count)) {G_count = 0; T_count = 0; A_count = 0; D_count = 0;} - else if ((ref_base == 'T') and (max_count == T_count)) {C_count = 0; G_count = 0; A_count = 0; D_count = 0;} - else if ((ref_base == 'A') and (max_count == A_count)) {C_count = 0; G_count = 0; T_count = 0; D_count = 0;} - if (max_count == I_count) {consensus_ += '-';} - else if (max_count == G_count) {consensus_ += 'G';} - else if (max_count == C_count) {consensus_ += 'C';} - else if (max_count == T_count) {consensus_ += 'T';} - else if (max_count == A_count) {consensus_ += 'A';} - else if (max_count == D_count) {consensus_ += '-';} - else if (max_count == N_count) {consensus_ += 'N';} + if ((index < insertion_bases_.length()) and (insertion_bases_[index] != '*')) {ref_base = (char)(*(ref_reader.iter(RefID, position)));} + + int allele_count = 0; + float D_rate = (D_count / (double)total_count); + float A_rate = (A_count / (double)total_count); + float C_rate = (C_count / (double)total_count); + float G_rate = (G_count / (double)total_count); + float T_rate = (T_count / (double)total_count); + if (A_rate > iupac_cutoff_) {allele_count++;} + if (C_rate > iupac_cutoff_) {allele_count++;} + if (G_rate > iupac_cutoff_) {allele_count++;} + if (T_rate > iupac_cutoff_) {allele_count++;} + if ((allele_count == 1) and (D_rate > iupac_cutoff_)) {D_count = 0;} + if (allele_count > 1) { + if (debug_) { + cerr << A_rate << " " << C_rate << " " << G_rate << " " << T_rate << " " << D_rate << endl; + } + if ((A_rate > iupac_cutoff_) and (G_rate > iupac_cutoff_)) { + //consensus_ += "R"; + if (ref_base == 'A') { consensus_ += "G"; } else { consensus_ += "A"; } + } else if ((C_rate > iupac_cutoff_) and (T_rate > iupac_cutoff_)) { + //consensus_ += "Y"; + if (ref_base == 'C') { consensus_ += "T"; } else { consensus_ += "C"; } + } else if ((G_rate > iupac_cutoff_) and (C_rate > iupac_cutoff_)) { + //consensus_ += "S"; + if (ref_base == 'G') { consensus_ += "C"; } else { consensus_ += "G"; } + } else if ((A_rate > iupac_cutoff_) and (T_rate > iupac_cutoff_)) { + //consensus_ += "W"; + if (ref_base == 'A') { consensus_ += "T"; } else { consensus_ += "A"; } + } else if ((G_rate > iupac_cutoff_) and (T_rate > iupac_cutoff_)) { + //consensus_ += "K"; + if (ref_base == 'G') { consensus_ += "T"; } else { consensus_ += "G"; } + } else if ((A_rate > iupac_cutoff_) and (C_rate > iupac_cutoff_)) { + //consensus_ += "M"; + if (ref_base == 'A') { consensus_ += "C"; } else { consensus_ += "A"; } + } else {consensus_ += "N";} + } else { + int max_count = max(max(max(max(max(max(max(max(max(max(max(max(A_count, C_count), G_count), T_count), N_count), D_count), I_count), R_count), Y_count), S_count), W_count), K_count), M_count); + if ((ref_base == 'G') and (max_count == G_count)) {C_count = 0; T_count = 0; A_count = 0; D_count = 0;} + else if ((ref_base == 'C') and (max_count == C_count)) {G_count = 0; T_count = 0; A_count = 0; D_count = 0;} + else if ((ref_base == 'T') and (max_count == T_count)) {C_count = 0; G_count = 0; A_count = 0; D_count = 0;} + else if ((ref_base == 'A') and (max_count == A_count)) {C_count = 0; G_count = 0; T_count = 0; D_count = 0;} + if (max_count == I_count) {consensus_ += '-';} + else if (max_count == R_count) {consensus_ += 'R';} + else if (max_count == Y_count) {consensus_ += 'Y';} + else if (max_count == S_count) {consensus_ += 'S';} + else if (max_count == W_count) {consensus_ += 'W';} + else if (max_count == K_count) {consensus_ += 'K';} + else if (max_count == M_count) {consensus_ += 'M';} + else if (max_count == G_count) {consensus_ += 'G';} + else if (max_count == C_count) {consensus_ += 'C';} + else if (max_count == T_count) {consensus_ += 'T';} + else if (max_count == A_count) {consensus_ += 'A';} + else if (max_count == D_count) {consensus_ += '-';} + else if (max_count == N_count) {consensus_ += 'N';} + } if (insertion_bases_[index] != '*') {position++;} } + // flow space consensus + if (flow_consensus_) { + if (reverse_) { + RevComplementInPlace(consensus_); + for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) { + RevComplementInPlace(*iter); + } + } + vector positions; + unsigned int read_index = 0; + for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) {positions.push_back(soft_clip_offset_[read_index]); read_index++;} + // setup flow prefix + vector values; + values.reserve(flow_indexes_.size()); + unsigned int flow_index = start_flow_; + for (unsigned int flow = 0; (flow < (unsigned int)start_flow_); ++flow) { + values.clear(); + read_index = 0; + for (vector >::iterator iter = flow_indexes_.begin(); (iter != flow_indexes_.end()); ++iter) { + values.push_back(measurement_vector_[read_index][flow]); + read_index++; + } + if (flow >= measurements_.size()) {measurements_.resize(flow + 1);} + measurements_[flow] = median(values); + } + vector v; + v.reserve(flow_indexes_.size()); + flow_index_.reserve(max_read_length_); + for (unsigned int index = 0; (index < consensus_.length()); ++index) { + if (consensus_[index] != '-') { + while ((flow_index < flow_order_.length()) and (consensus_[index] != flow_order_[flow_index])) {flow_index++;} + if (flow_index >= flow_order_.length()) {consensus_ = consensus_.substr(0, index);} + else { + read_index = 0; + v.clear(); + for (vector >::iterator iter = flow_indexes_.begin(); (iter != flow_indexes_.end()); ++iter) { + if ((positions[read_index] < (*iter).size()) and (index < aligned_bases_[read_index].length())) { + if (aligned_bases_[read_index][index] == consensus_[index]) { + unsigned int flow = iter->at(positions[read_index]); + v.push_back(measurement_vector_[read_index][flow]); + } + } + read_index++; + } + flow_index_.push_back(flow_index); + measurements_[flow_index] += median(v); + } + } + read_index = 0; + for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) { + if (index < iter->length()) { + if ((iter->at(index) != ' ') and (iter->at(index) != '-') and (iter->at(index) != '*')) {positions[read_index]++;} + } + read_index++; + } + } + if (reverse_) { + RevComplementInPlace(consensus_); + for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) { + RevComplementInPlace(*iter); + } + } + } } unsigned int Consensus::TrimConsensus() { std::vector offsets; unsigned int insertion_count = 0; for (std::map::iterator iterInsertions = insertions_.begin(); (iterInsertions != insertions_.end()); ++iterInsertions) { - offsets.push_back(insertion_count); + offsets.push_back(insertion_count); insertion_count += iterInsertions->second; } new_insertions_.clear(); @@ -174,21 +431,23 @@ unsigned int Consensus::TrimConsensus() { int offset_index = insertions_.size(); for (std::map::reverse_iterator iterInsertions = insertions_.rbegin(); (iterInsertions != insertions_.rend()); ++iterInsertions) { offset_index--; - for (unsigned int pos = (min((unsigned int)consensus_.length(), iterInsertions->first + iterInsertions->second + offsets[offset_index]) - 1); (pos >= iterInsertions->first + offsets[offset_index]); --pos) { - if (consensus_[pos] == '-') { - if (pos == consensus_.length() - 1) {consensus_ = consensus_.substr(0, pos);} - else { - consensus_ = consensus_.substr(0, pos) + consensus_.substr(pos + 1); - } + for (int pos = (min((unsigned int)consensus_.length(), iterInsertions->first + iterInsertions->second + offsets[offset_index]) - 1); (pos >= (int)(iterInsertions->first + offsets[offset_index])); --pos) { + if (pos >= (int)consensus_.size()) {continue;} + if (pos < 0) {break;} + if (consensus_[pos] == '-') { + if (pos == (int)consensus_.length() - 1) {consensus_ = consensus_.substr(0, pos);} + else { + consensus_ = consensus_.substr(0, pos) + consensus_.substr(pos + 1); + } iterInsertions->second--; } } - } + } // trim begining of consensus unsigned int new_position = min_start_position_; unsigned int removed_count = 0; for (unsigned int index = 0; (index < consensus_.length()); ++index) { - if (index >= consensus_.length()) {break;} + if (index >= consensus_.length()) {break;} if (consensus_[index] == '-') { if (index + 1 >= consensus_.length()) {consensus_ = "";} else {consensus_ = consensus_.substr(index + 1);} @@ -196,8 +455,8 @@ unsigned int Consensus::TrimConsensus() { new_position++; index--; } else {break;} - } - // adjust insertion positions + } + // adjust insertion positions for (std::map::iterator iterInsertions = insertions_.begin(); (iterInsertions != insertions_.end()); ++iterInsertions) { if (iterInsertions->second != 0) {new_insertions_[iterInsertions->first - removed_count] = iterInsertions->second;} } @@ -223,12 +482,11 @@ void Consensus::CalculateCigar() { for (unsigned int index = 0; (index < (iterInsertions->first + offset - pos)); ++index) { if (pos + index >= consensus_.length()) {end = true; break;} if (consensus_[pos + index] == '-') { - deletion_count++; offset++; - } - else { + deletion_count++; + } else { if (deletion_count > 0) { if (m_count > 0) {new_cigar_.push_back(CigarOp('M', m_count)); m_count = 0;} - new_cigar_.push_back(CigarOp('D', deletion_count)); + new_cigar_.push_back(CigarOp('D', deletion_count)); deletion_count = 0; } m_count++; @@ -245,8 +503,7 @@ void Consensus::CalculateCigar() { if (pos + index >= consensus_.length()) {break;} if (consensus_[pos + index] == '-') { deletion_count++; - } - else { + } else { if (deletion_count > 0) { if (m_count > 0) {new_cigar_.push_back(CigarOp('M', m_count)); m_count = 0;} new_cigar_.push_back(CigarOp('D', deletion_count)); @@ -257,17 +514,76 @@ void Consensus::CalculateCigar() { } if (m_count > 0) {new_cigar_.push_back(CigarOp('M', m_count)); m_count = 0;} } +void Consensus::PartiallyResetAlignment_(Alignment& alignment) +{ + long int start = alignment.start; + long int end = alignment.end; + alignment.Reset(); + alignment.start = start; + alignment.end = end; +} + + +bool Consensus::CalculateConsensus(ReferenceReader& ref_reader, vector& family_members, Alignment& alignment, const string& flow_order) { + // Empty family_members + if (family_members.empty()) { + return false; + } + + // Trivial tie case + if (family_members.size() == 2){ + if (family_members[0]->read_count == family_members[1]->read_count){ + // I pick the read with the better mapping quality to be the consensus. + // In this case, I implicitly prefer to get the reference alleles. + alignment = (family_members[0]->alignment.MapQuality > family_members[1]->alignment.MapQuality)? *(family_members[0]) : *(family_members[1]); + alignment.read_count = family_members[0]->read_count + family_members[1]->read_count; + PartiallyResetAlignment_(alignment); + return true; + } + } + + int fam_size = 0; + int max_read_count = -1; + unsigned int max_read_count_idx = 0; + + // Calculate the family size and find the read w/ max read count. + for (unsigned int read_idx = 0; read_idx < family_members.size(); ++read_idx){ + int read_count = family_members[read_idx]->read_count; + fam_size += read_count; + if (read_count > max_read_count){ + max_read_count = read_count; + max_read_count_idx = read_idx; + } + } + // Check if family_members has a consensus read that dominates all the others. + // If true, I am actually calculating the consensus of consensus reads, which is trivial in this case. + // It should be the case most of the time if the input bam is a consensus bam. + if (2 * max_read_count >= fam_size){ + alignment = *(family_members[max_read_count_idx]); + alignment.read_count = fam_size; + PartiallyResetAlignment_(alignment); + return true; + } -bool Consensus::CalculateConsensus(ReferenceReader& ref_reader, vector& family_members, Alignment& alignment) { - if (family_members.size() < 1) {return false;} - alignment = *(family_members[0]); - //cerr << endl << family_barcode << " " << family_members.size() << " " << family_members[0]->alignment.RefID << " " << family_members[0]->alignment.Position << endl; - //debug_ = false; - //if ((family_members[0]->alignment.Position <= 55259524) and (family_members[0]->alignment.GetEndPosition() >= 55259524)) {debug_ = true;} - //if (alignment.alignment.Name == "TSJAM:02884:02345") {debug_ = true;} - //if ((family_barcode == "TTGACTTTGTGATCATAAAGTCAA") and (family_members.size() == 15) and (family_members[0]->alignment.Position == 29443576)) {debug_ = true;} + // If I didn't input flow_order, I can't generate flow consensus. So I go through the base space version instead. + bool flow_consensus_temp = flow_consensus_; + if (flow_order.empty()){ + SetFlowConsensus(false); + } + + flow_order_ = flow_order; + alignment = Alignment(*(family_members[0])); + alignment.read_count = fam_size; + PartiallyResetAlignment_(alignment); + reverse_ = alignment.is_reverse_strand; + debug_ = false; + bool show_variants = false; + int show_chr = 1; + long int show_pos = 2488153; + //if ((family_members[0]->alignment.RefID == (show_chr - 1)) and (family_members[0]->alignment.Position <= show_pos) and (family_members[0]->alignment.GetEndPosition() >= show_pos)) {show_variants = true;} + //if (alignment.alignment.Name == "7XBVF:02889:02037") {debug_ = true;} if (debug_) {cerr << endl << family_members.size() << " " << family_members[0]->alignment.RefID << " " << family_members[0]->alignment.Position << endl;} - + min_start_position_ = 0; insertions_.clear(); aligned_bases_.clear(); @@ -276,28 +592,33 @@ bool Consensus::CalculateConsensus(ReferenceReader& ref_reader, vector::iterator iterCigar = new_cigar_.begin(); (iterCigar != new_cigar_.end()); ++iterCigar) { if (iterCigar->Type == 'M') {end += iterCigar->Length;} } - + // output new consensus and cigar + alignment.flow_index = flow_index_; + alignment.measurements = measurements_; + alignment.read_bases = query_bases; if (debug_) { bool run_exit = false; cerr << endl; - cerr << alignment.alignment.Name << endl; + cerr << alignment.alignment.Name << endl; + + unsigned int index = 0; + for (vector::iterator iter = alignment.measurements.begin(); (iter != alignment.measurements.end()); ++iter) { + cerr << "measurements " << index << " " << *iter << endl; + index++; + } + vector hp_counts = alignment.flow_index; + for (index = 0; (index < alignment.flow_index.size()); ++index) { + if (index == alignment.flow_index.size() - 1) {hp_counts[index] = 1;} + else { + unsigned int index2 = index + 1; + for (; (index2 < alignment.flow_index.size()); ++index2) { + if (alignment.flow_index[index2] != alignment.flow_index[index]) {break;} + } + for (unsigned int index3 = index; (index3 < index2); ++index3) { + hp_counts[index3] = index2 - index; + } + index += index2 - index - 1; + } + } + index = 0; + for (vector::iterator iter = alignment.flow_index.begin(); (iter != alignment.flow_index.end()); ++iter) { + if (*iter < (int)flow_order_.length()) { + cerr << "flow_index " << index << " " << *iter << " " << alignment.measurements[*iter] << " " << alignment.read_bases[index] << " " << flow_order_[*iter] << " " << hp_counts[index] << " " << (alignment.measurements[*iter] / hp_counts[index]) << endl; + } + index++; + } + cerr << start << endl; cerr << end << endl; cerr << new_position << endl; @@ -323,130 +675,818 @@ bool Consensus::CalculateConsensus(ReferenceReader& ref_reader, vectorType << " " << iterCigar->Length << endl; if (iterCigar->Type == 'D') {run_exit = true;} } - //if (new_position == 115256481) {run_exit = true;} if (run_exit) { //exit(1); } - //cerr << "alignment " << alignment_time << endl; - //cerr << "consensus " << consensus_time << endl; - //cerr << "trim " << trim_time << endl; - //cerr << "cigar " << cigar_time << endl; - cerr << endl; - cerr << "Done" << endl; + cerr << endl; } - alignment.next = NULL; - alignment.processing_prev = NULL; - alignment.processing_next = NULL; alignment.start = start; alignment.end = end; alignment.alignment.Position = new_position; alignment.alignment.MapQuality = map_quality; alignment.alignment.QueryBases = query_bases; alignment.alignment.Length = query_bases.length(); + alignment.alignment.Qualities = "*"; + alignment.alignment.Length = query_bases.length(); alignment.alignment.AlignedBases = consensus_; - alignment.alignment.CigarData = new_cigar_; - alignment.refmap_start.clear(); - alignment.refmap_code.clear(); - alignment.refmap_has_allele.clear(); - alignment.refmap_allele.clear(); - alignment.read_count = family_members.size(); + alignment.alignment.CigarData.swap(new_cigar_); + + alignment.alignment.RemoveTag("NM"); + alignment.alignment.RemoveTag("XM"); + alignment.alignment.RemoveTag("MD"); + alignment.alignment.RemoveTag("ZF"); + alignment.alignment.RemoveTag("ZP"); + alignment.alignment.RemoveTag("ZM"); + alignment.alignment.RemoveTag("ZN"); + alignment.alignment.RemoveTag("ZR"); + // calculate ZM tag + vector measurements; + for (vector::iterator iter = measurements_.begin(); (iter != measurements_.end()); ++iter) { + measurements.push_back((int)(*iter * 256)); + } + // calculate MD tag + //bool snp = false; + string md = ""; + int match_count = 0; + int mismatch_count = 0; + int total_match_count = 0; + unsigned int ref_pos = alignment.start; + unsigned int seq_pos = alignment.start; + for (vector::iterator iterCigar = new_cigar_.begin(); (iterCigar != new_cigar_.end()); ++iterCigar) { + if (iterCigar->Type == 'M') { + for (unsigned int i = 0; (i < iterCigar->Length); ++i) { + if ((unsigned int)(seq_pos - alignment.start) < alignment.alignment.AlignedBases.length()) { + if (alignment.alignment.AlignedBases[seq_pos - alignment.start] == (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos)))) { + match_count++; + total_match_count++; + } else { + //snp = true; + mismatch_count++; + if (match_count > 0) {md += std::to_string(match_count);} + md += alignment.alignment.AlignedBases[seq_pos - alignment.start]; + match_count = 0; + } + } + ref_pos++; + seq_pos++; + } + } else if (iterCigar->Type == 'D') { + for (unsigned int i = 0; (i < iterCigar->Length); ++i) { + if ((unsigned int)(seq_pos - alignment.start) < alignment.alignment.AlignedBases.length()) { + if (alignment.alignment.AlignedBases[seq_pos - alignment.start] == '-') { + if (match_count > 0) {md += std::to_string(match_count);} + md += "^"; + md += (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos))); + match_count = 0; + } + } + ref_pos++; + seq_pos++; + } + } else if (iterCigar->Type == 'I') { + seq_pos += iterCigar->Length; + } + } + if (match_count > 0) {md += std::to_string(match_count);} + // calculate ZP tag + vector phase_params; + unsigned int max_size = 0; + for (vector >::iterator iter = phase_params_.begin(); (iter != phase_params_.end()); ++iter) { + if (iter->size() > max_size) {max_size = iter->size();} + } + vector values; + unsigned int index = 0; + while (index < max_size) { + values.clear(); + for (vector >::iterator iter = phase_params_.begin(); (iter != phase_params_.end()); ++iter) { + if (index < iter->size()) {values.push_back(iter->at(index));} + } + if (values.size() > 0) { + phase_params.push_back(mean(values)); + } + index++; + } + alignment.alignment.AddTag("NM", "i", mismatch_count); + alignment.alignment.AddTag("XM", "i", total_match_count); + alignment.alignment.AddTag("MD", "Z", md); + alignment.alignment.AddTag("ZF", "i", start_flow_); + alignment.alignment.AddTag("ZP", phase_params); + alignment.alignment.AddTag("ZM", measurements); + + // New tags "ZR", "ZN", "ZS" from consensus + /* + vec_temp.resize(measurements_sd.size()); + for (unsigned int i_flow = 0; i_flow < measurements_sd.size(); ++i_flow){ + vec_temp[i_flow] = (int16_t) (measurements_sd[i_flow] * 256.0f); + } + alignment.EditTag("ZS", vec_temp); + */ + alignment.alignment.AddTag("ZR", "i", (int) family_members.size()); // ZR tag = read count, number of reads that form the consensus read + string read_names = ""; + for (unsigned int i_member = 0; i_member < family_members.size(); ++i_member){ + read_names += family_members[i_member]->alignment.Name; + if (i_member != family_members.size() - 1){ + read_names += ";"; + } + } + for (string::iterator c_it = read_names.begin(); c_it != read_names.end(); ++c_it) + if (*c_it == ':') {*c_it = '.';} + alignment.alignment.AddTag("ZN", "Z", read_names); // ZN tag = query names of the reads that from the consensus read + + if (flow_consensus_) { + if (alignment.read_bases.length() != alignment.flow_index.size()) { + error_ = true; + cerr << "consensus " << alignment.alignment.Name << " read_bases.length() " << alignment.read_bases.length() << " flow_index.size() " << alignment.flow_index.size() << endl; + } else { + if (reverse_) {RevComplementInPlace(alignment.read_bases);} + unsigned int index = 0; + for (vector::iterator iter = alignment.flow_index.begin(); (iter != alignment.flow_index.end()); ++iter) { + if (((*iter) < (int)flow_order_.length()) and (alignment.read_bases[index] != flow_order_[*iter])) { + error_ = true; + cerr << "consensus " << alignment.alignment.Name << " base mismatch at " << index << endl; + break; + } + index++; + } + unsigned int flow = start_flow_; + unsigned int base_index = 0; + while (base_index < query_bases.length() and flow < flow_order_.length()) { + while (flow < flow_order_.length() and flow_order_[flow] != alignment.read_bases[base_index]) { + flow++; + } + if (flow >= flow_order_.length()) {break;} + base_index++; + } + if (reverse_) {RevComplementInPlace(alignment.read_bases);} + if (base_index != query_bases.length()) { + error_ = true; + cerr << "WARNING in MolecularTag::CalculateConsensus: There are more bases in the read than fit into the flow order.\t" << alignment.alignment.Name << endl; + cerr << query_bases << endl; + } + } + } + if (error_) {cerr << "Read(s) have flow errors. Consensus rejected.\t" << alignment.alignment.Name << endl;} + if ((not error_) and ((debug_) or (show_variants))) { + cerr << alignment.alignment.Name << " " << (alignment.is_reverse_strand ? "reverse" : "forward") << endl << endl; + string ref_seq = ""; + string allele = ""; + string ref = ""; + string gt = ""; + unsigned int pos = 0; + unsigned int ref_pos = alignment.start; + unsigned int seq_pos = alignment.start; + unsigned int variant_count = 0; + for (vector::iterator iterCigar = alignment.alignment.CigarData.begin(); (iterCigar != alignment.alignment.CigarData.end()); ++iterCigar) { + if (iterCigar->Type == 'M') { + for (unsigned int i = 0; (i < iterCigar->Length); ++i) { + ref = (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos))); + if ((unsigned int)(seq_pos - alignment.start) < alignment.alignment.AlignedBases.length()) { + if (alignment.alignment.AlignedBases[seq_pos - alignment.start] != (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos)))) { + pos = ref_pos; + allele = alignment.alignment.AlignedBases[seq_pos - alignment.start]; + gt = "0/1"; + if ((allele == "A") or (allele == "C") or (allele == "G") or (allele == "T")) {gt = "1/1";} + if ((allele == "R") and (ref == "A")) {allele = "G";} + else if ((allele == "R") and (ref == "G")) {allele = "A";} + else if (allele == "R") {allele = "A,G"; gt = "1/2";} + else if ((allele == "Y") and (ref == "C")) {allele = "T";} + else if ((allele == "Y") and (ref == "T")) {allele = "C";} + else if (allele == "Y") {allele = "C,T"; gt = "1/2";} + else if ((allele == "S") and (ref == "G")) {allele = "C";} + else if ((allele == "S") and (ref == "C")) {allele = "G";} + else if (allele == "S") {allele = "C,G"; gt = "1/2";} + else if ((allele == "W") and (ref == "A")) {allele = "T";} + else if ((allele == "W") and (ref == "T")) {allele = "A";} + else if (allele == "W") {allele = "A,T"; gt = "1/2";} + else if ((allele == "K") and (ref == "G")) {allele = "T";} + else if ((allele == "K") and (ref == "T")) {allele = "G";} + else if (allele == "K") {allele = "G,T"; gt = "1/2";} + else if ((allele == "M") and (ref == "A")) {allele = "C";} + else if ((allele == "M") and (ref == "C")) {allele = "A";} + else if (allele == "M") {allele = "A,C"; gt = "1/2";} + if (alignment.alignment.RefID + 1 == 23) { + cerr << "chrX" << "\t" << (pos + 1) << "\t" << "." << "\t" << (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos))) << "\t" << allele << "\t" << 100 << "\t" << "PASS" << "\t" << "TYPE=snp" << "\t" << "GT" << "\t" << gt << ":" << endl; + } else { + cerr << "chr" << (alignment.alignment.RefID + 1) << "\t" << (pos + 1) << "\t" << "." << "\t" << (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos))) << "\t" << allele << "\t" << 100 << "\t" << "PASS" << "\t" << "TYPE=snp" << "\t" << "GT" << "\t" << gt << ":" << endl; + } + std::transform(ref.begin(), ref.end(), ref.begin(), ::tolower); + variant_count++; + } + } + ref_seq += ref; + ref_pos++; + seq_pos++; + } + } else if (iterCigar->Type == 'D') { + pos = ref_pos - 1; + ref = (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos - 1))); + allele = alignment.alignment.AlignedBases[seq_pos - alignment.start - 1]; + for (unsigned int i = 0; (i < iterCigar->Length); ++i) { + ref += (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos))); + ref_seq += (char)(*(ref_reader.iter(alignment.alignment.RefID, ref_pos))); + ref_pos++; + seq_pos++; + } + if (alignment.alignment.RefID + 1 == 23) { + cerr << "chrX" << "\t" << (pos + 1) << "\t" << "." << "\t" << ref << "\t" << allele << "\t" << 100 << "\t" << "PASS" << "\t" << "TYPE=del" << "\t" << "GT" << "\t" << "0/1:" << endl; + } else { + cerr << "chr" << (alignment.alignment.RefID + 1) << "\t" << (pos + 1) << "\t" << "." << "\t" << ref << "\t" << allele << "\t" << 100 << "\t" << "PASS" << "\t" << "TYPE=del" << "\t" << "GT" << "\t" << "0/1:" << endl; + } + variant_count++; + } else if (iterCigar->Type == 'I') { + pos = ref_pos - 1; + allele = alignment.alignment.AlignedBases[seq_pos - alignment.start - 1]; + for (unsigned int i = 0; (i < iterCigar->Length); ++i) { + allele += alignment.alignment.AlignedBases[seq_pos - alignment.start]; + ref_seq += "-"; + seq_pos++; + } + if (alignment.alignment.RefID + 1 == 23) { + cerr << "chrX" << "\t" << (pos + 1) << "\t" << "." << "\t" << (char)(*(ref_reader.iter(alignment.alignment.RefID, pos))) << "\t" << allele << "\t" << 100 << "\t" << "PASS" << "\t" << "TYPE=ins" << "\t" << "GT" << "\t" << "0/1:" << endl; + } else { + cerr << "chr" << (alignment.alignment.RefID + 1) << "\t" << (pos + 1) << "\t" << "." << "\t" << (char)(*(ref_reader.iter(alignment.alignment.RefID, pos))) << "\t" << allele << "\t" << 100 << "\t" << "PASS" << "\t" << "TYPE=ins" << "\t" << "GT" << "\t" << "0/1:" << endl; + } + variant_count++; + } + } + cerr << endl; + for (vector::iterator iter = aligned_bases_.begin(); (iter != aligned_bases_.end()); ++iter) { + cerr << *iter << endl; + } + cerr << endl << ref_seq << endl; + cerr << alignment.alignment.AlignedBases << endl; + cerr << endl << "Done" << endl; + } + SetFlowConsensus(flow_consensus_temp); + return (not error_); +} + +void MolecularTagManager::Initialize(MolecularTagTrimmer* const input_tag_trimmer, const SampleManager* const sample_manager) +{ + tag_trimmer = input_tag_trimmer; + if (not tag_trimmer->HaveTags()){ + multisample_prefix_tag_struct_.clear(); + multisample_suffix_tag_struct_.clear(); + return; + } + multisample_prefix_tag_struct_.assign(sample_manager->num_samples_, ""); + multisample_suffix_tag_struct_.assign(sample_manager->num_samples_, ""); + cout << "MolecularTagManager: Found "<< tag_trimmer->NumTaggedReadGroups() << " read group(s) with molecular tags." << endl; + if (tag_trimmer->NumReadGroups() > tag_trimmer->NumTaggedReadGroups()){ + cerr << "Warning: MolecularTagManager: Molecular tags not found in read group(s) {"; + for (map::const_iterator rg_it = sample_manager->read_group_to_sample_idx_.begin(); rg_it != sample_manager->read_group_to_sample_idx_.end(); ++rg_it){ + const string& read_group = rg_it->first; + if (not tag_trimmer->HasTags(read_group)){ + cerr << read_group << ", "; + } + } + cerr << "}. The reads in these group(s) may be filtered out." << endl; + } + + // Check the uniqueness of tag structures cross the read groups in each sample. + for (map::const_iterator rg_it = sample_manager->read_group_to_sample_idx_.begin(); rg_it != sample_manager->read_group_to_sample_idx_.end(); ++rg_it){ + const int& sample_idx = rg_it->second; + const string& read_group = rg_it->first; + if (not tag_trimmer->HasTags(read_group)){ + // I implicitly allow a sample has a read group has tag but another read group doesn't. + continue; + } + if (multisample_prefix_tag_struct_[sample_idx].empty() and multisample_suffix_tag_struct_[sample_idx].empty()){ + multisample_prefix_tag_struct_[sample_idx] = tag_trimmer->GetPrefixTag(read_group); + multisample_suffix_tag_struct_[sample_idx] = tag_trimmer->GetSuffixTag(read_group); + } + else{ + if (multisample_prefix_tag_struct_[sample_idx] != tag_trimmer->GetPrefixTag(read_group) + or multisample_suffix_tag_struct_[sample_idx] != tag_trimmer->GetSuffixTag(read_group)){ + cerr << "ERROR: MolecularTagManager: Variable tag structures found in the sample " << sample_manager->sample_names_[sample_idx] << " !" << endl; + exit(-1); + return; + } + } + } + + for (unsigned int sample_idx = 0; sample_idx < sample_manager->sample_names_.size(); ++sample_idx){ + cout <<"MolecularTagManager: Found the unique molecular tag structures in the sample "<< sample_manager->sample_names_[sample_idx] << ": "; + cout <<"prefix tag = "<< (multisample_prefix_tag_struct_[sample_idx].empty()? "NULL" : multisample_prefix_tag_struct_[sample_idx]) << ", "; + cout <<"suffix tag = "<< (multisample_suffix_tag_struct_[sample_idx].empty()? "NULL" : multisample_suffix_tag_struct_[sample_idx]) << endl << endl; + + } +} + +bool IsStrictness(const string& mol_tag, const string& tag_struct) +{ + if ((mol_tag.size() != tag_struct.size())){ + return false; + } + + string::const_iterator tag_it = mol_tag.begin(); + for (string::const_iterator struct_it = tag_struct.begin(); struct_it != tag_struct.end(); ++struct_it){ + if ((*struct_it != 'N') and (*tag_it != *struct_it)){ + return false; + } + ++tag_it; + } return true; } +// return true if the tags match the tag structures. +bool MolecularTagManager::IsStrictTag(const string& prefix_tag, const string& suffix_tag, int sample_idx) const +{ + sample_idx = max(0, sample_idx); // sample_idx = -1 indicates no multisample + if (not IsStrictness(prefix_tag, multisample_prefix_tag_struct_[sample_idx])){ + return false; + } + if (not IsStrictness(suffix_tag, multisample_suffix_tag_struct_[sample_idx])){ + return false; + } + return true; +} + +// sample_idx = -1 indicates no multisample +bool MolecularTagManager::IsStrictSuffixTag(const string& suffix_tag, int sample_idx) const +{ + return IsStrictness(suffix_tag, multisample_suffix_tag_struct_[max(0, sample_idx)]); +} + +// sample_idx = -1 indicates no multisample +bool MolecularTagManager::IsStrictPrefixTag(const string& prefix_tag, int sample_idx) const +{ + return IsStrictness(prefix_tag, multisample_prefix_tag_struct_[max(0, sample_idx)]); +} + +MolecularTagManager::MolecularTagManager() +{ + multisample_prefix_tag_struct_.clear(); + multisample_suffix_tag_struct_.clear(); + tag_trimmer = NULL; +} + +// I stole the terms flat and run from Earl's indel assembly algorithm. a) flat = nuc of homopolymer, b) run = length of homopolymer +// Example: base_seq = "TAACGGG" gives flat_and_run = {('T', 1), ('A', 2), ('C', 1), ('G': 3)} +void BaseSeqToFlatAndRun(const string& base_seq, vector >& flat_and_run) +{ + flat_and_run.clear(); + if (base_seq.empty()){ + return; + } + flat_and_run.reserve(base_seq.size()); + flat_and_run.push_back(pair(base_seq[0], 1)); + for (int base_idx = 1; base_idx < (int) base_seq.size(); ++base_idx){ + if (base_seq[base_idx] == base_seq[base_idx - 1]){ + ++(flat_and_run.back().second); + }else{ + flat_and_run.push_back(pair(base_seq[base_idx], 1)); + } + } +} + +// Convert the vector of flat&run pair to the base sequence. +string FlatAndRunToBaseSeq(const vector >::const_iterator& it_begin, const vector >::const_iterator& it_end) +{ + string base_seq(""); + for (vector >::const_iterator it = it_begin; it != it_end; ++it){ + base_seq += string(it->second, it->first); + } + return base_seq; +} + +// Claim the two vectors of flat&run pairs synchronized if the following two criteria are both satisfied +// a) they have the same flat (shrank to the smaller length) +// b) the discrepancy between the runs <= allowed_hp_indel_len. +bool IsSyncFlatAndRun(const vector >::const_iterator it_1_begin, + const vector >::const_iterator it_1_end, + const vector >::const_iterator it_2_begin, + const vector >::const_iterator it_2_end, + int allowed_hp_indel_len) +{ + vector >::const_iterator it_1 = it_1_begin; + vector >::const_iterator it_2 = it_2_begin; + + while (it_1 != it_1_end and it_2 != it_2_end){ + if (it_1->first != it_2->first + or abs(it_1->second - it_2->second) > allowed_hp_indel_len){ + return false; + } + ++it_1; + ++it_2; + } + return true; +} + +bool MolecularTagManager::IsFlowSynchronizedTags(string tag_1, string tag_2, bool is_prefix) const +{ + if (tag_1.size() != tag_2.size()){ + return false; + } + + if (not is_prefix){ + // Be consistent with the tag trimming direction in BaseCaller. + reverse(tag_1.begin(), tag_1.end()); + reverse(tag_2.begin(), tag_2.end()); + } + int allowed_hp_indel_len = 1; + vector > flat_and_run_1; + vector > flat_and_run_2; + BaseSeqToFlatAndRun(tag_1, flat_and_run_1); + BaseSeqToFlatAndRun(tag_2, flat_and_run_2); + return IsSyncFlatAndRun(flat_and_run_1.begin(), flat_and_run_1.end(), flat_and_run_2.begin(), flat_and_run_2.end(), allowed_hp_indel_len); +} + +// Determine whether 2 tags are "partial similar" in the sense that the molecular tag may suffer from PCR error and sequencing error. +// By adjusting the length of the homopolymers appropriately, if the delta between the two tags is nothing or just "one" SNP, then I claim they are similar. +bool MolecularTagManager::IsPartialSimilarTags(string tag_1, string tag_2, bool is_prefix) const +{ + if (tag_1.size() != tag_2.size()){ + return false; + } + + if (not is_prefix){ + // Be consistent with the tag trimming direction in BaseCaller. + reverse(tag_1.begin(), tag_1.end()); + reverse(tag_2.begin(), tag_2.end()); + } -long long int NucToLongLong(char nuc) { - if (nuc=='A' or nuc=='a') return 0; - if (nuc=='C' or nuc=='c') return 1; - if (nuc=='G' or nuc=='g') return 2; - if (nuc=='T' or nuc=='t') return 3; - return -1; + // I allow "one" SNP and (allowed_hp_indel_len)-mer HP-INDELs. + int allowed_hp_indel_len = 1; + const static vector kAllNucs = {"T", "A", "C", "G"}; + vector > flat_and_run_1; + vector > flat_and_run_2; + BaseSeqToFlatAndRun(tag_1, flat_and_run_1); + BaseSeqToFlatAndRun(tag_2, flat_and_run_2); + vector >::iterator it_1 = flat_and_run_1.begin(); + vector >::iterator it_2 = flat_and_run_2.begin(); + bool is_not_begin = false; + + while (it_1 != flat_and_run_1.end() and it_2 != flat_and_run_2.end()){ + // Is "flat" the same? + if (it_1->first == it_2->first){ + if (is_not_begin){ + // For similar tags, every large HP-INDEL must be explained by a SNP. Otherwise, I claim not similar. + if (abs((it_1 - 1)->second - (it_2 - 1)->second) > allowed_hp_indel_len){ + return false; + } + } + ++it_1; + ++it_2; + }else{ + // Now the "flats" of the two tag are different. + // Since I only allow one SNP, the brute-force algorithm can be very efficient. + // Let's try all 6 possible combinations for altering the first base of the homopolymer at the flats. See if I can obtain two synchronized vectors of flat&run pairs. + + // Set the anchors + string tag_1_anchor = ""; + string tag_2_anchor = ""; + vector >::iterator anchor_it_1 = it_1; + vector >::iterator anchor_it_2 = it_2; + if (is_not_begin){ + --anchor_it_1; + --anchor_it_2; + tag_1_anchor = string(anchor_it_1->second, anchor_it_1->first); + tag_2_anchor = string(anchor_it_2->second, anchor_it_2->first); + } + + // Set the paddings + --(it_1->second); + --(it_2->second); + string tag_1_padding = FlatAndRunToBaseSeq(it_1, flat_and_run_1.end()); + string tag_2_padding = FlatAndRunToBaseSeq(it_2, flat_and_run_2.end()); + ++(it_1->second); + ++(it_2->second); + + for (int try_nuc_idx = 0; try_nuc_idx < 4; ++try_nuc_idx){ + if (kAllNucs[try_nuc_idx][0] != it_1->first){ + // Let's alter one base (where the base is the first base of the flat&run pair *it_1) of tag_1 to kAllNucs[try_nuc_idx][0] + string try_base_seq_1 = tag_1_anchor + kAllNucs[try_nuc_idx] + tag_1_padding; + vector > try_flat_and_run_1; + BaseSeqToFlatAndRun(try_base_seq_1, try_flat_and_run_1); + if (IsSyncFlatAndRun(try_flat_and_run_1.begin(), try_flat_and_run_1.end(), anchor_it_2 ,flat_and_run_2.end(), allowed_hp_indel_len)){ + return true; + } + } + if (kAllNucs[try_nuc_idx][0] != it_2->first){ + // Let's alter one base (where the base is the first base of the flat&run pair *it_2) of tag_2 to kAllNucs[try_nuc_idx][0] + string try_base_seq_2 = tag_2_anchor + kAllNucs[try_nuc_idx] + tag_2_padding; + vector > try_flat_and_run_2; + BaseSeqToFlatAndRun(try_base_seq_2, try_flat_and_run_2); + if (IsSyncFlatAndRun(try_flat_and_run_2.begin(), try_flat_and_run_2.end(), anchor_it_1, flat_and_run_1.end(), allowed_hp_indel_len)){ + return true; + } + } + } + // One SNP + HP-INDELs can't explain the delta between the two tags. Thus I claim not partial similar. + return false; + } + is_not_begin = true; + } + return true; +} + + + +char MolecularFamilyGenerator::NucTo0123_(char nuc) const +{ + if (nuc == 'A') { return 0; } + if (nuc == 'C') { return 1; } + if (nuc == 'G') { return 2; } + if (nuc == 'T') { return 3; } + if (nuc == 'a') { return 0; } + if (nuc == 'c') { return 1; } + if (nuc == 'g') { return 2; } + if (nuc == 't') { return 3; } + return -1; } -//@TODO: This can be done even faster by using memset -long long BarcodeStrToLongLong(const string &barcode_str){ - // I currently support the molecular barcode with total length < 32. - if(barcode_str.size() > 31){ - cerr<<"Warning: Length of the molecular barcode "<< barcode_str << " > 31 !"< 31){ + cerr << "ERROR: Cannot hash a string of char TACG of length >= 32 to a 64-bit long long integer." << endl; + exit(1); return -1; } - long long barcode_long_long = 0; - for(string::const_iterator it = barcode_str.begin(); it != barcode_str.end(); ++it){ - long long nuc_long_long = NucToLongLong(*it); - if(nuc_long_long < 0){ - return -1; // I see something not TACG - } - barcode_long_long <<= 2; - barcode_long_long |= nuc_long_long; - } - return barcode_long_long; -} - - -void GenerateMyMolecularFamilies(PositionInProgress &bam_position, - vector< vector< MolecularFamily > > &my_molecular_families, - const ExtendParameters ¶meters, - int sample_index){ - //MolecularTagClassifier mol_tag_classifier; - vector< map > barcode_lookup_table; // lookup table for barcodes - barcode_lookup_table.resize(2); - //mol_tag_classifier.PropagateTagParameters(parameters.my_mol_param); - // my_molecular_families[0] for fwd strand, my_molecular_families[1] for rev strand - my_molecular_families.clear(); - my_molecular_families.resize(2); - my_molecular_families[0].reserve(6000); // reserve 6000 families on each strand - my_molecular_families[1].reserve(6000); - - for(Alignment* rai = bam_position.begin; rai != bam_position.end; rai = rai->next){ - if (rai == NULL) {bam_position.end = NULL; break;} - // skip the read if it is not for this sample - if(parameters.multisample){ - if(rai->sample_index != sample_index){ - continue; - } - } - if(rai->filtered or (not rai->tag_info.HasTags())){ - continue; - } - - string barcode_of_read = rai->tag_info.prefix_mol_tag + rai->tag_info.suffix_mol_tag; - long long barcode_long_long = BarcodeStrToLongLong(barcode_of_read); - - if(barcode_long_long < 0){ - cerr<<"Warning: Skip the unsupported molecular barcode "<< barcode_of_read << " !"<< endl; - rai->filtered = true; + + long long base_seq_long_long = 0; + char* long_long_tail_byte = (char*) &base_seq_long_long; // pointer to the first byte of base_seq_long_long, like a bit mask. + bool non_valid_string = false; + for (string::const_iterator it = base_seq.begin(); it != base_seq.end(); ++it) { + char nuc_in_0123 = NucTo0123_(*it); + non_valid_string += (nuc_in_0123 < 0); + base_seq_long_long <<= 2; // shift 2 bits to the left + *long_long_tail_byte |= nuc_in_0123; // set the first 2 bits of barcode_long_long from the right to be nuc_in_0123 + } + if (non_valid_string){ + cerr << "ERROR: Invalid molecular tag that contains a non-TACG character." << endl; + exit(1); + return -1; + } + return base_seq_long_long; +} + +void MolecularFamilyGenerator::FindFamilyForOneRead_(Alignment* rai, vector< vector >& my_molecular_families) +{ + string mol_tag = rai->tag_info.prefix_mol_tag + rai->tag_info.suffix_mol_tag; + int strand_key = (rai->is_reverse_strand)? 1 : 0; + bool is_new_tag = true; + unsigned int tag_index_in_my_molecular_families = 0; + + // Hashing mol_tag to a long long integer facilitates the mapping between the mol_tag to the index of my_molecular_families. + // Note that the length of the mol_tag must be < 32. + if (long_long_hashable_){ + long long long_long_tag = BaseSeqToLongLong_(mol_tag); + pair< map::iterator, bool> tag_finder; + // map mol_tag_long_long to the index of my_family_[strand_key] for mol_tag + tag_finder = long_long_tag_lookup_table_[strand_key].insert(pair(long_long_tag, my_molecular_families[strand_key].size())); + // Note that map::insert will not insert value into the key of the map if key is pre-existed. + // tag_finder.first->first = mol_tag_long_long + // tag_finder.first->second = the index of my_molecular_families[strand_key] for tag + // tag_finder.second indicates inserted or not. + // tag_finder.second = false if I got mol_tag previously, and hence long_long_tag_lookup_table_ is not updated. + // tag_finder.second = true if this is the first time we get mol_tag, and hence mol_tag_long_long is inserted into long_long_tag_lookup_table_[strand_key] + + is_new_tag = tag_finder.second; + tag_index_in_my_molecular_families = tag_finder.first->second; + } + // Map a string to the index of my_molecular_families, slow but always safe. + else{ + pair< map::iterator, bool> tag_finder; + tag_finder = string_tag_lookup_table_[strand_key].insert(pair(mol_tag, my_molecular_families[strand_key].size())); + is_new_tag = tag_finder.second; + tag_index_in_my_molecular_families = tag_finder.first->second; + } + + if (is_new_tag){ + // Generate a new family since this is the first time I get the mol_tag + my_molecular_families[strand_key].push_back(MolecularFamily(mol_tag, strand_key)); + } + // Add the read to the family + my_molecular_families[strand_key][tag_index_in_my_molecular_families].AddNewMember(rai); +} + +// Generate molecular families +// Inputs: bam_position, sample_index, +// Output: my_molecular_families +// Set sample_index = -1 if not multi-sample. +void MolecularFamilyGenerator::GenerateMyMolecularFamilies(const MolecularTagManager* const mol_tag_manager, + PositionInProgress& bam_position, + int sample_index, + vector< vector >& my_molecular_families) +{ + bool is_consensus_bam = false; + unsigned int prefix_tag_len = (mol_tag_manager->GetPrefixTagStruct(sample_index)).size(); + unsigned int suffix_tag_len = (mol_tag_manager->GetSuffixTagStruct(sample_index)).size(); + long_long_hashable_ = (prefix_tag_len + suffix_tag_len) < 32; + + my_molecular_families.resize(2); // my_molecular_families[0] for fwd strand, my_molecular_families[1] for rev strand + long_long_tag_lookup_table_.resize(2); + string_tag_lookup_table_.resize(2); + + for (int i_strand = 0; i_strand < 2; ++i_strand){ + my_molecular_families[i_strand].resize(0); + my_molecular_families[i_strand].reserve(20000); // Reverse for 20000 families (including non-functional ones) per strand should be enough most of the time. + long_long_tag_lookup_table_[i_strand].clear(); + string_tag_lookup_table_[i_strand].clear(); + } + + for (Alignment* rai = bam_position.begin; rai != bam_position.end; rai = rai->next) { + if (rai == NULL) { + bam_position.end = NULL; + return; + } + + if (rai->filtered or (not rai->tag_info.HasTags())) { continue; - } - - int strand_key = (rai->is_reverse_strand)? 1 : 0; - - pair< map::iterator, bool> find_barcode; - // map barcode_of_read to the index of my_family_[strand_key] for barcode_of_read - find_barcode = barcode_lookup_table[strand_key].insert(pair(barcode_long_long, my_molecular_families[strand_key].size())); - // Note that - // find_barcode.first->first == barcode_of_read - // find_barcode.first->second is the index of my_family_[strand_key] for barcode_of_read - // find_barcode.second == true if we got barcode_of_read previously and hence we don't change barcode_lookup_table - // find_barcode.second == false if this is the first time we get barcode_of_read and we insert the barcode_lookup_table into barcode_lookup_table[strand_key] - - // Initialize if this is the first time we get the barcode - if(find_barcode.second){ - my_molecular_families[strand_key].push_back(MolecularFamily(barcode_of_read, strand_key)); - } - // add the read to the family - // note that find_barcode.first->second == barcode_lookup_table[strand_key][barcode_of_read] - my_molecular_families[strand_key][find_barcode.first->second].AddNewMember(rai); - } + } + + // skip the read if it is not for this sample + if( sample_index >=0 and rai->sample_index != sample_index) { + continue; + } + + // Tag length check + if ((rai->tag_info.prefix_mol_tag.size() > 0 and rai->tag_info.prefix_mol_tag.size() != prefix_tag_len) + or (rai->tag_info.suffix_mol_tag.size() > 0 and rai->tag_info.suffix_mol_tag.size() != suffix_tag_len)){ + cerr << "MolecularFamilyGenerator: Warning: The molecular tag length of the read "<< rai->alignment.Name << " doesn't match the tag structure provided in the bam header." << endl; + continue; + } + + // Skip the read whose tags don't exactly match the tag structures if I use strict trim. + // Note mol_tag_manager->tag_trimmer->tag_trim_method_ = 0, 1 indicates strict, sloppy, respectively. + if (mol_tag_manager->tag_trimmer->GetTagTrimMethod() == 0){ + if (not mol_tag_manager->IsStrictTag(rai->tag_info.prefix_mol_tag, rai->tag_info.suffix_mol_tag, sample_index)){ + continue; + } + } + + // If any read has read_count > 1 then I am dealing with a consensus bam + is_consensus_bam += (rai->read_count > 1); + FindFamilyForOneRead_(rai, my_molecular_families); + } + + if (is_split_families_by_region_){ + SplitFamiliesByRegion_(my_molecular_families); + } + + // Finally, sort the members in each family by the read counts if it is a consensus bam + for (vector >::iterator strand_it = my_molecular_families.begin(); strand_it != my_molecular_families.end(); ++strand_it){ + for (vector::iterator fam_it = strand_it->begin(); fam_it != strand_it->end(); ++fam_it){ + if (is_consensus_bam){ + fam_it->SortAllFamilyMembers(); + } + else{ + fam_it->is_all_family_members_sorted = true; // Sort no needed means sorted. + } + } + } +} + +// Split a family into multiple if the reads cover different target regions. +void MolecularFamilyGenerator::SplitFamiliesByRegion_(vector< vector >& my_molecular_families) const +{ + if (not is_split_families_by_region_){ + return; + } + for (vector >::iterator strand_it = my_molecular_families.begin(); strand_it != my_molecular_families.end(); ++strand_it){ + unsigned int original_fam_num_on_strand = strand_it->size(); + for (unsigned int i_fam = 0; i_fam < original_fam_num_on_strand; ++i_fam){ + if (strand_it->at(i_fam).all_family_members.size() < 2){ + continue; + } + bool need_split = false; + vector::iterator read_it_0 = strand_it->at(i_fam).all_family_members.begin(); + for (vector::iterator read_it = (read_it_0 + 1); read_it != strand_it->at(i_fam).all_family_members.end(); ++read_it){ + if ((*read_it)->target_coverage_indices != (*read_it_0)->target_coverage_indices){ + // Need to split the family if there is one read has a different target_coverage_indices. + need_split = true; + break; + } + } + if (need_split){ + vector splitted_families_from_one_family; + splitted_families_from_one_family.clear(); + for (vector::iterator read_it = strand_it->at(i_fam).all_family_members.begin(); read_it != strand_it->at(i_fam).all_family_members.end(); ++read_it){ + bool is_target_coverage_indices_exists = false; + vector::iterator splitted_fam_it = splitted_families_from_one_family.begin(); + for (; splitted_fam_it != splitted_families_from_one_family.end(); ++splitted_fam_it){ + if (splitted_fam_it->all_family_members[0]->target_coverage_indices == (*read_it)->target_coverage_indices){ + is_target_coverage_indices_exists = true; + break; + } + } + + if (is_target_coverage_indices_exists){ + splitted_fam_it->AddNewMember(*read_it); + }else{ + splitted_families_from_one_family.push_back(MolecularFamily(strand_it->at(i_fam).family_barcode, strand_it->at(i_fam).strand_key)); + splitted_families_from_one_family.back().AddNewMember(*read_it); + } + } + strand_it->at(i_fam).all_family_members.swap(splitted_families_from_one_family.begin()->all_family_members); + for (vector::iterator splitted_fam_it = splitted_families_from_one_family.begin() + 1; splitted_fam_it != splitted_families_from_one_family.end(); ++splitted_fam_it){ + strand_it->push_back(*splitted_fam_it); + } + } + } + } } -// Remove the non-functional families in the vector -void RemoveNonFuncFamilies(vector< MolecularFamily > &my_molecular_families, unsigned int min_fam_size){ - vector< MolecularFamily > molecular_families_temp; - molecular_families_temp.reserve(my_molecular_families.size()); - for(vector< MolecularFamily >::iterator fam_it = my_molecular_families.begin(); - fam_it != my_molecular_families.end(); ++fam_it){ - if(fam_it->SetFunctionality(min_fam_size)){ - molecular_families_temp.push_back(*fam_it); +/* +// Note that consensus_position_ticket must be generated from all samples! +// I.e., if one sample produces a candidate, then the candidate will be applied to all other samples as well. +void GenerateConsensusPositionTicket(vector< vector< vector > > &my_molecular_families_multisample, + VariantCallerContext &vc, + Consensus &consensus, + list::iterator &consensus_position_ticket, + bool filter_all_reads_after_done) { + unsigned int min_family_size = (unsigned int) vc.parameters->tag_trimmer_parameters.min_family_size; + for (vector< vector< vector< MolecularFamily> > >::iterator sample_it = my_molecular_families_multisample.begin(); sample_it != my_molecular_families_multisample.end(); ++sample_it) { + for (vector< vector< MolecularFamily> >::iterator strand_it = sample_it->begin(); strand_it != sample_it->end(); ++strand_it) { + for (vector< MolecularFamily>::iterator fam_it = strand_it->begin(); fam_it != strand_it->end(); ++fam_it) { + // Is *fam_it functional? + if (fam_it->SetFuncFromAll(min_family_size)) { + if (fam_it->all_family_members.size() >= 1) { + map > alignment_map; + for (vector::iterator iter = fam_it->all_family_members.begin(); (iter != fam_it->all_family_members.end()); ++iter) { + if (((*iter)->start > vc.bam_walker->getEndPosition()) or ((*iter)->end < vc.bam_walker->getStartPosition())) {continue;} + if ((*iter)->is_reverse_strand) { + alignment_map[1000000 + floor(((*iter)->end - vc.bam_walker->getEndPosition()) / 10.0)].push_back(*iter); + } else { + alignment_map[floor(((*iter)->start - vc.bam_walker->getStartPosition()) / 10.0)].push_back(*iter); + } + } + for (map >::iterator iter = alignment_map.begin(); (iter != alignment_map.end()); ++iter) { + Alignment* alignment = new Alignment; + const ion::FlowOrder & flow_order = vc.global_context->flow_order_vector.at(iter->second[0]->flow_order_index); + string flow_order_str; + flow_order_str.reserve(flow_order.num_flows()); + for (int i = 0; (i < flow_order.num_flows()); ++i) { + flow_order_str += flow_order.nuc_at(i); + } + bool success = true; + success = consensus.CalculateConsensus(*vc.ref_reader, iter->second, *alignment, flow_order_str); + if ((not success) or (not vc.candidate_generator->BasicFilters(*alignment))) { + delete alignment; + } else { + vc.targets_manager->TrimAmpliseqPrimers(alignment, vc.bam_walker->GetRecentUnmergedTarget()); + if (alignment->filtered) {delete alignment;} + else { + if (consensus_position_ticket->begin == NULL) {consensus_position_ticket->begin = alignment;} + if (consensus_position_ticket->end != NULL) {consensus_position_ticket->end->next = alignment;} + consensus_position_ticket->end = alignment; + } + } + } + } + } + if (filter_all_reads_after_done){ + for (vector::iterator read_it = fam_it->all_family_members.begin(); read_it != fam_it->all_family_members.end(); ++read_it) + (*read_it)->filtered = true; + } + } + } + } + consensus_position_ticket->end = NULL; +} +*/ + + +// Note that consensus_position_ticket must be generated from all samples! +// I.e., if one sample produces a candidate, then the candidate will be applied to all other samples as well. +void GenerateConsensusPositionTicket(vector< vector< vector > > &my_molecular_families_multisample, + VariantCallerContext &vc, + Consensus &consensus, + list::iterator &consensus_position_ticket, + bool filter_all_reads_after_done) +{ + unsigned int min_family_size = (unsigned int) vc.parameters->tag_trimmer_parameters.min_family_size; + for (vector< vector< vector< MolecularFamily> > >::iterator sample_it = my_molecular_families_multisample.begin(); sample_it != my_molecular_families_multisample.end(); ++sample_it) { + for (vector< vector< MolecularFamily> >::iterator strand_it = sample_it->begin(); strand_it != sample_it->end(); ++strand_it) { + for (vector< MolecularFamily>::iterator fam_it = strand_it->begin(); fam_it != strand_it->end(); ++fam_it) { + // Is *fam_it functional? + if (fam_it->SetFuncFromAll(min_family_size)) { + Alignment* alignment = new Alignment; + bool success = consensus.CalculateConsensus(*vc.ref_reader, fam_it->all_family_members, *alignment); + if ((not success) or (not vc.candidate_generator->BasicFilters(*alignment))) { + delete alignment; + } + else { + vc.targets_manager->TrimAmpliseqPrimers(alignment, vc.bam_walker->GetRecentUnmergedTarget()); + if (alignment->filtered) {delete alignment;} + else { + if (consensus_position_ticket->begin == NULL) {consensus_position_ticket->begin = alignment;} + if (consensus_position_ticket->end != NULL) {consensus_position_ticket->end->next = alignment;} + consensus_position_ticket->end = alignment; + } + } + if (filter_all_reads_after_done){ + for (vector::iterator read_it = fam_it->all_family_members.begin(); read_it != fam_it->all_family_members.end(); ++read_it) + (*read_it)->filtered = true; + } + } + } } } - my_molecular_families.swap(molecular_families_temp); + consensus_position_ticket->end = NULL; } diff --git a/Analysis/VariantCaller/MolecularTag.h b/Analysis/VariantCaller/MolecularTag.h index caaa4c6b..5bf729e8 100644 --- a/Analysis/VariantCaller/MolecularTag.h +++ b/Analysis/VariantCaller/MolecularTag.h @@ -10,85 +10,215 @@ #include #include #include +#include +#include +#include "InputStructures.h" #include "ExtendParameters.h" #include "BAMWalkerEngine.h" -#include +#include "AlleleParser.h" +#include "MolecularTagTrimmer.h" using namespace std; +class Timer { +private: + string tag_; + double run_time_; + timeval start_time_; + timeval end_time_; + pthread_mutex_t mutextimer_; +public: +Timer(const string& tag) {tag_ = tag; run_time_ = 0; pthread_mutex_init(&mutextimer_, NULL);} +virtual ~Timer() {print();} +void start() {gettimeofday(&start_time_, NULL);} +void end() { + gettimeofday(&end_time_, NULL); + pthread_mutex_lock (&mutextimer_); + run_time_ += ((end_time_.tv_sec - start_time_.tv_sec) + ((end_time_.tv_usec - start_time_.tv_usec) / 1000000.0)); + pthread_mutex_unlock (&mutextimer_); +} +void print() {cerr << tag_ << " run_time = " << run_time_ << " seconds." << endl;} +}; + class Consensus { private: + bool reverse_; + string name_; + string flow_order_; unsigned int min_start_position_; std::map insertions_; std::map new_insertions_; vector aligned_bases_; + vector read_counts_; + vector > flow_indexes_; + vector > measurement_vector_; + int start_flow_; + vector flow_index_; + vector measurements_; + vector > phase_params_; std::basic_string::size_type max_read_length_; string insertion_bases_; string consensus_; vector new_cigar_; bool debug_; - //double alignment_time = 0; - //double consensus_time = 0; - //double trim_time = 0; - //double cigar_time = 0; + bool flow_consensus_; + int flow_order_index_; + vector soft_clip_offset_; + bool error_; + bool stitch_; + float iupac_cutoff_; unsigned int GetAlignedFamily(vector& family_members); void GetConsensus(ReferenceReader& ref_reader, unsigned int RefID); unsigned int TrimConsensus(); void CalculateCigar(); - + void PartiallyResetAlignment_(Alignment& alignment); + public: Consensus(); virtual ~Consensus(); + void SetIUPACCutoff(float f) {iupac_cutoff_ = f;} + void SetStitch(bool b) {stitch_ = true;} + void SetFlowConsensus(bool b) {flow_consensus_ = b;} void SetDebug(bool b) {debug_ = b;} void GetAlignedBases(vector& v) {v = aligned_bases_;} void GetInsertionBases(string& str) {str = insertion_bases_;} void GetConsensus(string& str) {str = consensus_;} - - bool CalculateConsensus(ReferenceReader& ref_reader, vector& family_members, Alignment& alignment); + bool CalculateConsensus(ReferenceReader& ref_reader, vector& family_members, Alignment& alignment, const string& flow_order = ""); }; template -class MolecularFamily { +class AbstractMolecularFamily { public: int strand_key; string family_barcode; - vector family_members; - vector family_members_temp; // in case we want to apply additional rules to filter out some reads - Alignment consensus_alignment; - bool operator<(const MolecularFamily &rhs) const { return this->family_members_temp.size() < rhs.family_members_temp.size(); } // use for std::sort - bool is_func_family_temp; // is_func_family_temp indicates the functionality of this family if the family members are family_members_temp - - MolecularFamily(const string &barcode, int strand): family_barcode(barcode){ - strand_key = strand; - ResetFamily(); + vector all_family_members; // All reads identified in the family, no additional filtering applied. + vector valid_family_members; // The reads from all_family_members that pass certain filtering criterion. + + AbstractMolecularFamily (const string &barcode = "", int strand = -1) + : strand_key(strand), family_barcode(barcode) { all_family_members.reserve(4); }; + + virtual ~AbstractMolecularFamily(){}; + virtual int CountFamSizeFromAll() = 0; + virtual int CountFamSizeFromValid() = 0; + + bool SetFuncFromAll(unsigned int min_fam_size){ + if (fam_size_ < 0) {CountFamSizeFromAll(); } + is_func_from_family_members = (fam_size_ >= (int) min_fam_size); + return is_func_from_family_members; }; - void ResetFamily(){ - family_members.clear(); - family_members.reserve(16); - family_members_temp.clear(); - is_func_family_ = false; - is_func_family_temp = false; - consensus_alignment.Reset(); + bool SetFuncFromValid(unsigned int min_fam_size){ + if (valid_fam_size_ < 0) {CountFamSizeFromValid();} + is_func_from_valid_family_members = (valid_fam_size_ >= (int) min_fam_size); + return is_func_from_valid_family_members; }; - void AddNewMember(const MemberType &new_member) { family_members.push_back(new_member); }; - bool SetFunctionality(unsigned int min_fam_size){ - is_func_family_ = family_members.size() >= min_fam_size; - return is_func_family_; + + void AddNewMember(const MemberType& new_member) + { all_family_members.push_back(new_member); }; + + bool GetFuncFromAll() const { + assert(fam_size_ > -1); // CountFamSizeFromAll must be done first. + return is_func_from_family_members; }; - bool GetFunctionality() const { return is_func_family_; }; - + + bool GetFuncFromValid() const { + assert(valid_fam_size_ > -1); // CountFamSizeFromValid must be done first. + return is_func_from_valid_family_members; + }; + + int GetFamSize() { + if (fam_size_ < 0) { CountFamSizeFromAll(); } + return fam_size_; + }; + + int GetValidFamSize() { + if (valid_fam_size_ < 0) { CountFamSizeFromValid(); } + return valid_fam_size_; + }; + + void ResetValidFamilyMembers() { + valid_family_members.resize(0); + valid_family_members.reserve(all_family_members.size()); + valid_fam_size_ = -1; + is_func_from_valid_family_members = false; + }; + +protected: + bool is_func_from_family_members = false; + bool is_func_from_valid_family_members = false; + int fam_size_ = -1; // Total read counts in all_family_members (a consensus read counted by its read_count) + int valid_fam_size_ = -1; // Total read counts in valid_family_members (a consensus read counted by its read_count) +}; + + +class MolecularFamily : public AbstractMolecularFamily +{ +public: + MolecularFamily(const string& barcode = "", int strand = -1) + : AbstractMolecularFamily(barcode, strand) {}; + int CountFamSizeFromAll(); + int CountFamSizeFromValid(); + void SortAllFamilyMembers(); + void SortValidFamilyMembers(); + void ResetValidFamilyMembers(); + virtual ~MolecularFamily(){}; + bool is_all_family_members_sorted = false; + bool is_valid_family_members_sorted = false; +}; + + +// An interface of handling the tag structures. +class MolecularTagManager +{ private: - bool is_func_family_; + vector multisample_prefix_tag_struct_; + vector multisample_suffix_tag_struct_; + +public: + MolecularTagManager(); + MolecularTagTrimmer* tag_trimmer; + void Initialize(MolecularTagTrimmer* const input_tag_trimmer, const SampleManager* const sample_manager); + bool IsStrictTag(const string& prefix_tag, const string& suffix_tag, int sample_idx) const; + bool IsStrictPrefixTag(const string& prefix_tag, int sample_idx) const; + bool IsStrictSuffixTag(const string& suffix_tag, int sample_idx) const; + string GetPrefixTagStruct(int sample_indx) const {return multisample_prefix_tag_struct_[max(0, sample_indx)];}; // sample_idx = -1 indicates no multisample + string GetSuffixTagStruct(int sample_indx) const {return multisample_suffix_tag_struct_[max(0, sample_indx)];}; // sample_idx = -1 indicates no multisample + bool IsPartialSimilarTags(string tag_1, string tag_2, bool is_prefix) const; + bool IsFlowSynchronizedTags(string tag_1, string tag_2, bool is_prefix) const; }; -void GenerateMyMolecularFamilies(PositionInProgress &bam_position, - vector< vector< MolecularFamily > > &my_molecular_families, - const ExtendParameters ¶meters, - int sample_index = -1); +class MolecularFamilyGenerator +{ +private: + bool long_long_hashable_ = false; + const bool is_split_families_by_region_ = true; // I will always split families by region. + vector< map > long_long_tag_lookup_table_; + vector< map > string_tag_lookup_table_; + void SplitFamiliesByRegion_(vector< vector >& my_molecular_families) const; + void FindFamilyForOneRead_(Alignment* rai, vector< vector >& my_molecular_families); + long long BaseSeqToLongLong_(const string& base_seq) const; + char NucTo0123_(char nuc) const; +public: + MolecularFamilyGenerator() {}; + void GenerateMyMolecularFamilies(const MolecularTagManager* const mol_tag_manager, + PositionInProgress& bam_position, + int sample_index, + vector< vector >& my_molecular_families); +}; + + +void GenerateConsensusPositionTicket(vector< vector< vector > > &my_molecular_families_multisample, + VariantCallerContext &vc, + Consensus &consensus, + list::iterator &consensus_position_ticket, + bool filter_all_reads_after_done = false); -void RemoveNonFuncFamilies(vector< MolecularFamily > &my_molecular_families, unsigned int min_fam_size); +void GenerateCandidatesFromConsensusPositionTicket(AlleleParser* candidate_generator, + const BAMWalkerEngine* bam_walker, + deque& variant_candidates, + list::iterator& consensus_position_ticket, + int haplotype_length); #endif /* MOLECULARTAG_H */ diff --git a/Analysis/VariantCaller/OrderedVCFWriter.h b/Analysis/VariantCaller/OrderedVCFWriter.h index ba74f333..318163fa 100644 --- a/Analysis/VariantCaller/OrderedVCFWriter.h +++ b/Analysis/VariantCaller/OrderedVCFWriter.h @@ -35,7 +35,7 @@ class OrderedVCFWriter { } - void Initialize(const string& output_vcf, const ExtendParameters& parameters, ReferenceReader& ref_reader, const SampleManager& sample_manager) { + void Initialize(const string& output_vcf, const ExtendParameters& parameters, ReferenceReader& ref_reader, const SampleManager& sample_manager, bool use_molecular_tag = false) { string filtered_vcf; size_t pos = output_vcf.rfind("."); @@ -57,7 +57,7 @@ class OrderedVCFWriter { } suppress_no_calls_ = parameters.my_controls.suppress_no_calls; - string vcf_header = getVCFHeader(¶meters, ref_reader, sample_manager.sample_names_, sample_manager.primary_sample_); + string vcf_header = getVCFHeader(¶meters, ref_reader, sample_manager.sample_names_, sample_manager.primary_sample_, use_molecular_tag); output_vcf_stream_ << vcf_header << endl; filtered_vcf_stream_ << vcf_header << endl; variant_initializer_.parseHeader(vcf_header); diff --git a/Analysis/VariantCaller/Reads/ExtendedReadInfo.cpp b/Analysis/VariantCaller/Reads/ExtendedReadInfo.cpp index 372f7410..767dbb5c 100644 --- a/Analysis/VariantCaller/Reads/ExtendedReadInfo.cpp +++ b/Analysis/VariantCaller/Reads/ExtendedReadInfo.cpp @@ -27,7 +27,8 @@ void CreateFlowIndex(Alignment *rai, const ion::FlowOrder & flow_order) base_idx++; } if (base_idx != rai->read_bases.length()) { - cerr << "WARNING in ExtendedReadInfo::CreateFlowIndex: There are more bases in the read than fit into the flow order."; + cerr << "ERROR: in ExtendedReadInfo::CreateFlowIndex: There are more bases in the read than fit into the flow order.\t" << rai->alignment.Name << endl; + cerr << rai->read_bases << endl; exit(1); } } @@ -107,6 +108,7 @@ void UnpackAlignmentInfo(Alignment *rai) // ------------------------------------------------------- // Unpacking read meta data and filtering read if this is not possible +// This function is called after the candidate generator's UnpackReadAlleles void UnpackOnLoad(Alignment *rai, const InputStructures &global_context) { @@ -116,6 +118,7 @@ void UnpackOnLoad(Alignment *rai, const InputStructures &global_context) rai->is_reverse_strand = rai->alignment.IsReverseStrand(); + if (not rai->alignment.GetTag("ZR", rai->read_count)) {rai->read_count = 1;} // Parse read name, run id & flow order index rai->runid.clear(); @@ -156,9 +159,28 @@ void UnpackOnLoad(Alignment *rai, const InputStructures &global_context) } rai->measurements.assign(global_context.num_flows_by_run_id.at(rai->runid), 0.0); for (size_t counter = 0; counter < quantized_measurements.size(); ++counter) - rai->measurements[counter] = (float)quantized_measurements[counter]/256; + rai->measurements[counter] = (float) quantized_measurements[counter] / 256.0f; rai->measurements_length = quantized_measurements.size(); + // Retrieve measurements standard deviation from ZS tag + quantized_measurements.resize(0); // I reuse quantized_measurements for getting the ZS tag. + // measurements_sd appears only if it is a consensus read + if (rai->read_count > 1){ + if (rai->alignment.GetTag("ZS", quantized_measurements)) { + if ((int) quantized_measurements.size() != rai->measurements_length){ + cerr << "ERROR: Normalized measurements ZM:tag length " << rai->measurements_length + << " != measurements standard deviation ZS:tag length " << quantized_measurements.size() + <<" in read " << rai->alignment.Name << endl; + exit(1); + } + rai->measurements_sd.assign(quantized_measurements.size(), 0.0f); + for (size_t counter = 0; counter < quantized_measurements.size(); ++counter) + rai->measurements_sd[counter] = (float) quantized_measurements[counter] / 256.0f; + } + + } + + // Retrieve phasing parameters from ZP tag if (not rai->alignment.GetTag("ZP", rai->phase_params)) { @@ -200,12 +222,12 @@ void UnpackOnLoad(Alignment *rai, const InputStructures &global_context) rai->start_flow = 0; if (not rai->alignment.GetTag("ZF", rai->start_flow)) { - uint8_t start_flow_byte = 0; - if (not rai->alignment.GetTag("ZF", start_flow_byte)) { + uint32_t start_flow = 0; + if (not rai->alignment.GetTag("ZF", start_flow)) { cerr << "ERROR: Start Flow ZF:tag not found in read " << rai->alignment.Name << endl; exit(1); } - rai->start_flow = (int)start_flow_byte; + rai->start_flow = (int) start_flow; } if (rai->start_flow == 0) { cerr << "WARNING: Start Flow ZF:tag has zero value in read " << rai->alignment.Name << endl; @@ -263,7 +285,239 @@ void UnpackOnLoad(Alignment *rai, const InputStructures &global_context) } +// Unpack the information used to generate flow space consensus +void UnpackOnLoadLight(Alignment *rai, const InputStructures &global_context) +{ + // No need to waste time if the read is filtered + if (rai->filtered) + return; + + if (not rai->alignment.GetTag("ZR", rai->read_count)) {rai->read_count = 1;} + + // I really need the ZA tag! + + rai->is_reverse_strand = rai->alignment.IsReverseStrand(); + + // Parse read name, run id & flow order index + + rai->runid.clear(); + if (not rai->alignment.Name.empty()) { + rai->well_rowcol.resize(2); + ion_readname_to_rowcol(rai->alignment.Name.c_str(), &rai->well_rowcol[0], &rai->well_rowcol[1]); + // extract runid while we are at it + rai->runid = rai->alignment.Name.substr(0,rai->alignment.Name.find(":")); + } + + if (rai->runid.empty()){ + cerr << "WARNING: Unable to determine run id of read " << rai->alignment.Name << endl; + rai->filtered = true; + return; + } + + map::const_iterator fo_it = global_context.flow_order_index_by_run_id.find(rai->runid); + if (fo_it == global_context.flow_order_index_by_run_id.end()){ + cerr << "WARNING: No matching flow oder found for read " << rai->alignment.Name << endl; + rai->filtered = true; + return; + } + + rai->flow_order_index = fo_it->second; + const ion::FlowOrder & flow_order = global_context.flow_order_vector.at(rai->flow_order_index); + + // Retrieve measurements from ZM tag + // No need to do it here to reduce memory usage + /* + vector quantized_measurements; + if (not rai->alignment.GetTag("ZM", quantized_measurements)) { + cerr << "ERROR: Normalized measurements ZM:tag is not present in read " << rai->alignment.Name << endl; + exit(1); + } + if ((int)quantized_measurements.size() > global_context.num_flows_by_run_id.at(rai->runid)) { + cerr << "ERROR: Normalized measurements ZM:tag length " << quantized_measurements.size() + << " exceeds flow order length " << global_context.num_flows_by_run_id.at(rai->runid) + <<" in read " << rai->alignment.Name << endl; + exit(1); + } + + rai->measurements.assign(global_context.num_flows_by_run_id.at(rai->runid), 0.0); + for (size_t counter = 0; counter < quantized_measurements.size(); ++counter) + rai->measurements[counter] = (float) quantized_measurements[counter] / 256.0f; + rai->measurements_length = quantized_measurements.size(); + */ + if (not rai->alignment.HasTag("ZM")) { + cerr << "ERROR: Normalized measurements ZM:tag is not present in read " << rai->alignment.Name << endl; + exit(1); + } + + + // Retrieve phasing parameters from ZP tag + + if (not rai->alignment.GetTag("ZP", rai->phase_params)) { + cerr << "ERROR: Phasing Parameters ZP:tag is not present in read " << rai->alignment.Name << endl; + exit(1); + } + if (rai->phase_params.size() != 3) { + cerr << "ERROR: Phasing Parameters ZP:tag does not have 3 phase parameters in read " << rai->alignment.Name << endl; + exit(1); + } + if (rai->phase_params[0] < 0 or rai->phase_params[0] > 1 or rai->phase_params[1] < 0 or rai->phase_params[1] > 1 + or rai->phase_params[2] < 0 or rai->phase_params[2] > 1) { + cerr << "ERROR: Phasing Parameters ZP:tag outside of [0,1] range in read " << rai->alignment.Name << endl; + exit(1); + } + + //rai->phase_params[2] = 0.0f; // ad-hoc corrector: zero droop + + // Populate read_bases (bases without rev-comp on reverse-mapped reads) and flow_index + + // No need to do it here to reduce memory usage + /* + rai->read_bases = rai->alignment.QueryBases; + if (rai->is_reverse_strand) + RevComplementInPlace(rai->read_bases); + if (rai->read_bases.empty()){ + cerr << "WARNING: Ignoring length zero read " << rai->alignment.Name << endl; + rai->filtered = true; + return; + } + */ + if (rai->alignment.QueryBases.empty()){ + cerr << "WARNING: Ignoring length zero read " << rai->alignment.Name << endl; + rai->filtered = true; + return; + } + + rai->start_flow = 0; + if (not rai->alignment.GetTag("ZF", rai->start_flow)) { + uint32_t start_flow = 0; + if (not rai->alignment.GetTag("ZF", start_flow)) { + cerr << "ERROR: Start Flow ZF:tag not found in read " << rai->alignment.Name << endl; + exit(1); + } + rai->start_flow = (int) start_flow; + } + if (rai->start_flow == 0) { + cerr << "WARNING: Start Flow ZF:tag has zero value in read " << rai->alignment.Name << endl; + rai->filtered = true; + return; + } + + + // Check validity of input arguments + if (rai->start_flow < 0 or rai->start_flow >= global_context.num_flows_by_run_id.at(rai->runid)) { + cerr << "ERROR: Start flow outside of [0,num_flows) range in read " << rai->alignment.Name << endl; + cerr << "Start flow: " << rai->start_flow << " Number of flows: " << global_context.flow_order_vector.at(rai->flow_order_index).num_flows(); + exit(1); + } + + // Retrieve read group name & generate prefix flow + + if (not rai->alignment.GetTag("RG",rai->read_group)) { + cerr << "WARNING: No read group found in read " << rai->alignment.Name << endl; + // No big problem, we'll just have to solve the prefix like it's 2013! + rai->read_group.clear(); + } + + // Get read prefix - hard clipped start of the read: [KS][ZT][ZE] + rai->prefix_flow = -1; + map::const_iterator key_it = global_context.key_by_read_group.find(rai->read_group); + if (key_it != global_context.key_by_read_group.end()) { + rai->prefix_bases = key_it->second; + + string temp_zt, temp_ze; + if (rai->alignment.GetTag("ZT", temp_zt)) + rai->prefix_bases += temp_zt; + if (rai->alignment.GetTag("ZE", temp_ze)) + rai->prefix_bases += temp_ze; + + if (not rai->prefix_bases.empty()) + GetPrefixFlow(rai, rai->prefix_bases, flow_order); + } + + // Get read suffix + rai->suffix_bases.clear(); + string temp_yt, temp_ye; + if (rai->alignment.GetTag("YE", temp_ye)) + rai->suffix_bases += temp_ye; + if (rai->alignment.GetTag("YT", temp_yt)) + rai->suffix_bases += temp_yt; + + if (not rai->prefix_bases.empty()) + GetPrefixFlow(rai, rai->prefix_bases, flow_order); + + + // Check consistency of prefix_flow and start_flow - maybe we don't have all info about hard clipped bases + char read_bases_at_0 = rai->is_reverse_strand? NucComplement(rai->alignment.QueryBases.back()) : rai->alignment.QueryBases[0]; + if (rai->prefix_flow >= 0) { + int check_start_flow = rai->prefix_flow; + while (check_start_flow < flow_order.num_flows() and flow_order.nuc_at(check_start_flow) != read_bases_at_0) + check_start_flow++; + if (check_start_flow != rai->start_flow) { + rai->prefix_flow = -1; + rai->prefix_bases.clear(); + cerr << "WARNING: Missing hard-clipped prefix bases in read "<< rai->alignment.Name << endl; + rai->filtered = true; + cerr << "start_flow = "<< rai->start_flow<<", check_start_flow = " << check_start_flow<::iterator target_idx_it = rai->target_coverage_indices.begin(); target_idx_it != rai->target_coverage_indices.end(); ++target_idx_it){ + int read_mismatch_limit_by_target = targets_manager->unmerged[*target_idx_it].read_mismatch_limit; + // See if override by this target? (-1 means no override) + if (read_mismatch_limit_by_target >= 0){ + if (is_override_by_target){ + // The read covers multiple targets with override. + if (read_mismatch_limit == 0 or read_mismatch_limit_by_target == 0){ + read_mismatch_limit = max(read_mismatch_limit, read_mismatch_limit_by_target); + }else{ + read_mismatch_limit = min(read_mismatch_limit, read_mismatch_limit_by_target); + } + }else{ + // This is the first time I see the override. + is_override_by_target = true; + read_mismatch_limit = read_mismatch_limit_by_target; + } + } + } + } + + if (rai->filtered or read_mismatch_limit == 0){ + return; + } + + int modified_read_mismatches = 0; + if (not rai->alignment.GetTag("NM", modified_read_mismatches)){ + cerr << "WARNING: Filter out the read "<< rai->alignment.Name << " w/o NM tag. Set read-mismatch-limit=0 to disable the check." << endl; + rai->filtered = true; + return; + } + // Note that the NM tag is number of all mismatched "bases", e.g. one gap open of X bases counts X. + for (vector::iterator cigar_it = rai->alignment.CigarData.begin(); cigar_it != rai->alignment.CigarData.end(); ++cigar_it){ + if ((cigar_it->Type == 'I' or cigar_it->Type == 'D') and cigar_it->Length > 0){ + modified_read_mismatches -= (int) (cigar_it->Length - 1); // One gap open or one insertion counts one in modified mismatches. + } + } + if (modified_read_mismatches < 0){ + // Something very wrong that I can catch. + cerr << "WARNING: The read "<< rai->alignment.Name << " is filtered out because of inconsistent CIGAR and NM tags. Set read-mismatch-limit=0 to disable the check." << endl; + rai->filtered = true; + } + if (modified_read_mismatches > read_mismatch_limit){ + rai->filtered = true; + } +} diff --git a/Analysis/VariantCaller/Reads/ExtendedReadInfo.h b/Analysis/VariantCaller/Reads/ExtendedReadInfo.h index d435d1e7..6dc9e06a 100644 --- a/Analysis/VariantCaller/Reads/ExtendedReadInfo.h +++ b/Analysis/VariantCaller/Reads/ExtendedReadInfo.h @@ -19,10 +19,7 @@ using namespace std; struct Alignment; void UnpackOnLoad(Alignment *rai, const InputStructures &global_context); - -//! @brief Creates a stack of reads that provide evidence in the case of our candidate variant -void StackUpOneVariant(vector& read_stack, int variant_start_pos, int variant_end_pos, - const ExtendParameters ¶meters, const PositionInProgress& bam_position); - +void UnpackOnLoadLight(Alignment *rai, const InputStructures &global_context); +void FilterByModifiedMismatches(Alignment *rai, int read_mismatch_limit, const TargetsManager *const targets_manager); #endif //EXTENDEDREADINFO_H diff --git a/Analysis/VariantCaller/SampleManager.cpp b/Analysis/VariantCaller/SampleManager.cpp index 6f976177..74b14ffe 100644 --- a/Analysis/VariantCaller/SampleManager.cpp +++ b/Analysis/VariantCaller/SampleManager.cpp @@ -12,26 +12,31 @@ // const string& force_sample_name, // const string& sample_name, +// ----------------------------------------------------------------------------------------- +// Function extracts the samples associated with read groups in bam_header +// primary_sample_name [in]: sets a primary sample name (default is first found in header) +// only the primary sample will be analyzed if we don't specify multi-sample analysis +// force_sample_name [in]: ignores all information in the BAM and force analyzes as one sample +// multisample [in/out]: activates multi-sample analysis for more than one sample in BAM -void SampleManager::Initialize (const SamHeader& bam_header, string& sample_name, const string& force_sample_name) + +void SampleManager::Initialize (const SamHeader& bam_header, const string& primary_sample_name, const string& force_sample_name, bool &multisample) { + // Iterate through samples in BAM header to extract sample information num_samples_ = 0; - + primary_sample_name_ = primary_sample_name; + for (SamReadGroupConstIterator read_group = bam_header.ReadGroups.Begin(); read_group < bam_header.ReadGroups.End(); ++read_group) { - string barcode = read_group->PlatformUnit; - string::size_type pos = barcode.rfind("/"); - if (pos != string::npos) {barcode = barcode.substr(pos + 1);} + string sample_name; if (force_sample_name.empty()) { - //sample_name = read_group->Sample + "." + barcode; sample_name = read_group->Sample; } else { sample_name = force_sample_name; } - if (read_group->ID.empty()) { cerr << "ERROR: One of BAM read groups is missing ID tag" << endl; exit(1); @@ -65,46 +70,57 @@ void SampleManager::Initialize (const SamHeader& bam_header, string& sample_name // if it's the same sample name and RG combo, no worries // TODO: what about other tags } + //cout << "SampleManager: Read group " << read_group->ID << " is associated with sample " << sample_name << endl; + } if (num_samples_ == 0) { cerr << "ERROR: BAM file(s) do not have any read group definitions" << endl; exit(1); } + // Do we have a multi-sample analysis? + else if (num_samples_ > 1) { + if (not multisample and primary_sample_name_.empty()){ + cerr << "ERROR: SampleManager: Multiple Samples (" << num_samples_ << ") found in BAM file/s provided. "<< endl; + //cerr << "ERROR: But neither a primary sample was provided nor multi-sample analysis enabled." << endl; + cerr << "ERROR: Please select primary sample name to process using the \"--sample-name\" parameter. " << endl; + //cerr << "ERROR: AND/OR enable multi-sample analysis using the \"--multisample-analysis\" parameter. " << endl; + exit(EXIT_FAILURE); + } + } + else // We only have one sample + multisample = false; + // Search for specified primary sample or select a default primary (first available) bool default_sample = false; - //now check if there are multiple samples in the BAM file and if so user should provide a sampleName to process - if (num_samples_ == 1 && sample_name.empty()) { - sample_name = sample_names_[0]; + if (primary_sample_name_.empty()) { + primary_sample_name_ = sample_names_[0]; default_sample = true; - - } else if (num_samples_ > 1 && sample_name.empty()) { - sample_name = sample_names_[0]; - //cerr << "ERROR: Multiple Samples found in BAM file/s provided. Torrent Variant Caller currently supports variant calling on only one sample. " << endl; - //cerr << "ERROR: Please select sample name to process using -g parameter. " << endl; - //exit(1); } bool primary_sample_found = false; for (int i = 0; i < num_samples_; ++i) { - if (sample_names_[i] == sample_name) { + if (sample_names_[i] == primary_sample_name_) { primary_sample_ = i; primary_sample_found = true; } + // AWalt added this because of IR-19679? + // TODO investigate why this is actually necessary? else { - string test = sample_name + "."; + string test = primary_sample_name_ + "."; if (strncmp(sample_names_[i].c_str(), test.c_str(), test.length()) == 0) { primary_sample_ = i; primary_sample_found = true; + primary_sample_name_ = sample_names_[primary_sample_]; } } } if (!primary_sample_found) { - cerr << "ERROR: Sample " << sample_name << " provided using -g option " + cerr << "ERROR: Sample " << primary_sample_name << " provided using \"--sample-name\" option " << "is not associated with any read groups in BAM file(s)" << endl; - exit(1); + exit(EXIT_FAILURE); } //now find the read group ID associated with this sample name @@ -113,20 +129,22 @@ void SampleManager::Initialize (const SamHeader& bam_header, string& sample_name if (primary_sample_ == p->second) num_primary_read_groups++; + if (multisample) + cout << "SampleManager: Multi-sample analysis enabled." << endl; if (!force_sample_name.empty()) cout << "SampleManager: All read groups forced to assume sample name " << force_sample_name << endl; cout << "SampleManager: Found " << read_group_to_sample_idx_.size() << " read group(s) and " << num_samples_ << " sample(s)." << endl; if (default_sample) - cout << "SampleManager: Primary sample \"" << sample_name << "\" (default) present in " << num_primary_read_groups << " read group(s)" << endl; + cout << "SampleManager: Primary sample \"" << primary_sample_name_ << "\" (default) present in " << num_primary_read_groups << " read group(s)" << endl; else - cout << "SampleManager: Primary sample \"" << sample_name << "\" (set via -g) " << num_primary_read_groups << " read group(s)" << endl; + cout << "SampleManager: Primary sample \"" << primary_sample_name_ << "\" (set via -g) " << num_primary_read_groups << " read group(s)" << endl; } +// -------------------------------------------------------------------- +// This function populates sample_index and primary_sample flag for read alignment objects - -//bool SampleManager::IdentifySample(Alignment& ra) const bool SampleManager::IdentifySample(const BamAlignment& alignment, int& sample_index, bool& primary_sample) const { diff --git a/Analysis/VariantCaller/SampleManager.h b/Analysis/VariantCaller/SampleManager.h index d431a6a0..cefc13e0 100644 --- a/Analysis/VariantCaller/SampleManager.h +++ b/Analysis/VariantCaller/SampleManager.h @@ -23,13 +23,14 @@ class SampleManager { SampleManager() : num_samples_(0), primary_sample_(0) {} ~SampleManager() {} - void Initialize (const SamHeader& bam_header, string& sample_name, const string& force_sample_name); + void Initialize (const SamHeader& bam_header, const string& primary_sample_name, const string& force_sample_name, bool &multisample); bool IdentifySample(const BamAlignment& alignment, int& sample_index, bool& primary_sample) const; - int num_samples_; - vector sample_names_; - map read_group_to_sample_idx_; - int primary_sample_; + int num_samples_; // Number of samples found in BAM(s) + vector sample_names_; // Names of samples + map read_group_to_sample_idx_; // Map of read group names to sample index + int primary_sample_; // index of the primary sample + string primary_sample_name_; // short for sample_names_[primary_sample_] }; diff --git a/Analysis/VariantCaller/Splice/ClassifyVariant.cpp b/Analysis/VariantCaller/Splice/ClassifyVariant.cpp index ecd55771..dce2af46 100644 --- a/Analysis/VariantCaller/Splice/ClassifyVariant.cpp +++ b/Analysis/VariantCaller/Splice/ClassifyVariant.cpp @@ -6,7 +6,7 @@ #include "ClassifyVariant.h" #include "ErrorMotifs.h" -#include "StackEngine.h" +#include "CrossHypotheses.h" // This function only works for the 1Base -> 1 Base snp representation void AlleleIdentity::SubCategorizeSNP(const LocalReferenceContext &reference_context) { @@ -202,35 +202,26 @@ void AlleleIdentity::SubCategorizeMNP(const LocalReferenceContext &reference_con // Test whether this is an HP-InDel void AlleleIdentity::IdentifyHPdeletion(const LocalReferenceContext& reference_context) { - // Get right anchor for better HP-InDel classification - right_anchor = 0; - // It's a deletion, so reference allele must be longer than alternative allele - int shorter_test_pos = altAllele.length() - 1; - int longer_test_pos = reference_context.reference_allele.length() - 1; - while (shorter_test_pos >= left_anchor and - altAllele[shorter_test_pos] == reference_context.reference_allele[longer_test_pos]) { - right_anchor++; - shorter_test_pos--; - longer_test_pos--; - } - - if (left_anchor+right_anchor < (int)altAllele.length()){ + if (left_anchor+right_anchor != (int) altAllele.length()){ // If the anchors do not add up to the length of the shorter allele, // a more complex substitution happened and we don't classify as HP-InDel status.isHPIndel = false; } else { - status.isHPIndel = reference_context.my_hp_length[left_anchor] > 1; - for (int i_base=left_anchor+1; (status.isHPIndel and i_base<(int)reference_context.reference_allele.length()-right_anchor); i_base++){ - status.isHPIndel = status.isHPIndel and (reference_context.my_hp_length[left_anchor] > 1); - } - } - inDelLength = reference_context.reference_allele.length() - altAllele.length(); + //status.isHPIndel = (left_anchor+right_anchor) == (int) altAllele.length(); + //for (int i_base=left_anchor+1; (status.isHPIndel and i_base<(int)reference_context.reference_allele.length()-right_anchor); i_base++){ + // status.isHPIndel = status.isHPIndel and (reference_context.my_hp_length[left_anchor] > 1); + //} + string padding_ref = string(1, reference_context.ref_left_hp_base) + reference_context.reference_allele + string(1, reference_context.ref_right_hp_base); + string padding_alt = string(1, reference_context.ref_left_hp_base) + altAllele + string(1, reference_context.ref_right_hp_base); + status.isHPIndel = IsHpIndel(padding_ref, padding_alt); + } + inDelLength = abs((int) reference_context.reference_allele.length() - (int) altAllele.length()); } // Test whether this is an HP-InDel void AlleleIdentity::IdentifyHPinsertion(const LocalReferenceContext& reference_context, - const ReferenceReader &ref_reader, int chr_idx) { + const ReferenceReader &ref_reader) { char ref_base_right_of_anchor; int ref_right_hp_length = 0; @@ -254,7 +245,7 @@ void AlleleIdentity::IdentifyHPinsertion(const LocalReferenceContext& reference_ status.isHPIndel = true; ref_hp_length = ref_right_hp_length; } - inDelLength = altAllele.length() - reference_context.reference_allele.length(); + inDelLength = abs((int) altAllele.length() - (int) reference_context.reference_allele.length()); if (status.isHPIndel) { for (int b_idx = left_anchor + 1; b_idx < left_anchor + inDelLength; b_idx++) { @@ -263,7 +254,7 @@ void AlleleIdentity::IdentifyHPinsertion(const LocalReferenceContext& reference_ } } else if (inDelLength == 1) { status.isHPIndel = IdentifyDyslexicMotive(altAllele[left_anchor], reference_context.position0+left_anchor, - ref_reader, chr_idx); + ref_reader, reference_context.chr_idx); } } @@ -318,7 +309,7 @@ bool AlleleIdentity::IdentifyDyslexicMotive(char base, int position, // We categorize InDels bool AlleleIdentity::SubCategorizeInDel(const LocalReferenceContext& reference_context, - const ReferenceReader &ref_reader, int chr_idx) { + const ReferenceReader &ref_reader) { // These fields are set no matter what status.isDeletion = (reference_context.reference_allele.length() > altAllele.length()); @@ -329,7 +320,7 @@ bool AlleleIdentity::SubCategorizeInDel(const LocalReferenceContext& reference_c ref_hp_length = reference_context.my_hp_length[left_anchor]; } else { // Insertion - IdentifyHPinsertion(reference_context, ref_reader, chr_idx); + IdentifyHPinsertion(reference_context, ref_reader); } if (DEBUG > 0){ @@ -345,7 +336,7 @@ bool AlleleIdentity::SubCategorizeInDel(const LocalReferenceContext& reference_c bool AlleleIdentity::CharacterizeVariantStatus(const LocalReferenceContext &reference_context, - const ReferenceReader &ref_reader, int chr_idx) + const ReferenceReader &ref_reader) { //cout << "Hello from CharacterizeVariantStatus; " << altAllele << endl; bool is_ok = true; @@ -356,37 +347,58 @@ bool AlleleIdentity::CharacterizeVariantStatus(const LocalReferenceContext &refe status.isPaddedSNP = false; status.doRealignment = false; - // Get Anchor length + if (altAllele == reference_context.reference_allele){ + status.isNoVariant = true; + status.isProblematicAllele = true; + status.isSNP = (altAllele.size() == 1); + status.isMNV = not (status.isSNP); + is_ok = false; + // I don't want to continue since it is not a variant. + return is_ok; + } + + // Get left anchor length ref_hp_length = reference_context.my_hp_length[0]; left_anchor = 0; - unsigned int a_idx = 0; - while (a_idx < altAllele.length() and a_idx < reference_context.reference_allele.length() - and altAllele[a_idx] == reference_context.reference_allele[a_idx]) { - a_idx++; - left_anchor++; + while (left_anchor < (int) altAllele.length() and left_anchor < ref_length + and altAllele[left_anchor] == reference_context.reference_allele[left_anchor]) { + ++left_anchor; + } + // Get right anchor length + // right anchor is obtained after I remove left anchor, which implies I prefer left alignment. + right_anchor = 0; + // It's a deletion, so reference allele must be longer than alternative allele + int alt_test_pos = (int) altAllele.length() - 1; + int ref_test_pos = ref_length - 1; + while (alt_test_pos >= left_anchor + and ref_test_pos >= left_anchor + and altAllele[alt_test_pos] == reference_context.reference_allele[ref_test_pos]) { + right_anchor++; + alt_test_pos--; + ref_test_pos--; + } + // Calculate the variant window (in reference coordinate) + start_variant_window = position0 + num_padding_added.first; + end_variant_window = position0 + ref_length - num_padding_added.second; + if (num_padding_added.first > 0 or num_padding_added.second > 0){ + assert(reference_context.reference_allele.substr(0, num_padding_added.first) == altAllele.substr(0, num_padding_added.first)); + assert(reference_context.reference_allele.substr((int) reference_context.reference_allele.size() - num_padding_added.second) == altAllele.substr((int) altAllele.size() - num_padding_added.second)); + assert(start_variant_window < end_variant_window); } - if (DEBUG > 0) - cout << "- Alternative Allele " << altAllele << " (anchor length " << left_anchor << ") "; + if (DEBUG > 0) + cout << "- Alternative Allele " << altAllele << " (left anchor length = " << left_anchor << " , right anchor length = " << right_anchor << ")"; - const string& ref_allele = reference_context.reference_allele; - const string& alt_allele = altAllele; - int ref_length = ref_allele.length(); - int alt_length = alt_allele.length(); - while (alt_length > 1 and ref_length > 1 and alt_allele[alt_length-1] == ref_allele[ref_length-1]) { - --alt_length; - --ref_length; - } - int prefix = 0; - while (prefix < alt_length and prefix < ref_length and alt_allele[prefix] == ref_allele[prefix]) - ++prefix; - ref_length -= prefix; - alt_length -= prefix; + // ref_length is the length of the anchor-removed reference allele + int ref_length = (int) reference_context.reference_allele.length() - (left_anchor + right_anchor); + // alt_length is the length of the anchor-removed alt allele + int alt_length = (int) altAllele.length() - (left_anchor + right_anchor); + assert(ref_length >= 0 and alt_length >= 0); // Change classification to better reflect what we can get with haplotyping if (altAllele.length() != reference_context.reference_allele.length()) { status.isIndel = true; - is_ok = SubCategorizeInDel(reference_context, ref_reader, chr_idx); + is_ok = SubCategorizeInDel(reference_context, ref_reader); } else if ((int)altAllele.length() == 1) { // Categorize function only works with this setting status.isSNP = true; @@ -396,8 +408,7 @@ bool AlleleIdentity::CharacterizeVariantStatus(const LocalReferenceContext &refe } else { status.isMNV = true; ref_hp_length = reference_context.my_hp_length[left_anchor]; - if (ref_length == 1 and alt_length == 1) - status.isPaddedSNP = true; + status.isPaddedSNP = (ref_length == 1 and alt_length == 1); SubCategorizeMNP(reference_context); if (DEBUG > 0) cout << " is an MNP." << endl; @@ -434,28 +445,34 @@ bool AlleleIdentity::getVariantType( const TIonMotifSet & ErrorMotifs, const ClassifyFilters &filter_variant, const ReferenceReader &ref_reader, - int chr_idx) { + const pair &alt_orig_padding) { altAllele = _altAllele; + position0 = (int) reference_context.position0; + ref_length = (int) reference_context.reference_allele.length(); + chr_idx = reference_context.chr_idx; + num_padding_added = alt_orig_padding; + bool is_ok = reference_context.context_detected; + // check position does not beyond the chromosome + is_ok *= not ((reference_context.position0 + (long) altAllele.length()) > ref_reader.chr_size(reference_context.chr_idx)); - if ((reference_context.position0 + (long)altAllele.length()) > ref_reader.chr_size(chr_idx)) { - is_ok = false; - } + // check alternative allele contains TACG only + is_ok *= CheckValidAltAllele(reference_context); // We should now be guaranteed a valid variant position in here if (is_ok) { - is_ok = CharacterizeVariantStatus(reference_context, ref_reader, chr_idx); - PredictSequenceMotifSSE(reference_context, ErrorMotifs, ref_reader, chr_idx); + is_ok = CharacterizeVariantStatus(reference_context, ref_reader); } - is_ok = is_ok and CheckValidAltAllele(reference_context); - if (!is_ok) { + if (is_ok) { + PredictSequenceMotifSSE(reference_context, ErrorMotifs, ref_reader); + }else{ status.isProblematicAllele = true; filterReasons.push_back("BADCANDIDATE"); } - return(is_ok); + return is_ok; } @@ -474,54 +491,45 @@ void AlleleIdentity::ModifyStartPosForAllele(int variantPos) { // Logic: When shifting a window of the same period as the MNR, the base entering the window has to be equal to the base leaving the window. // example with period 2: XYZACACA|CA|CACAIJK bool AlleleIdentity::IdentifyMultiNucRepeatSection(const LocalReferenceContext &seq_context, unsigned int rep_period, - const ReferenceReader &ref_reader, int chr_idx) { + const ReferenceReader &ref_reader) { //cout << "Hello from IdentifyMultiNucRepeatSection with period " << rep_period << "!"<< endl; unsigned int variantPos = seq_context.position0 + left_anchor; - if (variantPos + rep_period >= (unsigned long)ref_reader.chr_size(chr_idx)) - return (false); - - CircluarBuffer window(rep_period); - for (unsigned int idx = 0; idx < rep_period; idx++) - window.assign(idx, ref_reader.base(chr_idx,variantPos+idx)); + if (variantPos + rep_period >= (unsigned long)ref_reader.chr_size(seq_context.chr_idx)) + return false; - // Investigate (inclusive) start position of MNR region - start_window = variantPos - 1; // 1 anchor base - window.shiftLeft(1); - while (start_window > 0 and window.first() == ref_reader.base(chr_idx,start_window)) { - start_window--; - window.shiftLeft(1); - } + CircluarBuffer window(0); + start_splicing_window = seq_context.FindSplicingStartForMNR(ref_reader, variantPos, rep_period, window); // Investigate (exclusive) end position of MNR region - end_window = variantPos + rep_period; - if (end_window >= ref_reader.chr_size(chr_idx)) + end_splicing_window = variantPos + rep_period; + if (end_splicing_window >= ref_reader.chr_size(seq_context.chr_idx)) return false; for (unsigned int idx = 0; idx < rep_period; idx++) - window.assign(idx, ref_reader.base(chr_idx,variantPos+idx)); + window.assign(idx, ref_reader.base(seq_context.chr_idx,variantPos+idx)); window.shiftRight(1); - while (end_window < ref_reader.chr_size(chr_idx) and window.last() == ref_reader.base(chr_idx,end_window)) { - end_window++; + while (end_splicing_window < ref_reader.chr_size(seq_context.chr_idx) and window.last() == ref_reader.base(seq_context.chr_idx,end_splicing_window)) { + end_splicing_window++; window.shiftRight(1); } //cout << "Found repeat stretch of length: " << (end_window - start_window) << endl; // Require that a stretch of at least 3*rep_period has to be found to count as a MNR - if ((end_window - start_window) >= (3*(int)rep_period)) { + if ((end_splicing_window - start_splicing_window) >= (3*(int)rep_period)) { // Correct start and end of the window if they are not fully outside variant allele - if (start_window >= seq_context.position0) - start_window = seq_context.my_hp_start_pos[0] - 1; - if (end_window <= seq_context.right_hp_start) { + if (start_splicing_window >= seq_context.position0) + start_splicing_window = seq_context.StartSplicingExpandFromMyHpStart0(); + if (end_splicing_window <= seq_context.right_hp_start) { if (status.isInsertion) - end_window = seq_context.right_hp_start + seq_context.right_hp_length + 1; + end_splicing_window = seq_context.right_hp_start + seq_context.right_hp_length + 1; else - end_window = seq_context.right_hp_start + 1; + end_splicing_window = seq_context.right_hp_start + 1; } - if (start_window < 0) - start_window = 0; - if (end_window > ref_reader.chr_size(chr_idx)) - end_window = ref_reader.chr_size(chr_idx); + if (start_splicing_window < 0) + start_splicing_window = 0; + if (end_splicing_window > ref_reader.chr_size(seq_context.chr_idx)) + end_splicing_window = ref_reader.chr_size(seq_context.chr_idx); return (true); } else @@ -532,26 +540,27 @@ bool AlleleIdentity::IdentifyMultiNucRepeatSection(const LocalReferenceContext & // ----------------------------------------------------------------- -void AlleleIdentity::CalculateWindowForVariant(const LocalReferenceContext &seq_context, int DEBUG, - const ReferenceReader &ref_reader, int chr_idx) { +void AlleleIdentity::CalculateWindowForVariant(const LocalReferenceContext &seq_context, + const ReferenceReader &ref_reader) { // If we have an invalid vcf candidate, set a length zero window and exit if (!seq_context.context_detected or status.isProblematicAllele) { - start_window = seq_context.position0; - end_window = seq_context.position0; + cout <<" I am probematic!" < 0) { cout << "MNR found in allele " << seq_context.reference_allele << " -> " << altAllele << endl; - cout << "Window for allele " << altAllele << ": (" << start_window << ") "; - for (int p_idx = start_window; p_idx < end_window; p_idx++) - cout << ref_reader.base(chr_idx,p_idx); - cout << " (" << end_window << ") " << endl; + cout << "Window for allele " << altAllele << ": (" << start_splicing_window << ") "; + for (int p_idx = start_splicing_window; p_idx < end_splicing_window; p_idx++) + cout << ref_reader.base(seq_context.chr_idx,p_idx); + cout << " (" << end_splicing_window << ") " << endl; } return; // Found a matching period and computed window } @@ -559,44 +568,47 @@ void AlleleIdentity::CalculateWindowForVariant(const LocalReferenceContext &seq_ // not an MNR. Moving on along to InDels. if (status.isIndel) { // Default variant window - end_window = seq_context.right_hp_start +1; // Anchor base to the right of allele - start_window = seq_context.position0; + end_splicing_window = seq_context.right_hp_start +1; // Anchor base to the right of allele + start_splicing_window = seq_context.StartSplicingNoExpansion(); // Adjustments if necessary if (status.isDeletion) if (seq_context.my_hp_start_pos[left_anchor] == seq_context.my_hp_start_pos[0]) - start_window = seq_context.my_hp_start_pos[0] - 1; + start_splicing_window = seq_context.StartSplicingExpandFromMyHpStart0(); if (status.isInsertion) { if (left_anchor == 0) { - start_window = seq_context.my_hp_start_pos[0] - 1; + start_splicing_window = seq_context.StartSplicingExpandFromMyHpStart0(); } else if (altAllele[left_anchor] == altAllele[left_anchor - 1] and seq_context.position0 > (seq_context.my_hp_start_pos[left_anchor - 1] - 1)) { - start_window = seq_context.my_hp_start_pos[left_anchor - 1] - 1; + start_splicing_window = seq_context.StartSplicingExpandFromMyHpStartLeftAnchor(left_anchor); } if (altAllele[altAllele.length() - 1] == seq_context.ref_right_hp_base) { - end_window += seq_context.right_hp_length; + end_splicing_window += seq_context.right_hp_length; } } // Safety - if (start_window < 0) - start_window = 0; - if (end_window > ref_reader.chr_size(chr_idx)) - end_window = ref_reader.chr_size(chr_idx); + if (start_splicing_window < 0) + start_splicing_window = 0; + if (end_splicing_window > ref_reader.chr_size(seq_context.chr_idx)) + end_splicing_window = ref_reader.chr_size(seq_context.chr_idx); } else { // SNPs and MNVs are 1->1 base replacements - start_window = seq_context.position0; - end_window = seq_context.position0 + seq_context.reference_allele.length(); - } // */ + start_splicing_window = seq_context.StartSplicingNoExpansion(); + end_splicing_window = seq_context.position0 + seq_context.reference_allele.length(); + } + + // Final safety: splicing window is a super set of the interval spanned by the reference allele. + assert(start_splicing_window <= (int) seq_context.position0 and end_splicing_window >= (int) seq_context.position0 + (int) seq_context.reference_allele.length()); if (DEBUG > 0) { - cout << "Window for allele " << altAllele << ": (" << start_window << ") "; - for (int p_idx = start_window; p_idx < end_window; p_idx++) - cout << ref_reader.base(chr_idx,p_idx); - cout << " (" << end_window << ") " << endl; + cout << "Window for allele " << altAllele << ": (" << start_splicing_window << ") "; + for (int p_idx = start_splicing_window; p_idx < end_splicing_window; p_idx++) + cout << ref_reader.base(seq_context.chr_idx,p_idx); + cout << " (" << end_splicing_window << ") " << endl; } } @@ -606,7 +618,7 @@ void AlleleIdentity::CalculateWindowForVariant(const LocalReferenceContext &seq_ void AlleleIdentity::PredictSequenceMotifSSE(const LocalReferenceContext &reference_context, const TIonMotifSet & ErrorMotifs, - const ReferenceReader &ref_reader, int chr_idx) { + const ReferenceReader &ref_reader) { //cout << "Hello from PredictSequenceMotifSSE" << endl; sse_prob_positive_strand = 0; @@ -622,7 +634,7 @@ void AlleleIdentity::PredictSequenceMotifSSE(const LocalReferenceContext &refere unsigned context_left = var_position >= 10 ? 10 : var_position; //if (var_position + reference_context.my_hp_length.at(left_anchor) + 10 < ref_reader.chr_size(chr_idx)) - seqContext = ref_reader.substr(chr_idx, var_position - context_left, context_left + (unsigned int)reference_context.my_hp_length[left_anchor] + 10); + seqContext = ref_reader.substr(reference_context.chr_idx, var_position - context_left, context_left + (unsigned int)reference_context.my_hp_length[left_anchor] + 10); // else // seqContext = ref_reader.substr(chr_idx, var_position - context_left); @@ -651,104 +663,163 @@ void AlleleIdentity::DetectLongHPThresholdCases(const LocalReferenceContext &seq } } +void AlleleIdentity::DetectHpIndelCases(const vector &hp_indel_hrun, const vector &hp_ins_len, const vector &hp_del_len) { + if (status.isIndel && status.isHPIndel and inDelLength > 0) { + assert((hp_indel_hrun.size() == hp_ins_len.size()) and (hp_indel_hrun.size() == hp_del_len.size())); + for (unsigned int i_hp = 0; i_hp < hp_indel_hrun.size(); ++i_hp) { + if (ref_hp_length == hp_indel_hrun[i_hp]){ + if (status.isInsertion and inDelLength <= hp_ins_len[i_hp]){ + filterReasons.push_back("HPINSLEN"); + status.isProblematicAllele = true; + } + else if (status.isDeletion and inDelLength <= hp_del_len[i_hp]){ + filterReasons.push_back("HPDELLEN"); + status.isProblematicAllele = true; + } + } + } + } +} + + void AlleleIdentity::DetectNotAVariant(const LocalReferenceContext &seq_context) { - if (altAllele.compare(seq_context.reference_allele) == 0) { + if (status.isNoVariant) { //incorrect allele status is passed thru make it a no call status.isProblematicAllele = true; filterReasons.push_back("NOTAVARIANT"); } } - -void AlleleIdentity::DetectCasesToForceNoCall(const LocalReferenceContext &seq_context, const ClassifyFilters &filter_variant, +void AlleleIdentity::DetectCasesToForceNoCall(const LocalReferenceContext &seq_context, const ControlCallAndFilters& my_controls, const VariantSpecificParams& variant_specific_params) { DetectNotAVariant(seq_context); DetectLongHPThresholdCases(seq_context, variant_specific_params.hp_max_length_override ? - variant_specific_params.hp_max_length : filter_variant.hp_max_length); + variant_specific_params.hp_max_length : my_controls.filter_variant.hp_max_length); + DetectHpIndelCases(my_controls.filter_variant.filter_hp_indel_hrun, my_controls.filter_variant.filter_hp_ins_len, my_controls.filter_variant.filter_hp_del_len); } -// ==================================================================== - - -void EnsembleEval::SetupAllAlleles(const ExtendParameters ¶meters, - const InputStructures &global_context, - const ReferenceReader &ref_reader, - int chr_idx) -{ - seq_context.DetectContext(*variant, global_context.DEBUG, ref_reader, chr_idx); - allele_identity_vector.resize(variant->alt.size()); - - if (global_context.DEBUG > 0 and variant->alt.size()>0) { - cout << "Investigating variant candidate " << seq_context.reference_allele - << " -> " << variant->alt[0]; - for (uint8_t i_allele = 1; i_allele < allele_identity_vector.size(); i_allele++) - cout << ',' << variant->alt[i_allele]; - cout << endl; - } - - //now calculate the allele type (SNP/Indel/MNV/HPIndel etc.) and window for hypothesis calculation for each alt allele. - for (uint8_t i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { - - // TODO: Hotspot should be an allele property but we only set all or none to Hotspots, depending on the vcf record - allele_identity_vector[i_allele].status.isHotSpot = variant->isHotSpot; - allele_identity_vector[i_allele].filterReasons.clear(); - allele_identity_vector[i_allele].DEBUG = global_context.DEBUG; - - allele_identity_vector[i_allele].indelActAsHPIndel = parameters.my_controls.filter_variant.indel_as_hpindel; - - allele_identity_vector[i_allele].getVariantType(variant->alt[i_allele], seq_context, - global_context.ErrorMotifs, parameters.my_controls.filter_variant, ref_reader, chr_idx); - allele_identity_vector[i_allele].CalculateWindowForVariant(seq_context, global_context.DEBUG, ref_reader, chr_idx); - } - - //GetMultiAlleleVariantWindow(); - multiallele_window_start = -1; - multiallele_window_end = -1; +// ==================================================================== - // Mark Ensemble for realignment if any of the possible variants should be realigned - // TODO: Should we exclude already filtered alleles? - for (uint8_t i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { - //if (!allele_identity_vector[i_allele].status.isNoCallVariant) { - if (allele_identity_vector[i_allele].start_window < multiallele_window_start or multiallele_window_start == -1) - multiallele_window_start = allele_identity_vector[i_allele].start_window; - if (allele_identity_vector[i_allele].end_window > multiallele_window_end or multiallele_window_end == -1) - multiallele_window_end = allele_identity_vector[i_allele].end_window; - if (allele_identity_vector[i_allele].ActAsSNP() && parameters.my_controls.filter_variant.do_snp_realignment) { - doRealignment = doRealignment or allele_identity_vector[i_allele].status.doRealignment; - } - if (allele_identity_vector[i_allele].ActAsMNP() && parameters.my_controls.filter_variant.do_mnp_realignment) { - doRealignment = doRealignment or allele_identity_vector[i_allele].status.doRealignment; - } - } - // Hack: pass allele windows back down the object - for (uint8_t i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { - allele_identity_vector[i_allele].start_window = multiallele_window_start; - allele_identity_vector[i_allele].end_window = multiallele_window_end; - } - if (global_context.DEBUG > 0) { - cout << "Realignment for this candidate is turned " << (doRealignment ? "on" : "off") << endl; - cout << "Final window for multi-allele: " << ": (" << multiallele_window_start << ") "; - for (int p_idx = multiallele_window_start; p_idx < multiallele_window_end; p_idx++) - cout << ref_reader.base(chr_idx,p_idx); - cout << " (" << multiallele_window_end << ") " << endl; - } +// win_1 = [win1_start, win1_end), win_2 = [win2_start, win2_end), +// return (win_1 \cap win_2 != \emptyset) where \cap is the set intersection operator. +template +bool IsOverlappingWindows(MyIndexType win1_start, MyIndexType win1_end, MyIndexType win2_start, MyIndexType win2_end) +{ + if (win1_start >= win1_end or win2_start >= win2_end){ + // win_1 or win_2 is an empty set => empty set intersects any set is always empty + return false; + } + return (win1_start < win2_end) and (win1_end > win2_start); } -// ------------------------------------------------------------ - -void EnsembleEval::FilterAllAlleles(const ClassifyFilters &filter_variant, const vector& variant_specific_params) { - if (seq_context.context_detected) { - for (uint8_t i_allele = 0; i_allele < allele_identity_vector.size(); i_allele++) { - allele_identity_vector[i_allele].DetectCasesToForceNoCall(seq_context, filter_variant, variant_specific_params[i_allele]); - } - } +// Is there splicing hazard of this allele interfered by alt_x? +// Splicing hazard happened if the (my splicing window) \cap (my variant window)^c overlaps (alt_x's variant window) +// where \cap means intersection, ^c means complement. +bool AlleleIdentity::DetectSplicingHazard(const AlleleIdentity& alt_x) const{ + bool is_splicing_hazard = IsOverlappingWindows(start_splicing_window, start_variant_window, alt_x.start_variant_window, alt_x.end_variant_window) + or IsOverlappingWindows(end_variant_window, end_splicing_window, alt_x.start_variant_window, alt_x.end_variant_window); + return is_splicing_hazard; } - - +// The two alleles are connected (i.e., need to be evaluated together) if any of the following conditions is satisfied +// a) (variant window of alt1) intersects (variant window of alt2) +// b) There is splicing hazard of alt1 interfered by alt2. +// c) There is splicing hazard of alt2 interfered by alt1. +// Note: Fake HS allele means the HS allele has no read support. It won't interfere other alleles. But I need to make sure other alleles don't interfere it. +bool IsAllelePairConnected(const AlleleIdentity& alt1, const AlleleIdentity& alt2) +{ + bool debug = alt1.DEBUG or alt2.DEBUG; + // Alleles start at the same position should be evaluated together. + bool is_connect = alt1.start_variant_window == alt2.start_variant_window; + + // Print the allele information for debug + if (debug){ + cout << "+ Detecting connectivity of the allele pair (altX, altY) = (" + << alt1.altAllele << "@[" << alt1.position0 << ", " << alt1.position0 + alt1.ref_length << "), " + << alt2.altAllele << "@[" << alt2.position0 << ", " << alt2.position0 + alt2.ref_length << "))" << endl; + cout << " - (altX, altY) is Fake HS Allele? (" << alt1.status.isFakeHsAllele << ", " << alt2.status.isFakeHsAllele <<")" << endl; + } + + // Rule number one: start at the same position must be connected. + if (is_connect){ + if (debug){ + cout << " - Connected: altX and altY start at the same position." << endl; + } + return is_connect; + } + + // Exception for problematic alleles + if (alt1.status.isProblematicAllele or alt2.status.isProblematicAllele){ + if (debug){ + cout << (is_connect? " - Connected: altX or altY is problematic at the same position." : " - Not connected: altX or altY is problematic.") << endl; + } + return is_connect; + } + + // Exceptions for Fake HS alleles + if (alt1.status.isFakeHsAllele and alt2.status.isFakeHsAllele){ + if (debug){ + cout << (is_connect? " - Connected: both fake HS at the same position." : " - Not connected: both fake HS." ) << endl; + } + return is_connect; + }else if (alt1.status.isFakeHsAllele and alt1.ref_length >= 10 and (not alt1.status.doRealignment) + and alt2.status.isHPIndel and alt2.inDelLength == 1){ + if (debug){ + cout << (is_connect? " - Connected: altX and altY start at the same position." : " - Not connected: long Fake HS altX meets 1-mer HP-INDEL altY." ) << endl; + } + return is_connect; + }else if (alt2.status.isFakeHsAllele and alt2.ref_length >= 10 and (not alt2.status.doRealignment) + and alt1.status.isHPIndel and alt1.inDelLength == 1){ + if (debug){ + cout << (is_connect? " - Connected: altX and altY start at the same position." : " - Not connected: 1-mer HP-INDEL altX meets long Fake HS altY." ) << endl; + } + return is_connect; + } + + // Condition a) + bool is_variant_window_overlap = IsOverlappingWindows(alt1.start_variant_window, alt1.end_variant_window, alt2.start_variant_window, alt2.end_variant_window); + // Condition b) + bool is_alt1_interfered_by_alt2 = false; + if (not alt2.status.isFakeHsAllele){ + is_alt1_interfered_by_alt2 = alt1.DetectSplicingHazard(alt2); + } + // Condition c) + bool is_alt2_interfered_by_alt1 = false; + if (not alt1.status.isFakeHsAllele){ + is_alt2_interfered_by_alt1 = alt2.DetectSplicingHazard(alt1); + } + is_connect = is_variant_window_overlap or is_alt1_interfered_by_alt2 or is_alt2_interfered_by_alt1; + + // Print debug message + if (debug){ + if (not is_connect){ + cout << " - Not connected." << endl; + } + else{ + if (is_variant_window_overlap){ + cout << " - Connected: Overlapping variant windows: " + << "var_win_X = [" << alt1.start_variant_window << ", "<< alt1.end_variant_window << "), " + << "var_win_Y = [" << alt2.start_variant_window << ", "<< alt2.end_variant_window << ")" << endl; + } + if (is_alt1_interfered_by_alt2){ + cout << " - Connected: Splicing hazard of altX interfered by altY: " + << "splice_win_X = [" << alt1.start_splicing_window << ", "<< alt1.end_splicing_window << "), " + << "var_win_X = [" << alt1.start_variant_window << ", "<< alt1.end_variant_window << "), " + << "var_win_Y = [" << alt2.start_variant_window << ", "<< alt2.end_variant_window << ")" << endl; + } + if (is_alt2_interfered_by_alt1){ + cout << " - Connected: Splicing hazard of altY interfered by altX: " + << "splice_win_Y = [" << alt2.start_splicing_window << ", "<< alt2.end_splicing_window << "), " + << "var_win_Y = [" << alt2.start_variant_window << ", "<< alt2.end_variant_window << "), " + << "var_win_X = [" << alt1.start_variant_window << ", "<< alt1.end_variant_window << ")" << endl; + } + } + } + return is_connect; +} diff --git a/Analysis/VariantCaller/Splice/ClassifyVariant.h b/Analysis/VariantCaller/Splice/ClassifyVariant.h index 8672815c..b6b00ed8 100644 --- a/Analysis/VariantCaller/Splice/ClassifyVariant.h +++ b/Analysis/VariantCaller/Splice/ClassifyVariant.h @@ -53,9 +53,11 @@ class VarButton { //bool isComplex; // A complex allele is anything but snp, mnv and Indel //bool isComplexHP; // This complex allele involves a ref. HP of length > 1 - + bool isHotSpotAllele; // Signifies the hotspot allele + bool isFakeHsAllele; // Signifies a "fake" hotspot variant, i.e., the candidate generator sees no read supports me. bool isHotSpot; // Signifies a hotspot variant (set per variant for all alleles, regardless of their specific origin) bool isProblematicAllele; // There is something wrong with this allele, we should filter it. + bool isNoVariant; // The alternative allele is not a variant, i.e., altAllele = reference_context.reference_allele bool doRealignment; // Switch to turn realignment on or off VarButton() { @@ -68,78 +70,34 @@ class VarButton { isPaddedSNP = false; isIndel = false; isHotSpot = false; + isHotSpotAllele= false; + isFakeHsAllele = false; isProblematicAllele = false; + isNoVariant = false; doRealignment = false; } }; -// ------------------------------------------------------------------------ - -// example variants: -// VarButton isSNP true -// genome 44 45 46 47 48 49 (0 based) -// ref is A A A A T T -// alt is A -// altAllele is A -// left_anchor 0 (always) -// right_anchor 0 (always) -// inDelLength 0 (always) -// ref_hp_length 2 (>=1) -- T T satrting at 48 -// start_window <=48 -- calculated as min over all alt Alleles -// end_window >=49 -- calculated as max over all alt Alleles - -// VarButton isMNV true -// genome 44 45 46 47 48 49 (0 based) -// ref is A A A A T T -// alt is A G C -// altAllele is A G C -// left_anchor 1 -// right_anchor 0 -// inDelLength 0 (always) -// ref_hp_length 2 (>=1 always) -- T T starting at 48 -// start_window -// end_window - -// VarButton isIndel true, isDeletion false -// genome 42 42 44 45 46 47 48 49 50 51 52 (0 based) -// ref is C A A A A T G T A A A -// alt is d C G -// altAllele is T C G -// left_anchor 1 -// right_anchor 0 -// inDelLength 2 -// ref_hp_length 1 (G at 49) -// start_window -// end_window - - -// VarButton isIndel false, isInsertion true -// genome 42 42 44 45 46 47 48 49 50 51 52 (0 based) -// ref is C C A A A A T G T A A A -// alt is G GC -// altAllele is G G C -// left_anchor 0 -// right_anchor 0 -// inDelLength 3 -// ref_hp_length 4 (A at 47) -// start_window -// end_window - - class AlleleIdentity { public: VarButton status; //!< A bunch of flags saying what's going on with this allele string altAllele; //!< May contain left and/or right anchor bases, cannot be empty int DEBUG; - + int chr_idx; //!< Chromosome index> + int position0; //! + int ref_length; //! // useful context int left_anchor; //!< Number of left bases that are common between the ref. and alt. allele int right_anchor; //!< Number of right bases that are common between the ref. and alt. allele // left_anchor + right_anchor <= shorter allele length int inDelLength; //!< Difference in length between longer and shorter allele int ref_hp_length; //!< First base change is occurring in an HP of length ref_hp_length - int start_window; //!< Start of window of interest for this variant - int end_window; //!< End of window of interest for this variant + int start_splicing_window; //!< Start of splicing window of interest for this alt allele. + int end_splicing_window; //!< End of splicing window of interest for this alt allele => splicing window = [start_splicing_window, end_splicing_window) + int multiallele_window_start; //!< Start of splicing window of interest for the multi-allele variant + int multiallele_window_end; //!< End of splicing window of interest for multi-allele variant + int start_variant_window; //!< Start of the anchor-removed window of interest for this alt allele + int end_variant_window; //!< End of the anchor-removed window of interest for this alt allele => variant window = [start_variant_window, end_variant_window) // need to know when I do filtering float sse_prob_positive_strand; @@ -148,15 +106,28 @@ class AlleleIdentity { bool indelActAsHPIndel; // Switch to make all indels match HPIndel behavior - AlleleIdentity() { + // Level of flow-disruption vs ref: (-1, 0, 1, 2) = (indefinite, HP-INDEL, otherwise , FD) + int fd_level_vs_ref; + + // Extra (prefix, suffix) padding bases added into the alt allele if the alt allele representation is obtained by grouping with other alt alleles. + // (Note): For an standard indel representation e.g., T->TT, num_padding_added = (0, 0). + pair num_padding_added; + AlleleIdentity() { + position0 = -1; + chr_idx = -1; + ref_length = -1; inDelLength = 0; ref_hp_length = 0; //modified_start_pos = 0; left_anchor = 0; right_anchor = 0; - start_window = 0; - end_window = 0; + start_splicing_window = 0; + end_splicing_window = 0; + multiallele_window_start = 0; + multiallele_window_end = 0; + start_variant_window = 0; + end_variant_window = 0; DEBUG = 0; // filterable statuses @@ -164,34 +135,34 @@ class AlleleIdentity { sse_prob_negative_strand = 0; indelActAsHPIndel = false; + fd_level_vs_ref = -1; }; - bool Ordinary() { - return(status.isIndel && !(status.isHPIndel)); + bool Ordinary() const { + return (status.isIndel and (not status.isHPIndel)); }; - bool ActAsSNP(){ - // return(status.isSNP || status.isMNV || (status.isIndel && !status.isHPIndel)); + bool ActAsSNP() const { if (indelActAsHPIndel) - return(status.isSNP || status.isPaddedSNP); + return (status.isSNP or status.isPaddedSNP); else - return(status.isSNP || status.isPaddedSNP || (status.isIndel && !status.isHPIndel)); + return (status.isSNP or status.isPaddedSNP or (status.isIndel and (not status.isHPIndel))); } - bool ActAsMNP(){ - return(status.isMNV); + bool ActAsMNP() const { + return status.isMNV; } - bool ActAsHPIndel(){ + bool ActAsHPIndel() const { if (indelActAsHPIndel) - return(status.isIndel); + return status.isIndel; else - return(status.isIndel && status.isHPIndel); + return (status.isIndel and status.isHPIndel); } //void DetectPotentialCorrelation(const LocalReferenceContext &reference_context); bool SubCategorizeInDel(const LocalReferenceContext &reference_context, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); void IdentifyHPdeletion(const LocalReferenceContext& reference_context); void IdentifyHPinsertion(const LocalReferenceContext& reference_context, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); bool IdentifyDyslexicMotive(char base, int position, const ReferenceReader &ref_reader, int chr_idx); @@ -201,26 +172,31 @@ class AlleleIdentity { const TIonMotifSet & ErrorMotifs, const ClassifyFilters &filter_variant, const ReferenceReader &ref_reader, - int chr_idx); + const pair &alt_orig_padding); bool CharacterizeVariantStatus(const LocalReferenceContext &reference_context, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); bool CheckValidAltAllele(const LocalReferenceContext &reference_context); //void ModifyStartPosForAllele(int variantPos); bool IdentifyMultiNucRepeatSection(const LocalReferenceContext &seq_context, unsigned int rep_period, - const ReferenceReader &ref_reader, int chr_idx); - void CalculateWindowForVariant(const LocalReferenceContext &seq_context, int DEBUG, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); + + void CalculateWindowForVariant(const LocalReferenceContext &seq_context, + const ReferenceReader &ref_reader); - void DetectCasesToForceNoCall(const LocalReferenceContext &seq_context, const ClassifyFilters &filter_variant, + void DetectCasesToForceNoCall(const LocalReferenceContext &seq_context, const ControlCallAndFilters& my_controls, const VariantSpecificParams& variant_specific_params); void DetectLongHPThresholdCases(const LocalReferenceContext &seq_context, int maxHPLength); + void DetectHpIndelCases(const vector &hp_indel_hrun, const vector &hp_ins_len, const vector &hp_del_len); void DetectNotAVariant(const LocalReferenceContext &seq_context); void PredictSequenceMotifSSE(const LocalReferenceContext &reference_context, const TIonMotifSet & ErrorMotifs, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); + bool DetectSplicingHazard(const AlleleIdentity& alt_x) const; }; +bool IsAllelePairConnected(const AlleleIdentity& alt1, const AlleleIdentity& alt2); - +template +bool IsOverlappingWindows(MyIndexType win1_start, MyIndexType win1_end, MyIndexType win2_start, MyIndexType win2_end); #endif //CLASSIFYVARIANT_H diff --git a/Analysis/VariantCaller/Splice/LocalContext.cpp b/Analysis/VariantCaller/Splice/LocalContext.cpp index 528a20e4..7b22c3a1 100644 --- a/Analysis/VariantCaller/Splice/LocalContext.cpp +++ b/Analysis/VariantCaller/Splice/LocalContext.cpp @@ -6,18 +6,30 @@ #include "LocalContext.h" - +void LocalReferenceContext::DetectContextAtPosition(const ReferenceReader &ref_reader, int my_chr_idx, int position0, int ref_len){ + assert(ref_len > 0); + chr_idx = my_chr_idx; + vcf::Variant dummy_variant; + dummy_variant.sequenceName = ref_reader.chr_str(chr_idx); + dummy_variant.position = (long) (position0 + 1); + dummy_variant.ref = ref_reader.substr(chr_idx, (long) position0, (long) ref_len); + DetectContext(dummy_variant, 0, ref_reader); +} void LocalReferenceContext::DetectContext(const vcf::Variant &candidate_variant, int DEBUG, - const ReferenceReader &ref_reader, int chr_idx) { + const ReferenceReader &ref_reader) { // VCF stores positions in 1-based index; local_contig_sequence has a zero based index // all positions in this object are zero based so that they correspond to reference in memory. position0 = candidate_variant.position-1; contigName = candidate_variant.sequenceName; + if (chr_idx < 0) + chr_idx = ref_reader.chr_idx(contigName.c_str()); + else if (contigName != ref_reader.chr_str(chr_idx)) + chr_idx = ref_reader.chr_idx(contigName.c_str()); // Sanity checks if position is valid and reference allele matches reference - if (!ContextSanityChecks(candidate_variant, ref_reader, chr_idx)) + if (!ContextSanityChecks(candidate_variant, ref_reader)) return; my_hp_length.resize(reference_allele.length(), 0); @@ -105,14 +117,12 @@ void LocalReferenceContext::DetectContext(const vcf::Variant &candidate_variant, } bool LocalReferenceContext::ContextSanityChecks(const vcf::Variant &candidate_variant, - const ReferenceReader &ref_reader, int chr_idx) { + const ReferenceReader &ref_reader) { // Sanity checks that abort context detection reference_allele = candidate_variant.ref; context_detected = true; - - if (candidate_variant.position < 1 or candidate_variant.position > (long)ref_reader.chr_size(chr_idx)) { cerr << "Non-fatal ERROR: Candidate Variant Position is not within the Contig Bounds at VCF Position " << candidate_variant.sequenceName << ":" << candidate_variant.position @@ -163,3 +173,54 @@ bool LocalReferenceContext::ContextSanityChecks(const vcf::Variant &candidate_va return (context_detected); } + +// return the minimum possible splicing window start in this context +int LocalReferenceContext::SplicingLeftBound(const ReferenceReader &ref_reader) const{ + if (not context_detected){ + return StartSplicingNoExpansion(); + } + int splicing_left_bound = StartSplicingNoExpansion(); // splicing window start for SNP/MNP + splicing_left_bound = min(splicing_left_bound, StartSplicingExpandFromMyHpStart0()); // splicing window start for INDEL + for (int rep_period = min_mnr_rep_period; rep_period <= max_mnr_rep_period; ++rep_period){ + CircluarBuffer mnr_window(0); + int mnr_start = FindSplicingStartForMNR(ref_reader, position0, rep_period, mnr_window); + splicing_left_bound = min(splicing_left_bound, mnr_start); // splicing window start for MNR + } + splicing_left_bound = max(0, splicing_left_bound); // safety + return splicing_left_bound; +} + +// Splicing start for MNR only depends on context, so I do it here. +int LocalReferenceContext::FindSplicingStartForMNR(const ReferenceReader &ref_reader, int variant_pos, int rep_period, CircluarBuffer& window) const{ + int mnr_start = variant_pos; + if (variant_pos + rep_period >= (int) ref_reader.chr_size(chr_idx)){ + return mnr_start; + } + + --mnr_start; // 1 anchor base + window = CircluarBuffer(rep_period); + for (int idx = 0; idx < rep_period; idx++){ + window.assign(idx, ref_reader.base(chr_idx, variant_pos + idx)); + } + + // Investigate (inclusive) start position of MNR region + window.shiftLeft(1); + while (mnr_start > 0 and window.first() == ref_reader.base(chr_idx, mnr_start)) { + mnr_start--; + window.shiftLeft(1); + } + return mnr_start; +} + +// Originally, the start splicing window was calculated in AlleleIdentity::CalculateWindowForVariant. +// Now LocalReferenceContext::SplicingLeftBound wants to calculate the left bound of the splicing window, +// so I calculate the splicing window start here to make sure that LocalReferenceContext can handle all possible start splicing windows. +int LocalReferenceContext::StartSplicingNoExpansion() const{ + return (int) position0; +} +int LocalReferenceContext::StartSplicingExpandFromMyHpStart0() const{ + return my_hp_start_pos[0] - 1; +} +int LocalReferenceContext::StartSplicingExpandFromMyHpStartLeftAnchor(int left_anchor) const{ + return my_hp_start_pos[left_anchor - 1] - 1; +} diff --git a/Analysis/VariantCaller/Splice/LocalContext.h b/Analysis/VariantCaller/Splice/LocalContext.h index e1f24d81..bc3578a8 100644 --- a/Analysis/VariantCaller/Splice/LocalContext.h +++ b/Analysis/VariantCaller/Splice/LocalContext.h @@ -19,6 +19,7 @@ #include #include "ReferenceReader.h" +#include "MiscUtil.h" using namespace std; @@ -44,8 +45,10 @@ class LocalReferenceContext{ // all positions in this object are zero based so that they correspond to reference in memory. long position0; //!< Zero based allele start position. string contigName; //!< Contig Name from VCF + int chr_idx; //!< index associated with contigName bool context_detected; //!< Check to see if the reference allele matches the given genome position. + // Information about the bases in the reference allele string reference_allele; //!< The reference allele for this variant vector my_hp_length; //!< Member HP length for each base in the reference allele. @@ -59,11 +62,15 @@ class LocalReferenceContext{ char ref_right_hp_base; //!< Base comprising the HP to the right of the one containing the vcf position int right_hp_length; //!< Length of the HP to the left of of the HP encompassing position int right_hp_start; + // MNR + int min_mnr_rep_period; + int max_mnr_rep_period; LocalReferenceContext(){ my_hp_length.clear(); my_hp_start_pos.clear(); context_detected = false; + chr_idx = -1; position0 = -1; ref_left_hp_base = 'X'; // Something that can't occur in the reference ref_right_hp_base = 'X'; @@ -71,17 +78,30 @@ class LocalReferenceContext{ left_hp_length = 0; right_hp_start = 0; right_hp_length = 0; + min_mnr_rep_period = 2; + max_mnr_rep_period = 5; } //! @brief Fills in the member variables void DetectContext(const vcf::Variant &candidate_variant, int DEBUG, - const ReferenceReader &ref_reader, int chr_idx); - + const ReferenceReader &ref_reader); + //! @brief Detect the context for the single base reference at position0 (zero-based) only. + void DetectContextAtPosition(const ReferenceReader &ref_reader, int my_chr_idx, int position0, int ref_len); //! @brief Basic sanity checks on the provided vcf positions //! Sets member context_detected to true if sanity checks are passed. //! Returns false if VCF position is not valid. bool ContextSanityChecks(const vcf::Variant &candidate_variant, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); + //! @brief Calculate the left bound of the splicing window start in this context + int SplicingLeftBound(const ReferenceReader &ref_reader) const; + //! @brief Calculate the splicing window start for MNR (if any) in this context + int FindSplicingStartForMNR(const ReferenceReader &ref_reader, int variant_pos, int rep_period, CircluarBuffer& window) const; + //! @brief Calculate the splicing window start if I don't want to expand the splicing window to the left (primarily for SNP, MNP) + int StartSplicingNoExpansion() const; + //! @brief Calculate the splicing window start if I want to expand the splicing window to the left from the start position (primarily for INDEL) + int StartSplicingExpandFromMyHpStart0() const; + //! @brief Calculate the splicing window start if I want to expand the splicing window to the left from the anchor-removed start position (primarily for some INS) + int StartSplicingExpandFromMyHpStartLeftAnchor(int left_anchor) const; }; #endif //LOCALCONTEXT_H diff --git a/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.cpp b/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.cpp index 2248b922..d14fb193 100644 --- a/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.cpp +++ b/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.cpp @@ -9,7 +9,7 @@ bool SpliceVariantHypotheses(const Alignment ¤t_read, const EnsembleEval & const LocalReferenceContext &local_context, PersistingThreadObjects &thread_objects, int &splice_start_flow, int &splice_end_flow, vector &my_hypotheses, vector & same_as_null_hypothesis, bool & changed_alignment, const InputStructures &global_context, - const ReferenceReader &ref_reader, int chr_idx) + const ReferenceReader &ref_reader) { // Hypotheses: 1) Null; read as called 2) Reference Hypothesis 3-?) Variant Hypotheses @@ -51,7 +51,7 @@ bool SpliceVariantHypotheses(const Alignment ¤t_read, const EnsembleEval & // do realignment of a small region around variant if desired if (my_ensemble.doRealignment) { pretty_alignment = SpliceDoRealignement(thread_objects, current_read, local_context.position0, - changed_alignment, global_context.DEBUG, ref_reader, chr_idx); + changed_alignment, global_context.DEBUG, ref_reader, local_context.chr_idx); if (pretty_alignment.empty() and global_context.DEBUG > 0) cerr << "Realignment returned an empty string in read " << current_read.alignment.Name << endl; } @@ -70,8 +70,8 @@ bool SpliceVariantHypotheses(const Alignment ¤t_read, const EnsembleEval & // Basic sanity checks if (read_idx >= read_idx_max - or ref_idx > ref_reader.chr_size(chr_idx) - or (ref_idx == ref_reader.chr_size(chr_idx) and pretty_alignment[pretty_idx] != '+')) { + or ref_idx > ref_reader.chr_size(local_context.chr_idx) + or (ref_idx == ref_reader.chr_size(local_context.chr_idx) and pretty_alignment[pretty_idx] != '+')) { did_splicing = false; break; } @@ -93,7 +93,7 @@ bool SpliceVariantHypotheses(const Alignment ¤t_read, const EnsembleEval & // Have reference bases inside of window but outside of span of reference allele if (outside_ref_allele and !outside_of_window and pretty_alignment[pretty_idx] != '+') { for (unsigned int i_hyp = 1; i_hyp < my_hypotheses.size(); i_hyp++) - my_hypotheses[i_hyp].push_back(ref_reader.base(chr_idx,ref_idx)); + my_hypotheses[i_hyp].push_back(ref_reader.base(local_context.chr_idx,ref_idx)); } // Have read bases as called outside of variant window diff --git a/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.h b/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.h index 33ae36d1..a7140e72 100644 --- a/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.h +++ b/Analysis/VariantCaller/Splice/SpliceVariantHypotheses.h @@ -25,7 +25,7 @@ bool SpliceVariantHypotheses(const Alignment ¤t_read, const EnsembleEval & const LocalReferenceContext &local_context, PersistingThreadObjects &thread_objects, int &splice_start_flow, int &splice_end_flow, vector &my_hypotheses, vector & same_as_null_hypothesis, bool & changed_alignment, const InputStructures &global_context, - const ReferenceReader &ref_reader, int chr_idx); + const ReferenceReader &ref_reader); bool SpliceAddVariantAlleles(const Alignment ¤t_read, const string& pretty_alignment, diff --git a/Analysis/VariantCaller/TargetsManager.cpp b/Analysis/VariantCaller/TargetsManager.cpp index 1c71fcee..734423cd 100644 --- a/Analysis/VariantCaller/TargetsManager.cpp +++ b/Analysis/VariantCaller/TargetsManager.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "BAMWalkerEngine.h" //#include "ExtendParameters.h" @@ -15,10 +16,13 @@ TargetsManager::TargetsManager() { trim_ampliseq_primers = false; + min_coverage_fraction = 0.0f; + pthread_mutex_init(&coverage_counter_mutex_, NULL); } TargetsManager::~TargetsManager() { + pthread_mutex_destroy(&coverage_counter_mutex_); } bool CompareTargets(TargetsManager::UnmergedTarget *i, TargetsManager::UnmergedTarget *j) @@ -47,13 +51,12 @@ bool CompareTargets(TargetsManager::UnmergedTarget *i, TargetsManager::UnmergedT -void TargetsManager::Initialize(const ReferenceReader& ref_reader, const string& _targets, bool _trim_ampliseq_primers /*const ExtendParameters& parameters*/) +void TargetsManager::Initialize(const ReferenceReader& ref_reader, const string& _targets, float min_cov_frac, bool _trim_ampliseq_primers /*const ExtendParameters& parameters*/) { - + min_coverage_fraction = min_cov_frac; // // Step 1. Retrieve raw target definitions // - list raw_targets; if (not _targets.empty()) { @@ -137,35 +140,35 @@ void TargetsManager::Initialize(const ReferenceReader& ref_reader, const string& } - +// ------------------------------------------------------------------------------------- void TargetsManager::LoadRawTargets(const ReferenceReader& ref_reader, const string& bed_filename, list& raw_targets) { - FILE *bed_file = fopen(bed_filename.c_str(), "r"); - if (not bed_file) { + ifstream bedfile(bed_filename.c_str()); + if (not bedfile.is_open()){ cerr << "ERROR: Unable to open target file " << bed_filename << " : " << strerror(errno) << endl; exit(1); } - char line[4096]; - char chr_name[4096]; - int begin; - int end; - char region_name[4096]; + string line, bed_field; + vector bed_line; int line_number = 0; - while (fgets(line, 4096, bed_file)) { - ++line_number; + while(getline(bedfile, line)){ - if (strncmp(line,"track",5) == 0) { - // Parse track line if needed + ++line_number; + // Skip header line(s) + if (line.compare(0,5,"track")==0 or line.compare(0,7,"browser")==0 or line.length()==0) continue; - } + // Split line into tab separated fields + bed_line.clear(); + stringstream ss(line); + while (getline(ss, bed_field, '\t')) + bed_line.push_back(bed_field); - int num_fields = sscanf(line, "%s\t%d\t%d\t%s", chr_name, &begin, &end, region_name); - if (num_fields == 0) - continue; + // the first three columns are required in the bad format + unsigned int num_fields = bed_line.size(); if (num_fields < 3) { cerr << "ERROR: Failed to parse target file line " << line_number << endl; exit(1); @@ -173,91 +176,118 @@ void TargetsManager::LoadRawTargets(const ReferenceReader& ref_reader, const str raw_targets.push_back(UnmergedTarget()); UnmergedTarget& target = raw_targets.back(); - target.begin = begin; - target.end = end; - target.chr = ref_reader.chr_idx(chr_name); - if (num_fields > 3 and strcmp(region_name,".") != 0) - target.name = region_name; - - if (target.chr < 0) { - cerr << "ERROR: Target region " << target.name << " (" << chr_name << ":" << begin << "-" << end << ")" + target.chr = ref_reader.chr_idx(bed_line[0].c_str()); + target.begin = strtol (bed_line[1].c_str(), NULL, 0); + target.end = strtol (bed_line[2].c_str(), NULL, 0); + + if (num_fields > 3 and bed_line[3]!=".") + target.name = bed_line[3]; + + // Validate target + if (target.chr < 0){ + cerr << "ERROR: Target region " << target.name << " (" << bed_line[0] << ":" << bed_line[1] << "-" << bed_line[2] << ")" << " has unrecognized chromosome name" << endl; exit(1); } - - if (begin < 0 || end > ref_reader.chr_size(target.chr)) { - cerr << "ERROR: Target region " << target.name << " (" << chr_name << ":" << begin << "-" << end << ")" + if (target.begin < 0 || target.end > ref_reader.chr_size(target.chr)) { + cerr << "ERROR: Target region " << target.name << " (" << bed_line[0] << ":" << bed_line[1] << "-" << bed_line[2] << ")" << " is outside of reference sequence bounds (" - << chr_name << ":0-" << ref_reader.chr_size(target.chr) << ")" << endl; + << bed_line[0] << ":0-" << ref_reader.chr_size(target.chr) << ")" << endl; exit(1); } - if (end < begin) { - cerr << "ERROR: Target region " << target.name << " (" << chr_name << ":" << begin << "-" << end << ")" + if (target.end < target.begin) { + cerr << "ERROR: Target region " << target.name << " (" << bed_line[0] << ":" << bed_line[1] << "-" << bed_line[2] << ")" << " has inverted coordinates" << endl; exit(1); } - AddExtraTrim(target, line, num_fields); - } - fclose(bed_file); + // And now we simply assume that we have a beddetail file with at least 5 columns + if (num_fields > 4) + ParseBedInfoField(target, bed_line[num_fields-1]); + } + bedfile.close(); if (raw_targets.empty()) { - cerr << "ERROR: No targets loaded from " << bed_filename - << " after parsing " << line_number << " lines" << endl; + cerr << "ERROR: No targets loaded from " << bed_filename << " after parsing " << line_number << " lines" << endl; exit(1); } } -void TargetsManager::AddExtraTrim(UnmergedTarget& target, char *line, int num_fields) +// ------------------------------------------------------------------------------------- + +bool ParseInfoKey(const string key, string info, long &value) { + + size_t found = info.find(key); + if (found == string::npos){ + return false; + } + + info.erase(0, found+key.size()); + value = strtol (info.c_str(), NULL, 0); + if (value>=0) + return true; + else + return false; +} + +// ------------------------------------------------------------------------------------- + +void TargetsManager::ParseBedInfoField(UnmergedTarget& target, const string info) { - // parse extra trimming out of the bed file - // would be nice to unify with validate_bed using BedFile.h - char keybuffer[4096]; + // parse extra parameters out of the bed file info fields + long int temp = 0; + target.trim_left = 0; + if (ParseInfoKey("TRIM_LEFT=", info, temp)) + target.trim_left = temp; + target.trim_right = 0; + if (ParseInfoKey("TRIM_RIGHT=", info, temp)) + target.trim_right = temp; - int numfields = sscanf(line, "%*s\t%*d\t%*d\t%*s\t%*s\t%*s\t%*s\t%s", keybuffer); - if (numfields == 1) { - string keypairs = keybuffer; - string left = "TRIM_LEFT="; - string right = "TRIM_RIGHT="; - long int trim_l = 0; - long int trim_r = 0; - - // look for a trim_left field - size_t found = keypairs.find(left); - if (found != string::npos){ - string r1 = keypairs; - r1.erase(0, found+left.size()); - trim_l = strtol (r1.c_str(), NULL, 0); - } + target.hotspots_only = 0; + if (ParseInfoKey("HS_ONLY=", info, temp)) + target.hotspots_only = temp; - // look for a trim_right field - found = keypairs.find(right); - if (found != string::npos){ - string r1 = keypairs; - r1.erase(0, found+right.size()); - trim_r = strtol (r1.c_str(), NULL, 0); - } - assert(trim_l>=0); - assert(trim_r>=0); - target.trim_left = trim_l; - target.trim_right = trim_r; + target.read_mismatch_limit = -1; + if (ParseInfoKey("READ_MISMATCH_LIMIT=", info, temp)){ + target.read_mismatch_limit = temp; } - // cout << "trim assigned to amplicon starting " << target.begin << " with trimming " << target.trim_left << " and " << target.trim_right << endl; } +// ------------------------------------------------------------------------------------- +// expects a zero-based position to match target indexing. -void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hint) const +int TargetsManager::ReportHotspotsOnly(const MergedTarget &merged, int chr, long pos0) { + // Verify that (chr,pos) is inside the merged target + if (merged.chr != chr or pos0 < merged.begin or pos0 >= merged.end){ + cerr << "ReportHotspotsOnly ERROR: (chr=" << chr << ",pos=" << pos0 << ") not in target chr " + << merged.chr << ":" << merged.begin << "-" << merged.end << endl; + exit(1); + } + + // We take the first target index that contains the variant position, excluding primers. + unsigned int target_idx = merged.first_unmerged; + while (target_idx < unmerged.size() and unmerged[target_idx].end-unmerged[target_idx].trim_right < pos0) + ++target_idx; + + if (target_idx < unmerged.size() and pos0 >= unmerged[target_idx].begin) + return unmerged[target_idx].hotspots_only; + + return 0; +} + +// ------------------------------------------------------------------------------------- + +void TargetsManager::GetBestTargetIndex(Alignment *rai, int unmerged_target_hint, int& best_target_idx, int& best_fit_penalty, int& best_overlap) const +{ + // set these before any trimming rai->align_start = rai->alignment.Position; rai->align_end = rai->alignment.GetEndPosition(false, true); rai->old_cigar = rai->alignment.CigarData; - if (not trim_ampliseq_primers) - return; - // Step 1: Find the first potential target region int target_idx = unmerged_target_hint; @@ -271,10 +301,9 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin // Step 2: Iterate over potential target regions, evaluate fit, pick the best fit - - int best_target_idx = -1; - int best_fit_penalty = 100; - int best_overlap = 0; + best_target_idx = -1; + best_fit_penalty = 100; + best_overlap = 0; while (target_idx < (int)unmerged.size() and rai->alignment.RefID == unmerged[target_idx].chr and rai->end >= unmerged[target_idx].begin) { @@ -285,16 +314,22 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin int overlap = min(unmerged[target_idx].end, read_end) - max(unmerged[target_idx].begin, read_start); int fit_penalty = 100; + float overlap_ratio = (float) overlap / (float) (unmerged[target_idx].end - unmerged[target_idx].begin); + if (overlap_ratio > min_coverage_fraction) + rai->target_coverage_indices.push_back(target_idx); + if (not rai->alignment.IsReverseStrand()) { if (read_prefix_size > 0) fit_penalty = min(read_prefix_size,50) + max(0,50-overlap); else fit_penalty = min(-3*read_prefix_size,50) + max(0,50-overlap); + if (read_postfix_size > 30) fit_penalty += read_postfix_size-10; } else { if (read_postfix_size > 0) fit_penalty = min(read_postfix_size,50) + max(0,50-overlap); else fit_penalty = min(-3*read_postfix_size,50) + max(0,50-overlap); + if (read_prefix_size > 30) fit_penalty += read_prefix_size-10; } if (read_prefix_size > 0 and read_postfix_size > 0) fit_penalty -= 10; @@ -307,14 +342,34 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin ++target_idx; } + if (rai->target_coverage_indices.size() > 1){ + sort(rai->target_coverage_indices.begin(), rai->target_coverage_indices.end()); + } +} + - if (best_target_idx == -1) { - rai->filtered = true; - return; +void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hint) const +{ + int best_target_idx = -1; + int best_fit_penalty = 100; + int best_overlap = 0; + GetBestTargetIndex(rai, unmerged_target_hint, best_target_idx, best_fit_penalty, best_overlap); + + if (best_target_idx < 0 or rai->target_coverage_indices.empty()){ + rai->filtered = true; + return; } + if (not trim_ampliseq_primers){ + return; + } - // Step 3: Do the actual primer trimming. + // Step 1: Find the best target index + if (best_target_idx < 0){ + rai->filtered = true; + return; + } + // Step 2: Do the actual primer trimming. // // For now, only adjust Position and Cigar. // Later, also adjust MD tag. @@ -326,7 +381,7 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin vector::iterator old_op = old_cigar.begin(); int ref_pos = rai->alignment.Position; - // 3A: Cigar ops left of the target + // 2A: Cigar ops left of the target int begin = unmerged[best_target_idx].begin + unmerged[best_target_idx].trim_left; if (begin > unmerged[best_target_idx].end) @@ -373,8 +428,11 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin if (old_op->Type == 'D') { if (old_op->Length > gap) { - old_op->Length -= gap; - ref_pos += gap; + //old_op->Length -= gap; + //ref_pos += gap; + // avoid #S#D case, extend align position, remove the leading D. + ref_pos += old_op->Length; + ++old_op; break; } else { ref_pos += old_op->Length; @@ -385,7 +443,7 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin } - // 3B: Cigar ops in the middle of the target + // 2B: Cigar ops in the middle of the target rai->alignment.Position = ref_pos; @@ -419,13 +477,13 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin } if (old_op->Type == 'D') { - new_cigar.push_back(CigarOp('D')); if (old_op->Length > gap) { - new_cigar.back().Length = gap; - old_op->Length -= gap; - ref_pos += gap; + // last D op, remove this one + ref_pos += old_op->Length; + ++old_op; break; } else { + new_cigar.push_back(CigarOp('D')); new_cigar.back().Length = old_op->Length; ref_pos += old_op->Length; ++old_op; @@ -434,8 +492,7 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin } } - - // 3C: Cigar ops to the right of the target + // 2C: Cigar ops to the right of the target for (; old_op != old_cigar.end(); ++old_op) { if (old_op->Type == 'H' or old_op->Type == 'D') @@ -455,14 +512,72 @@ void TargetsManager::TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hin ZL << unmerged[best_target_idx].name << ":" << best_fit_penalty << ":" << best_overlap; rai->alignment.AddTag("ZL", "Z", ZL.str()); - - } +// Filter out a read if any of the following conditions is satisfied +// a) The read does not cover any region. +// b) The coverage ratio at the best region < min_coverage_ratio. +bool TargetsManager::FilterReadByRegion(Alignment* rai, int unmerged_target_hint) const +{ + bool is_filtered_out = false; + int best_target_idx = -1; + int best_fit_penalty = 100; + int best_overlap = 0; + GetBestTargetIndex(rai, unmerged_target_hint, best_target_idx, best_fit_penalty, best_overlap); + + // Filter out the read if it does not cover any region. + if (best_target_idx < 0 or rai->target_coverage_indices.empty()){ + is_filtered_out = true; + rai->filtered = true; + return is_filtered_out; + } + + return is_filtered_out; +} +void TargetsManager::WriteTargetsCoverage(const string& target_cov_file, const ReferenceReader& ref_reader) const { + ofstream target_cov_out; + target_cov_out.open(target_cov_file.c_str(), ofstream::out); + target_cov_out << "chr" << "\t" + << "pos_start" << "\t" + << "pos_end" << "\t" + << "name" << "\t" + << "read_depth" << "\t" + << "family_depth" << "\t" + << "fam_size_hist" << endl; + + for (vector::const_iterator target_it = unmerged.begin(); target_it != unmerged.end(); ++target_it){ + unsigned int check_read_cov = 0; + unsigned int check_fam_cov = 0; + target_cov_out << ref_reader.chr_str(target_it->chr) << "\t" + << target_it->begin << "\t" + << target_it->end << "\t" + << (target_it->name.empty()? "." : target_it->name) << "\t" + << target_it->my_stat.read_coverage << "\t" + << target_it->my_stat.family_coverage << "\t"; + for (map::const_iterator hist_it = target_it->my_stat.fam_size_hist.begin(); hist_it != target_it->my_stat.fam_size_hist.end(); ++hist_it){ + target_cov_out << "(" << hist_it->first << "," << hist_it->second <<"),"; + check_fam_cov += hist_it->second; + check_read_cov += (hist_it->second * (unsigned int) hist_it->first); + } + target_cov_out << endl; + // Check fam_size_hist matches read/family coverages. + assert((check_read_cov == target_it->my_stat.read_coverage) and (check_fam_cov == target_it->my_stat.family_coverage)); + } + target_cov_out.close(); +} - - - +void TargetsManager::AddCoverageToRegions(const map& stat_of_targets){ + pthread_mutex_lock(&coverage_counter_mutex_); + for (map::const_iterator stat_it = stat_of_targets.begin(); stat_it != stat_of_targets.end(); ++stat_it){ + int target_idx = stat_it->first; + unmerged[target_idx].my_stat.read_coverage += stat_it->second.read_coverage; + unmerged[target_idx].my_stat.family_coverage += stat_it->second.family_coverage; + for (map::const_iterator hist_it = stat_it->second.fam_size_hist.begin(); hist_it != stat_it->second.fam_size_hist.end(); ++hist_it){ + unmerged[target_idx].my_stat.fam_size_hist[hist_it->first] += hist_it->second; + } + } + pthread_mutex_unlock(&coverage_counter_mutex_); +} diff --git a/Analysis/VariantCaller/TargetsManager.h b/Analysis/VariantCaller/TargetsManager.h index 67b654ee..f75ad317 100644 --- a/Analysis/VariantCaller/TargetsManager.h +++ b/Analysis/VariantCaller/TargetsManager.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "ReferenceReader.h" struct Alignment; @@ -22,33 +24,51 @@ struct MergedTarget { int first_unmerged; }; +struct TargetStat{ + unsigned int read_coverage = 0; + unsigned int family_coverage = 0; + // fam_size_hist[x] = y indicates there are y families of size x. + map fam_size_hist; +}; + class TargetsManager { public: TargetsManager(); ~TargetsManager(); - void Initialize(const ReferenceReader& ref_reader, const string& _targets, bool _trim_ampliseq_primers = false); - + void Initialize(const ReferenceReader& ref_reader, const string& _targets, float min_cov_frac = 0.0f, bool _trim_ampliseq_primers = false); struct UnmergedTarget { - int chr; - int begin; - int end; - string name; - int merged; - int trim_left; - int trim_right; + int chr = 0; + int begin = 0; + int end = 0; + string name = ""; + int merged = 0; + int trim_left = 0; + int trim_right = 0; + int hotspots_only = 0; + int read_mismatch_limit = -1; + TargetStat my_stat; }; void LoadRawTargets(const ReferenceReader& ref_reader, const string& bed_filename, list& raw_targets); - void AddExtraTrim(UnmergedTarget& target, char *region_name, int num_fields); + void ParseBedInfoField(UnmergedTarget& target, const string info); void TrimAmpliseqPrimers(Alignment *rai, int unmerged_target_hint) const; + void GetBestTargetIndex(Alignment *rai, int unmerged_target_hint, int& best_target_idx, int& best_fit_penalty, int& best_overlap) const; + bool FilterReadByRegion(Alignment* rai, int unmerged_target_hint) const; + void AddCoverageToRegions(const map& stat_of_targets); + void WriteTargetsCoverage(const string& file_path, const ReferenceReader& ref_reader) const; + int ReportHotspotsOnly(const MergedTarget &merged, int chr, long pos); vector unmerged; vector merged; bool trim_ampliseq_primers; + // The following variables are just for bool FilterReadByRegion(Alignment* rai, int recent_target) use only. + float min_coverage_fraction; +private: + pthread_mutex_t coverage_counter_mutex_; }; diff --git a/Analysis/VariantCaller/VariantCaller.cpp b/Analysis/VariantCaller/VariantCaller.cpp index bf7d04ac..05b838a4 100644 --- a/Analysis/VariantCaller/VariantCaller.cpp +++ b/Analysis/VariantCaller/VariantCaller.cpp @@ -33,8 +33,8 @@ #include "tvcutils/unify_vcf.h" #include "IndelAssembly/IndelAssembly.h" - #include "MolecularTag.h" +#include "Consensus.h" using namespace std; @@ -48,9 +48,13 @@ void TheSilenceOfTheArmadillos(ofstream &null_ostream) void * VariantCallerWorker(void *input); -int main(int argc, char* argv[]) -{ +// -------------------------------------------------------------------------------------------------------------- +// The tvc exectuable is currently overloaded and harbors "tvc consensus" within +// Below function is the classic tvc main function + +static int main_tvc(int argc, char* argv[]) +{ printf("tvc %s-%s (%s) - Torrent Variant Caller\n\n", IonVersion::GetVersion().c_str(), IonVersion::GetRelease().c_str(), IonVersion::GetGitHash().c_str()); @@ -60,37 +64,21 @@ int main(int argc, char* argv[]) time_t start_time = time(NULL); - + // Read parameters and create output directories ExtendParameters parameters(argc, argv); - - mkdir(parameters.outputDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - if (parameters.program_flow.rich_json_diagnostic || parameters.program_flow.minimal_diagnostic) { - // make output directory "side effect bad" - parameters.program_flow.json_plot_dir = parameters.outputDir + "/json_diagnostic/"; - mkdir(parameters.program_flow.json_plot_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - } - ReferenceReader ref_reader; ref_reader.Initialize(parameters.fasta); TargetsManager targets_manager; - targets_manager.Initialize(ref_reader, parameters.targets, parameters.trim_ampliseq_primers); + targets_manager.Initialize(ref_reader, parameters.targets, parameters.min_cov_fraction, parameters.trim_ampliseq_primers); BAMWalkerEngine bam_walker; bam_walker.Initialize(ref_reader, targets_manager, parameters.bams, parameters.postprocessed_bam, parameters.prefixExclusion); bam_walker.GetProgramVersions(parameters.basecaller_version, parameters.tmap_version); SampleManager sample_manager; - string sample_name = parameters.sampleName; - sample_manager.Initialize(bam_walker.GetBamHeader(), parameters.sampleName, parameters.force_sample_name); - if ((sample_name == "") and (sample_manager.num_samples_ > 1)) { - parameters.multisample = true; - cout << "Multisample run detected." << endl; - cerr << "FATAL ERROR: Multisample runs are not currently supported." << endl; - exit(-1); - } + sample_manager.Initialize(bam_walker.GetBamHeader(), parameters.sampleName, parameters.force_sample_name, parameters.multisample); InputStructures global_context; global_context.Initialize(parameters, ref_reader, bam_walker.GetBamHeader()); @@ -98,8 +86,16 @@ int main(int argc, char* argv[]) MolecularTagTrimmer tag_trimmer; tag_trimmer.InitializeFromSamHeader(parameters.tag_trimmer_parameters, bam_walker.GetBamHeader()); + MolecularTagManager mol_tag_manager; + mol_tag_manager.Initialize(&tag_trimmer, &sample_manager); + + if (tag_trimmer.HaveTags()){ + cout << "TVC: Call small variants with molecular tags. Indel Assembly will be turned off automatically." << endl; + parameters.program_flow.do_indel_assembly = false; + } + OrderedVCFWriter vcf_writer; - vcf_writer.Initialize(parameters.outputDir + "/" + parameters.outputFile, parameters, ref_reader, sample_manager); + vcf_writer.Initialize(parameters.outputDir + "/" + parameters.small_variants_vcf, parameters, ref_reader, sample_manager, tag_trimmer.HaveTags()); OrderedBAMWriter bam_writer; @@ -115,14 +111,18 @@ int main(int argc, char* argv[]) parsed_opts.setReference(parameters.fasta); parsed_opts.setBams(parameters.bams); parsed_opts.setTargetFile(parameters.targets); - parsed_opts.setOutputVcf(parameters.outputDir + "/indel_assembly.vcf"); + parsed_opts.setOutputVcf(parameters.outputDir + "/" + parameters.indel_assembly_vcf); parsed_opts.setParametersFile(parameters_file); - parsed_opts.setSampleName(sample_name); + // Weird behavior of assembly where an empty sample name enables multi-sample analysis + parsed_opts.setSampleName(parameters.multisample ? "" : sample_manager.primary_sample_name_); // Print the indel_assembly parameters if do_indel_assembly = true - if(parameters.program_flow.do_indel_assembly) + if(parameters.program_flow.do_indel_assembly){ + cout << "TVC: Parsing Indel Assembly parameters." << endl; parsed_opts.processParameters(parameters.opts); - else - cout<<"TVC: Indel assembly off."<< endl; + } + else{ + cout<<"TVC: Indel Assembly off."<< endl; + } IndelAssembly indel_assembly(&parsed_opts, &ref_reader, &sample_manager, &targets_manager); @@ -143,23 +143,7 @@ int main(int argc, char* argv[]) vc.metrics_manager = &metrics_manager; vc.sample_manager = &sample_manager; vc.indel_assembly = &indel_assembly; - vc.tag_trimmer = &tag_trimmer; - - // Will tvc call small variants with mol tags? - bool use_molecular_tag = vc.tag_trimmer->HaveTags(); - - if(use_molecular_tag){ - cout<< "TVC: Found "<< vc.tag_trimmer->NumTaggedReadGroups() << " read group(s) with molecular tags. Call small variants using molecular tags." << endl; - if(vc.tag_trimmer->NumReadGroups() > vc.tag_trimmer->NumTaggedReadGroups()){ - cout<< "TVC: Molecular tags not found in read group(s) {"; - for(int read_group_idx = 0; read_group_idx < vc.tag_trimmer->NumReadGroups(); ++read_group_idx){ - if(not vc.tag_trimmer->HasTags(read_group_idx)){ - cout<program_flow.do_indel_assembly){ - indel_assembly.onTraversalDone(); - } - else{ - indel_assembly.out.close(); - } - - string novel_vcf = parameters.outputDir + "/" + parameters.outputFile; - string assembly_vcf = parameters.outputDir + "/indel_assembly.vcf"; - string hotspots_file = parameters.variantPriorsFile; - string output_vcf = parameters.outputDir + "/TSVC_variants.vcf"; - string tvc_metrics = parameters.outputDir + "/tvc_metrics.json"; - string input_depth = parameters.outputDir + "/depth.txt"; - string output_genome_vcf = parameters.outputDir + "/TSVC_variants.genome.vcf"; + // --- Indel Assembly - if (!parameters.postprocessed_bam.empty()) { - int return_code = system(string("samtools index " + parameters.postprocessed_bam).c_str()); - } + indel_assembly.onTraversalDone(vc.parameters->program_flow.do_indel_assembly); + cout << "[tvc] Finished indel assembly. Processing time: " << (time(NULL)-indel_start_time) << " seconds." << endl; - { // block serves as isolation of merging and building tabix index - // Prepare merger object - VcfOrderedMerger merger(novel_vcf, assembly_vcf, hotspots_file, output_vcf, tvc_metrics, input_depth, output_genome_vcf, ref_reader, targets_manager, 10, max(0, parameters.minCoverage), true); + // --- VCF post processing & merging steps + /*/ XXX Not being done in TVC 5.4 but in the variant caller pipeline - merger.perform(); - } + time_t post_proc_start = time(NULL); - build_index(parameters.outputDir + "/TSVC_variants.vcf"); - build_index(parameters.outputDir + "/TSVC_variants.genome.vcf"); + string small_variants_vcf = parameters.outputDir + "/" + parameters.small_variants_vcf; + string indel_assembly_vcf = parameters.outputDir + "/" + parameters.indel_assembly_vcf; + string hotspots_file = parameters.variantPriorsFile; + string merged_vcf = parameters.outputDir + "/" + parameters.merged_vcf; + string tvc_metrics = parameters.outputDir + "/tvc_metrics.json"; // Name hard coded in metrics manager FinalizeAndSave call + string input_depth = parameters.outputDir + "/depth.txt"; // Name hard coded in BAM walker initialization + string output_genome_vcf = parameters.outputDir + "/" + parameters.merged_genome_vcf; - cerr << endl; - cout << endl; - cout << "[tvc] Normal termination. Processing time: " << (time(NULL)-start_time) << " seconds." << endl; + // VCF merging & post processing filters / subset annotation + VcfOrderedMerger merger(small_variants_vcf, indel_assembly_vcf, hotspots_file, merged_vcf, tvc_metrics, input_depth, + output_genome_vcf, ref_reader, targets_manager, 10, max(0, parameters.minCoverage), true); + merger.SetVCFrecordFilters(parameters.my_controls.filter_by_target, parameters.my_controls.hotspot_positions_only, parameters.my_controls.hotspot_variants_only); + merger.perform(); - return 0; -} + build_index(merged_vcf); + build_index(output_genome_vcf); + cout << "[tvc] Finished vcf post processing. Processing time: " << (time(NULL)-post_proc_start) << " seconds." << endl; + // */ + cerr << endl; + cout << endl; + cout << "[tvc] Total Processing time: " << (time(NULL)-start_time) << " seconds." << endl; + return EXIT_SUCCESS; +} +// -------------------------------------------------------------------------------------------------------------- +// The tvc exectuable is currently overloaded and harbors "tvc consensus" +int main(int argc, char* argv[]) +{ + if (argc > 1){ + if (string(argv[1]) == "consensus"){ + return ConsensusMain(argc - 1, argv + 1); + } + } + return main_tvc(argc, argv); +} +// -------------------------------------------------------------------------------------------------------------- void * VariantCallerWorker(void *input) { - Consensus consensus; - list consensus_position; - consensus_position.push_back(PositionInProgress()); - list::iterator new_position_ticket = consensus_position.begin(); - new_position_ticket->begin = NULL; - new_position_ticket->end = NULL; BamAlignment alignment; VariantCallerContext& vc = *static_cast(input); @@ -270,17 +258,31 @@ void * VariantCallerWorker(void *input) Alignment * new_read[kReadBatchSize]; bool success[kReadBatchSize]; list::iterator position_ticket; + int prev_positioin_ticket_begin = -1; // position_ticket.begin at the previous while loop + int prev_positioin_ticket_end = -1; // position_ticket.end at the previous while loop PersistingThreadObjects thread_objects(*vc.global_context); + + Consensus consensus; + consensus.SetFlowConsensus(false); + list consensus_position_temp(1); + list::iterator consensus_position_ticket = consensus_position_temp.begin(); + consensus_position_ticket->begin = NULL; + consensus_position_ticket->end = NULL; + + CandidateExaminer my_examiner(&thread_objects, &vc); + bool more_positions = true; - bool use_molecular_tag = vc.tag_trimmer->HaveTags(); + // Molecular tagging related stuffs + const bool use_molecular_tag = vc.mol_tag_manager->tag_trimmer->HaveTags(); + vector< vector< vector > > my_molecular_families_multisample; + MolecularFamilyGenerator my_family_generator; pthread_mutex_lock(&vc.bam_walker_mutex); MetricsAccumulator& metrics_accumulator = vc.metrics_manager->NewAccumulator(); pthread_mutex_unlock(&vc.bam_walker_mutex); while (true /*more_positions*/) { - - // Opportunistic read removal + // Opportunistic read removal if (vc.bam_walker->EligibleForReadRemoval()) { if (pthread_mutex_trylock(&vc.read_removal_mutex) == 0) { @@ -374,24 +376,33 @@ void * VariantCallerWorker(void *input) } pthread_mutex_unlock(&vc.read_loading_mutex); - + bool more_read_come = false; for (int i = 0; i < kReadBatchSize and success[i]; ++i) { // 1) Filling in read body information and do initial set of read filters if (not vc.candidate_generator->BasicFilters(*new_read[i])) continue; // 2) Alignment information altering methods (can also filter a read) - if (not vc.tag_trimmer->GetTagsFromBamAlignment(new_read[i]->alignment, new_read[i]->tag_info)){ + if (not vc.mol_tag_manager->tag_trimmer->GetTagsFromBamAlignment(new_read[i]->alignment, new_read[i]->tag_info)){ new_read[i]->filtered = true; continue; } + + // 3) Filter by target and Trim ampliseq primer here vc.targets_manager->TrimAmpliseqPrimers(new_read[i], vc.bam_walker->GetRecentUnmergedTarget()); if (new_read[i]->filtered) continue; - // 3) Parsing alignment: Read filtering & populating allele specific data types in Alignment object + + // 4) Filter by read mismatch limit (note: NOT the filter for read-max-mismatch-fraction) here. + FilterByModifiedMismatches(new_read[i], vc.parameters->read_mismatch_limit, vc.targets_manager); + if (new_read[i]->filtered) + continue; + + // 5) Parsing alignment: Read filtering & populating allele specific data types in Alignment object vc.candidate_generator->UnpackReadAlleles(*new_read[i]); - // 4) Unpacking read meta data for evaluator + + // 6) Unpacking read meta data for evaluator UnpackOnLoad(new_read[i], *vc.global_context); } @@ -419,84 +430,55 @@ void * VariantCallerWorker(void *input) vc.bam_walker->BeginPositionProcessingTask(position_ticket); pthread_mutex_unlock(&vc.bam_walker_mutex); - //@TODO: Still not the optimal way to do this. - //@TODO: my_molecular_families_multisample should be dynamically updated with BAMWalkerEngine when we add or remove a read. - //@TODO: We should declare vector< vector< map > > > my_molecular_families_multisample; - // Gather the reads into families - // my_molecular_families_multisample.size() = # of samples - // my_molecular_families_multisample[sample_index].size() = 2 strands, 0 for fwd, 1 for rev - // my_molecular_families_multisample[sample_index][strand_key].size() # of families found on the strand - vector< vector< vector > > > my_molecular_families_multisample; - int sample_num = 1; if(use_molecular_tag){ + int sample_num = 1; if (vc.parameters->multisample){ sample_num = vc.sample_manager->num_samples_; } - my_molecular_families_multisample.clear(); - my_molecular_families_multisample.resize(sample_num); - - for (int sample_index = 0; (sample_index < sample_num); ++sample_index) { - GenerateMyMolecularFamilies(*position_ticket, my_molecular_families_multisample[sample_index], *(vc.parameters), sample_index); - for (vector > >::iterator iter = my_molecular_families_multisample[sample_index].begin(); (iter != my_molecular_families_multisample[sample_index].end()); ++iter) { - for (vector >::iterator iter2 = iter->begin(); (iter2 != iter->end()); ++iter2) { - if (iter2->family_members.size() >= (unsigned int)vc.parameters->tag_trimmer_parameters.min_family_size) { - Alignment* alignment = new Alignment; - consensus.CalculateConsensus(*vc.ref_reader, iter2->family_members, *alignment); - if (not vc.candidate_generator->BasicFilters(*alignment)) {delete alignment;} - else { - vc.targets_manager->TrimAmpliseqPrimers(alignment, vc.bam_walker->GetRecentUnmergedTarget()); - if (alignment->filtered) {delete alignment;} - else { - if (new_position_ticket->begin == NULL) {new_position_ticket->begin = alignment;} - if (new_position_ticket->end != NULL) {new_position_ticket->end->next = alignment;} - new_position_ticket->end = alignment; - } - } - } - } - } + // No need to generate families if position_ticket is not changed. + bool is_position_ticket_changed = true; + if (position_ticket->begin != NULL and position_ticket->end != NULL){ + is_position_ticket_changed = (prev_positioin_ticket_begin != position_ticket->begin->read_number) or (prev_positioin_ticket_end != position_ticket->end->read_number); + } + + if (is_position_ticket_changed){ + for (int sample_index = 0; sample_index < sample_num; ++sample_index) { + int overloaded_sample_index = vc.parameters->multisample ? sample_index : -1; + my_molecular_families_multisample.resize(sample_num); + my_family_generator.GenerateMyMolecularFamilies(vc.mol_tag_manager, *position_ticket, overloaded_sample_index, my_molecular_families_multisample[sample_index]); + } + GenerateConsensusPositionTicket(my_molecular_families_multisample, vc, consensus, consensus_position_ticket); + } + if (position_ticket->begin != NULL and position_ticket->end != NULL){ + prev_positioin_ticket_begin = position_ticket->begin->read_number; + prev_positioin_ticket_end = position_ticket->end->read_number; + }else{ + prev_positioin_ticket_begin = -1; + prev_positioin_ticket_end = -1; } - new_position_ticket->end = NULL; } + // Candidate Generation int haplotype_length = 1; - if (new_position_ticket->begin != NULL) { - Alignment* p = new_position_ticket->begin; - while ((p != NULL) and (p != new_position_ticket->end)) { + if(consensus_position_ticket->begin != NULL){ + for (Alignment *p = consensus_position_ticket->begin; p; p = p->next) { vc.candidate_generator->UnpackReadAlleles(*p); - p = p->next; } - vc.bam_walker->SetupPositionTicket(new_position_ticket); - vc.candidate_generator->GenerateCandidates(variant_candidates, new_position_ticket, haplotype_length); - while ((new_position_ticket->begin != NULL) and (new_position_ticket->begin != new_position_ticket->end)) { - p = new_position_ticket->begin; - new_position_ticket->begin = new_position_ticket->begin->next; + vc.bam_walker->SetupPositionTicket(consensus_position_ticket); + vc.candidate_generator->GenerateCandidates(variant_candidates, consensus_position_ticket, haplotype_length); + while ((consensus_position_ticket->begin != NULL) and (consensus_position_ticket->begin != consensus_position_ticket->end)) { + Alignment* p = consensus_position_ticket->begin; + consensus_position_ticket->begin = consensus_position_ticket->begin->next; delete p; } -/* - if ((variant_candidates.size() > 0) and (variant_candidates[0].variant.position == 55259515)) { - p = new_position_ticket->begin; - while ((p != NULL) and (p != new_position_ticket->end)) { - //if (p->alignment.Name == "TSJAM:02884:02345") { - string ref_string = ""; - ReferenceReader::iterator ref_ptr = vc.ref_reader->iter(p->alignment.RefID, p->alignment.Position); - for (unsigned int i = 0; (i < p->alignment.QueryBases.length()); ++i) { - ref_string += *ref_ptr; - ++ref_ptr; - } - if (ref_string != p->alignment.QueryBases) { - cerr << p->alignment.Name << endl; - cerr << "consensus\t" << p->alignment.Position << "\t" << p->alignment.QueryBases << endl; - cerr << "reference\t" << p->alignment.Position << "\t" << ref_string << endl; - cerr << endl; - } - //} - } - } -*/ + if (consensus_position_ticket->begin != NULL) { + delete consensus_position_ticket->begin; + consensus_position_ticket->begin = NULL; + consensus_position_ticket->end = NULL; + } } - else { - vc.candidate_generator->GenerateCandidates(variant_candidates, position_ticket, haplotype_length); + else{ + vc.candidate_generator->GenerateCandidates(variant_candidates, position_ticket, haplotype_length, &my_examiner); } pthread_mutex_lock(&vc.bam_walker_mutex); @@ -532,9 +514,11 @@ void * VariantCallerWorker(void *input) if (vc.parameters->multisample) { bool pass = false; // if pass == false the no reads for the candidate bool filter = true; + int evaluator_error_code = 0; for (int sample_index = 0; (sample_index < vc.sample_manager->num_samples_); ++sample_index) { - if (EnsembleProcessOneVariant(thread_objects, vc, *v, *position_ticket, - my_molecular_families_multisample[sample_index], sample_index)) {pass = true;} + evaluator_error_code = EnsembleProcessOneVariant(thread_objects, vc, *v, *position_ticket, my_molecular_families_multisample[sample_index], sample_index); + pass = evaluator_error_code == 0; + //TODO: czb: the logic here is a mess. Need clean-up! if (!v->variant.isFiltered) {filter = false;} v->variant.isFiltered = false; } @@ -548,14 +532,17 @@ void * VariantCallerWorker(void *input) } if (!pass) { for (int sample_index = 0; (sample_index < vc.sample_manager->num_samples_); ++sample_index) { - AutoFailTheCandidate(v->variant, vc.parameters->my_controls.use_position_bias, v->variant.sampleNames[sample_index], use_molecular_tag); + string my_reason = evaluator_error_code == 2? "NOVALIDFUNCFAM" : "NODATA"; + AutoFailTheCandidate(v->variant, vc.parameters->my_controls.use_position_bias, v->variant.sampleNames[sample_index], use_molecular_tag, my_reason); } } } else { - if (!EnsembleProcessOneVariant(thread_objects, vc, *v, *position_ticket, - my_molecular_families_multisample[0], 0)) { - AutoFailTheCandidate(v->variant, vc.parameters->my_controls.use_position_bias, v->variant.sampleNames[0], use_molecular_tag); + // We only call based on the reads of the primary sample if a sample name was provided + int evaluator_error_code = EnsembleProcessOneVariant(thread_objects, vc, *v, *position_ticket, my_molecular_families_multisample[0], vc.sample_manager->primary_sample_); + if (evaluator_error_code != 0) { + string my_reason = evaluator_error_code == 2? "NOVALIDFUNCFAM" : "NODATA"; + AutoFailTheCandidate(v->variant, vc.parameters->my_controls.use_position_bias, v->variant.sampleNames[0], use_molecular_tag, my_reason); } } //v->isFiltered = true; diff --git a/Analysis/VariantCaller/tmol/tmol.cpp b/Analysis/VariantCaller/tmol/tmol.cpp index 4b638fef..6b7d2e4c 100644 --- a/Analysis/VariantCaller/tmol/tmol.cpp +++ b/Analysis/VariantCaller/tmol/tmol.cpp @@ -30,7 +30,7 @@ inline string itos(int i){ stringstream s; s << i; return s.str();} inline string ctos(char * c, int len){ stringstream s; for(int i=0;i1) allele_type = 3; // mnp=3 if(faf >= min_freq) { @@ -1401,8 +1402,8 @@ void report_consensus_counts(string & ref_seq, string ref_name, map0.999?"1/1:":"0/1:") << cons_sz[i] << ":" << global_consensus[12*i+j] << ":" << af << ":"; + if(filter_out)var_calls << ref_name << "\t" << (start_pos+i+1) << "\t.\t" << ref_allele << "\t" << alt_allele << "\t" << qual << "\tNOCALL\tDP=" << cons_sz[i] << ";AO=" << global_consensus[12*i+j] << ";AF=" << af << ";MDP=" << functional_counts << ";MAO=" << alt_key->second << ";MAF=" << faf << ";LOD=" << output_lod << ";TYPE=" << vartypes[allele_type] << ";FR=systematic_error\tGT:DP:AO:AF:FS\t" << "./.:" << cons_sz[i] << ":" << global_consensus[12*i+j] << ":" << af << ":"; + else var_calls << ref_name << "\t" << (start_pos+i+1) << "\t.\t" << ref_allele << "\t" << alt_allele << "\t" << qual << "\tPASS\tDP=" << cons_sz[i] << ";AO=" << global_consensus[12*i+j] << ";AF=" << af << ";MDP=" << functional_counts << ";MAO=" << alt_key->second << ";MAF=" << faf << ";LOD=" << output_lod << ";TYPE=" << vartypes[allele_type] << "\tGT:DP:AO:AF:FS\t" << (faf>0.999?"1/1:":"0/1:") << cons_sz[i] << ":" << global_consensus[12*i+j] << ":" << af << ":"; for(int k=0;k<9;k++) var_calls << functional_pileup[i][k].get_allele_read_counts(alt_key->first) << ","; var_calls << functional_pileup[i][9].get_allele_read_counts(alt_key->first) << endl; reported = true; @@ -1410,7 +1411,7 @@ void report_consensus_counts(string & ref_seq, string ref_name, map 0 && strcmp(hotspot_rec[h-1].get_ref(0).c_str(),ref_allele.c_str()) == 0 && strcmp(hotspot_rec[h-1].get_alt(0).c_str(),alt_allele.c_str()) == 0) { - var_calls << ref_name << "\t" << (start_pos+i+1) << "\t.\t" << ref_allele << "\t" << alt_allele << "\t" << 0 << "\tNOCALL\tDP=" << cons_sz[i] << ";AO=" << global_consensus[12*i+j] << ";AF=" << af << ";MDP=" << functional_counts << ";MAO=" << alt_key->second << ";MAF=" << faf << ";LOD=" << output_lod << ";FR=MAF<"<< faf <<";HS\tGT:DP:AO:AF:FS\t" << "./.:" << cons_sz[i] << ":" << global_consensus[12*i+j] << ":" << af << ":"; + var_calls << ref_name << "\t" << (start_pos+i+1) << "\t.\t" << ref_allele << "\t" << alt_allele << "\t" << 0 << "\tNOCALL\tDP=" << cons_sz[i] << ";AO=" << global_consensus[12*i+j] << ";AF=" << af << ";MDP=" << functional_counts << ";MAO=" << alt_key->second << ";MAF=" << faf << ";LOD=" << output_lod << ";FR=MAF<"<< faf << ";TYPE=" << vartypes[allele_type] <<";HS\tGT:DP:AO:AF:FS\t" << "./.:" << cons_sz[i] << ":" << global_consensus[12*i+j] << ":" << af << ":"; for(int k=0;k<9;k++) var_calls << functional_pileup[i][k].get_allele_read_counts(alt_key->first) << ","; var_calls << functional_pileup[i][9].get_allele_read_counts(alt_key->first) << endl; reported = true; @@ -1419,9 +1420,15 @@ void report_consensus_counts(string & ref_seq, string ref_name, maphotspot_rec[h-1].get_alt(a).length()?0:(hotspot_rec[h-1].get_ref(a).length()1) allele_type = 3; // mnp + if((allele_type==0 && (hotspot_rec[h-1].get_alt(a).length()>1 || hotspot_rec[h-1].get_alt(a)[0]!=hotspot_rec[h-1].get_ref(a)[0])) || (allele_type==2 && (hotspot_rec[h-1].get_ref(a).length()>1 || hotspot_rec[h-1].get_alt(a)[0]!=hotspot_rec[h-1].get_ref(a)[0]))) allele_type = 4; // complex + var_calls << ref_name << "\t" << (start_pos+i+1) << "\t.\t" << hotspot_rec[h-1].get_ref(a) << "\t" << hotspot_rec[h-1].get_alt(a) << "\t" << 0 << "\tREF\tDP=" << cons_sz[i] << ";AO=.;AF=.;MDP=" << functional_counts << ";MAO=.;MAF=.;LOD=" << output_lod << ";TYPE=" << vartypes[allele_type] << ";HS\tGT:DP:AO:AF:FS\t" << "0/0:" << cons_sz[i] << ":.:.:"; for(int k=0;k<9;k++) var_calls << functional_pileup[i][k].get_allele_read_counts(ref_seq.substr(i,1)) << ","; var_calls << functional_pileup[i][9].get_allele_read_counts(ref_seq.substr(i,1)) << endl; + } } } } @@ -1703,13 +1710,19 @@ int main(int argc, char *argv[]) var_calls << "##fileformat=VCFv4.1" << endl; var_calls << "##FORMAT=" << endl; var_calls << "##FORMAT=" << endl; - var_calls << "##INFO=" << endl; + var_calls << "##FORMAT=" << endl; + var_calls << "##FORMAT=" << endl; + var_calls << "##FORMAT=" << endl; + var_calls << "##INFO=" << endl; var_calls << "##INFO=" << endl; var_calls << "##INFO=" << endl; var_calls << "##INFO=" << endl; var_calls << "##INFO=" << endl; var_calls << "##INFO=" << endl; var_calls << "##INFO=" << endl; + var_calls << "##INFO=" << endl; + var_calls << "##INFO=" << endl; + var_calls << "##INFO=" << endl; var_calls << "##Tagged Molecule Caller tool v." << full_version_string << endl; var_calls << "##Tagging Method=" << tag_method << endl; var_calls << "##Tagging Level=" << tag_level << endl; diff --git a/Analysis/VariantCaller/tvcutils/prepare_hotspots.cpp b/Analysis/VariantCaller/tvcutils/prepare_hotspots.cpp index 7de47e9a..bea2e74b 100644 --- a/Analysis/VariantCaller/tvcutils/prepare_hotspots.cpp +++ b/Analysis/VariantCaller/tvcutils/prepare_hotspots.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,8 @@ void PrepareHotspotsHelp() printf ("General options:\n"); printf (" -b,--input-bed FILE input is a hotspots BED file (either -b or -v required)\n"); printf (" -v,--input-vcf FILE input is a hotspots VCF file (either -b or -v required)\n"); + printf (" -p,--input-real-vcf FILE input is a real vcf file that we will process (when this is present -b and -v cannot, but -o must present)\n"); + printf (" -q,--output-fake-hot-vcf FILE output a hotspot file for subset alleles\n"); printf (" -d,--output-bed FILE output left-aligned hotspots in BED format [none]\n"); printf (" -o,--output-vcf FILE output is a hotspots VCF file. To be used as input to --hotspot-vcf argument of variant_caller_pipeline.py (recommended) [none]\n"); printf (" -r,--reference FILE FASTA file containing reference genome (required)\n"); @@ -221,12 +224,126 @@ class junction { vector junc_; }; +static bool is_mnp_indel(const char *r, int rl, const char *a, int al) +{ + if (rl == al) return true; + int i, j, k; + for (i = 0; i < rl and i < al; i++) if (r[i] != a[i]) break; + for (j = rl-1, k = al-1; j >=i and k >= i; j--, k--) if (r[j] != a[k]) break; + if (j < i or k < i) return true; + return false; +} + +bool allele_subset(int pos1, const char *ref1, const char *alt1, int pos2, const char *ref2, const char *alt2) +{ + int rlen1 = strlen(ref1); + int rlen2 = strlen(ref2); + if (pos1+rlen1 < pos2+rlen2) return false; + if (pos1 > pos2) return false; + // pos1 <= pos2 + int alen1 = strlen(alt1), alen2 = strlen(alt2); + if (strncmp(ref1+pos2-pos1, ref2, rlen2)!=0) return false; // not even right + const char *s = strstr(alt1, alt2); + while (s) { + // check left right portion + if (is_mnp_indel(ref1, pos2-pos1, alt1, s-alt1) and is_mnp_indel(ref1+pos2-pos1+rlen2, pos1+rlen1-pos2-rlen2, alt1+(s-alt1+alen2), alen1-(s-alt1+alen2))) return true; + s = strstr(s+1, alt2); // check if there are multiple occurence of the small variant + } + return false; +} + + +class one_vcfline { //ZZ for subset + public: + one_vcfline(char *r, char *alt, int p, int g1, int g2, char *line) { + strcpy(ref, r); + //split alt into alts + char *ss; + for (ss = strtok(alt, ","); ss; ss = strtok(NULL, ",")) { + alts.push_back(string(ss)); + } + pos = p; + affected = false; + gt1 = g1; gt2 = g2; + o_line = string(line); + }; + char ref[1024]; + vector alts; + int pos; + bool affected; + void check_subset(one_vcfline &newline) { + unsigned int i, j; + bool need_pad = false; + for (i = 0; i < newline.alts.size(); ) { + if (i+1 != newline.gt1 and i+1 != newline.gt2) {i++; continue;} + for (j = 0; j < alts.size(); j++) { + if (j+1 != gt1 and j+1 != gt2) continue; + if (allele_subset(pos, ref, alts[j].c_str(), newline.pos, newline.ref, newline.alts[i].c_str())) break; + if (newline.pos == pos+1 and strlen(ref) < strlen(newline.ref)) { + char tmp[1024], tmp2[1024]; + tmp2[1] = tmp[1] = 0; tmp[0] = tmp2[0] = ref[0]; strcat(tmp, newline.ref); strcat(tmp2, newline.alts[i].c_str()); + if (allele_subset(pos, tmp, tmp2, pos, ref, alts[j].c_str())) { need_pad = true;break;} + } + } + if (j < alts.size()) { + if (need_pad) { + padding_tail(newline.ref+strlen(ref)-1); + } + add_one(newline.pos, strlen(newline.ref), newline.alts[i]); + newline.remove_ith_alt(i); + affected = newline.affected = true; + // after erase, no need to do i++; + } else { + i++; + } + } + }; + void padding_tail(char *addition) { + strcat(ref, addition); + string s(addition); + for (unsigned int i = 0; i < alts.size(); i++) alts[i] += s; + }; + bool produce_hot_vcf(char *chr, FILE *fp, int &hot_n, FILE *hot_p) { // out to a file + if (not affected) {fprintf(fp, "%s", o_line.c_str()); return false;} + fprintf(hot_p,"%s\t%d\thotspot_%d\t%s\t", chr, pos, hot_n, ref); + hot_n++; + for (unsigned int i = 0; i < alts.size(); i++) { + if (i != 0) fprintf(hot_p, ","); + fprintf(hot_p, "%s", alts[i].c_str()); + } + fprintf(hot_p, "\t.\t.\t.\n"); + return true; + }; + bool produce_hot_vcf(char *chr, FILE *fp, int &hot_n) { + return produce_hot_vcf(chr, fp, hot_n, stdout); + } + void remove_ith_alt(int i) { + alts.erase(alts.begin()+i); + }; + unsigned int gt1, gt2; + protected: + void add_one(unsigned int p, unsigned int reflen, string &alt) { + string s; + unsigned int i; + for (i = pos; i < p; i++) s.push_back(ref[i-pos]); //padding front + // add middle + s += alt; + // padding back + for (i = p+reflen; i < pos+strlen(ref); i++) s.push_back(ref[i-pos]); + alts.push_back(s); + }; + string o_line; +}; + + int PrepareHotspots(int argc, const char *argv[]) { OptArgs opts; opts.ParseCmdLine(argc, argv); string input_bed_filename = opts.GetFirstString ('b', "input-bed", ""); string input_vcf_filename = opts.GetFirstString ('v', "input-vcf", ""); + string input_real_vcf_filename = opts.GetFirstString ('p', "input-real-vcf", ""); + string output_hot_vcf = opts.GetFirstString ('q', "output-fake-hot-vcf", ""); string output_bed_filename = opts.GetFirstString ('d', "output-bed", ""); string output_vcf_filename = opts.GetFirstString ('o', "output-vcf", ""); string reference_filename = opts.GetFirstString ('r', "reference", ""); @@ -236,11 +353,15 @@ int PrepareHotspots(int argc, const char *argv[]) bool allow_block_substitutions = opts.GetFirstBoolean('s', "allow-block-substitutions", true); opts.CheckNoLeftovers(); - if((input_bed_filename.empty() == input_vcf_filename.empty()) or + if((input_bed_filename.empty() == (input_vcf_filename.empty() and input_real_vcf_filename.empty())) or (output_bed_filename.empty() and output_vcf_filename.empty()) or reference_filename.empty()) { PrepareHotspotsHelp(); return 1; } + if ((not input_real_vcf_filename.empty()) and (output_vcf_filename.empty() or not input_vcf_filename.empty())) { + PrepareHotspotsHelp(); + return 1; + } // Populate chromosome list from reference.fai @@ -342,6 +463,13 @@ int PrepareHotspots(int argc, const char *argv[]) continue; } + // OID= table has special meaning + if (string::npos != string(line2).find("OID=")) { + line_status.push_back(LineStatus(line_number)); + line_status.back().filter_message_prefix = "Bed line contains OID="; + continue; + } + char *current_chr = strtok(line2, "\t\r\n"); char *current_start = strtok(NULL, "\t\r\n"); char *current_end = strtok(NULL, "\t\r\n"); @@ -406,6 +534,7 @@ int PrepareHotspots(int argc, const char *argv[]) for (char *pos = current_alt+4; *pos; ++pos) allele.alt += toupper(*pos); // here is the place to check the length of the hotspot cover the amplicon junction. ZZ + /* if (junc.contain(allele.chr_idx, allele.pos, (unsigned int) allele.ref.size())) { line_status.push_back(LineStatus(line_number)); line_status.back().filter_message_prefix = "hotspot BED line contain the complete overlapping region of two amplicon, the variant cannot be detected by tvc"; @@ -416,6 +545,7 @@ int PrepareHotspots(int argc, const char *argv[]) line_status.back().filter_message_prefix = "hotspot BED line is not contained in any amplicon, the variant cannot be detected by tvc"; continue; } + */ allele.filtered = false; line_status.push_back(LineStatus(line_number)); @@ -434,17 +564,50 @@ int PrepareHotspots(int argc, const char *argv[]) } - if (!input_vcf_filename.empty()) { - FILE *input = fopen(input_vcf_filename.c_str(),"r"); - if (!input) { - fprintf(stderr,"ERROR: Cannot open %s\n", input_vcf_filename.c_str()); - return 1; + if (!input_vcf_filename.empty() or !input_real_vcf_filename.empty()) { + + bool real_vcf = false; + FILE *input; + FILE *out_real = NULL; + FILE *out_hot = NULL; + int fake_ = 0; + int hn = 1; + if (!input_real_vcf_filename.empty()) { + real_vcf = true; + input = fopen(input_real_vcf_filename.c_str(),"r"); + if (!input) { + fprintf(stderr,"ERROR: Cannot open %s\n", input_real_vcf_filename.c_str()); + return 1; + } + out_real = fopen(output_vcf_filename.c_str(), "w"); + if (!out_real) { + fprintf(stderr,"ERROR: Cannot open %s\n", output_vcf_filename.c_str()); + return 1; + } + if (!output_hot_vcf.empty()) { + out_hot = fopen(output_hot_vcf.c_str(), "w"); + if (!out_hot) { + fprintf(stderr,"ERROR: Cannot open %s\n", output_hot_vcf.c_str()); + return 1; + } + } else out_hot = stdout; + fprintf(out_hot, "##fileformat=VCFv4.1\n##allowBlockSubstitutions=true\n#CHROM POS ID REF ALT QUAL FILTER INFO\n"); + } else { + input = fopen(input_vcf_filename.c_str(),"r"); + if (!input) { + fprintf(stderr,"ERROR: Cannot open %s\n", input_vcf_filename.c_str()); + return 1; + } } char line2[65536]; + char line3[65536]; int line_number = 0; bool line_overflow = false; + list vcflist; + + char last_chr[1024] = ""; while (fgets(line2, 65536, input) != NULL) { if (line2[0] and line2[strlen(line2)-1] != '\n' and strlen(line2) == 65535) { line_overflow = true; @@ -462,9 +625,12 @@ int PrepareHotspots(int argc, const char *argv[]) allow_block_substitutions = true; continue; } - if (line2[0] == '#') + if (line2[0] == '#') { + if (out_real) { fprintf(out_real, "%s", line2);} continue; + } + if (real_vcf) strcpy(line3, line2); char *current_chr = strtok(line2, "\t\r\n"); char *current_start = strtok(NULL, "\t\r\n"); char *current_id = strtok(NULL, "\t\r\n"); @@ -473,10 +639,13 @@ int PrepareHotspots(int argc, const char *argv[]) strtok(NULL, "\t\r\n"); // Ignore QUAL strtok(NULL, "\t\r\n"); // Ignore FILTER char *current_info = strtok(NULL, "\t\r\n"); + strtok(NULL, "\t\r\n"); + char *gt = strtok(NULL, "\t\r\n"); if (!current_chr or !current_start or !current_id or !current_ref or !current_alt) { line_status.push_back(LineStatus(line_number)); - line_status.back().filter_message_prefix = "Malformed hotspot VCF line: expected at least 5 fields"; + if (real_vcf) line_status.back().filter_message_prefix = "Malformed real VCF line: expected at least 5 fields"; + else line_status.back().filter_message_prefix = "Malformed hotspot VCF line: expected at least 5 fields"; continue; } @@ -505,9 +674,13 @@ int PrepareHotspots(int argc, const char *argv[]) // Process custom tags vector bstrand; vector hp_max_length; + string raw_oid; + string raw_omapalt; + string raw_oalt; + string raw_oref; + string raw_opos; + if (current_info) { - string raw_oid; - string raw_omapalt; string raw_bstrand; string raw_hp_max_length; for (char *next = strtok(current_info, ";"); next; next = strtok(NULL, ";")) { @@ -526,12 +699,18 @@ int PrepareHotspots(int argc, const char *argv[]) continue; if (strcmp(next, "FR") == 0) continue; - if (strcmp(next, "OPOS") == 0) + if (strcmp(next, "OPOS") == 0) { + raw_opos = value; continue; - if (strcmp(next, "OREF") == 0) + } + if (strcmp(next, "OREF") == 0) { + raw_oref = value; continue; - if (strcmp(next, "OALT") == 0) + } + if (strcmp(next, "OALT") == 0) { + raw_oalt = value; continue; + } if (strcmp(next, "OID") == 0) { raw_oid = value; continue; @@ -557,7 +736,33 @@ int PrepareHotspots(int argc, const char *argv[]) } - + if (real_vcf) { + //fprintf(stderr, "%s\n", gt); + if (gt == NULL) continue; + // get gt + int g1 = atoi(gt), g2; + gt = strchr(gt, '/'); + if (gt) g2 = atoi(gt+1); + else {fprintf(stderr, "GT not formatted right\n"); exit(1);} + //if (g1 == 0 and g2 == 0) continue; + unsigned int cur_pos = atoi(current_start); + one_vcfline newline(current_ref, current_alt, cur_pos, g1, g2, line3); + bool new_chr = false; + if (strcmp(current_chr, last_chr) != 0) { + new_chr = true; + } + while (not vcflist.empty()) { + if ((not new_chr) and vcflist.front().pos+strlen(vcflist.front().ref) > cur_pos) break; + if (vcflist.front().produce_hot_vcf(last_chr, out_real, hn, out_hot)) fake_++; + vcflist.pop_front(); + } + if (new_chr) strcpy(last_chr, current_chr); + for (list::iterator it = vcflist.begin(); it != vcflist.end(); it++) { + it->check_subset(newline); + } + if (not newline.alts.empty()) vcflist.push_back(newline); + continue; + } unsigned int allele_idx = 0; for (char *sub_alt = strtok(current_alt,","); sub_alt; sub_alt = strtok(NULL,",")) { @@ -597,8 +802,20 @@ int PrepareHotspots(int argc, const char *argv[]) } fclose(input); + if (real_vcf) { + while (not vcflist.empty()) { + if (vcflist.front().produce_hot_vcf(last_chr, out_real, hn, out_hot)) fake_++; + vcflist.pop_front(); + } + fclose(out_real); + fclose(out_hot); + if (fake_ > 0) + return 0; + else return 1; + } } + // Process by chromosome: // - Verify reference allele // - Left align @@ -637,6 +854,19 @@ int PrepareHotspots(int argc, const char *argv[]) for (deque::iterator A = alleles[chr_idx].begin(); A != alleles[chr_idx].end(); ++A) { + // check bed file + if (junc.contain(A->chr_idx, A->pos, (unsigned int) A->ref.size())) { + A->filtered = true; + A->line_status->filter_message_prefix = "hotspot BED line contain the complete overlapping region of two amplicon, the variant cannot be detected by tvc"; + continue; + } + if (not junc.contained_in_ampl(A->chr_idx, A->pos, (unsigned int) A->ref.size())) { + A->filtered = true; + A->line_status->filter_message_prefix = "hotspot BED line is not contained in any amplicon, the variant cannot be detected by tvc"; + continue; + } + + // Invalid characters bool valid = true; @@ -748,6 +978,7 @@ int PrepareHotspots(int argc, const char *argv[]) alt_end = alt_end_orig; } else { // left align the indel part, here either ref_end = 0 or alt_end = 0 + int opos = A->pos; while (A->pos > 0) { char nuc = ref_index[chr_idx].base(A->pos-1); if (ref_end > 0 and A->ref[ref_end-1] != nuc) @@ -759,10 +990,20 @@ int PrepareHotspots(int argc, const char *argv[]) A->pos--; } if (ref_end != ref_end_orig) { - // trailing part is aligned, the whole ref and alt need to be kept. + // trailing part is aligned, the whole ref and alt need to be kept. ZZ ref_end = A->ref.size(); alt_end = A->alt.size(); } + if (junc.contain(chr_idx, A->pos, ref_end) or not junc.contained_in_ampl(chr_idx, A->pos, ref_end)) { + // after left align the hotspot contain an overlap region, revert to the original ZZ + if (opos != A->pos) { + A->ref.erase(0, opos-A->pos); + A->alt.erase(0, opos-A->pos); + A->pos = opos; + ref_end = ref_end_orig; + alt_end = alt_end_orig; + } + } } } A->ref.resize(ref_end); diff --git a/Analysis/VariantCaller/tvcutils/split_vcf.cpp b/Analysis/VariantCaller/tvcutils/split_vcf.cpp index 58d6ca89..cccd67bb 100644 --- a/Analysis/VariantCaller/tvcutils/split_vcf.cpp +++ b/Analysis/VariantCaller/tvcutils/split_vcf.cpp @@ -220,10 +220,10 @@ void check_sample(FILE *input, vector& out_files, string& qual_field } if ((*tag_iter == "AO") or (*tag_iter == "DP") or - (*tag_iter == "MDP") or - (*tag_iter == "MAO") or - (*tag_iter == "MRO") or - (*tag_iter == "MAF") or + (*tag_iter == "MDP") or + (*tag_iter == "MAO") or + (*tag_iter == "MRO") or + (*tag_iter == "MAF") or (*tag_iter == "FAO") or (*tag_iter == "FDP") or (*tag_iter == "FRO") or @@ -350,4 +350,4 @@ int SplitVcf(int argc, const char *argv[]) } return 0; -} +} \ No newline at end of file diff --git a/Analysis/VariantCaller/tvcutils/unify_vcf.cpp b/Analysis/VariantCaller/tvcutils/unify_vcf.cpp index 95f9df47..dd4f8787 100644 --- a/Analysis/VariantCaller/tvcutils/unify_vcf.cpp +++ b/Analysis/VariantCaller/tvcutils/unify_vcf.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,41 @@ #define MAX_DP "MAX_DP" #define MIN_DP "MIN_DP" +using namespace std; + +// Help function for module + +void UnifyVcfHelp() +{ + printf ("\n"); + printf ("tvcutils %s-%s (%s) - Miscellaneous tools used by Torrent Variant Caller plugin and workflow.\n", + IonVersion::GetVersion().c_str(), IonVersion::GetRelease().c_str(), IonVersion::GetGitHash().c_str()); + printf ("\n"); + printf ("Usage: tvcutils unify_vcf [options]\n"); + printf ("\n"); + printf ("General options:\n"); + printf (" -t,--novel-tvc-vcf FILE small variants VCF file produced by TVC (required)\n"); + printf (" -i,--novel-assembly-vcf FILE long indels VCF file produced by TVCASSEMBLY (optional)\n"); + printf (" -a,--hotspot-annotation-vcf FILE hotspot annotations VCF file (optional)\n"); + printf (" -f,--reference-fasta FILE FASTA file containing reference genome (required)\n"); + printf (" -b,--target-file FILE target regions BED file (optional)\n"); + printf (" -o,--output-vcf FILE output merged and annotated VCF or VCF.GZ file (required)\n"); + printf (" -j,--tvc-metrics FILE JSON file with tvc metrics (optional)\n"); + printf (" -d,--input-depth FILE output of samtools depth. if provided, will cause generation of gvcf (optional, stdin ok)\n"); + printf (" -m,--min-depth INT minimum coverage depth in GVCF output (optional)\n"); + printf("\nVCF record filters:\n"); + printf(" --filter-by-target on/off Filter vcf records by meta information in the target bed file [on]\n"); + printf(" --hotspot-positions-only on/off Report only hotspot vcf records in final output [off]\n"); + printf(" --hotspot-variants-only on/off Suppress hotspot records that are no-calls or reference-calls [off]\n"); + printf("\nAllele Subset annotation:\n"); + printf(" --subset-check on/off Enables or disables subset allele subset annotation [on]\n"); + printf(" --subset-scores INT,INT,INT,INT Scores for Smith-Waterman aligner: match,mismatch,gap-open,gap-extend [1,-3,-5,-2]\n"); + printf(" --subset-simple-mnp on/off Simplified (faster) subset check for MNPs. [on]\n"); + printf ("\n"); +} + +// --------------------------------------------------------------------------------------- + void build_index(const string &path_in) { char buffer[33]; int return_code = 0; @@ -102,35 +138,7 @@ void build_index(const string &path_in) { if (tab == -1) {cerr << "build_index failed on tabix. " << return_code << endl;} } -using namespace std; - - - -void UnifyVcfHelp() -{ - printf ("\n"); - printf ("tvcutils %s-%s (%s) - Miscellaneous tools used by Torrent Variant Caller plugin and workflow.\n", - IonVersion::GetVersion().c_str(), IonVersion::GetRelease().c_str(), IonVersion::GetGitHash().c_str()); - printf ("\n"); - printf ("Usage: tvcutils unify_vcf [options]\n"); - printf ("\n"); - printf ("General options:\n"); - printf (" -t,--novel-tvc-vcf FILE small variants VCF file produced by TVC (required)\n"); - printf (" -i,--novel-assembly-vcf FILE long indels VCF file produced by TVCASSEMBLY (optional)\n"); - printf (" -a,--hotspot-annotation-vcf FILE hotspot annotations VCF file (optional)\n"); - printf (" -a,--hotspot-annotation-vcf FILE hotspot annotations VCF file (optional)\n"); - printf (" -f,--reference-fasta FILE FASTA file containing reference genome (required)\n"); - printf (" -b,--target-file FILE target regions BED file (optional)\n"); - printf (" -o,--output-vcf FILE output merged and annotated VCF or VCF.GZ file (required)\n"); - printf (" -j,--tvc-metrics FILE JSON file with tvc metrics (optional)\n"); - printf (" -d,--input-depth FILE output of samtools depth. if provided, will cause generation of gvcf (optional, stdin ok)\n"); - printf (" -m,--min-depth INT minimum coverage depth in GVCF output (optional)\n"); - printf ("\n"); -} - - - - +// --------------------------------------------------------------------------------------- CoverageInfoEntry* CoverageInfoEntry::parse(const char * line, const ReferenceReader& r) { CoverageInfoEntry* entry = new CoverageInfoEntry(r); @@ -180,7 +188,9 @@ int bgzf_stream::write_buffer() { return -1; } +// =========================================================================== // PriorityQueue methods implementations + bool PriorityQueue::get_next_variant(vcf::Variant* current) { // parse VCF line by line if (current->vcf->_done) return false; @@ -247,7 +257,47 @@ void PriorityQueue::left_align_variant(vcf::Variant* variant) { variant->alt[0] = alt; } +void PriorityQueue::open_vcf_file(vcf::VariantCallFile& vcf, string filename, bool parse_samples) { + // Open VCF file for parsing + if (!enabled) return; + vcf.parseSamples = parse_samples; + vcf.open(filename); + if (!vcf.is_open()) { + cerr << "ERROR: Could not open file : " << filename << " : " << strerror(errno) << endl; + exit(1); + } + vcf._done = false; + next(); +} + +void PriorityQueue::next() { + if (!enabled) return; + while (size() < _size) { + ComparableVcfVariant* v = new ComparableVcfVariant(merger, file, _vc++); + if (get_next_variant(v)) { + trim_variant(v); + left_align_variant(v); + push(v); + } else { + _size = 0; + delete_variant(v); + break; + } + } + delete_current(); + if (empty()) { + _current = NULL; + return; + } + _current = top(); + + pop(); +} + +// ===================================================================================== // VcfOrederedMerger methods implementations + + VcfOrderedMerger::VcfOrderedMerger(string& novel_tvc, string& assembly_tvc, string& hotspot_tvc, @@ -265,7 +315,13 @@ VcfOrderedMerger::VcfOrderedMerger(string& novel_tvc, assembly_queue(assembly_tvc, *this, r, w, la, true), hotspot_queue(hotspot_tvc, *this, r, w, la), bgz_out(output_tvc.c_str()), - current_cov_info(NULL) { + current_cov_info(NULL), + filter_by_target(true), + hotspot_positions_only(false), + hotspot_variants_only(false), + num_records(0), + num_filtered_records(0), + num_off_target(0) { if (!input_depth.empty()) { depth_in = (istream *)((input_depth == DEFAULT_STDIN_PARAM) ? &cin : new ifstream(input_depth.c_str())); gvcf_out = new ofstream(gvcf_output.c_str()); @@ -275,11 +331,21 @@ VcfOrderedMerger::VcfOrderedMerger(string& novel_tvc, current_target = targets_manager.merged.begin(); } + VcfOrderedMerger::~VcfOrderedMerger() { if (depth_in && depth_in != &cin) delete depth_in; if (gvcf_out) {gvcf_out->close(); delete gvcf_out;} } +void VcfOrderedMerger::SetVCFrecordFilters(bool filt_by_target, bool hotspot_pos_only, bool hotspot_var_only) +{ + filter_by_target = filt_by_target; + hotspot_positions_only = hotspot_pos_only; + hotspot_variants_only = hotspot_var_only; +} + +// ----------------------------------------------------------------------------------- + template int VcfOrderedMerger::variant_cmp(const T* v1, const vcf::Variant* v2) const { int idx_v1 = reference_reader.chr_idx(v1->sequenceName.c_str()); @@ -287,6 +353,22 @@ int VcfOrderedMerger::variant_cmp(const T* v1, const vcf::Variant* v2) const { return compare(idx_v1, v1->position, idx_v2, v2->position); } +// ----------------------------------------------------------------------------------- + +bool VcfOrderedMerger::too_far(vcf::Variant* v1, vcf::Variant* v2) { + if (v2 == NULL) return true; + int far = 50; + v2->position -= far; + int com = variant_cmp(v1, v2); + v2->position += far; // return to the original position + return (com == 1); +} + +// ----------------------------------------------------------------------------------- +// position in vcf is 1-based +// targets in bed format are 0-based open ended intervals +// with index conversion [0,x[ becomes ]0,x] + template bool VcfOrderedMerger::is_within_target_region(T *variant) { long pos = variant->position; @@ -300,10 +382,16 @@ bool VcfOrderedMerger::is_within_target_region(T *variant) { } return current_target != targets_manager.merged.end() && current_target->chr == chr_idx - && pos >= current_target->begin + && pos > current_target->begin && pos <= current_target->end; } + + + + +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::perform() { // merging ordered files loop while (novel_queue.has_value() && assembly_queue.has_value()) { @@ -328,17 +416,24 @@ void VcfOrderedMerger::perform() { assembly_queue.next(); } while (hotspot_queue.has_value()) { - blacklist_check(hotspot_queue.current()); + //blacklist_check(hotspot_queue.current()); + merge_annotation_into_vcf(hotspot_queue.current()); hotspot_queue.next(); } + flush_vcf(NULL); while (current_target != targets_manager.merged.end()) { gvcf_finish_region(); current_target++; } + cout << "VcfOrderedMerger: Wrote " << num_records << " vcf records, num_filtered_records= " + << num_filtered_records << " , num_off_target=" << num_off_target << endl; + allele_subset.print_stats(); } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::write_header(vcf::VariantCallFile& vcf, string json_path) { - extend_header(vcf); + extend_header(vcf, allele_subset.check_enabled); parse_parameters_from_json(json_path, vcf); // write out the header to output bgz_out << vcf.header << "\n"; @@ -353,6 +448,8 @@ void VcfOrderedMerger::write_header(vcf::VariantCallFile& vcf, string json_path) } } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::transfer_fields(map >& original_field, map >& new_field, map& field_types, @@ -388,6 +485,8 @@ void VcfOrderedMerger::transfer_fields(map >& original_fi } } +// ----------------------------------------------------------------------------------- + vcf::Variant* VcfOrderedMerger::merge_overlapping_variants() { vcf::Variant& novel_v = *novel_queue.current(); vcf::Variant& assembly_v = *assembly_queue.current(); @@ -431,6 +530,69 @@ vcf::Variant* VcfOrderedMerger::merge_overlapping_variants() { return assembly_queue.current(); } +// ----------------------------------------------------------------------------------- + +bool VcfOrderedMerger::find_match(vcf::Variant* merged_entry, string &hotspot_ref,vector::iterator oid, vector::iterator opos, vector::iterator oref, vector::iterator oalt, string *omapalt, int record_ref_extension, string &annotation_ref_extension) { + + if (merged_entry == NULL) return false; + string adj_omp; + if (record_ref_extension) { + if ((int)omapalt->length() < record_ref_extension || hotspot_ref.substr(hotspot_ref.length() - record_ref_extension) != + omapalt->substr(omapalt->length() - record_ref_extension)) { + return false; + cout << UNIFY_VARIANTS << " Hotspot annotation " << merged_entry->sequenceName + << ":" << merged_entry->position << ", allele " << *omapalt << " not eligible for shortening.\n"; + } + adj_omp = omapalt->substr(0, omapalt->length() - record_ref_extension); + } else adj_omp = *omapalt; + //*omapalt = *omapalt + annotation_ref_extension; + adj_omp = adj_omp+annotation_ref_extension; + + vector::iterator omapalti = + find(merged_entry->info["OMAPALT"].begin(), merged_entry->info["OMAPALT"].end(), adj_omp); + + if (omapalti == merged_entry->info["OMAPALT"].end()) { + return false; + // match older records. + cout << UNIFY_VARIANTS << " Hotspot annotation " << merged_entry->sequenceName + << ":" << merged_entry->position << ", allele " << *omapalt << " not found in merged variant file.\n"; + } + if (oref->length() >= 1 && oalt->length() >= 1 && (*oref)[0] == (*oalt)[0]) { + *oref = oref->substr(1); + *oalt = oalt->substr(1); + long p = atol(opos->c_str()); + stringstream ss; + ss<<++p; + *opos = ss.str(); + } + if (oref->empty()) { + *oref = "-"; + } + if(oalt->empty()) { + *oalt = "-"; + } + long idx = omapalti - merged_entry->info["OMAPALT"].begin(); + + if (merged_entry->info["OID"][idx] == ".") { + merged_entry->info["OID"][idx] = *oid; + merged_entry->info["OPOS"][idx] = *opos; + merged_entry->info["OREF"][idx] = *oref; + merged_entry->info["OALT"][idx] = *oalt; + merged_entry->info["OMAPALT"][idx] = /**omapalt*/ adj_omp; + } else { + merged_entry->info["OID"].push_back(*oid); + merged_entry->info["OPOS"].push_back(*opos); + merged_entry->info["OREF"].push_back(*oref); + merged_entry->info["OALT"].push_back(*oalt); + merged_entry->info["OMAPALT"].push_back(adj_omp /**omapalt*/); + } + if (merged_entry->id.size() == 0 or merged_entry->id == ".") merged_entry->id = *oid; + else merged_entry->id += ";"+ *oid; + return true; +} + +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::span_ref_and_alts() { long extension_length = novel_queue.current()->ref.length() - assembly_queue.current()->ref.length(); // extend alt sequences (span to the same length everywhere) @@ -455,6 +617,8 @@ void VcfOrderedMerger::span_ref_and_alts() { } } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::generate_novel_annotations(vcf::Variant* variant) { if (variant->alt.empty()) return; for (vector::iterator alt = variant->alt.begin(); alt != variant->alt.end(); alt++) { @@ -462,7 +626,6 @@ void VcfOrderedMerger::generate_novel_annotations(vcf::Variant* variant) { long opos = variant->position; string oref = variant->ref; string temp_alt = *alt; - // trim identical ends string::iterator orefi = oref.end(); string::iterator alti = temp_alt.end(); @@ -470,16 +633,14 @@ void VcfOrderedMerger::generate_novel_annotations(vcf::Variant* variant) { if (alti + 1 < temp_alt.end()) temp_alt.erase(alti + 1, temp_alt.end()); if (orefi + 1 < oref.end()) oref.erase(orefi + 1, oref.end()); - // trim identical beginnings orefi = oref.begin(); alti = temp_alt.begin(); - for (; *orefi == *alti && *alti != '-'; orefi++, alti++) { + for (; *orefi == *alti && *alti != '-' and alti < temp_alt.end() and orefi < oref.end(); orefi++, alti++) { opos++; } if (distance(temp_alt.begin(), alti) > 0) temp_alt.erase(temp_alt.begin(), alti); if (distance(oref.begin(), orefi) > 0) oref.erase(oref.begin(), orefi); - if (oref.empty()) oref = "-"; if (temp_alt.empty()) temp_alt = "-"; stringstream ss; @@ -490,18 +651,81 @@ void VcfOrderedMerger::generate_novel_annotations(vcf::Variant* variant) { push_value_to_vector(variant->info["OREF"], i, oref); push_value_to_vector(variant->info["OALT"], i, temp_alt); push_value_to_vector(variant->info["OMAPALT"], i, *alt); + // We only write a subset info field if there actually are subsets } } + +// ----------------------------------------------------------------------------------- +// implementation of subset check and annotation XXX + +void VcfOrderedMerger::annotate_subset(vcf::Variant* variant) { + + if (not allele_subset.check_enabled) + return; + // Immediately return if this is a NOCALL or not a multi allele variant + if (variant->filter != "PASS" or variant->alt.size()<2) + return; + + // unpack called alleles + vector called_alts; + string gt_field; + stringstream gt(variant->samples[variant->sampleNames[0]]["GT"][0]); + while (getline(gt, gt_field, '/')){ + if (gt_field!="." and gt_field!="0"){ + int called_allele = std::stoi(gt_field)-1; // Zero based index for vector access + // Have we seen this one before? + unsigned int idx = 0; + while (idx subset_info; + bool have_subsets = false; + + // For every alt allele we test it is a strict subset of any called alt alleles + for (unsigned int aidx=0; aidxalt.size(); ++aidx){ + string si_field; + for (vector::iterator calt=called_alts.begin(); calt!=called_alts.end(); ++calt){ + if ((int)aidx == *calt) + continue; + if (allele_subset.is_allele_subset(variant->ref, variant->alt[aidx], variant->alt[*calt])){ + have_subsets = true; + if (not si_field.empty()) + si_field += "/"; + si_field += std::to_string(*calt+1); // In vcf, make it 1-based again + } + } + if (si_field.empty()) + push_value_to_vector(subset_info, aidx, "."); + else + push_value_to_vector(subset_info, aidx, si_field); + } + + // And if we found subsets we annotate them in the info field + if (have_subsets) + variant->info["SUBSET"] = subset_info; +} + +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::merge_annotation_into_vcf(vcf::Variant* merged_entry, vcf::Variant* hotspot) { string annotation_ref_extension; long record_ref_extension = 0; - if (hotspot->ref.length() > merged_entry->ref.length()) { - record_ref_extension = hotspot->ref.length() - merged_entry->ref.length(); - } - if (hotspot->ref.length() < merged_entry->ref.length()) { - annotation_ref_extension = merged_entry->ref.substr(hotspot->ref.length()); + if (merged_entry) { + if (hotspot->ref.length() > merged_entry->ref.length()) { + record_ref_extension = hotspot->ref.length() - merged_entry->ref.length(); + } + if (hotspot->ref.length() < merged_entry->ref.length()) { + annotation_ref_extension = merged_entry->ref.substr(hotspot->ref.length()); + } } // Form blacklist @@ -513,7 +737,7 @@ void VcfOrderedMerger::merge_annotation_into_vcf(vcf::Variant* merged_entry, vcf blacklist[*key] = *v; } - vector filtered_oid; + //vector filtered_oid; for (vector::iterator oid = hotspot->info["OID"].begin(), opos = hotspot->info["OPOS"].begin(), @@ -525,74 +749,129 @@ void VcfOrderedMerger::merge_annotation_into_vcf(vcf::Variant* merged_entry, vcf oid++, opos++, oref++, oalt++, omapalt++) { if (!blacklist.empty() && blacklist[*omapalt] != ".") continue; - filtered_oid.push_back(*oid); - - if (record_ref_extension) { - if ((int)omapalt->length() < record_ref_extension || hotspot->ref.substr(hotspot->ref.length() - record_ref_extension) != - omapalt->substr(omapalt->length() - record_ref_extension)) { - cout << UNIFY_VARIANTS << " Hotspot annotation " << merged_entry->sequenceName - << ":" << merged_entry->position << ", allele " << *omapalt << " not eligible for shortening.\n"; - continue; - } - *omapalt = omapalt->substr(0, omapalt->length() - record_ref_extension); + //if (merged_entry) filtered_oid.push_back(*oid); + //ZZ + if (not find_match(merged_entry, hotspot->ref,oid, opos, oref, oalt, &(*omapalt), record_ref_extension, annotation_ref_extension)) { + list::reverse_iterator it; + bool found = false; + for (it = variant_list.rbegin(); it != variant_list.rend(); it++ ) { + if (too_far(&(*it), hotspot)) break; + int padding = hotspot->position-it->position; + if (padding > (int) it->ref.length()) continue; // not contain + long record_ref_ext = 0; // new + string annotation_ref_ext; // new + string x = it->ref.substr(0,padding)+*omapalt; + unsigned int rlen = padding + hotspot->ref.length(); + string hotspot_ref = it->ref.substr(0,padding)+hotspot->ref; + if (rlen > it->ref.length()) { + record_ref_ext = rlen - it->ref.length(); + } + if (rlen < it->ref.length()) { + annotation_ref_ext = it->ref.substr(rlen); + } + if (find_match(&(*it), hotspot_ref, oid, opos, oref, oalt, &x, record_ref_ext, annotation_ref_ext)) {found = true; break;} + } + if (not found){ + cout << UNIFY_VARIANTS << " Hotspot annotation " << hotspot->sequenceName + << ":" << hotspot->position << ", allele " << *omapalt << " not found in merged variant file.\n"; + } } - *omapalt = *omapalt + annotation_ref_extension; + } +} - vector::iterator omapalti = - find(merged_entry->info["OMAPALT"].begin(), merged_entry->info["OMAPALT"].end(), *omapalt); +// ----------------------------------------------------------------------------------- - if (omapalti == merged_entry->info["OMAPALT"].end()) { - cout << UNIFY_VARIANTS << " Hotspot annotation " << merged_entry->sequenceName - << ":" << merged_entry->position << ", allele " << *omapalt << " not found in merged variant file.\n"; - continue; - } - if (oref->length() >= 1 && oalt->length() >= 1 && (*oref)[0] == (*oalt)[0]) { - *oref = oref->substr(1); - *oalt = oalt->substr(1); - long p = atol(opos->c_str()); - stringstream ss; - ss<<++p; - *opos = ss.str(); - } - if (oref->empty()) { - *oref = "-"; - } - if(oalt->empty()) { - *oalt = "-"; - } - long idx = omapalti - merged_entry->info["OMAPALT"].begin(); +bool VcfOrderedMerger::filter_VCF_record(vcf::Variant* record) const +{ + bool is_HS = record->infoFlags.find("HS") != record->infoFlags.end(); + + // Hotspot positions only filter applied first + if (hotspot_positions_only and not is_HS) + return true; + + // Next we filter by target meta data + if (filter_by_target){ + // vcf->position is 1-based, whereas target bed fiels are 0-based indices. + int hs_only = targets_manager.ReportHotspotsOnly(*current_target, reference_reader.chr_idx(record->sequenceName.c_str()), record->position-1); + if (hs_only > 0 and not is_HS) + return true; + } - if (merged_entry->info["OID"][idx] == ".") { - merged_entry->info["OID"][idx] = *oid; - merged_entry->info["OPOS"][idx] = *opos; - merged_entry->info["OREF"][idx] = *oref; - merged_entry->info["OALT"][idx] = *oalt; - merged_entry->info["OMAPALT"][idx] = *omapalt; - } else { - merged_entry->info["OID"].push_back(*oid); - merged_entry->info["OPOS"].push_back(*opos); - merged_entry->info["OREF"].push_back(*oref); - merged_entry->info["OALT"].push_back(*oalt); - merged_entry->info["OMAPALT"].push_back(*omapalt); + // Finally filter hotspot lines that are REF of NOCALL + if (hotspot_variants_only and is_HS){ + // 1) Remove NOCALLs + if (record->filter != "PASS") + return true; + + // 2) Remove reference calls + bool ref_call = true; + string gt_field; + for (vector::const_iterator its = record->sampleNames.begin(); its != record->sampleNames.end(); ++its) { + if (not ref_call) + break; + map > & sampleOutput = record->samples[*its]; + map >::const_iterator itg = sampleOutput.find("GT"); + if (itg == sampleOutput.end()) + return false; + + for (vector::const_iterator itv = itg->second.begin(); itv!=itg->second.end(); ++itv){ + stringstream ss(*itv); + while (getline(ss, gt_field, '/')){ + if (gt_field == "0" or gt_field == ".") + continue; + else + ref_call = false; + } + } } + return ref_call; } - if (!filtered_oid.empty()) { - merged_entry->id = join(filtered_oid, ";"); + + return false; +} + +// ----------------------------------------------------------------------------------- +// XXX Code below writes lines to the vcf files + +void VcfOrderedMerger::flush_vcf(vcf::Variant* latest) +{ + while (not variant_list.empty()) { + vcf::Variant* current = &(*variant_list.begin()); + + if (too_far(current, latest)) { + if (is_within_target_region(current)) { + if (filter_VCF_record(current)) + ++num_filtered_records; + else { // Write out record if not filtered + gvcf_process(current->sequenceName.c_str(), current->position); + gvcf_out_variant(current); + bgz_out << *current << "\n"; + ++num_records; + } + } else { + ++num_off_target; + cout << UNIFY_VARIANTS " Skipping " << current->sequenceName << ":" << current->position + << " outside of target regions.\n"; + } + variant_list.pop_front(); + } else break; } + if (latest) + variant_list.push_back(*latest); } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::process_and_write_vcf_entry(vcf::Variant* current) { generate_novel_annotations(current); - process_annotation(current); - if (is_within_target_region(current)) { - gvcf_process(current->sequenceName.c_str(), current->position); - gvcf_out_variant(current); - bgz_out << *current << "\n"; - - } else cout << UNIFY_VARIANTS " Skipping " << current->sequenceName << ":" << current->position - << " outside of target regions.\n"; + process_annotation(current); // Adds hotspot annotation to entry + annotate_subset(current); // Checks if called alleles supersets of others + flush_vcf(current); // Filters entries and writes vcf files to file + return; } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::gvcf_out_variant(vcf::Variant *current) { if (gvcf_out) { while (current_cov_info && current_cov_info->position - current->position < (int)current->ref.length()) next_cov_entry(); @@ -600,6 +879,9 @@ void VcfOrderedMerger::gvcf_out_variant(vcf::Variant *current) { } } +// ----------------------------------------------------------------------------------- +// Generates coverage lines in gvcf file + void VcfOrderedMerger::gvcf_process(int chr, long position) { if (!gvcf_out || !current_cov_info) return; @@ -637,6 +919,8 @@ void VcfOrderedMerger::gvcf_process(int chr, long position) { } } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::gvcf_finish_region() { gvcf_process(current_target->chr, current_target->end + 1); } @@ -645,6 +929,8 @@ void VcfOrderedMerger::gvcf_process(const char * seq_name, long pos) { gvcf_process(reference_reader.chr_idx(seq_name), pos); } +// ----------------------------------------------------------------------------------- + vcf::Variant VcfOrderedMerger::generate_gvcf_entry(vcf::VariantCallFile& current, int chr, depth_info depth, long pos, long end) const { stringstream ss; @@ -676,6 +962,8 @@ vcf::Variant VcfOrderedMerger::generate_gvcf_entry(vcf::VariantCallFile& current return gvcf_entry; } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::next_cov_entry() { if (current_cov_info != NULL) delete current_cov_info; if (depth_in->eof()) { @@ -691,33 +979,28 @@ void VcfOrderedMerger::next_cov_entry() { current_cov_info = CoverageInfoEntry::parse(line, reference_reader); } +// ----------------------------------------------------------------------------------- + void VcfOrderedMerger::process_annotation(vcf::Variant* current) { + int cmp; - if (hotspots_.size() > 0) { - for (vector::iterator iter = hotspots_.begin(); (iter != hotspots_.end()); ++iter) { - cmp = variant_cmp(current, &(*iter)); - if (cmp == 1) break; + if (hotspot_queue.has_value()) + do { + cmp = variant_cmp(current, hotspot_queue.current()); + if (cmp == 1) { /*hotspots_.push_back(*hotspot_queue.current());*/ return;} if (cmp == 0) { - merge_annotation_into_vcf(current, &(*iter)); - break; + merge_annotation_into_vcf(current, hotspot_queue.current()); + hotspot_queue.next(); + return; } - //blacklist_check(&(*iter)); - } - } - if (hotspots_.size() > 100) {hotspots_.erase(hotspots_.begin());} - if (hotspot_queue.has_value()) do { - cmp = variant_cmp(current, hotspot_queue.current()); - hotspots_.push_back(*hotspot_queue.current()); - if (cmp == 1) return; - if (cmp == 0) { - merge_annotation_into_vcf(current, hotspot_queue.current()); + merge_annotation_into_vcf(hotspot_queue.current()) ; + //blacklist_check(hotspot_queue.current()); // does not need, it is checked in above hotspot_queue.next(); - return; } - blacklist_check(hotspot_queue.current()); - hotspot_queue.next(); - } while (cmp == -1 && hotspot_queue.has_value()); + while (cmp == -1 && hotspot_queue.has_value()); } + +// ----------------------------------------------------------------------------------- void VcfOrderedMerger::blacklist_check(vcf::Variant* hotspot) { map >::iterator bstr = hotspot->info.find("BSTRAND"); @@ -732,52 +1015,27 @@ void VcfOrderedMerger::blacklist_check(vcf::Variant* hotspot) { << hotspot->position << " not found in merged variant file.\n"; } -void PriorityQueue::open_vcf_file(vcf::VariantCallFile& vcf, string filename, bool parse_samples) { - // Open VCF file for parsing - if (!enabled) return; - vcf.parseSamples = parse_samples; - vcf.open(filename); - if (!vcf.is_open()) { - cerr << "ERROR: Could not open file : " << filename << " : " << strerror(errno) << endl; - exit(1); - } - vcf._done = false; - next(); -} - -void PriorityQueue::next() { - if (!enabled) return; - while (size() < _size) { - ComparableVcfVariant* v = new ComparableVcfVariant(merger, file, _vc++); - if (get_next_variant(v)) { - trim_variant(v); - left_align_variant(v); - push(v); - } else { - _size = 0; - delete_variant(v); - break; - } - } - delete_current(); - if (empty()) { - _current = NULL; - return; - } - _current = top(); - - pop(); -} - +// ================================================================================ // ComparableVcfVariant wrapper implementation + bool ComparableVcfVariant::operator<(ComparableVcfVariant& b) { int r = merger.variant_cmp(this, &b); // StableSorting - if (r == 0) { r = -comp(_t, b._t); } + if (r == 0) { + //ZZ favor HS tag + int x = 1, y= 1; + if (this->infoFlags.find("HS") == this->infoFlags.end()) x = 0; + if (b.infoFlags.find("HS")== b.infoFlags.end()) y = 0; + if (x == y) r = -comp(_t, b._t); + else if (x < y) r = -1; + else r = 1; + } return r == -1; } +// ================================================================================ // Common procedures (functions) + void push_value_to_vector(vector& v, long index, const string& entry) { if (index < 0 || (unsigned)index >= v.size()) v.push_back(entry); @@ -804,7 +1062,7 @@ int compare(const T1& p11, const T2& p12, const T1& p21, const T2& p22) { return compare(make_pair(p11, p12), make_pair(p21, p22)); } -void extend_header(vcf::VariantCallFile &vcf) { +void extend_header(vcf::VariantCallFile &vcf, bool add_subset) { // extend header with new info fields vcf.addHeaderLine("##INFO="); vcf.addHeaderLine("##INFO="); @@ -812,6 +1070,9 @@ void extend_header(vcf::VariantCallFile &vcf) { vcf.addHeaderLine("##INFO="); vcf.addHeaderLine("##INFO="); + if (add_subset) + vcf.addHeaderLine("##INFO="); } void parse_parameters_from_json(string filename, vcf::VariantCallFile& vcf) { @@ -876,6 +1137,9 @@ bool check_on_write(string filename, string param_name) { return true; } +// ============================================================================== +// XXX Main function + int UnifyVcf(int argc, const char *argv[]) { unsigned int DEFAULT_WINDOW_SIZE = 10; int DEFAULT_MIN_DP = 0; @@ -904,8 +1168,19 @@ int UnifyVcf(int argc, const char *argv[]) { string input_depth_param = "input-depth"; string input_depth = opts.GetFirstString('d', input_depth_param, ""); - int min_depth = opts.GetFirstInt('m', "min-depth", 0); - int window_size = opts.GetFirstInt('w', "window-size", DEFAULT_WINDOW_SIZE); + int min_depth = opts.GetFirstInt('m', "min-depth", 0); + int window_size = opts.GetFirstInt('w', "window-size", DEFAULT_WINDOW_SIZE); + + // VCF record filter settings + bool filter_by_target = opts.GetFirstBoolean ('-', "filter-by-target", true); + bool hotspot_positions_only = opts.GetFirstBoolean ('-', "hotspot-positions-only", false); + bool hotspot_variants_only = opts.GetFirstBoolean ('-', "hotspot-variants-only", false); + + // Subset annotation options + bool subset_debug = opts.GetFirstBoolean ('-', "subset-debug", false); + bool check_for_subsets = opts.GetFirstBoolean ('-', "subset-check", true); + bool subset_simple_mnp = opts.GetFirstBoolean ('-', "subset-simple-mnp", true); + vector subset_scores = opts.GetFirstIntVector('-', "subset-scores", "1,-3,-5,-2"); // check and fill optional arguments unsigned int w = (window_size < 1) ? DEFAULT_WINDOW_SIZE : (unsigned int) window_size; @@ -950,12 +1225,389 @@ int UnifyVcf(int argc, const char *argv[]) { // Prepare merger object VcfOrderedMerger merger(novel_vcf, assembly_vcf, hotspot_vcf, output_vcf, json_path, input_depth, output_gvcf, reference_reader, targets_namager, w, minimum_depth,DEFAULT_LEFT_ALIGN_FLAG); + // Set filtering options + merger.SetVCFrecordFilters(filter_by_target, hotspot_positions_only, hotspot_variants_only); + // Set subset annotation options + merger.allele_subset.debug = subset_debug; + merger.allele_subset.check_enabled = check_for_subsets; + merger.allele_subset.simple_mnp_alignment = subset_simple_mnp; + merger.allele_subset.SetAlignerScores(subset_scores); // Perform merging procedure merger.perform(); } // build tabix indices build_index(output_vcf); - if (!output_gvcf.empty()) build_index(output_gvcf); + if (!output_gvcf.empty()) + build_index(output_gvcf); return 0; } + +// ============================================================================== +// class AlleleSubsetCheck contains functionality to identify alleles that are strict +// subsets of other alleles +// The method to search for a subset is a two stage conditional multiple sequence alignment +// It's suboptimal compared to a full 3D multiple sequence alignment, but faster. +// TODO: explore vcflib Smith-Waterman instead of Realigner + +AlleleSubsetCheck::AlleleSubsetCheck() : + aligner_(50,10),check_enabled(true), simple_mnp_alignment(true) +{ + // Default to tmap default scores for alignment + vector def_scores(4, 1); + def_scores[1] = -3; // mismatch penalty + def_scores[2] = -5; // gap open penalty + def_scores[3] = -2; // gap extend penalty + aligner_.SetScores(def_scores); + + // We do not allow any clipping and we only align in forward direction + aligner_.SetClipping(0, true); + + reset_counters(); + failure_ = false; + debug = false; +} + +// -------------------------------------------------------------------- +// A fast simplified subset check for MNPs that only allows matches and mismatches as alignment operations +// Otherwise same conditional 2-stage alignment approach as in conditional_alignment_subset_check() +bool AlleleSubsetCheck::mnp_subset_check(const string & ref, const string &subset, const string &super) +{ + int n_edits_ref = 0; + int n_edits_sup = 0; + bool rmatch, smatch; + + for (unsigned int i=0; i< subset.length(); ++i){ + rmatch = ref[i]==subset[i]; + smatch = subset[i]!=super[i]; + + if (not rmatch) { + ++n_edits_ref; + if (not smatch) + return false; + } + else if (not smatch) + ++n_edits_sup; + } + return (n_edits_ref>0 and n_edits_sup>0); +} + +// -------------------------------------------------------------------- +// Check for M cigar operations which can be matches or mismatches + +bool AlleleSubsetCheck::is_match(const string & pretty_a, int aidx) +{ + if (aidx>=0 and aidx<(int)pretty_a.length()){ + return (pretty_a[aidx]=='|' or pretty_a[aidx]==' '); + } + else { + return true; // To get boundary conditions at edges right + } +} + +bool AlleleSubsetCheck::have_matches(const string & pretty_1, int idx1, const string & pretty_2, int idx2) +{ + return (is_match(pretty_1, idx1) and is_match(pretty_2, idx2)); +} + +// -------------------------------------------------------------------- +// A fast MNP alignment that only allows matches and mismatches as operations + +string AlleleSubsetCheck::get_mnp_pretty_align(const string & allele1, const string & allele2) +{ + if (allele1.length() != allele2.length()) + return ""; + string pretty_a(allele1.length(), '|'); + for (unsigned int i=0; i ref - subset alignment + + unsigned int start_position_shift; + string pretty_ref; + if (simple_mnp_alignment and ref.length()==subset.length()){ + pretty_ref = get_mnp_pretty_align(ref, subset); + } + else{ + aligner_.SetSequences(subset, ref, pretty_ref ,true); + aligner_.computeSWalignment(cigar_data, md_data, start_position_shift); + pretty_ref = aligner_.pretty_aln(); + } + + // Second stage, get subset - superset alignment + + string pretty_sup; + if (simple_mnp_alignment and subset.length()==superset.length()){ + pretty_sup = get_mnp_pretty_align(subset, superset); + } + else{ + aligner_.SetSequences(superset, subset, pretty_sup ,true); + aligner_.computeSWalignment(cigar_data, md_data, start_position_shift); + pretty_sup = aligner_.pretty_aln(); + } + + // Reconcile alignments to get conditional superset alignment + string cond_super_aln = get_cond_superset_alignment(pretty_ref, pretty_sup); + + // Debug break point here: XXX + if (debug and (failure_ or (cond_super_aln.length() > 0))) { + if (failure_) + cout << "FAILURE for Ref: " << ref << " \"" << pretty_ref << "\" Subset: " << subset << " \"" << pretty_sup << "\" Superset: " << superset << endl; + else + cout << "SUPERSET for Ref: " << ref << " \"" << pretty_ref << "\" Subset: " << subset << " \"" << pretty_sup << "\" Superset: " << superset << endl; + cout << "Conditional alignment: Ref: " << ref << " Superset: " << superset << " Pretty: \"" << cond_super_aln << "\"" << endl << endl; + //string dinput; + //getline(cin, dinput); + //cout << dinput << endl; + failure_ = false; + } + //*/ + + + if (cond_super_aln.length() > 0) + return true; + else + return false; +} + +// -------------------------------------------------------------------- +// Function reconciles the two alignment stages and creates a conditional +// ref<->superset alignment if possible. Otherwise returns an empty string. +// +// With events match='|' mismatch=' ' M=' 'or'|' insertion='+' deletion='-', +// to have a valid subset decomposition with independent, disjoint events +// if we only allow the following operations: +// (extension of a gap is NOT a superset of the shorter gap) +// +// Ref:t <-> q:Sub:t <-> q:Super +// | M +// ' ' | +// M|||M M---M +// M---M flanking M +// flanking M M+++M +// M+++M M|||M + + +string AlleleSubsetCheck::get_cond_superset_alignment(const string & pretty_ref, const string &pretty_super) +{ + string super_aln; + int ridx = 0; + int sidx = 0; + + // Iterate over the different alignment operations; one event per loop execution + while (ridx<(int)pretty_ref.length() or sidx<(int)pretty_super.length()) { + bool new_event = true; + + // Events that increment both ridx and sidx + if (ridx<(int)pretty_ref.length() and sidx<(int)pretty_super.length()){ + + // 1) Invalid subset operation + if (pretty_ref[ridx]==' ' and pretty_super[sidx]==' '){ + return ""; + } + + // 2) match event + if (pretty_ref[ridx]=='|' and pretty_super[sidx] =='|'){ + super_aln += '|'; + ++ridx; ++sidx; + continue; + } + + // 3) mismatches event + if (have_matches(pretty_ref, ridx, pretty_super, sidx)){ + super_aln += ' '; + ++ridx; ++sidx; + continue; + } + + // 4) Deletion in t:Sub <-> q:Super + while (ridx<(int)pretty_ref.length() and sidx<(int)pretty_super.length() and pretty_super[sidx] =='-'){ + if (pretty_ref[ridx] != '|') + return ""; + // Enforce left M operation + if (new_event){ + new_event = false; + if (not have_matches(pretty_ref, ridx-1, pretty_super, sidx-1)) + return ""; + } + super_aln += '-'; + ++ridx; ++sidx; + } + // Enforce trailing M operation if event was triggered + if (not new_event){ + if (not have_matches(pretty_ref, ridx, pretty_super, sidx)) + return ""; + continue; + } + + // 5) Insertion in t:Ref <-> q:Sub + while (ridx<(int)pretty_ref.length() and sidx<(int)pretty_super.length() and pretty_ref[ridx] =='+'){ + if (pretty_super[sidx] != '|') + return ""; + // Enforce left M operation + if (new_event){ + new_event = false; + if (not have_matches(pretty_ref, ridx-1, pretty_super, sidx-1)) + return ""; + } + super_aln += '+'; + ++ridx; ++sidx; + } + // Enforce trailing M operation if event was triggered + if (not new_event){ + if (not have_matches(pretty_ref, ridx, pretty_super, sidx)) + return ""; + continue; + } + + } // End double incrementing events + + // 6) Deletion in t:Ref <-> q:Sub + if (ridx<(int)pretty_ref.length() and pretty_ref[ridx] =='-'){ + // Need flanking M cigar operations + if (not (have_matches(pretty_ref, ridx-1, pretty_super, sidx-1) and is_match(pretty_super, sidx))) + return ""; + while (ridx<(int)pretty_ref.length() and pretty_ref[ridx] =='-'){ + super_aln += '-'; + ++ridx; + } + if (not is_match(pretty_ref, ridx)) + return ""; + continue; + } + + // 7) Insertion in t:Sub <-> q:Super + if (sidx<(int)pretty_super.length() and pretty_super[sidx] =='+'){ + // Need flanking M cigar operations + if (not (have_matches(pretty_ref, ridx-1, pretty_super, sidx-1)and is_match(pretty_ref, ridx))) + return ""; + while (sidx<(int)pretty_super.length() and pretty_super[sidx] =='+'){ + super_aln += '+'; + ++sidx; + } + if (not is_match(pretty_super, sidx)) + return ""; + continue; + } + + // 8) If we are at this point it means we went through the whole loop + // without triggering an event - Fail and report + //cerr << "WARNING AlleleSubsetCheck::get_cond_superset_alignment failed to trigger event for " + // << "\"" << pretty_ref << "\":" << ridx << " \"" << pretty_super << "\":" << sidx + // << " super_aln=" << super_aln << endl; + ++counter_failures; + failure_ = true; + return ""; + } + + return super_aln; +} + +// -------------------------------------------------------------------- + +void AlleleSubsetCheck::reset_counters() +{ + counter_num_align_checks = 0; + counter_num_align_subsets = 0; + counter_num_mnp_checks = 0; + counter_num_mnp_subsets = 0; + counter_failures = 0; +} + +// -------------------------------------------------------------------- +void AlleleSubsetCheck::print_stats() +{ + if (not check_enabled){ + cout << "Allele Subset Annotation Summary: Disabled." << endl; + return; + } + + ostringstream table; + table << endl << "Allele Subset Annotation Summary:" << endl; + table << setw(23) << " "; + table << setw(23) << "--------------------" << setw(23) << "--------------------" << endl; + table << setw(23) << " "; + table << setw(23) << "Pairs Investigated" << setw(23) << "Subsets found" << endl; + table << setw(23) << " "; + table << setw(23) << "--------------------" << setw(23) << "--------------------" << endl; + table << setw(23) << "MNP alignment"; + table << setw(23) << counter_num_mnp_checks << setw(23) << counter_num_mnp_subsets << endl; + table << setw(23) << "Smith-Waterman"; + table << setw(23) << counter_num_align_checks << setw(23) << counter_num_align_subsets << endl; + + table << setw(23) << " "; + table << setw(23) << "--------------------" << setw(23) << "--------------------" << endl; + table << setw(23) << "Total"; + table << setw(23) << (counter_num_mnp_checks+counter_num_align_checks) + << setw(23) << (counter_num_mnp_subsets+counter_num_align_subsets) << endl; + if (counter_failures>0) + table << "Number of pair failures: " << counter_failures << endl; + cout << table.str() << endl; +} + diff --git a/Analysis/VariantCaller/tvcutils/unify_vcf.h b/Analysis/VariantCaller/tvcutils/unify_vcf.h index 9a01c642..f165a335 100644 --- a/Analysis/VariantCaller/tvcutils/unify_vcf.h +++ b/Analysis/VariantCaller/tvcutils/unify_vcf.h @@ -5,6 +5,8 @@ #ifndef ION_ANALYSIS_UNIFY_VCF_H #define ION_ANALYSIS_UNIFY_VCF_H +#include "Realigner.h" + class bgzf_stream; class VcfOrderedMerger; class PriorityQueue; @@ -12,6 +14,57 @@ class ComparableVcfVariant; void build_index(const string &path_to_gz); +// --------------------------------------------------------------------------------------- + +class AlleleSubsetCheck +{ +private: + Realigner aligner_; + + // dummy variables for aligner + vector cigar_data; + vector md_data; + + // accounting variables + unsigned int counter_num_align_checks; + unsigned int counter_num_align_subsets; + unsigned int counter_num_mnp_checks; + unsigned int counter_num_mnp_subsets; + unsigned int counter_failures; + bool failure_; + + bool mnp_subset_check(const string & ref, const string &subset, const string &super); + bool conditional_alignment_subset_check(const string & ref, const string &subset, const string &super); + bool is_match(const string & pretty_a, int aidx); + bool have_matches(const string & pretty_a, int aidx, const string & pretty_b, int bidx); + bool anchor_sanity_check(const string & ref, const string &subset, const string &super); + string get_mnp_pretty_align(const string & allele1, const string & allele2); + string get_cond_superset_alignment(const string & pretty_ref, const string &pretty_super); + +public: + AlleleSubsetCheck(); + + // The main function to check if a subset allele hypothesis is in fact a subset of a superset allele + bool is_allele_subset(const string & ref, const string &subset, const string &super); + + // Adjusts Smith-Waterman penalties - match + void SetAlignerScores(const vector & scores) + { + aligner_.SetScores(scores); + } + + void reset_counters(); + void print_stats(); + + bool check_enabled; + bool simple_mnp_alignment; + bool debug; // If enabled shows information about failures as well as conditional subset alignments + +}; + + +// --------------------------------------------------------------------------------------- + struct CoverageInfoEntry { string sequenceName; long position; @@ -35,6 +88,8 @@ struct CoverageEntryComparator { bool operator()(const CoverageInfoEntry& lhs, const CoverageInfoEntry& rhs) { return lhs.cov < rhs.cov; } }; +// --------------------------------------------------------------------------------------- + class bgzf_stream { BGZF *bgzf; string data; @@ -55,6 +110,8 @@ class bgzf_stream { int write(int length); }; +// --------------------------------------------------------------------------------------- + class ComparableVcfVariant : public vcf::Variant { const VcfOrderedMerger& merger; unsigned long _t; @@ -65,12 +122,16 @@ class ComparableVcfVariant : public vcf::Variant { bool operator<(ComparableVcfVariant& b); }; +// --------------------------------------------------------------------------------------- + class VariantComparator { public: VariantComparator() {} bool operator()(ComparableVcfVariant* lhs, ComparableVcfVariant* rhs) { return (*lhs)<(*rhs); } }; +// --------------------------------------------------------------------------------------- + typedef priority_queue, VariantComparator> variant_queue; class PriorityQueue : private variant_queue { @@ -91,7 +152,10 @@ class PriorityQueue : private variant_queue { bool parse_samples = false) : variant_queue(VariantComparator()), _size(w), _vc(0), _current(NULL), left_align_enabled(la), enabled(!filename.empty()), - merger(merger), reference_reader(reader) { open_vcf_file(file, filename, parse_samples); } + merger(merger), reference_reader(reader) + { + open_vcf_file(file, filename, parse_samples); + } ~PriorityQueue() { while (_current) next(); } @@ -117,6 +181,8 @@ class PriorityQueue : private variant_queue { void delete_current() { delete_variant(_current); } }; +// --------------------------------------------------------------------------------------- + class VcfOrderedMerger { public: VcfOrderedMerger(string& novel_tvc, @@ -138,7 +204,11 @@ class VcfOrderedMerger { template bool is_within_target_region(T *variant); + void SetVCFrecordFilters(bool filt_by_target, bool hotspot_pos_only, bool hotspot_var_only); void perform(); + + AlleleSubsetCheck allele_subset; + private: istream* depth_in; @@ -148,20 +218,40 @@ class VcfOrderedMerger { bool left_align_enabled; size_t window_size, minimum_depth; PriorityQueue novel_queue, assembly_queue, hotspot_queue; - vector hotspots_; + list hotspots_; ofstream bgz_out; vector::iterator current_target; CoverageInfoEntry* current_cov_info; + // VCF record filters (applied during vcf merging) + bool filter_by_target; // Filter records based on mets information in target bed info field + bool hotspot_positions_only; // Output only vcf lines with the infoFlag 'HS' + bool hotspot_variants_only; // Suppress hotspot reference calls and no-calls from the final output vcf + + // Accounting variables + unsigned int num_records; + unsigned int num_filtered_records; + unsigned int num_off_target; + + // Function that determines if a vcf record should be filtered + bool filter_VCF_record(vcf::Variant* record) const; + void write_header(vcf::VariantCallFile& vcf, string json_path); vcf::Variant* merge_overlapping_variants(); + list variant_list; void span_ref_and_alts(); + bool too_far(vcf::Variant*, vcf::Variant*); void generate_novel_annotations(vcf::Variant* variant); void merge_annotation_into_vcf(vcf::Variant* merged_entry, vcf::Variant* hotspot); + void merge_annotation_into_vcf(vcf::Variant* hotspot) { merge_annotation_into_vcf(NULL, hotspot);}; + void flush_vcf(vcf::Variant* latest); + void annotate_subset(vcf::Variant* variant); + bool find_match(vcf::Variant* merged_entry, string &hotspot_ref,vector::iterator oid, vector::iterator opos, vector::iterator oref, vector::iterator oalt, string *omapalt, int record_ref_extension, string &annotation_ref_extension); + void process_and_write_vcf_entry(vcf::Variant* current); @@ -189,6 +279,8 @@ class VcfOrderedMerger { void gvcf_finish_region(); }; +// --------------------------------------------------------------------------------------- + void push_value_to_entry(vcf::Variant& gvcf_entry, size_t value, string key); void push_value_to_entry(vcf::Variant& gvcf_entry, string value, string key); void push_info_field(vcf::Variant& gvcf_entry, string value, string key); @@ -204,7 +296,7 @@ template int compare(const T1& p11, const T2& p12, const T1& p21, const T2& p22); void push_value_to_vector(vector& v, long index, const string& entry); -void extend_header(vcf::VariantCallFile &vcf); +void extend_header(vcf::VariantCallFile &vcf, bool add_subset); void parse_parameters_from_json(string filename, vcf::VariantCallFile& vcf); bool validate_filename_parameter(string filename, string param_name); diff --git a/Analysis/VariantCaller/tvcutils/validate_bed.cpp b/Analysis/VariantCaller/tvcutils/validate_bed.cpp index eb66f891..2d1d1a1c 100644 --- a/Analysis/VariantCaller/tvcutils/validate_bed.cpp +++ b/Analysis/VariantCaller/tvcutils/validate_bed.cpp @@ -48,7 +48,7 @@ void ValidateBedHelp() printf (" --validation-log FILE log file for user-readable warning/error messages [stdout]\n"); printf (" --meta-json FILE save validation and file statistics to json file [none]\n"); printf (" --unmerged-detail-bed FILE output a valid unmerged BED. To be used as input to --primer-trim-bed argument of variant_caller_pipeline.py (recommended) [none]\n"); - printf (" --unmerged-plain-bed FILE output a valid merged BED. To be used as input to --region-bed argument of variant_caller_pipeline.py (recommended) [none]\n"); + printf (" --unmerged-plain-bed FILE output a valid unmerged BED. To be used as input to --region-bed argument of variant_caller_pipeline.py (recommended) [none]\n"); printf (" --merged-detail-bed FILE output an (almost) valid bedDetail merged BED [none]\n"); printf (" --merged-plain-bed FILE output a valid plain merged BED [none]\n"); printf (" --effective-bed FILE output a valid effective BED [none]\n"); @@ -416,6 +416,10 @@ bool parse_track_line(char *track_line, int line_number, BedFile& bed) bed.log_line(kLineFixed, kUnsuppressable, line_number, 0, "track line has ionVersion higher than current ionVersion=4.0"); } + if (bed.ion_version == 0) { + bed.log_line(kLineFixed, kUnsuppressable, line_number, 0, "track line has no ionVersion number"); + } + return true; } @@ -424,23 +428,40 @@ void parse_bed_detail_targets(char *id, char *description, int line_number, int { // assert type=bedDetail + bool invalid_format = false; + string error_details; + if (bed.ion_version < 4.0) { + bed_line->gene_id = description; if (bed_line->gene_id == ".") bed_line->gene_id.clear(); + + size_t found = bed_line->gene_id.find("="); + if (found != string::npos) { + invalid_format = true; + if (not error_details.empty()) + error_details += "; "; + error_details += "For ionVersion < 4.0 GENE_ID column cannot contain an = sign"; + } + found = bed_line->gene_id.find(";"); + if (found != string::npos) { + invalid_format = true; + if (not error_details.empty()) + error_details += "; "; + error_details += "For ionVersion < 4.0 GENE_ID column cannot contain a ; character"; + } + + bed_line->submitted_region = id; if (bed_line->submitted_region == ".") bed_line->submitted_region.clear(); } else { - bool invalid_format = false; - if (description[0] == '.' and description[1] == 0) return; - string error_details; - while (*description) { if (*description == ';') { description++; @@ -451,7 +472,7 @@ void parse_bed_detail_targets(char *id, char *description, int line_number, int string key; bool invalid_char = false; for(; *description and *description != '=' and *description != ';'; ++description) { - if (isalnum(*description) or *description == '_' or *description == '.' or *description == '-') + if (isalnum(*description) or *description == '_' or *description == '.' or *description == '-' or *description == '*') key.push_back(*description); else { invalid_format = true; @@ -534,12 +555,10 @@ void parse_bed_detail_targets(char *id, char *description, int line_number, int bed_line->ion_value.push_back(value); } } - - - if (invalid_format) { - bed.log_column(kLineIgnored, kUnsuppressable, line_number, column, bed_line, "Problem parsing description column: ", error_details.c_str()); - bed_line->filtered = true; - } + } + if (invalid_format) { + bed.log_column(kLineIgnored, kUnsuppressable, line_number, column, bed_line, "Problem parsing description column: ", error_details.c_str()); + bed_line->filtered = true; } } @@ -569,7 +588,7 @@ void parse_bed_detail_hotspots(const char *id, char *description, int line_numbe // Parse names string key; for(; *id and *id != '=' and *id != ';'; ++id) { - if (isalnum(*id) or *id == '_' or *id == '.' or *id == '-') + if (isalnum(*id) or *id == '_' or *id == '.' or *id == '-' or *id == '*') key.push_back(*id); else { invalid_format = true; @@ -691,7 +710,7 @@ string validate_name(char *name, int line_number, int column, BedLine *bed_line, bool warning = false; for (; *name; ++name) { - if (isalnum(*name) or *name == '_' or *name == '.' or *name == '-' or *name == ':') + if (isalnum(*name) or *name == '_' or *name == '.' or *name == '-' or *name == ':' or *name == '*') corrected_name.push_back(*name); else warning = true; @@ -1012,8 +1031,10 @@ void merge_overlapping_regions(ReferenceReader& reference_reader, BedFile& bed) merged_line->name += A->name; merged_line->gene_id += "&"; merged_line->gene_id += A->gene_id; - merged_line->submitted_region += "&"; - merged_line->submitted_region += A->submitted_region; + if (not A->submitted_region.empty()) { + merged_line->submitted_region += "&"; + merged_line->submitted_region += A->submitted_region; + } merged_line->ref += "&"; merged_line->ref += A->ref; merged_line->obs += "&"; diff --git a/Analysis/VariantCaller/vcfcomp/vcfcomp.cpp b/Analysis/VariantCaller/vcfcomp/vcfcomp.cpp new file mode 100644 index 00000000..8c8a1da7 --- /dev/null +++ b/Analysis/VariantCaller/vcfcomp/vcfcomp.cpp @@ -0,0 +1,1638 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sam.h" +//#include "kstring.h" +#include "IonVersion.h" + + +using namespace std; + +int min_mapping_qv = 0; + +map, pair > map_target_size; +map > > map_target_ranges; +void init_target_size() { + map_target_ranges.clear(); +} + +//----------------------------------------------------- +string itos(long int i) { // convert int to string + stringstream s; + s << i; + return s.str(); +} +//----------------------------------------------------- +static int depth; + +typedef struct { + int beg, end; + samfile_t *in; +} tmpstruct_t; + +// callback for bam_fetch() +static int fetch_func(const bam1_t *b, void *data) { + if (b->core.qual < min_mapping_qv) { + return 1; + } + bam_plbuf_t *buf = (bam_plbuf_t*)data; + bam_plbuf_push(b, buf); + return 0; +} +// callback for bam_plbuf_init() +static int pileup_func(uint32_t tid, uint32_t pos, int n, const bam_pileup1_t *pl, void *data) { + tmpstruct_t *tmp = (tmpstruct_t*)data; + if ((int)pos >= tmp->beg && (int)pos < tmp->end) { + //printf("%s\t%d\t%d\n", tmp->in->header->target_name[tid], pos + 1, n); + depth = n; + } + return 0; +} + +//-------------------------------------------------------- + +string vartypes[5] {"SNP","DEL","INS","MNP","OTHER"}; +inline int vartype(const string ref, const string alt) { + if(ref.length() == alt.length()) { + if(ref.length() == 1) return 0; + else return 3; + } + if(ref.length() < alt.length()) return 2; + else return 1; +} +//-------------------------------------------------------- +struct VariantFeatures { +private: + std::map * info_key_value; +public: + string vc_record; + char gt_index; + + inline int get_int(string info_key) { + if(info_key_value!=NULL) { + std::map< string , string >::iterator key_val = info_key_value->find(info_key); + if( key_val != info_key_value->end() && key_val->second.length() >0 && isdigit((key_val->second)[0])) return atoi(&((key_val->second)[0])); //dangerous but fast, potential bug + } + return -1; + } + + inline float get_float(string info_key) { + if(info_key_value!=NULL) { + std::map< string , string >::iterator key_val = info_key_value->find(info_key); + if( key_val != info_key_value->end() && key_val->second.length() >0 && (isdigit((key_val->second)[0]) || (key_val->second)[0] == '-' || (key_val->second)[0] == '+')) return atof(&((key_val->second)[0])); //dangerous but fast, potential bug + } + return -1; + } + + + VariantFeatures(string _vc_record, char _index) { + vc_record = _vc_record; + gt_index = _index; + info_key_value=NULL; + }; + ~VariantFeatures() { + if(info_key_value != NULL) delete info_key_value; + } + + void parse_info_values() { + + int col_idx = 0; + std::stringstream cols(vc_record); + string vcf_col; + while(cols.good() && getline( cols, vcf_col, '\t') && ++col_idx<6); + if(col_idx == 6) { + + if(info_key_value != NULL) delete info_key_value; + info_key_value = new std::map(); + info_key_value->insert(std::pair("QUAL", vcf_col)); + + while(cols.good() && getline( cols, vcf_col, '\t') && ++col_idx<8); + if(col_idx == 8 && !vcf_col.empty()) { + std::stringstream features(vcf_col); + string flag; + while(features.good() && getline( features, flag, ';')) { + string::size_type pos = flag.find("=",0); + if(pos!=string::npos && pos < flag.length()-1) { + int val_idx = 0; + string flag_name = flag.substr(0,pos); + std::stringstream values(flag.substr(pos+1)); + string value; + while(values.good() && getline( values, value, ',') && ++val_idx < (int)gt_index); + if(val_idx == (int)gt_index || val_idx == 1) info_key_value->insert(std::pair(flag_name, value)); // if value already exists, that means that it is repeated twice in the vcf record, we keep first copy + } + } + } + } + }; + +}; +//-------------------------------------------------------- +struct VCFinfo { +private: + vector ref; + vector alt; + vector info; + int zyg; // -1 = not available, 0 = HOM, 1 = HET/REF, 2 = HET/NON-REF + int DP; // 0 = zero or not available + long int target_end_pos; + +public: + + inline void set_dp(int dp) { + DP= dp; + } + inline void set_zyg(int _zyg) { + zyg = _zyg; + } + inline int get_zyg() { + return zyg; + } + inline void set_target_end_pos(long int pos) { + target_end_pos = pos; + } + inline long int get_target_end_pos() { + return target_end_pos; + } + + void set_dp(string _info) { + DP=0; + if(!_info.empty()) { + string::size_type pos = _info.find("DP="); + if(pos!=string::npos && _info.length()>pos+3 && isdigit(_info[pos+3])) DP = atoi(&(_info[pos+3])); // fast, but dangerous, assumes string is represented in continuous array, potential bug + } + } + + int get_var_type(int idx) { //SNP=0,DEL=1,INS=2,MNP=3,OTHER=4 + if(idx>=(int)ref.size()) { + cerr << "get_var_type(idx): requested idx is larger than allele counts" << endl; + exit(1); + } + return vartype(ref.at(idx), alt.at(idx)); + } + + float get_int_flag_value(int idx,string FLAG) { + if(idx>=(int)info.size()) { + cerr << "get_int_flag_value(idx): requested idx is larger than allele counts" << endl; + exit(1); + } + return info.at(idx).get_float(FLAG); + } + + int alt_count() { + return alt.size(); + } + const string get_alt(int idx) { + return alt.at(idx); + } + const string get_ref(int idx) { + return ref.at(idx); + } + const string get_info(int idx) { + return info.at(idx).vc_record; + } + char get_gt_index(int idx) { + return info.at(idx).gt_index; + } + + + void parse_info_values() { + for(unsigned int i=0; i0) s << alt.at(0); + for(unsigned int i=1; i0) s << ref.at(0); + for(unsigned int i=1; i added_alt; + + std::stringstream cols(line); + while(cols.good()) { + string vcf_col; + getline( cols, vcf_col, '\t'); + + switch(++col_idx) { + case 4: { + ref_al = vcf_col; + break; + } + case 5: { + std::stringstream ss(vcf_col); + while(ss.good()) { + string tmp_alt; + getline( ss, tmp_alt, ','); + alt.push_back(tmp_alt); + } + break; + } + case 8: { + //info_al = vcf_col; + break; + } + case 9: { + std::stringstream ss(vcf_col); + int gt_col = -1; + string tmp_format; + while(ss.good()) { + gt_col++; + getline( ss, tmp_format, ':'); + if(!ignore_genotype && strcmp(tmp_format.c_str(), "GT")==0) { + GT_idx = gt_col; + break; + } + } + break; + } + case 10: { + if(GT_idx==-1) break; + int tmp_idx = GT_idx; // for now GT_idx represents column index + GT_idx = -1; + std::stringstream ss(vcf_col); + int gt_col = -1; + string tmp_value; + while(ss.good()) { + gt_col++; + getline( ss, tmp_value, ':'); + if(gt_col == tmp_idx) { + if( tmp_value.empty()) break; + std::stringstream sf(tmp_value); + + char separator = '|'; + if(tmp_value.find("/",0) != string::npos) separator = '/'; + else if(tmp_value.find("|",0) == string::npos) break; + + GT_idx = 0; // now GT_idx represents allele number + + string gt_value; + vector alt_subset; + while(sf.good()) { + getline(sf, gt_value, separator); + if(gt_value.length()>0 && isdigit(gt_value[0])) { + if( gt_value[0]!='0') { + GT_idx = atoi(gt_value.c_str()); + if(GT_idx-1<(int)alt.size()) { + bool already_added = false; + for(unsigned int i=0; i 0) { + if(GT_idx == -1) { + rows_missing_genotype++; + set_zyg(-1); + } else set_zyg( (!obs_ref_allele) ? ( alleles_loaded == 1 ? 0 : 2) : 1 ); + set_dp(info_al); + + for(int i=0; i contig; + +public: + int load_file(string fasta_file) { + ifstream infile; + infile.open(fasta_file.c_str()); + string line; + + if (!infile) { + cerr << "Unable to read " << fasta_file << endl; + exit(1); + } + + int contigs_loaded = 0; + string contig_name = ""; + string sequence = ""; + while (getline(infile, line)) { + if(line[0]=='>') { + if(contig_name!="") { + int pos = contig_name.find("\t"); + if (pos != -1) {contig_name = contig_name.substr(0, pos);} + std::pair::iterator,bool> ret = contig.insert ( std::pair(contig_name, sequence)); + if (ret.second==false) { + cerr << "Contig is listed twice in fasta. Abort! " << contig_name << endl; + exit(1); + } + contigs_loaded++; + cerr << contigs_loaded << " loaded " << contig_name << " length: " << sequence.length() << endl; + } + contig_name = line.substr(1); + sequence=""; + } else if(line[0]!='#') { + sequence += line; + } + } + + if(contig_name!="") { + int pos = contig_name.find("\t"); + if (pos != -1) {contig_name = contig_name.substr(0, pos);} + std::pair::iterator,bool> ret = contig.insert ( std::pair(contig_name, sequence)); + if (ret.second==false) { + cerr << "Contig is listed twice in fasta. Abort! " << contig_name << endl; + exit(1); + } + contigs_loaded++; + cerr << contigs_loaded << " loaded " << contig_name << " length: " << sequence.length() << endl; + } + return contigs_loaded; + } + + + int size() { + return contig.size(); + } + + bool left_align_indel(string chr, long & vcf_pos, string & ref, string & alt) { + int prefsize = vcf_pos<70?vcf_pos:70; //caution: assuming that vcf_pos > 0, based on vcf-format + string prefseq = ""; + try {prefseq = contig.find(chr)->second.substr(vcf_pos - prefsize, prefsize - 1);} catch(...) {prefseq = "";} + string refseq = prefseq + ref; + string altseq = prefseq + alt; + + int reflen = refseq.length(); + int altlen = altseq.length(); + int right_match = 0; + + while( right_match < reflen && right_match < altlen && refseq[reflen - 1 - right_match]==altseq[altlen - 1 - right_match]) { + right_match++; + } + if(right_match > 0) { + long l = reflen-right_match-ref.length(); + if (l < 0) {ref = refseq.substr(0, ref.length());} + else {ref = refseq.substr(reflen-right_match-ref.length(), ref.length());} + l = altlen-right_match-alt.length(); + if (l < 0) {alt = altseq.substr(0, alt.length());} + else {alt = altseq.substr(altlen-right_match-alt.length(), alt.length());} + vcf_pos -= right_match; + return true; + } + return false; + } + +}; + +//----------------------------------------------------- +class VCFList { + std::map > vcfrec; + +public: + void remove(VCFList* p, bool zygosity_match, bool allele_match) { + if (p == NULL) {return;} + for (std::map >::iterator iter = p->vcfrec.begin(); (iter != p->vcfrec.end()); ++iter) { + for (std::map::iterator iter_pos = iter->second.begin(); (iter_pos != iter->second.end()); ++iter_pos) { + std::map >::iterator iter2 = vcfrec.find(iter->first); + if (iter2 != vcfrec.end()) { + std::map::iterator iter2_pos = iter2->second.find(iter_pos->first); + if (iter2_pos != iter2->second.end()) { + if (zygosity_match) { + if ((iter_pos->second.ref_to_str().find(iter2_pos->second.ref_to_str()) != string::npos) and (iter_pos->second.alt_to_str().find(iter2_pos->second.alt_to_str()) != string::npos)and (iter2_pos->second.get_zyg() == iter_pos->second.get_zyg())) {iter2->second.erase(iter2_pos);} + } + else if (allele_match) { + cerr << iter2_pos->second.ref_to_str() << endl; + cerr << iter_pos->second.ref_to_str() << endl; + cerr << iter2_pos->second.alt_to_str() << endl; + cerr << iter_pos->second.alt_to_str() << endl; + if ((iter_pos->second.ref_to_str().find(iter2_pos->second.ref_to_str()) != string::npos) and (iter_pos->second.alt_to_str().find(iter2_pos->second.alt_to_str()) != string::npos)) {iter2->second.erase(iter2_pos);} + } + else { + iter2->second.erase(iter2_pos); + } + } + } + } + } + } + + long calculate_target_size(int DP) { + long target_size = 0; + for (map >::iterator chrit = vcfrec.begin(); (chrit != vcfrec.end()); ++chrit) { + for (map::iterator posit = chrit->second.begin(); (posit != chrit->second.end()); ++posit) { + if (posit->second.get_dp() >= DP) { + target_size += (posit->second.get_zyg() - posit->first); + } + } + } + return target_size; + } + +private: + void add(string chr, long genpos, VCFinfo & rec) { + std::map< string , std::map >::iterator it = vcfrec.find(chr); + if(it == vcfrec.end()) { + std::map emptymap; + it = vcfrec.insert(std::pair< string , std::map > (chr, emptymap)).first; + } + + std::pair::iterator,bool> ret = (it->second).insert ( std::pair(genpos, rec)); + if (ret.second==false) ret.first->second.add(rec); + } + + bool add(const string chr, const long genpos, const string ref, const string alt, const string info, const char gt_index, const int zyg) { + std::map< string , std::map >::iterator it = vcfrec.find(chr); + if(it == vcfrec.end()) { + std::map emptymap; + it = vcfrec.insert(std::pair< string , std::map > (chr, emptymap)).first; + } + + std::map::iterator ret = (it->second).find(genpos); + if(ret == (it->second).end()) { + (it->second).insert ( std::pair(genpos, VCFinfo(ref,alt,info, gt_index, zyg))); + } else { + if(ret->second.get_zyg() > 9) { // this is used only for BED files to store second coordinate + if(ret->second.get_zyg() < zyg) ret->second.set_zyg(zyg); + } else { + for(int i=0; isecond.alt_count(); i++) { + if(strcmp(ref.c_str(),ret->second.get_ref(i).c_str())==0 && strcmp(alt.c_str(),ret->second.get_alt(i).c_str())==0) return false; + } + ret->second.add(ref, alt, info, gt_index, zyg); + } + } + return true; + } + + + void add(string vcfline, long &alleles_loaded, long &rows_missing_genotype, long &het_rows, long & hom_rows, FASTA * reference = NULL, bool split_mnp = true, bool ignore_genotype = false) { + alleles_loaded = 0; + string::size_type pos = vcfline.find("\t",0); + + if( pos != string::npos && pos > 0 && pos < vcfline.length() - 1) { + string chr = vcfline.substr(0,pos); + long genpos = atoi(vcfline.substr(pos+1).c_str()); + + // extracting genotyped/or all alleles at that position and the info + // this eliminates non-called alleles that are mixed with genotyped alleles + + VCFinfo rec(vcfline, alleles_loaded, rows_missing_genotype, ignore_genotype); + + if(rec.get_zyg()==1) het_rows++; + if(rec.get_zyg()==0) hom_rows++; + for(int i=0; i T A + while(shift < (long)ref.length()-1 && shift< (long)alt.length()-1 && ref[(long)ref.length()-1-shift]==alt[(long)alt.length()-1-shift]) shift++; + if(shift>0) { + ref = ref.substr(0, ref.length()-shift); + alt = alt.substr(0,alt.length()-shift); + } + //TA TG -> A G + shift = 0; + while(shift < (long)ref.length()-1 && shift< (long)alt.length()-1 && ref[shift]==alt[shift]) shift++; + if(shift>0) { + ref = ref.substr(shift); + alt = alt.substr(shift); + } + long adjusted_pos = genpos+shift; + + int vt = vartype(ref,alt); + + //left-align indels + if(reference!=NULL && (vt == 1 || vt == 2)) reference->left_align_indel(chr, adjusted_pos, ref, alt); + + //split-MNPs into single SNPs, this is wrong but community still does it + if(split_mnp && vt == 3) { // XXX + for(unsigned int j=0; j > * getList() { + return &vcfrec; + } +//----------------------------------------------------- + long int load_file(string filename, FASTA * reference = NULL, bool split_mnp = true, bool ignore_genotype = false) { + ifstream infile; + infile.open(filename.c_str()); + string line; + + if (!infile) { + cerr << "Unable to read " << filename << endl; + exit(1); + } + + long rows_processed = 0; + long rows_loaded = 0; + long alleles_loaded = 0, al; + long rows_missing_genotype = 0; + long het_rows = 0, hom_rows = 0; + + while (getline(infile, line)) { + if( line[0]!='#') { + add(line, al, rows_missing_genotype, het_rows, hom_rows, reference, split_mnp, ignore_genotype); + if(al>0) { + rows_loaded++; + alleles_loaded+=al; + } + rows_processed++; + } + } + + infile.close(); + + cerr << "# informative vcf records:" << rows_processed << "\n# records containing alternative allele:" << rows_loaded << "\n# loaded alternative alleles:" << alleles_loaded << endl; + cerr << "# loaded vcf records with HET genotype:" << het_rows << "\n# loaded vcf records with HOM genotype:" << hom_rows << "\n# loaded vcf records with missing genotype:" << rows_missing_genotype << endl; + return rows_loaded; + } +//----------------------------------------------------- + long int load_bed_file(string filename) { + ifstream infile; + infile.open(filename.c_str()); + string line; + + if (!infile) { + cerr << "Unable to read " << filename << endl; + exit(1); + } + + long rows_processed = 0; + long total_target_size = 0; + init_target_size(); + + while (getline(infile, line)) + if( line[0]!='#') { + string::size_type pos = line.find("\t",0); + + if( pos != string::npos && pos > 0 && pos < line.length() - 1) { + string chr = line.substr(0,pos); + long start_pos = atoi(line.substr(pos+1).c_str()); + if( (pos = line.find("\t",pos+1)) != string::npos) { + long end_pos = atoi(line.substr(pos+1).c_str()); + if(add(chr, start_pos, "", "", "", 0, end_pos)) { + total_target_size += end_pos - start_pos; + map_target_ranges[chr].push_back(make_pair(start_pos, end_pos)); + } + } + rows_processed++; + } + } + + infile.close(); + + cerr << "# informative bed records:" << rows_processed << "\n# total target size:" << total_target_size << endl; + return rows_processed; + } + +// Supports overlapping target segments (on fly merging) +//----------------------------------------------------- + long get_target_vcf(VCFList * vcf_in, VCFList * vcf_out) { + + long records_on_target = 0; + for (std::map< string , std::map >::iterator bed_chrit = vcfrec.begin(); bed_chrit != vcfrec.end(); ++bed_chrit) { + + std::map< string , std::map >::iterator vcf_chrit = vcf_in->getList()->find(bed_chrit->first); + if(vcf_chrit == vcf_in->getList()->end()) continue; + + std::map::iterator vcf_posit = vcf_chrit->second.begin(); + + std::map::iterator bed_posit = bed_chrit->second.begin(); + + if(bed_posit == bed_chrit->second.end()) continue; + + long int reg_start = bed_posit->first; + long int reg_end = bed_posit->second.get_zyg(); + + bed_posit++; + + while (bed_posit != bed_chrit->second.end()) { + if( vcf_posit == vcf_chrit->second.end()) break; + + if(bed_posit->first <= reg_end) { + if(bed_posit->second.get_zyg() > reg_end ) reg_end = bed_posit->second.get_zyg(); + bed_posit++; + continue; + } + + if( reg_end < vcf_posit->first) { + reg_start = bed_posit->first; + reg_end = bed_posit->second.get_zyg(); + bed_posit++; + continue; + } + + while( vcf_posit != vcf_chrit->second.end() && reg_start >= vcf_posit->first) vcf_posit++; + while( vcf_posit != vcf_chrit->second.end() && reg_end >= vcf_posit->first) { + if((vcf_posit->first + (long)vcf_posit->second.get_ref(0).length()-1) <= reg_end) { + vcf_posit->second.set_target_end_pos(reg_end - vcf_posit->first); + vcf_out->add(vcf_chrit->first, vcf_posit->first, vcf_posit->second); + records_on_target++; + } + vcf_posit++; + } + } + + while( vcf_posit != vcf_chrit->second.end() && reg_start >= vcf_posit->first) vcf_posit++; + while( vcf_posit != vcf_chrit->second.end() && reg_end >= vcf_posit->first) { + if(vcf_posit->first + (long)vcf_posit->second.get_ref(0).length()-1 <= reg_end) { + vcf_posit->second.set_target_end_pos(reg_end - vcf_posit->first); + vcf_out->add(vcf_chrit->first, vcf_posit->first, vcf_posit->second); + records_on_target++; + } + vcf_posit++; + } + } + return records_on_target; + } +//----------------------------------------------------- + long merge_overlapping_segments(VCFList * merged_bed) { + long total_target_size = 0; + init_target_size(); + for (std::map< string , std::map >::iterator bed_chrit = vcfrec.begin(); bed_chrit != vcfrec.end(); ++bed_chrit) { + std::map::iterator bed_posit = bed_chrit->second.begin(); + + if(bed_posit == bed_chrit->second.end()) continue; + + long int reg_start = bed_posit->first; + long int reg_end = bed_posit->second.get_zyg(); + + bed_posit++; + + while (bed_posit != bed_chrit->second.end()) { + + if(bed_posit->first <= reg_end) { + if(bed_posit->second.get_zyg() > reg_end ) reg_end = bed_posit->second.get_zyg(); + bed_posit++; + continue; + } + + merged_bed->add(bed_chrit->first,reg_start, "", "", "", 0, reg_end); + total_target_size+= reg_end - reg_start; + map_target_ranges[bed_chrit->first].push_back(make_pair(reg_start, reg_end)); + + reg_start = bed_posit->first; + reg_end = bed_posit->second.get_zyg(); + bed_posit++; + + } + + merged_bed->add(bed_chrit->first,reg_start, "", "", "", 0, reg_end); + total_target_size+= reg_end - reg_start; + map_target_ranges[bed_chrit->first].push_back(make_pair(reg_start, reg_end)); + } + + return total_target_size; + } +//----------------------------------------------------- + long intersect(VCFList * in, VCFList * bed_out) { + long total_target_size = 0; + init_target_size(); + for (std::map< string , std::map >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) { + std::map< string , std::map >::iterator in_chrit = in->getList()->find(chrit->first); + if(in_chrit != in->getList()->end()) + for (std::map::iterator posit = chrit->second.begin(), in_posit = in_chrit->second.begin(); posit != chrit->second.end() && in_posit != in_chrit->second.end(); ) { + if(posit->first > in_posit->second.get_zyg()) { + in_posit++; + continue; + } + if(in_posit->first > posit->second.get_zyg()) { + posit++; + continue; + } + long reg_start = in_posit->first < posit->first ? posit->first : in_posit->first; + long reg_end = posit->second.get_zyg() < in_posit->second.get_zyg() ? posit->second.get_zyg() : in_posit->second.get_zyg(); + total_target_size += reg_end - reg_start; + map_target_ranges[in_chrit->first].push_back(make_pair(reg_start, reg_end)); + bed_out->add(chrit->first, reg_start, "", "", "", 0, reg_end); + if(posit->second.get_zyg() == reg_end) posit++; + else in_posit++; + } + } + return total_target_size; + } +//----------------------------------------------------- + void parse_info_values() { + for(std::map< string , std::map >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) + for(std::map::iterator posit = chrit->second.begin(); posit != chrit->second.end(); posit++) posit->second.parse_info_values(); + } + +//----------------------------------------------------- + int * get_var_counts(string basename = "", int DP = -1) { + ofstream output; + bool outfile = (basename!=""); + if(outfile)output.open(basename.c_str()); + if(!output) { + cerr << "Error writing output file: " << basename << endl; + } + + int * counts = new int[5] {0,0,0,0,0}; + for(std::map< string , std::map >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) + for(std::map::iterator posit = chrit->second.begin(); posit != chrit->second.end(); posit++) + for(int i=0; i< posit->second.alt_count(); i++) { + int vartype = posit->second.get_var_type(i); + if(DP==-1 || posit->second.get_dp()>=DP) counts[vartype]++; + if(output) output << chrit->first << "\t" << posit->first << "\t*"<< vartypes[vartype] <<"*\t" << posit->second.get_ref(i) << "\t" << posit->second.get_alt(i)<< "\t"<< posit->second.get_dp() << "\t.\t" << posit->second.get_info(i) << endl; + } + + if(outfile) output.close(); + + return counts; + } +//----------------------------------------------------- + int ** get_var_counts(string FLAG, float _min, float _step, int nbins, string FLAG2="", char cmp='>', float _val=0) { + float _max = _min + nbins*_step; + + int ** counts = new int*[2]; + counts[0] = new int[nbins+3]; + counts[1] = new int[nbins+3]; + + for(int i=0; i >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) + for(std::map::iterator posit = chrit->second.begin(); posit != chrit->second.end(); posit++) + for(int i=0; i < posit->second.alt_count(); i++) { + float flag_value = posit->second.get_int_flag_value(i,FLAG); + int vartype = posit->second.get_var_type(i) > 0; + + if(flag_value == -1 || (!FLAG2.empty() && ((cmp=='<' && posit->second.get_int_flag_value(i,FLAG2)<_val) || (cmp=='>' && posit->second.get_int_flag_value(i,FLAG2)>_val)))) counts[vartype][nbins+2]++; + else if(flag_value<_min) counts[vartype][0]++; + else if(flag_value >= _max) counts[vartype][nbins+1]++; + else + counts[vartype][1+(int)((flag_value-_min)/_step)]++; + } + + return counts; + } +//----------------------------------------------------- + + void positional_allele_match(VCFList * a, VCFList * match, VCFList * unmatch1, VCFList * unmatch2, bool require_allele_match, bool require_zygosity_match) { + for (std::map< string , std::map >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) { + std::map< string , std::map >::iterator achrit = a->getList()->find(chrit->first); + if(achrit != a->getList()->end()) { + std::map::iterator posit = chrit->second.begin(), aposit = achrit->second.begin(); + while(posit != chrit->second.end() && aposit != achrit->second.end()) { + if(posit->first < aposit->first) { + unmatch1->add(chrit->first,posit->first, posit->second); + posit++; + continue; + } else if(posit->first > aposit->first) { + unmatch2->add(achrit->first,aposit->first, aposit->second); + aposit++; + continue; + } else { + + if(!require_allele_match) { + if(!require_zygosity_match || (aposit->second.get_zyg() != -1 && posit->second.get_zyg() != -1 && aposit->second.get_zyg() == posit->second.get_zyg() )) + match->add(achrit->first,aposit->first, aposit->second); + else { + unmatch1->add(chrit->first,posit->first, posit->second); + unmatch2->add(achrit->first,aposit->first, aposit->second); + } + } else { + // allele matching + int n_match_alleles = 0; + for(int i=0; i < posit->second.alt_count(); i++) { + bool has_match = false; + for(int ai=0; ai < aposit->second.alt_count(); ai++) + if(strcmp(aposit->second.get_alt(ai).c_str(),posit->second.get_alt(i).c_str()) == 0) { + if(!require_zygosity_match || (aposit->second.get_zyg() != -1 && posit->second.get_zyg() != -1 && aposit->second.get_zyg() == posit->second.get_zyg() )) + match->add(achrit->first,aposit->first, aposit->second.get_ref(ai), aposit->second.get_alt(ai), aposit->second.get_info(ai), aposit->second.get_gt_index(ai), aposit->second.get_zyg()); + else { + unmatch1->add(chrit->first,posit->first, posit->second.get_ref(i), posit->second.get_alt(i), posit->second.get_info(i), posit->second.get_gt_index(i), posit->second.get_zyg()); + unmatch2->add(achrit->first,aposit->first, aposit->second.get_ref(ai), aposit->second.get_alt(ai), aposit->second.get_info(ai), aposit->second.get_gt_index(ai), aposit->second.get_zyg()); + } + has_match = true; + n_match_alleles++; + // break; + } + if(!has_match) unmatch1->add(chrit->first,posit->first, posit->second.get_ref(i), posit->second.get_alt(i), posit->second.get_info(i), posit->second.get_gt_index(i), posit->second.get_zyg()); + } + + if(n_match_allelessecond.alt_count()) + for(int ai=0; ai < aposit->second.alt_count(); ai++) { + bool has_match = false; + for(int i=0; i < posit->second.alt_count(); i++) + if(strcmp(aposit->second.get_alt(ai).c_str(),posit->second.get_alt(i).c_str()) == 0) { + has_match = true; + break; + } + if(!has_match) unmatch2->add(achrit->first,aposit->first, aposit->second.get_ref(ai), aposit->second.get_alt(ai), aposit->second.get_info(ai), aposit->second.get_gt_index(ai), aposit->second.get_zyg()); + } + + } + + ++posit; + ++aposit; + } + } + while(posit != chrit->second.end()) { + unmatch1->add(chrit->first,posit->first, posit->second); + posit++; + } + while(aposit != achrit->second.end()) { + unmatch2->add(achrit->first,aposit->first, aposit->second); + aposit++; + } + } else { + for(std::map::iterator posit = chrit->second.begin(); posit != chrit->second.end(); posit++) { + unmatch1->add(chrit->first,posit->first, posit->second); + } + } + } + + for (std::map< string , std::map >::iterator achrit = a->getList()->begin(); achrit != a->getList()->end(); ++achrit) { + std::map< string , std::map >::iterator chrit = vcfrec.find(achrit->first); + if(chrit == vcfrec.end()) { + for(std::map::iterator aposit = achrit->second.begin(); aposit != achrit->second.end(); aposit++) + unmatch2->add(achrit->first,aposit->first, aposit->second); + } + } + } +//----------------------------------------------------- + + void match(VCFList * a, VCFList * tp, VCFList * fn, VCFList * fp, FASTA * reference, bool require_allele_match, bool require_zygosity_match) { + positional_allele_match( a, tp, fn, fp, require_allele_match, require_zygosity_match); + // add haplotype, block, hp-error matchings + } + +//----------------------------------------------------- + + void set_positional_info_from_bam(const char * fname) { + if (strlen(fname) == 0) { + return; + } + tmpstruct_t tmp; + + tmp.beg = 0; + tmp.end = 0x7fffffff; + tmp.in = samopen(fname, "rb", 0); + if (tmp.in == 0) { + fprintf(stderr, "Fail to open BAM file %s\n", fname); + return; + } + + int ref; + bam_index_t *idx=NULL; + bam_plbuf_t *buf=NULL; + idx = bam_index_load(fname); // load BAM index + if (idx == 0) { + fprintf(stderr, "BAM indexing file is not available.\n"); + return; + } + + + for(std::map< string , std::map >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) + for(std::map::iterator posit = chrit->second.begin(); posit != chrit->second.end(); posit++) + for(int i=0; i< posit->second.alt_count(); i++) { + depth = -1; + string pos_id = chrit->first + ":" + itos(posit->first)+ "-" + itos(posit->first); + bam_parse_region(tmp.in->header, pos_id.c_str(), &ref, + &tmp.beg, &tmp.end); // parse the region + if (ref < 0) { + cerr <<"Invalid region " << pos_id << endl; + return; + } + buf = bam_plbuf_init(pileup_func, &tmp); // initialize pileup + bam_fetch(tmp.in->x.bam, idx, ref, tmp.beg, tmp.end, buf, fetch_func); + bam_plbuf_push(0, buf); // finalize pileup + posit->second.set_dp(depth); + } + bam_index_destroy(idx); + bam_plbuf_destroy(buf); + + samclose(tmp.in); + } + +//----------------------------------------------------- + + void set_dp_to_zero() { + + for(std::map< string , std::map >::iterator chrit = vcfrec.begin(); chrit != vcfrec.end(); ++chrit) + for(std::map::iterator posit = chrit->second.begin(); posit != chrit->second.end(); posit++) + posit->second.set_dp(0); + } + +}; + +//----------------------------------------------------------- +void print_stats(VCFList * target, VCFList * tp, VCFList * fp, VCFList * fn, string out_dir, + string out_pref, string out_format, string json_file = "", string DP = "", + long TARGET_SIZE = -1, string full_version_string="0.0") +{ + if(tp==NULL) return; + + std::map map_fp_counts; + for (int index = 0; (index < 100); ++index) {map_fp_counts[index] = 0;} + if (fp != NULL) { + for (std::map >::iterator iter1 = fp->getList()->begin(); (iter1 != fp->getList()->end()); ++iter1) { + for (std::map::iterator iter2 = iter1->second.begin(); (iter2 != iter1->second.end()); ++iter2) { + if (iter2->second.get_target_end_pos() < 100) { + map_fp_counts[iter2->second.get_target_end_pos()]++; + } + } + } + } + std::map map_fn_counts; + for (int index = 0; (index < 100); ++index) {map_fn_counts[index] = 0;} + if (fn != NULL) { + for (std::map >::iterator iter1 = fn->getList()->begin(); (iter1 != fn->getList()->end()); ++iter1) { + for (std::map::iterator iter2 = iter1->second.begin(); (iter2 != iter1->second.end()); ++iter2) { + if (iter2->second.get_target_end_pos() < 100) { + map_fn_counts[iter2->second.get_target_end_pos()]++; + } + } + } + } + ofstream fout; + fout.open((out_dir + "/distance_from_end.txt").c_str()); + fout << "#distance_from_target_end\tfp\tfn" << endl; + for (int index = 0; (index < 100); ++index) {fout << (index * -1) << "\t" << map_fp_counts[index] << "\t" << map_fn_counts[index] << endl;} + fout.close(); + + bool text = (strcmp(out_format.c_str(),"text") == 0); + bool html = (strcmp(out_format.c_str(),"html") == 0); + + vector cov; + cov.push_back(0); + + if(!DP.empty()) { + + std::stringstream ss(DP); + string tmp_value; + while(ss.good()) { + getline( ss, tmp_value, ','); + try { + int c = atoi(tmp_value.c_str()); + if(c>0) cov.push_back(c); + } catch(...) {} + } + } + + ofstream output; + bool outfile = (json_file!=""); + if(outfile) { + output.open(json_file.c_str()); + output << "{" << endl; + } + + for(unsigned int j=0; j=" << cov[j] <<"x:SNP,DEL,INS,MNP,SNP+MNP,INDEL,ALL" << (html?"
\n":"\n"); + + int * counts_tp = (j==0) ? tp->get_var_counts(out_pref+"_tp.vcf") : tp->get_var_counts("",cov[j]); + if(j==0) { + if(html) cout << "TP:"; + else cout << "("<< out_pref << "_tp.vcf" << ")TP:"; + } else cout << "TP:"; + + for(int i=0; i<4; i++) cout << counts_tp[i] << ","; + cout << (counts_tp[0] + counts_tp[3]) << ","; + cout << (counts_tp[1] + counts_tp[2]) << ","; + cout << (counts_tp[1] + counts_tp[0] + counts_tp[2] + counts_tp[3] ) << (html?"
\n":"\n"); + if(outfile) { + output << "\"TP-SNP-" << cov[j] << "\":"<< counts_tp[0] << "," << endl; + output << "\"TP-DEL-" << cov[j] << "\":"<< counts_tp[1] << "," << endl; + output << "\"TP-INS-" << cov[j] << "\":"<< counts_tp[2] << "," << endl; + output << "\"TP-MNP-" << cov[j] << "\":"<< counts_tp[3] << "," << endl; + output << "\"TP-SNPMNP-" << cov[j] << "\":"<< (counts_tp[0] + counts_tp[3]) << "," << endl; + output << "\"TP-INDEL-" << cov[j] << "\":"<< (counts_tp[1] + counts_tp[2]) << "," << endl; + output << "\"TP-ALL-" << cov[j] << "\":"<< (counts_tp[1] + counts_tp[0] + counts_tp[2] + counts_tp[3]) << "," << endl; + } + + if( fn != NULL && fp != NULL) { + + int * counts_fn = (j==0) ? fn->get_var_counts(out_pref+"_fn.vcf") : fn->get_var_counts("",cov[j]); + + if(j==0) { + if(html) cout << "FN:"; + else cout << "("<< out_pref << "_fn.vcf" << ")FN:"; + } else cout << "FN:"; + + for(int i=0; i<4; i++) cout << counts_fn[i] << ","; + cout << (counts_fn[0] + counts_fn[3]) << ","; + cout << (counts_fn[1] + counts_fn[2]) << ","; + cout << (counts_fn[1] + counts_fn[0] + counts_fn[2] + counts_fn[3] ) << (html?"
\n":"\n"); + + if(outfile) { + output << "\"FN-SNP-" << cov[j] << "\":"<< counts_fn[0] << "," << endl; + output << "\"FN-DEL-" << cov[j] << "\":"<< counts_fn[1] << "," << endl; + output << "\"FN-INS-" << cov[j] << "\":"<< counts_fn[2] << "," << endl; + output << "\"FN-MNP-" << cov[j] << "\":"<< counts_fn[3] << "," << endl; + output << "\"FN-SNPMNP-" << cov[j] << "\":"<< (counts_fn[0] + counts_fn[3]) << "," << endl; + output << "\"FN-INDEL-" << cov[j] << "\":"<< (counts_fn[1] + counts_fn[2]) << "," << endl; + output << "\"FN-ALL-" << cov[j] << "\":"<< (counts_fn[1] + counts_fn[0] + counts_fn[2] + counts_fn[3]) << "," << endl; + } + + + int * counts_fp = (j==0) ? fp->get_var_counts(out_pref+"_fp.vcf") : fp->get_var_counts("",cov[j]); + if(j==0) { + if(html) cout << "FP:"; + else cout << "("<< out_pref << "_fp.vcf" << ")FP:"; + } else cout << "FP:"; + + for(int i=0; i<4; i++) cout << counts_fp[i] << ","; + cout << (counts_fp[0] + counts_fp[3]) << ","; + cout << (counts_fp[1] + counts_fp[2]) << ","; + cout << (counts_fp[1] + counts_fp[0] + counts_fp[2] + counts_fp[3] ) << (html?"
\n":"\n"); + + if(outfile) { + output << "\"FP-SNP-" << cov[j] << "\":"<< counts_fp[0] << "," << endl; + output << "\"FP-DEL-" << cov[j] << "\":"<< counts_fp[1] << "," << endl; + output << "\"FP-INS-" << cov[j] << "\":"<< counts_fp[2] << "," << endl; + output << "\"FP-MNP-" << cov[j] << "\":"<< counts_fp[3] << "," << endl; + output << "\"FP-SNPMNP-" << cov[j] << "\":"<< (counts_fp[0] + counts_fp[3]) << "," << endl; + output << "\"FP-INDEL-" << cov[j] << "\":"<< (counts_fp[1] + counts_fp[2]) << "," << endl; + output << "\"FP-ALL-" << cov[j] << "\":"<< (counts_fp[1] + counts_fp[0] + counts_fp[2] + counts_fp[3]) << "," << endl; + } + int target_size = 0; + if (target != NULL) { + target_size = ((j==0) ? target->calculate_target_size(0) : target->calculate_target_size(cov[j])); + } + float snp_sens = (10000*(counts_tp[0] + counts_tp[3])/(counts_tp[0] + counts_tp[3] + counts_fn[0] + counts_fn[3]+0.0001))/100.00; + float indel_sens = (10000*(counts_tp[1] + counts_tp[2])/(counts_tp[1] + counts_tp[2] + counts_fn[1] + counts_fn[2]+0.0001))/100.00; + float total_sens = (10000*(counts_tp[0] + counts_tp[3]+ counts_tp[1] + counts_tp[2])/(counts_tp[0] + counts_tp[3] + counts_tp[1] + counts_tp[2] + counts_fn[0] + counts_fn[3] + counts_fn[1] + counts_fn[2]+0.0001))/100.00; + float snp_ppv = (10000*(counts_tp[0] + counts_tp[3])/(counts_tp[0] + counts_tp[3] + counts_fp[0] + counts_fp[3]+0.0001))/100.00; + float indel_ppv = (10000*(counts_tp[1] + counts_tp[2])/(counts_tp[1] + counts_tp[2] + counts_fp[1] + counts_fp[2]+0.0001))/100.00; + float total_ppv = (10000*(counts_tp[0] + counts_tp[3]+ counts_tp[1] + counts_tp[2])/(counts_tp[0] + counts_tp[3] + counts_tp[1] + counts_tp[2] + counts_fp[0] + counts_fp[3] + counts_fp[1] + counts_fp[2]+0.0001))/100.00; + float snp_fpr = ((1.0e+8)*(counts_fp[0] + counts_fp[3])/(target_size+0.0001))/100.00; + float indel_fpr = ((1.0e+8)*(counts_fp[1] + counts_fp[2])/(target_size+0.0001))/100.00; + float total_fpr = ((1.0e+8)*(counts_fp[0] + counts_fp[3]+counts_fp[1] + counts_fp[2])/(target_size+0.0001))/100.00; + + + cout << "SENS%:"<<"-,-,-,-,-," << snp_sens << "," << indel_sens << "," << total_sens << (html?"
\n":"\n"); + cout << "PPV%:"<<"-,-,-,-,-," << snp_ppv << "," << indel_ppv << "," << total_ppv << (html?"
\n":"\n"); + if (target_size>0) cout << "FP/Mb:"<<"-,-,-,-,-," << snp_fpr << "," << indel_fpr << "," << total_fpr << (html?"
\n":"\n"); + if ((target != NULL) and (cov.size() > 1)) { + cout << "COVERAGE:" << ((j==0) ? target->calculate_target_size(0) : target->calculate_target_size(cov[j])) << (html?"
\n":"\n"); + } + + if(outfile) { + output << "\"SENS-SNPMNP" << cov[j] << "\":"<< snp_sens << "," << endl; + output << "\"SENS-INDEL" << cov[j] << "\":"<< indel_sens << "," << endl; + output << "\"SENS-ALL" << cov[j] << "\":"<< total_sens << "," << endl; + output << "\"PPV-SNPMNP" << cov[j] << "\":"<< snp_ppv << "," << endl; + output << "\"PPV-INDEL" << cov[j] << "\":"<< indel_ppv << "," << endl; + output << "\"PPV-ALL" << cov[j] << "\":"<< total_ppv << "," << endl; + if(TARGET_SIZE>0) { + output << "\"FPR-SNPMNP" << cov[j] << "\":"<< snp_fpr << "," << endl; + output << "\"FPR-INDEL" << cov[j] << "\":"<< indel_fpr << "," << endl; + output << "\"FPR-ALL" << cov[j] << "\":"<< total_fpr << "," << endl; + } + } + + delete counts_fp; + delete counts_fn; + } + + delete counts_tp; + } + + if(outfile) { + output << "\"vcfcomp-version\":"<< "\"" << full_version_string << "\""<< "\n}" << endl; + output.close(); + } + +} + +void optimizer(VCFList * _tp,VCFList * _tp_filter,VCFList * _fp, VCFList * _fp_filter, string FLAG="QUAL", float _min = 15, float _step = 5, int nbins = 10, string FLAG2="", char cmp_operator='<', float _val = 0) { + +// RBI - filter_unusual_predictions +// MLLD - data_qual_string +// STB - xx_strand_bias +// STBP - xx_strand_bias_pval + + /* + ofstream output; + bool outfile = (basename!=""); + if(outfile)output.open(basename.c_str()); + if(!output) { cerr << "Error writing output file: " << basename << endl; } + */ + + int ** tp = FLAG2.empty() ? _tp->get_var_counts(FLAG, _min, _step, nbins): _tp->get_var_counts(FLAG, _min, _step, nbins, FLAG2, cmp_operator, _val); + int ** fn = FLAG2.empty() ? _tp_filter->get_var_counts(FLAG, _min, _step, nbins) : _tp_filter->get_var_counts(FLAG, _min, _step, nbins, FLAG2, cmp_operator, _val); + int ** fp = FLAG2.empty() ? _fp->get_var_counts(FLAG, _min, _step, nbins) : _fp->get_var_counts(FLAG, _min, _step, nbins, FLAG2, cmp_operator, _val); + int ** tn = FLAG2.empty() ? _fp_filter->get_var_counts(FLAG, _min, _step, nbins) : _fp_filter->get_var_counts(FLAG, _min, _step, nbins, FLAG2, cmp_operator, _val); + + //if(outfile) { + if(!FLAG2.empty()) cout << "fixing " << FLAG2 << (char)cmp_operator << _val << endl; + cout << "thresholding " << FLAG << " min="<< _min << " step=" << _step << " nbins=" << nbins << endl; + + cout << "FLAG" << "\t" << FLAG << "\t" << "true-snp" << "\t" << "true-indel" << "\t" << "fp-snp" << "\t" << "fp-indel" << "\t" << "tn-snp" << "\t" << "tn-indel" << endl; + int i=0; + cout << FLAG << "<<\t" << _min << "\t" << (tp[0][i] + fn[0][i]) << "\t" << (tp[1][i] + fn[1][i]) << "\t" << fp[0][i] << "\t" << fp[1][i] << "\t" << tn[0][i] << "\t" << tn[1][i] << endl; + for(i=1; i<=nbins+1; i++) cout << FLAG << ">=\t" << (((float)(i-1)*_step + _min)) << "\t" << (tp[0][i] + fn[0][i]) << "\t" << (tp[1][i] + fn[1][i]) << "\t" << fp[0][i] << "\t" << fp[1][i] << "\t" << tn[0][i] << "\t" << tn[1][i] << endl; + cout << FLAG << "\t" << "N/A\t" << (tp[0][i] + fn[0][i]) << "\t" << (tp[1][i] + fn[1][i]) << "\t" << fp[0][i] << "\t" << fp[1][i] << "\t" << tn[0][i] << "\t" << tn[1][i] << endl; + + //output.close(); + //} +} + +//-------------------------------------------------- +void print_usage(int ext_code, string full_version_string) { + cerr << "*****************************************************************" << endl; + cerr << "VCF comparison tool v." << full_version_string << endl; + cerr << "Copyright (C) 2015 Ion Torrent Systems, Inc. All Rights Reserved" << endl; + cerr << "The tool generates variant matching report in form of TP,FP,FN " << endl; + cerr << "printed to stdout, and creates corresponding files, e.g., _tp.vcf" << endl; + cerr << "column 3 and 6 in the output files are for var type and coverage." << endl; + cerr <<"******************************************************************" << endl; + cerr << "Usage: vcfcomp --main-vcf filename [--option value]" << endl << endl; + cerr << "[Options]:" << endl; + cerr << "--main-vcf filename this option is required. filename points to a file with variants in VCF format (requires at minimum first 5 VCF columns," << endl; + cerr << " # columns definitions are not required). if record is missing GT value, then all alternative alleles at that position are" << endl; + cerr << " evaluated disregarding zygosity. If record contains INFO field then key=value pairs are extracted for custom reports. If"<< endl; + cerr << " other optional vcf files are not provided, then metrics related to this file only, as well intersection with target(s) are" << endl; + cerr << " are reported in TP category in (_tp.vcf)." << endl; + cerr << "--truth-vcf filename filename with variants in VCF format (requires at minimum first 5 VCF columns). if genotype GT values are missing, then" << endl; + cerr << " all alternative alleles at the same position are evaluated disregarding zygosity. variants from main-vcf are compared to" << endl; + cerr << " variants in truth-vcf. matching variants are reported as TP in (_tp.vcf). variants in main-vcf, but not in truth-vcf are" << endl; + cerr << " reported as FP in (_fp.vcf). variants in truth-vcf, but not in main-vcf are reported as FN in (_fn.vcf)." << endl; + cerr << "--bg-vcf filename filename with variants in VCF format (requires at minimum first 5 VCF columns). if genotype GT values are missing, then" << endl; + cerr << " all alternative alleles at the same position are evaluated. this file is intended for exclusion of known back-ground variants"<< endl; + cerr << " or systematic errors from variant matching step. If trut-vcf is not provided then bg-vcf is used instead." << endl; + cerr << "--filter-vcf filename filename with variants in VCF format (requires at minimum first 5 VCF columns). if genotype GT values are missing, then" << endl; + cerr << " all alternative alleles at the same position are evaluated. it should include candidate variants filtered by variant caller."<< endl; + cerr << " it is in parameter optimization report." << endl; + cerr << "--target bedfile bedfile is a file with a set of target regions in BED format. limit analysis to variants with anchor base in the target region." << endl; + cerr << " In case of DEL or block-substitution entier block should be inside the target. this option can be used multiple times. vcf files" << endl; + cerr << " are normalized then intersected with all provided target files." << endl; + cerr << "--mt,--mb [a,z,p] variant matching criteria in addition to genomic position when comparing main-vcf with truth-vcf (--mt), and bg-vcf (--mb)." << endl; + cerr << " value (a) requires alternative allele match, (z) requires allele + zygosity match, (p) position only. Default '--mt a', and '--mb p'." << endl; + cerr << "--opref prefix preffix is appended to each output file, i.e., prefix_tp.vcf, prefix_fp.vcf, prefix_fn.vcf." << endl; + cerr << "--odir path output directory. default value '.'" << endl; + cerr << "--oformat format format can be text or html. analysis numbers are printed to standard output in convenient representation form." << endl; + cerr << "--ojson filename outputs stats into the filename in a json KEY:VALUE format"<< endl; + cerr << "--main-bam filename filename of the indexed BAM file. using this option may significantly impact the runtime. this file is used to recalculate" << endl; + cerr << " certain metrics for variant positions in case they are requested by analysis, but not provided in INFO field of main-vcf," << endl; + cerr << " or values from INFO field require to be recalculated (e.g., depth of coverage metric 'DP')." << endl; + cerr << "--min_mapping_qv The minimum mapping quality to use in calculating coverage." << endl; + cerr << "--depth cov_list additional coverage depth thresholds used in the report. multiple thresholds should be comma separated. for TP,FP reports" << endl; + cerr << " DP is extracted from main-vcf INFO field if available. for FN reports DP is recalulated from the main-bam file if provided." << endl; + cerr << " missing DP equals to DP=0. example: '-dp 20,30,50'. analysis for coverage >=0 is always reported and stored in the output files." << endl; + cerr << "--fasta reference filename with the reference sequence in FASTA format. If provided, then variant maching is done by normalizing variants to" << endl; + cerr << " the reference. that enables matching of differently represented equivalent variants. usually variants representation is already" << endl; + cerr << " reference normalized, thus this option may not affect the results, however if used it will significantly increase the runtime." << endl; + cerr << "--ignore-gt [m,t,b,f] ignore GT value when it is specified in (m)ain-vcf, (t)ruth-vcf, (b)g-vcf, (f)ilter-vcf. all listed alleles are loaded, zygosity" << endl; + cerr << " is ignored." << endl; + cerr << " intended to include into comparison alleles with empty ./. or reference 0/0 GT values. example: --ignore-gt mt." << endl; + cerr << "--mnp [split,keep] splits MNPs into single SNPs or keeps them unsplit. default '--mnp split'." << endl; + cerr << "--optimze expression use only with --filter-vcf. groups variants into bins defined by expression \"FLAG1,min,step,nbins:FLAG2,<|>,value\". FLAG2 is optional." << endl; + cerr << " variants are grouped into nbins+2 where first and last bins are for values with FLAG =(min+step*nbins). FLAG value is extrated" << endl; + cerr << " from vcf records, and it goes into N/A bin if missing. Most common optimization FLAGS are QUAL, MLLD, RBI, VARB, STB, STBP, FSAF, FSAR." << endl; + cerr << " FLAG2 is used to provide a single value. Example: \"MLLD,5,1,10:QUAL,>,15\" or \"VARB,0.1,0.01,20\" . Outputs table to std output. Multiple" << endl; + cerr << " expressions can be provided by separating them with ';', e.g. \"MLLD,5,1,10:QUAL,>,15;VARB,0.1,0.01,20\" ." << endl; + cerr << "in progress: combining vars, haplotype sequence match, approx var match up to hp-error, handling block-substitutions" << endl; + cerr << endl; + + exit(ext_code); +} +//-------------------------------------------------- +int main(int argc, char *argv[]) { + + string full_version_string = IonVersion::GetVersion() + "." +IonVersion::GetRelease() + + " (" + IonVersion::GetGitHash() + ") (" + IonVersion::GetBuildNum() + ")"; + +// Set default values + string bg_vcf_file = ""; + string truth_vcf_file = ""; + string main_vcf_file = ""; + string filter_vcf_file = ""; + string ref_file = ""; + string out_pref = ""; + string out_dir = "."; + string out_format = "text"; // {text,html,json,cvs} + string DP = ""; + string main_bam_file = ""; + string ignore_genotype = "f"; + string bg_match_criteria = ""; + string truth_match_criteria = "a"; + string optimize = ""; + string map_quality = "4"; + + vector targets; + bool split_mnp = true; + string json_file =""; + +// Read input options + int acnt = 1; + while(acntload_file(ref_file); + cerr << "loaded " << ncnt << " reference contigs." << endl; + } + + if(reference==NULL || reference->size()==0) { + cerr << "\nMissing reference data." << endl; + cerr << "Reference normalization is not performed." <load_file(main_vcf_file, reference, split_mnp, !ignore_genotype.empty() && ignore_genotype.find("m")!=string::npos); + } + + if(!truth_vcf_file.empty()) { + cerr << "\nLoading truth variants: " << truth_vcf_file << endl; + varTruth = new VCFList(); + varTruth->load_file(truth_vcf_file, reference, split_mnp, !ignore_genotype.empty() && ignore_genotype.find("t")!=string::npos); + } + + + if(!bg_vcf_file.empty()) { + cerr << "\nLoading background variants: " << bg_vcf_file << endl; + varBg = new VCFList(); + varBg->load_file(bg_vcf_file, reference, split_mnp, !ignore_genotype.empty() && ignore_genotype.find("b")!=string::npos); + } + + if(!filter_vcf_file.empty()) { + cerr << "\nLoading filtered-candidate variants: " << filter_vcf_file << endl; + varFilter = new VCFList(); + varFilter->load_file(filter_vcf_file, reference, split_mnp, !ignore_genotype.empty() && ignore_genotype.find("f")!=string::npos); + } + + long TARGET_SIZE = -1; + + VCFList * target = NULL; + if(targets.size()>0) { // intersect variant list with target regions + + for(unsigned int i=0; iload_bed_file(targets.at(i)); + + //merging target is required for correct target size calculation and intersection of multiple BEDs + //get_target_vcf supports BEDs with overlapping contigs + + cerr << "merging overlapping targets ... " << targets.at(i) << endl; + TARGET_SIZE = tmp_target->merge_overlapping_segments(merged_target); + cerr << "merged size: "<< TARGET_SIZE << endl; + + delete tmp_target; + if(i==0) target = merged_target; + else { + tmp_target = new VCFList(); + cerr << "intersect previous target with " << targets.at(i) << endl; + TARGET_SIZE = target->intersect(merged_target,tmp_target); + cerr << "resulting target size: " << TARGET_SIZE << endl; + delete target; + delete merged_target; + target = tmp_target; + } + } + target->set_positional_info_from_bam(main_bam_file.c_str()); + + cerr << "extract on-target vcf records ... " << endl; + if(varVC!=NULL) { + VCFList * _varVC = new VCFList(); + long vcrecords = target->get_target_vcf(varVC, _varVC); + cerr << "main-vcf records on target .. " << vcrecords << endl; + delete varVC; + varVC = _varVC; + } + if(varTruth!=NULL) { + VCFList * _varTruth = new VCFList(); + long trrecords = target->get_target_vcf(varTruth, _varTruth); + cerr << "truth-vcf records on target .. " << trrecords << endl; + delete varTruth; + varTruth = _varTruth; + } + if(varBg!=NULL) { + VCFList * _varBg = new VCFList(); + long bgrecords = target->get_target_vcf(varBg, _varBg); + cerr << "bg-vcf records on target .. " << bgrecords << endl; + delete varBg; + varBg = _varBg; + } + + if(varFilter!=NULL) { + VCFList * _varFilter = new VCFList(); + long bgrecords = target->get_target_vcf(varFilter, _varFilter); + cerr << "filter-vcf records on target .. " << bgrecords << endl; + delete varFilter; + varFilter = _varFilter; + } + + //if(target != NULL) delete target; + + } // finished target intersection + + // remove Bg from Truth + + bool zygosity_match = !bg_match_criteria.empty() && bg_match_criteria.find("z")!=string::npos; + bool allele_match = zygosity_match || (!bg_match_criteria.empty() && bg_match_criteria.find("a")!=string::npos); + //if ((zygosity_match) and (truth_match_criteria.find("p")!=string::npos)) {zygosity_match = false;} + cerr << "zygosity_match = " << zygosity_match << endl; + cerr << "allele_match = " << allele_match << endl; + varTruth->remove(varBg, zygosity_match, allele_match); + + VCFList *tp = NULL, *fp = NULL, *fn = NULL; + + if(!bg_vcf_file.empty()) { + bool require_zygosity_match = !bg_match_criteria.empty() && bg_match_criteria.find("z")!=string::npos; + bool require_allele_match = require_zygosity_match || (!bg_match_criteria.empty() && bg_match_criteria.find("a")!=string::npos); + + tp = new VCFList(); + fp = new VCFList(); + fn = new VCFList(); + varBg->match(varVC, tp, fn, fp, reference, require_allele_match, require_zygosity_match); + } + + if(!truth_vcf_file.empty()) { + bool require_zygosity_match = !truth_match_criteria.empty() && truth_match_criteria.find("z")!=string::npos; + bool require_allele_match = require_zygosity_match || (!truth_match_criteria.empty() && truth_match_criteria.find("a")!=string::npos); + if(fp!=NULL) { + if(varVC!=NULL) delete varVC; + varVC = new VCFList(*fp); + delete fp; + } + if(tp!=NULL) delete tp; + if(fn!=NULL) delete fn; + tp = new VCFList(); + fn = new VCFList(); + fp = new VCFList(); + + varTruth->match(varVC, tp, fn, fp, reference, require_allele_match, require_zygosity_match); + } + + if(!main_bam_file.empty()) { + cerr << "loading positional info from BAM file " << main_bam_file << endl; + fn->set_positional_info_from_bam(main_bam_file.c_str()); + } else if(fn!=NULL) fn->set_dp_to_zero(); + + bool html = (strcmp(out_format.c_str(),"html") == 0); + + cerr << endl; + cout << "vcfcomp version:"<< full_version_string << (html?"
\n":"\n"); + if(TARGET_SIZE>0) cout << "Final Target Size (bp):"<< TARGET_SIZE << (html?"
\n":"\n"); + cout << "\nReport for main-vcf\n**********************************************************" << endl; + print_stats(target, tp == NULL ? varVC : tp , fp, fn, out_dir, out_pref, out_format, json_file, DP, TARGET_SIZE, full_version_string ); + + // inspecting filtered file + + VCFList *tp_filter = NULL, *fp_filter = NULL, *fn_filter = NULL; + + if(varFilter!=NULL) { + + if(!bg_vcf_file.empty()) { + bool require_zygosity_match = !bg_match_criteria.empty() && bg_match_criteria.find("z")!=string::npos; + bool require_allele_match = require_zygosity_match || (!bg_match_criteria.empty() && bg_match_criteria.find("a")!=string::npos); + + tp_filter = new VCFList(); + fp_filter = new VCFList(); + fn_filter = new VCFList(); + varBg->match(varFilter, tp_filter, fn_filter, fp_filter, reference, require_allele_match, require_zygosity_match); + } + + if(!truth_vcf_file.empty()) { + bool require_zygosity_match = !truth_match_criteria.empty() && truth_match_criteria.find("z")!=string::npos; + bool require_allele_match = require_zygosity_match || (!truth_match_criteria.empty() && truth_match_criteria.find("a")!=string::npos); + + if(fp_filter!=NULL) { + if(varFilter!=NULL) delete varFilter; + varFilter = fp_filter; + } + if(tp_filter!=NULL) delete tp_filter; + if(fn_filter!=NULL) delete fn_filter; + tp_filter = new VCFList(); + fn_filter = new VCFList(); + fp_filter = new VCFList(); + + varTruth->match(varFilter, tp_filter, fn_filter, fp_filter, reference, require_allele_match, require_zygosity_match); + } + + if(!main_bam_file.empty()) { + cerr << "loading positional info from BAM file " << main_bam_file << endl; + fn_filter->set_positional_info_from_bam(main_bam_file.c_str()); + } else if(fn_filter!=NULL) fn_filter->set_dp_to_zero(); + + cout << "\nReport for filter-vcf\n**********************************************************" << endl; + print_stats(target, tp_filter == NULL ? varFilter : tp_filter , fp_filter, fn_filter, out_dir, out_pref + "_filtered", out_format, "", DP, TARGET_SIZE, full_version_string ); + + } + + if(target != NULL) delete target; + if(varBg!=NULL) delete varBg; + if(varTruth!=NULL) delete varTruth; + if(varVC!=NULL) delete varVC; + if(varFilter!=NULL) delete varFilter; + if(fn!=NULL) delete fn; + if(fn_filter!=NULL) delete fn_filter; + + + if(tp_filter!=NULL && !optimize.empty()) { + cout << "\nRunning parameter optimization\n**********************************************************" << endl; + cerr << "Parsing info values..." << endl; + tp->parse_info_values(); + tp_filter->parse_info_values(); + fp->parse_info_values(); + fp_filter->parse_info_values(); + cerr << "Optimizing ...." << endl; + + std::stringstream ss(optimize); + string expres; + while(ss.good()) { + getline( ss, expres, ';'); + if(!expres.empty()) { + std::stringstream aa(expres); + string first = "", second = ""; + if(aa.good()) getline( aa, first, ':'); + if(aa.good()) getline( aa, second, ':'); + + string FLAG="",FLAG2=""; + float _min = 15.0, _step = 5.0, _value = 0.0; + int _nbins = 0; + char comp_operator='<'; + + if(!first.empty()) { + std::stringstream bb(first); + + if(bb.good()) try { + string tmp; + getline(bb, FLAG, ','); + getline(bb, tmp, ','); + _min = atof(tmp.c_str()); + getline(bb, tmp, ','); + _step = atof(tmp.c_str()); + getline(bb, tmp, ','); + _nbins = atoi(tmp.c_str()); + } catch(...) { + FLAG=""; + } + } + + if(!second.empty()) { + std::stringstream bb(first); + + if(bb.good()) try { + string tmp; + getline(bb, FLAG2, ','); + getline(bb, tmp, ','); + comp_operator = tmp[0]!='<'?'>':'<'; + getline(bb, tmp, ','); + _value = atof(tmp.c_str()); + } catch(...) { + FLAG2=""; + } + } + + if(!FLAG.empty()) { + if(!FLAG2.empty()) optimizer(tp,tp_filter,fp,fp_filter, FLAG, _min, _step, _nbins, FLAG2, comp_operator, _value); + else optimizer(tp,tp_filter,fp,fp_filter, FLAG, _min, _step, _nbins); + } + } + } + } + + return 0; + // -- free-up memory, not necessary since program terminates here + if(reference!=NULL) delete reference; + if(tp!=NULL) delete tp; + if(fp!=NULL) delete fp; + if(tp_filter!=NULL) delete tp_filter; + if(fp_filter!=NULL) delete fp_filter; + + + +} + +//----------------------------------------------------- diff --git a/Analysis/bbctools/src/AmpliconRegionStatistics.cpp b/Analysis/bbctools/src/AmpliconRegionStatistics.cpp index 2af50749..af14fde0 100644 --- a/Analysis/bbctools/src/AmpliconRegionStatistics.cpp +++ b/Analysis/bbctools/src/AmpliconRegionStatistics.cpp @@ -154,6 +154,7 @@ void AmpliconRegionStatistics::TrackReadsOnRegion( const BamTools::BamAlignment } // pseudo-randomly choose best region of equivalent best regions TargetRegion *bestRegion = m_regionStack[ clockSeed % numBestRegions ]; + m_lastRegionAssigned = bestRegion; bool e2e_or_cov; if( m_sigFacCoverage ) { int32_t trgLen = bestRegion->trgEnd - bestRegion->trgSrt + 1; diff --git a/Analysis/bbctools/src/BbcCoarse.cpp b/Analysis/bbctools/src/BbcCoarse.cpp index c40d66ff..935dae58 100644 --- a/Analysis/bbctools/src/BbcCoarse.cpp +++ b/Analysis/bbctools/src/BbcCoarse.cpp @@ -327,7 +327,8 @@ bool BbcCoarse::SetReference( const BamTools::RefVector &references ) m_contigBinIndex[i] = references[i].RefLength / m_minorBinSize; if( references[i].RefLength % m_majorBinSize ) ++m_contigBinIndex[i]; } - m_numMajorBins = m_numContigs; + // m_minorPerMajor == 1 is a special case where major substitutes for minor bins + m_numMajorBins = m_contigBinIndex[m_numContigs-1]; } // allocate memory and sizes to be filled out or loaded (unused for V0 or V1 w/ minor contigs) delete [] m_majorBins; @@ -336,7 +337,8 @@ bool BbcCoarse::SetReference( const BamTools::RefVector &references ) free(m_minorBinPack); m_minorBinPack = NULL; if( m_write ) { - // estimate of typical work space: realloc() may be necessary for high coverage references + // sizeof(uint32_t) assumes sparse coverage: average 1 byte per targeted base coverage (or 2 for WGNM) + // realloc() may be necessary for high coverage references, doubling storage as necessary m_minorBinPackSize = m_numMajorBins * m_minorPerMajor * sizeof(uint32_t); // ~47Mb for human m_minorBinPack = (uint8_t *)malloc( m_minorBinPackSize ); } diff --git a/Analysis/bbctools/src/BbcCoarse.h b/Analysis/bbctools/src/BbcCoarse.h index 8b26f7ba..7f5193d7 100644 --- a/Analysis/bbctools/src/BbcCoarse.h +++ b/Analysis/bbctools/src/BbcCoarse.h @@ -136,7 +136,9 @@ class BbcCoarse inline void CheckMinorBinPack( int siz ) { if( m_minorBinPackTail + siz > m_minorBinPackSize ) { - m_minorBinPackSize <<= 1; + // double memory size until enough + while( m_minorBinPackTail + siz > m_minorBinPackSize ) + m_minorBinPackSize <<= 1; m_minorBinPack = (uint8_t *)realloc( m_minorBinPack, m_minorBinPackSize ); } } diff --git a/Analysis/bbctools/src/BbcMain.cpp b/Analysis/bbctools/src/BbcMain.cpp index 790d245d..b1d97645 100644 --- a/Analysis/bbctools/src/BbcMain.cpp +++ b/Analysis/bbctools/src/BbcMain.cpp @@ -20,6 +20,7 @@ using namespace BamTools; #include "BbcView.h" #include "BbcIndex.h" #include "BbcDepth.h" +#include "TrackReads.h" // Number alignments returned using SetRegion() appears incorrect when using closed regions! // It appears to work when using right-open regions but has MASSIVE performance issue. @@ -33,7 +34,7 @@ const int s_initialMinJumpLen = 1000000; const int s_initialMaxReadLen = 1000; // Current version number string for bbctools executable -const uint16_t s_versionNumber = 1003; +const uint16_t s_versionNumber = 1103; // Forward declarations of functions used by main() int bbctools_create( BbcUtils::OptParser &optParser ); @@ -56,7 +57,7 @@ int main( int argc, char* argv[] ) { if( subcmd == "create" ) { subcmdFunc = &bbctools_create; parseString = "A=annotationFields:B=bbc:C=covStats:D=covDepths:E=e2eGap,L=minAlignLength,M=minPcCov;"; - parseString += "P=primerLength,Q=minMAPQ,R=regions:S=sumStats:T=readType:a=autoCreateBamIndex "; + parseString += "O=readOrigin:P=primerLength,Q=minMAPQ,R=regions:S=sumStats:T=readType:a=autoCreateBamIndex "; parseString += "b=onTargetBases c=coarse i=index d=noDups r=onTargetReads s=samdepth u=unique"; maxArgs = 0; } else if( subcmd == "report" ) { @@ -122,6 +123,7 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { string sumstatsFile = optParser.getOptValue("sumStats"); string covstatsFile = optParser.getOptValue("covStats"); + string readOrigFile = optParser.getOptValue("readOrigin"); string readType = optParser.getOptValue("readType"); string covDepths = optParser.getOptValue("covDepths","-"); double minPcCov = optParser.getOptNumber("minPcCov"); @@ -144,12 +146,12 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { bool invertOnTarget = false; // check basic valid argument values and combinations - int numOuts = !bbcfileRoot.empty() + !covstatsFile.empty() + !sumstatsFile.empty(); - int numPipes = (bbcfileRoot == "-") + (covstatsFile == "-") + (sumstatsFile == "-"); + int numOuts = !bbcfileRoot.empty() + !covstatsFile.empty() + !sumstatsFile.empty() + !readOrigFile.empty(); + int numPipes = (bbcfileRoot == "-") + (covstatsFile == "-") + (sumstatsFile == "-") + (readOrigFile == "-"); if( numOuts == 0 && !f_bci && !f_cbc ) { bbcfileRoot = "-"; // default if no other output specified } else if( numPipes > 1 ) { - cerr << "Error: bbctools create: Only one file output (--covStats, --sumStats or --bbc) may be piped to STDOUT." << endl; + cerr << "Error: bbctools create: Only one file output (--covStats, --sumStats, --readOrigin or --bbc) may be piped to STDOUT." << endl; return -1; } else if( samdepth && numOuts ) { cerr << "Error: bbctools create: --samdepth (-s) option may only be used without other output options." << endl; @@ -192,6 +194,10 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { cerr << "Error: --samdepth option is not supported for BBC source files." << endl; return -1; } + if( !readOrigFile.empty() ) { + cerr << "Error: --readOrigin option is not supported for BBC source files." << endl; + return -1; + } } else { // check / open for multiple BAM file inputs if ( !bamReader.Open(cmdArgs) ) { @@ -209,12 +215,17 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { errMsg = BbcUtils::stringTrim(errMsg); errMsg[0] = toupper(errMsg[0]); cerr << endl << errMsg << "." << endl; - return -1; + return 1; } } // grab reference list from either input source const RefVector &references = haveBbcFile ? bbcView.GetReferenceData() : bamReader.GetReferenceData(); - + if( !references.size() ) { + // Issue would already been detected if input was BBC file + cerr << "ERROR: " << (cmdArgs.size() > 1 ? "One or more " : ""); + cerr << "BAM file contains unaligned reads (no references).\n"; + return 1; + } // check/set up target regions input regions/region statistics output RegionCoverage *regions = NULL; string covstatsStaticFields; @@ -312,6 +323,15 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { bbcView.ReadAll(); } } else { + // Test read tracking option for file write + TrackReads *readTracker = NULL; + try { + if( !readOrigFile.empty() ) + readTracker = new TrackReads( readOrigFile, regions ); + } catch( std::runtime_error & ) { + cerr << "ERROR: Unable to write to read tracking file " << readOrigFile << endl; + return 1; + } // BAM reader, BaseCoverage driver, dispatching to BbcCreate and BbcView objects BaseCoverage baseCov(references); baseCov.SetRegionCoverage(regions); @@ -320,9 +340,10 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { if( bbcfileRoot == "-" ) { baseCov.SetBbcView(&bbcView); } + // Certain options require that all reads are processed, invalidating other performance options + bool trackAllReads = !sumstatsFile.empty() || readTracker; // Implicit set of onlyOnTargetReads for performance when only these reads are required bool useBaseCov = (bbcfileRoot == "-" || bbcCreate); - bool trackAllReads = !sumstatsFile.empty(); if( !targetRegions.empty() && !trackAllReads ) { onlyOnTargetReads |= onlyOnTargetBases; if( samdepth || !useBaseCov ) onlyOnTargetReads = true; @@ -398,9 +419,8 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { onlyOnTargetReads = false; if( trackAllReads ) { // force tracking of off-target reads - if( trackAllReads ) { - regions->TrackReadsOnRegion(aln,endPos); - } + regions->TrackReadsOnRegion(aln,endPos); + if( readTracker ) readTracker->Write(aln,endPos); continue; } break; @@ -419,6 +439,7 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { // force tracking of off-target reads if( trackAllReads ) { regions->TrackReadsOnRegion(aln,endPos); + if( readTracker ) readTracker->Write(aln,endPos); } continue; // current is before next target region - fetch the next within bounds } @@ -436,6 +457,9 @@ int bbctools_create( BbcUtils::OptParser &optParser ) { if( regions ) { regions->TrackReadsOnRegion(aln,endPos); } + if( readTracker ) { + readTracker->Write(aln,endPos); + } } // flush and close objects associated with output baseCov.Flush(); @@ -599,7 +623,7 @@ int bbctools_view( BbcUtils::OptParser &optParser ) if( numBins == 0 && binSize == 0 && isolateRegions == false ) { // default view => disallow options dependent on binned-only options if( annotate ) { - cerr << "Error: The --annotationFields (-A) option is only valid for binned coverage (with -B, -N or -i options)." << endl; + cerr << "Error: The --Fields (-A) option is only valid for binned coverage (with -B, -N or -i options)." << endl; return -1; } if( srtBin ) { diff --git a/Analysis/bbctools/src/BbcUsage.h b/Analysis/bbctools/src/BbcUsage.h index 9baf82a8..50c42075 100644 --- a/Analysis/bbctools/src/BbcUsage.h +++ b/Analysis/bbctools/src/BbcUsage.h @@ -49,6 +49,17 @@ static void Usage( string subcmd, bool verbose = false ) { " text file with calculated fields that depend on the --readType and other options specified.\n" " Base and/or read coverage is provided per contig/chromosome, or for each target region if the\n" " --regions (-R) option is provided. A value of '-' may be used to pipe this output to STDOUT.\n" + " -O,--readOrigin File name for read origin output file, which is a tab-delimited text file\n" + " with the assigned target and/or aligned length for every mapped read in the input BAM file.\n" + " The target is identified as the line number of the output --covStats file, or 0 if the read did\n" + " not overlap any target. (It is also the line number for targets defined in the input --regions\n" + " file if this is ordered vs. the reference.) If no --covStats or --regions option is specified the\n" + " output will only have aligned lengths. A value of '-' may be used to pipe this output to STDOUT.\n" + " Usage notes: The association to original read is only made by the line number, which directly\n" + " corresponds to that in the BAM file, or record output by 'samtools view -F 4 '.\n" + " For this reason using a multiple input BAM files is not recommended. (Pre-merge instead.)\n" + " Similarly, if input BAM read filtering options -L, -Q, -d or -u are employed (see below) then\n" + " the SAM read line should be created using equivalent samtools view options.\n" " -R,--regions Input BED file for defining 'on-target' regions flagged in the BBC and\n" " and other coverage files created. A value of '-' may be used to explicitly indicate no target\n" " regions and may be used with an input BBC file to remove on-target information.\n" diff --git a/Analysis/bbctools/src/BbcView.cpp b/Analysis/bbctools/src/BbcView.cpp index a96f1cee..2e66a237 100644 --- a/Analysis/bbctools/src/BbcView.cpp +++ b/Analysis/bbctools/src/BbcView.cpp @@ -559,6 +559,7 @@ bool BbcView::ReadRegion( uint32_t srtContig, uint32_t srtPosition, uint32_t end StreamCoverage( srtPosition++, 0, 0, 0 ); if( srtPosition == endPos ) return true; } + if( srtContig > endContig || m_position >= endPos ) break; } else { if( srtContig > endContig || m_position >= endPos ) break; srtPosition = m_position; diff --git a/Analysis/bbctools/src/RegionCoverage.cpp b/Analysis/bbctools/src/RegionCoverage.cpp index 5840b53e..274c2514 100644 --- a/Analysis/bbctools/src/RegionCoverage.cpp +++ b/Analysis/bbctools/src/RegionCoverage.cpp @@ -20,6 +20,7 @@ RegionCoverage::RegionCoverage( const BamTools::RefVector& references ) : m_contigList(NULL) , m_bcovRegion(NULL) , m_rcovRegion(NULL) + , m_lastRegionAssigned(NULL) , m_numAuxFields(0) , m_ncovDepths(0) { @@ -57,6 +58,7 @@ void RegionCoverage::Clear() m_contigList = NULL; } m_bcovContigIdx = m_rcovContigIdx = m_numRefContigs; + m_lastRegionAssigned = NULL; } uint32_t *RegionCoverage::CreateTargetBinSizes( uint32_t &numBins, uint32_t binSize, @@ -229,6 +231,11 @@ string RegionCoverage::FieldsOnRegion( uint32_t contigIdx, uint32_t readSrt, uin return fieldVals; } +// Return the target index for the region the last call to ReadOnRegion() mapped to, or 0 if none +uint32_t RegionCoverage::GetLastReadOnRegionIdx() const { + return m_lastRegionAssigned ? m_lastRegionAssigned->trgIdx : 0; +} + bool RegionCoverage::GetNextRegion( int &contigIdx, int &srtPosition, int &endPosition, bool start ) { static uint32_t s_contig = 0; @@ -289,19 +296,23 @@ string RegionCoverage::Load( } m_numAuxFields = auxFieldsIdx.size(); vector auxFieldValues(m_numAuxFields,""); + size_t numAuxFields = auxFieldValues.size(); + bool auxWarn = true; ifstream trgfile(fileName.c_str()); if( !trgfile.is_open() ) { return "Failed to open region file "+fileName; } string line, lastContig(""); - int lineNum = 0, numTracks = 0; + int lineNum = 0, numTracks = 0, numFields = 0; + uint32_t srt, end; size_t contigIdx = 0; TargetContig *currentContig = NULL; while( getline(trgfile,line) ) { ++lineNum; vector fields = stringTrimSplit(line,'\t'); - if( fields[0].empty() ) continue; + int fsize = fields.size(); + if( !fsize ) continue; // handle header line - may later depend on fileType if( filetype == 1 && fields[0].substr(0,5) == "track" ) { if( ++numTracks > 1 ) { @@ -318,7 +329,6 @@ string RegionCoverage::Load( continue; } // check primary data fields - int fsize = fields.size(); for( size_t i = 0; i < 3; ++i ) { if( trgFieldsIdx[i] >= fsize || fields[trgFieldsIdx[i]].empty() ) { ostringstream ss; @@ -327,6 +337,16 @@ string RegionCoverage::Load( return ss.str(); } } + // check number of fields consistency + if( numFields != fsize ) { + if( !numFields ) { + numFields = fsize; + } else { + ostringstream ss; + ss << "Inconsistent number of fields in regions file " << fileName << " at line " << lineNum; + return ss.str(); + } + } // check for new contig start string contig = fields[trgFieldsIdx[0]]; if( lastContig != contig ) { @@ -343,8 +363,12 @@ string RegionCoverage::Load( currentContig = m_contigList[contigIdx]; } // get both coordinates to 1-base - uint32_t srt = stringToInteger(fields[trgFieldsIdx[1]]) + srtPosAdj; - uint32_t end = stringToInteger(fields[trgFieldsIdx[2]]); + try { + srt = stringToInteger(fields[trgFieldsIdx[1]]) + srtPosAdj; + end = stringToInteger(fields[trgFieldsIdx[2]]); + } catch( runtime_error &e ) { + return "Format error in regions file "+fileName+": "+e.what(); + } if( srt == end+1 ) { fprintf(stderr,"WARNING: Bed file region has zero length at line %d. Discarded.\n",lineNum); continue; @@ -356,10 +380,16 @@ string RegionCoverage::Load( return ss.str(); } // grab auxiliary fields for new region data - if( auxFieldValues.size() ) { + // - missing auxiliary fields are given value "" with a warning for first occurrence + if( numAuxFields ) { for( size_t i = 0; i < m_numAuxFields; ++i ) { int idx = auxFieldsIdx[i]; if( idx < 0 ) idx += fsize; + if( idx >= fsize && auxWarn ) { + cerr << "Warning: No value found for auxiliary field " << idx << " at line " << lineNum; + cerr << " of regions file " << fileName << ". Missing values will default to empty.\n"; + auxWarn = false; + } auxFieldValues[i] = (idx >= 0 && idx < fsize) ? fields[idx] : ""; } } @@ -367,8 +397,10 @@ string RegionCoverage::Load( } trgfile.close(); // create array of sorted regions for mapping + uint32_t rgnIdx = 0; for( size_t i = 0; i < m_numRefContigs; ++i ) { - m_contigList[i]->MakeSortedArray(); + m_contigList[i]->ReverseSort(rgnIdx); + rgnIdx += m_contigList[i]->numRegions; } return ""; } @@ -451,7 +483,7 @@ void RegionCoverage::SetWholeContigTargets() vector auxFieldValues; for( size_t i = 0; i < m_numRefContigs; ++i ) { m_contigList[i]->AddRegion( new TargetRegion( 1, m_contigList[i]->length, auxFieldValues ) ); - m_contigList[i]->MakeSortedArray(); + m_contigList[i]->ReverseSort(i); } } @@ -495,11 +527,8 @@ void RegionCoverage::Write( const string &filename, const string &columnTitles ) if( haveHeader ) fprintf( fout, "\n" ); } for( size_t i = 0; i < m_numRefContigs; ++i ) { - TargetRegion **targetRegionArray = m_contigList[i]->targetRegionArray; - if( !targetRegionArray ) continue; const char *contig = m_contigList[i]->id.c_str(); - for( size_t j = 0; j < m_contigList[i]->numRegions; ++j ) { - TargetRegion *tr = targetRegionArray[j]; + for( TargetRegion *tr = m_contigList[i]->targetRegionHead; tr; tr = tr->next ) { fprintf( fout, "%s\t%d\t%d", contig, tr->trgSrt, tr->trgEnd ); vector &auxData = tr->auxData; for( size_t k = 0; k < auxData.size(); ++k ) { @@ -643,6 +672,7 @@ uint32_t RegionCoverage::BaseOnRegion( uint32_t contigIdx, uint32_t position ) // Tracks first on-target in instance object so can be iterated over all hit targets. // Assumes reads are given in order (for a single contig) so the search starts where it left off // or at the start of new contig. A reset for the current contig can be forced by using readEnd = 0 +// Side-effect: Record the (reference sorted) index of the overlapped region or 0 if none. uint32_t RegionCoverage::ReadOnRegion( uint32_t contigIdx, uint32_t readSrt, uint32_t readEnd ) { if( contigIdx != m_rcovContigIdx && contigIdx < m_numRefContigs ) { @@ -654,7 +684,11 @@ uint32_t RegionCoverage::ReadOnRegion( uint32_t contigIdx, uint32_t readSrt, uin // here we want the first region overlapped by the read for( ; m_rcovRegion; m_rcovRegion = m_rcovRegion->next ) { if( readEnd < m_rcovRegion->trgSrt ) break; - if( readSrt <= m_rcovRegion->trgEnd ) return 1; + if( readSrt <= m_rcovRegion->trgEnd ) { + m_lastRegionAssigned = m_rcovRegion; + return 1; + } } + m_lastRegionAssigned = NULL; return 0; } diff --git a/Analysis/bbctools/src/RegionCoverage.h b/Analysis/bbctools/src/RegionCoverage.h index 762e7b01..0c09c0d4 100644 --- a/Analysis/bbctools/src/RegionCoverage.h +++ b/Analysis/bbctools/src/RegionCoverage.h @@ -25,6 +25,7 @@ class RegionCoverage protected: struct TargetRegion { + uint32_t trgIdx; uint32_t trgSrt; uint32_t trgEnd; vector auxData; @@ -33,7 +34,8 @@ class RegionCoverage TargetRegion *next; TargetRegion( uint32_t srt, uint32_t end, const vector& aux ) - : trgSrt(srt) + : trgIdx(0) + , trgSrt(srt) , trgEnd(end) , auxData(aux) , extData(NULL) @@ -54,8 +56,7 @@ class RegionCoverage , revReads(0) , fwdTrgReads(0) , revTrgReads(0) - , targetRegionHead(NULL) - , targetRegionArray(NULL) {} + , targetRegionHead(NULL) {} ~TargetContig(void) { TargetRegion *nrg = targetRegionHead; @@ -64,7 +65,6 @@ class RegionCoverage delete nrg; nrg = next; } - delete [] targetRegionArray; } // Resulting link list starting at targetRegionHead will be in reverse order of target regions. @@ -72,7 +72,7 @@ class RegionCoverage // reverse order f to top of the list given pre-ordered target regions. void AddRegion( TargetRegion *nrg ) { if( !nrg ) return; - ++numRegions; + nrg->trgIdx = ++numRegions; // initially set to order loaded if( !targetRegionHead ) { targetRegionHead = nrg; return; @@ -95,37 +95,27 @@ class RegionCoverage nrg->next = cur; } - // convert linked list into array and forward list for quick access - void MakeSortedArray(void) { - delete targetRegionArray; - if( !numRegions ) { - targetRegionArray = NULL; - return; - } - targetRegionArray = new TargetRegion*[numRegions]; - TargetRegion *cur = targetRegionHead; - int lastReg = (int)numRegions-1; - for( int i = lastReg; i >= 0; --i ) { - targetRegionArray[i] = cur; - cur = cur->next; + // convert reverse ordered linked list to be forward ordered and reset region index + void ReverseSort( uint32_t idxOffset = 0 ) { + TargetRegion *prv = NULL, *nxt; + idxOffset += numRegions; + for( TargetRegion *cur = targetRegionHead; cur; cur = nxt ) { + cur->trgIdx = idxOffset--; + nxt = cur->next; + cur->next = prv; + prv = cur; } - // reverse order of linked list so this may be used in addition to array - targetRegionHead = cur = targetRegionArray[0]; - for( int i = 0; i < lastReg; ++i ) { - cur = cur->next = targetRegionArray[i+1]; - } - cur->next = NULL; + targetRegionHead = prv; } string id; int32_t length; - size_t numRegions; + uint32_t numRegions; uint64_t fwdReads; uint64_t revReads; uint64_t fwdTrgReads; uint64_t revTrgReads; TargetRegion *targetRegionHead; - TargetRegion **targetRegionArray; }; size_t m_numRefContigs; @@ -140,6 +130,7 @@ class RegionCoverage TargetRegion *m_bcovRegion; uint32_t m_rcovContigIdx; TargetRegion *m_rcovRegion; + TargetRegion *m_lastRegionAssigned; // common optional depth at coverage stats size_t m_numAuxFields; @@ -169,6 +160,9 @@ class RegionCoverage // the given locus. If more than maxValues then return ,...(N)..., (N>1). string FieldsOnRegion( uint32_t contigIdx, uint32_t readSrt, uint32_t readEnd, uint32_t maxValues = 3 ); + // Return the target index for the region the last call to ReadOnRegion() mapped to, or 0 if none + uint32_t GetLastReadOnRegionIdx(void) const; + // An iterator of all target regions returning false for a call after the last region is returned. // The optional start argument may be used to reset this iterator to the first region. // Note that srtPositon is returned as a 0-based integer (etc.) for intended usage with the BamMultiReader::SetRegion(). diff --git a/Analysis/bbctools/src/TrackReads.cpp b/Analysis/bbctools/src/TrackReads.cpp new file mode 100644 index 00000000..4cfbd320 --- /dev/null +++ b/Analysis/bbctools/src/TrackReads.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2016 Thermo Fisher Scientific. All Rights Reserved. +/* + * TrackReads.cpp + * + * Created on: Sep 8, 2016 + * Author: Guy Del Mistro + */ + +#include "TrackReads.h" + +TrackReads::TrackReads( const string& filename, const RegionCoverage* regions ) + : m_filename(filename) + , m_regions(regions) + , m_outfile(NULL) +{ + if( !filename.empty() && filename != "-" ) { + m_outfile = fopen( m_filename.c_str(), "w" ); + if( !m_outfile ) throw std::runtime_error("TrackReads(): Failed to open file for write: "+m_filename); + } +} + +TrackReads::~TrackReads() +{ + if( m_outfile ) fclose(m_outfile); +} + +void TrackReads::Write( const BamTools::BamAlignment &aread, uint32_t endPos ) +{ + uint32_t rend = endPos ? endPos : aread.GetEndPosition(); + uint32_t rlen = rend - aread.Position; + FILE *out = m_outfile ? m_outfile : stdout; + if( m_regions ) { + fprintf( out, "%u\t%u\n",m_regions->GetLastReadOnRegionIdx(),rlen); + } else { + // Default output if no target regions resource was provided + fprintf( out, "%u\n",rlen); + } +} + diff --git a/Analysis/bbctools/src/TrackReads.h b/Analysis/bbctools/src/TrackReads.h new file mode 100644 index 00000000..6d25ee1a --- /dev/null +++ b/Analysis/bbctools/src/TrackReads.h @@ -0,0 +1,31 @@ +// Copyright (C) 2016 Thermo Fisher Scientific. All Rights Reserved. +/* + * TrackReads.h + * + * Created on: Sep 8, 2016 + * Author: Guy Del Mistro + */ + +// The TrackReads class is defined with access to the BAM read and target region data +// so that this read tracking output can be expanded on later. + +#ifndef TRACKREADS_H_ +#define TRACKREADS_H_ + +#include "RegionCoverage.h" + +class TrackReads +{ + public: + TrackReads( const string& filename, const RegionCoverage* regions = NULL ); + virtual ~TrackReads(void); + + void Write( const BamTools::BamAlignment &aread, uint32_t endPos ); + + private: + const string& m_filename; + const RegionCoverage* m_regions; + FILE *m_outfile; +}; + +#endif /* TRACKREADS_H_ */ diff --git a/Analysis/config/args_314_beadfind.json b/Analysis/config/args_314_beadfind.json index ea5c73c6..b8ea78b7 100644 --- a/Analysis/config/args_314_beadfind.json +++ b/Analysis/config/args_314_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 7.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 4.0, @@ -32,7 +34,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_314.bin" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_316_beadfind.json b/Analysis/config/args_316_beadfind.json index 0609b4ba..f40a9cf6 100644 --- a/Analysis/config/args_316_beadfind.json +++ b/Analysis/config/args_316_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 7.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 4.0, @@ -32,7 +34,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_316.bin" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_316v2_beadfind.json b/Analysis/config/args_316v2_beadfind.json index 3b9baf23..375774af 100644 --- a/Analysis/config/args_316v2_beadfind.json +++ b/Analysis/config/args_316v2_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 7.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 4.0, @@ -32,7 +34,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_316v2.bin" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_318D_beadfind.json b/Analysis/config/args_318D_beadfind.json index 59fadcb6..091d95b7 100644 --- a/Analysis/config/args_318D_beadfind.json +++ b/Analysis/config/args_318D_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 7.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 4.0, @@ -32,7 +34,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_318.bin" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_318_beadfind.json b/Analysis/config/args_318_beadfind.json index f00751bd..0ccfa267 100644 --- a/Analysis/config/args_318_beadfind.json +++ b/Analysis/config/args_318_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 7.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 4.0, @@ -32,7 +34,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_318.bin" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_318select_beadfind.json b/Analysis/config/args_318select_beadfind.json index c994637c..34b2b91e 100644 --- a/Analysis/config/args_318select_beadfind.json +++ b/Analysis/config/args_318select_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 7.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 4.0, diff --git a/Analysis/config/args_520_beadfind.json b/Analysis/config/args_520_beadfind.json index 2e0298dc..ec6b2dbb 100644 --- a/Analysis/config/args_520_beadfind.json +++ b/Analysis/config/args_520_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_520.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_521_beadfind.json b/Analysis/config/args_521_beadfind.json index db1e7f0a..b04c4fc6 100644 --- a/Analysis/config/args_521_beadfind.json +++ b/Analysis/config/args_521_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_521.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_522_beadfind.json b/Analysis/config/args_522_beadfind.json index 51b10819..65d3ddcd 100644 --- a/Analysis/config/args_522_beadfind.json +++ b/Analysis/config/args_522_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_522.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_530_beadfind.json b/Analysis/config/args_530_beadfind.json index 91177ecb..08b72012 100644 --- a/Analysis/config/args_530_beadfind.json +++ b/Analysis/config/args_530_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_530.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_540_beadfind.json b/Analysis/config/args_540_beadfind.json index 86536178..95cd65f0 100644 --- a/Analysis/config/args_540_beadfind.json +++ b/Analysis/config/args_540_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_540.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_541_beadfind.json b/Analysis/config/args_541_beadfind.json index b2c77b66..7f14e4ca 100644 --- a/Analysis/config/args_541_beadfind.json +++ b/Analysis/config/args_541_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_541.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_550_analysis.json b/Analysis/config/args_550_analysis.json index f2c87e80..c2e8a03f 100644 --- a/Analysis/config/args_550_analysis.json +++ b/Analysis/config/args_550_analysis.json @@ -39,7 +39,7 @@ "GlobalDefaultsForBkgModel" : { "barcode-spec-file" : "", "bkg-dont-emphasize-by-compression" : false, - "bkg-well-xtalk-name" : "/opt/ion/config/xtalk.p2.2.1.settings.20140120.json", + "bkg-well-xtalk-name" : "/opt/ion/config/xtalk.550.well.settings.json", "gopt" : "/opt/ion/config/gopt_550.param.json", "xtalk" : "disable" }, diff --git a/Analysis/config/args_550_beadfind.json b/Analysis/config/args_550_beadfind.json index e0dab861..07c39cdb 100644 --- a/Analysis/config/args_550_beadfind.json +++ b/Analysis/config/args_550_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_550.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_560_beadfind.json b/Analysis/config/args_560_beadfind.json index 8834ac79..00491c0c 100644 --- a/Analysis/config/args_560_beadfind.json +++ b/Analysis/config/args_560_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 10, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : true, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_560.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_900_beadfind.json b/Analysis/config/args_900_beadfind.json index 281d9398..505b4951 100644 --- a/Analysis/config/args_900_beadfind.json +++ b/Analysis/config/args_900_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_900.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_P1.0.19_beadfind.json b/Analysis/config/args_P1.0.19_beadfind.json index 9ebb525e..76a57eec 100644 --- a/Analysis/config/args_P1.0.19_beadfind.json +++ b/Analysis/config/args_P1.0.19_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_p1.0.19.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_P1.0.20_beadfind.json b/Analysis/config/args_P1.0.20_beadfind.json index 987fa6d0..108d961d 100644 --- a/Analysis/config/args_P1.0.20_beadfind.json +++ b/Analysis/config/args_P1.0.20_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_p1.0.20.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_P1.1.17_beadfind.json b/Analysis/config/args_P1.1.17_beadfind.json index 24a32856..c6e32dbc 100644 --- a/Analysis/config/args_P1.1.17_beadfind.json +++ b/Analysis/config/args_P1.1.17_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_p1.1.17.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_P1.1.541_beadfind.json b/Analysis/config/args_P1.1.541_beadfind.json index ef5c2745..c241619b 100644 --- a/Analysis/config/args_P1.1.541_beadfind.json +++ b/Analysis/config/args_P1.1.541_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, diff --git a/Analysis/config/args_P1.2.18_beadfind.json b/Analysis/config/args_P1.2.18_beadfind.json index e599586c..a31d2a54 100644 --- a/Analysis/config/args_P1.2.18_beadfind.json +++ b/Analysis/config/args_P1.2.18_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, diff --git a/Analysis/config/args_P2.0.1_beadfind.json b/Analysis/config/args_P2.0.1_beadfind.json index 9bc3bb30..628d1f77 100644 --- a/Analysis/config/args_P2.0.1_beadfind.json +++ b/Analysis/config/args_P2.0.1_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 80, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, diff --git a/Analysis/config/args_P2.1.1_beadfind.json b/Analysis/config/args_P2.1.1_beadfind.json index d7d03fac..3dbd2e8c 100644 --- a/Analysis/config/args_P2.1.1_beadfind.json +++ b/Analysis/config/args_P2.1.1_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, diff --git a/Analysis/config/args_P2.2.1_beadfind.json b/Analysis/config/args_P2.2.1_beadfind.json index c07e1838..047a0adc 100644 --- a/Analysis/config/args_P2.2.1_beadfind.json +++ b/Analysis/config/args_P2.2.1_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, diff --git a/Analysis/config/args_P2.2.2_beadfind.json b/Analysis/config/args_P2.2.2_beadfind.json index 17d24534..49a8808f 100644 --- a/Analysis/config/args_P2.2.2_beadfind.json +++ b/Analysis/config/args_P2.2.2_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_PQ.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], diff --git a/Analysis/config/args_P2.3.1_analysis.json b/Analysis/config/args_P2.3.1_analysis.json index ac10a548..d6549abc 100644 --- a/Analysis/config/args_P2.3.1_analysis.json +++ b/Analysis/config/args_P2.3.1_analysis.json @@ -39,7 +39,7 @@ "GlobalDefaultsForBkgModel" : { "barcode-spec-file" : "", "bkg-dont-emphasize-by-compression" : false, - "bkg-well-xtalk-name" : "/opt/ion/config/xtalk.p2.2.1.settings.20140120.json", + "bkg-well-xtalk-name" : "/opt/ion/config/xtalk.p2.3.1.well.settings.json", "gopt" : "/opt/ion/config/gopt_p1.1.17_ampliseq_exome.param.json", "xtalk" : "disable" }, @@ -72,7 +72,7 @@ "col-flicker-correct" : true, "col-flicker-correct-aggressive" : true, "col-flicker-correct-verbose" : false, - "corr-noise-correct" : true, + "corr-noise-correct" : false, "dat-postfix" : "dat", "flowtimeoffset" : 1000, "fluid-potential-correct" : false, @@ -190,6 +190,9 @@ "wells-save-queue-size" : 0 }, "ThumbnailControl" : { + "bkg-debug-files" : true, + "bkg-debug-param" : 3, + "gpuworkload" : 0.0, "mask-datacollect-exclude-regions" : false, "region-size" : [ 100, 100 ], "total-timeout" : 36000 diff --git a/Analysis/config/args_P2.3.1_beadfind.json b/Analysis/config/args_P2.3.1_beadfind.json index 25f8f1c5..6feb483e 100644 --- a/Analysis/config/args_P2.3.1_beadfind.json +++ b/Analysis/config/args_P2.3.1_beadfind.json @@ -11,6 +11,8 @@ "beadfind-lib-filt" : 1.0, "beadfind-lib-min-peak" : 10.0, "beadfind-mesh-step" : [ 108, 112 ], + "beadfind-acq-threshold" : [ -10, 3, 5, 500 ], + "beadfind-bf-threshold" : [ -5, 300, -20000, -10 ], "beadfind-min-tf-snr" : 4.0, "beadfind-minlive" : 0.00010, "beadfind-minlivesnr" : 3.0, @@ -31,7 +33,8 @@ "bfold" : true, "datacollect-gain-correction" : false, "noduds" : false, - "use-beadmask" : "" + "use-beadmask" : "", + "exclusion-mask" : "exclusionMask_550.txt" }, "DebugMe" : { "bkg-dbg-trace" : [], @@ -56,7 +59,7 @@ "col-flicker-correct" : true, "col-flicker-correct-aggressive" : true, "col-flicker-correct-verbose" : false, - "corr-noise-correct" : true, + "corr-noise-correct" : false, "dat-postfix" : "dat", "flowtimeoffset" : 1000, "fluid-potential-correct" : false, diff --git a/Analysis/config/exclusionMask_510.txt b/Analysis/config/exclusionMask_510.txt new file mode 100644 index 00000000..585d9601 --- /dev/null +++ b/Analysis/config/exclusionMask_510.txt @@ -0,0 +1,1729 @@ +7680 1728 +398 7288 +397 7288 +397 7289 +396 7290 +396 7290 +395 7291 +395 7291 +394 7292 +394 7293 +393 7293 +393 7294 +392 7294 +391 7295 +391 7296 +390 7296 +390 7297 +389 7298 +388 7298 +388 7299 +387 7300 +387 7300 +386 7301 +385 7301 +385 7302 +384 7303 +384 7303 +383 7304 +382 7305 +382 7305 +381 7306 +380 7307 +380 7307 +379 7308 +379 7309 +378 7309 +377 7310 +377 7311 +376 7311 +375 7312 +375 7312 +374 7313 +373 7314 +373 7314 +372 7315 +371 7316 +371 7316 +370 7317 +369 7318 +369 7318 +368 7319 +367 7320 +367 7320 +366 7321 +365 7322 +365 7322 +364 7323 +363 7324 +363 7324 +362 7325 +361 7326 +361 7326 +360 7327 +359 7327 +359 7328 +358 7329 +357 7329 +357 7330 +356 7331 +355 7331 +355 7332 +354 7333 +353 7333 +353 7334 +352 7334 +351 7335 +351 7336 +350 7336 +349 7337 +349 7338 +348 7338 +347 7339 +347 7339 +346 7340 +345 7341 +345 7341 +344 7342 +343 7343 +343 7343 +342 7344 +342 7344 +341 7345 +340 7346 +340 7346 +339 7347 +339 7347 +338 7348 +337 7349 +337 7349 +336 7350 +336 7351 +335 7351 +334 7352 +334 7352 +333 7353 +333 7354 +332 7354 +331 7355 +331 7355 +330 7356 +330 7357 +329 7357 +328 7358 +328 7358 +327 7359 +327 7360 +326 7360 +325 7361 +325 7361 +324 7362 +324 7363 +323 7363 +322 7364 +322 7364 +321 7365 +321 7366 +320 7366 +319 7367 +319 7367 +318 7368 +318 7369 +317 7369 +317 7370 +316 7370 +315 7371 +315 7371 +314 7372 +314 7373 +313 7373 +312 7374 +312 7374 +311 7375 +311 7375 +310 7376 +310 7377 +309 7377 +309 7378 +308 7378 +307 7379 +307 7379 +306 7380 +306 7380 +305 7381 +305 7382 +304 7382 +304 7383 +303 7383 +302 7384 +302 7384 +301 7385 +301 7385 +300 7386 +300 7386 +299 7387 +299 7387 +298 7388 +298 7389 +297 7389 +296 7390 +296 7390 +295 7391 +295 7391 +294 7392 +294 7392 +293 7393 +293 7393 +292 7394 +292 7394 +291 7395 +291 7395 +290 7396 +290 7396 +289 7397 +289 7397 +288 7398 +288 7398 +287 7399 +287 7399 +286 7400 +286 7400 +285 7401 +285 7401 +284 7402 +284 7402 +283 7403 +283 7403 +282 7404 +282 7404 +281 7405 +281 7405 +280 7406 +280 7406 +280 7406 +279 7407 +279 7407 +278 7408 +278 7408 +277 7409 +277 7409 +276 7410 +276 7410 +275 7411 +275 7411 +274 7412 +274 7412 +273 7413 +273 7413 +273 7414 +272 7414 +272 7414 +271 7415 +271 7415 +270 7416 +270 7416 +269 7417 +269 7417 +269 7418 +268 7418 +268 7419 +267 7419 +267 7419 +266 7420 +266 7420 +265 7421 +265 7421 +264 7422 +264 7422 +264 7423 +263 7423 +263 7424 +262 7424 +262 7424 +261 7425 +261 7425 +261 7426 +260 7426 +260 7427 +259 7427 +259 7428 +258 7428 +258 7428 +258 7429 +257 7429 +257 7430 +256 7430 +256 7431 +255 7431 +255 7431 +255 7432 +254 7432 +254 7433 +253 7433 +253 7434 +253 7434 +252 7435 +252 7435 +251 7435 +251 7436 +251 7436 +250 7437 +250 7437 +249 7438 +249 7438 +249 7439 +248 7439 +248 7439 +247 7440 +247 7440 +247 7441 +246 7441 +246 7442 +246 7442 +245 7442 +245 7443 +244 7443 +244 7444 +244 7444 +243 7445 +243 7445 +243 7446 +242 7446 +242 7446 +241 7447 +241 7447 +241 7448 +240 7448 +240 7449 +240 7449 +239 7450 +239 7450 +238 7450 +238 7451 +238 7451 +237 7452 +237 7452 +237 7453 +236 7453 +236 7453 +236 7454 +235 7454 +235 7455 +234 7455 +234 7456 +234 7456 +233 7456 +233 7457 +233 7457 +232 7458 +232 7458 +232 7459 +231 7459 +231 7459 +231 7460 +230 7460 +230 7461 +229 7461 +229 7461 +229 7462 +228 7462 +228 7463 +228 7463 +227 7463 +227 7464 +227 7464 +226 7465 +226 7465 +226 7465 +225 7466 +225 7466 +224 7467 +224 7467 +224 7467 +223 7468 +223 7468 +223 7469 +222 7469 +222 7469 +222 7470 +221 7470 +221 7470 +221 7471 +220 7471 +220 7472 +220 7472 +219 7472 +219 7473 +218 7473 +218 7473 +218 7474 +217 7474 +217 7474 +217 7475 +216 7475 +216 7476 +216 7476 +215 7476 +215 7477 +215 7477 +214 7477 +214 7478 +214 7478 +213 7478 +213 7479 +213 7479 +212 7479 +212 7480 +211 7480 +211 7480 +211 7481 +210 7481 +210 7481 +210 7481 +209 7482 +209 7482 +209 7482 +208 7483 +208 7483 +207 7483 +207 7484 +207 7484 +206 7484 +206 7485 +206 7485 +205 7485 +205 7485 +205 7486 +204 7486 +204 7486 +203 7487 +203 7487 +203 7487 +202 7487 +202 7488 +202 7488 +201 7488 +201 7489 +201 7489 +200 7489 +200 7489 +200 7490 +199 7490 +199 7490 +199 7491 +198 7491 +198 7491 +198 7491 +197 7492 +197 7492 +197 7492 +197 7493 +196 7493 +196 7493 +196 7493 +195 7494 +195 7494 +195 7494 +194 7494 +194 7495 +194 7495 +193 7495 +193 7495 +193 7496 +193 7496 +192 7496 +192 7496 +192 7497 +191 7497 +191 7497 +191 7497 +191 7498 +190 7498 +190 7498 +190 7498 +189 7499 +189 7499 +189 7499 +189 7499 +188 7500 +188 7500 +188 7500 +188 7500 +187 7500 +187 7501 +187 7501 +186 7501 +186 7501 +186 7502 +186 7502 +185 7502 +185 7502 +185 7502 +185 7503 +184 7503 +184 7503 +184 7503 +184 7503 +183 7504 +183 7504 +183 7504 +182 7504 +182 7504 +182 7505 +182 7505 +181 7505 +181 7505 +181 7505 +181 7506 +180 7506 +180 7506 +180 7506 +180 7506 +179 7507 +179 7507 +179 7507 +179 7507 +178 7507 +178 7507 +178 7508 +178 7508 +177 7508 +177 7508 +177 7508 +177 7509 +177 7509 +176 7509 +176 7509 +176 7509 +176 7509 +175 7510 +175 7510 +175 7510 +175 7510 +175 7510 +174 7511 +174 7511 +174 7511 +174 7511 +174 7511 +173 7511 +173 7512 +173 7512 +173 7512 +173 7512 +172 7512 +172 7512 +172 7513 +172 7513 +172 7513 +171 7513 +171 7513 +171 7513 +171 7514 +171 7514 +170 7514 +170 7514 +170 7514 +170 7514 +170 7515 +170 7515 +169 7515 +169 7515 +169 7515 +169 7515 +169 7516 +169 7516 +168 7516 +168 7516 +168 7516 +168 7516 +168 7516 +167 7517 +167 7517 +167 7517 +167 7517 +167 7517 +167 7517 +166 7517 +166 7518 +166 7518 +166 7518 +166 7518 +166 7518 +166 7518 +165 7518 +165 7519 +165 7519 +165 7519 +165 7519 +165 7519 +164 7519 +164 7519 +164 7519 +164 7520 +164 7520 +164 7520 +164 7520 +163 7520 +163 7520 +163 7520 +163 7521 +163 7521 +163 7521 +162 7521 +162 7521 +162 7521 +162 7521 +162 7522 +162 7522 +162 7522 +161 7522 +161 7522 +161 7522 +161 7522 +161 7522 +161 7523 +161 7523 +160 7523 +160 7523 +160 7523 +160 7523 +160 7523 +160 7524 +160 7524 +159 7524 +159 7524 +159 7524 +159 7524 +159 7524 +159 7525 +159 7525 +158 7525 +158 7525 +158 7525 +158 7525 +158 7525 +158 7526 +158 7526 +157 7526 +157 7526 +157 7526 +157 7526 +157 7526 +157 7527 +157 7527 +156 7527 +156 7527 +156 7527 +156 7527 +156 7527 +156 7528 +156 7528 +155 7528 +155 7528 +155 7528 +155 7528 +155 7528 +155 7529 +155 7529 +154 7529 +154 7529 +154 7529 +154 7529 +154 7529 +154 7530 +154 7530 +154 7530 +153 7530 +153 7530 +153 7530 +153 7530 +153 7530 +153 7531 +153 7531 +152 7531 +152 7531 +152 7531 +152 7531 +152 7531 +152 7532 +152 7532 +152 7532 +151 7532 +151 7532 +151 7532 +151 7532 +151 7533 +151 7533 +151 7533 +151 7533 +150 7533 +150 7533 +150 7533 +150 7534 +150 7534 +150 7534 +150 7534 +150 7534 +149 7534 +149 7535 +149 7535 +149 7535 +149 7535 +149 7535 +149 7535 +148 7535 +148 7536 +148 7536 +148 7536 +148 7536 +148 7536 +148 7536 +148 7537 +147 7537 +147 7537 +147 7537 +147 7537 +147 7537 +147 7537 +147 7538 +146 7538 +146 7538 +146 7538 +146 7538 +146 7538 +146 7539 +146 7539 +146 7539 +145 7539 +145 7539 +145 7539 +145 7540 +145 7540 +145 7540 +145 7540 +145 7540 +144 7540 +144 7540 +144 7541 +144 7541 +144 7541 +144 7541 +144 7541 +143 7541 +143 7541 +143 7542 +143 7542 +143 7542 +143 7542 +143 7542 +143 7542 +142 7542 +142 7543 +142 7543 +142 7543 +142 7543 +142 7543 +142 7543 +142 7543 +141 7544 +141 7544 +141 7544 +141 7544 +141 7544 +141 7544 +141 7544 +141 7545 +140 7545 +140 7545 +140 7545 +140 7545 +140 7545 +140 7546 +140 7546 +140 7546 +139 7546 +139 7546 +139 7546 +139 7547 +139 7547 +139 7547 +139 7547 +139 7547 +138 7547 +138 7548 +138 7548 +138 7548 +138 7548 +138 7548 +138 7548 +137 7549 +137 7549 +137 7549 +137 7549 +137 7549 +137 7549 +137 7550 +137 7550 +136 7550 +136 7550 +136 7550 +136 7550 +136 7551 +136 7551 +136 7551 +136 7551 +135 7551 +135 7551 +135 7552 +135 7552 +135 7552 +135 7552 +135 7552 +134 7552 +134 7553 +134 7553 +134 7553 +134 7553 +134 7553 +134 7553 +134 7554 +133 7554 +133 7554 +133 7554 +133 7554 +133 7555 +133 7555 +133 7555 +132 7555 +132 7555 +132 7555 +132 7556 +132 7556 +132 7556 +132 7556 +131 7556 +131 7556 +131 7556 +131 7557 +131 7557 +131 7557 +131 7557 +131 7557 +130 7557 +130 7558 +130 7558 +130 7558 +130 7558 +130 7559 +130 7559 +129 7559 +129 7559 +129 7560 +129 7560 +129 7560 +129 7560 +129 7560 +128 7561 +128 7561 +128 7561 +128 7561 +128 7561 +128 7561 +128 7562 +127 7562 +127 7562 +127 7562 +127 7562 +127 7562 +127 7562 +127 7562 +127 7562 +126 7562 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +126 7563 +125 7563 +125 7563 +125 7562 +125 7562 +125 7562 +125 7562 +125 7562 +125 7562 +125 7562 +125 7562 +125 7561 +125 7561 +125 7561 +125 7561 +125 7561 +125 7561 +125 7560 +125 7560 +125 7560 +125 7560 +125 7560 +126 7559 +126 7559 +126 7559 +126 7559 +126 7558 +126 7558 +126 7558 +126 7558 +126 7557 +126 7557 +126 7557 +126 7557 +127 7556 +127 7556 +127 7556 +127 7556 +127 7555 +127 7555 +127 7555 +127 7555 +127 7554 +128 7554 +128 7554 +128 7554 +128 7554 +128 7553 +128 7553 +128 7553 +129 7553 +129 7553 +129 7552 +129 7552 +129 7552 +129 7552 +129 7552 +129 7552 +130 7552 +130 7551 +130 7551 +130 7551 +130 7551 +130 7551 +130 7551 +130 7551 +131 7551 +131 7550 +131 7550 +131 7550 +131 7550 +131 7550 +131 7550 +131 7550 +132 7549 +132 7549 +132 7549 +132 7549 +132 7549 +132 7549 +132 7548 +132 7548 +132 7548 +133 7548 +133 7548 +133 7548 +133 7548 +133 7547 +133 7547 +133 7547 +133 7547 +133 7547 +134 7547 +134 7547 +134 7546 +134 7546 +134 7546 +134 7546 +134 7546 +134 7546 +135 7545 +135 7545 +135 7545 +135 7545 +135 7545 +135 7545 +135 7545 +135 7544 +136 7544 +136 7544 +136 7544 +136 7544 +136 7544 +136 7543 +136 7543 +136 7543 +136 7543 +137 7543 +137 7543 +137 7542 +137 7542 +137 7542 +137 7542 +137 7542 +137 7542 +138 7542 +138 7541 +138 7541 +138 7541 +138 7541 +138 7541 +138 7541 +138 7541 +139 7540 +139 7540 +139 7540 +139 7540 +139 7540 +139 7540 +139 7540 +139 7539 +140 7539 +140 7539 +140 7539 +140 7539 +140 7539 +140 7539 +140 7538 +140 7538 +141 7538 +141 7538 +141 7538 +141 7538 +141 7538 +141 7537 +141 7537 +142 7537 +142 7537 +142 7537 +142 7537 +142 7537 +142 7536 +142 7536 +142 7536 +143 7536 +143 7536 +143 7536 +143 7536 +143 7535 +143 7535 +143 7535 +143 7535 +143 7535 +144 7535 +144 7535 +144 7534 +144 7534 +144 7534 +144 7534 +144 7534 +144 7534 +145 7534 +145 7533 +145 7533 +145 7533 +145 7533 +145 7533 +145 7533 +145 7533 +146 7532 +146 7532 +146 7532 +146 7532 +146 7532 +146 7532 +146 7532 +146 7531 +147 7531 +147 7531 +147 7531 +147 7531 +147 7531 +147 7531 +147 7531 +147 7530 +148 7530 +148 7530 +148 7530 +148 7530 +148 7530 +148 7530 +148 7529 +149 7529 +149 7529 +149 7529 +149 7529 +149 7529 +149 7529 +149 7528 +150 7528 +150 7528 +150 7528 +150 7528 +150 7528 +150 7528 +150 7527 +150 7527 +151 7527 +151 7527 +151 7527 +151 7527 +151 7527 +151 7527 +151 7527 +152 7526 +152 7526 +152 7526 +152 7526 +152 7526 +152 7526 +152 7526 +152 7525 +153 7525 +153 7525 +153 7525 +153 7525 +153 7525 +153 7525 +153 7525 +153 7524 +154 7524 +154 7524 +154 7524 +154 7524 +154 7524 +154 7524 +154 7524 +154 7524 +155 7523 +155 7523 +155 7523 +155 7523 +155 7523 +155 7523 +155 7523 +155 7522 +156 7522 +156 7522 +156 7522 +156 7522 +156 7522 +156 7522 +156 7522 +157 7521 +157 7521 +157 7521 +157 7521 +157 7521 +157 7521 +157 7521 +157 7520 +158 7520 +158 7520 +158 7520 +158 7520 +158 7520 +158 7520 +158 7519 +158 7519 +159 7519 +159 7519 +159 7519 +159 7519 +159 7519 +159 7518 +159 7518 +160 7518 +160 7518 +160 7518 +160 7518 +160 7517 +160 7517 +160 7517 +161 7517 +161 7517 +161 7517 +161 7516 +161 7516 +161 7516 +161 7516 +162 7516 +162 7516 +162 7515 +162 7515 +162 7515 +162 7515 +162 7515 +162 7515 +163 7515 +163 7514 +163 7514 +163 7514 +163 7514 +163 7514 +164 7514 +164 7513 +164 7513 +164 7513 +164 7513 +164 7513 +164 7513 +165 7512 +165 7512 +165 7512 +165 7512 +165 7512 +165 7511 +166 7511 +166 7511 +166 7511 +166 7511 +166 7511 +166 7510 +167 7510 +167 7510 +167 7510 +167 7510 +167 7509 +167 7509 +168 7509 +168 7509 +168 7509 +168 7508 +168 7508 +168 7508 +169 7508 +169 7508 +169 7507 +169 7507 +169 7507 +169 7507 +170 7507 +170 7506 +170 7506 +170 7506 +170 7506 +171 7505 +171 7505 +171 7505 +171 7505 +171 7505 +172 7504 +172 7504 +172 7504 +172 7504 +172 7503 +173 7503 +173 7503 +173 7503 +173 7503 +173 7502 +174 7502 +174 7502 +174 7502 +174 7501 +174 7501 +175 7501 +175 7501 +175 7500 +175 7500 +176 7500 +176 7500 +176 7499 +176 7499 +176 7499 +177 7499 +177 7498 +177 7498 +177 7498 +178 7498 +178 7497 +178 7497 +178 7497 +179 7497 +179 7496 +179 7496 +179 7496 +180 7495 +180 7495 +180 7495 +180 7495 +181 7494 +181 7494 +181 7494 +181 7494 +182 7493 +182 7493 +182 7493 +182 7492 +183 7492 +183 7492 +183 7492 +183 7491 +184 7491 +184 7491 +184 7490 +185 7490 +185 7490 +185 7490 +185 7489 +186 7489 +186 7489 +186 7489 +187 7488 +187 7488 +187 7488 +188 7487 +188 7487 +188 7487 +189 7486 +189 7486 +189 7486 +189 7486 +190 7485 +190 7485 +190 7485 +191 7484 +191 7484 +191 7484 +192 7483 +192 7483 +192 7483 +193 7483 +193 7482 +193 7482 +194 7482 +194 7481 +194 7481 +195 7481 +195 7480 +195 7480 +196 7480 +196 7480 +196 7479 +197 7479 +197 7479 +197 7478 +198 7478 +198 7478 +198 7477 +199 7477 +199 7477 +200 7477 +200 7476 +200 7476 +201 7476 +201 7475 +201 7475 +202 7475 +202 7474 +202 7474 +203 7474 +203 7473 +204 7473 +204 7473 +204 7473 +205 7472 +205 7472 +205 7472 +206 7471 +206 7471 +206 7471 +207 7470 +207 7470 +208 7470 +208 7470 +208 7469 +209 7469 +209 7469 +210 7468 +210 7468 +210 7468 +211 7467 +211 7467 +211 7467 +212 7467 +212 7466 +213 7466 +213 7466 +213 7465 +214 7465 +214 7465 +215 7464 +215 7464 +215 7464 +216 7464 +216 7463 +217 7463 +217 7463 +217 7462 +218 7462 +218 7462 +219 7461 +219 7461 +219 7461 +220 7461 +220 7460 +221 7460 +221 7460 +221 7459 +222 7459 +222 7459 +223 7458 +223 7458 +224 7458 +224 7458 +224 7457 +225 7457 +225 7457 +226 7456 +226 7456 +227 7456 +227 7455 +227 7455 +228 7455 +228 7454 +229 7454 +229 7454 +229 7454 +230 7453 +230 7453 +231 7453 +231 7452 +231 7452 +232 7452 +232 7451 +233 7451 +233 7451 +234 7450 +234 7450 +234 7450 +235 7450 +235 7449 +236 7449 +236 7449 +237 7448 +237 7448 +237 7448 +238 7447 +238 7447 +239 7447 +239 7447 +239 7446 +240 7446 +240 7446 +241 7445 +241 7445 +241 7445 +242 7444 +242 7444 +243 7444 +243 7443 +243 7443 +244 7443 +244 7442 +245 7442 +245 7442 +245 7441 +246 7441 +246 7441 +247 7440 +247 7440 +247 7440 +248 7440 +248 7439 +249 7439 +249 7439 +249 7438 +250 7438 +250 7438 +251 7437 +251 7437 +251 7437 +252 7436 +252 7436 +253 7436 +253 7435 +253 7435 +254 7435 +254 7434 +255 7434 +255 7434 +255 7433 +256 7433 +256 7433 +257 7432 +257 7432 +257 7431 +258 7431 +258 7431 +259 7430 +259 7430 +259 7430 +260 7429 +260 7429 +261 7429 +261 7428 +261 7428 +262 7428 +262 7427 +262 7427 +263 7426 +263 7426 +264 7426 +264 7425 +264 7425 +265 7425 +265 7424 +266 7424 +266 7423 +266 7423 +267 7422 +267 7422 +268 7422 +268 7421 +268 7421 +269 7420 +269 7420 +270 7420 +270 7419 +270 7419 +271 7418 +271 7418 +271 7418 +272 7417 +272 7417 +273 7416 +273 7416 +273 7415 +274 7415 +274 7415 +275 7414 +275 7414 +275 7413 +276 7413 +276 7412 +277 7412 +277 7412 +277 7411 +278 7411 +278 7410 +279 7410 +279 7409 +279 7409 +280 7408 +280 7408 +281 7407 +281 7407 +282 7406 +282 7406 +282 7406 +283 7405 +283 7405 +284 7404 +284 7404 +285 7403 +285 7403 +285 7402 +286 7402 +286 7401 +287 7401 +287 7400 +288 7400 +288 7399 +288 7399 +289 7398 +289 7398 +290 7397 +290 7397 +291 7397 +291 7396 +292 7396 +292 7395 +292 7395 +293 7394 +293 7394 +294 7393 +294 7393 +295 7392 +295 7392 +296 7391 +296 7391 +296 7390 +297 7390 +297 7389 +298 7389 +298 7388 +299 7388 +299 7387 +300 7387 +300 7386 +301 7385 +301 7385 +302 7384 +302 7384 +303 7383 +303 7383 +304 7382 +304 7382 +305 7381 +305 7381 +305 7380 +306 7380 +306 7379 +307 7379 +307 7378 +308 7378 +308 7377 +309 7377 +309 7376 +310 7376 +310 7375 +311 7374 +311 7374 +312 7373 +312 7373 +313 7372 +313 7372 +314 7371 +314 7371 +315 7370 +315 7370 +316 7369 +316 7369 +317 7368 +317 7368 +318 7367 +318 7367 +319 7366 +319 7365 +320 7365 +320 7364 +321 7364 +321 7363 +322 7363 +323 7362 +323 7362 +324 7361 +324 7361 +325 7360 +325 7360 +326 7359 +326 7358 +327 7358 +327 7357 +328 7357 +328 7356 +329 7356 +330 7355 +330 7355 +331 7354 +331 7354 +332 7353 +332 7352 +333 7352 +333 7351 +334 7351 +334 7350 +335 7350 +336 7349 +336 7349 +337 7348 +337 7348 +338 7347 +338 7346 +339 7346 +339 7345 +340 7345 +341 7344 +341 7344 +342 7343 +342 7343 +343 7342 +343 7342 +344 7341 +344 7340 +345 7340 +346 7339 +346 7339 +347 7338 +347 7338 +348 7337 +348 7337 +349 7336 +350 7336 +350 7335 +351 7334 +351 7334 +352 7333 +352 7333 +353 7332 +354 7332 +354 7331 +355 7331 +355 7330 +356 7329 +357 7329 +357 7328 +358 7328 +358 7327 +359 7327 +359 7326 +360 7326 +361 7325 +361 7324 +362 7324 +362 7323 +363 7323 +364 7322 +364 7322 +365 7321 +365 7321 +366 7320 +367 7320 +367 7319 +368 7318 +368 7318 +369 7317 +370 7317 +370 7316 +371 7316 +371 7315 +372 7315 +373 7314 +373 7313 +374 7313 +374 7312 +375 7312 +376 7311 +376 7311 +377 7310 +378 7310 +378 7309 +379 7308 +380 7308 +380 7307 +381 7307 +381 7306 +382 7306 +383 7305 +383 7304 +384 7304 +385 7303 +385 7303 +386 7302 +387 7302 diff --git a/Analysis/config/exclusionMask_510.txt.png b/Analysis/config/exclusionMask_510.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..23497075a9c90db2290e0b85867d1b5bb24b4496 GIT binary patch literal 21093 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
Iuru!R_yF5|N=4BqpGn#^cKUcf> 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
Iuru!R_yF5|N=4BqpGn#^cKUcf> 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 literal 0 HcmV?d00001 diff --git a/Analysis/config/exclusionMask_540.txt b/Analysis/config/exclusionMask_540.txt index 12c125d3..f7b97bd1 100644 --- a/Analysis/config/exclusionMask_540.txt +++ b/Analysis/config/exclusionMask_540.txt @@ -1,10657 +1,10657 @@ 15456 10656 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -4898 5073 -4853 10304 -4808 10304 -4764 11047 -3850 11204 -3694 11603 -3598 11608 -3564 11763 -3516 11780 -3467 11797 -3418 11813 -3399 11830 -3392 11847 -3386 11864 -3380 11881 -3373 11897 -3367 11914 -3361 11931 -3355 11944 -3348 11953 -3342 11963 -3336 11972 -3330 11981 -3323 11990 -3317 12000 -3311 12009 -3305 12018 -3298 12028 -3292 12037 -3286 12046 -3280 12055 -3273 12065 -3267 12074 -3261 12083 -3254 12089 -3249 12096 -3244 12102 -3240 12109 -3235 12115 -3231 12122 -3227 12128 -3222 12134 -3218 12141 -3213 12147 -3209 12154 -3204 12160 -3200 12167 -3196 12173 -3191 12180 -3187 12186 -3182 12192 -3178 12199 -3173 12205 -3169 12212 -3165 12218 -3160 12225 -3156 12231 -3151 12237 -3147 12244 -3143 12250 -3138 12257 -3134 12263 -3129 12270 -3125 12276 -3120 12283 -3116 12289 -3112 12295 -3107 12302 -3103 12308 -3098 12315 -3094 12321 -3089 12328 -3085 12334 -3081 12340 -3077 12344 -3073 12349 -3068 12354 -3064 12359 -3060 12363 -3056 12368 -3052 12373 -3048 12378 -3044 12382 -3040 12387 -3035 12392 -3031 12397 -3027 12401 -3023 12406 -3019 12411 -3015 12416 -3011 12420 -3007 12425 -3002 12430 -2998 12435 -2994 12439 -2990 12444 -2986 12449 -2982 12454 -2978 12458 -2974 12463 -2969 12468 -2965 12473 -2961 12477 -2957 12482 -2953 12487 -2949 12491 -2945 12496 -2941 12501 -2936 12505 -2932 12509 -2928 12513 -2924 12517 -2920 12521 -2916 12524 -2912 12528 -2908 12532 -2903 12536 -2899 12540 -2895 12544 -2891 12547 -2887 12551 -2883 12555 -2879 12559 -2875 12563 -2871 12566 -2866 12570 -2861 12574 -2857 12578 -2852 12582 -2847 12585 -2842 12589 -2838 12593 -2833 12597 -2828 12601 -2824 12604 -2819 12608 -2814 12612 -2810 12616 -2805 12620 -2800 12623 -2796 12627 -2791 12631 -2786 12635 -2782 12639 -2777 12642 -2772 12645 -2768 12648 -2763 12651 -2758 12654 -2754 12657 -2749 12660 -2744 12663 -2739 12666 -2735 12669 -2730 12672 -2725 12675 -2721 12678 -2716 12681 -2711 12684 -2708 12687 -2705 12690 -2702 12694 -2699 12697 -2696 12700 -2693 12703 -2690 12706 -2687 12709 -2684 12712 -2681 12715 -2678 12718 -2675 12721 -2672 12724 -2669 12727 -2666 12730 -2663 12733 -2660 12736 -2657 12739 -2654 12742 -2651 12745 -2648 12748 -2645 12752 -2642 12755 -2639 12758 -2636 12761 -2633 12764 -2630 12767 -2627 12769 -2624 12772 -2621 12775 -2618 12777 -2615 12780 -2612 12783 -2609 12786 -2606 12788 -2603 12791 -2600 12794 -2597 12796 -2594 12799 -2591 12802 -2588 12804 -2585 12807 -2574 12810 -2569 12812 -2564 12815 -2560 12818 -2555 12821 -2550 12823 -2545 12826 -2542 12829 -2539 12831 -2537 12834 -2534 12837 -2531 12839 -2528 12842 -2525 12845 -2522 12847 -2520 12850 -2517 12853 -2514 12856 -2511 12858 -2508 12861 -2505 12864 -2503 12866 -2500 12869 -2497 12872 -2494 12874 -2492 12877 -2490 12880 -2488 12880 -2486 12880 -2484 12881 -2482 12884 -2480 12887 -2478 12890 -2476 12893 -2474 12896 -2472 12900 -2470 12903 -2468 12906 -2466 12909 -2464 12912 -2462 12915 -2460 12918 -2458 12921 -2456 12924 -2453 12927 -2451 12930 -2449 12933 -2447 12936 -2445 12939 -2443 12943 -2441 12946 -2439 12949 -2437 12952 -2435 12955 -2433 12958 -2431 12961 -2429 12964 -2427 12967 -2425 12970 -2423 12972 -2421 12975 -2419 12977 -2417 12980 -2415 12982 -2413 12984 -2411 12987 -2409 12989 -2407 12992 -2405 12994 -2403 12996 -2401 12999 -2399 13001 -2397 13004 -2395 13006 -2393 13008 -2391 13011 -2389 13013 -2387 13016 -2385 13018 -2383 13020 -2381 13023 -2379 13025 -2377 13028 -2375 13030 -2373 13032 -2371 13035 -2369 13037 -2367 13040 -2365 13042 -2363 13044 -2361 13047 -2359 13049 -2357 13052 -2355 13054 -2353 13056 -2351 13059 -2349 13061 -2347 13064 -2345 13066 -2343 13068 -2341 13071 -2339 13073 -2337 13076 -2335 13078 -2333 13080 -2331 13083 -2329 13085 -2327 13088 -2325 13090 -2323 13092 -2321 13095 -2319 13097 -2317 13100 -2315 13102 -2313 13104 -2311 13107 -2309 13109 -2307 13112 -2305 13114 -2303 13116 -2301 13119 -2298 13121 -2296 13124 -2294 13126 -2292 13128 -2290 13131 -2288 13133 -2286 13136 -2283 13138 -2281 13140 -2279 13143 -2277 13145 -2275 13148 -2273 13150 -2271 13152 -2268 13155 -2266 13157 -2264 13160 -2262 13162 -2260 13164 -2258 13167 -2256 13169 -2253 13172 -2251 13174 -2249 13176 -2247 13179 -2245 13181 -2243 13184 -2241 13186 -2238 13188 -2236 13191 -2234 13193 -2232 13195 -2230 13197 -2228 13199 -2226 13201 -2223 13203 -2221 13206 -2219 13208 -2217 13210 -2215 13212 -2213 13214 -2211 13216 -2208 13219 -2206 13221 -2204 13223 -2202 13225 -2200 13227 -2198 13229 -2196 13232 -2193 13234 -2191 13236 -2189 13238 -2187 13240 -2185 13242 -2183 13245 -2181 13247 -2179 13249 -2176 13251 -2174 13253 -2172 13255 -2170 13257 -2168 13260 -2166 13262 -2164 13264 -2161 13266 -2159 13268 -2157 13270 -2155 13273 -2153 13275 -2151 13277 -2149 13279 -2146 13281 -2144 13283 -2142 13286 -2140 13288 -2138 13290 -2136 13292 -2134 13294 -2131 13296 -2129 13298 -2127 13301 -2125 13303 -2123 13305 -2121 13307 -2119 13309 -2116 13311 -2114 13314 -2112 13316 -2110 13318 -2108 13320 -2106 13322 -2104 13324 -2101 13327 -2099 13329 -2097 13331 -2095 13333 -2093 13335 -2091 13337 -2089 13339 -2086 13342 -2084 13344 -2082 13346 -2080 13348 -2078 13350 -2076 13352 -2074 13355 -2071 13357 -2069 13359 -2067 13361 -2065 13363 -2063 13365 -2061 13368 -2059 13370 -2056 13372 -2054 13374 -2052 13376 -2050 13378 -2048 13381 -2046 13383 -2044 13385 -2041 13387 -2039 13389 -2037 13391 -2035 13393 -2033 13396 -2031 13398 -2029 13400 -2026 13402 -2024 13405 -2022 13407 -2020 13409 -2018 13412 -2016 13414 -2014 13416 -2011 13419 -2009 13421 -2007 13423 -2005 13425 -2003 13428 -2001 13430 -1999 13432 -1996 13435 -1994 13437 -1992 13439 -1990 13442 -1988 13444 -1986 13446 -1984 13448 -1981 13451 -1979 13453 -1977 13455 -1975 13458 -1973 13460 -1971 13462 -1969 13465 -1966 13467 -1964 13469 -1962 13471 -1960 13474 -1958 13476 -1956 13478 -1954 13481 -1952 13483 -1949 13485 -1947 13488 -1945 13490 -1943 13492 -1941 13494 -1939 13497 -1937 13499 -1934 13501 -1932 13504 -1930 13506 -1928 13508 -1926 13511 -1924 13513 -1922 13515 -1919 13517 -1917 13520 -1915 13522 -1913 13524 -1911 13527 -1909 13529 -1907 13531 -1904 13534 -1902 13536 -1900 13538 -1898 13540 -1896 13543 -1894 13545 -1892 13547 -1889 13550 -1887 13552 -1885 13555 -1883 13557 -1881 13559 -1879 13562 -1877 13564 -1874 13567 -1872 13569 -1870 13572 -1868 13574 -1866 13576 -1864 13579 -1862 13581 -1860 13584 -1858 13586 -1856 13588 -1854 13591 -1852 13593 -1850 13596 -1849 13598 -1847 13601 -1845 13603 -1843 13605 -1841 13608 -1839 13610 -1837 13613 -1835 13615 -1833 13618 -1831 13620 -1830 13622 -1828 13625 -1826 13627 -1824 13630 -1822 13632 -1820 13634 -1818 13637 -1816 13639 -1814 13642 -1813 13644 -1811 13647 -1809 13649 -1807 13651 -1805 13654 -1803 13656 -1801 13659 -1799 13661 -1797 13663 -1795 13666 -1794 13668 -1792 13671 -1790 13673 -1788 13676 -1786 13678 -1784 13680 -1782 13683 -1780 13685 -1778 13688 -1777 13690 -1775 13692 -1773 13695 -1771 13697 -1769 13700 -1767 13702 -1765 13705 -1763 13707 -1761 13709 -1759 13711 -1758 13712 -1756 13714 -1754 13715 -1752 13717 -1750 13719 -1748 13720 -1746 13722 -1744 13724 -1742 13725 -1741 13727 -1739 13729 -1737 13730 -1735 13732 -1733 13734 -1731 13735 -1729 13737 -1727 13739 -1725 13740 -1724 13742 -1722 13743 -1720 13745 -1718 13747 -1716 13748 -1714 13750 -1712 13752 -1710 13753 -1708 13755 -1706 13757 -1705 13758 -1703 13760 -1701 13762 -1699 13763 -1697 13765 -1695 13767 -1693 13768 -1691 13770 -1689 13771 -1688 13773 -1686 13775 -1684 13776 -1682 13778 -1680 13780 -1678 13781 -1676 13783 -1674 13785 -1672 13786 -1670 13788 -1669 13790 -1667 13791 -1665 13793 -1663 13795 -1661 13796 -1660 13798 -1658 13799 -1656 13801 -1654 13803 -1653 13804 -1651 13806 -1649 13808 -1647 13809 -1646 13811 -1644 13813 -1642 13814 -1640 13816 -1639 13818 -1637 13819 -1635 13821 -1633 13823 -1632 13824 -1630 13826 -1628 13827 -1626 13829 -1625 13831 -1623 13832 -1621 13834 -1619 13836 -1618 13837 -1616 13839 -1614 13841 -1612 13842 -1611 13844 -1609 13846 -1607 13847 -1605 13849 -1604 13851 -1602 13852 -1600 13854 -1598 13855 -1597 13857 -1595 13859 -1593 13860 -1591 13862 -1590 13864 -1588 13865 -1586 13867 -1584 13869 -1583 13870 -1581 13872 -1579 13874 -1577 13875 -1576 13877 -1574 13879 -1572 13880 -1570 13882 -1569 13883 -1567 13885 -1565 13887 -1563 13888 -1562 13890 -1560 13891 -1558 13893 -1556 13894 -1555 13896 -1553 13897 -1551 13898 -1549 13900 -1548 13901 -1546 13903 -1544 13904 -1542 13905 -1541 13907 -1539 13908 -1537 13910 -1535 13911 -1534 13912 -1532 13914 -1530 13915 -1528 13917 -1527 13918 -1525 13920 -1523 13921 -1521 13922 -1520 13924 -1518 13925 -1516 13927 -1514 13928 -1513 13929 -1511 13931 -1509 13932 -1507 13934 -1506 13935 -1505 13937 -1503 13938 -1502 13939 -1501 13941 -1499 13942 -1498 13944 -1497 13945 -1495 13946 -1494 13948 -1493 13949 -1491 13951 -1490 13952 -1489 13953 -1487 13955 -1486 13956 -1484 13958 -1483 13959 -1482 13961 -1480 13962 -1479 13963 -1478 13965 -1476 13966 -1475 13968 -1474 13969 -1472 13970 -1471 13972 -1470 13973 -1468 13975 -1467 13976 -1466 13977 -1464 13979 -1463 13980 -1462 13982 -1460 13983 -1459 13985 -1457 13986 -1456 13987 -1455 13989 -1453 13990 -1452 13992 -1451 13993 -1449 13994 -1448 13996 -1447 13997 -1445 13999 -1444 14000 -1443 14001 -1441 14003 -1440 14004 -1439 14006 -1437 14007 -1436 14009 -1434 14010 -1433 14011 -1432 14013 -1430 14014 -1429 14016 -1428 14017 -1426 14018 -1425 14020 -1424 14021 -1422 14023 -1421 14024 -1420 14025 -1418 14027 -1417 14028 -1416 14030 -1414 14031 -1413 14032 -1412 14033 -1410 14034 -1409 14035 -1407 14036 -1406 14038 -1405 14039 -1403 14040 -1402 14041 -1401 14042 -1399 14043 -1398 14044 -1397 14045 -1395 14047 -1394 14048 -1393 14049 -1391 14050 -1390 14051 -1389 14052 -1387 14053 -1386 14055 -1385 14056 -1384 14057 -1383 14058 -1382 14059 -1381 14060 -1380 14061 -1379 14062 -1378 14064 -1376 14065 -1375 14066 -1374 14067 -1373 14068 -1372 14069 -1371 14070 -1370 14072 -1369 14073 -1368 14074 -1366 14075 -1365 14076 -1364 14077 -1363 14078 -1362 14079 -1361 14081 -1360 14082 -1359 14083 -1358 14084 -1356 14085 -1355 14086 -1354 14087 -1353 14088 -1352 14090 -1351 14091 -1350 14092 -1349 14093 -1348 14094 -1347 14095 -1345 14096 -1344 14098 -1343 14099 -1342 14100 -1341 14101 -1340 14102 -1339 14103 -1338 14104 -1337 14105 -1335 14107 -1334 14108 -1333 14109 -1332 14110 -1331 14111 -1330 14112 -1329 14113 -1328 14115 -1327 14116 -1325 14117 -1324 14118 -1323 14119 -1322 14120 -1321 14121 -1320 14122 -1319 14123 -1318 14124 -1317 14125 -1316 14126 -1314 14127 -1313 14128 -1312 14128 -1311 14129 -1310 14130 -1309 14131 -1308 14132 -1307 14133 -1306 14134 -1304 14134 -1303 14135 -1302 14136 -1301 14137 -1300 14138 -1299 14139 -1298 14140 -1297 14141 -1296 14141 -1294 14142 -1293 14143 -1292 14144 -1291 14145 -1288 14146 -1287 14147 -1286 14147 -1285 14148 -1285 14149 -1284 14150 -1283 14151 -1282 14152 -1281 14153 -1280 14154 -1279 14154 -1278 14155 -1277 14156 -1276 14157 -1275 14158 -1274 14159 -1273 14160 -1272 14161 -1271 14161 -1270 14162 -1269 14165 -1268 14169 -1267 14170 -1266 14170 -1265 14171 -1264 14172 -1263 14173 -1262 14174 -1261 14174 -1260 14175 -1259 14176 -1258 14177 -1257 14178 -1256 14179 -1255 14179 -1254 14180 -1253 14181 -1252 14182 -1251 14183 -1250 14183 -1249 14184 -1248 14185 -1247 14186 -1246 14187 -1245 14187 -1244 14188 -1243 14189 -1242 14190 -1241 14191 -1240 14192 -1239 14192 -1238 14193 -1238 14194 -1237 14195 -1236 14196 -1235 14196 -1234 14197 -1233 14198 -1232 14199 -1231 14200 -1230 14201 -1229 14201 -1228 14202 -1228 14203 -1227 14204 -1226 14205 -1225 14205 -1224 14206 -1223 14207 -1222 14208 -1222 14209 -1221 14209 -1220 14210 -1219 14211 -1218 14212 -1217 14213 -1216 14214 -1215 14214 -1215 14215 -1214 14216 -1213 14217 -1212 14218 -1211 14218 -1210 14219 -1209 14220 -1209 14221 -1208 14222 -1207 14222 -1206 14223 -1205 14224 -1204 14225 -1203 14225 -1203 14226 -1202 14227 -1201 14227 -1200 14228 -1199 14229 -1198 14229 -1197 14230 -1196 14231 -1196 14231 -1195 14232 -1194 14233 -1193 14233 -1192 14234 -1191 14235 -1190 14235 -1190 14236 -1189 14237 -1188 14237 -1187 14238 -1186 14239 -1185 14239 -1184 14240 -1184 14241 -1183 14241 -1182 14242 -1181 14243 -1180 14243 -1179 14244 -1178 14245 -1177 14245 -1177 14246 -1176 14247 -1175 14247 -1174 14248 -1173 14249 -1172 14249 -1171 14250 -1171 14251 -1170 14251 -1169 14252 -1168 14253 -1167 14253 -1167 14254 -1166 14255 -1165 14255 -1164 14256 -1164 14257 -1163 14257 -1162 14258 -1162 14259 -1161 14259 -1160 14260 -1159 14261 -1159 14261 -1158 14262 -1157 14263 -1156 14263 -1156 14264 -1155 14265 -1154 14265 -1154 14266 -1153 14267 -1152 14267 -1151 14268 -1151 14269 -1150 14269 -1149 14270 -1148 14271 -1148 14271 -1147 14272 -1146 14273 -1146 14273 -1145 14274 -1144 14275 -1143 14275 -1143 14276 -1142 14277 -1141 14277 -1140 14278 -1140 14279 -1139 14279 -1138 14280 -1137 14281 -1137 14281 -1136 14282 -1135 14283 -1135 14283 -1134 14284 -1133 14284 -1132 14285 -1132 14286 -1131 14286 -1130 14287 -1129 14288 -1129 14288 -1128 14289 -1127 14290 -1127 14290 -1126 14291 -1125 14292 -1124 14292 -1124 14293 -1123 14294 -1122 14294 -1121 14295 -1121 14296 -1120 14296 -1119 14297 -1119 14298 -1118 14298 -1117 14299 -1116 14300 -1116 14300 -1115 14301 -1114 14302 -1113 14302 -1113 14303 -1112 14304 -1111 14304 -1111 14305 -1110 14306 -1109 14306 -1108 14307 -1108 14308 -1107 14308 -1106 14309 -1105 14310 -1105 14310 -1104 14311 -1103 14312 -1102 14312 -1102 14313 -1101 14314 -1100 14314 -1100 14315 -1099 14316 -1098 14316 -1097 14317 -1097 14318 -1096 14318 -1095 14319 -1094 14320 -1094 14320 -1093 14321 -1092 14322 -1092 14322 -1091 14323 -1090 14324 -1089 14324 -1089 14325 -1088 14326 -1087 14326 -1086 14327 -1086 14328 -1085 14328 -1084 14329 -1084 14330 -1083 14330 -1082 14331 -1081 14332 -1081 14332 -1080 14333 -1079 14334 -1079 14334 -1078 14335 -1077 14336 -1077 14336 -1076 14337 -1075 14338 -1075 14338 -1074 14339 -1073 14340 -1073 14340 -1072 14341 -1071 14342 -1071 14342 -1070 14343 -1069 14343 -1069 14344 -1068 14345 -1067 14345 -1067 14346 -1066 14347 -1065 14347 -1065 14348 -1064 14349 -1063 14349 -1062 14350 -1062 14350 -1061 14351 -1060 14352 -1060 14352 -1059 14353 -1058 14354 -1058 14354 -1057 14355 -1056 14355 -1056 14356 -1055 14357 -1054 14357 -1054 14358 -1053 14359 -1052 14359 -1052 14360 -1051 14360 -1050 14361 -1050 14362 -1049 14362 -1048 14363 -1048 14364 -1047 14364 -1046 14365 -1046 14366 -1045 14366 -1044 14367 -1044 14367 -1043 14368 -1042 14369 -1041 14369 -1041 14370 -1040 14371 -1039 14371 -1039 14372 -1038 14372 -1037 14373 -1037 14374 -1036 14374 -1035 14375 -1035 14376 -1034 14376 -1033 14377 -1033 14377 -1032 14378 -1031 14379 -1031 14379 -1030 14380 -1029 14381 -1029 14381 -1028 14382 -1027 14382 -1027 14383 -1026 14384 -1025 14384 -1025 14385 -1024 14386 -1023 14386 -1023 14387 -1022 14388 -1021 14388 -1020 14389 -1020 14389 -1019 14390 -1019 14391 -1018 14391 -1018 14392 -1017 14393 -1016 14393 -1016 14394 -1015 14394 -1015 14395 -1014 14396 -1013 14396 -1013 14397 -1012 14398 -1012 14398 -1011 14399 -1010 14399 -1010 14400 -1009 14401 -1009 14401 -1008 14402 -1008 14403 -1007 14403 -1006 14404 -1006 14405 -1005 14405 -1005 14406 -1004 14406 -1003 14407 -1003 14408 -1002 14408 -1002 14409 -1001 14410 -1001 14410 -1000 14411 -999 14411 -999 14412 -998 14413 -998 14413 -997 14414 -996 14415 -996 14415 -995 14416 -995 14416 -994 14417 -994 14418 -993 14418 -992 14419 -992 14420 -991 14420 -991 14421 -990 14422 -989 14422 -989 14423 -988 14423 -988 14424 -987 14425 -987 14425 -986 14426 -985 14427 -985 14427 -984 14428 -984 14428 -983 14429 -982 14430 -982 14430 -981 14431 -981 14432 -980 14432 -979 14433 -979 14434 -978 14434 -978 14435 -977 14435 -977 14436 -976 14437 -975 14437 -975 14429 -975 14430 -975 14430 -975 14431 -975 14432 -975 14432 -974 14433 -974 14434 -974 14435 -973 14435 -973 14436 -972 14437 -972 14437 -971 14438 -971 14439 -970 14439 -970 14440 -969 14441 -968 14441 -968 14442 -967 14443 -967 14443 -966 14444 -966 14445 -965 14445 -965 14446 -964 14447 -964 14447 -963 14448 -963 14449 -962 14450 -962 14450 -961 14451 -960 14452 -960 14452 -959 14453 -959 14454 -958 14454 -958 14455 -957 14456 -957 14456 -956 14457 -956 14458 -955 14458 -955 14459 -954 14460 -953 14460 -953 14461 -952 14462 -952 14463 -951 14463 -951 14464 -950 14465 -950 14465 -949 14466 -949 14467 -948 14467 -948 14468 -947 14469 -947 14469 -946 14470 -945 14471 -945 14471 -944 14472 -944 14473 -943 14473 -943 14474 -942 14475 -942 14476 -941 14476 -941 14477 -940 14478 -940 14478 -939 14479 -938 14480 -938 14480 -937 14481 -937 14482 -936 14482 -936 14483 -935 14484 -935 14484 -934 14485 -934 14486 -933 14486 -933 14487 -932 14488 -931 14489 -931 14489 -930 14490 -930 14491 -929 14491 -929 14492 -928 14493 -928 14493 -927 14494 -927 14495 -926 14495 -926 14496 -925 14497 -925 14497 -924 14498 -923 14499 -923 14499 -922 14500 -922 14501 -921 14502 -921 14502 -920 14503 -920 14504 -919 14504 -919 14505 -918 14506 -918 14506 -917 14507 -916 14508 -916 14508 -915 14509 -915 14510 -914 14510 -914 14511 -913 14512 -913 14512 -912 14513 -912 14514 -911 14515 -911 14515 -910 14516 -910 14517 -909 14517 -908 14518 -908 14519 -907 14519 -907 14520 -906 14521 -906 14521 -905 14522 -905 14523 -904 14523 -904 14524 -903 14525 -903 14525 -902 14526 -901 14527 -901 14527 -900 14528 -900 14529 -899 14530 -899 14530 -898 14531 -898 14532 -897 14532 -897 14533 -896 14534 -896 14534 -895 14535 -895 14536 -894 14536 -893 14537 -893 14538 -892 14538 -892 14539 -891 14540 -891 14540 -890 14541 -890 14542 -889 14543 -889 14543 -888 14544 -888 14545 -887 14545 -886 14546 -886 14546 -885 14547 -885 14547 -884 14548 -884 14549 -883 14549 -883 14550 -882 14550 -882 14551 -881 14551 -881 14552 -880 14553 -879 14553 -879 14554 -878 14554 -878 14555 -877 14555 -877 14556 -876 14556 -876 14557 -875 14558 -875 14558 -874 14559 -874 14559 -873 14560 -873 14560 -872 14561 -871 14562 -871 14562 -870 14563 -870 14563 -869 14564 -869 14564 -868 14565 -868 14565 -867 14566 -867 14567 -866 14567 -866 14568 -865 14568 -864 14569 -864 14569 -863 14570 -863 14571 -862 14571 -862 14572 -861 14572 -861 14573 -860 14573 -860 14574 -859 14575 -859 14575 -858 14576 -857 14576 -857 14577 -856 14577 -856 14578 -855 14578 -855 14579 -854 14580 -854 14580 -853 14581 -853 14581 -852 14582 -851 14582 -851 14583 -850 14584 -850 14584 -849 14585 -849 14585 -848 14586 -848 14586 -847 14587 -847 14587 -846 14588 -846 14589 -845 14589 -844 14590 -844 14590 -843 14591 -843 14591 -842 14592 -842 14593 -841 14593 -841 14594 -840 14594 -840 14595 -839 14595 -838 14596 -838 14596 -837 14597 -837 14598 -836 14598 -836 14599 -835 14599 -835 14600 -834 14600 -834 14601 -833 14602 -833 14602 -832 14603 -831 14603 -831 14604 -830 14604 -830 14605 -829 14605 -829 14606 -828 14607 -828 14607 -827 14608 -827 14608 -826 14609 -826 14609 -825 14610 -824 14611 -824 14611 -823 14612 -823 14612 -822 14613 -822 14613 -821 14614 -821 14614 -820 14615 -820 14616 -819 14616 -818 14617 -818 14617 -817 14618 -817 14618 -816 14619 -816 14620 -815 14620 -815 14621 -814 14621 -814 14622 -813 14622 -813 14623 -812 14624 -811 14624 -811 14625 -810 14625 -810 14626 -809 14626 -809 14627 -808 14627 -808 14628 -807 14629 -807 14629 -806 14630 -805 14630 -805 14631 -804 14631 -804 14632 -803 14633 -803 14633 -802 14634 -802 14634 -801 14635 -801 14635 -800 14636 -800 14636 -799 14637 -798 14638 -798 14638 -797 14639 -797 14639 -796 14640 -796 14640 -795 14641 -795 14642 -794 14642 -794 14643 -793 14643 -792 14644 -792 14644 -791 14645 -791 14645 -790 14646 -790 14647 -789 14647 -789 14648 -788 14648 -788 14649 -787 14650 -787 14650 -786 14651 -785 14651 -785 14652 -784 14652 -784 14653 -783 14654 -783 14654 -782 14655 -782 14655 -781 14656 -781 14656 -780 14657 -780 14658 -779 14658 -778 14659 -778 14659 -777 14660 -777 14660 -776 14661 -776 14662 -775 14662 -775 14663 -774 14663 -774 14664 -773 14664 -772 14665 -772 14666 -771 14666 -771 14667 -770 14667 -770 14668 -769 14669 -769 14669 -768 14670 -768 14670 -767 14671 -767 14671 -766 14672 -765 14673 -765 14673 -764 14674 -764 14674 -763 14675 -763 14675 -762 14676 -762 14677 -761 14677 -761 14678 -760 14678 -759 14679 -759 14679 -758 14680 -758 14681 -757 14681 -757 14682 -756 14682 -756 14683 -755 14683 -755 14684 -754 14685 -754 14685 -753 14686 -752 14686 -752 14687 -751 14688 -751 14688 -750 14689 -750 14689 -749 14690 -749 14690 -748 14691 -748 14692 -747 14692 -747 14693 -746 14693 -746 14694 -745 14694 -745 14695 -744 14696 -744 14696 -743 14697 -743 14697 -742 14698 -742 14698 -741 14699 -741 14700 -740 14700 -740 14701 -739 14701 -739 14702 -738 14702 -738 14703 -737 14704 -737 14704 -736 14705 -736 14705 -735 14706 -735 14707 -734 14707 -734 14708 -733 14708 -732 14709 -732 14709 -731 14710 -731 14711 -730 14711 -730 14712 -729 14712 -729 14713 -728 14713 -728 14714 -727 14714 -727 14715 -726 14715 -726 14716 -725 14716 -725 14717 -724 14717 -724 14717 -723 14718 -723 14718 -722 14719 -722 14719 -721 14720 -721 14720 -720 14721 -720 14721 -719 14721 -719 14722 -718 14722 -718 14723 -717 14723 -717 14724 -716 14724 -716 14724 -715 14725 -715 14725 -714 14726 -714 14726 -713 14727 -713 14727 -712 14728 -712 14728 -711 14728 -711 14729 -710 14729 -710 14730 -709 14730 -709 14731 -708 14731 -708 14731 -707 14732 -707 14732 -706 14733 -706 14733 -705 14734 -705 14734 -704 14735 -704 14735 -703 14735 -703 14736 -702 14736 -701 14737 -701 14737 -700 14738 -700 14738 -699 14738 -699 14739 -698 14739 -698 14740 -697 14740 -697 14741 -696 14741 -696 14741 -695 14742 -695 14742 -695 14743 -694 14743 -694 14744 -693 14744 -693 14745 -692 14745 -692 14745 -691 14746 -691 14746 -690 14747 -690 14747 -689 14748 -689 14748 -689 14748 -688 14749 -688 14749 -687 14750 -687 14750 -686 14751 -686 14751 -685 14752 -685 14752 -684 14752 -684 14753 -683 14753 -683 14754 -682 14754 -682 14755 -682 14755 -681 14755 -681 14756 -680 14756 -680 14757 -679 14757 -679 14758 -678 14758 -678 14759 -677 14759 -677 14759 -676 14760 -676 14760 -675 14761 -675 14761 -675 14762 -674 14762 -674 14762 -673 14763 -673 14763 -672 14764 -672 14764 -671 14765 -671 14765 -670 14765 -670 14766 -669 14766 -669 14767 -669 14767 -668 14768 -668 14768 -667 14769 -667 14769 -666 14769 -666 14770 -665 14770 -665 14771 -664 14771 -664 14772 -663 14772 -663 14772 -662 14773 -662 14773 -662 14774 -661 14774 -661 14775 -660 14775 -660 14776 -659 14776 -659 14776 -659 14777 -658 14777 -658 14778 -658 14778 -657 14778 -657 14779 -657 14779 -656 14780 -656 14780 -656 14780 -655 14781 -655 14781 -654 14781 -654 14782 -654 14782 -653 14783 -653 14783 -653 14783 -652 14784 -652 14784 -652 14784 -651 14785 -651 14785 -651 14786 -650 14786 -650 14786 -650 14787 -649 14787 -649 14788 -649 14788 -648 14788 -648 14789 -648 14789 -647 14789 -647 14790 -647 14790 -646 14791 -646 14791 -646 14791 -645 14792 -645 14792 -644 14793 -644 14793 -644 14793 -643 14794 -643 14794 -643 14794 -642 14795 -642 14795 -642 14796 -641 14796 -641 14796 -641 14797 -640 14797 -640 14797 -640 14798 -639 14798 -639 14799 -639 14799 -638 14799 -638 14800 -638 14800 -637 14801 -637 14801 -637 14801 -636 14802 -636 14802 -636 14802 -635 14803 -635 14803 -634 14804 -634 14804 -634 14804 -633 14805 -633 14805 -633 14806 -632 14806 -632 14806 -632 14807 -631 14807 -631 14807 -631 14808 -630 14808 -630 14809 -630 14809 -629 14809 -629 14810 -629 14810 -628 14810 -628 14811 -628 14811 -627 14812 -627 14812 -627 14812 -626 14813 -626 14813 -625 14814 -625 14814 -625 14814 -624 14815 -624 14815 -624 14815 -623 14816 -623 14816 -623 14817 -622 14817 -622 14817 -622 14818 -621 14818 -621 14819 -621 14819 -620 14819 -620 14820 -620 14820 -620 14820 -619 14821 -619 14821 -619 14822 -618 14822 -618 14822 -618 14823 -618 14823 -617 14823 -617 14824 -617 14824 -616 14825 -616 14825 -616 14825 -616 14826 -615 14826 -615 14827 -615 14827 -614 14827 -614 14828 -614 14828 -614 14828 -613 14829 -613 14829 -613 14830 -613 14830 -612 14830 -612 14831 -612 14831 -611 14832 -611 14832 -611 14832 -611 14833 -610 14833 -610 14833 -610 14834 -609 14834 -609 14835 -609 14835 -609 14835 -608 14836 -608 14836 -608 14837 -607 14837 -607 14837 -607 14838 -607 14838 -606 14839 -606 14839 -606 14839 -605 14840 -605 14840 -605 14840 -605 14841 -604 14841 -604 14842 -604 14842 -603 14842 -603 14843 -603 14843 -603 14844 -602 14844 -602 14844 -602 14845 -602 14845 -601 14846 -601 14846 -601 14846 -600 14847 -600 14847 -600 14848 -600 14848 -599 14848 -599 14849 -599 14849 -598 14849 -598 14850 -598 14850 -598 14851 -597 14851 -597 14851 -597 14852 -596 14852 -596 14853 -596 14853 -596 14853 -595 14854 -595 14854 -595 14855 -594 14855 -594 14855 -594 14856 -594 14856 -593 14857 -593 14857 -593 14857 -593 14858 -592 14858 -592 14858 -592 14859 -591 14859 -591 14860 -591 14860 -591 14860 -590 14861 -590 14861 -590 14862 -589 14862 -589 14862 -589 14863 -589 14863 -588 14864 -588 14864 -588 14864 -587 14865 -587 14865 -587 14866 -587 14866 -586 14866 -586 14867 -586 14867 -585 14867 -585 14868 -585 14868 -585 14869 -584 14869 -584 14869 -584 14870 -583 14870 -583 14871 -583 14871 -583 14871 -582 14872 -582 14872 -582 14873 -582 14873 -581 14873 -581 14874 -581 14874 -580 14874 -580 14875 -580 14875 -580 14876 -579 14876 -579 14876 -579 14877 -578 14877 -578 14878 -578 14878 -578 14878 -577 14879 -577 14879 -577 14880 -576 14880 -576 14880 -576 14881 -576 14881 -575 14882 -575 14882 -575 14882 -574 14883 -574 14883 -574 14883 -574 14884 -573 14884 -573 14885 -573 14885 -572 14885 -572 14886 -572 14886 -572 14887 -571 14887 -571 14887 -571 14888 -571 14888 -570 14889 -570 14889 -570 14889 -569 14890 -569 14890 -569 14891 -569 14891 -568 14891 -568 14892 -568 14892 -567 14893 -567 14893 -567 14893 -567 14894 -566 14894 -566 14895 -566 14895 -565 14896 -565 14896 -565 14896 -565 14897 -564 14897 -564 14898 -564 14898 -563 14899 -563 14899 -563 14899 -563 14900 -562 14900 -562 14901 -562 14901 -562 14902 -561 14902 -561 14902 -561 14903 -560 14903 -560 14904 -560 14904 -560 14905 -559 14905 -559 14906 -559 14906 -558 14906 -558 14907 -558 14907 -558 14908 -557 14908 -557 14909 -557 14909 -556 14909 -556 14910 -556 14910 -556 14911 -555 14911 -555 14912 -555 14912 -554 14912 -554 14913 -554 14913 -554 14914 -553 14914 -553 14915 -553 14915 -553 14916 -552 14916 -552 14916 -552 14917 -551 14917 -551 14918 -551 14918 -551 14919 -550 14919 -550 14919 -550 14920 -549 14920 -549 14921 -549 14921 -549 14922 -548 14922 -548 14922 -548 14923 -547 14923 -547 14924 -547 14924 -547 14925 -546 14925 -546 14925 -546 14926 -545 14926 -545 14927 -545 14927 -545 14928 -544 14928 -544 14929 -544 14929 -544 14929 -543 14930 -543 14930 -543 14931 -542 14931 -542 14932 -542 14932 -542 14932 -541 14933 -541 14933 -541 14934 -540 14934 -540 14935 -540 14935 -540 14935 -539 14936 -539 14936 -539 14937 -538 14937 -538 14938 -538 14938 -538 14939 -537 14939 -537 14939 -537 14940 -537 14940 -536 14941 -536 14941 -536 14942 -535 14942 -535 14942 -535 14943 -535 14943 -534 14944 -534 14944 -534 14944 -533 14944 -533 14945 -533 14945 -533 14945 -532 14945 -532 14945 -532 14946 -531 14946 -531 14946 -531 14946 -531 14947 -530 14947 -530 14947 -530 14947 -529 14947 -529 14948 -529 14948 -529 14948 -528 14948 -528 14949 -528 14949 -528 14949 -527 14949 -527 14949 -527 14950 -526 14950 -526 14950 -526 14950 -525 14951 -525 14951 -525 14951 -525 14951 -524 14951 -524 14952 -524 14952 -523 14952 -523 14952 -523 14952 -522 14953 -522 14953 -522 14953 -521 14953 -521 14954 -521 14954 -520 14954 -520 14954 -520 14954 -519 14955 -519 14955 -519 14955 -518 14955 -518 14956 -518 14956 -518 14956 -517 14956 -517 14956 -517 14957 -516 14957 -516 14957 -516 14957 -515 14957 -515 14958 -515 14958 -514 14958 -514 14958 -514 14959 -513 14959 -513 14959 -513 14959 -512 14959 -512 14960 -512 14960 -511 14960 -511 14960 -511 14961 -510 14961 -510 14961 -510 14961 -510 14961 -509 14962 -509 14962 -509 14962 -508 14962 -508 14962 -508 14963 -507 14963 -507 14963 -507 14963 -506 14964 -506 14964 -506 14964 -505 14964 -505 14964 -505 14965 -504 14965 -504 14965 -504 14965 -503 14966 -503 14966 -503 14966 -503 14966 -502 14966 -502 14967 -502 14967 -501 14967 -501 14967 -501 14967 -500 14968 -500 14968 -500 14968 -499 14968 -499 14969 -499 14969 -498 14969 -498 14969 -498 14969 -498 14970 -498 14970 -497 14970 -497 14970 -497 14971 -497 14971 -496 14971 -496 14971 -496 14971 -496 14971 -495 14972 -495 14972 -495 14972 -495 14972 -495 14972 -494 14972 -494 14972 -494 14973 -494 14973 -493 14973 -493 14973 -493 14973 -493 14973 -492 14973 -492 14974 -492 14974 -492 14974 -492 14974 -491 14974 -491 14974 -491 14974 -491 14975 -490 14975 -490 14975 -490 14975 -490 14975 -489 14975 -489 14976 -489 14976 -489 14976 -488 14976 -488 14976 -488 14976 -488 14976 -488 14977 -487 14977 -487 14977 -487 14977 -487 14977 -486 14977 -486 14977 -486 14978 -486 14978 -485 14978 -485 14978 -485 14978 -485 14978 -485 14979 -484 14979 -484 14979 -484 14979 -484 14979 -483 14979 -483 14979 -483 14980 -483 14980 -482 14980 -482 14980 -482 14980 -482 14980 -482 14980 -481 14981 -481 14981 -481 14981 -481 14981 -480 14981 -480 14981 -480 14981 -480 14982 -479 14982 -479 14982 -479 14982 -479 14982 -478 14982 -478 14983 -478 14983 -478 14983 -478 14983 -477 14983 -477 14983 -477 14983 -477 14984 -476 14984 -476 14984 -476 14984 -476 14984 -475 14984 -475 14984 -475 14985 -475 14985 -475 14985 -474 14985 -474 14985 -474 14985 -474 14986 -473 14986 -473 14986 -473 14986 -473 14986 -472 14986 -472 14986 -472 14987 -472 14987 -472 14987 -471 14987 -471 14987 -471 14987 -471 14987 -471 14988 -470 14988 -470 14988 -470 14988 -470 14988 -470 14988 -470 14988 -469 14989 -469 14989 -469 14989 -469 14989 -469 14989 -468 14989 -468 14990 -468 14990 -468 14990 -468 14990 -468 14990 -467 14990 -467 14990 -467 14991 -467 14991 -467 14991 -466 14991 -466 14991 -466 14991 -466 14991 -466 14992 -466 14992 -465 14992 -465 14992 -465 14992 -465 14992 -465 14993 -464 14993 -464 14993 -464 14993 -464 14993 -464 14993 -464 14993 -463 14994 -463 14994 -463 14994 -463 14994 -463 14994 -462 14994 -462 14994 -462 14995 -462 14995 -462 14995 -462 14995 -461 14995 -461 14995 -461 14995 -461 14996 -461 14996 -460 14996 -460 14996 -460 14996 -460 14996 -460 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14998 -459 14998 -459 14998 -459 14998 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 15000 -459 15000 -459 15000 -459 15000 -458 15000 -458 15000 -458 15000 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15002 -458 15002 -458 15002 -457 15002 -457 15002 -457 15002 -457 15003 -457 15003 -457 15003 -457 15003 -457 15003 -456 15003 -456 15003 -456 15004 -456 15004 -456 15004 -456 15004 -456 15004 -455 15004 -455 15004 -455 15005 -455 15005 -455 15005 -455 15005 -455 15005 -454 15005 -454 15006 -454 15006 -454 15006 -454 15006 -454 15006 -454 15006 -453 15006 -453 15007 -453 15007 -453 15007 -453 15007 -453 15007 -453 15007 -452 15007 -452 15008 -452 15008 -452 15008 -452 15008 -452 15008 -452 15008 -451 15008 -451 15009 -451 15009 -451 15009 -451 15009 -451 15009 -451 15009 -451 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15013 -447 15013 -447 15013 -447 15013 -447 15013 -447 15013 -447 15014 -447 15014 -446 15014 -446 15014 -446 15014 -446 15014 -446 15014 -446 15015 -446 15015 -445 15015 -445 15015 -445 15015 -445 15015 -445 15015 -445 15016 -445 15016 -445 15016 -444 15016 -444 15016 -444 15016 -444 15016 -444 15017 -444 15017 -444 15017 -443 15017 -443 15017 -443 15017 -443 15018 -443 15018 -443 15018 -443 15018 -442 15018 -442 15018 -442 15018 -442 15019 -442 15019 -442 15019 -442 15019 -441 15019 -441 15019 -441 15019 -441 15020 -441 15020 -441 15020 -441 15020 -440 15020 -440 15020 -440 15021 -440 15021 -440 15021 -440 15021 -440 15021 -439 15021 -439 15021 -439 15022 -439 15022 -439 15022 -439 15022 -439 15022 -439 15022 -438 15022 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -437 15023 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -433 15028 -433 15028 -433 15028 -433 15028 -433 15028 -433 15028 -433 15029 -433 15029 -432 15029 -432 15029 -432 15029 -432 15029 -432 15029 -432 15030 -432 15030 -431 15030 -431 15030 -431 15030 -431 15030 -431 15030 -431 15031 -431 15031 -430 15031 -430 15031 -430 15031 -430 15031 -430 15032 -430 15032 -430 15032 -429 15032 -429 15032 -429 15032 -429 15032 -429 15033 -429 15033 -429 15033 -428 15033 -428 15033 -428 15033 -428 15033 -428 15034 -428 15034 -428 15034 -427 15034 -427 15034 -427 15034 -427 15034 -427 15035 -427 15035 -427 15035 -427 15035 -426 15035 -426 15035 -426 15036 -426 15036 -426 15036 -426 15036 -426 15036 -425 15036 -425 15036 -425 15037 -425 15037 -425 15037 -425 15037 -425 15037 -424 15037 -424 15037 -424 15038 -424 15038 -424 15038 -424 15038 -424 15038 -423 15038 -423 15038 -423 15039 -423 15039 -423 15039 -423 15039 -423 15039 -422 15039 -422 15040 -422 15040 -422 15040 -422 15040 -422 15040 -422 15040 -421 15040 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -419 15043 -419 15043 -419 15043 -419 15043 -419 15043 -419 15043 -419 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15046 -416 15046 -416 15046 -416 15046 -416 15046 -416 15046 -416 15047 -416 15047 -415 15047 -415 15047 -415 15047 -415 15047 -415 15048 -415 15048 -415 15048 -415 15048 -414 15048 -414 15048 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -413 15049 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -412 15050 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15054 -410 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15055 -409 15055 -409 15055 -408 15055 -408 15055 -408 15055 -408 15056 -408 15056 -408 15056 -408 15056 -407 15056 -407 15056 -407 15057 -407 15057 -407 15057 -407 15057 -407 15057 -406 15057 -406 15058 -406 15058 -406 15058 -406 15058 -406 15058 -406 15058 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -404 15060 -404 15060 -404 15060 -404 15060 -404 15060 -404 15060 -404 15061 -403 15061 -403 15061 -403 15061 -403 15061 -403 15061 -403 15062 -403 15062 -403 15062 -402 15062 -402 15062 -402 15062 -402 15063 -402 15063 -402 15063 -402 15063 -402 15063 -401 15063 -401 15063 -401 15064 -401 15064 -401 15064 -401 15064 -401 15064 -401 15064 -401 15065 -400 15065 -400 15065 -400 15065 -400 15065 -400 15065 -400 15066 -400 15066 -400 15066 -399 15066 -399 15066 -399 15066 -399 15067 -399 15067 -399 15067 -399 15067 -399 15067 -399 15067 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15069 -397 15069 -397 15069 -397 15069 -397 15069 -397 15069 -397 15070 -397 15070 -397 15070 -397 15070 -396 15070 -396 15070 -396 15071 -396 15071 -396 15071 -396 15071 -396 15071 -396 15071 -396 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15073 -395 15073 -394 15073 -394 15073 -394 15073 -394 15073 -394 15074 -394 15074 -394 15074 -394 15074 -394 15074 -393 15074 -393 15075 -393 15075 -393 15075 -393 15075 -393 15075 -393 15075 -393 15076 -392 15076 -392 15076 -392 15076 -392 15076 -392 15076 -392 15077 -392 15077 -392 15077 -392 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15078 -391 15078 -391 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -388 15079 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -387 15080 -387 15080 -387 15080 -387 15080 -387 15081 -387 15081 -387 15081 -387 15081 -387 15081 -386 15081 -386 15081 -386 15081 -386 15081 -386 15081 -386 15082 -386 15082 -386 15082 -386 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -383 15083 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -382 15084 -382 15084 -382 15084 -382 15085 -382 15085 -382 15085 -382 15085 -382 15085 -382 15085 -381 15085 -381 15085 -381 15085 -381 15085 -381 15085 -381 15086 -381 15086 -381 15086 -381 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15087 -380 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -377 15088 -377 15088 -377 15088 -377 15089 -377 15089 -377 15089 -377 15089 -377 15089 -377 15089 -376 15089 -376 15089 -376 15089 -376 15089 -376 15090 -376 15090 -376 15090 -376 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15091 -375 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -372 15092 -372 15092 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -371 15093 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -370 15094 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15099 -367 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15100 -366 15100 -365 15100 -365 15100 -365 15100 -365 15100 -365 15100 -365 15101 -365 15101 -365 15101 -365 15101 -364 15101 -364 15101 -364 15101 -364 15101 -364 15102 -364 15102 -363 15102 -363 15102 -363 15102 -363 15102 -363 15102 -362 15102 -362 15103 -362 15103 -362 15103 -362 15103 -362 15103 -361 15103 -361 15103 -361 15103 -361 15104 -361 15104 -360 15104 -360 15104 -360 15104 -360 15104 -360 15104 -360 15104 -359 15105 -359 15105 -359 15105 -359 15105 -359 15105 -358 15105 -358 15105 -358 15105 -358 15106 -358 15106 -357 15106 -357 15106 -357 15106 -357 15106 -357 15106 -357 15106 -356 15107 -356 15107 -356 15107 -356 15107 -356 15107 -355 15107 -355 15107 -355 15107 -355 15108 -355 15108 -355 15108 -354 15108 -354 15108 -354 15108 -354 15108 -354 15109 -353 15109 -353 15109 -353 15109 -353 15109 -353 15109 -353 15109 -352 15109 -352 15110 -352 15110 -352 15110 -352 15110 -351 15110 -351 15110 -351 15110 -351 15110 -351 15111 -350 15111 -350 15111 -350 15111 -350 15111 -350 15111 -350 15111 -349 15111 -349 15112 -349 15112 -349 15112 -349 15112 -348 15112 -348 15112 -348 15112 -348 15112 -348 15113 -348 15113 -347 15113 -347 15113 -347 15113 -347 15113 -347 15113 -346 15113 -346 15114 -346 15114 -346 15114 -346 15114 -346 15114 -345 15114 -345 15114 -345 15114 -345 15115 -345 15115 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -342 15116 -342 15116 -342 15117 -342 15117 -342 15117 -341 15117 -341 15117 -341 15117 -341 15117 -341 15117 -341 15118 -340 15118 -340 15118 -340 15118 -340 15118 -340 15118 -339 15118 -339 15119 -339 15119 -339 15119 -339 15119 -339 15119 -338 15119 -338 15120 -338 15120 -338 15120 -338 15120 -337 15120 -337 15120 -337 15121 -337 15121 -337 15121 -336 15121 -336 15121 -336 15121 -336 15122 -336 15122 -336 15122 -336 15122 -336 15122 -335 15122 -335 15123 -335 15123 -335 15123 -335 15123 -335 15123 -335 15123 -335 15124 -335 15124 -335 15124 -335 15124 -335 15124 -334 15124 -334 15125 -334 15125 -334 15125 -334 15125 -334 15125 -334 15125 -334 15126 -334 15126 -334 15126 -334 15126 -334 15126 -334 15126 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15128 -333 15128 -333 15128 -333 15128 -333 15128 -333 15128 -332 15129 -332 15129 -332 15129 -332 15129 -332 15129 -332 15129 -332 15130 -332 15130 -332 15130 -332 15130 -332 15130 -332 15130 -332 15131 -331 15131 -331 15131 -331 15131 -331 15131 -331 15131 -331 15132 -331 15132 -331 15132 -331 15132 -331 15132 -331 15132 -331 15133 -330 15133 -330 15133 -330 15133 -330 15133 -330 15133 -330 15134 -330 15134 -330 15134 -330 15134 -330 15134 -330 15134 -330 15135 -330 15135 -329 15135 -329 15135 -329 15135 -329 15135 -329 15136 -329 15136 -329 15136 -329 15136 -329 15136 -329 15136 -329 15137 -329 15137 -328 15137 -328 15137 -328 15137 -328 15137 -328 15138 -328 15138 -328 15138 -328 15138 -328 15138 -328 15138 -328 15139 -328 15139 -328 15139 -327 15139 -327 15139 -327 15139 -327 15140 -327 15140 -327 15140 -327 15140 -327 15140 -327 15140 -327 15141 -327 15141 -327 15141 -326 15141 -326 15141 -326 15141 -326 15142 -326 15142 -326 15142 -326 15142 -326 15142 -326 15142 -326 15143 -326 15143 -326 15143 -325 15143 -325 15143 -325 15143 -325 15144 -325 15144 -325 15144 -325 15144 -325 15144 -325 15144 -325 15145 -325 15145 -325 15145 -325 15145 -324 15145 -324 15145 -324 15146 -324 15146 -324 15146 -324 15146 -324 15146 -324 15146 -324 15147 -324 15147 -324 15147 -324 15147 -323 15147 -323 15147 -323 15148 -323 15148 -323 15148 -323 15148 -323 15148 -323 15148 -323 15149 -323 15149 -323 15149 -323 15149 -323 15149 -322 15149 -322 15150 -322 15150 -322 15150 -322 15150 -322 15150 -322 15150 -322 15151 -322 15151 -322 15151 -322 15151 -322 15151 -321 15151 -321 15151 -321 15152 -321 15152 -321 15152 -321 15152 -321 15152 -321 15152 -321 15153 -321 15153 -321 15153 -321 15153 -321 15153 -320 15153 -320 15153 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15155 -319 15155 -319 15155 -319 15155 -319 15155 -319 15155 -319 15156 -319 15156 -319 15156 -319 15156 -318 15156 -318 15156 -318 15156 -318 15157 -318 15157 -318 15157 -318 15157 -318 15157 -317 15157 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15160 -316 15160 -315 15160 -315 15160 -315 15160 -315 15160 -315 15161 -315 15161 -315 15161 -315 15161 -315 15161 -314 15161 -314 15161 -314 15162 -314 15162 -314 15162 -314 15162 -314 15162 -314 15162 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15164 -313 15164 -312 15164 -312 15164 -312 15164 -312 15164 -312 15164 -312 15165 -312 15165 -312 15165 -311 15165 -311 15165 -311 15165 -311 15166 -311 15166 -311 15166 -311 15166 -311 15166 -311 15166 -310 15166 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15168 -309 15168 -309 15168 -309 15168 -309 15168 -309 15168 -309 15169 -309 15169 -309 15169 -308 15169 -308 15169 -308 15169 -308 15169 -308 15170 -308 15170 -308 15170 -308 15170 -308 15170 -307 15170 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15173 -306 15173 -305 15173 -305 15173 -305 15173 -305 15173 -305 15174 -305 15174 -305 15174 -305 15174 -304 15174 -304 15174 -304 15174 -304 15175 -304 15175 -304 15175 -304 15175 -304 15175 -304 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -293 15175 -293 15175 -293 15175 -293 15175 -293 15178 -292 15178 -288 15178 -288 15178 -288 15178 -288 15178 -288 15178 -288 15178 -287 15178 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -286 15179 -286 15179 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -285 15180 -285 15180 -285 15180 -285 15181 -285 15181 -285 15181 -285 15181 -285 15181 -285 15181 -284 15181 -284 15181 -284 15181 -284 15181 -284 15181 -284 15182 -284 15182 -284 15182 -284 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15183 -283 15183 -283 15183 -283 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15184 -282 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -278 15186 -278 15186 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -277 15187 -277 15187 -277 15187 -277 15187 -277 15188 -277 15188 -277 15188 -277 15188 -277 15188 -277 15188 -276 15188 -276 15188 -276 15188 -276 15188 -276 15189 -276 15189 -276 15189 -276 15189 -276 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15190 -275 15190 -275 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15191 -274 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -271 15192 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -270 15193 -270 15193 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -269 15194 -269 15194 -269 15194 -269 15195 -269 15195 -269 15195 -269 15195 -269 15195 -269 15195 -268 15195 -268 15195 -268 15195 -268 15195 -268 15196 -268 15196 -268 15196 -268 15196 -268 15196 -268 15196 -267 15196 -267 15196 -267 15196 -267 15197 -267 15197 -267 15197 -267 15197 -267 15197 -267 15197 -266 15197 -266 15197 -266 15197 -266 15198 -266 15198 -266 15198 -266 15198 -266 15198 -266 15198 -265 15198 -265 15198 -265 15198 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -264 15199 -264 15199 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -263 15200 -263 15200 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -262 15201 -262 15201 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -261 15202 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -260 15203 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -259 15204 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -258 15205 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -257 15206 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -256 15207 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15216 -249 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15217 -248 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15219 -246 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15220 -245 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15221 -244 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15222 -243 15222 -243 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15223 -242 15223 -242 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15224 -241 15224 -241 15224 -240 15224 -240 15224 -240 15224 -240 15224 -240 15224 -240 15224 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -238 15225 -238 15225 -238 15226 -238 15226 -238 15226 -238 15226 -237 15226 -237 15226 -237 15226 -237 15226 -237 15226 -237 15227 -236 15227 -236 15227 -236 15227 -236 15227 -236 15227 -236 15227 -235 15227 -235 15227 -235 15228 -235 15228 -235 15228 -235 15228 -234 15228 -234 15228 -234 15228 -234 15228 -234 15228 -234 15229 -234 15229 -233 15229 -233 15229 -233 15229 -233 15229 -233 15229 -233 15229 -232 15229 -232 15229 -232 15230 -232 15230 -232 15230 -232 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -228 15232 -228 15232 -228 15232 -228 15233 -228 15233 -228 15233 -227 15233 -227 15233 -227 15233 -227 15233 -227 15233 -227 15233 -226 15234 -226 15234 -226 15234 -226 15234 -226 15234 -226 15234 -225 15234 -225 15234 -225 15234 -225 15235 -225 15235 -225 15235 -225 15235 -224 15235 -224 15235 -224 15235 -224 15235 -224 15236 -224 15236 -223 15236 -223 15236 -223 15236 -223 15236 -223 15236 -223 15236 -222 15236 -222 15237 -222 15237 -222 15237 -222 15237 -222 15237 -221 15237 -221 15237 -221 15237 -221 15237 -221 15238 -221 15238 -221 15238 -220 15238 -220 15238 -220 15238 -220 15238 -220 15238 -220 15239 -219 15239 -219 15239 -219 15239 -219 15239 -219 15239 -219 15239 -218 15239 -218 15239 -218 15240 -218 15240 -218 15240 -218 15240 -217 15240 -217 15240 -217 15240 -217 15240 -217 15240 -217 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -215 15241 -215 15242 -215 15242 -215 15242 -215 15242 -215 15242 -214 15242 -214 15242 -214 15242 -214 15243 -214 15243 -214 15243 -213 15243 -213 15243 -213 15243 -213 15243 -213 15243 -213 15243 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -211 15244 -211 15244 -211 15245 -211 15245 -211 15245 -211 15245 -210 15245 -210 15245 -210 15245 -210 15245 -210 15245 -210 15246 -209 15246 -209 15246 -209 15246 -209 15246 -209 15246 -209 15246 -208 15246 -208 15247 -208 15247 -208 15247 -208 15247 -207 15247 -207 15247 -207 15247 -207 15247 -207 15247 -207 15248 -206 15248 -206 15248 -206 15248 -206 15248 -206 15248 -206 15248 -205 15248 -205 15248 -205 15249 -205 15249 -205 15249 -204 15249 -204 15249 -204 15249 -204 15249 -204 15249 -204 15250 -203 15250 -203 15250 -203 15250 -203 15250 -203 15250 -202 15250 -202 15250 -202 15250 -202 15251 -202 15251 -202 15251 -201 15251 -201 15251 -201 15251 -201 15251 -201 15251 -200 15251 -200 15252 -200 15252 -200 15252 -200 15252 -200 15252 -199 15252 -199 15252 -199 15252 -199 15252 -199 15253 -199 15253 -198 15253 -198 15253 -198 15253 -198 15253 -198 15253 -197 15253 -197 15254 -197 15254 -197 15254 -197 15254 -197 15254 -196 15254 -196 15254 -196 15254 -196 15254 -196 15255 -195 15255 -195 15255 -195 15255 -195 15255 -195 15255 -195 15255 -194 15255 -194 15255 -194 15256 -194 15256 -194 15256 -193 15256 -193 15256 -193 15256 -193 15256 -193 15256 -193 15256 -192 15257 -192 15257 -192 15257 -192 15257 -192 15257 -191 15257 -191 15257 -191 15257 -191 15258 -191 15258 -191 15258 -190 15258 -190 15258 -190 15258 -190 15258 -190 15258 -190 15258 -189 15259 -189 15259 -189 15259 -189 15259 -189 15259 -188 15259 -188 15259 -188 15259 -188 15259 -188 15260 -188 15260 -187 15260 -187 15260 -187 15260 -187 15260 -187 15260 -186 15260 -186 15261 -186 15261 -186 15261 -186 15261 -186 15261 -185 15261 -185 15261 -185 15261 -185 15261 -185 15261 -184 15262 -184 15262 -184 15262 -184 15262 -184 15262 -184 15262 -183 15262 -183 15262 -183 15262 -183 15262 -183 15262 -183 15262 -182 15262 -182 15263 -182 15263 -182 15263 -182 15263 -181 15263 -181 15263 -181 15263 -181 15263 -181 15263 -181 15263 -180 15263 -180 15263 -180 15263 -180 15264 -180 15264 -179 15264 -179 15264 -179 15264 -179 15264 -179 15264 -179 15264 -178 15264 -178 15264 -178 15264 -178 15264 -178 15264 -177 15264 -177 15265 -177 15265 -177 15265 -177 15265 -177 15265 -176 15265 -176 15265 -176 15265 -176 15265 -176 15265 -176 15265 -175 15265 -175 15265 -175 15266 -175 15266 -175 15266 -174 15266 -174 15266 -174 15266 -174 15266 -174 15266 -174 15266 -173 15266 -173 15266 -173 15266 -173 15266 -173 15267 -172 15267 -172 15267 -172 15267 -172 15267 -172 15267 -172 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -169 15268 -169 15268 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -168 15269 -168 15269 -168 15269 -168 15269 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15271 -167 15271 -167 15271 -167 15271 -167 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15272 -166 15272 -166 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -163 15273 -163 15273 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -162 15274 -162 15274 -162 15274 -162 15274 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15276 -161 15276 -161 15276 -161 15276 -161 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15277 -160 15277 -160 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -157 15278 -157 15278 -157 15278 -157 15278 -157 15278 -157 15279 -157 15279 -157 15279 -157 15279 -157 15279 -157 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15280 -156 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -154 15280 -154 15280 -154 15280 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15282 -153 15282 -153 15282 -153 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -150 15283 -150 15283 -150 15283 -150 15283 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -148 15288 -148 15288 -147 15288 -147 15288 -147 15288 -146 15288 -146 15288 -146 15288 -145 15288 -145 15288 -145 15288 -144 15288 -144 15288 -144 15288 -143 15288 -143 15289 -142 15289 -142 15289 -142 15289 -141 15289 -141 15289 -141 15289 -140 15289 -140 15289 -140 15289 -139 15289 -139 15289 -139 15289 -138 15290 -138 15290 -137 15290 -137 15290 -137 15290 -136 15290 -136 15290 -136 15290 -135 15290 -135 15290 -135 15290 -134 15290 -134 15291 -134 15291 -133 15291 -133 15291 -133 15291 -132 15291 -132 15291 -131 15291 -131 15291 -131 15291 -130 15291 -130 15292 -130 15292 -129 15292 -129 15292 -129 15292 -128 15292 -128 15292 -128 15292 -127 15292 -127 15292 -126 15292 -126 15292 -126 15293 -125 15293 -125 15293 -125 15293 -124 15293 -124 15293 -124 15293 -123 15293 -123 15293 -123 15293 -122 15293 -122 15293 -121 15294 -121 15294 -121 15294 -120 15294 -120 15294 -120 15294 -119 15294 -119 15294 -119 15294 -118 15294 -118 15294 -118 15294 -117 15295 -117 15295 -117 15295 -117 15295 -117 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15296 -116 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -114 15296 -114 15296 -114 15296 -114 15297 -114 15297 -114 15297 -114 15297 -114 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -111 15298 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -110 15299 -110 15299 -110 15300 -110 15300 -110 15300 -110 15300 -110 15300 -110 15300 -109 15300 -109 15300 -109 15300 -109 15301 -109 15301 -109 15301 -109 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15302 -108 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15303 -106 15303 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -105 15302 -105 15302 -105 15301 -105 15301 -105 15301 -105 15301 -105 15301 -104 15301 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -108 15303 -108 15303 -108 15303 -108 15303 -108 15303 -109 15303 -109 15303 -109 15303 -109 15303 -109 15303 -109 15303 -110 15303 -110 15303 -110 15303 -110 15303 -110 15303 -111 15303 -111 15303 -111 15303 -111 15303 -111 15303 -111 15303 -112 15303 -112 15303 -112 15303 -112 15303 -112 15303 -113 15303 -113 15303 -113 15303 -113 15303 -113 15303 -113 15303 -114 15303 -114 15303 -114 15303 -114 15303 -114 15303 -114 15303 -115 15303 -115 15303 -115 15303 -115 15303 -115 15303 -116 15303 -116 15303 -116 15303 -116 15303 -116 15303 -116 15303 -117 15303 -117 15303 -117 15303 -117 15303 -117 15303 -118 15303 -118 15302 -118 15302 -118 15302 -118 15302 -119 15302 -119 15302 -119 15302 -119 15302 -119 15302 -120 15302 -120 15302 -120 15302 -120 15302 -121 15302 -121 15302 -121 15302 -121 15302 -121 15302 -122 15302 -122 15302 -122 15302 -122 15302 -123 15302 -123 15302 -123 15302 -123 15302 -123 15302 -124 15302 -124 15302 -124 15302 -124 15302 -125 15301 -125 15301 -125 15301 -125 15301 -125 15301 -126 15301 -126 15301 -126 15301 -126 15301 -127 15301 -127 15301 -127 15301 -127 15301 -127 15301 -128 15301 -128 15301 -128 15301 -128 15301 -129 15301 -129 15301 -129 15301 -129 15301 -129 15301 -130 15301 -130 15301 -130 15301 -130 15301 -131 15301 -131 15301 -131 15301 -131 15300 -132 15300 -132 15300 -132 15300 -132 15300 -132 15300 -133 15300 -133 15300 -133 15300 -133 15300 -134 15300 -134 15300 -134 15300 -134 15300 -134 15300 -135 15300 -135 15300 -135 15300 -135 15300 -136 15300 -136 15300 -136 15300 -136 15300 -136 15300 -137 15300 -137 15300 -137 15300 -137 15300 -138 15300 -138 15300 -138 15300 -138 15299 -138 15299 -139 15299 -139 15299 -139 15299 -139 15299 -140 15299 -140 15299 -140 15299 -140 15299 -140 15299 -141 15299 -141 15299 -141 15298 -141 15298 -142 15298 -142 15298 -142 15298 -142 15298 -142 15298 -143 15298 -143 15298 -143 15298 -143 15298 -144 15297 -144 15297 -144 15297 -144 15297 -144 15297 -145 15297 -145 15297 -145 15297 -145 15297 -146 15297 -146 15297 -146 15296 -146 15296 -147 15296 -147 15296 -147 15296 -147 15296 -147 15296 -148 15296 -148 15296 -148 15296 -148 15296 -149 15295 -149 15295 -149 15295 -149 15295 -149 15295 -150 15295 -150 15295 -150 15295 -150 15295 -150 15295 -151 15295 -151 15295 -151 15294 -151 15294 -151 15294 -151 15294 -152 15294 -152 15294 -152 15294 -152 15294 -152 15294 -152 15294 -153 15294 -153 15293 -153 15293 -153 15293 -153 15293 -154 15293 -154 15293 -154 15293 -154 15293 -154 15293 -154 15293 -155 15293 -155 15292 -155 15292 -155 15292 -155 15292 -155 15292 -156 15292 -156 15292 -156 15292 -156 15292 -156 15292 -157 15292 -157 15291 -157 15291 -157 15291 -157 15291 -157 15291 -158 15291 -158 15291 -158 15291 -158 15291 -158 15291 -158 15291 -159 15290 -159 15290 -159 15290 -159 15290 -159 15290 -159 15290 -160 15290 -160 15290 -160 15290 -160 15290 -160 15290 -161 15290 -161 15289 -161 15289 -161 15289 -161 15289 -161 15289 -162 15289 -162 15289 -162 15289 -162 15289 -162 15289 -162 15289 -163 15288 -163 15288 -163 15288 -163 15288 -163 15288 -164 15288 -164 15288 -164 15288 -164 15288 -164 15288 -164 15288 -165 15287 -165 15287 -165 15287 -165 15287 -165 15287 -165 15287 -166 15287 -166 15287 -166 15287 -166 15287 -166 15287 -167 15286 -167 15286 -167 15286 -167 15286 -167 15286 -167 15286 -168 15286 -168 15286 -168 15286 -168 15286 -168 15286 -168 15285 -169 15285 -169 15285 -169 15285 -169 15285 -169 15285 -170 15285 -170 15285 -170 15285 -170 15285 -170 15285 -170 15285 -171 15284 -171 15284 -171 15284 -171 15284 -171 15284 -171 15284 -172 15284 -172 15284 -172 15284 -172 15284 -172 15284 -173 15283 -173 15283 -173 15283 -173 15283 -173 15283 -173 15283 -174 15283 -174 15283 -174 15283 -174 15283 -174 15283 -174 15283 -175 15282 -175 15282 -175 15282 -175 15282 -175 15282 -176 15282 -176 15282 -176 15282 -176 15282 -176 15282 -176 15282 -177 15282 -177 15281 -177 15281 -177 15281 -177 15281 -177 15281 -178 15281 -178 15281 -178 15281 -178 15281 -178 15281 -179 15281 -179 15281 -179 15280 -179 15280 -179 15280 -179 15280 -180 15280 -180 15280 -180 15280 -180 15280 -180 15280 -180 15280 -181 15280 -181 15279 -181 15279 -181 15279 -181 15279 -182 15279 -182 15279 -182 15279 -182 15279 -182 15279 -182 15279 -183 15279 -183 15279 -183 15278 -183 15278 -183 15278 -183 15278 -184 15278 -184 15278 -184 15278 -184 15278 -184 15278 -184 15278 -185 15278 -185 15278 -185 15277 -185 15277 -185 15277 -186 15277 -186 15277 -186 15277 -186 15277 -186 15277 -186 15277 -187 15277 -187 15277 -187 15276 -187 15276 -187 15276 -187 15276 -188 15276 -188 15276 -188 15276 -188 15276 -188 15276 -189 15276 -189 15276 -189 15276 -189 15275 -189 15275 -189 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -191 15275 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -192 15274 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15270 -194 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15269 -195 15269 -195 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15268 -196 15268 -196 15268 -196 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15267 -197 15267 -197 15267 -197 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15266 -198 15266 -198 15266 -198 15266 -198 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15265 -199 15265 -199 15265 -199 15265 -199 15265 -199 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -201 15264 -201 15264 -201 15264 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15261 -202 15261 -202 15261 -202 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -204 15260 -204 15260 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15257 -205 15257 -205 15257 -205 15257 -205 15257 -206 15257 -206 15257 -206 15257 -206 15257 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -207 15256 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15254 -207 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15253 -208 15253 -208 15253 -208 15253 -208 15253 -209 15253 -209 15253 -209 15253 -209 15253 -209 15252 -209 15252 -209 15252 -209 15252 -209 15252 -209 15252 -210 15252 -210 15252 -210 15252 -210 15251 -210 15251 -210 15251 -210 15251 -210 15251 -210 15251 -211 15251 -211 15251 -211 15251 -211 15250 -211 15250 -211 15250 -211 15250 -211 15250 -211 15250 -212 15250 -212 15250 -212 15250 -212 15250 -212 15249 -212 15249 -212 15249 -212 15249 -212 15249 -213 15249 -213 15249 -213 15249 -213 15249 -213 15248 -213 15248 -213 15248 -213 15248 -213 15248 -214 15248 -214 15248 -214 15248 -214 15248 -214 15247 -214 15247 -214 15247 -214 15247 -214 15247 -215 15247 -215 15247 -215 15247 -215 15247 -215 15246 -215 15246 -215 15246 -215 15246 -215 15246 -216 15246 -216 15246 -216 15246 -216 15246 -216 15245 -216 15245 -216 15245 -216 15245 -216 15245 -217 15245 -217 15245 -217 15245 -217 15245 -217 15245 -217 15244 -217 15244 -217 15244 -217 15244 -217 15244 -218 15244 -218 15244 -218 15244 -218 15244 -218 15243 -218 15243 -218 15243 -218 15243 -218 15243 -219 15243 -219 15243 -219 15243 -219 15243 -219 15242 -219 15242 -219 15242 -219 15242 -219 15242 -220 15242 -220 15242 -220 15242 -220 15242 -220 15241 -220 15241 -220 15241 -220 15241 -220 15241 -221 15241 -221 15241 -221 15241 -221 15241 -221 15240 -221 15240 -221 15240 -221 15240 -221 15240 -222 15240 -222 15240 -222 15240 -222 15240 -222 15240 -222 15239 -222 15239 -222 15239 -222 15239 -223 15239 -223 15239 -223 15239 -223 15239 -223 15239 -223 15238 -223 15238 -223 15238 -223 15238 -224 15238 -224 15238 -224 15238 -224 15238 -224 15238 -224 15237 -224 15237 -224 15237 -224 15237 -225 15237 -225 15237 -225 15237 -225 15237 -225 15237 -225 15236 -225 15236 -225 15236 -225 15236 -225 15236 -226 15236 -226 15236 -226 15236 -226 15236 -226 15235 -226 15235 -226 15235 -226 15235 -226 15235 -227 15235 -227 15235 -227 15235 -227 15235 -227 15235 -227 15234 -227 15234 -227 15234 -227 15234 -228 15234 -228 15234 -228 15234 -228 15234 -228 15234 -228 15233 -228 15233 -228 15233 -228 15233 -228 15233 -229 15233 -229 15233 -229 15233 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -230 15232 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15230 -230 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15229 -231 15229 -231 15229 -231 15229 -232 15229 -232 15229 -232 15229 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15225 -234 15225 -234 15225 -235 15225 -235 15225 -235 15225 -235 15225 -235 15224 -235 15224 -235 15224 -235 15224 -235 15224 -235 15224 -236 15224 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15221 -237 15221 -237 15221 -237 15221 -238 15221 -238 15221 -238 15221 -238 15220 -238 15220 -238 15220 -238 15220 -238 15220 -238 15220 -239 15220 -239 15220 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15217 -240 15217 -240 15217 -241 15217 -241 15217 -241 15217 -241 15217 -241 15216 -241 15216 -241 15216 -241 15216 -241 15216 -241 15216 -242 15216 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15213 -243 15213 -244 15213 -244 15213 -244 15213 -244 15213 -244 15213 -244 15212 -244 15212 -244 15212 -244 15212 -244 15212 -245 15212 -245 15212 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15209 -246 15209 -246 15209 -247 15209 -247 15209 -247 15209 -247 15209 -247 15208 -247 15208 -247 15208 -247 15208 -247 15208 -248 15208 -248 15208 -248 15208 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15203 -251 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -257 15199 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -258 15198 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -259 15197 -259 15197 -259 15196 -259 15196 -259 15196 -259 15196 -259 15196 -260 15196 -260 15196 -260 15196 -260 15195 -260 15195 -260 15195 -260 15195 -260 15195 -261 15195 -261 15195 -261 15195 -261 15195 -261 15194 -261 15194 -261 15194 -261 15194 -262 15194 -262 15194 -262 15194 -262 15194 -262 15193 -262 15193 -262 15193 -262 15193 -263 15193 -263 15193 -263 15193 -263 15193 -263 15193 -263 15192 -263 15192 -263 15192 -264 15192 -264 15192 -264 15192 -264 15192 -264 15192 -264 15191 -264 15191 -264 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15190 -265 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -270 15187 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -271 15186 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -272 15185 -272 15185 -272 15184 -272 15184 -272 15184 -272 15184 -272 15184 -272 15184 -273 15184 -273 15184 -273 15183 -273 15183 -273 15183 -273 15183 -273 15183 -274 15183 -274 15183 -274 15183 -274 15183 -274 15182 -274 15182 -274 15182 -274 15182 -275 15182 -275 15182 -275 15182 -275 15182 -275 15181 -275 15181 -275 15181 -275 15181 -276 15181 -276 15181 -276 15181 -276 15181 -276 15181 -276 15180 -276 15180 -276 15180 -277 15180 -277 15180 -277 15180 -277 15180 -277 15180 -277 15179 -277 15179 -277 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15178 -278 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15177 -279 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -282 15175 -282 15175 -282 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15174 -283 15174 -283 15174 -283 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15173 -284 15173 -284 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15172 -285 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -291 15168 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -292 15167 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -293 15166 -293 15166 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -294 15165 -294 15165 -294 15165 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -295 15164 -295 15164 -295 15164 -295 15164 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -296 15163 -296 15163 -296 15163 -296 15163 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -297 15162 -297 15162 -297 15162 -297 15162 -297 15162 -297 15161 -297 15161 -297 15161 -297 15161 -297 15161 -297 15161 -298 15161 -298 15161 -298 15161 -298 15161 -298 15161 -298 15160 -298 15160 -298 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15159 -299 15159 -299 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -303 15157 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -304 15156 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -305 15155 -305 15155 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -306 15154 -306 15154 -306 15154 -306 15153 -306 15153 -306 15153 -306 15153 -306 15153 -307 15153 -307 15153 -307 15153 -307 15153 -307 15152 -307 15152 -307 15152 -307 15152 -307 15152 -308 15152 -308 15152 -308 15152 -308 15152 -308 15152 -308 15151 -308 15151 -308 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15150 -309 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -313 15148 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -314 15147 -314 15147 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -315 15146 -315 15146 -315 15145 -315 15145 -315 15145 -315 15145 -315 15145 -315 15145 -316 15145 -316 15145 -316 15145 -316 15145 -316 15144 -316 15144 -316 15144 -316 15144 -316 15144 -317 15144 -317 15144 -317 15144 -317 15144 -317 15143 -317 15143 -317 15143 -317 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15142 -318 15142 -318 15142 -319 15142 -319 15142 -319 15142 -319 15142 -319 15142 -319 15141 -319 15141 -319 15141 -320 15141 -320 15141 -320 15141 -320 15141 -320 15141 -320 15140 -320 15140 -320 15140 -320 15140 -321 15140 -321 15140 -321 15140 -321 15140 -321 15139 -321 15139 -321 15139 -321 15139 -322 15139 -322 15139 -322 15139 -322 15139 -322 15138 -322 15138 -322 15138 -322 15138 -322 15138 -323 15138 -323 15138 -323 15137 -323 15137 -323 15137 -323 15137 -323 15137 -323 15137 -324 15137 -324 15137 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -325 15136 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -326 15135 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15128 -331 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15127 -332 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15126 -333 15126 -333 15126 -333 15126 -334 15126 -334 15126 -334 15126 -334 15126 -334 15125 -334 15125 -334 15125 -334 15125 -335 15125 -335 15125 -335 15125 -335 15125 -335 15124 -335 15124 -335 15124 -335 15124 -335 15124 -336 15124 -336 15124 -336 15124 -336 15123 -336 15123 -336 15123 -336 15123 -336 15123 -337 15123 -337 15123 -337 15123 -337 15122 -337 15122 -337 15122 -337 15122 -337 15122 -337 15122 -338 15122 -338 15122 -338 15121 -338 15121 -338 15121 -338 15121 -338 15121 -338 15121 -339 15121 -339 15121 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -340 15120 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -341 15119 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -343 15117 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -344 15116 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -345 15115 -345 15115 -345 15114 -345 15114 -345 15114 -345 15114 -345 15114 -345 15114 -346 15114 -346 15114 -346 15113 -346 15113 -346 15113 -346 15113 -346 15113 -346 15113 -347 15113 -347 15113 -347 15112 -347 15112 -347 15112 -347 15112 -347 15112 -348 15112 -348 15112 -348 15112 -348 15111 -348 15111 -348 15111 -348 15111 -348 15111 -349 15111 -349 15111 -349 15111 -349 15110 -349 15110 -349 15110 -349 15110 -350 15110 -350 15110 -350 15110 -350 15110 -350 15109 -350 15109 -350 15109 -350 15109 -351 15109 -351 15109 -351 15109 -351 15109 -351 15108 -351 15108 -351 15108 -352 15108 -352 15108 -352 15108 -352 15108 -352 15108 -352 15107 -352 15107 -352 15107 -353 15107 -353 15107 -353 15107 -353 15107 -353 15107 -353 15106 -353 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15105 -354 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15104 -355 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -361 15100 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -362 15099 -362 15098 -362 15098 -362 15098 -362 15098 -362 15098 -362 15098 -363 15098 -363 15098 -363 15097 -363 15097 -363 15097 -363 15097 -363 15097 -363 15097 -364 15097 -364 15097 -364 15096 -364 15096 -364 15096 -364 15096 -364 15096 -365 15096 -365 15096 -365 15096 -365 15095 -365 15095 -365 15095 -365 15095 -365 15095 -366 15095 -366 15095 -366 15095 -366 15094 -366 15094 -366 15094 -366 15094 -366 15094 -367 15094 -367 15094 -367 15094 -367 15093 -367 15093 -367 15093 -367 15093 -368 15093 -368 15093 -368 15093 -368 15093 -368 15092 -368 15092 -368 15092 -368 15092 -369 15092 -369 15092 -369 15092 -369 15092 -369 15091 -369 15091 -369 15091 -370 15091 -370 15091 -370 15091 -370 15091 -370 15091 -370 15090 -370 15090 -370 15090 -371 15090 -371 15090 -371 15090 -371 15090 -371 15090 -371 15089 -371 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15088 -372 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -379 15083 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -380 15082 -380 15081 -380 15081 -380 15081 -380 15081 -380 15081 -380 15081 -381 15081 -381 15081 -381 15080 -381 15080 -381 15080 -381 15080 -381 15080 -382 15080 -382 15080 -382 15080 -382 15079 -382 15079 -382 15079 -383 15079 -383 15079 -383 15079 -383 15079 -383 15079 -383 15078 -383 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15077 -385 15077 -385 15077 -385 15077 -385 15077 -385 15077 -385 15077 -386 15077 -386 15076 -386 15076 -386 15076 -386 15076 -386 15076 -386 15076 -387 15076 -387 15075 -387 15075 -387 15075 -387 15075 -387 15075 -388 15075 -388 15075 -388 15074 -388 15074 -388 15074 -388 15074 -388 15074 -389 15074 -389 15074 -389 15073 -389 15073 -389 15073 -389 15073 -389 15073 -390 15073 -390 15073 -390 15072 -390 15072 -390 15072 -390 15072 -391 15072 -391 15072 -391 15071 -391 15071 -391 15071 -391 15071 -391 15071 -392 15071 -392 15071 -392 15070 -392 15070 -392 15070 -392 15070 -393 15070 -393 15070 -393 15070 -393 15069 -393 15069 -393 15069 -393 15069 -394 15069 -394 15069 -394 15069 -394 15068 -394 15068 -394 15068 -394 15068 -395 15068 -395 15068 -395 15068 -395 15067 -395 15067 -395 15067 -396 15067 -396 15067 -396 15067 -396 15066 -396 15066 -396 15066 -396 15066 -397 15066 -397 15066 -397 15066 -397 15065 -397 15065 -397 15065 -398 15065 -398 15065 -398 15065 -398 15065 -398 15064 -398 15064 -398 15064 -399 15064 -399 15064 -399 15064 -399 15064 -399 15063 -399 15063 -399 15063 -400 15063 -400 15063 -400 15063 -400 15062 -400 15062 -400 15062 -401 15062 -401 15062 -401 15062 -401 15062 -401 15061 -401 15061 -401 15061 -402 15061 -402 15061 -402 15061 -402 15061 -402 15060 -402 15060 -403 15060 -403 15060 -403 15060 -403 15060 -403 15060 -403 15059 -403 15059 -404 15059 -404 15059 -404 15059 -404 15059 -404 15059 -404 15058 -404 15058 -405 15058 -405 15058 -405 15058 -405 15058 -405 15057 -405 15057 -406 15057 -406 15057 -406 15057 -406 15057 -406 15057 -406 15056 -406 15056 -407 15056 -407 15056 -407 15056 -407 15056 -407 15056 -407 15055 -407 15055 -408 15055 -408 15055 -408 15055 -408 15055 -408 15055 -408 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15053 -409 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -414 15048 -415 15048 -415 15048 -415 15048 -415 15048 -415 15048 -415 15048 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -418 15045 -418 15045 -418 15045 -418 15045 -418 15045 -418 15045 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -420 15043 -420 15043 -420 15043 -420 15043 -420 15043 -420 15043 -421 15043 -421 15042 -421 15042 -421 15042 -421 15042 -421 15042 -421 15042 -422 15042 -422 15041 -422 15041 -422 15041 -422 15041 -422 15041 -422 15041 -423 15040 -423 15040 -423 15040 -423 15040 -423 15040 -423 15040 -424 15040 -424 15039 -424 15039 -424 15039 -424 15039 -424 15039 -424 15039 -425 15039 -425 15038 -425 15038 -425 15038 -425 15038 -425 15038 -426 15038 -426 15037 -426 15037 -426 15037 -426 15037 -426 15037 -426 15037 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -428 15035 -428 15035 -428 15035 -428 15035 -428 15035 -428 15035 -428 15034 -429 15034 -429 15034 -429 15034 -429 15034 -429 15034 -429 15033 -429 15033 -430 15033 -430 15033 -430 15033 -430 15033 -430 15033 -430 15032 -430 15032 -430 15032 -431 15032 -431 15032 -431 15032 -431 15031 -431 15031 -431 15031 -431 15031 -431 15031 -432 15031 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -433 15029 -433 15029 -433 15029 -433 15029 -433 15029 -433 15029 -433 15028 -434 15028 -434 15028 -434 15028 -434 15028 -434 15028 -434 15027 -434 15027 -434 15027 -435 15027 -435 15027 -435 15027 -435 15027 -435 15026 -435 15026 -435 15026 -435 15026 -436 15026 -436 15026 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15022 -438 15022 -439 15022 -439 15022 -439 15022 -439 15022 -439 15021 -439 15021 -439 15021 -439 15021 -440 15021 -440 15021 -440 15021 -440 15020 -440 15020 -440 15020 -440 15020 -440 15020 -441 15020 -441 15019 -441 15019 -441 15019 -441 15019 -441 15019 -441 15019 -441 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15017 -443 15017 -443 15017 -443 15017 -443 15017 -443 15017 -443 15016 -443 15016 -443 15016 -444 15016 -444 15016 -444 15016 -444 15015 -444 15015 -444 15015 -444 15015 -444 15015 -445 15015 -445 15015 -445 15014 -445 15014 -445 15014 -445 15014 -445 15014 -445 15014 -446 15013 -446 15013 -446 15013 -446 15013 -446 15013 -446 15013 -446 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15011 -447 15011 -448 15011 -448 15011 -448 15011 -448 15011 -448 15010 -448 15010 -448 15010 -448 15010 -449 15010 -449 15010 -449 15009 -449 15009 -449 15009 -449 15009 -449 15009 -449 15009 -450 15009 -450 15008 -450 15008 -450 15008 -450 15008 -450 15008 -450 15008 -451 15007 -451 15007 -451 15007 -451 15007 -451 15007 -451 15007 -451 15006 -451 15006 -452 15006 -452 15006 -452 15006 -453 15003 -454 15003 -455 15003 -455 15002 -455 15002 -455 15002 -455 15002 -455 15002 -455 15002 -456 15001 -456 15001 -456 15001 -456 15001 -456 15001 -456 15000 -457 15000 -457 15000 -457 15000 -457 15000 -457 14999 -457 14999 -457 14999 -458 14999 -458 14999 -458 14999 -458 14998 -458 14998 -458 14998 -459 14998 -459 14998 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -460 14997 -460 14996 -460 14996 -460 14996 -460 14996 -460 14996 -461 14995 -461 14995 -461 14995 -461 14995 -461 14995 -461 14995 -461 14994 -462 14994 -462 14994 -462 14994 -462 14994 -462 14993 -462 14993 -463 14993 -463 14993 -463 14993 -463 14992 -463 14992 -463 14992 -463 14992 -464 14992 -464 14992 -464 14991 -464 14991 -464 14991 -464 14991 -464 14991 -465 14990 -465 14990 -465 14990 -465 14990 -465 14990 -465 14990 -466 14989 -466 14989 -466 14989 -466 14989 -466 14989 -466 14988 -466 14988 -467 14988 -467 14988 -467 14988 -467 14988 -467 14987 -468 14987 -468 14987 -468 14987 -468 14987 -468 14986 -469 14986 -469 14986 -469 14986 -469 14986 -469 14985 -470 14985 -470 14985 -470 14985 -470 14985 -471 14985 -471 14984 -471 14984 -471 14984 -471 14984 -472 14984 -472 14983 -472 14983 -472 14983 -472 14983 -473 14983 -473 14983 -473 14982 -473 14982 -474 14982 -474 14982 -474 14982 -474 14981 -474 14981 -475 14981 -475 14981 -475 14981 -475 14980 -475 14980 -476 14980 -476 14980 -476 14980 -476 14980 -477 14979 -477 14979 -477 14979 -477 14979 -477 14979 -478 14978 -478 14978 -478 14978 -478 14978 -478 14978 -479 14978 -479 14977 -479 14977 -479 14977 -479 14977 -480 14977 -480 14976 -480 14976 -480 14976 -481 14976 -481 14976 -481 14976 -481 14975 -481 14975 -482 14975 -482 14975 -482 14975 -482 14974 -482 14974 -483 14974 -483 14974 -483 14974 -483 14973 -484 14973 -484 14973 -484 14973 -484 14973 -484 14973 -485 14972 -485 14972 -485 14972 -485 14972 -485 14972 -486 14971 -486 14971 -486 14971 -486 14971 -487 14971 -487 14971 -487 14970 -487 14970 -487 14970 -488 14970 -488 14970 -488 14969 -488 14969 -488 14969 -489 14969 -489 14969 -489 14968 -489 14968 -490 14968 -490 14968 -490 14968 -490 14968 -490 14967 -491 14967 -491 14967 -491 14967 -491 14967 -491 14966 -492 14966 -492 14966 -492 14966 -492 14966 -492 14966 -493 14965 -493 14965 -493 14965 -493 14965 -494 14965 -494 14964 -494 14964 -494 14964 -494 14964 -495 14964 -495 14964 -495 14963 -495 14963 -495 14963 -496 14963 -496 14962 -496 14962 -496 14962 -497 14961 -497 14961 -497 14961 -497 14961 -497 14960 -498 14960 -498 14960 -498 14959 -498 14959 -498 14959 -499 14959 -499 14958 -499 14958 -499 14958 -500 14957 -500 14957 -500 14957 -500 14956 -500 14956 -501 14956 -501 14956 -501 14955 -501 14955 -501 14955 -502 14954 -502 14954 -502 14954 -502 14953 -503 14953 -503 14953 -504 14953 -504 14952 -504 14952 -505 14952 -505 14951 -505 14951 -506 14951 -506 14951 -506 14950 -507 14950 -507 14950 -507 14949 -508 14949 -508 14949 -508 14948 -509 14948 -509 14948 -509 14948 -510 14947 -510 14947 -510 14947 -511 14946 -511 14946 -511 14946 -512 14945 -512 14945 -513 14945 -513 14945 -513 14944 -514 14944 -514 14944 -514 14943 -515 14943 -515 14943 -515 14943 -516 14942 -516 14942 -516 14942 -517 14941 -517 14941 -517 14941 -518 14940 -518 14940 -518 14940 -519 14940 -519 14939 -519 14939 -520 14939 -520 14938 -520 14938 -521 14938 -521 14938 -522 14937 -522 14937 -522 14937 -523 14936 -523 14936 -523 14936 -524 14935 -524 14935 -524 14935 -525 14935 -525 14934 -525 14934 -526 14934 -526 14933 -526 14933 -527 14933 -527 14932 -527 14932 -528 14932 -528 14932 -528 14931 -529 14931 -529 14931 -529 14930 -530 14930 -530 14930 -531 14930 -531 14929 -531 14929 -532 14929 -532 14928 -532 14928 -533 14928 -533 14927 -533 14927 -534 14927 -534 14927 -534 14926 -535 14926 -535 14926 -535 14925 -536 14925 -536 14925 -536 14924 -537 14924 -537 14924 -537 14924 -538 14923 -538 14923 -538 14923 -539 14922 -539 14922 -540 14922 -540 14922 -540 14921 -541 14921 -541 14921 -541 14920 -542 14920 -542 14920 -542 14919 -543 14919 -543 14919 -543 14919 -544 14918 -544 14918 -544 14918 -545 14917 -545 14917 -545 14917 -546 14917 -546 14916 -546 14916 -547 14916 -547 14915 -547 14915 -548 14915 -548 14914 -549 14914 -549 14914 -549 14914 -550 14913 -550 14913 -550 14913 -551 14912 -551 14912 -551 14912 -552 14911 -552 14911 -552 14911 -553 14911 -553 14910 -553 14910 -554 14910 -554 14909 -554 14909 -555 14909 -555 14909 -555 14908 -556 14908 -556 14908 -556 14907 -557 14907 -557 14907 -558 14906 -558 14906 -558 14906 -559 14906 -559 14905 -559 14905 -560 14905 -560 14904 -560 14904 -561 14904 -561 14903 -561 14903 -562 14903 -562 14903 -562 14902 -563 14902 -563 14902 -563 14901 -564 14901 -564 14901 -564 14901 -565 14900 -565 14900 -565 14900 -566 14899 -566 14899 -567 14899 -567 14898 -567 14898 -568 14898 -568 14898 -568 14897 -569 14897 -569 14897 -569 14896 -570 14896 -570 14896 -570 14896 -571 14895 -571 14895 -571 14895 -572 14894 -572 14894 -572 14894 -573 14893 -573 14893 -573 14893 -574 14893 -574 14892 -574 14892 -575 14892 -575 14891 -576 14891 -576 14891 -576 14890 -577 14890 -577 14890 -577 14890 -578 14889 -578 14889 -578 14889 -579 14888 -579 14888 -579 14888 -580 14888 -580 14887 -580 14887 -581 14887 -581 14886 -581 14886 -582 14886 -582 14885 -582 14885 -583 14885 -583 14885 -583 14884 -584 14884 -584 14884 -585 14883 -585 14883 -585 14883 -586 14883 -586 14882 -586 14882 -587 14882 -587 14881 -587 14881 -588 14881 -588 14880 -588 14880 -589 14880 -589 14880 -589 14879 -590 14879 -590 14879 -590 14878 -591 14878 -591 14878 -591 14877 -592 14877 -592 14877 -592 14876 -593 14876 -593 14876 -594 14875 -594 14875 -594 14875 -595 14874 -595 14874 -595 14874 -596 14873 -596 14873 -596 14873 -597 14872 -597 14872 -597 14871 -598 14871 -598 14871 -598 14870 -599 14870 -599 14870 -599 14869 -600 14869 -600 14869 -600 14868 -601 14868 -601 14868 -601 14867 -602 14867 -602 14866 -603 14866 -603 14866 -603 14865 -604 14865 -604 14865 -604 14864 -605 14864 -605 14864 -606 14863 -606 14863 -606 14863 -607 14862 -607 14862 -607 14861 -608 14861 -608 14861 -609 14860 -609 14860 -609 14860 -610 14859 -610 14859 -610 14859 -611 14858 -611 14858 -611 14858 -612 14857 -612 14857 -613 14856 -613 14856 -613 14856 -614 14855 -614 14855 -614 14855 -615 14854 -615 14854 -616 14854 -616 14853 -616 14853 -617 14853 -617 14852 -617 14852 -618 14851 -618 14851 -619 14851 -619 14850 -619 14850 -620 14850 -620 14849 -620 14849 -621 14849 -621 14848 -622 14848 -622 14848 -622 14847 -623 14847 -623 14846 -623 14846 -624 14846 -624 14845 -625 14845 -625 14845 -625 14844 -626 14844 -626 14844 -626 14843 -627 14843 -627 14843 -627 14842 -628 14842 -628 14841 -629 14841 -629 14841 -629 14840 -630 14840 -630 14840 -630 14839 -631 14839 -631 14839 -632 14838 -632 14838 -632 14838 -633 14837 -633 14837 -633 14836 -634 14836 -634 14836 -635 14835 -635 14835 -635 14835 -636 14834 -636 14834 -636 14834 -637 14833 -637 14833 -638 14833 -638 14832 -638 14832 -639 14831 -639 14831 -639 14831 -640 14830 -640 14830 -641 14830 -641 14829 -641 14829 -642 14829 -642 14828 -642 14828 -643 14827 -643 14827 -643 14827 -644 14826 -644 14826 -645 14826 -645 14825 -645 14825 -646 14825 -646 14824 -646 14824 -647 14824 -647 14823 -648 14823 -648 14822 -648 14822 -649 14822 -649 14821 -649 14821 -650 14821 -650 14820 -651 14820 -651 14820 -651 14819 -652 14819 -652 14819 -652 14818 -653 14818 -653 14817 -654 14817 -654 14817 -654 14816 -655 14816 -655 14816 -655 14815 -656 14815 -656 14815 -656 14814 -657 14814 -657 14814 -658 14813 -658 14813 -658 14812 -659 14812 -659 14812 -659 14811 -660 14811 -660 14811 -661 14810 -661 14810 -661 14810 -662 14809 -662 14809 -662 14809 -663 14808 -663 14808 -664 14807 -664 14807 -664 14807 -665 14806 -665 14806 -665 14806 -666 14805 -666 14805 -667 14805 -667 14804 -667 14804 -668 14804 -668 14803 -668 14803 -669 14802 -669 14802 -670 14802 -670 14801 -670 14801 -671 14801 -671 14800 -671 14800 -672 14800 -672 14799 -672 14799 -673 14799 -673 14798 -674 14798 -674 14797 -675 14797 -675 14797 -675 14796 -676 14796 -676 14795 -677 14794 -677 14794 -678 14793 -678 14793 -679 14792 -679 14792 -679 14791 -680 14791 -680 14790 -681 14789 -681 14789 -682 14788 -682 14788 -683 14787 -683 14787 -684 14786 -684 14786 -684 14785 -685 14784 -685 14784 -686 14783 -686 14783 -687 14782 -687 14782 -688 14781 -688 14781 -688 14780 -689 14779 -689 14779 -690 14778 -690 14778 -691 14777 -691 14777 -692 14776 -692 14776 -693 14775 -693 14774 -693 14774 -694 14773 -694 14773 -695 14772 -695 14772 -696 14771 -696 14771 -697 14770 -697 14769 -697 14769 -698 14768 -698 14768 -699 14767 -699 14767 -700 14766 -700 14766 -701 14765 -701 14764 -702 14764 -702 14763 -702 14763 -703 14762 -703 14762 -704 14761 -704 14761 -705 14760 -705 14759 -706 14759 -706 14758 -706 14758 -707 14757 -707 14757 -708 14756 -708 14756 -709 14755 -709 14754 -710 14754 -710 14753 -711 14753 -711 14752 -711 14752 -712 14751 -712 14751 -713 14750 -713 14749 -714 14749 -714 14748 -715 14748 -715 14747 -715 14747 -716 14746 -716 14746 -717 14745 -717 14744 -718 14744 -718 14743 -719 14743 -719 14742 -720 14742 -720 14741 -720 14741 -721 14740 -721 14739 -722 14739 -722 14738 -723 14738 -723 14737 -724 14737 -724 14736 -724 14736 -725 14735 -725 14734 -726 14734 -726 14733 -727 14733 -727 14732 -728 14732 -728 14731 -729 14731 -729 14730 -729 14729 -730 14729 -730 14728 -731 14728 -731 14727 -732 14727 -732 14726 -733 14726 -733 14725 -733 14724 -734 14724 -734 14723 -735 14723 -735 14722 -736 14722 -736 14721 -737 14721 -737 14720 -738 14719 -738 14719 -738 14718 -739 14718 -739 14717 -740 14717 -740 14716 -741 14716 -741 14715 -742 14714 -742 14714 -742 14713 -743 14713 -743 14712 -744 14712 -744 14711 -745 14711 -745 14710 -746 14709 -746 14709 -747 14708 -747 14708 -747 14707 -748 14707 -748 14706 -749 14706 -749 14705 -750 14704 -750 14704 -751 14703 -751 14703 -751 14702 -752 14702 -752 14701 -753 14701 -753 14700 -754 14699 -754 14699 -755 14698 -755 14698 -756 14697 -756 14697 -756 14696 -757 14696 -757 14695 -758 14694 -758 14694 -759 14693 -759 14693 -760 14692 -760 14692 -760 14691 -761 14691 -761 14690 -762 14689 -762 14689 -763 14688 -763 14688 -764 14687 -764 14687 -765 14686 -765 14686 -765 14685 -766 14684 -767 14684 -767 14683 -768 14683 -768 14682 -769 14682 -770 14681 -770 14681 -771 14680 -771 14679 -772 14679 -773 14678 -773 14678 -774 14677 -774 14677 -775 14676 -776 14676 -776 14675 -777 14674 -777 14674 -778 14673 -779 14673 -779 14672 -780 14672 -780 14671 -781 14671 -782 14670 -782 14669 -783 14669 -783 14668 -784 14668 -785 14667 -785 14667 -786 14666 -786 14666 -787 14665 -788 14664 -788 14664 -789 14663 -789 14663 -790 14662 -791 14662 -791 14661 -792 14661 -792 14660 -793 14659 -794 14659 -794 14658 -795 14658 -795 14657 -796 14657 -797 14656 -797 14656 -798 14655 -798 14654 -799 14654 -800 14653 -800 14653 -801 14652 -801 14652 -802 14651 -803 14651 -803 14650 -804 14649 -804 14649 -805 14648 -806 14648 -806 14647 -807 14647 -807 14646 -808 14646 -809 14645 -809 14644 -810 14644 -810 14643 -811 14643 -812 14642 -812 14642 -813 14641 -813 14641 -814 14640 -815 14639 -815 14639 -816 14638 -816 14638 -817 14637 -818 14637 -818 14636 -819 14636 -819 14635 -820 14634 -821 14634 -821 14633 -822 14633 -822 14632 -823 14632 -824 14631 -824 14631 -825 14630 -825 14629 -826 14629 -827 14628 -827 14628 -828 14627 -828 14627 -829 14626 -830 14626 -830 14625 -831 14624 -831 14624 -832 14623 -833 14623 -833 14622 -834 14622 -834 14621 -835 14621 -836 14620 -836 14619 -837 14619 -837 14618 -838 14618 -839 14617 -839 14617 -840 14616 -840 14615 -841 14615 -842 14614 -842 14614 -843 14613 -843 14612 -844 14612 -845 14611 -845 14611 -846 14610 -846 14609 -847 14609 -848 14608 -848 14608 -849 14607 -849 14606 -850 14606 -851 14605 -851 14605 -852 14604 -852 14603 -853 14603 -854 14602 -854 14602 -855 14601 -855 14600 -856 14600 -857 14599 -857 14599 -858 14598 -858 14597 -859 14597 -860 14596 -860 14596 -861 14595 -861 14594 -862 14594 -863 14593 -863 14593 -864 14592 -864 14591 -865 14591 -866 14590 -866 14589 -867 14589 -867 14588 -868 14588 -869 14587 -869 14586 -870 14586 -870 14585 -871 14585 -872 14584 -872 14583 -873 14583 -873 14582 -874 14582 -875 14581 -875 14580 -876 14580 -876 14579 -877 14579 -878 14578 -878 14577 -879 14577 -879 14576 -880 14576 -881 14575 -881 14574 -882 14574 -882 14573 -883 14573 -884 14572 -884 14571 -885 14571 -885 14570 -886 14570 -887 14569 -887 14568 -888 14568 -888 14567 -889 14567 -890 14566 -890 14565 -891 14565 -891 14564 -892 14564 -893 14563 -893 14562 -894 14562 -895 14561 -895 14561 -896 14560 -896 14559 -897 14559 -898 14558 -898 14557 -899 14557 -900 14556 -900 14556 -901 14555 -902 14554 -902 14554 -903 14553 -904 14553 -904 14552 -905 14551 -906 14551 -906 14550 -907 14550 -908 14549 -908 14548 -909 14548 -910 14547 -910 14547 -911 14546 -912 14545 -912 14545 -913 14544 -913 14544 -914 14543 -915 14542 -915 14542 -916 14541 -917 14541 -917 14540 -918 14539 -919 14539 -919 14538 -920 14538 -921 14537 -921 14536 -922 14536 -923 14535 -923 14535 -924 14534 -925 14533 -925 14533 -926 14532 -927 14532 -927 14531 -928 14530 -929 14530 -929 14529 -930 14528 -930 14528 -931 14527 -932 14527 -932 14526 -933 14525 -934 14525 -934 14524 -935 14524 -936 14523 -936 14522 -937 14522 -938 14521 -938 14521 -939 14520 -940 14519 -940 14519 -941 14518 -942 14518 -942 14517 -943 14516 -944 14516 -944 14515 -945 14515 -946 14514 -946 14513 -947 14513 -947 14512 -948 14512 -949 14511 -949 14510 -950 14510 -951 14509 -951 14509 -952 14508 -953 14507 -953 14507 -954 14506 -955 14506 -955 14505 -956 14504 -957 14504 -957 14503 -958 14503 -959 14502 -959 14501 -960 14501 -961 14500 -961 14500 -962 14499 -963 14498 -963 14498 -964 14497 -964 14496 -965 14496 -966 14495 -966 14495 -967 14494 -968 14493 -968 14493 -969 14492 -970 14492 -970 14491 -971 14490 -972 14490 -972 14489 -973 14489 -974 14488 -974 14487 -975 14487 -976 14486 -976 14486 -977 14485 -978 14484 -978 14484 -979 14483 -980 14483 -980 14482 -981 14481 -981 14481 -982 14480 -983 14480 -983 14479 -984 14478 -985 14478 -985 14477 -986 14477 -987 14476 -987 14475 -988 14475 -989 14474 -989 14474 -990 14473 -991 14472 -991 14472 -992 14471 -993 14471 -993 14470 -994 14469 -995 14469 -995 14468 -996 14467 -997 14467 -997 14466 -998 14466 -998 14465 -999 14464 -1000 14464 -1000 14463 -1001 14463 -1002 14462 -1002 14461 -1003 14461 -1004 14460 -1004 14460 -1005 14459 -1006 14459 -1006 14458 -1007 14458 -1008 14457 -1008 14457 -1009 14456 -1010 14456 -1010 14455 -1011 14455 -1012 14454 -1012 14454 -1013 14453 -1014 14453 -1014 14452 -1014 14453 -1014 14453 -1015 14452 -1015 14451 -1016 14451 -1017 14450 -1017 14450 -1018 14449 -1018 14448 -1019 14448 -1019 14447 -1020 14446 -1021 14446 -1021 14445 -1022 14444 -1022 14444 -1023 14443 -1023 14443 -1024 14442 -1025 14441 -1025 14441 -1026 14440 -1026 14439 -1027 14439 -1027 14438 -1028 14437 -1029 14437 -1029 14436 -1030 14436 -1030 14435 -1031 14434 -1031 14434 -1032 14433 -1033 14432 -1033 14432 -1034 14431 -1034 14430 -1035 14430 -1035 14429 -1036 14429 -1037 14428 -1037 14427 -1038 14427 -1038 14426 -1039 14425 -1039 14425 -1040 14424 -1041 14423 -1041 14423 -1042 14422 -1042 14422 -1043 14421 -1043 14420 -1044 14420 -1045 14419 -1045 14418 -1046 14418 -1046 14417 -1047 14416 -1047 14416 -1048 14415 -1049 14415 -1049 14414 -1050 14413 -1050 14413 -1051 14412 -1051 14411 -1052 14411 -1053 14410 -1053 14409 -1054 14409 -1054 14408 -1055 14408 -1055 14407 -1056 14406 -1056 14406 -1057 14405 -1058 14404 -1058 14404 -1059 14403 -1059 14402 -1060 14402 -1060 14401 -1061 14401 -1062 14400 -1062 14399 -1063 14399 -1063 14398 -1064 14397 -1064 14397 -1065 14396 -1066 14395 -1066 14395 -1067 14394 -1067 14394 -1068 14393 -1069 14392 -1069 14392 -1070 14391 -1071 14390 -1071 14390 -1072 14389 -1073 14388 -1073 14388 -1074 14387 -1075 14387 -1076 14386 -1076 14385 -1077 14385 -1078 14384 -1078 14383 -1079 14383 -1080 14382 -1080 14381 -1081 14381 -1082 14380 -1083 14380 -1083 14379 -1084 14378 -1085 14378 -1085 14377 -1086 14376 -1087 14376 -1087 14375 -1088 14374 -1089 14374 -1090 14373 -1090 14373 -1091 14372 -1092 14371 -1092 14371 -1093 14370 -1094 14369 -1094 14369 -1095 14368 -1096 14367 -1097 14367 -1097 14366 -1098 14366 -1099 14365 -1099 14364 -1100 14364 -1101 14363 -1101 14362 -1102 14362 -1103 14361 -1104 14360 -1104 14360 -1105 14359 -1106 14359 -1106 14358 -1107 14357 -1108 14356 -1108 14356 -1109 14355 -1110 14354 -1111 14353 -1111 14353 -1112 14352 -1113 14351 -1113 14351 -1114 14350 -1115 14349 -1115 14348 -1116 14348 -1117 14347 -1118 14346 -1118 14345 -1119 14345 -1120 14344 -1120 14343 -1121 14342 -1122 14342 -1122 14341 -1123 14340 -1124 14339 -1125 14339 -1125 14338 -1126 14337 -1127 14336 -1127 14336 -1128 14335 -1129 14334 -1129 14334 -1130 14333 -1131 14332 -1132 14331 -1132 14331 -1133 14330 -1134 14329 -1134 14328 -1135 14328 -1136 14327 -1136 14326 -1137 14325 -1138 14325 -1139 14324 -1139 14323 -1140 14322 -1141 14322 -1141 14321 -1142 14320 -1143 14319 -1143 14319 -1144 14318 -1145 14317 -1146 14316 -1146 14316 -1147 14315 -1148 14314 -1148 14314 -1149 14313 -1150 14312 -1150 14311 -1151 14311 -1152 14310 -1153 14309 -1154 14308 -1155 14308 -1156 14307 -1156 14306 -1157 14305 -1158 14305 -1159 14304 -1160 14303 -1161 14302 -1162 14302 -1162 14301 -1163 14300 -1164 14299 -1165 14299 -1166 14298 -1167 14297 -1168 14297 -1169 14296 -1169 14295 -1170 14294 -1171 14294 -1172 14293 -1173 14292 -1174 14291 -1175 14291 -1175 14290 -1176 14289 -1177 14288 -1178 14288 -1179 14287 -1180 14286 -1181 14285 -1182 14285 -1182 14284 -1183 14283 -1184 14282 -1185 14282 -1186 14281 -1187 14280 -1188 14280 -1189 14279 -1189 14278 -1190 14277 -1191 14277 -1192 14276 -1193 14275 -1194 14274 -1195 14274 -1195 14273 -1196 14272 -1197 14271 -1198 14271 -1199 14270 -1200 14269 -1201 14268 -1202 14268 -1202 14267 -1203 14266 -1204 14265 -1205 14265 -1206 14264 -1207 14263 -1208 14263 -1209 14262 -1209 14261 -1210 14260 -1211 14260 -1212 14259 -1213 14258 -1214 14257 -1215 14257 -1215 14256 -1216 14255 -1217 14254 -1218 14254 -1219 14253 -1220 14252 -1221 14251 -1222 14251 -1222 14250 -1223 14249 -1224 14248 -1225 14248 -1226 14247 -1227 14246 -1228 14246 -1228 14245 -1229 14244 -1230 14243 -1231 14242 -1232 14242 -1233 14241 -1234 14240 -1235 14239 -1235 14238 -1236 14237 -1237 14236 -1238 14235 -1239 14234 -1240 14234 -1241 14233 -1242 14232 -1242 14231 -1243 14230 -1244 14229 -1245 14228 -1246 14227 -1247 14226 -1248 14225 -1248 14225 -1249 14224 -1250 14223 -1251 14222 -1252 14221 -1253 14220 -1254 14219 -1255 14218 -1255 14217 -1256 14217 -1257 14216 -1258 14215 -1259 14214 -1260 14213 -1261 14212 -1261 14211 -1262 14210 -1263 14209 -1264 14208 -1265 14208 -1266 14207 -1267 14206 -1268 14205 -1268 14204 -1269 14203 -1270 14202 -1271 14201 -1272 14200 -1273 14200 -1274 14199 -1275 14198 -1275 14197 -1276 14196 -1277 14195 -1278 14194 -1279 14193 -1280 14192 -1281 14191 -1281 14191 -1282 14190 -1283 14189 -1284 14188 -1285 14187 -1286 14186 -1287 14185 -1288 14184 -1289 14183 -1290 14183 -1291 14182 -1292 14181 -1293 14180 -1294 14179 -1295 14178 -1296 14177 -1297 14176 -1298 14175 -1299 14174 -1300 14174 -1301 14173 -1302 14172 -1303 14171 -1304 14170 -1305 14169 -1306 14168 -1307 14168 -1308 14167 -1309 14166 -1310 14165 -1311 14164 -1312 14163 -1313 14162 -1314 14161 -1315 14160 -1316 14159 -1317 14158 -1318 14157 -1319 14156 -1320 14155 -1321 14154 -1322 14153 -1324 14152 -1325 14151 -1326 14150 -1327 14149 -1328 14148 -1329 14147 -1330 14146 -1331 14145 -1332 14144 -1333 14143 -1334 14142 -1335 14141 -1336 14140 -1337 14139 -1338 14138 -1339 14137 -1340 14136 -1341 14135 -1342 14134 -1343 14133 -1344 14132 -1345 14131 -1346 14130 -1347 14129 -1348 14128 -1349 14127 -1350 14126 -1351 14125 -1352 14124 -1353 14123 -1354 14121 -1355 14120 -1356 14119 -1357 14118 -1358 14117 -1359 14116 -1360 14114 -1361 14113 -1362 14112 -1363 14111 -1364 14110 -1365 14109 -1366 14107 -1367 14106 -1368 14105 -1369 14104 -1370 14103 -1371 14102 -1372 14100 -1373 14099 -1374 14098 -1375 14097 -1376 14096 -1378 14095 -1379 14093 -1380 14092 -1381 14091 -1382 14090 -1383 14089 -1384 14088 -1385 14086 -1386 14085 -1387 14084 -1388 14083 -1389 14082 -1390 14081 -1391 14079 -1392 14078 -1393 14077 -1394 14076 -1395 14075 -1396 14074 -1397 14073 -1398 14071 -1400 14070 -1401 14069 -1402 14068 -1403 14067 -1404 14066 -1405 14064 -1407 14063 -1408 14062 -1409 14061 -1410 14060 -1411 14059 -1412 14057 -1413 14056 -1415 14055 -1416 14054 -1417 14053 -1418 14052 -1419 14050 -1420 14049 -1421 14048 -1423 14047 -1424 14046 -1425 14045 -1426 14043 -1427 14042 -1428 14041 -1430 14040 -1431 14039 -1432 14038 -1433 14036 -1434 14035 -1435 14034 -1436 14033 -1438 14032 -1439 14031 -1440 14030 -1441 14028 -1442 14027 -1443 14026 -1444 14025 -1446 14024 -1447 14023 -1448 14021 -1449 14020 -1450 14019 -1451 14018 -1453 14017 -1454 14016 -1455 14014 -1456 14013 -1457 14012 -1458 14011 -1459 14010 -1461 14009 -1462 14007 -1463 14006 -1464 14005 -1465 14004 -1466 14003 -1467 14002 -1469 14000 -1470 13999 -1471 13998 -1472 13997 -1473 13996 -1474 13995 -1476 13993 -1477 13992 -1478 13991 -1479 13990 -1480 13989 -1481 13988 -1482 13986 -1484 13985 -1485 13984 -1486 13983 -1487 13982 -1488 13981 -1489 13980 -1491 13978 -1492 13977 -1493 13976 -1494 13975 -1495 13974 -1496 13973 -1497 13971 -1499 13970 -1500 13969 -1501 13968 -1502 13966 -1503 13965 -1504 13964 -1505 13962 -1507 13961 -1508 13960 -1509 13958 -1510 13957 -1511 13955 -1512 13954 -1514 13953 -1515 13951 -1516 13950 -1518 13949 -1519 13947 -1520 13946 -1522 13944 -1523 13943 -1525 13942 -1526 13940 -1527 13939 -1529 13938 -1530 13936 -1531 13935 -1533 13934 -1534 13932 -1536 13931 -1537 13929 -1538 13928 -1540 13927 -1541 13925 -1543 13924 -1544 13923 -1545 13921 -1547 13920 -1548 13919 -1550 13917 -1551 13916 -1552 13914 -1554 13913 -1555 13912 -1557 13910 -1558 13909 -1559 13908 -1561 13906 -1562 13905 -1563 13903 -1565 13902 -1566 13901 -1568 13899 -1569 13898 -1570 13897 -1572 13895 -1573 13894 -1575 13893 -1576 13891 -1577 13890 -1579 13888 -1580 13887 -1582 13886 -1583 13884 -1584 13883 -1586 13882 -1587 13880 -1589 13879 -1590 13877 -1591 13876 -1593 13875 -1594 13873 -1596 13872 -1597 13871 -1598 13869 -1600 13868 -1601 13867 -1602 13865 -1604 13864 -1605 13862 -1607 13861 -1608 13860 -1609 13858 -1611 13857 -1612 13856 -1614 13854 -1615 13853 -1616 13851 -1618 13850 -1619 13849 -1621 13847 -1622 13846 -1623 13845 -1625 13843 -1626 13842 -1628 13841 -1629 13839 -1630 13838 -1632 13836 -1633 13835 -1634 13834 -1636 13832 -1637 13831 -1639 13830 -1640 13828 -1641 13827 -1643 13825 -1644 13824 -1646 13823 -1647 13821 -1648 13820 -1650 13819 -1651 13817 -1653 13816 -1654 13815 -1655 13813 -1657 13812 -1658 13810 -1660 13809 -1661 13808 -1662 13806 -1664 13805 -1665 13804 -1666 13802 -1668 13801 -1669 13800 -1671 13798 -1672 13797 -1673 13795 -1675 13794 -1676 13793 -1678 13791 -1679 13790 -1680 13789 -1682 13787 -1683 13786 -1685 13784 -1686 13783 -1687 13781 -1689 13780 -1690 13778 -1692 13776 -1693 13774 -1694 13773 -1696 13771 -1697 13769 -1698 13767 -1700 13766 -1701 13764 -1703 13762 -1704 13760 -1706 13759 -1708 13757 -1709 13755 -1711 13754 -1713 13752 -1715 13750 -1716 13748 -1718 13747 -1720 13745 -1722 13743 -1723 13741 -1725 13740 -1727 13738 -1729 13736 -1731 13734 -1732 13733 -1734 13731 -1736 13729 -1738 13728 -1739 13726 -1741 13724 -1743 13722 -1745 13721 -1746 13719 -1748 13717 -1750 13715 -1752 13714 -1753 13712 -1755 13710 -1757 13709 -1759 13707 -1761 13705 -1762 13703 -1764 13702 -1766 13700 -1768 13698 -1769 13696 -1771 13695 -1773 13693 -1775 13691 -1776 13689 -1778 13688 -1780 13686 -1782 13684 -1783 13683 -1785 13681 -1787 13679 -1789 13677 -1791 13676 -1792 13674 -1794 13672 -1796 13670 -1798 13669 -1799 13667 -1801 13665 -1803 13664 -1805 13662 -1806 13660 -1808 13658 -1810 13657 -1812 13655 -1813 13653 -1815 13651 -1817 13650 -1819 13648 -1821 13646 -1822 13644 -1824 13643 -1826 13641 -1828 13639 -1829 13638 -1831 13636 -1833 13634 -1835 13632 -1836 13631 -1838 13629 -1840 13627 -1842 13625 -1843 13624 -1845 13622 -1847 13620 -1849 13618 -1851 13617 -1852 13615 -1854 13613 -1856 13612 -1858 13610 -1859 13608 -1861 13606 -1863 13605 -1865 13603 -1866 13601 -1868 13599 -1870 13598 -1872 13596 -1873 13594 -1875 13593 -1877 13591 -1879 13589 -1881 13587 -1882 13586 -1884 13584 -1886 13582 -1888 13580 -1889 13579 -1891 13577 -1893 13575 -1895 13573 -1897 13572 -1899 13570 -1901 13568 -1903 13567 -1905 13565 -1907 13563 -1909 13561 -1911 13560 -1913 13558 -1915 13556 -1917 13554 -1919 13553 -1921 13551 -1923 13549 -1925 13547 -1927 13545 -1929 13543 -1931 13540 -1933 13538 -1935 13536 -1937 13534 -1939 13531 -1941 13529 -1943 13527 -1945 13525 -1947 13522 -1949 13520 -1951 13518 -1953 13516 -1955 13514 -1957 13511 -1959 13509 -1961 13507 -1963 13505 -1965 13502 -1967 13500 -1969 13498 -1971 13496 -1973 13493 -1975 13491 -1977 13489 -1979 13487 -1981 13484 -1983 13482 -1985 13480 -1987 13478 -1989 13475 -1991 13473 -1993 13471 -1995 13469 -1997 13466 -1999 13464 -2001 13462 -2003 13460 -2005 13458 -2007 13455 -2009 13453 -2011 13451 -2013 13449 -2016 13446 -2018 13444 -2020 13442 -2022 13440 -2024 13437 -2026 13435 -2028 13433 -2030 13431 -2032 13428 -2034 13426 -2036 13424 -2038 13422 -2040 13419 -2042 13417 -2044 13415 -2046 13413 -2048 13410 -2050 13408 -2052 13406 -2054 13404 -2056 13401 -2058 13399 -2060 13397 -2062 13395 -2064 13393 -2066 13390 -2068 13388 -2070 13386 -2072 13384 -2074 13381 -2076 13379 -2078 13377 -2080 13375 -2082 13372 -2084 13370 -2086 13368 -2088 13366 -2090 13363 -2092 13361 -2094 13359 -2096 13357 -2098 13354 -2100 13352 -2102 13350 -2104 13347 -2106 13345 -2108 13343 -2110 13340 -2112 13338 -2114 13336 -2116 13333 -2118 13331 -2120 13329 -2122 13327 -2124 13324 -2126 13322 -2128 13320 -2130 13317 -2132 13315 -2134 13313 -2136 13310 -2137 13308 -2139 13306 -2141 13303 -2143 13301 -2145 13299 -2147 13296 -2149 13294 -2151 13292 -2153 13289 -2155 13287 -2157 13285 -2159 13283 -2161 13280 -2163 13278 -2165 13276 -2167 13273 -2169 13271 -2170 13269 -2172 13266 -2174 13264 -2176 13262 -2178 13259 -2180 13257 -2182 13255 -2184 13252 -2186 13250 -2188 13248 -2190 13245 -2192 13243 -2194 13241 -2196 13238 -2198 13236 -2200 13234 -2201 13232 -2203 13229 -2205 13227 -2207 13225 -2209 13222 -2211 13220 -2213 13218 -2215 13215 -2217 13213 -2219 13211 -2221 13208 -2223 13206 -2225 13204 -2227 13201 -2229 13199 -2231 13197 -2233 13194 -2234 13192 -2236 13190 -2238 13187 -2240 13185 -2242 13183 -2244 13181 -2246 13178 -2248 13176 -2250 13174 -2252 13171 -2254 13169 -2256 13167 -2258 13164 -2260 13162 -2262 13160 -2264 13157 -2265 13155 -2267 13153 -2269 13150 -2271 13148 -2273 13146 -2275 13143 -2277 13141 -2279 13138 -2281 13136 -2283 13133 -2285 13131 -2287 13128 -2289 13126 -2291 13123 -2293 13121 -2295 13118 -2297 13115 -2299 13113 -2301 13110 -2303 13108 -2305 13105 -2307 13103 -2309 13100 -2311 13098 -2313 13095 -2315 13092 -2317 13090 -2319 13087 -2322 13085 -2324 13082 -2326 13080 -2328 13077 -2330 13075 -2332 13072 -2334 13070 -2336 13067 -2338 13064 -2340 13062 -2342 13059 -2344 13057 -2346 13054 -2348 13052 -2350 13049 -2352 13047 -2354 13044 -2356 13041 -2359 13039 -2361 13036 -2363 13034 -2365 13031 -2367 13029 -2369 13026 -2371 13024 -2373 13021 -2375 13018 -2377 13016 -2379 13013 -2381 13011 -2383 13008 -2385 13006 -2387 13003 -2389 13001 -2391 12998 -2393 12995 -2395 12993 -2398 12990 -2400 12988 -2402 12985 -2404 12983 -2406 12981 -2408 12979 -2410 12976 -2412 12974 -2414 12972 -2416 12970 -2418 12967 -2420 12965 -2422 12963 -2424 12961 -2426 12958 -2428 12956 -2430 12954 -2432 12952 -2435 12949 -2438 12947 -2440 12945 -2443 12942 -2446 12940 -2448 12938 -2451 12936 -2454 12933 -2456 12931 -2459 12929 -2462 12927 -2464 12924 -2467 12922 -2470 12920 -2472 12918 -2475 12915 -2478 12913 -2480 12911 -2483 12909 -2486 12906 -2488 12904 -2491 12902 -2494 12900 -2496 12897 -2499 12895 -2502 12893 -2505 12891 -2507 12888 -2510 12886 -2513 12884 -2515 12882 -2518 12880 -2521 12878 -2523 12875 -2526 12873 -2529 12871 -2531 12869 -2534 12866 -2537 12864 -2539 12862 -2542 12860 -2545 12857 -2547 12855 -2550 12853 -2553 12851 -2555 12849 -2558 12846 -2561 12844 -2563 12842 -2566 12840 -2569 12837 -2571 12835 -2574 12833 -2582 12831 -2585 12828 -2588 12826 -2590 12824 -2593 12822 -2596 12819 -2599 12817 -2602 12815 -2605 12813 -2608 12810 -2611 12808 -2614 12806 -2617 12804 -2620 12802 -2623 12799 -2626 12797 -2629 12795 -2632 12793 -2635 12790 -2638 12788 -2641 12786 -2643 12784 -2646 12781 -2649 12779 -2652 12777 -2655 12775 -2658 12772 -2661 12770 -2664 12767 -2667 12763 -2670 12760 -2673 12756 -2676 12753 -2679 12749 -2682 12745 -2685 12742 -2688 12738 -2691 12735 -2693 12731 -2696 12727 -2700 12724 -2703 12720 -2707 12717 -2710 12713 -2714 12709 -2717 12706 -2721 12702 -2724 12699 -2728 12695 -2731 12691 -2735 12688 -2739 12684 -2742 12681 -2746 12677 -2749 12673 -2753 12670 -2756 12666 -2760 12663 -2763 12659 -2767 12655 -2770 12652 -2774 12648 -2777 12645 -2781 12641 -2784 12637 -2788 12634 -2791 12630 -2795 12627 -2798 12623 -2802 12619 -2805 12616 -2809 12612 -2812 12609 -2816 12605 -2820 12601 -2823 12598 -2827 12594 -2830 12590 -2834 12586 -2837 12581 -2841 12577 -2844 12573 -2848 12568 -2851 12564 -2855 12559 -2858 12555 -2862 12550 -2865 12546 -2869 12541 -2873 12537 -2877 12533 -2882 12528 -2886 12524 -2891 12519 -2895 12515 -2900 12510 -2905 12506 -2909 12501 -2914 12497 -2918 12493 -2923 12488 -2927 12484 -2932 12479 -2937 12475 -2941 12470 -2946 12466 -2950 12461 -2955 12457 -2959 12453 -2964 12448 -2968 12444 -2973 12439 -2978 12435 -2982 12430 -2987 12426 -2991 12421 -2996 12417 -3000 12411 -3005 12405 -3010 12399 -3014 12393 -3019 12387 -3025 12381 -3031 12375 -3036 12369 -3042 12363 -3047 12357 -3053 12351 -3058 12345 -3064 12339 -3070 12333 -3075 12327 -3081 12321 -3086 12315 -3092 12308 -3097 12302 -3103 12296 -3109 12290 -3114 12284 -3120 12278 -3125 12272 -3131 12266 -3136 12260 -3142 12254 -3148 12248 -3153 12242 -3159 12236 -3164 12230 -3170 12224 -3175 12218 -3181 12211 -3187 12204 -3192 12196 -3198 12188 -3203 12181 -3209 12173 -3215 12166 -3220 12158 -3227 12150 -3242 12143 -3257 12135 -3272 12128 -3287 12120 -3302 12112 -3317 12105 -3332 12097 -3348 12087 -3363 12070 -3402 12053 -3498 12036 -3611 12019 -3764 12003 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +3043 12432 +3039 12436 +3035 12440 +3031 12444 +3027 12448 +3023 12452 +3019 12456 +3015 12460 +3011 12464 +3007 12468 +3003 12472 +2999 12476 +2996 12480 +2992 12484 +2988 12488 +2984 12492 +2980 12495 +2976 12499 +2972 12503 +2969 12507 +2965 12511 +2961 12515 +2957 12519 +2953 12522 +2950 12526 +2946 12530 +2942 12534 +2938 12538 +2934 12542 +2931 12545 +2927 12549 +2923 12553 +2919 12557 +2916 12560 +2912 12564 +2908 12568 +2904 12572 +2901 12575 +2897 12579 +2893 12583 +2890 12586 +2886 12590 +2882 12594 +2879 12598 +2875 12601 +2871 12605 +2868 12609 +2864 12612 +2860 12616 +2857 12619 +2853 12623 +2850 12627 +2846 12630 +2842 12634 +2839 12638 +2835 12641 +2832 12645 +2828 12648 +2824 12652 +2821 12655 +2817 12659 +2814 12663 +2810 12666 +2807 12670 +2803 12673 +2800 12677 +2796 12680 +2793 12684 +2789 12687 +2786 12691 +2782 12694 +2779 12698 +2775 12701 +2772 12705 +2768 12708 +2765 12711 +2761 12715 +2758 12718 +2755 12722 +2751 12725 +2748 12729 +2744 12732 +2741 12735 +2738 12739 +2734 12742 +2731 12745 +2727 12749 +2724 12752 +2721 12756 +2717 12759 +2714 12762 +2711 12766 +2707 12769 +2704 12772 +2701 12776 +2697 12779 +2694 12782 +2691 12785 +2687 12789 +2684 12792 +2681 12795 +2678 12799 +2674 12802 +2671 12805 +2668 12808 +2664 12812 +2661 12815 +2658 12818 +2655 12821 +2652 12824 +2648 12828 +2645 12831 +2642 12834 +2639 12837 +2635 12840 +2632 12843 +2629 12847 +2626 12850 +2623 12853 +2620 12856 +2616 12859 +2613 12862 +2610 12865 +2607 12869 +2604 12872 +2601 12875 +2598 12878 +2594 12881 +2591 12884 +2588 12887 +2585 12890 +2582 12893 +2579 12896 +2576 12899 +2573 12902 +2570 12905 +2567 12908 +2564 12912 +2561 12915 +2558 12918 +2554 12921 +2551 12924 +2548 12927 +2545 12930 +2542 12932 +2539 12935 +2536 12938 +2533 12941 +2530 12944 +2527 12947 +2524 12950 +2521 12953 +2518 12956 +2516 12959 +2513 12962 +2510 12965 +2507 12968 +2504 12971 +2501 12975 +2498 12978 +2495 12981 +2492 12984 +2489 12987 +2486 12991 +2483 12994 +2480 12997 +2478 13000 +2475 13003 +2472 13006 +2469 13009 +2466 13013 +2463 13016 +2460 13019 +2457 13022 +2455 13025 +2452 13028 +2449 13031 +2446 13034 +2443 13037 +2441 13040 +2438 13043 +2435 13046 +2432 13049 +2429 13052 +2427 13055 +2424 13058 +2421 13061 +2418 13064 +2415 13067 +2413 13070 +2410 13073 +2407 13076 +2404 13079 +2402 13082 +2399 13085 +2396 13088 +2394 13091 +2391 13094 +2388 13096 +2385 13099 +2383 13102 +2380 13105 +2377 13108 +2375 13111 +2372 13114 +2369 13116 +2367 13119 +2364 13122 +2361 13125 +2359 13128 +2356 13130 +2353 13133 +2351 13136 +2348 13139 +2345 13142 +2343 13144 +2340 13147 +2338 13150 +2335 13153 +2332 13155 +2330 13158 +2327 13161 +2325 13163 +2322 13166 +2319 13169 +2317 13171 +2314 13174 +2312 13177 +2309 13179 +2307 13182 +2304 13185 +2302 13187 +2299 13190 +2297 13193 +2294 13195 +2291 13198 +2289 13201 +2286 13203 +2284 13206 +2281 13208 +2279 13211 +2276 13213 +2274 13216 +2271 13219 +2269 13221 +2267 13224 +2264 13226 +2262 13229 +2259 13231 +2257 13234 +2254 13236 +2252 13239 +2249 13241 +2247 13244 +2245 13246 +2242 13249 +2240 13251 +2237 13254 +2235 13256 +2232 13259 +2230 13261 +2228 13264 +2225 13266 +2223 13268 +2221 13271 +2218 13273 +2216 13276 +2213 13278 +2211 13280 +2209 13283 +2206 13285 +2204 13288 +2202 13290 +2199 13292 +2197 13295 +2195 13297 +2192 13299 +2190 13302 +2188 13304 +2185 13306 +2183 13309 +2181 13311 +2178 13313 +2176 13316 +2174 13318 +2172 13320 +2169 13322 +2167 13325 +2165 13327 +2163 13329 +2160 13331 +2158 13334 +2156 13336 +2154 13338 +2151 13340 +2149 13343 +2147 13345 +2145 13347 +2142 13349 +2140 13352 +2138 13354 +2136 13356 +2133 13358 +2131 13360 +2129 13362 +2127 13365 +2125 13367 +2122 13369 +2120 13371 +2118 13373 +2116 13375 +2114 13378 +2112 13380 +2109 13382 +2107 13384 +2105 13386 +2103 13388 +2101 13390 +2099 13392 +2097 13394 +2094 13397 +2092 13399 +2090 13401 +2088 13403 +2086 13405 +2084 13407 +2082 13409 +2080 13411 +2077 13413 +2075 13415 +2073 13417 +2071 13419 +2069 13421 +2067 13423 +2065 13425 +2063 13427 +2061 13429 +2059 13431 +2057 13433 +2055 13435 +2053 13437 +2051 13439 +2048 13441 +2046 13443 +2044 13445 +2042 13447 +2040 13449 +2038 13451 +2036 13453 +2034 13455 +2032 13457 +2030 13459 +2028 13461 +2026 13463 +2024 13465 +2022 13466 +2020 13468 +2018 13470 +2016 13472 +2014 13474 +2012 13476 +2010 13478 +2008 13480 +2006 13482 +2004 13483 +2002 13485 +2000 13487 +1999 13489 +1997 13491 +1995 13493 +1993 13495 +1991 13496 +1989 13498 +1987 13500 +1985 13502 +1983 13504 +1981 13506 +1979 13507 +1977 13509 +1975 13511 +1973 13513 +1972 13515 +1970 13516 +1968 13518 +1966 13520 +1964 13522 +1962 13524 +1960 13525 +1958 13527 +1956 13529 +1955 13531 +1953 13532 +1951 13534 +1949 13536 +1947 13538 +1945 13539 +1943 13541 +1942 13543 +1940 13545 +1938 13546 +1936 13548 +1934 13550 +1932 13551 +1931 13553 +1929 13555 +1927 13557 +1925 13558 +1923 13560 +1921 13562 +1920 13563 +1918 13565 +1916 13567 +1914 13568 +1912 13570 +1911 13572 +1909 13573 +1907 13575 +1905 13577 +1903 13578 +1902 13580 +1900 13582 +1898 13583 +1896 13585 +1895 13587 +1893 13588 +1891 13590 +1889 13592 +1888 13593 +1886 13595 +1884 13596 +1882 13598 +1881 13600 +1879 13601 +1877 13603 +1875 13604 +1874 13606 +1872 13608 +1870 13609 +1868 13611 +1867 13612 +1865 13614 +1863 13616 +1862 13617 +1860 13619 +1858 13620 +1856 13622 +1855 13623 +1853 13625 +1851 13627 +1850 13628 +1848 13630 +1846 13631 +1845 13633 +1843 13634 +1841 13636 +1839 13637 +1838 13639 +1836 13640 +1834 13642 +1833 13643 +1831 13645 +1829 13646 +1828 13648 +1826 13649 +1824 13651 +1823 13652 +1821 13654 +1819 13655 +1818 13657 +1816 13658 +1814 13660 +1813 13661 +1811 13663 +1810 13664 +1808 13666 +1806 13667 +1805 13668 +1803 13670 +1801 13671 +1800 13673 +1798 13674 +1796 13676 +1795 13677 +1793 13679 +1792 13680 +1790 13682 +1788 13683 +1787 13684 +1785 13686 +1784 13687 +1782 13689 +1780 13690 +1779 13692 +1777 13693 +1776 13694 +1774 13696 +1772 13697 +1771 13699 +1769 13700 +1768 13701 +1766 13703 +1765 13704 +1763 13706 +1761 13707 +1760 13709 +1758 13710 +1757 13711 +1755 13713 +1754 13714 +1752 13716 +1750 13717 +1749 13719 +1747 13720 +1746 13721 +1744 13723 +1743 13724 +1741 13726 +1739 13727 +1738 13729 +1736 13730 +1735 13732 +1733 13733 +1732 13735 +1730 13737 +1729 13738 +1727 13740 +1725 13741 +1724 13743 +1722 13744 +1721 13746 +1719 13747 +1718 13749 +1716 13751 +1715 13752 +1713 13754 +1711 13755 +1710 13757 +1708 13759 +1707 13760 +1705 13762 +1704 13763 +1702 13765 +1701 13767 +1699 13768 +1698 13770 +1696 13771 +1695 13773 +1693 13775 +1692 13776 +1690 13778 +1688 13780 +1687 13781 +1685 13783 +1684 13784 +1682 13786 +1681 13788 +1679 13789 +1678 13791 +1676 13792 +1675 13794 +1673 13796 +1672 13797 +1670 13799 +1669 13801 +1667 13802 +1666 13804 +1664 13805 +1663 13807 +1661 13809 +1660 13810 +1658 13812 +1657 13814 +1655 13815 +1654 13817 +1652 13818 +1651 13820 +1649 13822 +1648 13823 +1646 13825 +1645 13826 +1643 13828 +1642 13830 +1640 13831 +1639 13833 +1637 13834 +1636 13836 +1634 13838 +1633 13839 +1631 13841 +1630 13842 +1628 13844 +1627 13846 +1625 13847 +1624 13849 +1622 13850 +1621 13852 +1619 13854 +1618 13855 +1616 13857 +1615 13858 +1613 13860 +1612 13862 +1611 13863 +1609 13865 +1608 13866 +1606 13868 +1605 13870 +1603 13871 +1602 13873 +1600 13874 +1599 13876 +1597 13878 +1596 13879 +1594 13881 +1593 13882 +1591 13884 +1590 13885 +1588 13887 +1587 13889 +1586 13890 +1584 13892 +1583 13893 +1581 13895 +1580 13896 +1578 13898 +1577 13900 +1575 13901 +1574 13903 +1572 13904 +1571 13906 +1570 13907 +1568 13909 +1567 13910 +1565 13912 +1564 13914 +1562 13915 +1561 13917 +1559 13918 +1558 13920 +1557 13921 +1555 13923 +1554 13924 +1552 13926 +1551 13927 +1549 13929 +1548 13930 +1547 13932 +1545 13933 +1544 13935 +1542 13936 +1541 13938 +1539 13939 +1538 13941 +1537 13942 +1535 13944 +1534 13945 +1532 13947 +1531 13948 +1530 13950 +1528 13951 +1527 13953 +1525 13954 +1524 13956 +1523 13957 +1521 13959 +1520 13960 +1518 13962 +1517 13963 +1516 13964 +1514 13966 +1513 13967 +1511 13969 +1510 13970 +1509 13972 +1507 13973 +1506 13975 +1504 13976 +1503 13977 +1502 13979 +1500 13980 +1499 13982 +1497 13983 +1496 13985 +1495 13986 +1493 13987 +1492 13989 +1491 13990 +1489 13992 +1488 13993 +1487 13995 +1485 13996 +1484 13997 +1482 13999 +1481 14000 +1480 14002 +1478 14003 +1477 14004 +1476 14006 +1474 14007 +1473 14009 +1472 14010 +1470 14011 +1469 14013 +1468 14014 +1466 14016 +1465 14017 +1464 14018 +1462 14020 +1461 14021 +1460 14022 +1458 14024 +1457 14025 +1456 14027 +1454 14028 +1453 14029 +1452 14031 +1451 14032 +1449 14033 +1448 14035 +1447 14036 +1445 14038 +1444 14039 +1443 14040 +1441 14042 +1440 14043 +1439 14044 +1437 14046 +1436 14047 +1435 14049 +1434 14050 +1432 14051 +1431 14053 +1430 14054 +1428 14055 +1427 14057 +1426 14058 +1425 14059 +1423 14061 +1422 14062 +1421 14064 +1419 14065 +1418 14066 +1417 14068 +1416 14069 +1414 14070 +1413 14072 +1412 14073 +1410 14074 +1409 14076 +1408 14077 +1407 14078 +1405 14080 +1404 14081 +1403 14082 +1402 14084 +1400 14085 +1399 14086 +1398 14088 +1397 14089 +1395 14090 +1394 14092 +1393 14093 +1392 14094 +1390 14096 +1389 14097 +1388 14098 +1387 14100 +1385 14101 +1384 14102 +1383 14104 +1382 14105 +1380 14106 +1379 14108 +1378 14109 +1377 14110 +1375 14112 +1374 14113 +1373 14114 +1372 14115 +1370 14117 +1369 14118 +1368 14119 +1367 14121 +1365 14122 +1364 14123 +1363 14125 +1362 14126 +1361 14127 +1359 14129 +1358 14130 +1357 14131 +1356 14132 +1355 14134 +1353 14135 +1352 14136 +1351 14138 +1350 14139 +1348 14140 +1347 14141 +1346 14143 +1345 14144 +1344 14145 +1342 14147 +1341 14148 +1340 14149 +1339 14150 +1338 14152 +1336 14153 +1335 14154 +1334 14155 +1333 14157 +1332 14158 +1330 14159 +1329 14161 +1328 14162 +1327 14163 +1326 14164 +1325 14166 +1323 14167 +1322 14168 +1321 14169 +1320 14171 +1319 14172 +1317 14173 +1316 14174 +1315 14176 +1314 14177 +1313 14178 +1312 14179 +1310 14181 +1309 14182 +1308 14183 +1307 14184 +1306 14186 +1305 14187 +1303 14188 +1302 14189 +1301 14191 +1300 14192 +1299 14193 +1298 14194 +1296 14195 +1295 14197 +1294 14198 +1293 14199 +1292 14200 +1291 14202 +1290 14203 +1288 14204 +1287 14205 +1286 14206 +1285 14208 +1284 14209 +1283 14210 +1282 14211 +1280 14212 +1279 14214 +1278 14215 +1277 14216 +1276 14217 +1275 14218 +1274 14220 +1272 14221 +1271 14222 +1270 14223 +1269 14224 +1268 14226 +1267 14227 +1266 14228 +1265 14229 +1263 14230 +1262 14231 +1261 14233 +1260 14234 +1259 14235 +1258 14236 +1257 14237 +1256 14238 +1255 14240 +1253 14241 +1252 14242 +1251 14243 +1250 14244 +1249 14245 +1248 14246 +1247 14248 +1246 14249 +1245 14250 +1244 14251 +1242 14252 +1241 14253 +1240 14254 +1239 14256 +1238 14257 +1237 14258 +1236 14259 +1235 14260 +1234 14261 +1233 14262 +1232 14263 +1231 14265 +1229 14266 +1228 14267 +1227 14268 +1226 14269 +1225 14270 +1224 14271 +1223 14272 +1222 14273 +1221 14275 +1220 14276 +1219 14277 +1218 14278 +1217 14279 +1216 14280 +1215 14281 +1213 14282 +1212 14283 +1211 14284 +1210 14286 +1209 14287 +1208 14288 +1207 14289 +1206 14290 +1205 14291 +1204 14292 +1203 14293 +1202 14294 +1201 14295 +1200 14296 +1199 14297 +1198 14298 +1197 14300 +1196 14301 +1195 14302 +1194 14303 +1193 14304 +1192 14305 +1191 14306 +1190 14307 +1188 14308 +1187 14309 +1186 14310 +1185 14311 +1184 14312 +1183 14313 +1182 14314 +1181 14315 +1180 14316 +1179 14317 +1178 14318 +1177 14320 +1176 14321 +1175 14322 +1174 14323 +1173 14324 +1172 14325 +1171 14326 +1170 14327 +1169 14328 +1168 14329 +1167 14330 +1166 14331 +1165 14332 +1164 14333 +1163 14334 +1162 14335 +1161 14336 +1160 14337 +1159 14338 +1158 14339 +1158 14340 +1157 14341 +1156 14342 +1155 14343 +1154 14344 +1153 14345 +1152 14346 +1151 14347 +1150 14348 +1149 14349 +1148 14350 +1147 14351 +1146 14352 +1145 14353 +1144 14354 +1143 14355 +1142 14356 +1141 14357 +1140 14358 +1139 14359 +1138 14360 +1137 14361 +1136 14362 +1135 14363 +1134 14364 +1133 14365 +1133 14366 +1132 14367 +1131 14367 +1130 14368 +1129 14369 +1128 14370 +1127 14371 +1126 14372 +1125 14373 +1124 14374 +1123 14375 +1122 14376 +1121 14377 +1120 14378 +1119 14379 +1118 14380 +1117 14381 +1117 14382 +1116 14383 +1115 14384 +1114 14385 +1113 14386 +1112 14387 +1111 14388 +1110 14389 +1109 14389 +1108 14390 +1107 14391 +1106 14392 +1105 14393 +1105 14394 +1104 14395 +1103 14396 +1102 14397 +1101 14398 +1100 14399 +1099 14400 +1098 14401 +1097 14402 +1096 14403 +1095 14403 +1094 14404 +1094 14405 +1093 14406 +1092 14407 +1091 14408 +1090 14409 +1089 14410 +1088 14411 +1087 14412 +1086 14413 +1085 14414 +1085 14414 +1084 14415 +1083 14416 +1082 14417 +1081 14418 +1080 14419 +1079 14420 +1078 14421 +1077 14422 +1077 14423 +1076 14424 +1075 14424 +1074 14425 +1073 14426 +1072 14427 +1071 14428 +1070 14429 +1069 14430 +1069 14431 +1068 14432 +1067 14433 +1066 14433 +1065 14434 +1064 14435 +1063 14436 +1062 14437 +1061 14438 +1061 14439 +1060 14440 +1059 14440 +1058 14441 +1057 14442 +1056 14443 +1055 14444 +1054 14445 +1054 14446 +1053 14447 +1052 14447 +1051 14448 +1050 14449 +1049 14450 +1048 14451 +1048 14452 +1047 14453 +1046 14454 +1045 14454 +1044 14455 +1043 14456 +1042 14457 +1041 14458 +1041 14459 +1040 14460 +1039 14460 +1038 14461 +1037 14462 +1036 14463 +1035 14464 +1035 14465 +1034 14465 +1033 14466 +1032 14467 +1031 14468 +1030 14469 +1029 14470 +1029 14471 +1028 14471 +1027 14472 +1026 14473 +1025 14474 +1024 14475 +1024 14476 +1023 14476 +1022 14477 +1021 14478 +1020 14479 +1019 14480 +1018 14480 +1018 14481 +1017 14482 +1016 14483 +1015 14484 +1014 14485 +1013 14485 +1013 14486 +1012 14487 +1011 14488 +1010 14489 +1009 14489 +1008 14490 +1007 14491 +1007 14492 +1006 14493 +1005 14493 +1004 14494 +1003 14495 +1002 14496 +1002 14497 +1001 14497 +1000 14498 +999 14499 +998 14500 +997 14501 +997 14501 +996 14502 +995 14503 +994 14504 +993 14505 +992 14505 +991 14506 +991 14507 +990 14508 +989 14508 +988 14509 +987 14510 +986 14511 +986 14512 +985 14512 +984 14513 +983 14514 +982 14515 +981 14515 +981 14516 +980 14517 +979 14518 +978 14519 +977 14519 +976 14520 +976 14521 +975 14522 +974 14522 +973 14523 +972 14524 +971 14525 +971 14525 +970 14526 +969 14527 +968 14528 +967 14528 +966 14529 +966 14530 +965 14531 +964 14531 +963 14532 +962 14533 +961 14534 +961 14535 +960 14535 +959 14536 +958 14537 +957 14538 +956 14538 +956 14539 +955 14540 +954 14541 +953 14541 +952 14542 +952 14543 +951 14544 +950 14544 +949 14545 +948 14546 +947 14547 +947 14547 +946 14548 +945 14549 +944 14550 +943 14550 +943 14551 +942 14552 +941 14553 +940 14554 +939 14554 +938 14555 +938 14556 +937 14557 +936 14557 +935 14558 +934 14559 +934 14560 +933 14560 +932 14561 +931 14562 +930 14563 +930 14563 +929 14564 +928 14565 +927 14566 +926 14566 +926 14567 +925 14568 +924 14569 +923 14569 +922 14570 +922 14571 +921 14572 +920 14572 +919 14573 +918 14574 +918 14575 +917 14575 +916 14576 +915 14577 +914 14578 +914 14578 +913 14579 +912 14580 +911 14581 +910 14581 +910 14582 +909 14583 +908 14584 +907 14584 +907 14585 +906 14586 +905 14587 +904 14587 +903 14588 +903 14589 +902 14590 +901 14590 +900 14591 +899 14592 +899 14593 +898 14593 +897 14594 +896 14595 +896 14595 +895 14596 +894 14597 +893 14598 +892 14598 +892 14599 +891 14600 +890 14601 +889 14601 +889 14602 +888 14603 +887 14604 +886 14604 +885 14605 +885 14606 +884 14607 +883 14607 +882 14608 +882 14609 +881 14609 +880 14610 +879 14611 +879 14612 +878 14612 +877 14613 +876 14614 +875 14615 +875 14615 +874 14616 +873 14617 +872 14618 +872 14618 +871 14619 +870 14620 +869 14620 +869 14621 +868 14622 +867 14623 +866 14623 +866 14624 +865 14625 +864 14626 +863 14626 +863 14627 +862 14628 +861 14628 +860 14629 +860 14630 +859 14631 +858 14631 +857 14632 +857 14633 +856 14634 +855 14634 +854 14635 +854 14636 +853 14636 +852 14637 +851 14638 +851 14639 +850 14639 +849 14640 +848 14641 +848 14641 +847 14642 +846 14643 +845 14644 +845 14644 +844 14645 +843 14646 +842 14647 +842 14647 +841 14648 +840 14649 +839 14649 +839 14650 +838 14651 +837 14652 +836 14652 +836 14653 +835 14654 +834 14654 +833 14655 +833 14656 +832 14657 +831 14657 +831 14658 +830 14659 +829 14659 +828 14660 +828 14661 +827 14662 +826 14662 +825 14663 +825 14664 +824 14664 +823 14665 +823 14666 +822 14667 +821 14667 +820 14668 +820 14669 +819 14669 +818 14670 +817 14671 +817 14671 +816 14672 +815 14673 +815 14674 +814 14674 +813 14675 +812 14676 +812 14676 +811 14677 +810 14678 +810 14678 +809 14679 +808 14680 +807 14681 +807 14681 +806 14682 +805 14683 +805 14683 +804 14684 +803 14685 +802 14685 +802 14686 +801 14687 +800 14688 +800 14688 +799 14689 +798 14690 +797 14690 +797 14691 +796 14692 +795 14692 +795 14693 +794 14694 +793 14694 +792 14695 +792 14696 +791 14697 +790 14697 +790 14698 +789 14699 +788 14699 +788 14700 +787 14701 +786 14701 +785 14702 +785 14703 +784 14703 +783 14704 +783 14705 +782 14705 +781 14706 +780 14707 +780 14707 +779 14708 +778 14709 +778 14709 +777 14710 +776 14711 +776 14711 +775 14712 +774 14713 +773 14713 +773 14714 +772 14715 +771 14715 +771 14716 +770 14717 +769 14717 +769 14718 +768 14719 +767 14719 +766 14720 +766 14721 +765 14721 +764 14722 +764 14723 +763 14723 +762 14724 +762 14725 +761 14725 +760 14726 +760 14727 +759 14727 +758 14728 +757 14728 +757 14729 +756 14730 +755 14730 +755 14731 +754 14732 +753 14732 +753 14733 +752 14734 +751 14734 +751 14735 +750 14735 +749 14736 +749 14737 +748 14737 +747 14738 +746 14739 +746 14739 +745 14740 +744 14741 +744 14741 +743 14742 +742 14742 +742 14743 +741 14744 +740 14744 +740 14745 +739 14746 +738 14746 +738 14747 +737 14747 +736 14748 +736 14749 +735 14749 +734 14750 +734 14751 +733 14751 +732 14752 +732 14752 +731 14753 +730 14754 +730 14754 +729 14755 +728 14756 +728 14756 +727 14757 +726 14757 +726 14758 +725 14759 +724 14759 +724 14760 +723 14760 +722 14761 +722 14762 +721 14762 +720 14763 +720 14764 +719 14764 +718 14765 +718 14765 +717 14766 +716 14767 +716 14767 +715 14768 +715 14768 +714 14769 +713 14770 +713 14770 +712 14771 +711 14771 +711 14772 +710 14773 +709 14773 +709 14774 +708 14775 +707 14775 +707 14776 +706 14776 +706 14777 +705 14778 +704 14778 +704 14779 +703 14779 +702 14780 +702 14780 +701 14781 +700 14782 +700 14782 +699 14783 +699 14783 +698 14784 +697 14785 +697 14785 +696 14786 +695 14786 +695 14787 +694 14788 +694 14788 +693 14789 +692 14789 +692 14790 +691 14791 +690 14791 +690 14792 +689 14792 +689 14793 +688 14793 +687 14794 +687 14795 +686 14795 +685 14796 +685 14796 +684 14797 +684 14797 +683 14798 +682 14799 +682 14799 +681 14800 +681 14800 +680 14801 +679 14801 +679 14802 +678 14803 +677 14803 +677 14804 +676 14804 +676 14805 +675 14805 +674 14806 +674 14806 +673 14807 +673 14808 +672 14808 +671 14809 +671 14809 +670 14810 +670 14810 +669 14811 +669 14811 +668 14812 +667 14813 +667 14813 +666 14814 +666 14814 +665 14815 +664 14815 +664 14816 +663 14816 +663 14817 +662 14817 +661 14818 +661 14819 +660 14819 +660 14820 +659 14820 +659 14821 +658 14821 +657 14822 +657 14822 +656 14823 +656 14823 +655 14824 +654 14824 +654 14825 +653 14826 +653 14826 +652 14827 +652 14827 +651 14828 +650 14828 +650 14829 +649 14829 +649 14830 +648 14830 +648 14831 +647 14831 +647 14832 +646 14832 +645 14833 +645 14833 +644 14834 +644 14834 +643 14835 +643 14835 +642 14836 +641 14836 +641 14837 +640 14838 +640 14838 +639 14839 +639 14839 +638 14840 +638 14840 +637 14841 +637 14841 +636 14842 +635 14842 +635 14843 +634 14843 +634 14844 +633 14844 +633 14845 +632 14845 +632 14846 +631 14846 +631 14847 +630 14847 +629 14848 +629 14848 +628 14849 +628 14849 +627 14850 +627 14850 +626 14851 +626 14851 +625 14852 +625 14852 +624 14853 +624 14853 +623 14854 +623 14854 +622 14855 +621 14855 +621 14855 +620 14856 +620 14856 +619 14857 +619 14857 +618 14858 +618 14858 +617 14859 +617 14859 +616 14860 +616 14860 +615 14861 +615 14861 +614 14862 +614 14862 +613 14863 +613 14863 +612 14864 +612 14864 +611 14865 +611 14865 +610 14866 +610 14866 +609 14866 +609 14867 +608 14867 +608 14868 +607 14868 +607 14869 +606 14869 +606 14870 +605 14870 +605 14871 +604 14871 +604 14872 +603 14872 +603 14873 +602 14873 +602 14873 +601 14874 +601 14874 +600 14875 +600 14875 +599 14876 +599 14876 +598 14877 +598 14877 +597 14878 +597 14878 +596 14878 +596 14879 +595 14879 +595 14880 +594 14880 +594 14881 +593 14881 +593 14882 +592 14882 +592 14883 +591 14883 +591 14883 +590 14884 +590 14884 +589 14885 +589 14885 +589 14886 +588 14886 +588 14887 +587 14887 +587 14887 +586 14888 +586 14888 +585 14889 +585 14889 +584 14890 +584 14890 +583 14891 +583 14891 +582 14892 +582 14892 +582 14892 +581 14893 +581 14893 +580 14894 +580 14894 +579 14895 +579 14895 +578 14896 +578 14896 +577 14897 +577 14897 +576 14897 +576 14898 +576 14898 +575 14899 +575 14899 +574 14900 +574 14900 +573 14901 +573 14901 +572 14901 +572 14902 +572 14902 +571 14903 +571 14903 +570 14904 +570 14904 +569 14905 +569 14905 +568 14906 +568 14906 +568 14906 +567 14907 +567 14907 +566 14908 +566 14908 +565 14909 +565 14909 +564 14909 +564 14910 +564 14910 +563 14911 +563 14911 +562 14912 +562 14912 +561 14913 +561 14913 +561 14913 +560 14914 +560 14914 +559 14915 +559 14915 +559 14916 +558 14916 +558 14916 +557 14917 +557 14917 +556 14918 +556 14918 +556 14919 +555 14919 +555 14919 +554 14920 +554 14920 +553 14921 +553 14921 +553 14922 +552 14922 +552 14922 +551 14923 +551 14923 +551 14924 +550 14924 +550 14925 +549 14925 +549 14925 +549 14926 +548 14926 +548 14927 +547 14927 +547 14928 +546 14928 +546 14928 +546 14929 +545 14929 +545 14930 +544 14930 +544 14931 +544 14931 +543 14931 +543 14932 +542 14932 +542 14933 +542 14933 +541 14934 +541 14934 +540 14934 +540 14935 +539 14935 +539 14936 +539 14936 +538 14937 +538 14937 +537 14937 +537 14938 +537 14938 +536 14939 +536 14939 +535 14940 +535 14940 +535 14940 +534 14941 +534 14941 +533 14942 +533 14942 +533 14943 +532 14943 +532 14943 +531 14944 +531 14944 +531 14945 +530 14945 +530 14946 +529 14946 +529 14946 +529 14947 +528 14947 +528 14948 +527 14948 +527 14949 +527 14949 +526 14949 +526 14950 +525 14950 +525 14951 +525 14951 +524 14952 +524 14952 +524 14952 +523 14953 +523 14953 +522 14954 +522 14954 +522 14954 +521 14955 +521 14955 +520 14956 +520 14956 +520 14957 +519 14957 +519 14957 +518 14958 +518 14958 +518 14959 +517 14959 +517 14960 +517 14960 +516 14960 +516 14961 +515 14961 +515 14962 +515 14962 +514 14963 +514 14963 +514 14963 +513 14964 +513 14964 +512 14965 +512 14965 +512 14966 +511 14966 +511 14966 +511 14967 +510 14967 +510 14968 +509 14968 +509 14969 +509 14969 +508 14969 +508 14970 +508 14970 +507 14971 +507 14971 +506 14972 +506 14972 +506 14972 +505 14973 +505 14973 +505 14974 +504 14974 +504 14975 +503 14975 +503 14976 +503 14976 +502 14976 +502 14977 +502 14977 +501 14978 +501 14978 +500 14979 +500 14979 +500 14979 +499 14980 +499 14980 +499 14981 +498 14981 +498 14982 +497 14982 +497 14982 +497 14983 +496 14983 +496 14984 +496 14984 +495 14985 +495 14985 +495 14985 +494 14986 +494 14986 +493 14987 +493 14987 +493 14988 +492 14988 +492 14989 +492 14989 +491 14989 +491 14990 +491 14990 +490 14991 +490 14991 +490 14992 +489 14992 +489 14992 +488 14993 +488 14993 +488 14994 +487 14994 +487 14995 +487 14995 +486 14995 +486 14996 +486 14996 +485 14997 +485 14997 +485 14998 +484 14998 +484 14999 +483 14999 +483 14999 +483 15000 +482 15000 +482 15001 +482 15001 +481 15002 +481 15002 +481 15003 +480 15003 +480 15003 +480 15004 +479 15004 +479 15005 +479 15005 +478 15006 +478 15006 +478 15006 +477 15007 +477 15007 +476 15008 +476 15008 +476 15009 +475 15009 +475 15009 +475 15010 +474 15010 +474 15011 +474 15011 +473 15012 +473 15012 +473 15012 +472 15013 +472 15013 +472 15014 +471 15014 +471 15015 +471 15015 +470 15016 +470 15016 +470 15016 +469 15017 +469 15017 +469 15018 +468 15018 +468 15019 +468 15019 +467 15019 +467 15020 +467 15020 +466 15021 +466 15021 +466 15022 +465 15022 +465 15022 +464 15023 +464 15023 +464 15024 +463 15024 +463 15025 +463 15025 +462 15025 +462 15026 +462 15026 +461 15027 +461 15027 +461 15028 +460 15028 +460 15028 +460 15029 +459 15029 +459 15030 +459 15030 +458 15031 +458 15031 +458 15031 +457 15032 +457 15032 +457 15033 +456 15033 +456 15034 +456 15034 +455 15034 +455 15035 +455 15035 +454 15036 +454 15036 +454 15036 +453 15037 +453 15037 +453 15038 +452 15038 +452 15039 +452 15039 +451 15039 +451 15040 +451 15040 +450 15041 +450 15041 +450 15042 +449 15042 +449 15042 +449 15043 +448 15043 +448 15044 +448 15044 +447 15044 +447 15045 +447 15045 +446 15046 +446 15046 +446 15047 +445 15047 +445 15047 +445 15048 +444 15048 +444 15049 +444 15049 +443 15049 +443 15050 +443 15050 +443 15051 +442 15051 +442 15051 +442 15052 +441 15052 +441 15053 +441 15053 +440 15054 +440 15054 +440 15054 +439 15055 +439 15055 +439 15056 +438 15056 +438 15056 +438 15057 +437 15057 +437 15058 +437 15058 +436 15058 +436 15059 +436 15059 +435 15060 +435 15060 +435 15060 +434 15061 +434 15061 +434 15062 +433 15062 +433 15062 +433 15063 +432 15063 +432 15064 +432 15064 +432 15064 +431 15065 +431 15065 +431 15066 +430 15066 +430 15066 +430 15067 +429 15067 +429 15068 +429 15068 +428 15068 +428 15069 +428 15069 +427 15070 +427 15070 +427 15070 +427 15071 +426 15071 +426 15071 +426 15072 +425 15072 +425 15073 +425 15073 +424 15073 +424 15074 +424 15074 +423 15075 +423 15075 +423 15075 +423 15076 +422 15076 +422 15077 +422 15077 +421 15077 +421 15078 +421 15078 +420 15078 +420 15079 +420 15079 +419 15080 +419 15080 +419 15080 +419 15081 +418 15081 +418 15082 +418 15082 +417 15082 +417 15083 +417 15083 +416 15083 +416 15084 +416 15084 +416 15085 +415 15085 +415 15085 +415 15086 +414 15086 +414 15087 +414 15087 +413 15087 +413 15088 +413 15088 +413 15088 +412 15089 +412 15089 +412 15090 +411 15090 +411 15090 +411 15091 +410 15091 +410 15091 +410 15092 +410 15092 +409 15093 +409 15093 +409 15093 +408 15094 +408 15094 +408 15094 +408 15095 +407 15095 +407 15096 +407 15096 +406 15096 +406 15097 +406 15097 +405 15097 +405 15098 +405 15098 +405 15098 +404 15099 +404 15099 +404 15100 +403 15100 +403 15100 +403 15101 +403 15101 +402 15101 +402 15102 +402 15102 +401 15103 +401 15103 +401 15103 +400 15104 +400 15104 +400 15104 +400 15105 +399 15105 +399 15105 +399 15106 +398 15106 +398 15107 +398 15107 +398 15107 +397 15108 +397 15108 +397 15108 +396 15109 +396 15109 +396 15110 +396 15110 +395 15110 +395 15111 +395 15111 +394 15111 +394 15112 +394 15112 +394 15112 +393 15113 +393 15113 +393 15113 +392 15114 +392 15114 +392 15115 +392 15115 +391 15115 +391 15116 +391 15116 +390 15116 +390 15117 +390 15117 +390 15117 +389 15118 +389 15118 +389 15118 +388 15119 +388 15119 +388 15119 +388 15120 +387 15120 +387 15120 +387 15121 +386 15121 +386 15121 +386 15122 +386 15122 +385 15123 +385 15123 +385 15123 +384 15124 +384 15124 +384 15124 +384 15125 +383 15125 +383 15125 +383 15126 +383 15126 +382 15126 +382 15127 +382 15127 +381 15127 +381 15128 +381 15128 +381 15128 +380 15129 +380 15129 +380 15129 +380 15130 +379 15130 +379 15130 +379 15131 +378 15131 +378 15131 +378 15131 +378 15132 +377 15132 +377 15132 +377 15133 +377 15133 +376 15133 +376 15134 +376 15134 +375 15134 +375 15135 +375 15135 +375 15135 +374 15136 +374 15136 +374 15136 +374 15137 +373 15137 +373 15137 +373 15138 +373 15138 +372 15138 +372 15138 +372 15139 +372 15139 +371 15139 +371 15140 +371 15140 +371 15140 +370 15141 +370 15141 +370 15141 +369 15142 +369 15142 +369 15142 +369 15142 +368 15143 +368 15143 +368 15143 +368 15144 +367 15144 +367 15144 +367 15145 +367 15145 +366 15145 +366 15145 +366 15146 +366 15146 +365 15146 +365 15147 +365 15147 +365 15147 +364 15147 +364 15148 +364 15148 +364 15148 +363 15149 +363 15149 +363 15149 +363 15149 +362 15150 +362 15150 +362 15150 +362 15151 +361 15151 +361 15151 +361 15151 +361 15152 +361 15152 +360 15152 +360 15152 +360 15153 +360 15153 +359 15153 +359 15154 +359 15154 +359 15154 +358 15154 +358 15155 +358 15155 +358 15155 +357 15155 +357 15156 +357 15156 +357 15156 +357 15156 +356 15157 +356 15157 +356 15157 +356 15157 +355 15158 +355 15158 +355 15158 +355 15158 +354 15159 +354 15159 +354 15159 +354 15159 +354 15160 +353 15160 +353 15160 +353 15160 +353 15161 +352 15161 +352 15161 +352 15161 +352 15162 +352 15162 +351 15162 +351 15162 +351 15163 +351 15163 +350 15163 +350 15163 +350 15163 +350 15164 +350 15164 +349 15164 +349 15164 +349 15165 +349 15165 +349 15165 +348 15165 +348 15166 +348 15166 +348 15166 +348 15166 +347 15166 +347 15167 +347 15167 +347 15167 +346 15167 +346 15168 +346 15168 +346 15168 +346 15168 +345 15168 +345 15169 +345 15169 +345 15169 +345 15169 +344 15170 +344 15170 +344 15170 +344 15170 +344 15170 +343 15171 +343 15171 +343 15171 +343 15171 +343 15171 +342 15172 +342 15172 +342 15172 +342 15172 +342 15173 +341 15173 +341 15173 +341 15173 +341 15173 +341 15174 +341 15174 +340 15174 +340 15174 +340 15174 +340 15175 +340 15175 +339 15175 +339 15175 +339 15175 +339 15175 +339 15176 +338 15176 +338 15176 +338 15176 +338 15176 +338 15177 +338 15177 +337 15177 +337 15177 +337 15177 +337 15178 +337 15178 +336 15178 +336 15178 +336 15178 +336 15179 +336 15179 +336 15179 +335 15179 +335 15179 +335 15179 +335 15180 +335 15180 +334 15180 +334 15180 +334 15180 +334 15180 +334 15181 +334 15181 +333 15181 +333 15181 +333 15181 +333 15182 +333 15182 +333 15182 +332 15182 +332 15182 +332 15182 +332 15183 +332 15183 +332 15183 +331 15183 +331 15183 +331 15183 +331 15184 +331 15184 +331 15184 +330 15184 +330 15184 +330 15184 +330 15185 +330 15185 +330 15185 +329 15185 +329 15185 +329 15185 +329 15185 +329 15186 +329 15186 +328 15186 +328 15186 +328 15186 +328 15186 +328 15187 +328 15187 +328 15187 +327 15187 +327 15187 +327 15187 +327 15187 +327 15188 +327 15188 +326 15188 +326 15188 +326 15188 +326 15188 +326 15188 +326 15189 +326 15189 +325 15189 +325 15189 +325 15189 +325 15189 +325 15189 +325 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +321 15192 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +320 15193 +320 15194 +320 15194 +320 15194 +320 15194 +320 15194 +320 15194 +319 15194 +319 15194 +319 15195 +319 15195 +319 15195 +319 15195 +319 15195 +319 15195 +318 15195 +318 15195 +318 15195 +318 15196 +318 15196 +318 15196 +318 15196 +318 15196 +317 15196 +317 15196 +317 15196 +317 15196 +317 15196 +317 15197 +317 15197 +317 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15198 +316 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +314 15198 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +313 15199 +313 15199 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +312 15200 +312 15200 +312 15200 +312 15200 +312 15201 +312 15201 +312 15201 +312 15201 +312 15201 +312 15201 +311 15201 +311 15201 +311 15201 +311 15201 +311 15202 +311 15202 +311 15202 +311 15202 +311 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15203 +310 15203 +310 15203 +310 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15204 +309 15204 +309 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15205 +308 15205 +308 15205 +308 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15206 +307 15206 +307 15206 +307 15206 +307 15206 +306 15206 +306 15206 +306 15206 +306 15206 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +305 15207 +305 15207 +305 15207 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +304 15208 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15211 +303 15211 +303 15211 +302 15211 +302 15211 +302 15211 +302 15211 +302 15211 +302 15212 +302 15212 +302 15212 +302 15212 +302 15212 +302 15212 +301 15212 +301 15212 +301 15212 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +300 15213 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15216 +299 15216 +299 15216 +299 15216 +298 15216 +298 15216 +298 15216 +298 15216 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +297 15217 +297 15217 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15220 +296 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15221 +295 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15222 +294 15222 +294 15222 +294 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15223 +293 15223 +293 15223 +293 15223 +293 15223 +292 15223 +292 15223 +292 15223 +292 15223 +292 15223 +292 15224 +292 15224 +292 15224 +292 15224 +292 15224 +292 15224 +291 15224 +291 15224 +291 15224 +291 15224 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +290 15225 +290 15225 +290 15225 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +289 15226 +289 15226 +289 15226 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +288 15227 +288 15227 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +287 15228 +287 15228 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +286 15229 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +285 15230 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15235 +282 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15236 +281 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15237 +280 15237 +280 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15238 +279 15238 +279 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15239 +278 15239 +278 15239 +278 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15240 +277 15240 +277 15240 +277 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15241 +276 15241 +276 15241 +276 15241 +275 15241 +275 15241 +275 15241 +275 15241 +275 15241 +275 15242 +275 15242 +275 15242 +275 15242 +275 15242 +274 15242 +274 15242 +274 15242 +274 15242 +274 15242 +274 15243 +274 15243 +274 15243 +274 15243 +274 15243 +273 15243 +273 15243 +273 15243 +273 15243 +273 15243 +273 15244 +273 15244 +273 15244 +273 15244 +272 15244 +272 15244 +272 15244 +272 15244 +272 15244 +272 15245 +272 15245 +272 15245 +272 15245 +272 15245 +271 15245 +271 15245 +271 15245 +271 15245 +271 15245 +271 15246 +271 15246 +271 15246 +271 15246 +271 15246 +270 15246 +270 15246 +270 15246 +270 15246 +270 15247 +270 15247 +270 15247 +270 15247 +270 15247 +270 15247 +269 15247 +269 15247 +269 15247 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +268 15248 +268 15248 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +267 15249 +267 15249 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +266 15250 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +265 15251 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +264 15252 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15257 +261 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15258 +260 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15259 +259 15259 +259 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15260 +258 15260 +258 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15261 +257 15261 +257 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15262 +256 15262 +256 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15263 +255 15263 +255 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15264 +254 15264 +254 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15265 +253 15265 +253 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15266 +252 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15267 +251 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +245 15271 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +244 15272 +244 15272 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +243 15273 +243 15273 +243 15273 +243 15274 +243 15274 +243 15274 +243 15274 +243 15274 +243 15274 +242 15274 +242 15274 +242 15274 +242 15274 +242 15275 +242 15275 +242 15275 +242 15275 +242 15275 +242 15275 +241 15275 +241 15275 +241 15275 +241 15275 +241 15275 +241 15276 +241 15276 +241 15276 +241 15276 +241 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15277 +240 15277 +240 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15278 +239 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +236 15279 +236 15279 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +235 15280 +235 15280 +235 15280 +235 15281 +235 15281 +235 15281 +235 15281 +235 15281 +235 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15282 +234 15282 +234 15282 +234 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +231 15283 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +230 15284 +230 15284 +230 15284 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +229 15285 +229 15285 +229 15285 +229 15285 +229 15285 +229 15286 +229 15286 +229 15286 +229 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +226 15287 +226 15287 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +225 15288 +225 15288 +225 15288 +225 15288 +225 15289 +225 15289 +225 15289 +225 15289 +225 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15290 +224 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +222 15290 +222 15290 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +221 15291 +221 15291 +221 15291 +221 15291 +221 15291 +221 15292 +221 15292 +221 15292 +221 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +218 15293 +218 15293 +218 15293 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15295 +217 15295 +217 15295 +217 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +214 15296 +214 15296 +214 15296 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15298 +213 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +211 15298 +211 15298 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15300 +210 15300 +210 15300 +210 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +207 15301 +207 15301 +207 15301 +207 15301 +207 15302 +207 15302 +207 15302 +207 15302 +207 15302 +207 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15303 +206 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +204 15303 +204 15303 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15305 +203 15305 +203 15305 +203 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +201 15305 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +200 15306 +200 15306 +200 15306 +200 15306 +200 15306 +200 15307 +200 15307 +200 15307 +200 15307 +200 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +197 15308 +197 15308 +197 15308 +197 15308 +197 15309 +197 15309 +197 15309 +197 15309 +197 15309 +197 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +194 15310 +194 15310 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15312 +193 15312 +193 15312 +193 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +190 15313 +190 15313 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +189 15314 +189 15314 +189 15314 +189 15314 +189 15315 +189 15315 +189 15315 +189 15315 +189 15315 +189 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15316 +188 15316 +188 15316 +188 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +185 15317 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +184 15318 +184 15318 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +183 15319 +183 15319 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +182 15320 +182 15320 +182 15320 +182 15320 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +181 15321 +181 15321 +181 15321 +181 15321 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +180 15322 +180 15322 +180 15322 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +179 15323 +179 15323 +179 15323 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +178 15324 +178 15324 +178 15324 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +177 15325 +177 15325 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +176 15326 +176 15326 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +175 15327 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15331 +173 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15332 +172 15332 +172 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15333 +171 15333 +171 15333 +171 15333 +171 15333 +171 15333 +170 15333 +170 15333 +170 15333 +170 15333 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +169 15334 +169 15334 +169 15334 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +168 15335 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15338 +167 15338 +167 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15339 +166 15339 +166 15339 +166 15339 +166 15339 +166 15339 +165 15339 +165 15339 +165 15339 +165 15339 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +164 15340 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15342 +164 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15343 +163 15343 +163 15343 +163 15343 +163 15343 +162 15343 +162 15343 +162 15343 +162 15343 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +161 15344 +161 15344 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15347 +160 15347 +160 15347 +160 15347 +159 15347 +159 15347 +159 15347 +159 15347 +159 15347 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +158 15348 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15350 +158 15350 +158 15350 +157 15350 +157 15350 +157 15350 +157 15350 +157 15350 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +156 15351 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15353 +156 15353 +156 15353 +155 15353 +155 15353 +155 15353 +155 15353 +155 15353 +155 15354 +155 15354 +155 15354 +155 15354 +155 15354 +155 15354 +154 15354 +154 15354 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15356 +154 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15357 +153 15357 +153 15357 +153 15357 +153 15357 +152 15357 +152 15357 +152 15357 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15360 +151 15360 +151 15360 +150 15360 +150 15360 +150 15360 +150 15360 +150 15360 +150 15361 +150 15361 +150 15361 +150 15361 +150 15361 +150 15361 +149 15361 +149 15361 +149 15361 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15364 +148 15364 +148 15364 +147 15364 +147 15364 +147 15364 +147 15364 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15367 +146 15367 +146 15367 +146 15367 +146 15367 +145 15367 +145 15367 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15370 +144 15370 +144 15370 +144 15370 +144 15370 +144 15370 +143 15370 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15372 +143 15372 +143 15372 +142 15372 +142 15372 +142 15372 +142 15372 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15375 +141 15375 +141 15375 +141 15375 +140 15375 +140 15375 +140 15375 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15378 +139 15378 +139 15378 +139 15378 +138 15378 +138 15378 +138 15378 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15381 +137 15381 +137 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15382 +136 15382 +136 15382 +136 15382 +136 15382 +135 15382 +135 15382 +135 15382 +135 15382 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +134 15383 +134 15383 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +129 15388 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +128 15389 +128 15389 +128 15389 +128 15390 +128 15390 +128 15390 +128 15390 +128 15390 +128 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +125 15391 +125 15391 +125 15391 +125 15391 +125 15392 +125 15392 +125 15392 +125 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +123 15392 +123 15392 +123 15392 +123 15392 +123 15393 +123 15393 +123 15393 +123 15393 +123 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +117 15394 +117 15394 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +109 15395 +109 15395 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15393 +107 15393 +107 15393 +107 15393 +107 15393 +107 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +105 15393 +105 15393 +105 15393 +105 15393 +105 15393 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15389 +102 15389 +102 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +100 15389 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +99 15388 +99 15388 +99 15388 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +98 15387 +98 15387 +98 15387 +98 15387 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +97 15386 +97 15386 +97 15386 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +91 15382 +91 15382 +91 15382 +91 15382 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15387 +93 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +99 15389 +99 15389 +99 15389 +99 15389 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15391 +100 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15393 +107 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +110 15393 +110 15393 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +116 15392 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +119 15391 +119 15391 +119 15391 +119 15391 +119 15391 +119 15390 +119 15390 +119 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15388 +123 15388 +123 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +125 15388 +125 15388 +125 15388 +125 15388 +125 15387 +125 15387 +125 15387 +125 15387 +125 15387 +125 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15386 +126 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +128 15386 +128 15386 +128 15386 +128 15385 +128 15385 +128 15385 +128 15385 +128 15385 +128 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15384 +129 15384 +129 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +132 15383 +132 15383 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +133 15382 +133 15382 +133 15382 +133 15382 +133 15381 +133 15381 +133 15381 +133 15381 +133 15381 +134 15381 +134 15381 +134 15381 +134 15381 +134 15381 +134 15380 +134 15380 +134 15380 +134 15380 +134 15380 +135 15380 +135 15380 +135 15380 +135 15380 +135 15380 +135 15379 +135 15379 +135 15379 +135 15379 +135 15379 +136 15379 +136 15379 +136 15379 +136 15379 +136 15379 +136 15378 +136 15378 +136 15378 +136 15378 +136 15378 +137 15378 +137 15378 +137 15378 +137 15378 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +138 15377 +138 15377 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15374 +139 15374 +139 15374 +140 15374 +140 15374 +140 15374 +140 15374 +140 15374 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +141 15373 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15371 +141 15371 +141 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +143 15370 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15368 +143 15368 +143 15368 +143 15368 +144 15368 +144 15368 +144 15368 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15366 +144 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +146 15365 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15363 +146 15363 +146 15363 +146 15363 +146 15363 +147 15363 +147 15363 +147 15363 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15361 +147 15361 +147 15361 +148 15361 +148 15361 +148 15361 +148 15361 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15356 +150 15356 +150 15356 +150 15356 +150 15356 +150 15356 +151 15356 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15354 +151 15354 +151 15354 +152 15354 +152 15354 +152 15354 +152 15354 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15351 +153 15351 +153 15351 +153 15351 +153 15351 +154 15351 +154 15351 +154 15351 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15349 +154 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15348 +155 15348 +155 15348 +155 15348 +155 15348 +155 15348 +156 15348 +156 15348 +156 15348 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15345 +157 15345 +157 15345 +157 15345 +157 15345 +158 15345 +158 15345 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15343 +158 15343 +158 15343 +159 15343 +159 15343 +159 15343 +159 15343 +159 15343 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +160 15342 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15340 +160 15340 +160 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15339 +161 15339 +161 15339 +161 15339 +161 15339 +161 15339 +162 15339 +162 15339 +162 15339 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15337 +162 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15336 +163 15336 +163 15336 +163 15336 +163 15336 +164 15336 +164 15336 +164 15336 +164 15336 +164 15336 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +165 15335 +165 15335 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15333 +165 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15332 +166 15332 +166 15332 +166 15332 +167 15332 +167 15332 +167 15332 +167 15332 +167 15332 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +168 15331 +168 15331 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15328 +169 15328 +169 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15327 +170 15327 +170 15327 +170 15327 +171 15327 +171 15327 +171 15327 +171 15327 +171 15327 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +172 15326 +172 15326 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +173 15325 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15321 +175 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +183 15315 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +184 15314 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +185 15313 +185 15313 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +186 15312 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15309 +187 15309 +187 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15308 +188 15308 +188 15308 +188 15308 +188 15308 +188 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +190 15307 +190 15307 +190 15307 +190 15307 +190 15307 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +191 15306 +191 15306 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +192 15305 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15302 +193 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15301 +194 15301 +194 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15300 +195 15300 +195 15300 +195 15300 +195 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15299 +196 15299 +196 15299 +196 15299 +196 15299 +196 15299 +197 15299 +197 15299 +197 15299 +197 15299 +197 15299 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +198 15298 +198 15298 +198 15298 +198 15298 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +199 15297 +199 15297 +199 15297 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +200 15296 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15292 +202 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15291 +203 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15290 +204 15290 +204 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15289 +205 15289 +205 15289 +205 15289 +205 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15288 +206 15288 +206 15288 +206 15288 +206 15288 +206 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +208 15287 +208 15287 +208 15287 +208 15287 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +209 15286 +209 15286 +209 15286 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +210 15285 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15280 +213 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15279 +214 15279 +214 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15278 +215 15278 +215 15278 +215 15278 +215 15278 +216 15278 +216 15278 +216 15278 +216 15278 +216 15278 +216 15277 +216 15277 +216 15277 +216 15277 +216 15277 +216 15277 +217 15277 +217 15277 +217 15277 +217 15277 +217 15277 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +218 15276 +218 15276 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +219 15275 +219 15275 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15272 +220 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15271 +221 15271 +221 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15270 +222 15270 +222 15270 +222 15270 +223 15270 +223 15270 +223 15270 +223 15270 +223 15270 +223 15269 +223 15269 +223 15269 +223 15269 +223 15269 +223 15269 +224 15269 +224 15269 +224 15269 +224 15269 +224 15268 +224 15268 +224 15268 +224 15268 +224 15268 +224 15268 +225 15268 +225 15268 +225 15268 +225 15268 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +226 15267 +226 15267 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15263 +228 15263 +228 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15262 +229 15262 +229 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15261 +230 15261 +230 15261 +230 15261 +230 15261 +231 15261 +231 15261 +231 15261 +231 15261 +231 15261 +231 15260 +231 15260 +231 15260 +231 15260 +231 15260 +232 15260 +232 15260 +232 15260 +232 15260 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +233 15259 +233 15259 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +234 15258 +234 15258 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15254 +236 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15253 +237 15253 +237 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15252 +238 15252 +238 15252 +238 15252 +239 15252 +239 15252 +239 15252 +239 15252 +239 15252 +239 15251 +239 15251 +239 15251 +239 15251 +239 15251 +240 15251 +240 15251 +240 15251 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +241 15250 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15246 +243 15246 +243 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15245 +244 15245 +244 15245 +245 15245 +245 15245 +245 15245 +245 15245 +245 15245 +245 15244 +245 15244 +245 15244 +245 15244 +245 15244 +246 15244 +246 15244 +246 15244 +246 15244 +246 15243 +246 15243 +246 15243 +246 15243 +246 15243 +246 15243 +247 15243 +247 15243 +247 15243 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +248 15242 +248 15242 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +249 15241 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15238 +250 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15237 +251 15237 +251 15237 +251 15237 +252 15237 +252 15237 +252 15237 +252 15237 +252 15237 +252 15236 +252 15236 +252 15236 +252 15236 +252 15236 +253 15236 +253 15236 +253 15236 +253 15236 +253 15235 +253 15235 +253 15235 +253 15235 +253 15235 +253 15235 +254 15235 +254 15235 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +255 15234 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15231 +256 15231 +256 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15230 +257 15230 +257 15230 +257 15230 +258 15230 +258 15230 +258 15230 +258 15230 +258 15229 +258 15229 +258 15229 +258 15229 +258 15229 +258 15229 +259 15229 +259 15229 +259 15229 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +260 15228 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15225 +261 15225 +261 15225 +262 15225 +262 15225 +262 15225 +262 15225 +262 15225 +262 15224 +262 15224 +262 15224 +262 15224 +262 15224 +263 15224 +263 15224 +263 15224 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +264 15223 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15220 +265 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15219 +266 15219 +266 15219 +266 15219 +266 15219 +267 15219 +267 15219 +267 15219 +267 15219 +267 15218 +267 15218 +267 15218 +267 15218 +267 15218 +267 15218 +268 15218 +268 15218 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +269 15217 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15215 +269 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15214 +270 15214 +270 15214 +271 15214 +271 15214 +271 15214 +271 15214 +271 15214 +271 15213 +271 15213 +271 15213 +271 15213 +271 15213 +271 15213 +272 15213 +272 15213 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15210 +273 15210 +273 15210 +273 15210 +274 15210 +274 15210 +274 15210 +274 15210 +274 15209 +274 15209 +274 15209 +274 15209 +274 15209 +274 15209 +275 15209 +275 15209 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15206 +276 15206 +276 15206 +276 15206 +277 15206 +277 15206 +277 15206 +277 15206 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +278 15205 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15202 +279 15202 +279 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15201 +280 15201 +280 15201 +280 15201 +280 15201 +281 15201 +281 15201 +281 15201 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15198 +282 15198 +282 15198 +283 15198 +283 15198 +283 15198 +283 15198 +283 15198 +283 15197 +283 15197 +283 15197 +283 15197 +283 15197 +283 15197 +284 15197 +284 15197 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15194 +285 15194 +285 15194 +285 15194 +285 15194 +286 15194 +286 15194 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15191 +287 15191 +287 15191 +287 15191 +287 15191 +287 15191 +288 15191 +288 15191 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15189 +288 15189 +289 15189 +289 15189 +289 15189 +289 15189 +289 15189 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +290 15187 +290 15187 +290 15187 +290 15187 +290 15187 +290 15187 +290 15186 +290 15186 +290 15186 +290 15186 +290 15186 +290 15186 +291 15186 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15184 +291 15184 +291 15184 +292 15184 +292 15184 +292 15184 +292 15183 +292 15183 +292 15183 +292 15183 +292 15183 +292 15183 +292 15182 +292 15182 +293 15182 +293 15182 +293 15182 +293 15182 +293 15182 +293 15181 +293 15181 +293 15181 +293 15181 +293 15181 +293 15181 +293 15180 +294 15180 +294 15180 +294 15180 +294 15180 +294 15180 +294 15179 +294 15179 +294 15179 +294 15179 +294 15179 +294 15179 +295 15178 +295 15178 +295 15178 +295 15178 +295 15178 +295 15178 +295 15177 +295 15177 +295 15177 +295 15177 +296 15177 +296 15176 +296 15176 +296 15176 +296 15176 +296 15176 +296 15176 +296 15175 +296 15175 +296 15175 +296 15175 +297 15175 +297 15175 +297 15174 +297 15174 +297 15174 +297 15174 +297 15174 +297 15173 +297 15173 +298 15173 +298 15173 +298 15173 +298 15173 +298 15172 +298 15172 +298 15172 +298 15172 +298 15172 +298 15171 +299 15171 +299 15171 +299 15171 +299 15171 +299 15171 +299 15170 +299 15170 +299 15170 +299 15170 +300 15170 +300 15169 +300 15169 +300 15169 +300 15169 +300 15169 +300 15168 +300 15168 +300 15168 +300 15168 +301 15168 +301 15167 +301 15167 +301 15167 +301 15167 +301 15167 +301 15166 +301 15166 +302 15166 +302 15166 +302 15166 +302 15165 +302 15165 +302 15165 +302 15165 +302 15165 +302 15164 +303 15164 +303 15164 +303 15164 +303 15164 +303 15163 +303 15163 +303 15163 +303 15163 +304 15163 +304 15162 +304 15162 +304 15162 +304 15162 +304 15162 +304 15161 +304 15161 +305 15161 +305 15161 +305 15161 +305 15160 +305 15160 +305 15160 +305 15160 +305 15160 +306 15159 +306 15159 +306 15159 +306 15159 +306 15158 +306 15158 +306 15158 +306 15158 +307 15158 +307 15157 +307 15157 +307 15157 +307 15157 +307 15157 +307 15156 +308 15156 +308 15156 +308 15156 +308 15155 +308 15155 +308 15155 +308 15155 +309 15155 +309 15154 +309 15154 +309 15154 +309 15154 +309 15153 +309 15153 +309 15153 +310 15153 +310 15153 +310 15152 +310 15152 +310 15152 +310 15152 +310 15151 +311 15151 +311 15151 +311 15151 +311 15151 +311 15150 +311 15150 +312 15150 +312 15150 +312 15149 +312 15149 +312 15149 +312 15149 +312 15149 +313 15148 +313 15148 +313 15148 +313 15148 +313 15147 +313 15147 +314 15147 +314 15147 +314 15146 +314 15146 +314 15146 +314 15146 +314 15145 +315 15145 +315 15145 +315 15145 +315 15144 +315 15144 +315 15144 +316 15144 +316 15144 +316 15143 +316 15143 +316 15143 +316 15143 +317 15142 +317 15142 +317 15142 +317 15142 +317 15141 +317 15141 +318 15141 +318 15141 +318 15140 +318 15140 +318 15140 +318 15140 +319 15139 +319 15139 +319 15139 +319 15139 +319 15138 +319 15138 +320 15138 +320 15138 +320 15137 +320 15137 +320 15137 +320 15137 +321 15136 +321 15136 +321 15136 +321 15135 +321 15135 +322 15135 +322 15135 +322 15134 +322 15134 +322 15134 +322 15134 +323 15133 +323 15133 +323 15133 +323 15133 +323 15132 +324 15132 +324 15132 +324 15132 +324 15131 +324 15131 +324 15131 +325 15130 +325 15130 +325 15130 +325 15130 +325 15129 +326 15129 +326 15129 +326 15129 +326 15128 +326 15128 +327 15128 +327 15127 +327 15127 +327 15127 +327 15127 +328 15126 +328 15126 +328 15126 +328 15126 +328 15125 +329 15125 +329 15125 +329 15124 +329 15124 +329 15124 +330 15124 +330 15123 +330 15123 +330 15123 +330 15123 +331 15122 +331 15122 +331 15122 +331 15121 +331 15121 +332 15121 +332 15121 +332 15120 +332 15120 +332 15120 +333 15119 +333 15119 +333 15119 +333 15119 +333 15118 +334 15118 +334 15118 +334 15117 +334 15117 +335 15117 +335 15117 +335 15116 +335 15116 +335 15116 +336 15116 +336 15115 +336 15115 +336 15115 +336 15114 +337 15114 +337 15114 +337 15114 +337 15113 +338 15113 +338 15113 +338 15112 +338 15112 +338 15112 +339 15112 +339 15111 +339 15111 +339 15111 +340 15110 +340 15110 +340 15110 +340 15109 +341 15109 +341 15109 +341 15109 +341 15108 +341 15108 +342 15108 +342 15107 +342 15107 +342 15107 +343 15107 +343 15106 +343 15106 +343 15106 +344 15105 +344 15105 +344 15105 +344 15105 +345 15104 +345 15104 +345 15104 +345 15103 +345 15103 +346 15103 +346 15103 +346 15102 +346 15102 +347 15102 +347 15101 +347 15101 +347 15101 +348 15101 +348 15100 +348 15100 +348 15100 +349 15099 +349 15099 +349 15099 +349 15098 +350 15098 +350 15098 +350 15098 +350 15097 +351 15097 +351 15097 +351 15096 +351 15096 +352 15096 +352 15096 +352 15095 +352 15095 +353 15095 +353 15094 +353 15094 +353 15094 +354 15093 +354 15093 +354 15093 +355 15093 +355 15092 +355 15092 +355 15092 +356 15092 +356 15091 +356 15091 +356 15091 +357 15091 +357 15090 +357 15090 +357 15090 +358 15090 +358 15089 +358 15089 +359 15089 +359 15089 +359 15088 +359 15088 +360 15088 +360 15088 +360 15087 +360 15087 +361 15087 +361 15087 +361 15086 +362 15086 +362 15086 +362 15086 +362 15085 +363 15085 +363 15085 +363 15085 +363 15084 +364 15084 +364 15084 +364 15083 +365 15083 +365 15083 +365 15083 +365 15082 +366 15082 +366 15082 +366 15082 +367 15081 +367 15081 +367 15081 +367 15081 +368 15080 +368 15080 +368 15080 +369 15080 +369 15079 +369 15079 +369 15079 +370 15079 +370 15078 +370 15078 +371 15078 +371 15078 +371 15077 +371 15077 +372 15077 +372 15076 +372 15076 +373 15076 +373 15076 +373 15075 +373 15075 +374 15075 +374 15075 +374 15074 +375 15074 +375 15074 +375 15074 +376 15073 +376 15073 +376 15073 +376 15072 +377 15072 +377 15072 +377 15072 +378 15071 +378 15071 +378 15071 +378 15071 +379 15070 +379 15070 +379 15070 +380 15070 +380 15069 +380 15069 +381 15069 +381 15068 +381 15068 +381 15068 +382 15068 +382 15067 +382 15067 +383 15067 +383 15067 +383 15066 +384 15066 +384 15066 +384 15065 +384 15065 +385 15065 +385 15065 +385 15064 +386 15064 +386 15064 +386 15064 +387 15063 +387 15063 +387 15063 +388 15062 +388 15062 +388 15062 +388 15062 +389 15061 +389 15061 +389 15061 +390 15060 +390 15060 +390 15060 +391 15060 +391 15059 +391 15059 +392 15059 +392 15059 +392 15058 +392 15058 +393 15058 +393 15057 +393 15057 +394 15057 +394 15057 +394 15056 +395 15056 +395 15056 +395 15055 +396 15055 +396 15055 +396 15055 +397 15054 +397 15054 +397 15054 +397 15053 +398 15053 +398 15053 +398 15053 +399 15052 +399 15052 +399 15052 +400 15051 +400 15051 +400 15051 +401 15051 +401 15050 +401 15050 +402 15050 +402 15049 +402 15049 +403 15049 +403 15049 +403 15048 +403 15048 +404 15048 +404 15047 +404 15047 +405 15047 +405 15046 +405 15046 +406 15046 +406 15046 +406 15045 +407 15045 +407 15045 +407 15044 +408 15044 +408 15044 +408 15044 +409 15043 +409 15043 +409 15043 +410 15042 +410 15042 +410 15042 +411 15041 +411 15041 +411 15041 +411 15041 +412 15040 +412 15040 +412 15040 +413 15039 +413 15039 +413 15039 +414 15038 +414 15038 +414 15038 +415 15038 +415 15037 +415 15037 +416 15037 +416 15036 +416 15036 +417 15036 +417 15035 +417 15035 +418 15035 +418 15035 +418 15034 +419 15034 +419 15034 +419 15033 +420 15033 +420 15033 +420 15032 +421 15032 +421 15032 +421 15032 +422 15031 +422 15031 +422 15031 +423 15030 +423 15030 +423 15030 +424 15029 +424 15029 +424 15029 +425 15029 +425 15028 +425 15028 +426 15028 +426 15027 +426 15027 +427 15027 +427 15026 +427 15026 +428 15026 +428 15025 +428 15025 +429 15025 +429 15025 +429 15024 +430 15024 +430 15024 +430 15023 +431 15023 +431 15023 +431 15022 +432 15022 +432 15022 +432 15022 +433 15021 +433 15021 +433 15021 +434 15020 +434 15020 +434 15020 +435 15019 +435 15019 +435 15019 +436 15018 +436 15018 +436 15018 +437 15017 +437 15017 +437 15017 +438 15017 +438 15016 +438 15016 +439 15016 +439 15015 +439 15015 +440 15015 +440 15014 +441 15014 +441 15014 +441 15013 +442 15013 +442 15013 +442 15013 +443 15012 +443 15012 +443 15012 +444 15011 +444 15011 +444 15011 +445 15010 +445 15010 +445 15010 +446 15009 +446 15009 +446 15009 +447 15008 +447 15008 +447 15008 +448 15007 +448 15007 +448 15007 +449 15006 +449 15006 +449 15006 +450 15006 +450 15005 +451 15005 +451 15005 +451 15004 +452 15004 +452 15004 +452 15003 +453 15003 +453 15003 +453 15002 +454 15002 +454 15002 +454 15001 +455 15001 +455 15001 +455 15000 +456 15000 +456 15000 +456 14999 +457 14999 +457 14999 +457 14998 +458 14998 +458 14998 +459 14997 +459 14997 +459 14997 +460 14996 +460 14996 +460 14996 +461 14995 +461 14995 +461 14995 +462 14994 +462 14994 +462 14994 +463 14993 +463 14993 +463 14993 +464 14992 +464 14992 +464 14992 +465 14991 +465 14991 +466 14991 +466 14990 +466 14990 +467 14990 +467 14989 +467 14989 +468 14989 +468 14988 +468 14988 +469 14988 +469 14987 +469 14987 +470 14987 +470 14986 +470 14986 +471 14986 +471 14985 +472 14985 +472 14985 +472 14984 +473 14984 +473 14984 +473 14983 +474 14983 +474 14983 +474 14982 +475 14982 +475 14982 +475 14981 +476 14981 +476 14981 +476 14980 +477 14980 +477 14980 +478 14979 +478 14979 +478 14979 +479 14978 +479 14978 +479 14978 +480 14977 +480 14977 +480 14977 +481 14976 +481 14976 +481 14975 +482 14975 +482 14975 +482 14974 +483 14974 +483 14974 +484 14973 +484 14973 +484 14973 +485 14972 +485 14972 +485 14972 +486 14971 +486 14971 +486 14971 +487 14970 +487 14970 +488 14969 +488 14969 +488 14969 +489 14968 +489 14968 +489 14968 +490 14967 +490 14967 +490 14967 +491 14966 +491 14966 +491 14965 +492 14965 +492 14965 +493 14964 +493 14964 +493 14964 +494 14963 +494 14963 +494 14963 +495 14962 +495 14962 +495 14961 +496 14961 +496 14961 +497 14960 +497 14960 +497 14960 +498 14959 +498 14959 +498 14958 +499 14958 +499 14958 +499 14957 +500 14957 +500 14957 +501 14956 +501 14956 +501 14955 +502 14955 +502 14955 +502 14954 +503 14954 +503 14954 +504 14953 +504 14953 +504 14952 +505 14952 +505 14952 +505 14951 +506 14951 +506 14951 +507 14950 +507 14950 +507 14949 +508 14949 +508 14949 +508 14948 +509 14948 +509 14947 +510 14947 +510 14947 +510 14946 +511 14946 +511 14946 +512 14945 +512 14945 +512 14944 +513 14944 +513 14944 +513 14943 +514 14943 +514 14942 +515 14942 +515 14942 +515 14941 +516 14941 +516 14940 +517 14940 +517 14940 +517 14939 +518 14939 +518 14938 +519 14938 +519 14938 +519 14937 +520 14937 +520 14936 +521 14936 +521 14936 +521 14935 +522 14935 +522 14934 +523 14934 +523 14934 +523 14933 +524 14933 +524 14932 +525 14932 +525 14932 +525 14931 +526 14931 +526 14930 +527 14930 +527 14930 +527 14929 +528 14929 +528 14928 +529 14928 +529 14928 +529 14927 +530 14927 +530 14926 +531 14926 +531 14926 +531 14925 +532 14925 +532 14924 +533 14924 +533 14924 +533 14923 +534 14923 +534 14922 +535 14922 +535 14922 +536 14921 +536 14921 +536 14920 +537 14920 +537 14919 +538 14919 +538 14919 +538 14918 +539 14918 +539 14918 +540 14917 +540 14917 +540 14916 +541 14916 +541 14916 +542 14915 +542 14915 +543 14915 +543 14914 +543 14914 +544 14914 +544 14913 +545 14913 +545 14912 +546 14912 +546 14912 +546 14911 +547 14911 +547 14911 +548 14910 +548 14910 +548 14909 +549 14909 +549 14909 +550 14908 +550 14908 +551 14908 +551 14907 +551 14907 +552 14906 +552 14906 +553 14906 +553 14905 +554 14905 +554 14905 +554 14904 +555 14904 +555 14903 +556 14903 +556 14903 +557 14902 +557 14902 +557 14902 +558 14901 +558 14901 +559 14900 +559 14900 +560 14900 +560 14899 +560 14899 +561 14898 +561 14898 +562 14898 +562 14897 +563 14897 +563 14897 +563 14896 +564 14896 +564 14895 +565 14895 +565 14895 +566 14894 +566 14894 +566 14893 +567 14893 +567 14893 +568 14892 +568 14892 +569 14891 +569 14891 +569 14891 +570 14890 +570 14890 +571 14889 +571 14889 +572 14889 +572 14888 +572 14888 +573 14887 +573 14887 +574 14887 +574 14886 +575 14886 +575 14885 +576 14885 +576 14884 +576 14884 +577 14884 +577 14883 +578 14883 +578 14882 +579 14882 +579 14882 +580 14881 +580 14881 +580 14880 +581 14880 +581 14879 +582 14879 +582 14879 +583 14878 +583 14878 +584 14877 +584 14877 +584 14876 +585 14876 +585 14876 +586 14875 +586 14875 +587 14874 +587 14874 +588 14873 +588 14873 +588 14873 +589 14872 +589 14872 +590 14871 +590 14871 +591 14870 +591 14870 +592 14870 +592 14869 +592 14869 +593 14868 +593 14868 +594 14867 +594 14867 +595 14867 +595 14866 +596 14866 +596 14865 +597 14865 +597 14864 +597 14864 +598 14863 +598 14863 +599 14863 +599 14862 +600 14862 +600 14861 +601 14861 +601 14860 +602 14860 +602 14859 +603 14859 +603 14858 +603 14858 +604 14858 +604 14857 +605 14857 +605 14856 +606 14856 +606 14855 +607 14855 +607 14854 +608 14854 +608 14853 +609 14853 +609 14852 +610 14852 +610 14852 +611 14851 +611 14851 +612 14850 +612 14850 +613 14849 +613 14849 +613 14848 +614 14848 +614 14847 +615 14847 +615 14846 +616 14846 +616 14845 +617 14845 +617 14844 +618 14844 +618 14844 +619 14843 +619 14843 +620 14842 +620 14842 +621 14841 +621 14841 +622 14840 +622 14840 +623 14839 +623 14839 +624 14838 +624 14838 +625 14837 +625 14837 +626 14836 +626 14836 +627 14835 +627 14835 +628 14834 +628 14834 +629 14833 +629 14833 +630 14832 +630 14832 +631 14832 +631 14831 +632 14831 +632 14830 +633 14830 +633 14829 +634 14829 +634 14828 +635 14828 +635 14827 +636 14827 +637 14826 +637 14826 +638 14825 +638 14825 +639 14824 +639 14824 +640 14823 +640 14823 +641 14822 +641 14822 +642 14821 +642 14821 +643 14820 +643 14820 +644 14819 +644 14819 +645 14818 +645 14818 +646 14817 +646 14817 +647 14816 +648 14816 +648 14815 +649 14815 +649 14814 +650 14814 +650 14813 +651 14813 +651 14812 +652 14812 +652 14811 +653 14811 +653 14810 +654 14810 +655 14809 +655 14809 +656 14808 +656 14808 +657 14807 +657 14807 +658 14806 +658 14806 +659 14805 +659 14805 +660 14804 +661 14803 +661 14803 +662 14802 +662 14802 +663 14801 +663 14801 +664 14800 +664 14800 +665 14799 +666 14799 +666 14798 +667 14798 +667 14797 +668 14797 +668 14796 +669 14796 +669 14795 +670 14795 +671 14794 +671 14794 +672 14793 +672 14793 +673 14792 +673 14792 +674 14791 +675 14790 +675 14790 +676 14789 +676 14789 +677 14788 +677 14788 +678 14787 +679 14787 +679 14786 +680 14786 +680 14785 +681 14785 +681 14784 +682 14784 +683 14783 +683 14782 +684 14782 +684 14781 +685 14781 +685 14780 +686 14780 +687 14779 +687 14779 +688 14778 +688 14778 +689 14777 +690 14776 +690 14776 +691 14775 +691 14775 +692 14774 +693 14774 +693 14773 +694 14773 +694 14772 +695 14771 +695 14771 +696 14770 +697 14770 +697 14769 +698 14769 +698 14768 +699 14768 +700 14767 +700 14766 +701 14766 +701 14765 +702 14765 +703 14764 +703 14764 +704 14763 +704 14763 +705 14762 +706 14761 +706 14761 +707 14760 +707 14760 +708 14759 +709 14759 +709 14758 +710 14757 +711 14757 +711 14756 +712 14756 +712 14755 +713 14755 +714 14754 +714 14753 +715 14753 +715 14752 +716 14752 +717 14751 +717 14751 +718 14750 +718 14749 +719 14749 +720 14748 +720 14748 +721 14747 +722 14747 +722 14746 +723 14745 +723 14745 +724 14744 +725 14744 +725 14743 +726 14743 +727 14742 +727 14741 +728 14741 +728 14740 +729 14740 +730 14739 +730 14739 +731 14738 +732 14737 +732 14737 +733 14736 +733 14736 +734 14735 +735 14734 +735 14734 +736 14733 +737 14733 +737 14732 +738 14731 +738 14731 +739 14730 +740 14730 +740 14729 +741 14729 +742 14728 +742 14727 +743 14727 +744 14726 +744 14726 +745 14725 +746 14724 +746 14724 +747 14723 +747 14723 +748 14722 +749 14721 +749 14721 +750 14720 +751 14720 +751 14719 +752 14719 +753 14718 +753 14717 +754 14717 +755 14716 +755 14716 +756 14715 +757 14714 +757 14714 +758 14713 +759 14713 +759 14712 +760 14711 +761 14711 +761 14710 +762 14709 +763 14709 +763 14708 +764 14708 +765 14707 +765 14706 +766 14706 +767 14705 +767 14705 +768 14704 +769 14703 +769 14703 +770 14702 +771 14702 +771 14701 +772 14700 +773 14700 +773 14699 +774 14699 +775 14698 +775 14697 +776 14697 +777 14696 +777 14695 +778 14695 +779 14694 +779 14694 +780 14693 +781 14692 +781 14692 +782 14691 +783 14691 +783 14690 +784 14689 +785 14689 +785 14688 +786 14687 +787 14687 +787 14686 +788 14686 +789 14685 +790 14684 +790 14684 +791 14683 +792 14683 +792 14682 +793 14681 +794 14681 +794 14680 +795 14679 +796 14679 +796 14678 +797 14678 +798 14677 +798 14676 +799 14676 +800 14675 +800 14674 +801 14674 +802 14673 +803 14673 +803 14672 +804 14671 +805 14671 +805 14670 +806 14669 +807 14669 +807 14668 +808 14667 +809 14667 +810 14666 +810 14666 +811 14665 +812 14664 +812 14664 +813 14663 +814 14662 +814 14662 +815 14661 +816 14660 +817 14660 +817 14659 +818 14659 +819 14658 +819 14657 +820 14657 +821 14656 +821 14655 +822 14655 +823 14654 +824 14653 +824 14653 +825 14652 +826 14651 +826 14651 +827 14650 +828 14650 +829 14649 +829 14648 +830 14648 +831 14647 +831 14646 +832 14646 +833 14645 +833 14644 +834 14644 +835 14643 +836 14642 +836 14642 +837 14641 +838 14640 +838 14640 +839 14639 +840 14638 +841 14638 +841 14637 +842 14637 +843 14636 +844 14635 +844 14635 +845 14634 +846 14633 +846 14633 +847 14632 +848 14631 +849 14631 +849 14630 +850 14629 +851 14629 +851 14628 +852 14627 +853 14627 +854 14626 +854 14625 +855 14625 +856 14624 +856 14623 +857 14623 +858 14622 +859 14621 +859 14621 +860 14620 +861 14619 +862 14619 +862 14618 +863 14617 +864 14617 +865 14616 +865 14615 +866 14615 +867 14614 +867 14613 +868 14613 +869 14612 +870 14611 +870 14611 +871 14610 +872 14609 +873 14609 +873 14608 +874 14607 +875 14607 +876 14606 +876 14605 +877 14605 +878 14604 +878 14603 +879 14603 +880 14602 +881 14601 +881 14601 +882 14600 +883 14599 +884 14599 +884 14598 +885 14597 +886 14597 +887 14596 +887 14595 +888 14595 +889 14594 +890 14593 +890 14593 +891 14592 +892 14591 +893 14591 +893 14590 +894 14589 +895 14589 +896 14588 +896 14587 +897 14587 +898 14586 +899 14585 +899 14585 +900 14584 +901 14583 +902 14583 +902 14582 +903 14581 +904 14581 +905 14580 +905 14579 +906 14579 +907 14578 +908 14577 +908 14577 +909 14576 +910 14575 +911 14575 +911 14574 +912 14573 +913 14573 +914 14572 +914 14571 +915 14571 +916 14570 +917 14569 +918 14569 +918 14568 +919 14567 +920 14567 +921 14566 +921 14565 +922 14565 +923 14564 +924 14563 +924 14563 +925 14562 +926 14561 +927 14561 +927 14560 +928 14559 +929 14559 +930 14558 +931 14557 +931 14557 +932 14556 +933 14555 +934 14554 +934 14554 +935 14553 +936 14552 +937 14552 +938 14551 +938 14550 +939 14550 +940 14549 +941 14548 +941 14547 +942 14547 +943 14546 +944 14545 +945 14545 +945 14544 +946 14543 +947 14542 +948 14542 +949 14541 +949 14540 +950 14539 +951 14539 +952 14538 +952 14537 +953 14537 +954 14536 +955 14535 +956 14534 +956 14534 +957 14533 +958 14532 +959 14531 +960 14531 +960 14530 +961 14529 +962 14528 +963 14528 +964 14527 +964 14526 +965 14526 +966 14525 +967 14524 +968 14523 +968 14523 +969 14522 +970 14521 +971 14520 +972 14520 +972 14519 +973 14518 +974 14517 +975 14516 +976 14516 +976 14515 +977 14514 +978 14513 +979 14513 +980 14512 +980 14511 +981 14510 +982 14510 +983 14509 +984 14508 +984 14507 +985 14507 +986 14506 +987 14505 +988 14504 +989 14503 +989 14503 +990 14502 +991 14501 +992 14500 +993 14500 +993 14499 +994 14498 +995 14497 +996 14496 +997 14496 +998 14495 +998 14494 +999 14493 +1000 14492 +1001 14492 +1002 14491 +1002 14490 +1003 14489 +1004 14489 +1005 14488 +1006 14487 +1007 14486 +1007 14485 +1008 14485 +1009 14484 +1010 14483 +1011 14482 +1012 14481 +1012 14481 +1013 14480 +1014 14479 +1015 14478 +1016 14477 +1017 14476 +1017 14476 +1018 14475 +1019 14474 +1020 14473 +1021 14472 +1022 14472 +1023 14471 +1023 14470 +1024 14469 +1025 14468 +1026 14467 +1027 14467 +1028 14466 +1028 14465 +1029 14464 +1030 14463 +1031 14462 +1032 14462 +1033 14461 +1034 14460 +1034 14459 +1035 14458 +1036 14457 +1037 14457 +1038 14456 +1039 14455 +1039 14454 +1040 14453 +1041 14452 +1042 14452 +1043 14451 +1044 14450 +1045 14449 +1045 14448 +1046 14447 +1047 14446 +1048 14446 +1049 14445 +1050 14444 +1051 14443 +1051 14442 +1052 14441 +1053 14440 +1054 14440 +1055 14439 +1056 14438 +1057 14437 +1058 14436 +1058 14435 +1059 14434 +1060 14433 +1061 14433 +1062 14432 +1063 14431 +1064 14430 +1064 14429 +1065 14428 +1066 14427 +1067 14426 +1068 14425 +1069 14425 +1070 14424 +1071 14423 +1071 14422 +1072 14421 +1073 14420 +1074 14419 +1075 14418 +1076 14417 +1077 14416 +1078 14415 +1078 14415 +1079 14414 +1080 14413 +1081 14412 +1082 14411 +1083 14410 +1084 14409 +1085 14408 +1085 14407 +1086 14406 +1087 14405 +1088 14404 +1089 14403 +1090 14402 +1091 14402 +1092 14401 +1093 14400 +1093 14399 +1094 14398 +1095 14397 +1096 14396 +1097 14395 +1098 14394 +1099 14393 +1100 14392 +1101 14391 +1101 14390 +1102 14389 +1103 14388 +1104 14387 +1105 14386 +1106 14385 +1107 14384 +1108 14383 +1109 14382 +1109 14381 +1110 14380 +1111 14379 +1112 14378 +1113 14377 +1114 14377 +1115 14376 +1116 14375 +1117 14374 +1118 14373 +1118 14372 +1119 14371 +1120 14370 +1121 14369 +1122 14368 +1123 14366 +1124 14365 +1125 14364 +1126 14363 +1127 14362 +1128 14361 +1128 14360 +1129 14359 +1130 14358 +1131 14357 +1132 14356 +1133 14355 +1134 14354 +1135 14353 +1136 14352 +1137 14351 +1138 14350 +1139 14349 +1139 14348 +1140 14347 +1141 14346 +1142 14345 +1143 14344 +1144 14343 +1145 14342 +1146 14340 +1147 14339 +1148 14338 +1149 14337 +1150 14336 +1151 14335 +1152 14334 +1152 14333 +1153 14332 +1154 14331 +1155 14330 +1156 14329 +1157 14327 +1158 14326 +1159 14325 +1160 14324 +1161 14323 +1162 14322 +1163 14321 +1164 14320 +1165 14319 +1166 14318 +1167 14316 +1167 14315 +1168 14314 +1169 14313 +1170 14312 +1171 14311 +1172 14310 +1173 14309 +1174 14307 +1175 14306 +1176 14305 +1177 14304 +1178 14303 +1179 14302 +1180 14301 +1181 14300 +1182 14298 +1183 14297 +1184 14296 +1185 14295 +1186 14294 +1187 14293 +1187 14292 +1188 14290 +1189 14289 +1190 14288 +1191 14287 +1192 14286 +1193 14285 +1194 14283 +1195 14282 +1196 14281 +1197 14280 +1198 14279 +1199 14278 +1200 14276 +1201 14275 +1202 14274 +1203 14273 +1204 14272 +1205 14271 +1206 14269 +1207 14268 +1208 14267 +1209 14266 +1210 14265 +1211 14264 +1212 14262 +1213 14261 +1214 14260 +1215 14259 +1216 14258 +1217 14256 +1218 14255 +1219 14254 +1220 14253 +1221 14252 +1222 14250 +1223 14249 +1224 14248 +1225 14247 +1226 14246 +1227 14244 +1228 14243 +1229 14242 +1230 14241 +1231 14239 +1232 14238 +1233 14237 +1234 14236 +1235 14234 +1236 14233 +1237 14232 +1238 14231 +1239 14230 +1240 14228 +1241 14227 +1242 14226 +1243 14225 +1244 14223 +1245 14222 +1246 14221 +1247 14220 +1248 14218 +1249 14217 +1250 14216 +1251 14215 +1252 14213 +1253 14212 +1255 14211 +1256 14209 +1257 14208 +1258 14207 +1259 14206 +1260 14204 +1261 14203 +1262 14202 +1263 14201 +1264 14199 +1265 14198 +1266 14197 +1267 14195 +1268 14194 +1269 14193 +1270 14192 +1271 14190 +1272 14189 +1273 14188 +1274 14186 +1276 14185 +1277 14184 +1278 14183 +1279 14181 +1280 14180 +1281 14179 +1282 14177 +1283 14176 +1284 14175 +1285 14173 +1286 14172 +1287 14171 +1288 14169 +1289 14168 +1291 14167 +1292 14165 +1293 14164 +1294 14163 +1295 14161 +1296 14160 +1297 14159 +1298 14157 +1299 14156 +1300 14155 +1301 14153 +1302 14152 +1304 14151 +1305 14149 +1306 14148 +1307 14147 +1308 14145 +1309 14144 +1310 14143 +1311 14141 +1312 14140 +1313 14139 +1314 14137 +1316 14136 +1317 14135 +1318 14133 +1319 14132 +1320 14130 +1321 14129 +1322 14128 +1323 14126 +1325 14125 +1326 14124 +1327 14122 +1328 14121 +1329 14119 +1330 14118 +1331 14117 +1332 14115 +1334 14114 +1335 14113 +1336 14111 +1337 14110 +1338 14108 +1339 14107 +1340 14106 +1342 14104 +1343 14103 +1344 14101 +1345 14100 +1346 14099 +1347 14097 +1349 14096 +1350 14094 +1351 14093 +1352 14092 +1353 14090 +1354 14089 +1356 14087 +1357 14086 +1358 14084 +1359 14083 +1360 14082 +1361 14080 +1363 14079 +1364 14077 +1365 14076 +1366 14075 +1367 14073 +1368 14072 +1370 14070 +1371 14069 +1372 14067 +1373 14066 +1374 14064 +1376 14063 +1377 14062 +1378 14060 +1379 14059 +1380 14057 +1382 14056 +1383 14054 +1384 14053 +1385 14051 +1386 14050 +1388 14049 +1389 14047 +1390 14046 +1391 14044 +1392 14043 +1394 14041 +1395 14040 +1396 14038 +1397 14037 +1399 14035 +1400 14034 +1401 14032 +1402 14031 +1403 14029 +1405 14028 +1406 14027 +1407 14025 +1408 14024 +1410 14022 +1411 14021 +1412 14019 +1413 14018 +1415 14016 +1416 14015 +1417 14013 +1418 14012 +1420 14010 +1421 14009 +1422 14007 +1423 14006 +1425 14004 +1426 14003 +1427 14001 +1428 14000 +1430 13998 +1431 13996 +1432 13995 +1433 13993 +1435 13992 +1436 13990 +1437 13989 +1438 13987 +1440 13986 +1441 13984 +1442 13983 +1443 13981 +1445 13980 +1446 13978 +1447 13977 +1448 13975 +1450 13974 +1451 13972 +1452 13971 +1454 13969 +1455 13967 +1456 13966 +1457 13964 +1459 13963 +1460 13961 +1461 13960 +1463 13958 +1464 13957 +1465 13955 +1466 13954 +1468 13952 +1469 13951 +1470 13949 +1472 13947 +1473 13946 +1474 13944 +1475 13943 +1477 13941 +1478 13940 +1479 13938 +1481 13937 +1482 13935 +1483 13934 +1484 13932 +1486 13930 +1487 13929 +1488 13927 +1490 13926 +1491 13924 +1492 13923 +1493 13921 +1495 13920 +1496 13918 +1497 13917 +1499 13915 +1500 13913 +1501 13912 +1502 13910 +1504 13909 +1505 13907 +1506 13906 +1508 13904 +1509 13902 +1510 13901 +1512 13899 +1513 13898 +1514 13896 +1515 13895 +1517 13893 +1518 13892 +1519 13890 +1521 13888 +1522 13887 +1523 13885 +1525 13884 +1526 13882 +1527 13881 +1528 13879 +1530 13877 +1531 13876 +1532 13874 +1534 13873 +1535 13871 +1536 13869 +1538 13868 +1539 13866 +1540 13865 +1541 13863 +1543 13862 +1544 13860 +1545 13858 +1547 13857 +1548 13855 +1549 13854 +1551 13852 +1552 13850 +1553 13849 +1555 13847 +1556 13846 +1557 13844 +1559 13843 +1560 13841 +1561 13839 +1563 13838 +1564 13836 +1565 13835 +1567 13833 +1568 13831 +1569 13830 +1571 13828 +1572 13827 +1573 13825 +1575 13823 +1576 13822 +1577 13820 +1579 13818 +1580 13817 +1581 13815 +1583 13814 +1584 13812 +1586 13810 +1587 13809 +1588 13807 +1590 13806 +1591 13804 +1592 13802 +1594 13801 +1595 13799 +1597 13797 +1598 13796 +1599 13794 +1601 13793 +1602 13791 +1604 13789 +1605 13788 +1606 13786 +1608 13784 +1609 13783 +1611 13781 +1612 13779 +1613 13778 +1615 13776 +1616 13774 +1618 13773 +1619 13771 +1620 13770 +1622 13768 +1623 13766 +1625 13765 +1626 13763 +1628 13761 +1629 13760 +1630 13758 +1632 13756 +1633 13755 +1635 13753 +1636 13751 +1638 13750 +1639 13748 +1640 13747 +1642 13745 +1643 13743 +1645 13742 +1646 13740 +1648 13738 +1649 13737 +1651 13735 +1652 13733 +1654 13732 +1655 13730 +1657 13728 +1658 13727 +1660 13725 +1661 13723 +1662 13722 +1664 13720 +1665 13718 +1667 13717 +1668 13715 +1670 13714 +1671 13712 +1673 13710 +1674 13709 +1676 13707 +1677 13706 +1679 13704 +1680 13702 +1682 13701 +1684 13699 +1685 13698 +1687 13696 +1688 13694 +1690 13693 +1691 13691 +1693 13689 +1694 13688 +1696 13686 +1697 13685 +1699 13683 +1700 13681 +1702 13680 +1703 13678 +1705 13676 +1707 13675 +1708 13673 +1710 13671 +1711 13670 +1713 13668 +1714 13666 +1716 13665 +1717 13663 +1719 13661 +1720 13660 +1722 13658 +1724 13656 +1725 13655 +1727 13653 +1728 13651 +1730 13650 +1731 13648 +1733 13646 +1735 13645 +1736 13643 +1738 13641 +1739 13640 +1741 13638 +1743 13636 +1744 13634 +1746 13633 +1747 13631 +1749 13629 +1751 13628 +1752 13626 +1754 13624 +1756 13622 +1757 13621 +1759 13619 +1760 13617 +1762 13615 +1764 13614 +1765 13612 +1767 13610 +1769 13608 +1770 13607 +1772 13605 +1774 13603 +1775 13601 +1777 13600 +1779 13598 +1780 13596 +1782 13594 +1784 13593 +1785 13591 +1787 13589 +1789 13587 +1790 13586 +1792 13584 +1794 13582 +1796 13580 +1797 13578 +1799 13577 +1801 13575 +1802 13573 +1804 13571 +1806 13569 +1808 13568 +1809 13566 +1811 13564 +1813 13562 +1815 13560 +1816 13558 +1818 13557 +1820 13555 +1822 13553 +1823 13551 +1825 13549 +1827 13547 +1829 13546 +1830 13544 +1832 13542 +1834 13540 +1836 13538 +1838 13536 +1839 13534 +1841 13533 +1843 13531 +1845 13529 +1847 13527 +1848 13525 +1850 13523 +1852 13521 +1854 13519 +1856 13518 +1858 13516 +1859 13514 +1861 13512 +1863 13510 +1865 13508 +1867 13506 +1869 13504 +1871 13502 +1872 13500 +1874 13499 +1876 13497 +1878 13495 +1880 13493 +1882 13491 +1884 13489 +1886 13487 +1887 13485 +1889 13483 +1891 13481 +1893 13479 +1895 13477 +1897 13475 +1899 13473 +1901 13471 +1903 13469 +1905 13467 +1907 13465 +1909 13463 +1911 13461 +1912 13460 +1914 13458 +1916 13456 +1918 13454 +1920 13452 +1922 13450 +1924 13448 +1926 13446 +1928 13444 +1930 13442 +1932 13440 +1934 13438 +1936 13436 +1938 13434 +1940 13432 +1942 13430 +1944 13428 +1946 13426 +1948 13424 +1950 13422 +1952 13420 +1954 13418 +1956 13416 +1959 13414 +1961 13412 +1963 13410 +1965 13408 +1967 13406 +1969 13404 +1971 13402 +1973 13400 +1975 13398 +1977 13396 +1979 13394 +1981 13392 +1983 13390 +1986 13388 +1988 13386 +1990 13384 +1992 13382 +1994 13380 +1996 13378 +1998 13376 +2000 13374 +2003 13372 +2005 13370 +2007 13367 +2009 13365 +2011 13363 +2013 13361 +2016 13359 +2018 13357 +2020 13355 +2022 13353 +2024 13351 +2026 13349 +2029 13347 +2031 13345 +2033 13342 +2035 13340 +2038 13338 +2040 13336 +2042 13334 +2044 13332 +2046 13330 +2049 13328 +2051 13325 +2053 13323 +2055 13321 +2058 13319 +2060 13317 +2062 13315 +2065 13313 +2067 13310 +2069 13308 +2071 13306 +2074 13304 +2076 13302 +2078 13300 +2081 13297 +2083 13295 +2085 13293 +2088 13291 +2090 13289 +2092 13287 +2095 13284 +2097 13282 +2099 13280 +2102 13278 +2104 13276 +2106 13273 +2109 13271 +2111 13269 +2114 13267 +2116 13265 +2118 13262 +2121 13260 +2123 13258 +2126 13256 +2128 13253 +2130 13251 +2133 13249 +2135 13247 +2138 13244 +2140 13242 +2143 13240 +2145 13238 +2147 13235 +2150 13233 +2152 13231 +2155 13229 +2157 13226 +2160 13224 +2162 13222 +2165 13219 +2167 13217 +2170 13215 +2172 13212 +2175 13210 +2177 13208 +2180 13206 +2182 13203 +2185 13201 +2188 13199 +2190 13196 +2193 13194 +2195 13192 +2198 13189 +2200 13187 +2203 13185 +2205 13182 +2208 13180 +2211 13178 +2213 13175 +2216 13173 +2218 13170 +2221 13168 +2224 13166 +2226 13163 +2229 13161 +2232 13159 +2234 13156 +2237 13154 +2240 13151 +2242 13149 +2245 13147 +2248 13144 +2250 13142 +2253 13139 +2256 13137 +2258 13134 +2261 13132 +2264 13130 +2266 13127 +2269 13125 +2272 13122 +2275 13120 +2277 13117 +2280 13115 +2283 13113 +2286 13110 +2288 13108 +2291 13105 +2294 13103 +2297 13100 +2300 13098 +2302 13095 +2305 13093 +2308 13090 +2311 13088 +2314 13085 +2316 13083 +2319 13080 +2322 13078 +2325 13075 +2328 13073 +2331 13070 +2333 13068 +2336 13065 +2339 13063 +2342 13060 +2345 13057 +2348 13055 +2351 13052 +2354 13050 +2356 13047 +2359 13045 +2362 13042 +2365 13040 +2368 13037 +2371 13034 +2374 13032 +2377 13029 +2380 13027 +2383 13024 +2386 13021 +2389 13019 +2392 13016 +2395 13014 +2398 13011 +2401 13008 +2404 13006 +2407 13003 +2410 13000 +2413 12998 +2416 12995 +2419 12992 +2422 12990 +2425 12987 +2428 12985 +2431 12982 +2434 12979 +2437 12977 +2440 12974 +2444 12971 +2447 12968 +2450 12966 +2453 12963 +2456 12960 +2459 12958 +2462 12955 +2465 12952 +2469 12950 +2472 12947 +2475 12944 +2478 12941 +2481 12939 +2484 12936 +2488 12933 +2491 12930 +2494 12928 +2497 12925 +2500 12922 +2504 12919 +2507 12917 +2510 12914 +2513 12911 +2516 12908 +2520 12905 +2523 12903 +2526 12900 +2530 12897 +2533 12894 +2536 12891 +2539 12889 +2543 12886 +2546 12883 +2549 12880 +2553 12877 +2556 12875 +2559 12872 +2563 12869 +2566 12866 +2569 12863 +2573 12860 +2576 12857 +2579 12855 +2583 12852 +2586 12849 +2590 12846 +2593 12843 +2596 12840 +2600 12837 +2603 12834 +2607 12831 +2610 12829 +2614 12826 +2617 12823 +2620 12820 +2624 12817 +2627 12814 +2631 12811 +2634 12808 +2638 12805 +2641 12802 +2645 12799 +2648 12796 +2652 12793 +2655 12790 +2659 12787 +2663 12784 +2666 12781 +2670 12778 +2673 12775 +2677 12772 +2680 12769 +2684 12766 +2688 12763 +2691 12760 +2695 12757 +2698 12754 +2702 12751 +2706 12748 +2709 12745 +2713 12742 +2717 12739 +2720 12736 +2724 12733 +2728 12730 +2731 12727 +2735 12724 +2739 12721 +2742 12717 +2746 12714 +2750 12711 +2754 12708 +2757 12705 +2761 12702 +2765 12699 +2769 12696 +2772 12693 +2776 12689 +2780 12686 +2784 12683 +2788 12680 +2791 12677 +2795 12674 +2799 12671 +2803 12667 +2807 12664 +2810 12661 +2814 12658 +2818 12655 +2822 12651 +2826 12648 +2830 12645 +2834 12642 +2838 12639 +2841 12635 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 diff --git a/Analysis/config/exclusionMask_540.txt.png b/Analysis/config/exclusionMask_540.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee73ce02e00f9dbc6ab91c4884e1e3307fcdaec GIT binary patch literal 213354 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�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 RQL#ldq0gYWy|MbN{{xIu*ysQN literal 0 HcmV?d00001 diff --git a/Analysis/config/exclusionMask_541.txt b/Analysis/config/exclusionMask_541.txt index 12c125d3..f7b97bd1 100644 --- a/Analysis/config/exclusionMask_541.txt +++ b/Analysis/config/exclusionMask_541.txt @@ -1,10657 +1,10657 @@ 15456 10656 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -4898 5073 -4853 10304 -4808 10304 -4764 11047 -3850 11204 -3694 11603 -3598 11608 -3564 11763 -3516 11780 -3467 11797 -3418 11813 -3399 11830 -3392 11847 -3386 11864 -3380 11881 -3373 11897 -3367 11914 -3361 11931 -3355 11944 -3348 11953 -3342 11963 -3336 11972 -3330 11981 -3323 11990 -3317 12000 -3311 12009 -3305 12018 -3298 12028 -3292 12037 -3286 12046 -3280 12055 -3273 12065 -3267 12074 -3261 12083 -3254 12089 -3249 12096 -3244 12102 -3240 12109 -3235 12115 -3231 12122 -3227 12128 -3222 12134 -3218 12141 -3213 12147 -3209 12154 -3204 12160 -3200 12167 -3196 12173 -3191 12180 -3187 12186 -3182 12192 -3178 12199 -3173 12205 -3169 12212 -3165 12218 -3160 12225 -3156 12231 -3151 12237 -3147 12244 -3143 12250 -3138 12257 -3134 12263 -3129 12270 -3125 12276 -3120 12283 -3116 12289 -3112 12295 -3107 12302 -3103 12308 -3098 12315 -3094 12321 -3089 12328 -3085 12334 -3081 12340 -3077 12344 -3073 12349 -3068 12354 -3064 12359 -3060 12363 -3056 12368 -3052 12373 -3048 12378 -3044 12382 -3040 12387 -3035 12392 -3031 12397 -3027 12401 -3023 12406 -3019 12411 -3015 12416 -3011 12420 -3007 12425 -3002 12430 -2998 12435 -2994 12439 -2990 12444 -2986 12449 -2982 12454 -2978 12458 -2974 12463 -2969 12468 -2965 12473 -2961 12477 -2957 12482 -2953 12487 -2949 12491 -2945 12496 -2941 12501 -2936 12505 -2932 12509 -2928 12513 -2924 12517 -2920 12521 -2916 12524 -2912 12528 -2908 12532 -2903 12536 -2899 12540 -2895 12544 -2891 12547 -2887 12551 -2883 12555 -2879 12559 -2875 12563 -2871 12566 -2866 12570 -2861 12574 -2857 12578 -2852 12582 -2847 12585 -2842 12589 -2838 12593 -2833 12597 -2828 12601 -2824 12604 -2819 12608 -2814 12612 -2810 12616 -2805 12620 -2800 12623 -2796 12627 -2791 12631 -2786 12635 -2782 12639 -2777 12642 -2772 12645 -2768 12648 -2763 12651 -2758 12654 -2754 12657 -2749 12660 -2744 12663 -2739 12666 -2735 12669 -2730 12672 -2725 12675 -2721 12678 -2716 12681 -2711 12684 -2708 12687 -2705 12690 -2702 12694 -2699 12697 -2696 12700 -2693 12703 -2690 12706 -2687 12709 -2684 12712 -2681 12715 -2678 12718 -2675 12721 -2672 12724 -2669 12727 -2666 12730 -2663 12733 -2660 12736 -2657 12739 -2654 12742 -2651 12745 -2648 12748 -2645 12752 -2642 12755 -2639 12758 -2636 12761 -2633 12764 -2630 12767 -2627 12769 -2624 12772 -2621 12775 -2618 12777 -2615 12780 -2612 12783 -2609 12786 -2606 12788 -2603 12791 -2600 12794 -2597 12796 -2594 12799 -2591 12802 -2588 12804 -2585 12807 -2574 12810 -2569 12812 -2564 12815 -2560 12818 -2555 12821 -2550 12823 -2545 12826 -2542 12829 -2539 12831 -2537 12834 -2534 12837 -2531 12839 -2528 12842 -2525 12845 -2522 12847 -2520 12850 -2517 12853 -2514 12856 -2511 12858 -2508 12861 -2505 12864 -2503 12866 -2500 12869 -2497 12872 -2494 12874 -2492 12877 -2490 12880 -2488 12880 -2486 12880 -2484 12881 -2482 12884 -2480 12887 -2478 12890 -2476 12893 -2474 12896 -2472 12900 -2470 12903 -2468 12906 -2466 12909 -2464 12912 -2462 12915 -2460 12918 -2458 12921 -2456 12924 -2453 12927 -2451 12930 -2449 12933 -2447 12936 -2445 12939 -2443 12943 -2441 12946 -2439 12949 -2437 12952 -2435 12955 -2433 12958 -2431 12961 -2429 12964 -2427 12967 -2425 12970 -2423 12972 -2421 12975 -2419 12977 -2417 12980 -2415 12982 -2413 12984 -2411 12987 -2409 12989 -2407 12992 -2405 12994 -2403 12996 -2401 12999 -2399 13001 -2397 13004 -2395 13006 -2393 13008 -2391 13011 -2389 13013 -2387 13016 -2385 13018 -2383 13020 -2381 13023 -2379 13025 -2377 13028 -2375 13030 -2373 13032 -2371 13035 -2369 13037 -2367 13040 -2365 13042 -2363 13044 -2361 13047 -2359 13049 -2357 13052 -2355 13054 -2353 13056 -2351 13059 -2349 13061 -2347 13064 -2345 13066 -2343 13068 -2341 13071 -2339 13073 -2337 13076 -2335 13078 -2333 13080 -2331 13083 -2329 13085 -2327 13088 -2325 13090 -2323 13092 -2321 13095 -2319 13097 -2317 13100 -2315 13102 -2313 13104 -2311 13107 -2309 13109 -2307 13112 -2305 13114 -2303 13116 -2301 13119 -2298 13121 -2296 13124 -2294 13126 -2292 13128 -2290 13131 -2288 13133 -2286 13136 -2283 13138 -2281 13140 -2279 13143 -2277 13145 -2275 13148 -2273 13150 -2271 13152 -2268 13155 -2266 13157 -2264 13160 -2262 13162 -2260 13164 -2258 13167 -2256 13169 -2253 13172 -2251 13174 -2249 13176 -2247 13179 -2245 13181 -2243 13184 -2241 13186 -2238 13188 -2236 13191 -2234 13193 -2232 13195 -2230 13197 -2228 13199 -2226 13201 -2223 13203 -2221 13206 -2219 13208 -2217 13210 -2215 13212 -2213 13214 -2211 13216 -2208 13219 -2206 13221 -2204 13223 -2202 13225 -2200 13227 -2198 13229 -2196 13232 -2193 13234 -2191 13236 -2189 13238 -2187 13240 -2185 13242 -2183 13245 -2181 13247 -2179 13249 -2176 13251 -2174 13253 -2172 13255 -2170 13257 -2168 13260 -2166 13262 -2164 13264 -2161 13266 -2159 13268 -2157 13270 -2155 13273 -2153 13275 -2151 13277 -2149 13279 -2146 13281 -2144 13283 -2142 13286 -2140 13288 -2138 13290 -2136 13292 -2134 13294 -2131 13296 -2129 13298 -2127 13301 -2125 13303 -2123 13305 -2121 13307 -2119 13309 -2116 13311 -2114 13314 -2112 13316 -2110 13318 -2108 13320 -2106 13322 -2104 13324 -2101 13327 -2099 13329 -2097 13331 -2095 13333 -2093 13335 -2091 13337 -2089 13339 -2086 13342 -2084 13344 -2082 13346 -2080 13348 -2078 13350 -2076 13352 -2074 13355 -2071 13357 -2069 13359 -2067 13361 -2065 13363 -2063 13365 -2061 13368 -2059 13370 -2056 13372 -2054 13374 -2052 13376 -2050 13378 -2048 13381 -2046 13383 -2044 13385 -2041 13387 -2039 13389 -2037 13391 -2035 13393 -2033 13396 -2031 13398 -2029 13400 -2026 13402 -2024 13405 -2022 13407 -2020 13409 -2018 13412 -2016 13414 -2014 13416 -2011 13419 -2009 13421 -2007 13423 -2005 13425 -2003 13428 -2001 13430 -1999 13432 -1996 13435 -1994 13437 -1992 13439 -1990 13442 -1988 13444 -1986 13446 -1984 13448 -1981 13451 -1979 13453 -1977 13455 -1975 13458 -1973 13460 -1971 13462 -1969 13465 -1966 13467 -1964 13469 -1962 13471 -1960 13474 -1958 13476 -1956 13478 -1954 13481 -1952 13483 -1949 13485 -1947 13488 -1945 13490 -1943 13492 -1941 13494 -1939 13497 -1937 13499 -1934 13501 -1932 13504 -1930 13506 -1928 13508 -1926 13511 -1924 13513 -1922 13515 -1919 13517 -1917 13520 -1915 13522 -1913 13524 -1911 13527 -1909 13529 -1907 13531 -1904 13534 -1902 13536 -1900 13538 -1898 13540 -1896 13543 -1894 13545 -1892 13547 -1889 13550 -1887 13552 -1885 13555 -1883 13557 -1881 13559 -1879 13562 -1877 13564 -1874 13567 -1872 13569 -1870 13572 -1868 13574 -1866 13576 -1864 13579 -1862 13581 -1860 13584 -1858 13586 -1856 13588 -1854 13591 -1852 13593 -1850 13596 -1849 13598 -1847 13601 -1845 13603 -1843 13605 -1841 13608 -1839 13610 -1837 13613 -1835 13615 -1833 13618 -1831 13620 -1830 13622 -1828 13625 -1826 13627 -1824 13630 -1822 13632 -1820 13634 -1818 13637 -1816 13639 -1814 13642 -1813 13644 -1811 13647 -1809 13649 -1807 13651 -1805 13654 -1803 13656 -1801 13659 -1799 13661 -1797 13663 -1795 13666 -1794 13668 -1792 13671 -1790 13673 -1788 13676 -1786 13678 -1784 13680 -1782 13683 -1780 13685 -1778 13688 -1777 13690 -1775 13692 -1773 13695 -1771 13697 -1769 13700 -1767 13702 -1765 13705 -1763 13707 -1761 13709 -1759 13711 -1758 13712 -1756 13714 -1754 13715 -1752 13717 -1750 13719 -1748 13720 -1746 13722 -1744 13724 -1742 13725 -1741 13727 -1739 13729 -1737 13730 -1735 13732 -1733 13734 -1731 13735 -1729 13737 -1727 13739 -1725 13740 -1724 13742 -1722 13743 -1720 13745 -1718 13747 -1716 13748 -1714 13750 -1712 13752 -1710 13753 -1708 13755 -1706 13757 -1705 13758 -1703 13760 -1701 13762 -1699 13763 -1697 13765 -1695 13767 -1693 13768 -1691 13770 -1689 13771 -1688 13773 -1686 13775 -1684 13776 -1682 13778 -1680 13780 -1678 13781 -1676 13783 -1674 13785 -1672 13786 -1670 13788 -1669 13790 -1667 13791 -1665 13793 -1663 13795 -1661 13796 -1660 13798 -1658 13799 -1656 13801 -1654 13803 -1653 13804 -1651 13806 -1649 13808 -1647 13809 -1646 13811 -1644 13813 -1642 13814 -1640 13816 -1639 13818 -1637 13819 -1635 13821 -1633 13823 -1632 13824 -1630 13826 -1628 13827 -1626 13829 -1625 13831 -1623 13832 -1621 13834 -1619 13836 -1618 13837 -1616 13839 -1614 13841 -1612 13842 -1611 13844 -1609 13846 -1607 13847 -1605 13849 -1604 13851 -1602 13852 -1600 13854 -1598 13855 -1597 13857 -1595 13859 -1593 13860 -1591 13862 -1590 13864 -1588 13865 -1586 13867 -1584 13869 -1583 13870 -1581 13872 -1579 13874 -1577 13875 -1576 13877 -1574 13879 -1572 13880 -1570 13882 -1569 13883 -1567 13885 -1565 13887 -1563 13888 -1562 13890 -1560 13891 -1558 13893 -1556 13894 -1555 13896 -1553 13897 -1551 13898 -1549 13900 -1548 13901 -1546 13903 -1544 13904 -1542 13905 -1541 13907 -1539 13908 -1537 13910 -1535 13911 -1534 13912 -1532 13914 -1530 13915 -1528 13917 -1527 13918 -1525 13920 -1523 13921 -1521 13922 -1520 13924 -1518 13925 -1516 13927 -1514 13928 -1513 13929 -1511 13931 -1509 13932 -1507 13934 -1506 13935 -1505 13937 -1503 13938 -1502 13939 -1501 13941 -1499 13942 -1498 13944 -1497 13945 -1495 13946 -1494 13948 -1493 13949 -1491 13951 -1490 13952 -1489 13953 -1487 13955 -1486 13956 -1484 13958 -1483 13959 -1482 13961 -1480 13962 -1479 13963 -1478 13965 -1476 13966 -1475 13968 -1474 13969 -1472 13970 -1471 13972 -1470 13973 -1468 13975 -1467 13976 -1466 13977 -1464 13979 -1463 13980 -1462 13982 -1460 13983 -1459 13985 -1457 13986 -1456 13987 -1455 13989 -1453 13990 -1452 13992 -1451 13993 -1449 13994 -1448 13996 -1447 13997 -1445 13999 -1444 14000 -1443 14001 -1441 14003 -1440 14004 -1439 14006 -1437 14007 -1436 14009 -1434 14010 -1433 14011 -1432 14013 -1430 14014 -1429 14016 -1428 14017 -1426 14018 -1425 14020 -1424 14021 -1422 14023 -1421 14024 -1420 14025 -1418 14027 -1417 14028 -1416 14030 -1414 14031 -1413 14032 -1412 14033 -1410 14034 -1409 14035 -1407 14036 -1406 14038 -1405 14039 -1403 14040 -1402 14041 -1401 14042 -1399 14043 -1398 14044 -1397 14045 -1395 14047 -1394 14048 -1393 14049 -1391 14050 -1390 14051 -1389 14052 -1387 14053 -1386 14055 -1385 14056 -1384 14057 -1383 14058 -1382 14059 -1381 14060 -1380 14061 -1379 14062 -1378 14064 -1376 14065 -1375 14066 -1374 14067 -1373 14068 -1372 14069 -1371 14070 -1370 14072 -1369 14073 -1368 14074 -1366 14075 -1365 14076 -1364 14077 -1363 14078 -1362 14079 -1361 14081 -1360 14082 -1359 14083 -1358 14084 -1356 14085 -1355 14086 -1354 14087 -1353 14088 -1352 14090 -1351 14091 -1350 14092 -1349 14093 -1348 14094 -1347 14095 -1345 14096 -1344 14098 -1343 14099 -1342 14100 -1341 14101 -1340 14102 -1339 14103 -1338 14104 -1337 14105 -1335 14107 -1334 14108 -1333 14109 -1332 14110 -1331 14111 -1330 14112 -1329 14113 -1328 14115 -1327 14116 -1325 14117 -1324 14118 -1323 14119 -1322 14120 -1321 14121 -1320 14122 -1319 14123 -1318 14124 -1317 14125 -1316 14126 -1314 14127 -1313 14128 -1312 14128 -1311 14129 -1310 14130 -1309 14131 -1308 14132 -1307 14133 -1306 14134 -1304 14134 -1303 14135 -1302 14136 -1301 14137 -1300 14138 -1299 14139 -1298 14140 -1297 14141 -1296 14141 -1294 14142 -1293 14143 -1292 14144 -1291 14145 -1288 14146 -1287 14147 -1286 14147 -1285 14148 -1285 14149 -1284 14150 -1283 14151 -1282 14152 -1281 14153 -1280 14154 -1279 14154 -1278 14155 -1277 14156 -1276 14157 -1275 14158 -1274 14159 -1273 14160 -1272 14161 -1271 14161 -1270 14162 -1269 14165 -1268 14169 -1267 14170 -1266 14170 -1265 14171 -1264 14172 -1263 14173 -1262 14174 -1261 14174 -1260 14175 -1259 14176 -1258 14177 -1257 14178 -1256 14179 -1255 14179 -1254 14180 -1253 14181 -1252 14182 -1251 14183 -1250 14183 -1249 14184 -1248 14185 -1247 14186 -1246 14187 -1245 14187 -1244 14188 -1243 14189 -1242 14190 -1241 14191 -1240 14192 -1239 14192 -1238 14193 -1238 14194 -1237 14195 -1236 14196 -1235 14196 -1234 14197 -1233 14198 -1232 14199 -1231 14200 -1230 14201 -1229 14201 -1228 14202 -1228 14203 -1227 14204 -1226 14205 -1225 14205 -1224 14206 -1223 14207 -1222 14208 -1222 14209 -1221 14209 -1220 14210 -1219 14211 -1218 14212 -1217 14213 -1216 14214 -1215 14214 -1215 14215 -1214 14216 -1213 14217 -1212 14218 -1211 14218 -1210 14219 -1209 14220 -1209 14221 -1208 14222 -1207 14222 -1206 14223 -1205 14224 -1204 14225 -1203 14225 -1203 14226 -1202 14227 -1201 14227 -1200 14228 -1199 14229 -1198 14229 -1197 14230 -1196 14231 -1196 14231 -1195 14232 -1194 14233 -1193 14233 -1192 14234 -1191 14235 -1190 14235 -1190 14236 -1189 14237 -1188 14237 -1187 14238 -1186 14239 -1185 14239 -1184 14240 -1184 14241 -1183 14241 -1182 14242 -1181 14243 -1180 14243 -1179 14244 -1178 14245 -1177 14245 -1177 14246 -1176 14247 -1175 14247 -1174 14248 -1173 14249 -1172 14249 -1171 14250 -1171 14251 -1170 14251 -1169 14252 -1168 14253 -1167 14253 -1167 14254 -1166 14255 -1165 14255 -1164 14256 -1164 14257 -1163 14257 -1162 14258 -1162 14259 -1161 14259 -1160 14260 -1159 14261 -1159 14261 -1158 14262 -1157 14263 -1156 14263 -1156 14264 -1155 14265 -1154 14265 -1154 14266 -1153 14267 -1152 14267 -1151 14268 -1151 14269 -1150 14269 -1149 14270 -1148 14271 -1148 14271 -1147 14272 -1146 14273 -1146 14273 -1145 14274 -1144 14275 -1143 14275 -1143 14276 -1142 14277 -1141 14277 -1140 14278 -1140 14279 -1139 14279 -1138 14280 -1137 14281 -1137 14281 -1136 14282 -1135 14283 -1135 14283 -1134 14284 -1133 14284 -1132 14285 -1132 14286 -1131 14286 -1130 14287 -1129 14288 -1129 14288 -1128 14289 -1127 14290 -1127 14290 -1126 14291 -1125 14292 -1124 14292 -1124 14293 -1123 14294 -1122 14294 -1121 14295 -1121 14296 -1120 14296 -1119 14297 -1119 14298 -1118 14298 -1117 14299 -1116 14300 -1116 14300 -1115 14301 -1114 14302 -1113 14302 -1113 14303 -1112 14304 -1111 14304 -1111 14305 -1110 14306 -1109 14306 -1108 14307 -1108 14308 -1107 14308 -1106 14309 -1105 14310 -1105 14310 -1104 14311 -1103 14312 -1102 14312 -1102 14313 -1101 14314 -1100 14314 -1100 14315 -1099 14316 -1098 14316 -1097 14317 -1097 14318 -1096 14318 -1095 14319 -1094 14320 -1094 14320 -1093 14321 -1092 14322 -1092 14322 -1091 14323 -1090 14324 -1089 14324 -1089 14325 -1088 14326 -1087 14326 -1086 14327 -1086 14328 -1085 14328 -1084 14329 -1084 14330 -1083 14330 -1082 14331 -1081 14332 -1081 14332 -1080 14333 -1079 14334 -1079 14334 -1078 14335 -1077 14336 -1077 14336 -1076 14337 -1075 14338 -1075 14338 -1074 14339 -1073 14340 -1073 14340 -1072 14341 -1071 14342 -1071 14342 -1070 14343 -1069 14343 -1069 14344 -1068 14345 -1067 14345 -1067 14346 -1066 14347 -1065 14347 -1065 14348 -1064 14349 -1063 14349 -1062 14350 -1062 14350 -1061 14351 -1060 14352 -1060 14352 -1059 14353 -1058 14354 -1058 14354 -1057 14355 -1056 14355 -1056 14356 -1055 14357 -1054 14357 -1054 14358 -1053 14359 -1052 14359 -1052 14360 -1051 14360 -1050 14361 -1050 14362 -1049 14362 -1048 14363 -1048 14364 -1047 14364 -1046 14365 -1046 14366 -1045 14366 -1044 14367 -1044 14367 -1043 14368 -1042 14369 -1041 14369 -1041 14370 -1040 14371 -1039 14371 -1039 14372 -1038 14372 -1037 14373 -1037 14374 -1036 14374 -1035 14375 -1035 14376 -1034 14376 -1033 14377 -1033 14377 -1032 14378 -1031 14379 -1031 14379 -1030 14380 -1029 14381 -1029 14381 -1028 14382 -1027 14382 -1027 14383 -1026 14384 -1025 14384 -1025 14385 -1024 14386 -1023 14386 -1023 14387 -1022 14388 -1021 14388 -1020 14389 -1020 14389 -1019 14390 -1019 14391 -1018 14391 -1018 14392 -1017 14393 -1016 14393 -1016 14394 -1015 14394 -1015 14395 -1014 14396 -1013 14396 -1013 14397 -1012 14398 -1012 14398 -1011 14399 -1010 14399 -1010 14400 -1009 14401 -1009 14401 -1008 14402 -1008 14403 -1007 14403 -1006 14404 -1006 14405 -1005 14405 -1005 14406 -1004 14406 -1003 14407 -1003 14408 -1002 14408 -1002 14409 -1001 14410 -1001 14410 -1000 14411 -999 14411 -999 14412 -998 14413 -998 14413 -997 14414 -996 14415 -996 14415 -995 14416 -995 14416 -994 14417 -994 14418 -993 14418 -992 14419 -992 14420 -991 14420 -991 14421 -990 14422 -989 14422 -989 14423 -988 14423 -988 14424 -987 14425 -987 14425 -986 14426 -985 14427 -985 14427 -984 14428 -984 14428 -983 14429 -982 14430 -982 14430 -981 14431 -981 14432 -980 14432 -979 14433 -979 14434 -978 14434 -978 14435 -977 14435 -977 14436 -976 14437 -975 14437 -975 14429 -975 14430 -975 14430 -975 14431 -975 14432 -975 14432 -974 14433 -974 14434 -974 14435 -973 14435 -973 14436 -972 14437 -972 14437 -971 14438 -971 14439 -970 14439 -970 14440 -969 14441 -968 14441 -968 14442 -967 14443 -967 14443 -966 14444 -966 14445 -965 14445 -965 14446 -964 14447 -964 14447 -963 14448 -963 14449 -962 14450 -962 14450 -961 14451 -960 14452 -960 14452 -959 14453 -959 14454 -958 14454 -958 14455 -957 14456 -957 14456 -956 14457 -956 14458 -955 14458 -955 14459 -954 14460 -953 14460 -953 14461 -952 14462 -952 14463 -951 14463 -951 14464 -950 14465 -950 14465 -949 14466 -949 14467 -948 14467 -948 14468 -947 14469 -947 14469 -946 14470 -945 14471 -945 14471 -944 14472 -944 14473 -943 14473 -943 14474 -942 14475 -942 14476 -941 14476 -941 14477 -940 14478 -940 14478 -939 14479 -938 14480 -938 14480 -937 14481 -937 14482 -936 14482 -936 14483 -935 14484 -935 14484 -934 14485 -934 14486 -933 14486 -933 14487 -932 14488 -931 14489 -931 14489 -930 14490 -930 14491 -929 14491 -929 14492 -928 14493 -928 14493 -927 14494 -927 14495 -926 14495 -926 14496 -925 14497 -925 14497 -924 14498 -923 14499 -923 14499 -922 14500 -922 14501 -921 14502 -921 14502 -920 14503 -920 14504 -919 14504 -919 14505 -918 14506 -918 14506 -917 14507 -916 14508 -916 14508 -915 14509 -915 14510 -914 14510 -914 14511 -913 14512 -913 14512 -912 14513 -912 14514 -911 14515 -911 14515 -910 14516 -910 14517 -909 14517 -908 14518 -908 14519 -907 14519 -907 14520 -906 14521 -906 14521 -905 14522 -905 14523 -904 14523 -904 14524 -903 14525 -903 14525 -902 14526 -901 14527 -901 14527 -900 14528 -900 14529 -899 14530 -899 14530 -898 14531 -898 14532 -897 14532 -897 14533 -896 14534 -896 14534 -895 14535 -895 14536 -894 14536 -893 14537 -893 14538 -892 14538 -892 14539 -891 14540 -891 14540 -890 14541 -890 14542 -889 14543 -889 14543 -888 14544 -888 14545 -887 14545 -886 14546 -886 14546 -885 14547 -885 14547 -884 14548 -884 14549 -883 14549 -883 14550 -882 14550 -882 14551 -881 14551 -881 14552 -880 14553 -879 14553 -879 14554 -878 14554 -878 14555 -877 14555 -877 14556 -876 14556 -876 14557 -875 14558 -875 14558 -874 14559 -874 14559 -873 14560 -873 14560 -872 14561 -871 14562 -871 14562 -870 14563 -870 14563 -869 14564 -869 14564 -868 14565 -868 14565 -867 14566 -867 14567 -866 14567 -866 14568 -865 14568 -864 14569 -864 14569 -863 14570 -863 14571 -862 14571 -862 14572 -861 14572 -861 14573 -860 14573 -860 14574 -859 14575 -859 14575 -858 14576 -857 14576 -857 14577 -856 14577 -856 14578 -855 14578 -855 14579 -854 14580 -854 14580 -853 14581 -853 14581 -852 14582 -851 14582 -851 14583 -850 14584 -850 14584 -849 14585 -849 14585 -848 14586 -848 14586 -847 14587 -847 14587 -846 14588 -846 14589 -845 14589 -844 14590 -844 14590 -843 14591 -843 14591 -842 14592 -842 14593 -841 14593 -841 14594 -840 14594 -840 14595 -839 14595 -838 14596 -838 14596 -837 14597 -837 14598 -836 14598 -836 14599 -835 14599 -835 14600 -834 14600 -834 14601 -833 14602 -833 14602 -832 14603 -831 14603 -831 14604 -830 14604 -830 14605 -829 14605 -829 14606 -828 14607 -828 14607 -827 14608 -827 14608 -826 14609 -826 14609 -825 14610 -824 14611 -824 14611 -823 14612 -823 14612 -822 14613 -822 14613 -821 14614 -821 14614 -820 14615 -820 14616 -819 14616 -818 14617 -818 14617 -817 14618 -817 14618 -816 14619 -816 14620 -815 14620 -815 14621 -814 14621 -814 14622 -813 14622 -813 14623 -812 14624 -811 14624 -811 14625 -810 14625 -810 14626 -809 14626 -809 14627 -808 14627 -808 14628 -807 14629 -807 14629 -806 14630 -805 14630 -805 14631 -804 14631 -804 14632 -803 14633 -803 14633 -802 14634 -802 14634 -801 14635 -801 14635 -800 14636 -800 14636 -799 14637 -798 14638 -798 14638 -797 14639 -797 14639 -796 14640 -796 14640 -795 14641 -795 14642 -794 14642 -794 14643 -793 14643 -792 14644 -792 14644 -791 14645 -791 14645 -790 14646 -790 14647 -789 14647 -789 14648 -788 14648 -788 14649 -787 14650 -787 14650 -786 14651 -785 14651 -785 14652 -784 14652 -784 14653 -783 14654 -783 14654 -782 14655 -782 14655 -781 14656 -781 14656 -780 14657 -780 14658 -779 14658 -778 14659 -778 14659 -777 14660 -777 14660 -776 14661 -776 14662 -775 14662 -775 14663 -774 14663 -774 14664 -773 14664 -772 14665 -772 14666 -771 14666 -771 14667 -770 14667 -770 14668 -769 14669 -769 14669 -768 14670 -768 14670 -767 14671 -767 14671 -766 14672 -765 14673 -765 14673 -764 14674 -764 14674 -763 14675 -763 14675 -762 14676 -762 14677 -761 14677 -761 14678 -760 14678 -759 14679 -759 14679 -758 14680 -758 14681 -757 14681 -757 14682 -756 14682 -756 14683 -755 14683 -755 14684 -754 14685 -754 14685 -753 14686 -752 14686 -752 14687 -751 14688 -751 14688 -750 14689 -750 14689 -749 14690 -749 14690 -748 14691 -748 14692 -747 14692 -747 14693 -746 14693 -746 14694 -745 14694 -745 14695 -744 14696 -744 14696 -743 14697 -743 14697 -742 14698 -742 14698 -741 14699 -741 14700 -740 14700 -740 14701 -739 14701 -739 14702 -738 14702 -738 14703 -737 14704 -737 14704 -736 14705 -736 14705 -735 14706 -735 14707 -734 14707 -734 14708 -733 14708 -732 14709 -732 14709 -731 14710 -731 14711 -730 14711 -730 14712 -729 14712 -729 14713 -728 14713 -728 14714 -727 14714 -727 14715 -726 14715 -726 14716 -725 14716 -725 14717 -724 14717 -724 14717 -723 14718 -723 14718 -722 14719 -722 14719 -721 14720 -721 14720 -720 14721 -720 14721 -719 14721 -719 14722 -718 14722 -718 14723 -717 14723 -717 14724 -716 14724 -716 14724 -715 14725 -715 14725 -714 14726 -714 14726 -713 14727 -713 14727 -712 14728 -712 14728 -711 14728 -711 14729 -710 14729 -710 14730 -709 14730 -709 14731 -708 14731 -708 14731 -707 14732 -707 14732 -706 14733 -706 14733 -705 14734 -705 14734 -704 14735 -704 14735 -703 14735 -703 14736 -702 14736 -701 14737 -701 14737 -700 14738 -700 14738 -699 14738 -699 14739 -698 14739 -698 14740 -697 14740 -697 14741 -696 14741 -696 14741 -695 14742 -695 14742 -695 14743 -694 14743 -694 14744 -693 14744 -693 14745 -692 14745 -692 14745 -691 14746 -691 14746 -690 14747 -690 14747 -689 14748 -689 14748 -689 14748 -688 14749 -688 14749 -687 14750 -687 14750 -686 14751 -686 14751 -685 14752 -685 14752 -684 14752 -684 14753 -683 14753 -683 14754 -682 14754 -682 14755 -682 14755 -681 14755 -681 14756 -680 14756 -680 14757 -679 14757 -679 14758 -678 14758 -678 14759 -677 14759 -677 14759 -676 14760 -676 14760 -675 14761 -675 14761 -675 14762 -674 14762 -674 14762 -673 14763 -673 14763 -672 14764 -672 14764 -671 14765 -671 14765 -670 14765 -670 14766 -669 14766 -669 14767 -669 14767 -668 14768 -668 14768 -667 14769 -667 14769 -666 14769 -666 14770 -665 14770 -665 14771 -664 14771 -664 14772 -663 14772 -663 14772 -662 14773 -662 14773 -662 14774 -661 14774 -661 14775 -660 14775 -660 14776 -659 14776 -659 14776 -659 14777 -658 14777 -658 14778 -658 14778 -657 14778 -657 14779 -657 14779 -656 14780 -656 14780 -656 14780 -655 14781 -655 14781 -654 14781 -654 14782 -654 14782 -653 14783 -653 14783 -653 14783 -652 14784 -652 14784 -652 14784 -651 14785 -651 14785 -651 14786 -650 14786 -650 14786 -650 14787 -649 14787 -649 14788 -649 14788 -648 14788 -648 14789 -648 14789 -647 14789 -647 14790 -647 14790 -646 14791 -646 14791 -646 14791 -645 14792 -645 14792 -644 14793 -644 14793 -644 14793 -643 14794 -643 14794 -643 14794 -642 14795 -642 14795 -642 14796 -641 14796 -641 14796 -641 14797 -640 14797 -640 14797 -640 14798 -639 14798 -639 14799 -639 14799 -638 14799 -638 14800 -638 14800 -637 14801 -637 14801 -637 14801 -636 14802 -636 14802 -636 14802 -635 14803 -635 14803 -634 14804 -634 14804 -634 14804 -633 14805 -633 14805 -633 14806 -632 14806 -632 14806 -632 14807 -631 14807 -631 14807 -631 14808 -630 14808 -630 14809 -630 14809 -629 14809 -629 14810 -629 14810 -628 14810 -628 14811 -628 14811 -627 14812 -627 14812 -627 14812 -626 14813 -626 14813 -625 14814 -625 14814 -625 14814 -624 14815 -624 14815 -624 14815 -623 14816 -623 14816 -623 14817 -622 14817 -622 14817 -622 14818 -621 14818 -621 14819 -621 14819 -620 14819 -620 14820 -620 14820 -620 14820 -619 14821 -619 14821 -619 14822 -618 14822 -618 14822 -618 14823 -618 14823 -617 14823 -617 14824 -617 14824 -616 14825 -616 14825 -616 14825 -616 14826 -615 14826 -615 14827 -615 14827 -614 14827 -614 14828 -614 14828 -614 14828 -613 14829 -613 14829 -613 14830 -613 14830 -612 14830 -612 14831 -612 14831 -611 14832 -611 14832 -611 14832 -611 14833 -610 14833 -610 14833 -610 14834 -609 14834 -609 14835 -609 14835 -609 14835 -608 14836 -608 14836 -608 14837 -607 14837 -607 14837 -607 14838 -607 14838 -606 14839 -606 14839 -606 14839 -605 14840 -605 14840 -605 14840 -605 14841 -604 14841 -604 14842 -604 14842 -603 14842 -603 14843 -603 14843 -603 14844 -602 14844 -602 14844 -602 14845 -602 14845 -601 14846 -601 14846 -601 14846 -600 14847 -600 14847 -600 14848 -600 14848 -599 14848 -599 14849 -599 14849 -598 14849 -598 14850 -598 14850 -598 14851 -597 14851 -597 14851 -597 14852 -596 14852 -596 14853 -596 14853 -596 14853 -595 14854 -595 14854 -595 14855 -594 14855 -594 14855 -594 14856 -594 14856 -593 14857 -593 14857 -593 14857 -593 14858 -592 14858 -592 14858 -592 14859 -591 14859 -591 14860 -591 14860 -591 14860 -590 14861 -590 14861 -590 14862 -589 14862 -589 14862 -589 14863 -589 14863 -588 14864 -588 14864 -588 14864 -587 14865 -587 14865 -587 14866 -587 14866 -586 14866 -586 14867 -586 14867 -585 14867 -585 14868 -585 14868 -585 14869 -584 14869 -584 14869 -584 14870 -583 14870 -583 14871 -583 14871 -583 14871 -582 14872 -582 14872 -582 14873 -582 14873 -581 14873 -581 14874 -581 14874 -580 14874 -580 14875 -580 14875 -580 14876 -579 14876 -579 14876 -579 14877 -578 14877 -578 14878 -578 14878 -578 14878 -577 14879 -577 14879 -577 14880 -576 14880 -576 14880 -576 14881 -576 14881 -575 14882 -575 14882 -575 14882 -574 14883 -574 14883 -574 14883 -574 14884 -573 14884 -573 14885 -573 14885 -572 14885 -572 14886 -572 14886 -572 14887 -571 14887 -571 14887 -571 14888 -571 14888 -570 14889 -570 14889 -570 14889 -569 14890 -569 14890 -569 14891 -569 14891 -568 14891 -568 14892 -568 14892 -567 14893 -567 14893 -567 14893 -567 14894 -566 14894 -566 14895 -566 14895 -565 14896 -565 14896 -565 14896 -565 14897 -564 14897 -564 14898 -564 14898 -563 14899 -563 14899 -563 14899 -563 14900 -562 14900 -562 14901 -562 14901 -562 14902 -561 14902 -561 14902 -561 14903 -560 14903 -560 14904 -560 14904 -560 14905 -559 14905 -559 14906 -559 14906 -558 14906 -558 14907 -558 14907 -558 14908 -557 14908 -557 14909 -557 14909 -556 14909 -556 14910 -556 14910 -556 14911 -555 14911 -555 14912 -555 14912 -554 14912 -554 14913 -554 14913 -554 14914 -553 14914 -553 14915 -553 14915 -553 14916 -552 14916 -552 14916 -552 14917 -551 14917 -551 14918 -551 14918 -551 14919 -550 14919 -550 14919 -550 14920 -549 14920 -549 14921 -549 14921 -549 14922 -548 14922 -548 14922 -548 14923 -547 14923 -547 14924 -547 14924 -547 14925 -546 14925 -546 14925 -546 14926 -545 14926 -545 14927 -545 14927 -545 14928 -544 14928 -544 14929 -544 14929 -544 14929 -543 14930 -543 14930 -543 14931 -542 14931 -542 14932 -542 14932 -542 14932 -541 14933 -541 14933 -541 14934 -540 14934 -540 14935 -540 14935 -540 14935 -539 14936 -539 14936 -539 14937 -538 14937 -538 14938 -538 14938 -538 14939 -537 14939 -537 14939 -537 14940 -537 14940 -536 14941 -536 14941 -536 14942 -535 14942 -535 14942 -535 14943 -535 14943 -534 14944 -534 14944 -534 14944 -533 14944 -533 14945 -533 14945 -533 14945 -532 14945 -532 14945 -532 14946 -531 14946 -531 14946 -531 14946 -531 14947 -530 14947 -530 14947 -530 14947 -529 14947 -529 14948 -529 14948 -529 14948 -528 14948 -528 14949 -528 14949 -528 14949 -527 14949 -527 14949 -527 14950 -526 14950 -526 14950 -526 14950 -525 14951 -525 14951 -525 14951 -525 14951 -524 14951 -524 14952 -524 14952 -523 14952 -523 14952 -523 14952 -522 14953 -522 14953 -522 14953 -521 14953 -521 14954 -521 14954 -520 14954 -520 14954 -520 14954 -519 14955 -519 14955 -519 14955 -518 14955 -518 14956 -518 14956 -518 14956 -517 14956 -517 14956 -517 14957 -516 14957 -516 14957 -516 14957 -515 14957 -515 14958 -515 14958 -514 14958 -514 14958 -514 14959 -513 14959 -513 14959 -513 14959 -512 14959 -512 14960 -512 14960 -511 14960 -511 14960 -511 14961 -510 14961 -510 14961 -510 14961 -510 14961 -509 14962 -509 14962 -509 14962 -508 14962 -508 14962 -508 14963 -507 14963 -507 14963 -507 14963 -506 14964 -506 14964 -506 14964 -505 14964 -505 14964 -505 14965 -504 14965 -504 14965 -504 14965 -503 14966 -503 14966 -503 14966 -503 14966 -502 14966 -502 14967 -502 14967 -501 14967 -501 14967 -501 14967 -500 14968 -500 14968 -500 14968 -499 14968 -499 14969 -499 14969 -498 14969 -498 14969 -498 14969 -498 14970 -498 14970 -497 14970 -497 14970 -497 14971 -497 14971 -496 14971 -496 14971 -496 14971 -496 14971 -495 14972 -495 14972 -495 14972 -495 14972 -495 14972 -494 14972 -494 14972 -494 14973 -494 14973 -493 14973 -493 14973 -493 14973 -493 14973 -492 14973 -492 14974 -492 14974 -492 14974 -492 14974 -491 14974 -491 14974 -491 14974 -491 14975 -490 14975 -490 14975 -490 14975 -490 14975 -489 14975 -489 14976 -489 14976 -489 14976 -488 14976 -488 14976 -488 14976 -488 14976 -488 14977 -487 14977 -487 14977 -487 14977 -487 14977 -486 14977 -486 14977 -486 14978 -486 14978 -485 14978 -485 14978 -485 14978 -485 14978 -485 14979 -484 14979 -484 14979 -484 14979 -484 14979 -483 14979 -483 14979 -483 14980 -483 14980 -482 14980 -482 14980 -482 14980 -482 14980 -482 14980 -481 14981 -481 14981 -481 14981 -481 14981 -480 14981 -480 14981 -480 14981 -480 14982 -479 14982 -479 14982 -479 14982 -479 14982 -478 14982 -478 14983 -478 14983 -478 14983 -478 14983 -477 14983 -477 14983 -477 14983 -477 14984 -476 14984 -476 14984 -476 14984 -476 14984 -475 14984 -475 14984 -475 14985 -475 14985 -475 14985 -474 14985 -474 14985 -474 14985 -474 14986 -473 14986 -473 14986 -473 14986 -473 14986 -472 14986 -472 14986 -472 14987 -472 14987 -472 14987 -471 14987 -471 14987 -471 14987 -471 14987 -471 14988 -470 14988 -470 14988 -470 14988 -470 14988 -470 14988 -470 14988 -469 14989 -469 14989 -469 14989 -469 14989 -469 14989 -468 14989 -468 14990 -468 14990 -468 14990 -468 14990 -468 14990 -467 14990 -467 14990 -467 14991 -467 14991 -467 14991 -466 14991 -466 14991 -466 14991 -466 14991 -466 14992 -466 14992 -465 14992 -465 14992 -465 14992 -465 14992 -465 14993 -464 14993 -464 14993 -464 14993 -464 14993 -464 14993 -464 14993 -463 14994 -463 14994 -463 14994 -463 14994 -463 14994 -462 14994 -462 14994 -462 14995 -462 14995 -462 14995 -462 14995 -461 14995 -461 14995 -461 14995 -461 14996 -461 14996 -460 14996 -460 14996 -460 14996 -460 14996 -460 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14998 -459 14998 -459 14998 -459 14998 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 15000 -459 15000 -459 15000 -459 15000 -458 15000 -458 15000 -458 15000 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15002 -458 15002 -458 15002 -457 15002 -457 15002 -457 15002 -457 15003 -457 15003 -457 15003 -457 15003 -457 15003 -456 15003 -456 15003 -456 15004 -456 15004 -456 15004 -456 15004 -456 15004 -455 15004 -455 15004 -455 15005 -455 15005 -455 15005 -455 15005 -455 15005 -454 15005 -454 15006 -454 15006 -454 15006 -454 15006 -454 15006 -454 15006 -453 15006 -453 15007 -453 15007 -453 15007 -453 15007 -453 15007 -453 15007 -452 15007 -452 15008 -452 15008 -452 15008 -452 15008 -452 15008 -452 15008 -451 15008 -451 15009 -451 15009 -451 15009 -451 15009 -451 15009 -451 15009 -451 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15013 -447 15013 -447 15013 -447 15013 -447 15013 -447 15013 -447 15014 -447 15014 -446 15014 -446 15014 -446 15014 -446 15014 -446 15014 -446 15015 -446 15015 -445 15015 -445 15015 -445 15015 -445 15015 -445 15015 -445 15016 -445 15016 -445 15016 -444 15016 -444 15016 -444 15016 -444 15016 -444 15017 -444 15017 -444 15017 -443 15017 -443 15017 -443 15017 -443 15018 -443 15018 -443 15018 -443 15018 -442 15018 -442 15018 -442 15018 -442 15019 -442 15019 -442 15019 -442 15019 -441 15019 -441 15019 -441 15019 -441 15020 -441 15020 -441 15020 -441 15020 -440 15020 -440 15020 -440 15021 -440 15021 -440 15021 -440 15021 -440 15021 -439 15021 -439 15021 -439 15022 -439 15022 -439 15022 -439 15022 -439 15022 -439 15022 -438 15022 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -437 15023 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -433 15028 -433 15028 -433 15028 -433 15028 -433 15028 -433 15028 -433 15029 -433 15029 -432 15029 -432 15029 -432 15029 -432 15029 -432 15029 -432 15030 -432 15030 -431 15030 -431 15030 -431 15030 -431 15030 -431 15030 -431 15031 -431 15031 -430 15031 -430 15031 -430 15031 -430 15031 -430 15032 -430 15032 -430 15032 -429 15032 -429 15032 -429 15032 -429 15032 -429 15033 -429 15033 -429 15033 -428 15033 -428 15033 -428 15033 -428 15033 -428 15034 -428 15034 -428 15034 -427 15034 -427 15034 -427 15034 -427 15034 -427 15035 -427 15035 -427 15035 -427 15035 -426 15035 -426 15035 -426 15036 -426 15036 -426 15036 -426 15036 -426 15036 -425 15036 -425 15036 -425 15037 -425 15037 -425 15037 -425 15037 -425 15037 -424 15037 -424 15037 -424 15038 -424 15038 -424 15038 -424 15038 -424 15038 -423 15038 -423 15038 -423 15039 -423 15039 -423 15039 -423 15039 -423 15039 -422 15039 -422 15040 -422 15040 -422 15040 -422 15040 -422 15040 -422 15040 -421 15040 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -419 15043 -419 15043 -419 15043 -419 15043 -419 15043 -419 15043 -419 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15046 -416 15046 -416 15046 -416 15046 -416 15046 -416 15046 -416 15047 -416 15047 -415 15047 -415 15047 -415 15047 -415 15047 -415 15048 -415 15048 -415 15048 -415 15048 -414 15048 -414 15048 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -413 15049 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -412 15050 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15054 -410 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15055 -409 15055 -409 15055 -408 15055 -408 15055 -408 15055 -408 15056 -408 15056 -408 15056 -408 15056 -407 15056 -407 15056 -407 15057 -407 15057 -407 15057 -407 15057 -407 15057 -406 15057 -406 15058 -406 15058 -406 15058 -406 15058 -406 15058 -406 15058 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -404 15060 -404 15060 -404 15060 -404 15060 -404 15060 -404 15060 -404 15061 -403 15061 -403 15061 -403 15061 -403 15061 -403 15061 -403 15062 -403 15062 -403 15062 -402 15062 -402 15062 -402 15062 -402 15063 -402 15063 -402 15063 -402 15063 -402 15063 -401 15063 -401 15063 -401 15064 -401 15064 -401 15064 -401 15064 -401 15064 -401 15064 -401 15065 -400 15065 -400 15065 -400 15065 -400 15065 -400 15065 -400 15066 -400 15066 -400 15066 -399 15066 -399 15066 -399 15066 -399 15067 -399 15067 -399 15067 -399 15067 -399 15067 -399 15067 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15069 -397 15069 -397 15069 -397 15069 -397 15069 -397 15069 -397 15070 -397 15070 -397 15070 -397 15070 -396 15070 -396 15070 -396 15071 -396 15071 -396 15071 -396 15071 -396 15071 -396 15071 -396 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15073 -395 15073 -394 15073 -394 15073 -394 15073 -394 15073 -394 15074 -394 15074 -394 15074 -394 15074 -394 15074 -393 15074 -393 15075 -393 15075 -393 15075 -393 15075 -393 15075 -393 15075 -393 15076 -392 15076 -392 15076 -392 15076 -392 15076 -392 15076 -392 15077 -392 15077 -392 15077 -392 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15078 -391 15078 -391 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -388 15079 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -387 15080 -387 15080 -387 15080 -387 15080 -387 15081 -387 15081 -387 15081 -387 15081 -387 15081 -386 15081 -386 15081 -386 15081 -386 15081 -386 15081 -386 15082 -386 15082 -386 15082 -386 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -383 15083 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -382 15084 -382 15084 -382 15084 -382 15085 -382 15085 -382 15085 -382 15085 -382 15085 -382 15085 -381 15085 -381 15085 -381 15085 -381 15085 -381 15085 -381 15086 -381 15086 -381 15086 -381 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15087 -380 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -377 15088 -377 15088 -377 15088 -377 15089 -377 15089 -377 15089 -377 15089 -377 15089 -377 15089 -376 15089 -376 15089 -376 15089 -376 15089 -376 15090 -376 15090 -376 15090 -376 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15091 -375 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -372 15092 -372 15092 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -371 15093 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -370 15094 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15099 -367 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15100 -366 15100 -365 15100 -365 15100 -365 15100 -365 15100 -365 15100 -365 15101 -365 15101 -365 15101 -365 15101 -364 15101 -364 15101 -364 15101 -364 15101 -364 15102 -364 15102 -363 15102 -363 15102 -363 15102 -363 15102 -363 15102 -362 15102 -362 15103 -362 15103 -362 15103 -362 15103 -362 15103 -361 15103 -361 15103 -361 15103 -361 15104 -361 15104 -360 15104 -360 15104 -360 15104 -360 15104 -360 15104 -360 15104 -359 15105 -359 15105 -359 15105 -359 15105 -359 15105 -358 15105 -358 15105 -358 15105 -358 15106 -358 15106 -357 15106 -357 15106 -357 15106 -357 15106 -357 15106 -357 15106 -356 15107 -356 15107 -356 15107 -356 15107 -356 15107 -355 15107 -355 15107 -355 15107 -355 15108 -355 15108 -355 15108 -354 15108 -354 15108 -354 15108 -354 15108 -354 15109 -353 15109 -353 15109 -353 15109 -353 15109 -353 15109 -353 15109 -352 15109 -352 15110 -352 15110 -352 15110 -352 15110 -351 15110 -351 15110 -351 15110 -351 15110 -351 15111 -350 15111 -350 15111 -350 15111 -350 15111 -350 15111 -350 15111 -349 15111 -349 15112 -349 15112 -349 15112 -349 15112 -348 15112 -348 15112 -348 15112 -348 15112 -348 15113 -348 15113 -347 15113 -347 15113 -347 15113 -347 15113 -347 15113 -346 15113 -346 15114 -346 15114 -346 15114 -346 15114 -346 15114 -345 15114 -345 15114 -345 15114 -345 15115 -345 15115 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -342 15116 -342 15116 -342 15117 -342 15117 -342 15117 -341 15117 -341 15117 -341 15117 -341 15117 -341 15117 -341 15118 -340 15118 -340 15118 -340 15118 -340 15118 -340 15118 -339 15118 -339 15119 -339 15119 -339 15119 -339 15119 -339 15119 -338 15119 -338 15120 -338 15120 -338 15120 -338 15120 -337 15120 -337 15120 -337 15121 -337 15121 -337 15121 -336 15121 -336 15121 -336 15121 -336 15122 -336 15122 -336 15122 -336 15122 -336 15122 -335 15122 -335 15123 -335 15123 -335 15123 -335 15123 -335 15123 -335 15123 -335 15124 -335 15124 -335 15124 -335 15124 -335 15124 -334 15124 -334 15125 -334 15125 -334 15125 -334 15125 -334 15125 -334 15125 -334 15126 -334 15126 -334 15126 -334 15126 -334 15126 -334 15126 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15128 -333 15128 -333 15128 -333 15128 -333 15128 -333 15128 -332 15129 -332 15129 -332 15129 -332 15129 -332 15129 -332 15129 -332 15130 -332 15130 -332 15130 -332 15130 -332 15130 -332 15130 -332 15131 -331 15131 -331 15131 -331 15131 -331 15131 -331 15131 -331 15132 -331 15132 -331 15132 -331 15132 -331 15132 -331 15132 -331 15133 -330 15133 -330 15133 -330 15133 -330 15133 -330 15133 -330 15134 -330 15134 -330 15134 -330 15134 -330 15134 -330 15134 -330 15135 -330 15135 -329 15135 -329 15135 -329 15135 -329 15135 -329 15136 -329 15136 -329 15136 -329 15136 -329 15136 -329 15136 -329 15137 -329 15137 -328 15137 -328 15137 -328 15137 -328 15137 -328 15138 -328 15138 -328 15138 -328 15138 -328 15138 -328 15138 -328 15139 -328 15139 -328 15139 -327 15139 -327 15139 -327 15139 -327 15140 -327 15140 -327 15140 -327 15140 -327 15140 -327 15140 -327 15141 -327 15141 -327 15141 -326 15141 -326 15141 -326 15141 -326 15142 -326 15142 -326 15142 -326 15142 -326 15142 -326 15142 -326 15143 -326 15143 -326 15143 -325 15143 -325 15143 -325 15143 -325 15144 -325 15144 -325 15144 -325 15144 -325 15144 -325 15144 -325 15145 -325 15145 -325 15145 -325 15145 -324 15145 -324 15145 -324 15146 -324 15146 -324 15146 -324 15146 -324 15146 -324 15146 -324 15147 -324 15147 -324 15147 -324 15147 -323 15147 -323 15147 -323 15148 -323 15148 -323 15148 -323 15148 -323 15148 -323 15148 -323 15149 -323 15149 -323 15149 -323 15149 -323 15149 -322 15149 -322 15150 -322 15150 -322 15150 -322 15150 -322 15150 -322 15150 -322 15151 -322 15151 -322 15151 -322 15151 -322 15151 -321 15151 -321 15151 -321 15152 -321 15152 -321 15152 -321 15152 -321 15152 -321 15152 -321 15153 -321 15153 -321 15153 -321 15153 -321 15153 -320 15153 -320 15153 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15155 -319 15155 -319 15155 -319 15155 -319 15155 -319 15155 -319 15156 -319 15156 -319 15156 -319 15156 -318 15156 -318 15156 -318 15156 -318 15157 -318 15157 -318 15157 -318 15157 -318 15157 -317 15157 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15160 -316 15160 -315 15160 -315 15160 -315 15160 -315 15160 -315 15161 -315 15161 -315 15161 -315 15161 -315 15161 -314 15161 -314 15161 -314 15162 -314 15162 -314 15162 -314 15162 -314 15162 -314 15162 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15164 -313 15164 -312 15164 -312 15164 -312 15164 -312 15164 -312 15164 -312 15165 -312 15165 -312 15165 -311 15165 -311 15165 -311 15165 -311 15166 -311 15166 -311 15166 -311 15166 -311 15166 -311 15166 -310 15166 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15168 -309 15168 -309 15168 -309 15168 -309 15168 -309 15168 -309 15169 -309 15169 -309 15169 -308 15169 -308 15169 -308 15169 -308 15169 -308 15170 -308 15170 -308 15170 -308 15170 -308 15170 -307 15170 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15173 -306 15173 -305 15173 -305 15173 -305 15173 -305 15173 -305 15174 -305 15174 -305 15174 -305 15174 -304 15174 -304 15174 -304 15174 -304 15175 -304 15175 -304 15175 -304 15175 -304 15175 -304 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -293 15175 -293 15175 -293 15175 -293 15175 -293 15178 -292 15178 -288 15178 -288 15178 -288 15178 -288 15178 -288 15178 -288 15178 -287 15178 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -286 15179 -286 15179 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -285 15180 -285 15180 -285 15180 -285 15181 -285 15181 -285 15181 -285 15181 -285 15181 -285 15181 -284 15181 -284 15181 -284 15181 -284 15181 -284 15181 -284 15182 -284 15182 -284 15182 -284 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15183 -283 15183 -283 15183 -283 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15184 -282 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -278 15186 -278 15186 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -277 15187 -277 15187 -277 15187 -277 15187 -277 15188 -277 15188 -277 15188 -277 15188 -277 15188 -277 15188 -276 15188 -276 15188 -276 15188 -276 15188 -276 15189 -276 15189 -276 15189 -276 15189 -276 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15190 -275 15190 -275 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15191 -274 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -271 15192 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -270 15193 -270 15193 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -269 15194 -269 15194 -269 15194 -269 15195 -269 15195 -269 15195 -269 15195 -269 15195 -269 15195 -268 15195 -268 15195 -268 15195 -268 15195 -268 15196 -268 15196 -268 15196 -268 15196 -268 15196 -268 15196 -267 15196 -267 15196 -267 15196 -267 15197 -267 15197 -267 15197 -267 15197 -267 15197 -267 15197 -266 15197 -266 15197 -266 15197 -266 15198 -266 15198 -266 15198 -266 15198 -266 15198 -266 15198 -265 15198 -265 15198 -265 15198 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -264 15199 -264 15199 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -263 15200 -263 15200 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -262 15201 -262 15201 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -261 15202 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -260 15203 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -259 15204 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -258 15205 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -257 15206 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -256 15207 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15216 -249 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15217 -248 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15219 -246 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15220 -245 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15221 -244 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15222 -243 15222 -243 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15223 -242 15223 -242 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15224 -241 15224 -241 15224 -240 15224 -240 15224 -240 15224 -240 15224 -240 15224 -240 15224 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -238 15225 -238 15225 -238 15226 -238 15226 -238 15226 -238 15226 -237 15226 -237 15226 -237 15226 -237 15226 -237 15226 -237 15227 -236 15227 -236 15227 -236 15227 -236 15227 -236 15227 -236 15227 -235 15227 -235 15227 -235 15228 -235 15228 -235 15228 -235 15228 -234 15228 -234 15228 -234 15228 -234 15228 -234 15228 -234 15229 -234 15229 -233 15229 -233 15229 -233 15229 -233 15229 -233 15229 -233 15229 -232 15229 -232 15229 -232 15230 -232 15230 -232 15230 -232 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -228 15232 -228 15232 -228 15232 -228 15233 -228 15233 -228 15233 -227 15233 -227 15233 -227 15233 -227 15233 -227 15233 -227 15233 -226 15234 -226 15234 -226 15234 -226 15234 -226 15234 -226 15234 -225 15234 -225 15234 -225 15234 -225 15235 -225 15235 -225 15235 -225 15235 -224 15235 -224 15235 -224 15235 -224 15235 -224 15236 -224 15236 -223 15236 -223 15236 -223 15236 -223 15236 -223 15236 -223 15236 -222 15236 -222 15237 -222 15237 -222 15237 -222 15237 -222 15237 -221 15237 -221 15237 -221 15237 -221 15237 -221 15238 -221 15238 -221 15238 -220 15238 -220 15238 -220 15238 -220 15238 -220 15238 -220 15239 -219 15239 -219 15239 -219 15239 -219 15239 -219 15239 -219 15239 -218 15239 -218 15239 -218 15240 -218 15240 -218 15240 -218 15240 -217 15240 -217 15240 -217 15240 -217 15240 -217 15240 -217 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -215 15241 -215 15242 -215 15242 -215 15242 -215 15242 -215 15242 -214 15242 -214 15242 -214 15242 -214 15243 -214 15243 -214 15243 -213 15243 -213 15243 -213 15243 -213 15243 -213 15243 -213 15243 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -211 15244 -211 15244 -211 15245 -211 15245 -211 15245 -211 15245 -210 15245 -210 15245 -210 15245 -210 15245 -210 15245 -210 15246 -209 15246 -209 15246 -209 15246 -209 15246 -209 15246 -209 15246 -208 15246 -208 15247 -208 15247 -208 15247 -208 15247 -207 15247 -207 15247 -207 15247 -207 15247 -207 15247 -207 15248 -206 15248 -206 15248 -206 15248 -206 15248 -206 15248 -206 15248 -205 15248 -205 15248 -205 15249 -205 15249 -205 15249 -204 15249 -204 15249 -204 15249 -204 15249 -204 15249 -204 15250 -203 15250 -203 15250 -203 15250 -203 15250 -203 15250 -202 15250 -202 15250 -202 15250 -202 15251 -202 15251 -202 15251 -201 15251 -201 15251 -201 15251 -201 15251 -201 15251 -200 15251 -200 15252 -200 15252 -200 15252 -200 15252 -200 15252 -199 15252 -199 15252 -199 15252 -199 15252 -199 15253 -199 15253 -198 15253 -198 15253 -198 15253 -198 15253 -198 15253 -197 15253 -197 15254 -197 15254 -197 15254 -197 15254 -197 15254 -196 15254 -196 15254 -196 15254 -196 15254 -196 15255 -195 15255 -195 15255 -195 15255 -195 15255 -195 15255 -195 15255 -194 15255 -194 15255 -194 15256 -194 15256 -194 15256 -193 15256 -193 15256 -193 15256 -193 15256 -193 15256 -193 15256 -192 15257 -192 15257 -192 15257 -192 15257 -192 15257 -191 15257 -191 15257 -191 15257 -191 15258 -191 15258 -191 15258 -190 15258 -190 15258 -190 15258 -190 15258 -190 15258 -190 15258 -189 15259 -189 15259 -189 15259 -189 15259 -189 15259 -188 15259 -188 15259 -188 15259 -188 15259 -188 15260 -188 15260 -187 15260 -187 15260 -187 15260 -187 15260 -187 15260 -186 15260 -186 15261 -186 15261 -186 15261 -186 15261 -186 15261 -185 15261 -185 15261 -185 15261 -185 15261 -185 15261 -184 15262 -184 15262 -184 15262 -184 15262 -184 15262 -184 15262 -183 15262 -183 15262 -183 15262 -183 15262 -183 15262 -183 15262 -182 15262 -182 15263 -182 15263 -182 15263 -182 15263 -181 15263 -181 15263 -181 15263 -181 15263 -181 15263 -181 15263 -180 15263 -180 15263 -180 15263 -180 15264 -180 15264 -179 15264 -179 15264 -179 15264 -179 15264 -179 15264 -179 15264 -178 15264 -178 15264 -178 15264 -178 15264 -178 15264 -177 15264 -177 15265 -177 15265 -177 15265 -177 15265 -177 15265 -176 15265 -176 15265 -176 15265 -176 15265 -176 15265 -176 15265 -175 15265 -175 15265 -175 15266 -175 15266 -175 15266 -174 15266 -174 15266 -174 15266 -174 15266 -174 15266 -174 15266 -173 15266 -173 15266 -173 15266 -173 15266 -173 15267 -172 15267 -172 15267 -172 15267 -172 15267 -172 15267 -172 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -169 15268 -169 15268 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -168 15269 -168 15269 -168 15269 -168 15269 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15271 -167 15271 -167 15271 -167 15271 -167 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15272 -166 15272 -166 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -163 15273 -163 15273 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -162 15274 -162 15274 -162 15274 -162 15274 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15276 -161 15276 -161 15276 -161 15276 -161 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15277 -160 15277 -160 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -157 15278 -157 15278 -157 15278 -157 15278 -157 15278 -157 15279 -157 15279 -157 15279 -157 15279 -157 15279 -157 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15280 -156 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -154 15280 -154 15280 -154 15280 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15282 -153 15282 -153 15282 -153 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -150 15283 -150 15283 -150 15283 -150 15283 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -148 15288 -148 15288 -147 15288 -147 15288 -147 15288 -146 15288 -146 15288 -146 15288 -145 15288 -145 15288 -145 15288 -144 15288 -144 15288 -144 15288 -143 15288 -143 15289 -142 15289 -142 15289 -142 15289 -141 15289 -141 15289 -141 15289 -140 15289 -140 15289 -140 15289 -139 15289 -139 15289 -139 15289 -138 15290 -138 15290 -137 15290 -137 15290 -137 15290 -136 15290 -136 15290 -136 15290 -135 15290 -135 15290 -135 15290 -134 15290 -134 15291 -134 15291 -133 15291 -133 15291 -133 15291 -132 15291 -132 15291 -131 15291 -131 15291 -131 15291 -130 15291 -130 15292 -130 15292 -129 15292 -129 15292 -129 15292 -128 15292 -128 15292 -128 15292 -127 15292 -127 15292 -126 15292 -126 15292 -126 15293 -125 15293 -125 15293 -125 15293 -124 15293 -124 15293 -124 15293 -123 15293 -123 15293 -123 15293 -122 15293 -122 15293 -121 15294 -121 15294 -121 15294 -120 15294 -120 15294 -120 15294 -119 15294 -119 15294 -119 15294 -118 15294 -118 15294 -118 15294 -117 15295 -117 15295 -117 15295 -117 15295 -117 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15296 -116 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -114 15296 -114 15296 -114 15296 -114 15297 -114 15297 -114 15297 -114 15297 -114 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -111 15298 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -110 15299 -110 15299 -110 15300 -110 15300 -110 15300 -110 15300 -110 15300 -110 15300 -109 15300 -109 15300 -109 15300 -109 15301 -109 15301 -109 15301 -109 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15302 -108 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15303 -106 15303 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -105 15302 -105 15302 -105 15301 -105 15301 -105 15301 -105 15301 -105 15301 -104 15301 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -108 15303 -108 15303 -108 15303 -108 15303 -108 15303 -109 15303 -109 15303 -109 15303 -109 15303 -109 15303 -109 15303 -110 15303 -110 15303 -110 15303 -110 15303 -110 15303 -111 15303 -111 15303 -111 15303 -111 15303 -111 15303 -111 15303 -112 15303 -112 15303 -112 15303 -112 15303 -112 15303 -113 15303 -113 15303 -113 15303 -113 15303 -113 15303 -113 15303 -114 15303 -114 15303 -114 15303 -114 15303 -114 15303 -114 15303 -115 15303 -115 15303 -115 15303 -115 15303 -115 15303 -116 15303 -116 15303 -116 15303 -116 15303 -116 15303 -116 15303 -117 15303 -117 15303 -117 15303 -117 15303 -117 15303 -118 15303 -118 15302 -118 15302 -118 15302 -118 15302 -119 15302 -119 15302 -119 15302 -119 15302 -119 15302 -120 15302 -120 15302 -120 15302 -120 15302 -121 15302 -121 15302 -121 15302 -121 15302 -121 15302 -122 15302 -122 15302 -122 15302 -122 15302 -123 15302 -123 15302 -123 15302 -123 15302 -123 15302 -124 15302 -124 15302 -124 15302 -124 15302 -125 15301 -125 15301 -125 15301 -125 15301 -125 15301 -126 15301 -126 15301 -126 15301 -126 15301 -127 15301 -127 15301 -127 15301 -127 15301 -127 15301 -128 15301 -128 15301 -128 15301 -128 15301 -129 15301 -129 15301 -129 15301 -129 15301 -129 15301 -130 15301 -130 15301 -130 15301 -130 15301 -131 15301 -131 15301 -131 15301 -131 15300 -132 15300 -132 15300 -132 15300 -132 15300 -132 15300 -133 15300 -133 15300 -133 15300 -133 15300 -134 15300 -134 15300 -134 15300 -134 15300 -134 15300 -135 15300 -135 15300 -135 15300 -135 15300 -136 15300 -136 15300 -136 15300 -136 15300 -136 15300 -137 15300 -137 15300 -137 15300 -137 15300 -138 15300 -138 15300 -138 15300 -138 15299 -138 15299 -139 15299 -139 15299 -139 15299 -139 15299 -140 15299 -140 15299 -140 15299 -140 15299 -140 15299 -141 15299 -141 15299 -141 15298 -141 15298 -142 15298 -142 15298 -142 15298 -142 15298 -142 15298 -143 15298 -143 15298 -143 15298 -143 15298 -144 15297 -144 15297 -144 15297 -144 15297 -144 15297 -145 15297 -145 15297 -145 15297 -145 15297 -146 15297 -146 15297 -146 15296 -146 15296 -147 15296 -147 15296 -147 15296 -147 15296 -147 15296 -148 15296 -148 15296 -148 15296 -148 15296 -149 15295 -149 15295 -149 15295 -149 15295 -149 15295 -150 15295 -150 15295 -150 15295 -150 15295 -150 15295 -151 15295 -151 15295 -151 15294 -151 15294 -151 15294 -151 15294 -152 15294 -152 15294 -152 15294 -152 15294 -152 15294 -152 15294 -153 15294 -153 15293 -153 15293 -153 15293 -153 15293 -154 15293 -154 15293 -154 15293 -154 15293 -154 15293 -154 15293 -155 15293 -155 15292 -155 15292 -155 15292 -155 15292 -155 15292 -156 15292 -156 15292 -156 15292 -156 15292 -156 15292 -157 15292 -157 15291 -157 15291 -157 15291 -157 15291 -157 15291 -158 15291 -158 15291 -158 15291 -158 15291 -158 15291 -158 15291 -159 15290 -159 15290 -159 15290 -159 15290 -159 15290 -159 15290 -160 15290 -160 15290 -160 15290 -160 15290 -160 15290 -161 15290 -161 15289 -161 15289 -161 15289 -161 15289 -161 15289 -162 15289 -162 15289 -162 15289 -162 15289 -162 15289 -162 15289 -163 15288 -163 15288 -163 15288 -163 15288 -163 15288 -164 15288 -164 15288 -164 15288 -164 15288 -164 15288 -164 15288 -165 15287 -165 15287 -165 15287 -165 15287 -165 15287 -165 15287 -166 15287 -166 15287 -166 15287 -166 15287 -166 15287 -167 15286 -167 15286 -167 15286 -167 15286 -167 15286 -167 15286 -168 15286 -168 15286 -168 15286 -168 15286 -168 15286 -168 15285 -169 15285 -169 15285 -169 15285 -169 15285 -169 15285 -170 15285 -170 15285 -170 15285 -170 15285 -170 15285 -170 15285 -171 15284 -171 15284 -171 15284 -171 15284 -171 15284 -171 15284 -172 15284 -172 15284 -172 15284 -172 15284 -172 15284 -173 15283 -173 15283 -173 15283 -173 15283 -173 15283 -173 15283 -174 15283 -174 15283 -174 15283 -174 15283 -174 15283 -174 15283 -175 15282 -175 15282 -175 15282 -175 15282 -175 15282 -176 15282 -176 15282 -176 15282 -176 15282 -176 15282 -176 15282 -177 15282 -177 15281 -177 15281 -177 15281 -177 15281 -177 15281 -178 15281 -178 15281 -178 15281 -178 15281 -178 15281 -179 15281 -179 15281 -179 15280 -179 15280 -179 15280 -179 15280 -180 15280 -180 15280 -180 15280 -180 15280 -180 15280 -180 15280 -181 15280 -181 15279 -181 15279 -181 15279 -181 15279 -182 15279 -182 15279 -182 15279 -182 15279 -182 15279 -182 15279 -183 15279 -183 15279 -183 15278 -183 15278 -183 15278 -183 15278 -184 15278 -184 15278 -184 15278 -184 15278 -184 15278 -184 15278 -185 15278 -185 15278 -185 15277 -185 15277 -185 15277 -186 15277 -186 15277 -186 15277 -186 15277 -186 15277 -186 15277 -187 15277 -187 15277 -187 15276 -187 15276 -187 15276 -187 15276 -188 15276 -188 15276 -188 15276 -188 15276 -188 15276 -189 15276 -189 15276 -189 15276 -189 15275 -189 15275 -189 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -191 15275 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -192 15274 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15270 -194 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15269 -195 15269 -195 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15268 -196 15268 -196 15268 -196 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15267 -197 15267 -197 15267 -197 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15266 -198 15266 -198 15266 -198 15266 -198 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15265 -199 15265 -199 15265 -199 15265 -199 15265 -199 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -201 15264 -201 15264 -201 15264 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15261 -202 15261 -202 15261 -202 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -204 15260 -204 15260 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15257 -205 15257 -205 15257 -205 15257 -205 15257 -206 15257 -206 15257 -206 15257 -206 15257 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -207 15256 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15254 -207 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15253 -208 15253 -208 15253 -208 15253 -208 15253 -209 15253 -209 15253 -209 15253 -209 15253 -209 15252 -209 15252 -209 15252 -209 15252 -209 15252 -209 15252 -210 15252 -210 15252 -210 15252 -210 15251 -210 15251 -210 15251 -210 15251 -210 15251 -210 15251 -211 15251 -211 15251 -211 15251 -211 15250 -211 15250 -211 15250 -211 15250 -211 15250 -211 15250 -212 15250 -212 15250 -212 15250 -212 15250 -212 15249 -212 15249 -212 15249 -212 15249 -212 15249 -213 15249 -213 15249 -213 15249 -213 15249 -213 15248 -213 15248 -213 15248 -213 15248 -213 15248 -214 15248 -214 15248 -214 15248 -214 15248 -214 15247 -214 15247 -214 15247 -214 15247 -214 15247 -215 15247 -215 15247 -215 15247 -215 15247 -215 15246 -215 15246 -215 15246 -215 15246 -215 15246 -216 15246 -216 15246 -216 15246 -216 15246 -216 15245 -216 15245 -216 15245 -216 15245 -216 15245 -217 15245 -217 15245 -217 15245 -217 15245 -217 15245 -217 15244 -217 15244 -217 15244 -217 15244 -217 15244 -218 15244 -218 15244 -218 15244 -218 15244 -218 15243 -218 15243 -218 15243 -218 15243 -218 15243 -219 15243 -219 15243 -219 15243 -219 15243 -219 15242 -219 15242 -219 15242 -219 15242 -219 15242 -220 15242 -220 15242 -220 15242 -220 15242 -220 15241 -220 15241 -220 15241 -220 15241 -220 15241 -221 15241 -221 15241 -221 15241 -221 15241 -221 15240 -221 15240 -221 15240 -221 15240 -221 15240 -222 15240 -222 15240 -222 15240 -222 15240 -222 15240 -222 15239 -222 15239 -222 15239 -222 15239 -223 15239 -223 15239 -223 15239 -223 15239 -223 15239 -223 15238 -223 15238 -223 15238 -223 15238 -224 15238 -224 15238 -224 15238 -224 15238 -224 15238 -224 15237 -224 15237 -224 15237 -224 15237 -225 15237 -225 15237 -225 15237 -225 15237 -225 15237 -225 15236 -225 15236 -225 15236 -225 15236 -225 15236 -226 15236 -226 15236 -226 15236 -226 15236 -226 15235 -226 15235 -226 15235 -226 15235 -226 15235 -227 15235 -227 15235 -227 15235 -227 15235 -227 15235 -227 15234 -227 15234 -227 15234 -227 15234 -228 15234 -228 15234 -228 15234 -228 15234 -228 15234 -228 15233 -228 15233 -228 15233 -228 15233 -228 15233 -229 15233 -229 15233 -229 15233 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -230 15232 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15230 -230 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15229 -231 15229 -231 15229 -231 15229 -232 15229 -232 15229 -232 15229 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15225 -234 15225 -234 15225 -235 15225 -235 15225 -235 15225 -235 15225 -235 15224 -235 15224 -235 15224 -235 15224 -235 15224 -235 15224 -236 15224 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15221 -237 15221 -237 15221 -237 15221 -238 15221 -238 15221 -238 15221 -238 15220 -238 15220 -238 15220 -238 15220 -238 15220 -238 15220 -239 15220 -239 15220 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15217 -240 15217 -240 15217 -241 15217 -241 15217 -241 15217 -241 15217 -241 15216 -241 15216 -241 15216 -241 15216 -241 15216 -241 15216 -242 15216 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15213 -243 15213 -244 15213 -244 15213 -244 15213 -244 15213 -244 15213 -244 15212 -244 15212 -244 15212 -244 15212 -244 15212 -245 15212 -245 15212 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15209 -246 15209 -246 15209 -247 15209 -247 15209 -247 15209 -247 15209 -247 15208 -247 15208 -247 15208 -247 15208 -247 15208 -248 15208 -248 15208 -248 15208 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15203 -251 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -257 15199 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -258 15198 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -259 15197 -259 15197 -259 15196 -259 15196 -259 15196 -259 15196 -259 15196 -260 15196 -260 15196 -260 15196 -260 15195 -260 15195 -260 15195 -260 15195 -260 15195 -261 15195 -261 15195 -261 15195 -261 15195 -261 15194 -261 15194 -261 15194 -261 15194 -262 15194 -262 15194 -262 15194 -262 15194 -262 15193 -262 15193 -262 15193 -262 15193 -263 15193 -263 15193 -263 15193 -263 15193 -263 15193 -263 15192 -263 15192 -263 15192 -264 15192 -264 15192 -264 15192 -264 15192 -264 15192 -264 15191 -264 15191 -264 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15190 -265 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -270 15187 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -271 15186 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -272 15185 -272 15185 -272 15184 -272 15184 -272 15184 -272 15184 -272 15184 -272 15184 -273 15184 -273 15184 -273 15183 -273 15183 -273 15183 -273 15183 -273 15183 -274 15183 -274 15183 -274 15183 -274 15183 -274 15182 -274 15182 -274 15182 -274 15182 -275 15182 -275 15182 -275 15182 -275 15182 -275 15181 -275 15181 -275 15181 -275 15181 -276 15181 -276 15181 -276 15181 -276 15181 -276 15181 -276 15180 -276 15180 -276 15180 -277 15180 -277 15180 -277 15180 -277 15180 -277 15180 -277 15179 -277 15179 -277 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15178 -278 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15177 -279 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -282 15175 -282 15175 -282 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15174 -283 15174 -283 15174 -283 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15173 -284 15173 -284 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15172 -285 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -291 15168 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -292 15167 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -293 15166 -293 15166 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -294 15165 -294 15165 -294 15165 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -295 15164 -295 15164 -295 15164 -295 15164 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -296 15163 -296 15163 -296 15163 -296 15163 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -297 15162 -297 15162 -297 15162 -297 15162 -297 15162 -297 15161 -297 15161 -297 15161 -297 15161 -297 15161 -297 15161 -298 15161 -298 15161 -298 15161 -298 15161 -298 15161 -298 15160 -298 15160 -298 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15159 -299 15159 -299 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -303 15157 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -304 15156 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -305 15155 -305 15155 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -306 15154 -306 15154 -306 15154 -306 15153 -306 15153 -306 15153 -306 15153 -306 15153 -307 15153 -307 15153 -307 15153 -307 15153 -307 15152 -307 15152 -307 15152 -307 15152 -307 15152 -308 15152 -308 15152 -308 15152 -308 15152 -308 15152 -308 15151 -308 15151 -308 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15150 -309 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -313 15148 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -314 15147 -314 15147 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -315 15146 -315 15146 -315 15145 -315 15145 -315 15145 -315 15145 -315 15145 -315 15145 -316 15145 -316 15145 -316 15145 -316 15145 -316 15144 -316 15144 -316 15144 -316 15144 -316 15144 -317 15144 -317 15144 -317 15144 -317 15144 -317 15143 -317 15143 -317 15143 -317 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15142 -318 15142 -318 15142 -319 15142 -319 15142 -319 15142 -319 15142 -319 15142 -319 15141 -319 15141 -319 15141 -320 15141 -320 15141 -320 15141 -320 15141 -320 15141 -320 15140 -320 15140 -320 15140 -320 15140 -321 15140 -321 15140 -321 15140 -321 15140 -321 15139 -321 15139 -321 15139 -321 15139 -322 15139 -322 15139 -322 15139 -322 15139 -322 15138 -322 15138 -322 15138 -322 15138 -322 15138 -323 15138 -323 15138 -323 15137 -323 15137 -323 15137 -323 15137 -323 15137 -323 15137 -324 15137 -324 15137 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -325 15136 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -326 15135 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15128 -331 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15127 -332 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15126 -333 15126 -333 15126 -333 15126 -334 15126 -334 15126 -334 15126 -334 15126 -334 15125 -334 15125 -334 15125 -334 15125 -335 15125 -335 15125 -335 15125 -335 15125 -335 15124 -335 15124 -335 15124 -335 15124 -335 15124 -336 15124 -336 15124 -336 15124 -336 15123 -336 15123 -336 15123 -336 15123 -336 15123 -337 15123 -337 15123 -337 15123 -337 15122 -337 15122 -337 15122 -337 15122 -337 15122 -337 15122 -338 15122 -338 15122 -338 15121 -338 15121 -338 15121 -338 15121 -338 15121 -338 15121 -339 15121 -339 15121 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -340 15120 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -341 15119 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -343 15117 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -344 15116 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -345 15115 -345 15115 -345 15114 -345 15114 -345 15114 -345 15114 -345 15114 -345 15114 -346 15114 -346 15114 -346 15113 -346 15113 -346 15113 -346 15113 -346 15113 -346 15113 -347 15113 -347 15113 -347 15112 -347 15112 -347 15112 -347 15112 -347 15112 -348 15112 -348 15112 -348 15112 -348 15111 -348 15111 -348 15111 -348 15111 -348 15111 -349 15111 -349 15111 -349 15111 -349 15110 -349 15110 -349 15110 -349 15110 -350 15110 -350 15110 -350 15110 -350 15110 -350 15109 -350 15109 -350 15109 -350 15109 -351 15109 -351 15109 -351 15109 -351 15109 -351 15108 -351 15108 -351 15108 -352 15108 -352 15108 -352 15108 -352 15108 -352 15108 -352 15107 -352 15107 -352 15107 -353 15107 -353 15107 -353 15107 -353 15107 -353 15107 -353 15106 -353 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15105 -354 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15104 -355 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -361 15100 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -362 15099 -362 15098 -362 15098 -362 15098 -362 15098 -362 15098 -362 15098 -363 15098 -363 15098 -363 15097 -363 15097 -363 15097 -363 15097 -363 15097 -363 15097 -364 15097 -364 15097 -364 15096 -364 15096 -364 15096 -364 15096 -364 15096 -365 15096 -365 15096 -365 15096 -365 15095 -365 15095 -365 15095 -365 15095 -365 15095 -366 15095 -366 15095 -366 15095 -366 15094 -366 15094 -366 15094 -366 15094 -366 15094 -367 15094 -367 15094 -367 15094 -367 15093 -367 15093 -367 15093 -367 15093 -368 15093 -368 15093 -368 15093 -368 15093 -368 15092 -368 15092 -368 15092 -368 15092 -369 15092 -369 15092 -369 15092 -369 15092 -369 15091 -369 15091 -369 15091 -370 15091 -370 15091 -370 15091 -370 15091 -370 15091 -370 15090 -370 15090 -370 15090 -371 15090 -371 15090 -371 15090 -371 15090 -371 15090 -371 15089 -371 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15088 -372 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -379 15083 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -380 15082 -380 15081 -380 15081 -380 15081 -380 15081 -380 15081 -380 15081 -381 15081 -381 15081 -381 15080 -381 15080 -381 15080 -381 15080 -381 15080 -382 15080 -382 15080 -382 15080 -382 15079 -382 15079 -382 15079 -383 15079 -383 15079 -383 15079 -383 15079 -383 15079 -383 15078 -383 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15077 -385 15077 -385 15077 -385 15077 -385 15077 -385 15077 -385 15077 -386 15077 -386 15076 -386 15076 -386 15076 -386 15076 -386 15076 -386 15076 -387 15076 -387 15075 -387 15075 -387 15075 -387 15075 -387 15075 -388 15075 -388 15075 -388 15074 -388 15074 -388 15074 -388 15074 -388 15074 -389 15074 -389 15074 -389 15073 -389 15073 -389 15073 -389 15073 -389 15073 -390 15073 -390 15073 -390 15072 -390 15072 -390 15072 -390 15072 -391 15072 -391 15072 -391 15071 -391 15071 -391 15071 -391 15071 -391 15071 -392 15071 -392 15071 -392 15070 -392 15070 -392 15070 -392 15070 -393 15070 -393 15070 -393 15070 -393 15069 -393 15069 -393 15069 -393 15069 -394 15069 -394 15069 -394 15069 -394 15068 -394 15068 -394 15068 -394 15068 -395 15068 -395 15068 -395 15068 -395 15067 -395 15067 -395 15067 -396 15067 -396 15067 -396 15067 -396 15066 -396 15066 -396 15066 -396 15066 -397 15066 -397 15066 -397 15066 -397 15065 -397 15065 -397 15065 -398 15065 -398 15065 -398 15065 -398 15065 -398 15064 -398 15064 -398 15064 -399 15064 -399 15064 -399 15064 -399 15064 -399 15063 -399 15063 -399 15063 -400 15063 -400 15063 -400 15063 -400 15062 -400 15062 -400 15062 -401 15062 -401 15062 -401 15062 -401 15062 -401 15061 -401 15061 -401 15061 -402 15061 -402 15061 -402 15061 -402 15061 -402 15060 -402 15060 -403 15060 -403 15060 -403 15060 -403 15060 -403 15060 -403 15059 -403 15059 -404 15059 -404 15059 -404 15059 -404 15059 -404 15059 -404 15058 -404 15058 -405 15058 -405 15058 -405 15058 -405 15058 -405 15057 -405 15057 -406 15057 -406 15057 -406 15057 -406 15057 -406 15057 -406 15056 -406 15056 -407 15056 -407 15056 -407 15056 -407 15056 -407 15056 -407 15055 -407 15055 -408 15055 -408 15055 -408 15055 -408 15055 -408 15055 -408 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15053 -409 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -414 15048 -415 15048 -415 15048 -415 15048 -415 15048 -415 15048 -415 15048 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -418 15045 -418 15045 -418 15045 -418 15045 -418 15045 -418 15045 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -420 15043 -420 15043 -420 15043 -420 15043 -420 15043 -420 15043 -421 15043 -421 15042 -421 15042 -421 15042 -421 15042 -421 15042 -421 15042 -422 15042 -422 15041 -422 15041 -422 15041 -422 15041 -422 15041 -422 15041 -423 15040 -423 15040 -423 15040 -423 15040 -423 15040 -423 15040 -424 15040 -424 15039 -424 15039 -424 15039 -424 15039 -424 15039 -424 15039 -425 15039 -425 15038 -425 15038 -425 15038 -425 15038 -425 15038 -426 15038 -426 15037 -426 15037 -426 15037 -426 15037 -426 15037 -426 15037 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -428 15035 -428 15035 -428 15035 -428 15035 -428 15035 -428 15035 -428 15034 -429 15034 -429 15034 -429 15034 -429 15034 -429 15034 -429 15033 -429 15033 -430 15033 -430 15033 -430 15033 -430 15033 -430 15033 -430 15032 -430 15032 -430 15032 -431 15032 -431 15032 -431 15032 -431 15031 -431 15031 -431 15031 -431 15031 -431 15031 -432 15031 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -433 15029 -433 15029 -433 15029 -433 15029 -433 15029 -433 15029 -433 15028 -434 15028 -434 15028 -434 15028 -434 15028 -434 15028 -434 15027 -434 15027 -434 15027 -435 15027 -435 15027 -435 15027 -435 15027 -435 15026 -435 15026 -435 15026 -435 15026 -436 15026 -436 15026 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15022 -438 15022 -439 15022 -439 15022 -439 15022 -439 15022 -439 15021 -439 15021 -439 15021 -439 15021 -440 15021 -440 15021 -440 15021 -440 15020 -440 15020 -440 15020 -440 15020 -440 15020 -441 15020 -441 15019 -441 15019 -441 15019 -441 15019 -441 15019 -441 15019 -441 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15017 -443 15017 -443 15017 -443 15017 -443 15017 -443 15017 -443 15016 -443 15016 -443 15016 -444 15016 -444 15016 -444 15016 -444 15015 -444 15015 -444 15015 -444 15015 -444 15015 -445 15015 -445 15015 -445 15014 -445 15014 -445 15014 -445 15014 -445 15014 -445 15014 -446 15013 -446 15013 -446 15013 -446 15013 -446 15013 -446 15013 -446 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15011 -447 15011 -448 15011 -448 15011 -448 15011 -448 15011 -448 15010 -448 15010 -448 15010 -448 15010 -449 15010 -449 15010 -449 15009 -449 15009 -449 15009 -449 15009 -449 15009 -449 15009 -450 15009 -450 15008 -450 15008 -450 15008 -450 15008 -450 15008 -450 15008 -451 15007 -451 15007 -451 15007 -451 15007 -451 15007 -451 15007 -451 15006 -451 15006 -452 15006 -452 15006 -452 15006 -453 15003 -454 15003 -455 15003 -455 15002 -455 15002 -455 15002 -455 15002 -455 15002 -455 15002 -456 15001 -456 15001 -456 15001 -456 15001 -456 15001 -456 15000 -457 15000 -457 15000 -457 15000 -457 15000 -457 14999 -457 14999 -457 14999 -458 14999 -458 14999 -458 14999 -458 14998 -458 14998 -458 14998 -459 14998 -459 14998 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -460 14997 -460 14996 -460 14996 -460 14996 -460 14996 -460 14996 -461 14995 -461 14995 -461 14995 -461 14995 -461 14995 -461 14995 -461 14994 -462 14994 -462 14994 -462 14994 -462 14994 -462 14993 -462 14993 -463 14993 -463 14993 -463 14993 -463 14992 -463 14992 -463 14992 -463 14992 -464 14992 -464 14992 -464 14991 -464 14991 -464 14991 -464 14991 -464 14991 -465 14990 -465 14990 -465 14990 -465 14990 -465 14990 -465 14990 -466 14989 -466 14989 -466 14989 -466 14989 -466 14989 -466 14988 -466 14988 -467 14988 -467 14988 -467 14988 -467 14988 -467 14987 -468 14987 -468 14987 -468 14987 -468 14987 -468 14986 -469 14986 -469 14986 -469 14986 -469 14986 -469 14985 -470 14985 -470 14985 -470 14985 -470 14985 -471 14985 -471 14984 -471 14984 -471 14984 -471 14984 -472 14984 -472 14983 -472 14983 -472 14983 -472 14983 -473 14983 -473 14983 -473 14982 -473 14982 -474 14982 -474 14982 -474 14982 -474 14981 -474 14981 -475 14981 -475 14981 -475 14981 -475 14980 -475 14980 -476 14980 -476 14980 -476 14980 -476 14980 -477 14979 -477 14979 -477 14979 -477 14979 -477 14979 -478 14978 -478 14978 -478 14978 -478 14978 -478 14978 -479 14978 -479 14977 -479 14977 -479 14977 -479 14977 -480 14977 -480 14976 -480 14976 -480 14976 -481 14976 -481 14976 -481 14976 -481 14975 -481 14975 -482 14975 -482 14975 -482 14975 -482 14974 -482 14974 -483 14974 -483 14974 -483 14974 -483 14973 -484 14973 -484 14973 -484 14973 -484 14973 -484 14973 -485 14972 -485 14972 -485 14972 -485 14972 -485 14972 -486 14971 -486 14971 -486 14971 -486 14971 -487 14971 -487 14971 -487 14970 -487 14970 -487 14970 -488 14970 -488 14970 -488 14969 -488 14969 -488 14969 -489 14969 -489 14969 -489 14968 -489 14968 -490 14968 -490 14968 -490 14968 -490 14968 -490 14967 -491 14967 -491 14967 -491 14967 -491 14967 -491 14966 -492 14966 -492 14966 -492 14966 -492 14966 -492 14966 -493 14965 -493 14965 -493 14965 -493 14965 -494 14965 -494 14964 -494 14964 -494 14964 -494 14964 -495 14964 -495 14964 -495 14963 -495 14963 -495 14963 -496 14963 -496 14962 -496 14962 -496 14962 -497 14961 -497 14961 -497 14961 -497 14961 -497 14960 -498 14960 -498 14960 -498 14959 -498 14959 -498 14959 -499 14959 -499 14958 -499 14958 -499 14958 -500 14957 -500 14957 -500 14957 -500 14956 -500 14956 -501 14956 -501 14956 -501 14955 -501 14955 -501 14955 -502 14954 -502 14954 -502 14954 -502 14953 -503 14953 -503 14953 -504 14953 -504 14952 -504 14952 -505 14952 -505 14951 -505 14951 -506 14951 -506 14951 -506 14950 -507 14950 -507 14950 -507 14949 -508 14949 -508 14949 -508 14948 -509 14948 -509 14948 -509 14948 -510 14947 -510 14947 -510 14947 -511 14946 -511 14946 -511 14946 -512 14945 -512 14945 -513 14945 -513 14945 -513 14944 -514 14944 -514 14944 -514 14943 -515 14943 -515 14943 -515 14943 -516 14942 -516 14942 -516 14942 -517 14941 -517 14941 -517 14941 -518 14940 -518 14940 -518 14940 -519 14940 -519 14939 -519 14939 -520 14939 -520 14938 -520 14938 -521 14938 -521 14938 -522 14937 -522 14937 -522 14937 -523 14936 -523 14936 -523 14936 -524 14935 -524 14935 -524 14935 -525 14935 -525 14934 -525 14934 -526 14934 -526 14933 -526 14933 -527 14933 -527 14932 -527 14932 -528 14932 -528 14932 -528 14931 -529 14931 -529 14931 -529 14930 -530 14930 -530 14930 -531 14930 -531 14929 -531 14929 -532 14929 -532 14928 -532 14928 -533 14928 -533 14927 -533 14927 -534 14927 -534 14927 -534 14926 -535 14926 -535 14926 -535 14925 -536 14925 -536 14925 -536 14924 -537 14924 -537 14924 -537 14924 -538 14923 -538 14923 -538 14923 -539 14922 -539 14922 -540 14922 -540 14922 -540 14921 -541 14921 -541 14921 -541 14920 -542 14920 -542 14920 -542 14919 -543 14919 -543 14919 -543 14919 -544 14918 -544 14918 -544 14918 -545 14917 -545 14917 -545 14917 -546 14917 -546 14916 -546 14916 -547 14916 -547 14915 -547 14915 -548 14915 -548 14914 -549 14914 -549 14914 -549 14914 -550 14913 -550 14913 -550 14913 -551 14912 -551 14912 -551 14912 -552 14911 -552 14911 -552 14911 -553 14911 -553 14910 -553 14910 -554 14910 -554 14909 -554 14909 -555 14909 -555 14909 -555 14908 -556 14908 -556 14908 -556 14907 -557 14907 -557 14907 -558 14906 -558 14906 -558 14906 -559 14906 -559 14905 -559 14905 -560 14905 -560 14904 -560 14904 -561 14904 -561 14903 -561 14903 -562 14903 -562 14903 -562 14902 -563 14902 -563 14902 -563 14901 -564 14901 -564 14901 -564 14901 -565 14900 -565 14900 -565 14900 -566 14899 -566 14899 -567 14899 -567 14898 -567 14898 -568 14898 -568 14898 -568 14897 -569 14897 -569 14897 -569 14896 -570 14896 -570 14896 -570 14896 -571 14895 -571 14895 -571 14895 -572 14894 -572 14894 -572 14894 -573 14893 -573 14893 -573 14893 -574 14893 -574 14892 -574 14892 -575 14892 -575 14891 -576 14891 -576 14891 -576 14890 -577 14890 -577 14890 -577 14890 -578 14889 -578 14889 -578 14889 -579 14888 -579 14888 -579 14888 -580 14888 -580 14887 -580 14887 -581 14887 -581 14886 -581 14886 -582 14886 -582 14885 -582 14885 -583 14885 -583 14885 -583 14884 -584 14884 -584 14884 -585 14883 -585 14883 -585 14883 -586 14883 -586 14882 -586 14882 -587 14882 -587 14881 -587 14881 -588 14881 -588 14880 -588 14880 -589 14880 -589 14880 -589 14879 -590 14879 -590 14879 -590 14878 -591 14878 -591 14878 -591 14877 -592 14877 -592 14877 -592 14876 -593 14876 -593 14876 -594 14875 -594 14875 -594 14875 -595 14874 -595 14874 -595 14874 -596 14873 -596 14873 -596 14873 -597 14872 -597 14872 -597 14871 -598 14871 -598 14871 -598 14870 -599 14870 -599 14870 -599 14869 -600 14869 -600 14869 -600 14868 -601 14868 -601 14868 -601 14867 -602 14867 -602 14866 -603 14866 -603 14866 -603 14865 -604 14865 -604 14865 -604 14864 -605 14864 -605 14864 -606 14863 -606 14863 -606 14863 -607 14862 -607 14862 -607 14861 -608 14861 -608 14861 -609 14860 -609 14860 -609 14860 -610 14859 -610 14859 -610 14859 -611 14858 -611 14858 -611 14858 -612 14857 -612 14857 -613 14856 -613 14856 -613 14856 -614 14855 -614 14855 -614 14855 -615 14854 -615 14854 -616 14854 -616 14853 -616 14853 -617 14853 -617 14852 -617 14852 -618 14851 -618 14851 -619 14851 -619 14850 -619 14850 -620 14850 -620 14849 -620 14849 -621 14849 -621 14848 -622 14848 -622 14848 -622 14847 -623 14847 -623 14846 -623 14846 -624 14846 -624 14845 -625 14845 -625 14845 -625 14844 -626 14844 -626 14844 -626 14843 -627 14843 -627 14843 -627 14842 -628 14842 -628 14841 -629 14841 -629 14841 -629 14840 -630 14840 -630 14840 -630 14839 -631 14839 -631 14839 -632 14838 -632 14838 -632 14838 -633 14837 -633 14837 -633 14836 -634 14836 -634 14836 -635 14835 -635 14835 -635 14835 -636 14834 -636 14834 -636 14834 -637 14833 -637 14833 -638 14833 -638 14832 -638 14832 -639 14831 -639 14831 -639 14831 -640 14830 -640 14830 -641 14830 -641 14829 -641 14829 -642 14829 -642 14828 -642 14828 -643 14827 -643 14827 -643 14827 -644 14826 -644 14826 -645 14826 -645 14825 -645 14825 -646 14825 -646 14824 -646 14824 -647 14824 -647 14823 -648 14823 -648 14822 -648 14822 -649 14822 -649 14821 -649 14821 -650 14821 -650 14820 -651 14820 -651 14820 -651 14819 -652 14819 -652 14819 -652 14818 -653 14818 -653 14817 -654 14817 -654 14817 -654 14816 -655 14816 -655 14816 -655 14815 -656 14815 -656 14815 -656 14814 -657 14814 -657 14814 -658 14813 -658 14813 -658 14812 -659 14812 -659 14812 -659 14811 -660 14811 -660 14811 -661 14810 -661 14810 -661 14810 -662 14809 -662 14809 -662 14809 -663 14808 -663 14808 -664 14807 -664 14807 -664 14807 -665 14806 -665 14806 -665 14806 -666 14805 -666 14805 -667 14805 -667 14804 -667 14804 -668 14804 -668 14803 -668 14803 -669 14802 -669 14802 -670 14802 -670 14801 -670 14801 -671 14801 -671 14800 -671 14800 -672 14800 -672 14799 -672 14799 -673 14799 -673 14798 -674 14798 -674 14797 -675 14797 -675 14797 -675 14796 -676 14796 -676 14795 -677 14794 -677 14794 -678 14793 -678 14793 -679 14792 -679 14792 -679 14791 -680 14791 -680 14790 -681 14789 -681 14789 -682 14788 -682 14788 -683 14787 -683 14787 -684 14786 -684 14786 -684 14785 -685 14784 -685 14784 -686 14783 -686 14783 -687 14782 -687 14782 -688 14781 -688 14781 -688 14780 -689 14779 -689 14779 -690 14778 -690 14778 -691 14777 -691 14777 -692 14776 -692 14776 -693 14775 -693 14774 -693 14774 -694 14773 -694 14773 -695 14772 -695 14772 -696 14771 -696 14771 -697 14770 -697 14769 -697 14769 -698 14768 -698 14768 -699 14767 -699 14767 -700 14766 -700 14766 -701 14765 -701 14764 -702 14764 -702 14763 -702 14763 -703 14762 -703 14762 -704 14761 -704 14761 -705 14760 -705 14759 -706 14759 -706 14758 -706 14758 -707 14757 -707 14757 -708 14756 -708 14756 -709 14755 -709 14754 -710 14754 -710 14753 -711 14753 -711 14752 -711 14752 -712 14751 -712 14751 -713 14750 -713 14749 -714 14749 -714 14748 -715 14748 -715 14747 -715 14747 -716 14746 -716 14746 -717 14745 -717 14744 -718 14744 -718 14743 -719 14743 -719 14742 -720 14742 -720 14741 -720 14741 -721 14740 -721 14739 -722 14739 -722 14738 -723 14738 -723 14737 -724 14737 -724 14736 -724 14736 -725 14735 -725 14734 -726 14734 -726 14733 -727 14733 -727 14732 -728 14732 -728 14731 -729 14731 -729 14730 -729 14729 -730 14729 -730 14728 -731 14728 -731 14727 -732 14727 -732 14726 -733 14726 -733 14725 -733 14724 -734 14724 -734 14723 -735 14723 -735 14722 -736 14722 -736 14721 -737 14721 -737 14720 -738 14719 -738 14719 -738 14718 -739 14718 -739 14717 -740 14717 -740 14716 -741 14716 -741 14715 -742 14714 -742 14714 -742 14713 -743 14713 -743 14712 -744 14712 -744 14711 -745 14711 -745 14710 -746 14709 -746 14709 -747 14708 -747 14708 -747 14707 -748 14707 -748 14706 -749 14706 -749 14705 -750 14704 -750 14704 -751 14703 -751 14703 -751 14702 -752 14702 -752 14701 -753 14701 -753 14700 -754 14699 -754 14699 -755 14698 -755 14698 -756 14697 -756 14697 -756 14696 -757 14696 -757 14695 -758 14694 -758 14694 -759 14693 -759 14693 -760 14692 -760 14692 -760 14691 -761 14691 -761 14690 -762 14689 -762 14689 -763 14688 -763 14688 -764 14687 -764 14687 -765 14686 -765 14686 -765 14685 -766 14684 -767 14684 -767 14683 -768 14683 -768 14682 -769 14682 -770 14681 -770 14681 -771 14680 -771 14679 -772 14679 -773 14678 -773 14678 -774 14677 -774 14677 -775 14676 -776 14676 -776 14675 -777 14674 -777 14674 -778 14673 -779 14673 -779 14672 -780 14672 -780 14671 -781 14671 -782 14670 -782 14669 -783 14669 -783 14668 -784 14668 -785 14667 -785 14667 -786 14666 -786 14666 -787 14665 -788 14664 -788 14664 -789 14663 -789 14663 -790 14662 -791 14662 -791 14661 -792 14661 -792 14660 -793 14659 -794 14659 -794 14658 -795 14658 -795 14657 -796 14657 -797 14656 -797 14656 -798 14655 -798 14654 -799 14654 -800 14653 -800 14653 -801 14652 -801 14652 -802 14651 -803 14651 -803 14650 -804 14649 -804 14649 -805 14648 -806 14648 -806 14647 -807 14647 -807 14646 -808 14646 -809 14645 -809 14644 -810 14644 -810 14643 -811 14643 -812 14642 -812 14642 -813 14641 -813 14641 -814 14640 -815 14639 -815 14639 -816 14638 -816 14638 -817 14637 -818 14637 -818 14636 -819 14636 -819 14635 -820 14634 -821 14634 -821 14633 -822 14633 -822 14632 -823 14632 -824 14631 -824 14631 -825 14630 -825 14629 -826 14629 -827 14628 -827 14628 -828 14627 -828 14627 -829 14626 -830 14626 -830 14625 -831 14624 -831 14624 -832 14623 -833 14623 -833 14622 -834 14622 -834 14621 -835 14621 -836 14620 -836 14619 -837 14619 -837 14618 -838 14618 -839 14617 -839 14617 -840 14616 -840 14615 -841 14615 -842 14614 -842 14614 -843 14613 -843 14612 -844 14612 -845 14611 -845 14611 -846 14610 -846 14609 -847 14609 -848 14608 -848 14608 -849 14607 -849 14606 -850 14606 -851 14605 -851 14605 -852 14604 -852 14603 -853 14603 -854 14602 -854 14602 -855 14601 -855 14600 -856 14600 -857 14599 -857 14599 -858 14598 -858 14597 -859 14597 -860 14596 -860 14596 -861 14595 -861 14594 -862 14594 -863 14593 -863 14593 -864 14592 -864 14591 -865 14591 -866 14590 -866 14589 -867 14589 -867 14588 -868 14588 -869 14587 -869 14586 -870 14586 -870 14585 -871 14585 -872 14584 -872 14583 -873 14583 -873 14582 -874 14582 -875 14581 -875 14580 -876 14580 -876 14579 -877 14579 -878 14578 -878 14577 -879 14577 -879 14576 -880 14576 -881 14575 -881 14574 -882 14574 -882 14573 -883 14573 -884 14572 -884 14571 -885 14571 -885 14570 -886 14570 -887 14569 -887 14568 -888 14568 -888 14567 -889 14567 -890 14566 -890 14565 -891 14565 -891 14564 -892 14564 -893 14563 -893 14562 -894 14562 -895 14561 -895 14561 -896 14560 -896 14559 -897 14559 -898 14558 -898 14557 -899 14557 -900 14556 -900 14556 -901 14555 -902 14554 -902 14554 -903 14553 -904 14553 -904 14552 -905 14551 -906 14551 -906 14550 -907 14550 -908 14549 -908 14548 -909 14548 -910 14547 -910 14547 -911 14546 -912 14545 -912 14545 -913 14544 -913 14544 -914 14543 -915 14542 -915 14542 -916 14541 -917 14541 -917 14540 -918 14539 -919 14539 -919 14538 -920 14538 -921 14537 -921 14536 -922 14536 -923 14535 -923 14535 -924 14534 -925 14533 -925 14533 -926 14532 -927 14532 -927 14531 -928 14530 -929 14530 -929 14529 -930 14528 -930 14528 -931 14527 -932 14527 -932 14526 -933 14525 -934 14525 -934 14524 -935 14524 -936 14523 -936 14522 -937 14522 -938 14521 -938 14521 -939 14520 -940 14519 -940 14519 -941 14518 -942 14518 -942 14517 -943 14516 -944 14516 -944 14515 -945 14515 -946 14514 -946 14513 -947 14513 -947 14512 -948 14512 -949 14511 -949 14510 -950 14510 -951 14509 -951 14509 -952 14508 -953 14507 -953 14507 -954 14506 -955 14506 -955 14505 -956 14504 -957 14504 -957 14503 -958 14503 -959 14502 -959 14501 -960 14501 -961 14500 -961 14500 -962 14499 -963 14498 -963 14498 -964 14497 -964 14496 -965 14496 -966 14495 -966 14495 -967 14494 -968 14493 -968 14493 -969 14492 -970 14492 -970 14491 -971 14490 -972 14490 -972 14489 -973 14489 -974 14488 -974 14487 -975 14487 -976 14486 -976 14486 -977 14485 -978 14484 -978 14484 -979 14483 -980 14483 -980 14482 -981 14481 -981 14481 -982 14480 -983 14480 -983 14479 -984 14478 -985 14478 -985 14477 -986 14477 -987 14476 -987 14475 -988 14475 -989 14474 -989 14474 -990 14473 -991 14472 -991 14472 -992 14471 -993 14471 -993 14470 -994 14469 -995 14469 -995 14468 -996 14467 -997 14467 -997 14466 -998 14466 -998 14465 -999 14464 -1000 14464 -1000 14463 -1001 14463 -1002 14462 -1002 14461 -1003 14461 -1004 14460 -1004 14460 -1005 14459 -1006 14459 -1006 14458 -1007 14458 -1008 14457 -1008 14457 -1009 14456 -1010 14456 -1010 14455 -1011 14455 -1012 14454 -1012 14454 -1013 14453 -1014 14453 -1014 14452 -1014 14453 -1014 14453 -1015 14452 -1015 14451 -1016 14451 -1017 14450 -1017 14450 -1018 14449 -1018 14448 -1019 14448 -1019 14447 -1020 14446 -1021 14446 -1021 14445 -1022 14444 -1022 14444 -1023 14443 -1023 14443 -1024 14442 -1025 14441 -1025 14441 -1026 14440 -1026 14439 -1027 14439 -1027 14438 -1028 14437 -1029 14437 -1029 14436 -1030 14436 -1030 14435 -1031 14434 -1031 14434 -1032 14433 -1033 14432 -1033 14432 -1034 14431 -1034 14430 -1035 14430 -1035 14429 -1036 14429 -1037 14428 -1037 14427 -1038 14427 -1038 14426 -1039 14425 -1039 14425 -1040 14424 -1041 14423 -1041 14423 -1042 14422 -1042 14422 -1043 14421 -1043 14420 -1044 14420 -1045 14419 -1045 14418 -1046 14418 -1046 14417 -1047 14416 -1047 14416 -1048 14415 -1049 14415 -1049 14414 -1050 14413 -1050 14413 -1051 14412 -1051 14411 -1052 14411 -1053 14410 -1053 14409 -1054 14409 -1054 14408 -1055 14408 -1055 14407 -1056 14406 -1056 14406 -1057 14405 -1058 14404 -1058 14404 -1059 14403 -1059 14402 -1060 14402 -1060 14401 -1061 14401 -1062 14400 -1062 14399 -1063 14399 -1063 14398 -1064 14397 -1064 14397 -1065 14396 -1066 14395 -1066 14395 -1067 14394 -1067 14394 -1068 14393 -1069 14392 -1069 14392 -1070 14391 -1071 14390 -1071 14390 -1072 14389 -1073 14388 -1073 14388 -1074 14387 -1075 14387 -1076 14386 -1076 14385 -1077 14385 -1078 14384 -1078 14383 -1079 14383 -1080 14382 -1080 14381 -1081 14381 -1082 14380 -1083 14380 -1083 14379 -1084 14378 -1085 14378 -1085 14377 -1086 14376 -1087 14376 -1087 14375 -1088 14374 -1089 14374 -1090 14373 -1090 14373 -1091 14372 -1092 14371 -1092 14371 -1093 14370 -1094 14369 -1094 14369 -1095 14368 -1096 14367 -1097 14367 -1097 14366 -1098 14366 -1099 14365 -1099 14364 -1100 14364 -1101 14363 -1101 14362 -1102 14362 -1103 14361 -1104 14360 -1104 14360 -1105 14359 -1106 14359 -1106 14358 -1107 14357 -1108 14356 -1108 14356 -1109 14355 -1110 14354 -1111 14353 -1111 14353 -1112 14352 -1113 14351 -1113 14351 -1114 14350 -1115 14349 -1115 14348 -1116 14348 -1117 14347 -1118 14346 -1118 14345 -1119 14345 -1120 14344 -1120 14343 -1121 14342 -1122 14342 -1122 14341 -1123 14340 -1124 14339 -1125 14339 -1125 14338 -1126 14337 -1127 14336 -1127 14336 -1128 14335 -1129 14334 -1129 14334 -1130 14333 -1131 14332 -1132 14331 -1132 14331 -1133 14330 -1134 14329 -1134 14328 -1135 14328 -1136 14327 -1136 14326 -1137 14325 -1138 14325 -1139 14324 -1139 14323 -1140 14322 -1141 14322 -1141 14321 -1142 14320 -1143 14319 -1143 14319 -1144 14318 -1145 14317 -1146 14316 -1146 14316 -1147 14315 -1148 14314 -1148 14314 -1149 14313 -1150 14312 -1150 14311 -1151 14311 -1152 14310 -1153 14309 -1154 14308 -1155 14308 -1156 14307 -1156 14306 -1157 14305 -1158 14305 -1159 14304 -1160 14303 -1161 14302 -1162 14302 -1162 14301 -1163 14300 -1164 14299 -1165 14299 -1166 14298 -1167 14297 -1168 14297 -1169 14296 -1169 14295 -1170 14294 -1171 14294 -1172 14293 -1173 14292 -1174 14291 -1175 14291 -1175 14290 -1176 14289 -1177 14288 -1178 14288 -1179 14287 -1180 14286 -1181 14285 -1182 14285 -1182 14284 -1183 14283 -1184 14282 -1185 14282 -1186 14281 -1187 14280 -1188 14280 -1189 14279 -1189 14278 -1190 14277 -1191 14277 -1192 14276 -1193 14275 -1194 14274 -1195 14274 -1195 14273 -1196 14272 -1197 14271 -1198 14271 -1199 14270 -1200 14269 -1201 14268 -1202 14268 -1202 14267 -1203 14266 -1204 14265 -1205 14265 -1206 14264 -1207 14263 -1208 14263 -1209 14262 -1209 14261 -1210 14260 -1211 14260 -1212 14259 -1213 14258 -1214 14257 -1215 14257 -1215 14256 -1216 14255 -1217 14254 -1218 14254 -1219 14253 -1220 14252 -1221 14251 -1222 14251 -1222 14250 -1223 14249 -1224 14248 -1225 14248 -1226 14247 -1227 14246 -1228 14246 -1228 14245 -1229 14244 -1230 14243 -1231 14242 -1232 14242 -1233 14241 -1234 14240 -1235 14239 -1235 14238 -1236 14237 -1237 14236 -1238 14235 -1239 14234 -1240 14234 -1241 14233 -1242 14232 -1242 14231 -1243 14230 -1244 14229 -1245 14228 -1246 14227 -1247 14226 -1248 14225 -1248 14225 -1249 14224 -1250 14223 -1251 14222 -1252 14221 -1253 14220 -1254 14219 -1255 14218 -1255 14217 -1256 14217 -1257 14216 -1258 14215 -1259 14214 -1260 14213 -1261 14212 -1261 14211 -1262 14210 -1263 14209 -1264 14208 -1265 14208 -1266 14207 -1267 14206 -1268 14205 -1268 14204 -1269 14203 -1270 14202 -1271 14201 -1272 14200 -1273 14200 -1274 14199 -1275 14198 -1275 14197 -1276 14196 -1277 14195 -1278 14194 -1279 14193 -1280 14192 -1281 14191 -1281 14191 -1282 14190 -1283 14189 -1284 14188 -1285 14187 -1286 14186 -1287 14185 -1288 14184 -1289 14183 -1290 14183 -1291 14182 -1292 14181 -1293 14180 -1294 14179 -1295 14178 -1296 14177 -1297 14176 -1298 14175 -1299 14174 -1300 14174 -1301 14173 -1302 14172 -1303 14171 -1304 14170 -1305 14169 -1306 14168 -1307 14168 -1308 14167 -1309 14166 -1310 14165 -1311 14164 -1312 14163 -1313 14162 -1314 14161 -1315 14160 -1316 14159 -1317 14158 -1318 14157 -1319 14156 -1320 14155 -1321 14154 -1322 14153 -1324 14152 -1325 14151 -1326 14150 -1327 14149 -1328 14148 -1329 14147 -1330 14146 -1331 14145 -1332 14144 -1333 14143 -1334 14142 -1335 14141 -1336 14140 -1337 14139 -1338 14138 -1339 14137 -1340 14136 -1341 14135 -1342 14134 -1343 14133 -1344 14132 -1345 14131 -1346 14130 -1347 14129 -1348 14128 -1349 14127 -1350 14126 -1351 14125 -1352 14124 -1353 14123 -1354 14121 -1355 14120 -1356 14119 -1357 14118 -1358 14117 -1359 14116 -1360 14114 -1361 14113 -1362 14112 -1363 14111 -1364 14110 -1365 14109 -1366 14107 -1367 14106 -1368 14105 -1369 14104 -1370 14103 -1371 14102 -1372 14100 -1373 14099 -1374 14098 -1375 14097 -1376 14096 -1378 14095 -1379 14093 -1380 14092 -1381 14091 -1382 14090 -1383 14089 -1384 14088 -1385 14086 -1386 14085 -1387 14084 -1388 14083 -1389 14082 -1390 14081 -1391 14079 -1392 14078 -1393 14077 -1394 14076 -1395 14075 -1396 14074 -1397 14073 -1398 14071 -1400 14070 -1401 14069 -1402 14068 -1403 14067 -1404 14066 -1405 14064 -1407 14063 -1408 14062 -1409 14061 -1410 14060 -1411 14059 -1412 14057 -1413 14056 -1415 14055 -1416 14054 -1417 14053 -1418 14052 -1419 14050 -1420 14049 -1421 14048 -1423 14047 -1424 14046 -1425 14045 -1426 14043 -1427 14042 -1428 14041 -1430 14040 -1431 14039 -1432 14038 -1433 14036 -1434 14035 -1435 14034 -1436 14033 -1438 14032 -1439 14031 -1440 14030 -1441 14028 -1442 14027 -1443 14026 -1444 14025 -1446 14024 -1447 14023 -1448 14021 -1449 14020 -1450 14019 -1451 14018 -1453 14017 -1454 14016 -1455 14014 -1456 14013 -1457 14012 -1458 14011 -1459 14010 -1461 14009 -1462 14007 -1463 14006 -1464 14005 -1465 14004 -1466 14003 -1467 14002 -1469 14000 -1470 13999 -1471 13998 -1472 13997 -1473 13996 -1474 13995 -1476 13993 -1477 13992 -1478 13991 -1479 13990 -1480 13989 -1481 13988 -1482 13986 -1484 13985 -1485 13984 -1486 13983 -1487 13982 -1488 13981 -1489 13980 -1491 13978 -1492 13977 -1493 13976 -1494 13975 -1495 13974 -1496 13973 -1497 13971 -1499 13970 -1500 13969 -1501 13968 -1502 13966 -1503 13965 -1504 13964 -1505 13962 -1507 13961 -1508 13960 -1509 13958 -1510 13957 -1511 13955 -1512 13954 -1514 13953 -1515 13951 -1516 13950 -1518 13949 -1519 13947 -1520 13946 -1522 13944 -1523 13943 -1525 13942 -1526 13940 -1527 13939 -1529 13938 -1530 13936 -1531 13935 -1533 13934 -1534 13932 -1536 13931 -1537 13929 -1538 13928 -1540 13927 -1541 13925 -1543 13924 -1544 13923 -1545 13921 -1547 13920 -1548 13919 -1550 13917 -1551 13916 -1552 13914 -1554 13913 -1555 13912 -1557 13910 -1558 13909 -1559 13908 -1561 13906 -1562 13905 -1563 13903 -1565 13902 -1566 13901 -1568 13899 -1569 13898 -1570 13897 -1572 13895 -1573 13894 -1575 13893 -1576 13891 -1577 13890 -1579 13888 -1580 13887 -1582 13886 -1583 13884 -1584 13883 -1586 13882 -1587 13880 -1589 13879 -1590 13877 -1591 13876 -1593 13875 -1594 13873 -1596 13872 -1597 13871 -1598 13869 -1600 13868 -1601 13867 -1602 13865 -1604 13864 -1605 13862 -1607 13861 -1608 13860 -1609 13858 -1611 13857 -1612 13856 -1614 13854 -1615 13853 -1616 13851 -1618 13850 -1619 13849 -1621 13847 -1622 13846 -1623 13845 -1625 13843 -1626 13842 -1628 13841 -1629 13839 -1630 13838 -1632 13836 -1633 13835 -1634 13834 -1636 13832 -1637 13831 -1639 13830 -1640 13828 -1641 13827 -1643 13825 -1644 13824 -1646 13823 -1647 13821 -1648 13820 -1650 13819 -1651 13817 -1653 13816 -1654 13815 -1655 13813 -1657 13812 -1658 13810 -1660 13809 -1661 13808 -1662 13806 -1664 13805 -1665 13804 -1666 13802 -1668 13801 -1669 13800 -1671 13798 -1672 13797 -1673 13795 -1675 13794 -1676 13793 -1678 13791 -1679 13790 -1680 13789 -1682 13787 -1683 13786 -1685 13784 -1686 13783 -1687 13781 -1689 13780 -1690 13778 -1692 13776 -1693 13774 -1694 13773 -1696 13771 -1697 13769 -1698 13767 -1700 13766 -1701 13764 -1703 13762 -1704 13760 -1706 13759 -1708 13757 -1709 13755 -1711 13754 -1713 13752 -1715 13750 -1716 13748 -1718 13747 -1720 13745 -1722 13743 -1723 13741 -1725 13740 -1727 13738 -1729 13736 -1731 13734 -1732 13733 -1734 13731 -1736 13729 -1738 13728 -1739 13726 -1741 13724 -1743 13722 -1745 13721 -1746 13719 -1748 13717 -1750 13715 -1752 13714 -1753 13712 -1755 13710 -1757 13709 -1759 13707 -1761 13705 -1762 13703 -1764 13702 -1766 13700 -1768 13698 -1769 13696 -1771 13695 -1773 13693 -1775 13691 -1776 13689 -1778 13688 -1780 13686 -1782 13684 -1783 13683 -1785 13681 -1787 13679 -1789 13677 -1791 13676 -1792 13674 -1794 13672 -1796 13670 -1798 13669 -1799 13667 -1801 13665 -1803 13664 -1805 13662 -1806 13660 -1808 13658 -1810 13657 -1812 13655 -1813 13653 -1815 13651 -1817 13650 -1819 13648 -1821 13646 -1822 13644 -1824 13643 -1826 13641 -1828 13639 -1829 13638 -1831 13636 -1833 13634 -1835 13632 -1836 13631 -1838 13629 -1840 13627 -1842 13625 -1843 13624 -1845 13622 -1847 13620 -1849 13618 -1851 13617 -1852 13615 -1854 13613 -1856 13612 -1858 13610 -1859 13608 -1861 13606 -1863 13605 -1865 13603 -1866 13601 -1868 13599 -1870 13598 -1872 13596 -1873 13594 -1875 13593 -1877 13591 -1879 13589 -1881 13587 -1882 13586 -1884 13584 -1886 13582 -1888 13580 -1889 13579 -1891 13577 -1893 13575 -1895 13573 -1897 13572 -1899 13570 -1901 13568 -1903 13567 -1905 13565 -1907 13563 -1909 13561 -1911 13560 -1913 13558 -1915 13556 -1917 13554 -1919 13553 -1921 13551 -1923 13549 -1925 13547 -1927 13545 -1929 13543 -1931 13540 -1933 13538 -1935 13536 -1937 13534 -1939 13531 -1941 13529 -1943 13527 -1945 13525 -1947 13522 -1949 13520 -1951 13518 -1953 13516 -1955 13514 -1957 13511 -1959 13509 -1961 13507 -1963 13505 -1965 13502 -1967 13500 -1969 13498 -1971 13496 -1973 13493 -1975 13491 -1977 13489 -1979 13487 -1981 13484 -1983 13482 -1985 13480 -1987 13478 -1989 13475 -1991 13473 -1993 13471 -1995 13469 -1997 13466 -1999 13464 -2001 13462 -2003 13460 -2005 13458 -2007 13455 -2009 13453 -2011 13451 -2013 13449 -2016 13446 -2018 13444 -2020 13442 -2022 13440 -2024 13437 -2026 13435 -2028 13433 -2030 13431 -2032 13428 -2034 13426 -2036 13424 -2038 13422 -2040 13419 -2042 13417 -2044 13415 -2046 13413 -2048 13410 -2050 13408 -2052 13406 -2054 13404 -2056 13401 -2058 13399 -2060 13397 -2062 13395 -2064 13393 -2066 13390 -2068 13388 -2070 13386 -2072 13384 -2074 13381 -2076 13379 -2078 13377 -2080 13375 -2082 13372 -2084 13370 -2086 13368 -2088 13366 -2090 13363 -2092 13361 -2094 13359 -2096 13357 -2098 13354 -2100 13352 -2102 13350 -2104 13347 -2106 13345 -2108 13343 -2110 13340 -2112 13338 -2114 13336 -2116 13333 -2118 13331 -2120 13329 -2122 13327 -2124 13324 -2126 13322 -2128 13320 -2130 13317 -2132 13315 -2134 13313 -2136 13310 -2137 13308 -2139 13306 -2141 13303 -2143 13301 -2145 13299 -2147 13296 -2149 13294 -2151 13292 -2153 13289 -2155 13287 -2157 13285 -2159 13283 -2161 13280 -2163 13278 -2165 13276 -2167 13273 -2169 13271 -2170 13269 -2172 13266 -2174 13264 -2176 13262 -2178 13259 -2180 13257 -2182 13255 -2184 13252 -2186 13250 -2188 13248 -2190 13245 -2192 13243 -2194 13241 -2196 13238 -2198 13236 -2200 13234 -2201 13232 -2203 13229 -2205 13227 -2207 13225 -2209 13222 -2211 13220 -2213 13218 -2215 13215 -2217 13213 -2219 13211 -2221 13208 -2223 13206 -2225 13204 -2227 13201 -2229 13199 -2231 13197 -2233 13194 -2234 13192 -2236 13190 -2238 13187 -2240 13185 -2242 13183 -2244 13181 -2246 13178 -2248 13176 -2250 13174 -2252 13171 -2254 13169 -2256 13167 -2258 13164 -2260 13162 -2262 13160 -2264 13157 -2265 13155 -2267 13153 -2269 13150 -2271 13148 -2273 13146 -2275 13143 -2277 13141 -2279 13138 -2281 13136 -2283 13133 -2285 13131 -2287 13128 -2289 13126 -2291 13123 -2293 13121 -2295 13118 -2297 13115 -2299 13113 -2301 13110 -2303 13108 -2305 13105 -2307 13103 -2309 13100 -2311 13098 -2313 13095 -2315 13092 -2317 13090 -2319 13087 -2322 13085 -2324 13082 -2326 13080 -2328 13077 -2330 13075 -2332 13072 -2334 13070 -2336 13067 -2338 13064 -2340 13062 -2342 13059 -2344 13057 -2346 13054 -2348 13052 -2350 13049 -2352 13047 -2354 13044 -2356 13041 -2359 13039 -2361 13036 -2363 13034 -2365 13031 -2367 13029 -2369 13026 -2371 13024 -2373 13021 -2375 13018 -2377 13016 -2379 13013 -2381 13011 -2383 13008 -2385 13006 -2387 13003 -2389 13001 -2391 12998 -2393 12995 -2395 12993 -2398 12990 -2400 12988 -2402 12985 -2404 12983 -2406 12981 -2408 12979 -2410 12976 -2412 12974 -2414 12972 -2416 12970 -2418 12967 -2420 12965 -2422 12963 -2424 12961 -2426 12958 -2428 12956 -2430 12954 -2432 12952 -2435 12949 -2438 12947 -2440 12945 -2443 12942 -2446 12940 -2448 12938 -2451 12936 -2454 12933 -2456 12931 -2459 12929 -2462 12927 -2464 12924 -2467 12922 -2470 12920 -2472 12918 -2475 12915 -2478 12913 -2480 12911 -2483 12909 -2486 12906 -2488 12904 -2491 12902 -2494 12900 -2496 12897 -2499 12895 -2502 12893 -2505 12891 -2507 12888 -2510 12886 -2513 12884 -2515 12882 -2518 12880 -2521 12878 -2523 12875 -2526 12873 -2529 12871 -2531 12869 -2534 12866 -2537 12864 -2539 12862 -2542 12860 -2545 12857 -2547 12855 -2550 12853 -2553 12851 -2555 12849 -2558 12846 -2561 12844 -2563 12842 -2566 12840 -2569 12837 -2571 12835 -2574 12833 -2582 12831 -2585 12828 -2588 12826 -2590 12824 -2593 12822 -2596 12819 -2599 12817 -2602 12815 -2605 12813 -2608 12810 -2611 12808 -2614 12806 -2617 12804 -2620 12802 -2623 12799 -2626 12797 -2629 12795 -2632 12793 -2635 12790 -2638 12788 -2641 12786 -2643 12784 -2646 12781 -2649 12779 -2652 12777 -2655 12775 -2658 12772 -2661 12770 -2664 12767 -2667 12763 -2670 12760 -2673 12756 -2676 12753 -2679 12749 -2682 12745 -2685 12742 -2688 12738 -2691 12735 -2693 12731 -2696 12727 -2700 12724 -2703 12720 -2707 12717 -2710 12713 -2714 12709 -2717 12706 -2721 12702 -2724 12699 -2728 12695 -2731 12691 -2735 12688 -2739 12684 -2742 12681 -2746 12677 -2749 12673 -2753 12670 -2756 12666 -2760 12663 -2763 12659 -2767 12655 -2770 12652 -2774 12648 -2777 12645 -2781 12641 -2784 12637 -2788 12634 -2791 12630 -2795 12627 -2798 12623 -2802 12619 -2805 12616 -2809 12612 -2812 12609 -2816 12605 -2820 12601 -2823 12598 -2827 12594 -2830 12590 -2834 12586 -2837 12581 -2841 12577 -2844 12573 -2848 12568 -2851 12564 -2855 12559 -2858 12555 -2862 12550 -2865 12546 -2869 12541 -2873 12537 -2877 12533 -2882 12528 -2886 12524 -2891 12519 -2895 12515 -2900 12510 -2905 12506 -2909 12501 -2914 12497 -2918 12493 -2923 12488 -2927 12484 -2932 12479 -2937 12475 -2941 12470 -2946 12466 -2950 12461 -2955 12457 -2959 12453 -2964 12448 -2968 12444 -2973 12439 -2978 12435 -2982 12430 -2987 12426 -2991 12421 -2996 12417 -3000 12411 -3005 12405 -3010 12399 -3014 12393 -3019 12387 -3025 12381 -3031 12375 -3036 12369 -3042 12363 -3047 12357 -3053 12351 -3058 12345 -3064 12339 -3070 12333 -3075 12327 -3081 12321 -3086 12315 -3092 12308 -3097 12302 -3103 12296 -3109 12290 -3114 12284 -3120 12278 -3125 12272 -3131 12266 -3136 12260 -3142 12254 -3148 12248 -3153 12242 -3159 12236 -3164 12230 -3170 12224 -3175 12218 -3181 12211 -3187 12204 -3192 12196 -3198 12188 -3203 12181 -3209 12173 -3215 12166 -3220 12158 -3227 12150 -3242 12143 -3257 12135 -3272 12128 -3287 12120 -3302 12112 -3317 12105 -3332 12097 -3348 12087 -3363 12070 -3402 12053 -3498 12036 -3611 12019 -3764 12003 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +3043 12432 +3039 12436 +3035 12440 +3031 12444 +3027 12448 +3023 12452 +3019 12456 +3015 12460 +3011 12464 +3007 12468 +3003 12472 +2999 12476 +2996 12480 +2992 12484 +2988 12488 +2984 12492 +2980 12495 +2976 12499 +2972 12503 +2969 12507 +2965 12511 +2961 12515 +2957 12519 +2953 12522 +2950 12526 +2946 12530 +2942 12534 +2938 12538 +2934 12542 +2931 12545 +2927 12549 +2923 12553 +2919 12557 +2916 12560 +2912 12564 +2908 12568 +2904 12572 +2901 12575 +2897 12579 +2893 12583 +2890 12586 +2886 12590 +2882 12594 +2879 12598 +2875 12601 +2871 12605 +2868 12609 +2864 12612 +2860 12616 +2857 12619 +2853 12623 +2850 12627 +2846 12630 +2842 12634 +2839 12638 +2835 12641 +2832 12645 +2828 12648 +2824 12652 +2821 12655 +2817 12659 +2814 12663 +2810 12666 +2807 12670 +2803 12673 +2800 12677 +2796 12680 +2793 12684 +2789 12687 +2786 12691 +2782 12694 +2779 12698 +2775 12701 +2772 12705 +2768 12708 +2765 12711 +2761 12715 +2758 12718 +2755 12722 +2751 12725 +2748 12729 +2744 12732 +2741 12735 +2738 12739 +2734 12742 +2731 12745 +2727 12749 +2724 12752 +2721 12756 +2717 12759 +2714 12762 +2711 12766 +2707 12769 +2704 12772 +2701 12776 +2697 12779 +2694 12782 +2691 12785 +2687 12789 +2684 12792 +2681 12795 +2678 12799 +2674 12802 +2671 12805 +2668 12808 +2664 12812 +2661 12815 +2658 12818 +2655 12821 +2652 12824 +2648 12828 +2645 12831 +2642 12834 +2639 12837 +2635 12840 +2632 12843 +2629 12847 +2626 12850 +2623 12853 +2620 12856 +2616 12859 +2613 12862 +2610 12865 +2607 12869 +2604 12872 +2601 12875 +2598 12878 +2594 12881 +2591 12884 +2588 12887 +2585 12890 +2582 12893 +2579 12896 +2576 12899 +2573 12902 +2570 12905 +2567 12908 +2564 12912 +2561 12915 +2558 12918 +2554 12921 +2551 12924 +2548 12927 +2545 12930 +2542 12932 +2539 12935 +2536 12938 +2533 12941 +2530 12944 +2527 12947 +2524 12950 +2521 12953 +2518 12956 +2516 12959 +2513 12962 +2510 12965 +2507 12968 +2504 12971 +2501 12975 +2498 12978 +2495 12981 +2492 12984 +2489 12987 +2486 12991 +2483 12994 +2480 12997 +2478 13000 +2475 13003 +2472 13006 +2469 13009 +2466 13013 +2463 13016 +2460 13019 +2457 13022 +2455 13025 +2452 13028 +2449 13031 +2446 13034 +2443 13037 +2441 13040 +2438 13043 +2435 13046 +2432 13049 +2429 13052 +2427 13055 +2424 13058 +2421 13061 +2418 13064 +2415 13067 +2413 13070 +2410 13073 +2407 13076 +2404 13079 +2402 13082 +2399 13085 +2396 13088 +2394 13091 +2391 13094 +2388 13096 +2385 13099 +2383 13102 +2380 13105 +2377 13108 +2375 13111 +2372 13114 +2369 13116 +2367 13119 +2364 13122 +2361 13125 +2359 13128 +2356 13130 +2353 13133 +2351 13136 +2348 13139 +2345 13142 +2343 13144 +2340 13147 +2338 13150 +2335 13153 +2332 13155 +2330 13158 +2327 13161 +2325 13163 +2322 13166 +2319 13169 +2317 13171 +2314 13174 +2312 13177 +2309 13179 +2307 13182 +2304 13185 +2302 13187 +2299 13190 +2297 13193 +2294 13195 +2291 13198 +2289 13201 +2286 13203 +2284 13206 +2281 13208 +2279 13211 +2276 13213 +2274 13216 +2271 13219 +2269 13221 +2267 13224 +2264 13226 +2262 13229 +2259 13231 +2257 13234 +2254 13236 +2252 13239 +2249 13241 +2247 13244 +2245 13246 +2242 13249 +2240 13251 +2237 13254 +2235 13256 +2232 13259 +2230 13261 +2228 13264 +2225 13266 +2223 13268 +2221 13271 +2218 13273 +2216 13276 +2213 13278 +2211 13280 +2209 13283 +2206 13285 +2204 13288 +2202 13290 +2199 13292 +2197 13295 +2195 13297 +2192 13299 +2190 13302 +2188 13304 +2185 13306 +2183 13309 +2181 13311 +2178 13313 +2176 13316 +2174 13318 +2172 13320 +2169 13322 +2167 13325 +2165 13327 +2163 13329 +2160 13331 +2158 13334 +2156 13336 +2154 13338 +2151 13340 +2149 13343 +2147 13345 +2145 13347 +2142 13349 +2140 13352 +2138 13354 +2136 13356 +2133 13358 +2131 13360 +2129 13362 +2127 13365 +2125 13367 +2122 13369 +2120 13371 +2118 13373 +2116 13375 +2114 13378 +2112 13380 +2109 13382 +2107 13384 +2105 13386 +2103 13388 +2101 13390 +2099 13392 +2097 13394 +2094 13397 +2092 13399 +2090 13401 +2088 13403 +2086 13405 +2084 13407 +2082 13409 +2080 13411 +2077 13413 +2075 13415 +2073 13417 +2071 13419 +2069 13421 +2067 13423 +2065 13425 +2063 13427 +2061 13429 +2059 13431 +2057 13433 +2055 13435 +2053 13437 +2051 13439 +2048 13441 +2046 13443 +2044 13445 +2042 13447 +2040 13449 +2038 13451 +2036 13453 +2034 13455 +2032 13457 +2030 13459 +2028 13461 +2026 13463 +2024 13465 +2022 13466 +2020 13468 +2018 13470 +2016 13472 +2014 13474 +2012 13476 +2010 13478 +2008 13480 +2006 13482 +2004 13483 +2002 13485 +2000 13487 +1999 13489 +1997 13491 +1995 13493 +1993 13495 +1991 13496 +1989 13498 +1987 13500 +1985 13502 +1983 13504 +1981 13506 +1979 13507 +1977 13509 +1975 13511 +1973 13513 +1972 13515 +1970 13516 +1968 13518 +1966 13520 +1964 13522 +1962 13524 +1960 13525 +1958 13527 +1956 13529 +1955 13531 +1953 13532 +1951 13534 +1949 13536 +1947 13538 +1945 13539 +1943 13541 +1942 13543 +1940 13545 +1938 13546 +1936 13548 +1934 13550 +1932 13551 +1931 13553 +1929 13555 +1927 13557 +1925 13558 +1923 13560 +1921 13562 +1920 13563 +1918 13565 +1916 13567 +1914 13568 +1912 13570 +1911 13572 +1909 13573 +1907 13575 +1905 13577 +1903 13578 +1902 13580 +1900 13582 +1898 13583 +1896 13585 +1895 13587 +1893 13588 +1891 13590 +1889 13592 +1888 13593 +1886 13595 +1884 13596 +1882 13598 +1881 13600 +1879 13601 +1877 13603 +1875 13604 +1874 13606 +1872 13608 +1870 13609 +1868 13611 +1867 13612 +1865 13614 +1863 13616 +1862 13617 +1860 13619 +1858 13620 +1856 13622 +1855 13623 +1853 13625 +1851 13627 +1850 13628 +1848 13630 +1846 13631 +1845 13633 +1843 13634 +1841 13636 +1839 13637 +1838 13639 +1836 13640 +1834 13642 +1833 13643 +1831 13645 +1829 13646 +1828 13648 +1826 13649 +1824 13651 +1823 13652 +1821 13654 +1819 13655 +1818 13657 +1816 13658 +1814 13660 +1813 13661 +1811 13663 +1810 13664 +1808 13666 +1806 13667 +1805 13668 +1803 13670 +1801 13671 +1800 13673 +1798 13674 +1796 13676 +1795 13677 +1793 13679 +1792 13680 +1790 13682 +1788 13683 +1787 13684 +1785 13686 +1784 13687 +1782 13689 +1780 13690 +1779 13692 +1777 13693 +1776 13694 +1774 13696 +1772 13697 +1771 13699 +1769 13700 +1768 13701 +1766 13703 +1765 13704 +1763 13706 +1761 13707 +1760 13709 +1758 13710 +1757 13711 +1755 13713 +1754 13714 +1752 13716 +1750 13717 +1749 13719 +1747 13720 +1746 13721 +1744 13723 +1743 13724 +1741 13726 +1739 13727 +1738 13729 +1736 13730 +1735 13732 +1733 13733 +1732 13735 +1730 13737 +1729 13738 +1727 13740 +1725 13741 +1724 13743 +1722 13744 +1721 13746 +1719 13747 +1718 13749 +1716 13751 +1715 13752 +1713 13754 +1711 13755 +1710 13757 +1708 13759 +1707 13760 +1705 13762 +1704 13763 +1702 13765 +1701 13767 +1699 13768 +1698 13770 +1696 13771 +1695 13773 +1693 13775 +1692 13776 +1690 13778 +1688 13780 +1687 13781 +1685 13783 +1684 13784 +1682 13786 +1681 13788 +1679 13789 +1678 13791 +1676 13792 +1675 13794 +1673 13796 +1672 13797 +1670 13799 +1669 13801 +1667 13802 +1666 13804 +1664 13805 +1663 13807 +1661 13809 +1660 13810 +1658 13812 +1657 13814 +1655 13815 +1654 13817 +1652 13818 +1651 13820 +1649 13822 +1648 13823 +1646 13825 +1645 13826 +1643 13828 +1642 13830 +1640 13831 +1639 13833 +1637 13834 +1636 13836 +1634 13838 +1633 13839 +1631 13841 +1630 13842 +1628 13844 +1627 13846 +1625 13847 +1624 13849 +1622 13850 +1621 13852 +1619 13854 +1618 13855 +1616 13857 +1615 13858 +1613 13860 +1612 13862 +1611 13863 +1609 13865 +1608 13866 +1606 13868 +1605 13870 +1603 13871 +1602 13873 +1600 13874 +1599 13876 +1597 13878 +1596 13879 +1594 13881 +1593 13882 +1591 13884 +1590 13885 +1588 13887 +1587 13889 +1586 13890 +1584 13892 +1583 13893 +1581 13895 +1580 13896 +1578 13898 +1577 13900 +1575 13901 +1574 13903 +1572 13904 +1571 13906 +1570 13907 +1568 13909 +1567 13910 +1565 13912 +1564 13914 +1562 13915 +1561 13917 +1559 13918 +1558 13920 +1557 13921 +1555 13923 +1554 13924 +1552 13926 +1551 13927 +1549 13929 +1548 13930 +1547 13932 +1545 13933 +1544 13935 +1542 13936 +1541 13938 +1539 13939 +1538 13941 +1537 13942 +1535 13944 +1534 13945 +1532 13947 +1531 13948 +1530 13950 +1528 13951 +1527 13953 +1525 13954 +1524 13956 +1523 13957 +1521 13959 +1520 13960 +1518 13962 +1517 13963 +1516 13964 +1514 13966 +1513 13967 +1511 13969 +1510 13970 +1509 13972 +1507 13973 +1506 13975 +1504 13976 +1503 13977 +1502 13979 +1500 13980 +1499 13982 +1497 13983 +1496 13985 +1495 13986 +1493 13987 +1492 13989 +1491 13990 +1489 13992 +1488 13993 +1487 13995 +1485 13996 +1484 13997 +1482 13999 +1481 14000 +1480 14002 +1478 14003 +1477 14004 +1476 14006 +1474 14007 +1473 14009 +1472 14010 +1470 14011 +1469 14013 +1468 14014 +1466 14016 +1465 14017 +1464 14018 +1462 14020 +1461 14021 +1460 14022 +1458 14024 +1457 14025 +1456 14027 +1454 14028 +1453 14029 +1452 14031 +1451 14032 +1449 14033 +1448 14035 +1447 14036 +1445 14038 +1444 14039 +1443 14040 +1441 14042 +1440 14043 +1439 14044 +1437 14046 +1436 14047 +1435 14049 +1434 14050 +1432 14051 +1431 14053 +1430 14054 +1428 14055 +1427 14057 +1426 14058 +1425 14059 +1423 14061 +1422 14062 +1421 14064 +1419 14065 +1418 14066 +1417 14068 +1416 14069 +1414 14070 +1413 14072 +1412 14073 +1410 14074 +1409 14076 +1408 14077 +1407 14078 +1405 14080 +1404 14081 +1403 14082 +1402 14084 +1400 14085 +1399 14086 +1398 14088 +1397 14089 +1395 14090 +1394 14092 +1393 14093 +1392 14094 +1390 14096 +1389 14097 +1388 14098 +1387 14100 +1385 14101 +1384 14102 +1383 14104 +1382 14105 +1380 14106 +1379 14108 +1378 14109 +1377 14110 +1375 14112 +1374 14113 +1373 14114 +1372 14115 +1370 14117 +1369 14118 +1368 14119 +1367 14121 +1365 14122 +1364 14123 +1363 14125 +1362 14126 +1361 14127 +1359 14129 +1358 14130 +1357 14131 +1356 14132 +1355 14134 +1353 14135 +1352 14136 +1351 14138 +1350 14139 +1348 14140 +1347 14141 +1346 14143 +1345 14144 +1344 14145 +1342 14147 +1341 14148 +1340 14149 +1339 14150 +1338 14152 +1336 14153 +1335 14154 +1334 14155 +1333 14157 +1332 14158 +1330 14159 +1329 14161 +1328 14162 +1327 14163 +1326 14164 +1325 14166 +1323 14167 +1322 14168 +1321 14169 +1320 14171 +1319 14172 +1317 14173 +1316 14174 +1315 14176 +1314 14177 +1313 14178 +1312 14179 +1310 14181 +1309 14182 +1308 14183 +1307 14184 +1306 14186 +1305 14187 +1303 14188 +1302 14189 +1301 14191 +1300 14192 +1299 14193 +1298 14194 +1296 14195 +1295 14197 +1294 14198 +1293 14199 +1292 14200 +1291 14202 +1290 14203 +1288 14204 +1287 14205 +1286 14206 +1285 14208 +1284 14209 +1283 14210 +1282 14211 +1280 14212 +1279 14214 +1278 14215 +1277 14216 +1276 14217 +1275 14218 +1274 14220 +1272 14221 +1271 14222 +1270 14223 +1269 14224 +1268 14226 +1267 14227 +1266 14228 +1265 14229 +1263 14230 +1262 14231 +1261 14233 +1260 14234 +1259 14235 +1258 14236 +1257 14237 +1256 14238 +1255 14240 +1253 14241 +1252 14242 +1251 14243 +1250 14244 +1249 14245 +1248 14246 +1247 14248 +1246 14249 +1245 14250 +1244 14251 +1242 14252 +1241 14253 +1240 14254 +1239 14256 +1238 14257 +1237 14258 +1236 14259 +1235 14260 +1234 14261 +1233 14262 +1232 14263 +1231 14265 +1229 14266 +1228 14267 +1227 14268 +1226 14269 +1225 14270 +1224 14271 +1223 14272 +1222 14273 +1221 14275 +1220 14276 +1219 14277 +1218 14278 +1217 14279 +1216 14280 +1215 14281 +1213 14282 +1212 14283 +1211 14284 +1210 14286 +1209 14287 +1208 14288 +1207 14289 +1206 14290 +1205 14291 +1204 14292 +1203 14293 +1202 14294 +1201 14295 +1200 14296 +1199 14297 +1198 14298 +1197 14300 +1196 14301 +1195 14302 +1194 14303 +1193 14304 +1192 14305 +1191 14306 +1190 14307 +1188 14308 +1187 14309 +1186 14310 +1185 14311 +1184 14312 +1183 14313 +1182 14314 +1181 14315 +1180 14316 +1179 14317 +1178 14318 +1177 14320 +1176 14321 +1175 14322 +1174 14323 +1173 14324 +1172 14325 +1171 14326 +1170 14327 +1169 14328 +1168 14329 +1167 14330 +1166 14331 +1165 14332 +1164 14333 +1163 14334 +1162 14335 +1161 14336 +1160 14337 +1159 14338 +1158 14339 +1158 14340 +1157 14341 +1156 14342 +1155 14343 +1154 14344 +1153 14345 +1152 14346 +1151 14347 +1150 14348 +1149 14349 +1148 14350 +1147 14351 +1146 14352 +1145 14353 +1144 14354 +1143 14355 +1142 14356 +1141 14357 +1140 14358 +1139 14359 +1138 14360 +1137 14361 +1136 14362 +1135 14363 +1134 14364 +1133 14365 +1133 14366 +1132 14367 +1131 14367 +1130 14368 +1129 14369 +1128 14370 +1127 14371 +1126 14372 +1125 14373 +1124 14374 +1123 14375 +1122 14376 +1121 14377 +1120 14378 +1119 14379 +1118 14380 +1117 14381 +1117 14382 +1116 14383 +1115 14384 +1114 14385 +1113 14386 +1112 14387 +1111 14388 +1110 14389 +1109 14389 +1108 14390 +1107 14391 +1106 14392 +1105 14393 +1105 14394 +1104 14395 +1103 14396 +1102 14397 +1101 14398 +1100 14399 +1099 14400 +1098 14401 +1097 14402 +1096 14403 +1095 14403 +1094 14404 +1094 14405 +1093 14406 +1092 14407 +1091 14408 +1090 14409 +1089 14410 +1088 14411 +1087 14412 +1086 14413 +1085 14414 +1085 14414 +1084 14415 +1083 14416 +1082 14417 +1081 14418 +1080 14419 +1079 14420 +1078 14421 +1077 14422 +1077 14423 +1076 14424 +1075 14424 +1074 14425 +1073 14426 +1072 14427 +1071 14428 +1070 14429 +1069 14430 +1069 14431 +1068 14432 +1067 14433 +1066 14433 +1065 14434 +1064 14435 +1063 14436 +1062 14437 +1061 14438 +1061 14439 +1060 14440 +1059 14440 +1058 14441 +1057 14442 +1056 14443 +1055 14444 +1054 14445 +1054 14446 +1053 14447 +1052 14447 +1051 14448 +1050 14449 +1049 14450 +1048 14451 +1048 14452 +1047 14453 +1046 14454 +1045 14454 +1044 14455 +1043 14456 +1042 14457 +1041 14458 +1041 14459 +1040 14460 +1039 14460 +1038 14461 +1037 14462 +1036 14463 +1035 14464 +1035 14465 +1034 14465 +1033 14466 +1032 14467 +1031 14468 +1030 14469 +1029 14470 +1029 14471 +1028 14471 +1027 14472 +1026 14473 +1025 14474 +1024 14475 +1024 14476 +1023 14476 +1022 14477 +1021 14478 +1020 14479 +1019 14480 +1018 14480 +1018 14481 +1017 14482 +1016 14483 +1015 14484 +1014 14485 +1013 14485 +1013 14486 +1012 14487 +1011 14488 +1010 14489 +1009 14489 +1008 14490 +1007 14491 +1007 14492 +1006 14493 +1005 14493 +1004 14494 +1003 14495 +1002 14496 +1002 14497 +1001 14497 +1000 14498 +999 14499 +998 14500 +997 14501 +997 14501 +996 14502 +995 14503 +994 14504 +993 14505 +992 14505 +991 14506 +991 14507 +990 14508 +989 14508 +988 14509 +987 14510 +986 14511 +986 14512 +985 14512 +984 14513 +983 14514 +982 14515 +981 14515 +981 14516 +980 14517 +979 14518 +978 14519 +977 14519 +976 14520 +976 14521 +975 14522 +974 14522 +973 14523 +972 14524 +971 14525 +971 14525 +970 14526 +969 14527 +968 14528 +967 14528 +966 14529 +966 14530 +965 14531 +964 14531 +963 14532 +962 14533 +961 14534 +961 14535 +960 14535 +959 14536 +958 14537 +957 14538 +956 14538 +956 14539 +955 14540 +954 14541 +953 14541 +952 14542 +952 14543 +951 14544 +950 14544 +949 14545 +948 14546 +947 14547 +947 14547 +946 14548 +945 14549 +944 14550 +943 14550 +943 14551 +942 14552 +941 14553 +940 14554 +939 14554 +938 14555 +938 14556 +937 14557 +936 14557 +935 14558 +934 14559 +934 14560 +933 14560 +932 14561 +931 14562 +930 14563 +930 14563 +929 14564 +928 14565 +927 14566 +926 14566 +926 14567 +925 14568 +924 14569 +923 14569 +922 14570 +922 14571 +921 14572 +920 14572 +919 14573 +918 14574 +918 14575 +917 14575 +916 14576 +915 14577 +914 14578 +914 14578 +913 14579 +912 14580 +911 14581 +910 14581 +910 14582 +909 14583 +908 14584 +907 14584 +907 14585 +906 14586 +905 14587 +904 14587 +903 14588 +903 14589 +902 14590 +901 14590 +900 14591 +899 14592 +899 14593 +898 14593 +897 14594 +896 14595 +896 14595 +895 14596 +894 14597 +893 14598 +892 14598 +892 14599 +891 14600 +890 14601 +889 14601 +889 14602 +888 14603 +887 14604 +886 14604 +885 14605 +885 14606 +884 14607 +883 14607 +882 14608 +882 14609 +881 14609 +880 14610 +879 14611 +879 14612 +878 14612 +877 14613 +876 14614 +875 14615 +875 14615 +874 14616 +873 14617 +872 14618 +872 14618 +871 14619 +870 14620 +869 14620 +869 14621 +868 14622 +867 14623 +866 14623 +866 14624 +865 14625 +864 14626 +863 14626 +863 14627 +862 14628 +861 14628 +860 14629 +860 14630 +859 14631 +858 14631 +857 14632 +857 14633 +856 14634 +855 14634 +854 14635 +854 14636 +853 14636 +852 14637 +851 14638 +851 14639 +850 14639 +849 14640 +848 14641 +848 14641 +847 14642 +846 14643 +845 14644 +845 14644 +844 14645 +843 14646 +842 14647 +842 14647 +841 14648 +840 14649 +839 14649 +839 14650 +838 14651 +837 14652 +836 14652 +836 14653 +835 14654 +834 14654 +833 14655 +833 14656 +832 14657 +831 14657 +831 14658 +830 14659 +829 14659 +828 14660 +828 14661 +827 14662 +826 14662 +825 14663 +825 14664 +824 14664 +823 14665 +823 14666 +822 14667 +821 14667 +820 14668 +820 14669 +819 14669 +818 14670 +817 14671 +817 14671 +816 14672 +815 14673 +815 14674 +814 14674 +813 14675 +812 14676 +812 14676 +811 14677 +810 14678 +810 14678 +809 14679 +808 14680 +807 14681 +807 14681 +806 14682 +805 14683 +805 14683 +804 14684 +803 14685 +802 14685 +802 14686 +801 14687 +800 14688 +800 14688 +799 14689 +798 14690 +797 14690 +797 14691 +796 14692 +795 14692 +795 14693 +794 14694 +793 14694 +792 14695 +792 14696 +791 14697 +790 14697 +790 14698 +789 14699 +788 14699 +788 14700 +787 14701 +786 14701 +785 14702 +785 14703 +784 14703 +783 14704 +783 14705 +782 14705 +781 14706 +780 14707 +780 14707 +779 14708 +778 14709 +778 14709 +777 14710 +776 14711 +776 14711 +775 14712 +774 14713 +773 14713 +773 14714 +772 14715 +771 14715 +771 14716 +770 14717 +769 14717 +769 14718 +768 14719 +767 14719 +766 14720 +766 14721 +765 14721 +764 14722 +764 14723 +763 14723 +762 14724 +762 14725 +761 14725 +760 14726 +760 14727 +759 14727 +758 14728 +757 14728 +757 14729 +756 14730 +755 14730 +755 14731 +754 14732 +753 14732 +753 14733 +752 14734 +751 14734 +751 14735 +750 14735 +749 14736 +749 14737 +748 14737 +747 14738 +746 14739 +746 14739 +745 14740 +744 14741 +744 14741 +743 14742 +742 14742 +742 14743 +741 14744 +740 14744 +740 14745 +739 14746 +738 14746 +738 14747 +737 14747 +736 14748 +736 14749 +735 14749 +734 14750 +734 14751 +733 14751 +732 14752 +732 14752 +731 14753 +730 14754 +730 14754 +729 14755 +728 14756 +728 14756 +727 14757 +726 14757 +726 14758 +725 14759 +724 14759 +724 14760 +723 14760 +722 14761 +722 14762 +721 14762 +720 14763 +720 14764 +719 14764 +718 14765 +718 14765 +717 14766 +716 14767 +716 14767 +715 14768 +715 14768 +714 14769 +713 14770 +713 14770 +712 14771 +711 14771 +711 14772 +710 14773 +709 14773 +709 14774 +708 14775 +707 14775 +707 14776 +706 14776 +706 14777 +705 14778 +704 14778 +704 14779 +703 14779 +702 14780 +702 14780 +701 14781 +700 14782 +700 14782 +699 14783 +699 14783 +698 14784 +697 14785 +697 14785 +696 14786 +695 14786 +695 14787 +694 14788 +694 14788 +693 14789 +692 14789 +692 14790 +691 14791 +690 14791 +690 14792 +689 14792 +689 14793 +688 14793 +687 14794 +687 14795 +686 14795 +685 14796 +685 14796 +684 14797 +684 14797 +683 14798 +682 14799 +682 14799 +681 14800 +681 14800 +680 14801 +679 14801 +679 14802 +678 14803 +677 14803 +677 14804 +676 14804 +676 14805 +675 14805 +674 14806 +674 14806 +673 14807 +673 14808 +672 14808 +671 14809 +671 14809 +670 14810 +670 14810 +669 14811 +669 14811 +668 14812 +667 14813 +667 14813 +666 14814 +666 14814 +665 14815 +664 14815 +664 14816 +663 14816 +663 14817 +662 14817 +661 14818 +661 14819 +660 14819 +660 14820 +659 14820 +659 14821 +658 14821 +657 14822 +657 14822 +656 14823 +656 14823 +655 14824 +654 14824 +654 14825 +653 14826 +653 14826 +652 14827 +652 14827 +651 14828 +650 14828 +650 14829 +649 14829 +649 14830 +648 14830 +648 14831 +647 14831 +647 14832 +646 14832 +645 14833 +645 14833 +644 14834 +644 14834 +643 14835 +643 14835 +642 14836 +641 14836 +641 14837 +640 14838 +640 14838 +639 14839 +639 14839 +638 14840 +638 14840 +637 14841 +637 14841 +636 14842 +635 14842 +635 14843 +634 14843 +634 14844 +633 14844 +633 14845 +632 14845 +632 14846 +631 14846 +631 14847 +630 14847 +629 14848 +629 14848 +628 14849 +628 14849 +627 14850 +627 14850 +626 14851 +626 14851 +625 14852 +625 14852 +624 14853 +624 14853 +623 14854 +623 14854 +622 14855 +621 14855 +621 14855 +620 14856 +620 14856 +619 14857 +619 14857 +618 14858 +618 14858 +617 14859 +617 14859 +616 14860 +616 14860 +615 14861 +615 14861 +614 14862 +614 14862 +613 14863 +613 14863 +612 14864 +612 14864 +611 14865 +611 14865 +610 14866 +610 14866 +609 14866 +609 14867 +608 14867 +608 14868 +607 14868 +607 14869 +606 14869 +606 14870 +605 14870 +605 14871 +604 14871 +604 14872 +603 14872 +603 14873 +602 14873 +602 14873 +601 14874 +601 14874 +600 14875 +600 14875 +599 14876 +599 14876 +598 14877 +598 14877 +597 14878 +597 14878 +596 14878 +596 14879 +595 14879 +595 14880 +594 14880 +594 14881 +593 14881 +593 14882 +592 14882 +592 14883 +591 14883 +591 14883 +590 14884 +590 14884 +589 14885 +589 14885 +589 14886 +588 14886 +588 14887 +587 14887 +587 14887 +586 14888 +586 14888 +585 14889 +585 14889 +584 14890 +584 14890 +583 14891 +583 14891 +582 14892 +582 14892 +582 14892 +581 14893 +581 14893 +580 14894 +580 14894 +579 14895 +579 14895 +578 14896 +578 14896 +577 14897 +577 14897 +576 14897 +576 14898 +576 14898 +575 14899 +575 14899 +574 14900 +574 14900 +573 14901 +573 14901 +572 14901 +572 14902 +572 14902 +571 14903 +571 14903 +570 14904 +570 14904 +569 14905 +569 14905 +568 14906 +568 14906 +568 14906 +567 14907 +567 14907 +566 14908 +566 14908 +565 14909 +565 14909 +564 14909 +564 14910 +564 14910 +563 14911 +563 14911 +562 14912 +562 14912 +561 14913 +561 14913 +561 14913 +560 14914 +560 14914 +559 14915 +559 14915 +559 14916 +558 14916 +558 14916 +557 14917 +557 14917 +556 14918 +556 14918 +556 14919 +555 14919 +555 14919 +554 14920 +554 14920 +553 14921 +553 14921 +553 14922 +552 14922 +552 14922 +551 14923 +551 14923 +551 14924 +550 14924 +550 14925 +549 14925 +549 14925 +549 14926 +548 14926 +548 14927 +547 14927 +547 14928 +546 14928 +546 14928 +546 14929 +545 14929 +545 14930 +544 14930 +544 14931 +544 14931 +543 14931 +543 14932 +542 14932 +542 14933 +542 14933 +541 14934 +541 14934 +540 14934 +540 14935 +539 14935 +539 14936 +539 14936 +538 14937 +538 14937 +537 14937 +537 14938 +537 14938 +536 14939 +536 14939 +535 14940 +535 14940 +535 14940 +534 14941 +534 14941 +533 14942 +533 14942 +533 14943 +532 14943 +532 14943 +531 14944 +531 14944 +531 14945 +530 14945 +530 14946 +529 14946 +529 14946 +529 14947 +528 14947 +528 14948 +527 14948 +527 14949 +527 14949 +526 14949 +526 14950 +525 14950 +525 14951 +525 14951 +524 14952 +524 14952 +524 14952 +523 14953 +523 14953 +522 14954 +522 14954 +522 14954 +521 14955 +521 14955 +520 14956 +520 14956 +520 14957 +519 14957 +519 14957 +518 14958 +518 14958 +518 14959 +517 14959 +517 14960 +517 14960 +516 14960 +516 14961 +515 14961 +515 14962 +515 14962 +514 14963 +514 14963 +514 14963 +513 14964 +513 14964 +512 14965 +512 14965 +512 14966 +511 14966 +511 14966 +511 14967 +510 14967 +510 14968 +509 14968 +509 14969 +509 14969 +508 14969 +508 14970 +508 14970 +507 14971 +507 14971 +506 14972 +506 14972 +506 14972 +505 14973 +505 14973 +505 14974 +504 14974 +504 14975 +503 14975 +503 14976 +503 14976 +502 14976 +502 14977 +502 14977 +501 14978 +501 14978 +500 14979 +500 14979 +500 14979 +499 14980 +499 14980 +499 14981 +498 14981 +498 14982 +497 14982 +497 14982 +497 14983 +496 14983 +496 14984 +496 14984 +495 14985 +495 14985 +495 14985 +494 14986 +494 14986 +493 14987 +493 14987 +493 14988 +492 14988 +492 14989 +492 14989 +491 14989 +491 14990 +491 14990 +490 14991 +490 14991 +490 14992 +489 14992 +489 14992 +488 14993 +488 14993 +488 14994 +487 14994 +487 14995 +487 14995 +486 14995 +486 14996 +486 14996 +485 14997 +485 14997 +485 14998 +484 14998 +484 14999 +483 14999 +483 14999 +483 15000 +482 15000 +482 15001 +482 15001 +481 15002 +481 15002 +481 15003 +480 15003 +480 15003 +480 15004 +479 15004 +479 15005 +479 15005 +478 15006 +478 15006 +478 15006 +477 15007 +477 15007 +476 15008 +476 15008 +476 15009 +475 15009 +475 15009 +475 15010 +474 15010 +474 15011 +474 15011 +473 15012 +473 15012 +473 15012 +472 15013 +472 15013 +472 15014 +471 15014 +471 15015 +471 15015 +470 15016 +470 15016 +470 15016 +469 15017 +469 15017 +469 15018 +468 15018 +468 15019 +468 15019 +467 15019 +467 15020 +467 15020 +466 15021 +466 15021 +466 15022 +465 15022 +465 15022 +464 15023 +464 15023 +464 15024 +463 15024 +463 15025 +463 15025 +462 15025 +462 15026 +462 15026 +461 15027 +461 15027 +461 15028 +460 15028 +460 15028 +460 15029 +459 15029 +459 15030 +459 15030 +458 15031 +458 15031 +458 15031 +457 15032 +457 15032 +457 15033 +456 15033 +456 15034 +456 15034 +455 15034 +455 15035 +455 15035 +454 15036 +454 15036 +454 15036 +453 15037 +453 15037 +453 15038 +452 15038 +452 15039 +452 15039 +451 15039 +451 15040 +451 15040 +450 15041 +450 15041 +450 15042 +449 15042 +449 15042 +449 15043 +448 15043 +448 15044 +448 15044 +447 15044 +447 15045 +447 15045 +446 15046 +446 15046 +446 15047 +445 15047 +445 15047 +445 15048 +444 15048 +444 15049 +444 15049 +443 15049 +443 15050 +443 15050 +443 15051 +442 15051 +442 15051 +442 15052 +441 15052 +441 15053 +441 15053 +440 15054 +440 15054 +440 15054 +439 15055 +439 15055 +439 15056 +438 15056 +438 15056 +438 15057 +437 15057 +437 15058 +437 15058 +436 15058 +436 15059 +436 15059 +435 15060 +435 15060 +435 15060 +434 15061 +434 15061 +434 15062 +433 15062 +433 15062 +433 15063 +432 15063 +432 15064 +432 15064 +432 15064 +431 15065 +431 15065 +431 15066 +430 15066 +430 15066 +430 15067 +429 15067 +429 15068 +429 15068 +428 15068 +428 15069 +428 15069 +427 15070 +427 15070 +427 15070 +427 15071 +426 15071 +426 15071 +426 15072 +425 15072 +425 15073 +425 15073 +424 15073 +424 15074 +424 15074 +423 15075 +423 15075 +423 15075 +423 15076 +422 15076 +422 15077 +422 15077 +421 15077 +421 15078 +421 15078 +420 15078 +420 15079 +420 15079 +419 15080 +419 15080 +419 15080 +419 15081 +418 15081 +418 15082 +418 15082 +417 15082 +417 15083 +417 15083 +416 15083 +416 15084 +416 15084 +416 15085 +415 15085 +415 15085 +415 15086 +414 15086 +414 15087 +414 15087 +413 15087 +413 15088 +413 15088 +413 15088 +412 15089 +412 15089 +412 15090 +411 15090 +411 15090 +411 15091 +410 15091 +410 15091 +410 15092 +410 15092 +409 15093 +409 15093 +409 15093 +408 15094 +408 15094 +408 15094 +408 15095 +407 15095 +407 15096 +407 15096 +406 15096 +406 15097 +406 15097 +405 15097 +405 15098 +405 15098 +405 15098 +404 15099 +404 15099 +404 15100 +403 15100 +403 15100 +403 15101 +403 15101 +402 15101 +402 15102 +402 15102 +401 15103 +401 15103 +401 15103 +400 15104 +400 15104 +400 15104 +400 15105 +399 15105 +399 15105 +399 15106 +398 15106 +398 15107 +398 15107 +398 15107 +397 15108 +397 15108 +397 15108 +396 15109 +396 15109 +396 15110 +396 15110 +395 15110 +395 15111 +395 15111 +394 15111 +394 15112 +394 15112 +394 15112 +393 15113 +393 15113 +393 15113 +392 15114 +392 15114 +392 15115 +392 15115 +391 15115 +391 15116 +391 15116 +390 15116 +390 15117 +390 15117 +390 15117 +389 15118 +389 15118 +389 15118 +388 15119 +388 15119 +388 15119 +388 15120 +387 15120 +387 15120 +387 15121 +386 15121 +386 15121 +386 15122 +386 15122 +385 15123 +385 15123 +385 15123 +384 15124 +384 15124 +384 15124 +384 15125 +383 15125 +383 15125 +383 15126 +383 15126 +382 15126 +382 15127 +382 15127 +381 15127 +381 15128 +381 15128 +381 15128 +380 15129 +380 15129 +380 15129 +380 15130 +379 15130 +379 15130 +379 15131 +378 15131 +378 15131 +378 15131 +378 15132 +377 15132 +377 15132 +377 15133 +377 15133 +376 15133 +376 15134 +376 15134 +375 15134 +375 15135 +375 15135 +375 15135 +374 15136 +374 15136 +374 15136 +374 15137 +373 15137 +373 15137 +373 15138 +373 15138 +372 15138 +372 15138 +372 15139 +372 15139 +371 15139 +371 15140 +371 15140 +371 15140 +370 15141 +370 15141 +370 15141 +369 15142 +369 15142 +369 15142 +369 15142 +368 15143 +368 15143 +368 15143 +368 15144 +367 15144 +367 15144 +367 15145 +367 15145 +366 15145 +366 15145 +366 15146 +366 15146 +365 15146 +365 15147 +365 15147 +365 15147 +364 15147 +364 15148 +364 15148 +364 15148 +363 15149 +363 15149 +363 15149 +363 15149 +362 15150 +362 15150 +362 15150 +362 15151 +361 15151 +361 15151 +361 15151 +361 15152 +361 15152 +360 15152 +360 15152 +360 15153 +360 15153 +359 15153 +359 15154 +359 15154 +359 15154 +358 15154 +358 15155 +358 15155 +358 15155 +357 15155 +357 15156 +357 15156 +357 15156 +357 15156 +356 15157 +356 15157 +356 15157 +356 15157 +355 15158 +355 15158 +355 15158 +355 15158 +354 15159 +354 15159 +354 15159 +354 15159 +354 15160 +353 15160 +353 15160 +353 15160 +353 15161 +352 15161 +352 15161 +352 15161 +352 15162 +352 15162 +351 15162 +351 15162 +351 15163 +351 15163 +350 15163 +350 15163 +350 15163 +350 15164 +350 15164 +349 15164 +349 15164 +349 15165 +349 15165 +349 15165 +348 15165 +348 15166 +348 15166 +348 15166 +348 15166 +347 15166 +347 15167 +347 15167 +347 15167 +346 15167 +346 15168 +346 15168 +346 15168 +346 15168 +345 15168 +345 15169 +345 15169 +345 15169 +345 15169 +344 15170 +344 15170 +344 15170 +344 15170 +344 15170 +343 15171 +343 15171 +343 15171 +343 15171 +343 15171 +342 15172 +342 15172 +342 15172 +342 15172 +342 15173 +341 15173 +341 15173 +341 15173 +341 15173 +341 15174 +341 15174 +340 15174 +340 15174 +340 15174 +340 15175 +340 15175 +339 15175 +339 15175 +339 15175 +339 15175 +339 15176 +338 15176 +338 15176 +338 15176 +338 15176 +338 15177 +338 15177 +337 15177 +337 15177 +337 15177 +337 15178 +337 15178 +336 15178 +336 15178 +336 15178 +336 15179 +336 15179 +336 15179 +335 15179 +335 15179 +335 15179 +335 15180 +335 15180 +334 15180 +334 15180 +334 15180 +334 15180 +334 15181 +334 15181 +333 15181 +333 15181 +333 15181 +333 15182 +333 15182 +333 15182 +332 15182 +332 15182 +332 15182 +332 15183 +332 15183 +332 15183 +331 15183 +331 15183 +331 15183 +331 15184 +331 15184 +331 15184 +330 15184 +330 15184 +330 15184 +330 15185 +330 15185 +330 15185 +329 15185 +329 15185 +329 15185 +329 15185 +329 15186 +329 15186 +328 15186 +328 15186 +328 15186 +328 15186 +328 15187 +328 15187 +328 15187 +327 15187 +327 15187 +327 15187 +327 15187 +327 15188 +327 15188 +326 15188 +326 15188 +326 15188 +326 15188 +326 15188 +326 15189 +326 15189 +325 15189 +325 15189 +325 15189 +325 15189 +325 15189 +325 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +321 15192 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +320 15193 +320 15194 +320 15194 +320 15194 +320 15194 +320 15194 +320 15194 +319 15194 +319 15194 +319 15195 +319 15195 +319 15195 +319 15195 +319 15195 +319 15195 +318 15195 +318 15195 +318 15195 +318 15196 +318 15196 +318 15196 +318 15196 +318 15196 +317 15196 +317 15196 +317 15196 +317 15196 +317 15196 +317 15197 +317 15197 +317 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15198 +316 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +314 15198 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +313 15199 +313 15199 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +312 15200 +312 15200 +312 15200 +312 15200 +312 15201 +312 15201 +312 15201 +312 15201 +312 15201 +312 15201 +311 15201 +311 15201 +311 15201 +311 15201 +311 15202 +311 15202 +311 15202 +311 15202 +311 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15203 +310 15203 +310 15203 +310 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15204 +309 15204 +309 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15205 +308 15205 +308 15205 +308 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15206 +307 15206 +307 15206 +307 15206 +307 15206 +306 15206 +306 15206 +306 15206 +306 15206 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +305 15207 +305 15207 +305 15207 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +304 15208 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15211 +303 15211 +303 15211 +302 15211 +302 15211 +302 15211 +302 15211 +302 15211 +302 15212 +302 15212 +302 15212 +302 15212 +302 15212 +302 15212 +301 15212 +301 15212 +301 15212 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +300 15213 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15216 +299 15216 +299 15216 +299 15216 +298 15216 +298 15216 +298 15216 +298 15216 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +297 15217 +297 15217 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15220 +296 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15221 +295 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15222 +294 15222 +294 15222 +294 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15223 +293 15223 +293 15223 +293 15223 +293 15223 +292 15223 +292 15223 +292 15223 +292 15223 +292 15223 +292 15224 +292 15224 +292 15224 +292 15224 +292 15224 +292 15224 +291 15224 +291 15224 +291 15224 +291 15224 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +290 15225 +290 15225 +290 15225 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +289 15226 +289 15226 +289 15226 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +288 15227 +288 15227 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +287 15228 +287 15228 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +286 15229 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +285 15230 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15235 +282 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15236 +281 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15237 +280 15237 +280 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15238 +279 15238 +279 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15239 +278 15239 +278 15239 +278 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15240 +277 15240 +277 15240 +277 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15241 +276 15241 +276 15241 +276 15241 +275 15241 +275 15241 +275 15241 +275 15241 +275 15241 +275 15242 +275 15242 +275 15242 +275 15242 +275 15242 +274 15242 +274 15242 +274 15242 +274 15242 +274 15242 +274 15243 +274 15243 +274 15243 +274 15243 +274 15243 +273 15243 +273 15243 +273 15243 +273 15243 +273 15243 +273 15244 +273 15244 +273 15244 +273 15244 +272 15244 +272 15244 +272 15244 +272 15244 +272 15244 +272 15245 +272 15245 +272 15245 +272 15245 +272 15245 +271 15245 +271 15245 +271 15245 +271 15245 +271 15245 +271 15246 +271 15246 +271 15246 +271 15246 +271 15246 +270 15246 +270 15246 +270 15246 +270 15246 +270 15247 +270 15247 +270 15247 +270 15247 +270 15247 +270 15247 +269 15247 +269 15247 +269 15247 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +268 15248 +268 15248 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +267 15249 +267 15249 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +266 15250 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +265 15251 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +264 15252 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15257 +261 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15258 +260 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15259 +259 15259 +259 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15260 +258 15260 +258 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15261 +257 15261 +257 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15262 +256 15262 +256 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15263 +255 15263 +255 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15264 +254 15264 +254 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15265 +253 15265 +253 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15266 +252 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15267 +251 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +245 15271 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +244 15272 +244 15272 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +243 15273 +243 15273 +243 15273 +243 15274 +243 15274 +243 15274 +243 15274 +243 15274 +243 15274 +242 15274 +242 15274 +242 15274 +242 15274 +242 15275 +242 15275 +242 15275 +242 15275 +242 15275 +242 15275 +241 15275 +241 15275 +241 15275 +241 15275 +241 15275 +241 15276 +241 15276 +241 15276 +241 15276 +241 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15277 +240 15277 +240 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15278 +239 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +236 15279 +236 15279 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +235 15280 +235 15280 +235 15280 +235 15281 +235 15281 +235 15281 +235 15281 +235 15281 +235 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15282 +234 15282 +234 15282 +234 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +231 15283 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +230 15284 +230 15284 +230 15284 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +229 15285 +229 15285 +229 15285 +229 15285 +229 15285 +229 15286 +229 15286 +229 15286 +229 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +226 15287 +226 15287 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +225 15288 +225 15288 +225 15288 +225 15288 +225 15289 +225 15289 +225 15289 +225 15289 +225 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15290 +224 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +222 15290 +222 15290 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +221 15291 +221 15291 +221 15291 +221 15291 +221 15291 +221 15292 +221 15292 +221 15292 +221 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +218 15293 +218 15293 +218 15293 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15295 +217 15295 +217 15295 +217 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +214 15296 +214 15296 +214 15296 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15298 +213 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +211 15298 +211 15298 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15300 +210 15300 +210 15300 +210 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +207 15301 +207 15301 +207 15301 +207 15301 +207 15302 +207 15302 +207 15302 +207 15302 +207 15302 +207 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15303 +206 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +204 15303 +204 15303 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15305 +203 15305 +203 15305 +203 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +201 15305 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +200 15306 +200 15306 +200 15306 +200 15306 +200 15306 +200 15307 +200 15307 +200 15307 +200 15307 +200 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +197 15308 +197 15308 +197 15308 +197 15308 +197 15309 +197 15309 +197 15309 +197 15309 +197 15309 +197 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +194 15310 +194 15310 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15312 +193 15312 +193 15312 +193 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +190 15313 +190 15313 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +189 15314 +189 15314 +189 15314 +189 15314 +189 15315 +189 15315 +189 15315 +189 15315 +189 15315 +189 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15316 +188 15316 +188 15316 +188 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +185 15317 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +184 15318 +184 15318 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +183 15319 +183 15319 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +182 15320 +182 15320 +182 15320 +182 15320 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +181 15321 +181 15321 +181 15321 +181 15321 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +180 15322 +180 15322 +180 15322 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +179 15323 +179 15323 +179 15323 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +178 15324 +178 15324 +178 15324 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +177 15325 +177 15325 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +176 15326 +176 15326 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +175 15327 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15331 +173 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15332 +172 15332 +172 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15333 +171 15333 +171 15333 +171 15333 +171 15333 +171 15333 +170 15333 +170 15333 +170 15333 +170 15333 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +169 15334 +169 15334 +169 15334 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +168 15335 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15338 +167 15338 +167 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15339 +166 15339 +166 15339 +166 15339 +166 15339 +166 15339 +165 15339 +165 15339 +165 15339 +165 15339 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +164 15340 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15342 +164 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15343 +163 15343 +163 15343 +163 15343 +163 15343 +162 15343 +162 15343 +162 15343 +162 15343 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +161 15344 +161 15344 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15347 +160 15347 +160 15347 +160 15347 +159 15347 +159 15347 +159 15347 +159 15347 +159 15347 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +158 15348 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15350 +158 15350 +158 15350 +157 15350 +157 15350 +157 15350 +157 15350 +157 15350 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +156 15351 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15353 +156 15353 +156 15353 +155 15353 +155 15353 +155 15353 +155 15353 +155 15353 +155 15354 +155 15354 +155 15354 +155 15354 +155 15354 +155 15354 +154 15354 +154 15354 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15356 +154 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15357 +153 15357 +153 15357 +153 15357 +153 15357 +152 15357 +152 15357 +152 15357 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15360 +151 15360 +151 15360 +150 15360 +150 15360 +150 15360 +150 15360 +150 15360 +150 15361 +150 15361 +150 15361 +150 15361 +150 15361 +150 15361 +149 15361 +149 15361 +149 15361 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15364 +148 15364 +148 15364 +147 15364 +147 15364 +147 15364 +147 15364 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15367 +146 15367 +146 15367 +146 15367 +146 15367 +145 15367 +145 15367 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15370 +144 15370 +144 15370 +144 15370 +144 15370 +144 15370 +143 15370 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15372 +143 15372 +143 15372 +142 15372 +142 15372 +142 15372 +142 15372 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15375 +141 15375 +141 15375 +141 15375 +140 15375 +140 15375 +140 15375 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15378 +139 15378 +139 15378 +139 15378 +138 15378 +138 15378 +138 15378 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15381 +137 15381 +137 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15382 +136 15382 +136 15382 +136 15382 +136 15382 +135 15382 +135 15382 +135 15382 +135 15382 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +134 15383 +134 15383 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +129 15388 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +128 15389 +128 15389 +128 15389 +128 15390 +128 15390 +128 15390 +128 15390 +128 15390 +128 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +125 15391 +125 15391 +125 15391 +125 15391 +125 15392 +125 15392 +125 15392 +125 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +123 15392 +123 15392 +123 15392 +123 15392 +123 15393 +123 15393 +123 15393 +123 15393 +123 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +117 15394 +117 15394 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +109 15395 +109 15395 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15393 +107 15393 +107 15393 +107 15393 +107 15393 +107 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +105 15393 +105 15393 +105 15393 +105 15393 +105 15393 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15389 +102 15389 +102 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +100 15389 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +99 15388 +99 15388 +99 15388 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +98 15387 +98 15387 +98 15387 +98 15387 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +97 15386 +97 15386 +97 15386 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +91 15382 +91 15382 +91 15382 +91 15382 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15387 +93 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +99 15389 +99 15389 +99 15389 +99 15389 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15391 +100 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15393 +107 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +110 15393 +110 15393 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +116 15392 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +119 15391 +119 15391 +119 15391 +119 15391 +119 15391 +119 15390 +119 15390 +119 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15388 +123 15388 +123 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +125 15388 +125 15388 +125 15388 +125 15388 +125 15387 +125 15387 +125 15387 +125 15387 +125 15387 +125 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15386 +126 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +128 15386 +128 15386 +128 15386 +128 15385 +128 15385 +128 15385 +128 15385 +128 15385 +128 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15384 +129 15384 +129 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +132 15383 +132 15383 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +133 15382 +133 15382 +133 15382 +133 15382 +133 15381 +133 15381 +133 15381 +133 15381 +133 15381 +134 15381 +134 15381 +134 15381 +134 15381 +134 15381 +134 15380 +134 15380 +134 15380 +134 15380 +134 15380 +135 15380 +135 15380 +135 15380 +135 15380 +135 15380 +135 15379 +135 15379 +135 15379 +135 15379 +135 15379 +136 15379 +136 15379 +136 15379 +136 15379 +136 15379 +136 15378 +136 15378 +136 15378 +136 15378 +136 15378 +137 15378 +137 15378 +137 15378 +137 15378 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +138 15377 +138 15377 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15374 +139 15374 +139 15374 +140 15374 +140 15374 +140 15374 +140 15374 +140 15374 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +141 15373 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15371 +141 15371 +141 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +143 15370 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15368 +143 15368 +143 15368 +143 15368 +144 15368 +144 15368 +144 15368 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15366 +144 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +146 15365 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15363 +146 15363 +146 15363 +146 15363 +146 15363 +147 15363 +147 15363 +147 15363 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15361 +147 15361 +147 15361 +148 15361 +148 15361 +148 15361 +148 15361 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15356 +150 15356 +150 15356 +150 15356 +150 15356 +150 15356 +151 15356 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15354 +151 15354 +151 15354 +152 15354 +152 15354 +152 15354 +152 15354 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15351 +153 15351 +153 15351 +153 15351 +153 15351 +154 15351 +154 15351 +154 15351 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15349 +154 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15348 +155 15348 +155 15348 +155 15348 +155 15348 +155 15348 +156 15348 +156 15348 +156 15348 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15345 +157 15345 +157 15345 +157 15345 +157 15345 +158 15345 +158 15345 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15343 +158 15343 +158 15343 +159 15343 +159 15343 +159 15343 +159 15343 +159 15343 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +160 15342 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15340 +160 15340 +160 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15339 +161 15339 +161 15339 +161 15339 +161 15339 +161 15339 +162 15339 +162 15339 +162 15339 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15337 +162 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15336 +163 15336 +163 15336 +163 15336 +163 15336 +164 15336 +164 15336 +164 15336 +164 15336 +164 15336 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +165 15335 +165 15335 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15333 +165 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15332 +166 15332 +166 15332 +166 15332 +167 15332 +167 15332 +167 15332 +167 15332 +167 15332 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +168 15331 +168 15331 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15328 +169 15328 +169 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15327 +170 15327 +170 15327 +170 15327 +171 15327 +171 15327 +171 15327 +171 15327 +171 15327 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +172 15326 +172 15326 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +173 15325 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15321 +175 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +183 15315 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +184 15314 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +185 15313 +185 15313 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +186 15312 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15309 +187 15309 +187 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15308 +188 15308 +188 15308 +188 15308 +188 15308 +188 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +190 15307 +190 15307 +190 15307 +190 15307 +190 15307 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +191 15306 +191 15306 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +192 15305 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15302 +193 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15301 +194 15301 +194 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15300 +195 15300 +195 15300 +195 15300 +195 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15299 +196 15299 +196 15299 +196 15299 +196 15299 +196 15299 +197 15299 +197 15299 +197 15299 +197 15299 +197 15299 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +198 15298 +198 15298 +198 15298 +198 15298 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +199 15297 +199 15297 +199 15297 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +200 15296 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15292 +202 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15291 +203 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15290 +204 15290 +204 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15289 +205 15289 +205 15289 +205 15289 +205 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15288 +206 15288 +206 15288 +206 15288 +206 15288 +206 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +208 15287 +208 15287 +208 15287 +208 15287 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +209 15286 +209 15286 +209 15286 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +210 15285 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15280 +213 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15279 +214 15279 +214 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15278 +215 15278 +215 15278 +215 15278 +215 15278 +216 15278 +216 15278 +216 15278 +216 15278 +216 15278 +216 15277 +216 15277 +216 15277 +216 15277 +216 15277 +216 15277 +217 15277 +217 15277 +217 15277 +217 15277 +217 15277 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +218 15276 +218 15276 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +219 15275 +219 15275 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15272 +220 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15271 +221 15271 +221 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15270 +222 15270 +222 15270 +222 15270 +223 15270 +223 15270 +223 15270 +223 15270 +223 15270 +223 15269 +223 15269 +223 15269 +223 15269 +223 15269 +223 15269 +224 15269 +224 15269 +224 15269 +224 15269 +224 15268 +224 15268 +224 15268 +224 15268 +224 15268 +224 15268 +225 15268 +225 15268 +225 15268 +225 15268 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +226 15267 +226 15267 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15263 +228 15263 +228 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15262 +229 15262 +229 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15261 +230 15261 +230 15261 +230 15261 +230 15261 +231 15261 +231 15261 +231 15261 +231 15261 +231 15261 +231 15260 +231 15260 +231 15260 +231 15260 +231 15260 +232 15260 +232 15260 +232 15260 +232 15260 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +233 15259 +233 15259 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +234 15258 +234 15258 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15254 +236 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15253 +237 15253 +237 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15252 +238 15252 +238 15252 +238 15252 +239 15252 +239 15252 +239 15252 +239 15252 +239 15252 +239 15251 +239 15251 +239 15251 +239 15251 +239 15251 +240 15251 +240 15251 +240 15251 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +241 15250 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15246 +243 15246 +243 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15245 +244 15245 +244 15245 +245 15245 +245 15245 +245 15245 +245 15245 +245 15245 +245 15244 +245 15244 +245 15244 +245 15244 +245 15244 +246 15244 +246 15244 +246 15244 +246 15244 +246 15243 +246 15243 +246 15243 +246 15243 +246 15243 +246 15243 +247 15243 +247 15243 +247 15243 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +248 15242 +248 15242 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +249 15241 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15238 +250 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15237 +251 15237 +251 15237 +251 15237 +252 15237 +252 15237 +252 15237 +252 15237 +252 15237 +252 15236 +252 15236 +252 15236 +252 15236 +252 15236 +253 15236 +253 15236 +253 15236 +253 15236 +253 15235 +253 15235 +253 15235 +253 15235 +253 15235 +253 15235 +254 15235 +254 15235 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +255 15234 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15231 +256 15231 +256 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15230 +257 15230 +257 15230 +257 15230 +258 15230 +258 15230 +258 15230 +258 15230 +258 15229 +258 15229 +258 15229 +258 15229 +258 15229 +258 15229 +259 15229 +259 15229 +259 15229 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +260 15228 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15225 +261 15225 +261 15225 +262 15225 +262 15225 +262 15225 +262 15225 +262 15225 +262 15224 +262 15224 +262 15224 +262 15224 +262 15224 +263 15224 +263 15224 +263 15224 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +264 15223 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15220 +265 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15219 +266 15219 +266 15219 +266 15219 +266 15219 +267 15219 +267 15219 +267 15219 +267 15219 +267 15218 +267 15218 +267 15218 +267 15218 +267 15218 +267 15218 +268 15218 +268 15218 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +269 15217 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15215 +269 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15214 +270 15214 +270 15214 +271 15214 +271 15214 +271 15214 +271 15214 +271 15214 +271 15213 +271 15213 +271 15213 +271 15213 +271 15213 +271 15213 +272 15213 +272 15213 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15210 +273 15210 +273 15210 +273 15210 +274 15210 +274 15210 +274 15210 +274 15210 +274 15209 +274 15209 +274 15209 +274 15209 +274 15209 +274 15209 +275 15209 +275 15209 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15206 +276 15206 +276 15206 +276 15206 +277 15206 +277 15206 +277 15206 +277 15206 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +278 15205 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15202 +279 15202 +279 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15201 +280 15201 +280 15201 +280 15201 +280 15201 +281 15201 +281 15201 +281 15201 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15198 +282 15198 +282 15198 +283 15198 +283 15198 +283 15198 +283 15198 +283 15198 +283 15197 +283 15197 +283 15197 +283 15197 +283 15197 +283 15197 +284 15197 +284 15197 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15194 +285 15194 +285 15194 +285 15194 +285 15194 +286 15194 +286 15194 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15191 +287 15191 +287 15191 +287 15191 +287 15191 +287 15191 +288 15191 +288 15191 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15189 +288 15189 +289 15189 +289 15189 +289 15189 +289 15189 +289 15189 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +290 15187 +290 15187 +290 15187 +290 15187 +290 15187 +290 15187 +290 15186 +290 15186 +290 15186 +290 15186 +290 15186 +290 15186 +291 15186 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15184 +291 15184 +291 15184 +292 15184 +292 15184 +292 15184 +292 15183 +292 15183 +292 15183 +292 15183 +292 15183 +292 15183 +292 15182 +292 15182 +293 15182 +293 15182 +293 15182 +293 15182 +293 15182 +293 15181 +293 15181 +293 15181 +293 15181 +293 15181 +293 15181 +293 15180 +294 15180 +294 15180 +294 15180 +294 15180 +294 15180 +294 15179 +294 15179 +294 15179 +294 15179 +294 15179 +294 15179 +295 15178 +295 15178 +295 15178 +295 15178 +295 15178 +295 15178 +295 15177 +295 15177 +295 15177 +295 15177 +296 15177 +296 15176 +296 15176 +296 15176 +296 15176 +296 15176 +296 15176 +296 15175 +296 15175 +296 15175 +296 15175 +297 15175 +297 15175 +297 15174 +297 15174 +297 15174 +297 15174 +297 15174 +297 15173 +297 15173 +298 15173 +298 15173 +298 15173 +298 15173 +298 15172 +298 15172 +298 15172 +298 15172 +298 15172 +298 15171 +299 15171 +299 15171 +299 15171 +299 15171 +299 15171 +299 15170 +299 15170 +299 15170 +299 15170 +300 15170 +300 15169 +300 15169 +300 15169 +300 15169 +300 15169 +300 15168 +300 15168 +300 15168 +300 15168 +301 15168 +301 15167 +301 15167 +301 15167 +301 15167 +301 15167 +301 15166 +301 15166 +302 15166 +302 15166 +302 15166 +302 15165 +302 15165 +302 15165 +302 15165 +302 15165 +302 15164 +303 15164 +303 15164 +303 15164 +303 15164 +303 15163 +303 15163 +303 15163 +303 15163 +304 15163 +304 15162 +304 15162 +304 15162 +304 15162 +304 15162 +304 15161 +304 15161 +305 15161 +305 15161 +305 15161 +305 15160 +305 15160 +305 15160 +305 15160 +305 15160 +306 15159 +306 15159 +306 15159 +306 15159 +306 15158 +306 15158 +306 15158 +306 15158 +307 15158 +307 15157 +307 15157 +307 15157 +307 15157 +307 15157 +307 15156 +308 15156 +308 15156 +308 15156 +308 15155 +308 15155 +308 15155 +308 15155 +309 15155 +309 15154 +309 15154 +309 15154 +309 15154 +309 15153 +309 15153 +309 15153 +310 15153 +310 15153 +310 15152 +310 15152 +310 15152 +310 15152 +310 15151 +311 15151 +311 15151 +311 15151 +311 15151 +311 15150 +311 15150 +312 15150 +312 15150 +312 15149 +312 15149 +312 15149 +312 15149 +312 15149 +313 15148 +313 15148 +313 15148 +313 15148 +313 15147 +313 15147 +314 15147 +314 15147 +314 15146 +314 15146 +314 15146 +314 15146 +314 15145 +315 15145 +315 15145 +315 15145 +315 15144 +315 15144 +315 15144 +316 15144 +316 15144 +316 15143 +316 15143 +316 15143 +316 15143 +317 15142 +317 15142 +317 15142 +317 15142 +317 15141 +317 15141 +318 15141 +318 15141 +318 15140 +318 15140 +318 15140 +318 15140 +319 15139 +319 15139 +319 15139 +319 15139 +319 15138 +319 15138 +320 15138 +320 15138 +320 15137 +320 15137 +320 15137 +320 15137 +321 15136 +321 15136 +321 15136 +321 15135 +321 15135 +322 15135 +322 15135 +322 15134 +322 15134 +322 15134 +322 15134 +323 15133 +323 15133 +323 15133 +323 15133 +323 15132 +324 15132 +324 15132 +324 15132 +324 15131 +324 15131 +324 15131 +325 15130 +325 15130 +325 15130 +325 15130 +325 15129 +326 15129 +326 15129 +326 15129 +326 15128 +326 15128 +327 15128 +327 15127 +327 15127 +327 15127 +327 15127 +328 15126 +328 15126 +328 15126 +328 15126 +328 15125 +329 15125 +329 15125 +329 15124 +329 15124 +329 15124 +330 15124 +330 15123 +330 15123 +330 15123 +330 15123 +331 15122 +331 15122 +331 15122 +331 15121 +331 15121 +332 15121 +332 15121 +332 15120 +332 15120 +332 15120 +333 15119 +333 15119 +333 15119 +333 15119 +333 15118 +334 15118 +334 15118 +334 15117 +334 15117 +335 15117 +335 15117 +335 15116 +335 15116 +335 15116 +336 15116 +336 15115 +336 15115 +336 15115 +336 15114 +337 15114 +337 15114 +337 15114 +337 15113 +338 15113 +338 15113 +338 15112 +338 15112 +338 15112 +339 15112 +339 15111 +339 15111 +339 15111 +340 15110 +340 15110 +340 15110 +340 15109 +341 15109 +341 15109 +341 15109 +341 15108 +341 15108 +342 15108 +342 15107 +342 15107 +342 15107 +343 15107 +343 15106 +343 15106 +343 15106 +344 15105 +344 15105 +344 15105 +344 15105 +345 15104 +345 15104 +345 15104 +345 15103 +345 15103 +346 15103 +346 15103 +346 15102 +346 15102 +347 15102 +347 15101 +347 15101 +347 15101 +348 15101 +348 15100 +348 15100 +348 15100 +349 15099 +349 15099 +349 15099 +349 15098 +350 15098 +350 15098 +350 15098 +350 15097 +351 15097 +351 15097 +351 15096 +351 15096 +352 15096 +352 15096 +352 15095 +352 15095 +353 15095 +353 15094 +353 15094 +353 15094 +354 15093 +354 15093 +354 15093 +355 15093 +355 15092 +355 15092 +355 15092 +356 15092 +356 15091 +356 15091 +356 15091 +357 15091 +357 15090 +357 15090 +357 15090 +358 15090 +358 15089 +358 15089 +359 15089 +359 15089 +359 15088 +359 15088 +360 15088 +360 15088 +360 15087 +360 15087 +361 15087 +361 15087 +361 15086 +362 15086 +362 15086 +362 15086 +362 15085 +363 15085 +363 15085 +363 15085 +363 15084 +364 15084 +364 15084 +364 15083 +365 15083 +365 15083 +365 15083 +365 15082 +366 15082 +366 15082 +366 15082 +367 15081 +367 15081 +367 15081 +367 15081 +368 15080 +368 15080 +368 15080 +369 15080 +369 15079 +369 15079 +369 15079 +370 15079 +370 15078 +370 15078 +371 15078 +371 15078 +371 15077 +371 15077 +372 15077 +372 15076 +372 15076 +373 15076 +373 15076 +373 15075 +373 15075 +374 15075 +374 15075 +374 15074 +375 15074 +375 15074 +375 15074 +376 15073 +376 15073 +376 15073 +376 15072 +377 15072 +377 15072 +377 15072 +378 15071 +378 15071 +378 15071 +378 15071 +379 15070 +379 15070 +379 15070 +380 15070 +380 15069 +380 15069 +381 15069 +381 15068 +381 15068 +381 15068 +382 15068 +382 15067 +382 15067 +383 15067 +383 15067 +383 15066 +384 15066 +384 15066 +384 15065 +384 15065 +385 15065 +385 15065 +385 15064 +386 15064 +386 15064 +386 15064 +387 15063 +387 15063 +387 15063 +388 15062 +388 15062 +388 15062 +388 15062 +389 15061 +389 15061 +389 15061 +390 15060 +390 15060 +390 15060 +391 15060 +391 15059 +391 15059 +392 15059 +392 15059 +392 15058 +392 15058 +393 15058 +393 15057 +393 15057 +394 15057 +394 15057 +394 15056 +395 15056 +395 15056 +395 15055 +396 15055 +396 15055 +396 15055 +397 15054 +397 15054 +397 15054 +397 15053 +398 15053 +398 15053 +398 15053 +399 15052 +399 15052 +399 15052 +400 15051 +400 15051 +400 15051 +401 15051 +401 15050 +401 15050 +402 15050 +402 15049 +402 15049 +403 15049 +403 15049 +403 15048 +403 15048 +404 15048 +404 15047 +404 15047 +405 15047 +405 15046 +405 15046 +406 15046 +406 15046 +406 15045 +407 15045 +407 15045 +407 15044 +408 15044 +408 15044 +408 15044 +409 15043 +409 15043 +409 15043 +410 15042 +410 15042 +410 15042 +411 15041 +411 15041 +411 15041 +411 15041 +412 15040 +412 15040 +412 15040 +413 15039 +413 15039 +413 15039 +414 15038 +414 15038 +414 15038 +415 15038 +415 15037 +415 15037 +416 15037 +416 15036 +416 15036 +417 15036 +417 15035 +417 15035 +418 15035 +418 15035 +418 15034 +419 15034 +419 15034 +419 15033 +420 15033 +420 15033 +420 15032 +421 15032 +421 15032 +421 15032 +422 15031 +422 15031 +422 15031 +423 15030 +423 15030 +423 15030 +424 15029 +424 15029 +424 15029 +425 15029 +425 15028 +425 15028 +426 15028 +426 15027 +426 15027 +427 15027 +427 15026 +427 15026 +428 15026 +428 15025 +428 15025 +429 15025 +429 15025 +429 15024 +430 15024 +430 15024 +430 15023 +431 15023 +431 15023 +431 15022 +432 15022 +432 15022 +432 15022 +433 15021 +433 15021 +433 15021 +434 15020 +434 15020 +434 15020 +435 15019 +435 15019 +435 15019 +436 15018 +436 15018 +436 15018 +437 15017 +437 15017 +437 15017 +438 15017 +438 15016 +438 15016 +439 15016 +439 15015 +439 15015 +440 15015 +440 15014 +441 15014 +441 15014 +441 15013 +442 15013 +442 15013 +442 15013 +443 15012 +443 15012 +443 15012 +444 15011 +444 15011 +444 15011 +445 15010 +445 15010 +445 15010 +446 15009 +446 15009 +446 15009 +447 15008 +447 15008 +447 15008 +448 15007 +448 15007 +448 15007 +449 15006 +449 15006 +449 15006 +450 15006 +450 15005 +451 15005 +451 15005 +451 15004 +452 15004 +452 15004 +452 15003 +453 15003 +453 15003 +453 15002 +454 15002 +454 15002 +454 15001 +455 15001 +455 15001 +455 15000 +456 15000 +456 15000 +456 14999 +457 14999 +457 14999 +457 14998 +458 14998 +458 14998 +459 14997 +459 14997 +459 14997 +460 14996 +460 14996 +460 14996 +461 14995 +461 14995 +461 14995 +462 14994 +462 14994 +462 14994 +463 14993 +463 14993 +463 14993 +464 14992 +464 14992 +464 14992 +465 14991 +465 14991 +466 14991 +466 14990 +466 14990 +467 14990 +467 14989 +467 14989 +468 14989 +468 14988 +468 14988 +469 14988 +469 14987 +469 14987 +470 14987 +470 14986 +470 14986 +471 14986 +471 14985 +472 14985 +472 14985 +472 14984 +473 14984 +473 14984 +473 14983 +474 14983 +474 14983 +474 14982 +475 14982 +475 14982 +475 14981 +476 14981 +476 14981 +476 14980 +477 14980 +477 14980 +478 14979 +478 14979 +478 14979 +479 14978 +479 14978 +479 14978 +480 14977 +480 14977 +480 14977 +481 14976 +481 14976 +481 14975 +482 14975 +482 14975 +482 14974 +483 14974 +483 14974 +484 14973 +484 14973 +484 14973 +485 14972 +485 14972 +485 14972 +486 14971 +486 14971 +486 14971 +487 14970 +487 14970 +488 14969 +488 14969 +488 14969 +489 14968 +489 14968 +489 14968 +490 14967 +490 14967 +490 14967 +491 14966 +491 14966 +491 14965 +492 14965 +492 14965 +493 14964 +493 14964 +493 14964 +494 14963 +494 14963 +494 14963 +495 14962 +495 14962 +495 14961 +496 14961 +496 14961 +497 14960 +497 14960 +497 14960 +498 14959 +498 14959 +498 14958 +499 14958 +499 14958 +499 14957 +500 14957 +500 14957 +501 14956 +501 14956 +501 14955 +502 14955 +502 14955 +502 14954 +503 14954 +503 14954 +504 14953 +504 14953 +504 14952 +505 14952 +505 14952 +505 14951 +506 14951 +506 14951 +507 14950 +507 14950 +507 14949 +508 14949 +508 14949 +508 14948 +509 14948 +509 14947 +510 14947 +510 14947 +510 14946 +511 14946 +511 14946 +512 14945 +512 14945 +512 14944 +513 14944 +513 14944 +513 14943 +514 14943 +514 14942 +515 14942 +515 14942 +515 14941 +516 14941 +516 14940 +517 14940 +517 14940 +517 14939 +518 14939 +518 14938 +519 14938 +519 14938 +519 14937 +520 14937 +520 14936 +521 14936 +521 14936 +521 14935 +522 14935 +522 14934 +523 14934 +523 14934 +523 14933 +524 14933 +524 14932 +525 14932 +525 14932 +525 14931 +526 14931 +526 14930 +527 14930 +527 14930 +527 14929 +528 14929 +528 14928 +529 14928 +529 14928 +529 14927 +530 14927 +530 14926 +531 14926 +531 14926 +531 14925 +532 14925 +532 14924 +533 14924 +533 14924 +533 14923 +534 14923 +534 14922 +535 14922 +535 14922 +536 14921 +536 14921 +536 14920 +537 14920 +537 14919 +538 14919 +538 14919 +538 14918 +539 14918 +539 14918 +540 14917 +540 14917 +540 14916 +541 14916 +541 14916 +542 14915 +542 14915 +543 14915 +543 14914 +543 14914 +544 14914 +544 14913 +545 14913 +545 14912 +546 14912 +546 14912 +546 14911 +547 14911 +547 14911 +548 14910 +548 14910 +548 14909 +549 14909 +549 14909 +550 14908 +550 14908 +551 14908 +551 14907 +551 14907 +552 14906 +552 14906 +553 14906 +553 14905 +554 14905 +554 14905 +554 14904 +555 14904 +555 14903 +556 14903 +556 14903 +557 14902 +557 14902 +557 14902 +558 14901 +558 14901 +559 14900 +559 14900 +560 14900 +560 14899 +560 14899 +561 14898 +561 14898 +562 14898 +562 14897 +563 14897 +563 14897 +563 14896 +564 14896 +564 14895 +565 14895 +565 14895 +566 14894 +566 14894 +566 14893 +567 14893 +567 14893 +568 14892 +568 14892 +569 14891 +569 14891 +569 14891 +570 14890 +570 14890 +571 14889 +571 14889 +572 14889 +572 14888 +572 14888 +573 14887 +573 14887 +574 14887 +574 14886 +575 14886 +575 14885 +576 14885 +576 14884 +576 14884 +577 14884 +577 14883 +578 14883 +578 14882 +579 14882 +579 14882 +580 14881 +580 14881 +580 14880 +581 14880 +581 14879 +582 14879 +582 14879 +583 14878 +583 14878 +584 14877 +584 14877 +584 14876 +585 14876 +585 14876 +586 14875 +586 14875 +587 14874 +587 14874 +588 14873 +588 14873 +588 14873 +589 14872 +589 14872 +590 14871 +590 14871 +591 14870 +591 14870 +592 14870 +592 14869 +592 14869 +593 14868 +593 14868 +594 14867 +594 14867 +595 14867 +595 14866 +596 14866 +596 14865 +597 14865 +597 14864 +597 14864 +598 14863 +598 14863 +599 14863 +599 14862 +600 14862 +600 14861 +601 14861 +601 14860 +602 14860 +602 14859 +603 14859 +603 14858 +603 14858 +604 14858 +604 14857 +605 14857 +605 14856 +606 14856 +606 14855 +607 14855 +607 14854 +608 14854 +608 14853 +609 14853 +609 14852 +610 14852 +610 14852 +611 14851 +611 14851 +612 14850 +612 14850 +613 14849 +613 14849 +613 14848 +614 14848 +614 14847 +615 14847 +615 14846 +616 14846 +616 14845 +617 14845 +617 14844 +618 14844 +618 14844 +619 14843 +619 14843 +620 14842 +620 14842 +621 14841 +621 14841 +622 14840 +622 14840 +623 14839 +623 14839 +624 14838 +624 14838 +625 14837 +625 14837 +626 14836 +626 14836 +627 14835 +627 14835 +628 14834 +628 14834 +629 14833 +629 14833 +630 14832 +630 14832 +631 14832 +631 14831 +632 14831 +632 14830 +633 14830 +633 14829 +634 14829 +634 14828 +635 14828 +635 14827 +636 14827 +637 14826 +637 14826 +638 14825 +638 14825 +639 14824 +639 14824 +640 14823 +640 14823 +641 14822 +641 14822 +642 14821 +642 14821 +643 14820 +643 14820 +644 14819 +644 14819 +645 14818 +645 14818 +646 14817 +646 14817 +647 14816 +648 14816 +648 14815 +649 14815 +649 14814 +650 14814 +650 14813 +651 14813 +651 14812 +652 14812 +652 14811 +653 14811 +653 14810 +654 14810 +655 14809 +655 14809 +656 14808 +656 14808 +657 14807 +657 14807 +658 14806 +658 14806 +659 14805 +659 14805 +660 14804 +661 14803 +661 14803 +662 14802 +662 14802 +663 14801 +663 14801 +664 14800 +664 14800 +665 14799 +666 14799 +666 14798 +667 14798 +667 14797 +668 14797 +668 14796 +669 14796 +669 14795 +670 14795 +671 14794 +671 14794 +672 14793 +672 14793 +673 14792 +673 14792 +674 14791 +675 14790 +675 14790 +676 14789 +676 14789 +677 14788 +677 14788 +678 14787 +679 14787 +679 14786 +680 14786 +680 14785 +681 14785 +681 14784 +682 14784 +683 14783 +683 14782 +684 14782 +684 14781 +685 14781 +685 14780 +686 14780 +687 14779 +687 14779 +688 14778 +688 14778 +689 14777 +690 14776 +690 14776 +691 14775 +691 14775 +692 14774 +693 14774 +693 14773 +694 14773 +694 14772 +695 14771 +695 14771 +696 14770 +697 14770 +697 14769 +698 14769 +698 14768 +699 14768 +700 14767 +700 14766 +701 14766 +701 14765 +702 14765 +703 14764 +703 14764 +704 14763 +704 14763 +705 14762 +706 14761 +706 14761 +707 14760 +707 14760 +708 14759 +709 14759 +709 14758 +710 14757 +711 14757 +711 14756 +712 14756 +712 14755 +713 14755 +714 14754 +714 14753 +715 14753 +715 14752 +716 14752 +717 14751 +717 14751 +718 14750 +718 14749 +719 14749 +720 14748 +720 14748 +721 14747 +722 14747 +722 14746 +723 14745 +723 14745 +724 14744 +725 14744 +725 14743 +726 14743 +727 14742 +727 14741 +728 14741 +728 14740 +729 14740 +730 14739 +730 14739 +731 14738 +732 14737 +732 14737 +733 14736 +733 14736 +734 14735 +735 14734 +735 14734 +736 14733 +737 14733 +737 14732 +738 14731 +738 14731 +739 14730 +740 14730 +740 14729 +741 14729 +742 14728 +742 14727 +743 14727 +744 14726 +744 14726 +745 14725 +746 14724 +746 14724 +747 14723 +747 14723 +748 14722 +749 14721 +749 14721 +750 14720 +751 14720 +751 14719 +752 14719 +753 14718 +753 14717 +754 14717 +755 14716 +755 14716 +756 14715 +757 14714 +757 14714 +758 14713 +759 14713 +759 14712 +760 14711 +761 14711 +761 14710 +762 14709 +763 14709 +763 14708 +764 14708 +765 14707 +765 14706 +766 14706 +767 14705 +767 14705 +768 14704 +769 14703 +769 14703 +770 14702 +771 14702 +771 14701 +772 14700 +773 14700 +773 14699 +774 14699 +775 14698 +775 14697 +776 14697 +777 14696 +777 14695 +778 14695 +779 14694 +779 14694 +780 14693 +781 14692 +781 14692 +782 14691 +783 14691 +783 14690 +784 14689 +785 14689 +785 14688 +786 14687 +787 14687 +787 14686 +788 14686 +789 14685 +790 14684 +790 14684 +791 14683 +792 14683 +792 14682 +793 14681 +794 14681 +794 14680 +795 14679 +796 14679 +796 14678 +797 14678 +798 14677 +798 14676 +799 14676 +800 14675 +800 14674 +801 14674 +802 14673 +803 14673 +803 14672 +804 14671 +805 14671 +805 14670 +806 14669 +807 14669 +807 14668 +808 14667 +809 14667 +810 14666 +810 14666 +811 14665 +812 14664 +812 14664 +813 14663 +814 14662 +814 14662 +815 14661 +816 14660 +817 14660 +817 14659 +818 14659 +819 14658 +819 14657 +820 14657 +821 14656 +821 14655 +822 14655 +823 14654 +824 14653 +824 14653 +825 14652 +826 14651 +826 14651 +827 14650 +828 14650 +829 14649 +829 14648 +830 14648 +831 14647 +831 14646 +832 14646 +833 14645 +833 14644 +834 14644 +835 14643 +836 14642 +836 14642 +837 14641 +838 14640 +838 14640 +839 14639 +840 14638 +841 14638 +841 14637 +842 14637 +843 14636 +844 14635 +844 14635 +845 14634 +846 14633 +846 14633 +847 14632 +848 14631 +849 14631 +849 14630 +850 14629 +851 14629 +851 14628 +852 14627 +853 14627 +854 14626 +854 14625 +855 14625 +856 14624 +856 14623 +857 14623 +858 14622 +859 14621 +859 14621 +860 14620 +861 14619 +862 14619 +862 14618 +863 14617 +864 14617 +865 14616 +865 14615 +866 14615 +867 14614 +867 14613 +868 14613 +869 14612 +870 14611 +870 14611 +871 14610 +872 14609 +873 14609 +873 14608 +874 14607 +875 14607 +876 14606 +876 14605 +877 14605 +878 14604 +878 14603 +879 14603 +880 14602 +881 14601 +881 14601 +882 14600 +883 14599 +884 14599 +884 14598 +885 14597 +886 14597 +887 14596 +887 14595 +888 14595 +889 14594 +890 14593 +890 14593 +891 14592 +892 14591 +893 14591 +893 14590 +894 14589 +895 14589 +896 14588 +896 14587 +897 14587 +898 14586 +899 14585 +899 14585 +900 14584 +901 14583 +902 14583 +902 14582 +903 14581 +904 14581 +905 14580 +905 14579 +906 14579 +907 14578 +908 14577 +908 14577 +909 14576 +910 14575 +911 14575 +911 14574 +912 14573 +913 14573 +914 14572 +914 14571 +915 14571 +916 14570 +917 14569 +918 14569 +918 14568 +919 14567 +920 14567 +921 14566 +921 14565 +922 14565 +923 14564 +924 14563 +924 14563 +925 14562 +926 14561 +927 14561 +927 14560 +928 14559 +929 14559 +930 14558 +931 14557 +931 14557 +932 14556 +933 14555 +934 14554 +934 14554 +935 14553 +936 14552 +937 14552 +938 14551 +938 14550 +939 14550 +940 14549 +941 14548 +941 14547 +942 14547 +943 14546 +944 14545 +945 14545 +945 14544 +946 14543 +947 14542 +948 14542 +949 14541 +949 14540 +950 14539 +951 14539 +952 14538 +952 14537 +953 14537 +954 14536 +955 14535 +956 14534 +956 14534 +957 14533 +958 14532 +959 14531 +960 14531 +960 14530 +961 14529 +962 14528 +963 14528 +964 14527 +964 14526 +965 14526 +966 14525 +967 14524 +968 14523 +968 14523 +969 14522 +970 14521 +971 14520 +972 14520 +972 14519 +973 14518 +974 14517 +975 14516 +976 14516 +976 14515 +977 14514 +978 14513 +979 14513 +980 14512 +980 14511 +981 14510 +982 14510 +983 14509 +984 14508 +984 14507 +985 14507 +986 14506 +987 14505 +988 14504 +989 14503 +989 14503 +990 14502 +991 14501 +992 14500 +993 14500 +993 14499 +994 14498 +995 14497 +996 14496 +997 14496 +998 14495 +998 14494 +999 14493 +1000 14492 +1001 14492 +1002 14491 +1002 14490 +1003 14489 +1004 14489 +1005 14488 +1006 14487 +1007 14486 +1007 14485 +1008 14485 +1009 14484 +1010 14483 +1011 14482 +1012 14481 +1012 14481 +1013 14480 +1014 14479 +1015 14478 +1016 14477 +1017 14476 +1017 14476 +1018 14475 +1019 14474 +1020 14473 +1021 14472 +1022 14472 +1023 14471 +1023 14470 +1024 14469 +1025 14468 +1026 14467 +1027 14467 +1028 14466 +1028 14465 +1029 14464 +1030 14463 +1031 14462 +1032 14462 +1033 14461 +1034 14460 +1034 14459 +1035 14458 +1036 14457 +1037 14457 +1038 14456 +1039 14455 +1039 14454 +1040 14453 +1041 14452 +1042 14452 +1043 14451 +1044 14450 +1045 14449 +1045 14448 +1046 14447 +1047 14446 +1048 14446 +1049 14445 +1050 14444 +1051 14443 +1051 14442 +1052 14441 +1053 14440 +1054 14440 +1055 14439 +1056 14438 +1057 14437 +1058 14436 +1058 14435 +1059 14434 +1060 14433 +1061 14433 +1062 14432 +1063 14431 +1064 14430 +1064 14429 +1065 14428 +1066 14427 +1067 14426 +1068 14425 +1069 14425 +1070 14424 +1071 14423 +1071 14422 +1072 14421 +1073 14420 +1074 14419 +1075 14418 +1076 14417 +1077 14416 +1078 14415 +1078 14415 +1079 14414 +1080 14413 +1081 14412 +1082 14411 +1083 14410 +1084 14409 +1085 14408 +1085 14407 +1086 14406 +1087 14405 +1088 14404 +1089 14403 +1090 14402 +1091 14402 +1092 14401 +1093 14400 +1093 14399 +1094 14398 +1095 14397 +1096 14396 +1097 14395 +1098 14394 +1099 14393 +1100 14392 +1101 14391 +1101 14390 +1102 14389 +1103 14388 +1104 14387 +1105 14386 +1106 14385 +1107 14384 +1108 14383 +1109 14382 +1109 14381 +1110 14380 +1111 14379 +1112 14378 +1113 14377 +1114 14377 +1115 14376 +1116 14375 +1117 14374 +1118 14373 +1118 14372 +1119 14371 +1120 14370 +1121 14369 +1122 14368 +1123 14366 +1124 14365 +1125 14364 +1126 14363 +1127 14362 +1128 14361 +1128 14360 +1129 14359 +1130 14358 +1131 14357 +1132 14356 +1133 14355 +1134 14354 +1135 14353 +1136 14352 +1137 14351 +1138 14350 +1139 14349 +1139 14348 +1140 14347 +1141 14346 +1142 14345 +1143 14344 +1144 14343 +1145 14342 +1146 14340 +1147 14339 +1148 14338 +1149 14337 +1150 14336 +1151 14335 +1152 14334 +1152 14333 +1153 14332 +1154 14331 +1155 14330 +1156 14329 +1157 14327 +1158 14326 +1159 14325 +1160 14324 +1161 14323 +1162 14322 +1163 14321 +1164 14320 +1165 14319 +1166 14318 +1167 14316 +1167 14315 +1168 14314 +1169 14313 +1170 14312 +1171 14311 +1172 14310 +1173 14309 +1174 14307 +1175 14306 +1176 14305 +1177 14304 +1178 14303 +1179 14302 +1180 14301 +1181 14300 +1182 14298 +1183 14297 +1184 14296 +1185 14295 +1186 14294 +1187 14293 +1187 14292 +1188 14290 +1189 14289 +1190 14288 +1191 14287 +1192 14286 +1193 14285 +1194 14283 +1195 14282 +1196 14281 +1197 14280 +1198 14279 +1199 14278 +1200 14276 +1201 14275 +1202 14274 +1203 14273 +1204 14272 +1205 14271 +1206 14269 +1207 14268 +1208 14267 +1209 14266 +1210 14265 +1211 14264 +1212 14262 +1213 14261 +1214 14260 +1215 14259 +1216 14258 +1217 14256 +1218 14255 +1219 14254 +1220 14253 +1221 14252 +1222 14250 +1223 14249 +1224 14248 +1225 14247 +1226 14246 +1227 14244 +1228 14243 +1229 14242 +1230 14241 +1231 14239 +1232 14238 +1233 14237 +1234 14236 +1235 14234 +1236 14233 +1237 14232 +1238 14231 +1239 14230 +1240 14228 +1241 14227 +1242 14226 +1243 14225 +1244 14223 +1245 14222 +1246 14221 +1247 14220 +1248 14218 +1249 14217 +1250 14216 +1251 14215 +1252 14213 +1253 14212 +1255 14211 +1256 14209 +1257 14208 +1258 14207 +1259 14206 +1260 14204 +1261 14203 +1262 14202 +1263 14201 +1264 14199 +1265 14198 +1266 14197 +1267 14195 +1268 14194 +1269 14193 +1270 14192 +1271 14190 +1272 14189 +1273 14188 +1274 14186 +1276 14185 +1277 14184 +1278 14183 +1279 14181 +1280 14180 +1281 14179 +1282 14177 +1283 14176 +1284 14175 +1285 14173 +1286 14172 +1287 14171 +1288 14169 +1289 14168 +1291 14167 +1292 14165 +1293 14164 +1294 14163 +1295 14161 +1296 14160 +1297 14159 +1298 14157 +1299 14156 +1300 14155 +1301 14153 +1302 14152 +1304 14151 +1305 14149 +1306 14148 +1307 14147 +1308 14145 +1309 14144 +1310 14143 +1311 14141 +1312 14140 +1313 14139 +1314 14137 +1316 14136 +1317 14135 +1318 14133 +1319 14132 +1320 14130 +1321 14129 +1322 14128 +1323 14126 +1325 14125 +1326 14124 +1327 14122 +1328 14121 +1329 14119 +1330 14118 +1331 14117 +1332 14115 +1334 14114 +1335 14113 +1336 14111 +1337 14110 +1338 14108 +1339 14107 +1340 14106 +1342 14104 +1343 14103 +1344 14101 +1345 14100 +1346 14099 +1347 14097 +1349 14096 +1350 14094 +1351 14093 +1352 14092 +1353 14090 +1354 14089 +1356 14087 +1357 14086 +1358 14084 +1359 14083 +1360 14082 +1361 14080 +1363 14079 +1364 14077 +1365 14076 +1366 14075 +1367 14073 +1368 14072 +1370 14070 +1371 14069 +1372 14067 +1373 14066 +1374 14064 +1376 14063 +1377 14062 +1378 14060 +1379 14059 +1380 14057 +1382 14056 +1383 14054 +1384 14053 +1385 14051 +1386 14050 +1388 14049 +1389 14047 +1390 14046 +1391 14044 +1392 14043 +1394 14041 +1395 14040 +1396 14038 +1397 14037 +1399 14035 +1400 14034 +1401 14032 +1402 14031 +1403 14029 +1405 14028 +1406 14027 +1407 14025 +1408 14024 +1410 14022 +1411 14021 +1412 14019 +1413 14018 +1415 14016 +1416 14015 +1417 14013 +1418 14012 +1420 14010 +1421 14009 +1422 14007 +1423 14006 +1425 14004 +1426 14003 +1427 14001 +1428 14000 +1430 13998 +1431 13996 +1432 13995 +1433 13993 +1435 13992 +1436 13990 +1437 13989 +1438 13987 +1440 13986 +1441 13984 +1442 13983 +1443 13981 +1445 13980 +1446 13978 +1447 13977 +1448 13975 +1450 13974 +1451 13972 +1452 13971 +1454 13969 +1455 13967 +1456 13966 +1457 13964 +1459 13963 +1460 13961 +1461 13960 +1463 13958 +1464 13957 +1465 13955 +1466 13954 +1468 13952 +1469 13951 +1470 13949 +1472 13947 +1473 13946 +1474 13944 +1475 13943 +1477 13941 +1478 13940 +1479 13938 +1481 13937 +1482 13935 +1483 13934 +1484 13932 +1486 13930 +1487 13929 +1488 13927 +1490 13926 +1491 13924 +1492 13923 +1493 13921 +1495 13920 +1496 13918 +1497 13917 +1499 13915 +1500 13913 +1501 13912 +1502 13910 +1504 13909 +1505 13907 +1506 13906 +1508 13904 +1509 13902 +1510 13901 +1512 13899 +1513 13898 +1514 13896 +1515 13895 +1517 13893 +1518 13892 +1519 13890 +1521 13888 +1522 13887 +1523 13885 +1525 13884 +1526 13882 +1527 13881 +1528 13879 +1530 13877 +1531 13876 +1532 13874 +1534 13873 +1535 13871 +1536 13869 +1538 13868 +1539 13866 +1540 13865 +1541 13863 +1543 13862 +1544 13860 +1545 13858 +1547 13857 +1548 13855 +1549 13854 +1551 13852 +1552 13850 +1553 13849 +1555 13847 +1556 13846 +1557 13844 +1559 13843 +1560 13841 +1561 13839 +1563 13838 +1564 13836 +1565 13835 +1567 13833 +1568 13831 +1569 13830 +1571 13828 +1572 13827 +1573 13825 +1575 13823 +1576 13822 +1577 13820 +1579 13818 +1580 13817 +1581 13815 +1583 13814 +1584 13812 +1586 13810 +1587 13809 +1588 13807 +1590 13806 +1591 13804 +1592 13802 +1594 13801 +1595 13799 +1597 13797 +1598 13796 +1599 13794 +1601 13793 +1602 13791 +1604 13789 +1605 13788 +1606 13786 +1608 13784 +1609 13783 +1611 13781 +1612 13779 +1613 13778 +1615 13776 +1616 13774 +1618 13773 +1619 13771 +1620 13770 +1622 13768 +1623 13766 +1625 13765 +1626 13763 +1628 13761 +1629 13760 +1630 13758 +1632 13756 +1633 13755 +1635 13753 +1636 13751 +1638 13750 +1639 13748 +1640 13747 +1642 13745 +1643 13743 +1645 13742 +1646 13740 +1648 13738 +1649 13737 +1651 13735 +1652 13733 +1654 13732 +1655 13730 +1657 13728 +1658 13727 +1660 13725 +1661 13723 +1662 13722 +1664 13720 +1665 13718 +1667 13717 +1668 13715 +1670 13714 +1671 13712 +1673 13710 +1674 13709 +1676 13707 +1677 13706 +1679 13704 +1680 13702 +1682 13701 +1684 13699 +1685 13698 +1687 13696 +1688 13694 +1690 13693 +1691 13691 +1693 13689 +1694 13688 +1696 13686 +1697 13685 +1699 13683 +1700 13681 +1702 13680 +1703 13678 +1705 13676 +1707 13675 +1708 13673 +1710 13671 +1711 13670 +1713 13668 +1714 13666 +1716 13665 +1717 13663 +1719 13661 +1720 13660 +1722 13658 +1724 13656 +1725 13655 +1727 13653 +1728 13651 +1730 13650 +1731 13648 +1733 13646 +1735 13645 +1736 13643 +1738 13641 +1739 13640 +1741 13638 +1743 13636 +1744 13634 +1746 13633 +1747 13631 +1749 13629 +1751 13628 +1752 13626 +1754 13624 +1756 13622 +1757 13621 +1759 13619 +1760 13617 +1762 13615 +1764 13614 +1765 13612 +1767 13610 +1769 13608 +1770 13607 +1772 13605 +1774 13603 +1775 13601 +1777 13600 +1779 13598 +1780 13596 +1782 13594 +1784 13593 +1785 13591 +1787 13589 +1789 13587 +1790 13586 +1792 13584 +1794 13582 +1796 13580 +1797 13578 +1799 13577 +1801 13575 +1802 13573 +1804 13571 +1806 13569 +1808 13568 +1809 13566 +1811 13564 +1813 13562 +1815 13560 +1816 13558 +1818 13557 +1820 13555 +1822 13553 +1823 13551 +1825 13549 +1827 13547 +1829 13546 +1830 13544 +1832 13542 +1834 13540 +1836 13538 +1838 13536 +1839 13534 +1841 13533 +1843 13531 +1845 13529 +1847 13527 +1848 13525 +1850 13523 +1852 13521 +1854 13519 +1856 13518 +1858 13516 +1859 13514 +1861 13512 +1863 13510 +1865 13508 +1867 13506 +1869 13504 +1871 13502 +1872 13500 +1874 13499 +1876 13497 +1878 13495 +1880 13493 +1882 13491 +1884 13489 +1886 13487 +1887 13485 +1889 13483 +1891 13481 +1893 13479 +1895 13477 +1897 13475 +1899 13473 +1901 13471 +1903 13469 +1905 13467 +1907 13465 +1909 13463 +1911 13461 +1912 13460 +1914 13458 +1916 13456 +1918 13454 +1920 13452 +1922 13450 +1924 13448 +1926 13446 +1928 13444 +1930 13442 +1932 13440 +1934 13438 +1936 13436 +1938 13434 +1940 13432 +1942 13430 +1944 13428 +1946 13426 +1948 13424 +1950 13422 +1952 13420 +1954 13418 +1956 13416 +1959 13414 +1961 13412 +1963 13410 +1965 13408 +1967 13406 +1969 13404 +1971 13402 +1973 13400 +1975 13398 +1977 13396 +1979 13394 +1981 13392 +1983 13390 +1986 13388 +1988 13386 +1990 13384 +1992 13382 +1994 13380 +1996 13378 +1998 13376 +2000 13374 +2003 13372 +2005 13370 +2007 13367 +2009 13365 +2011 13363 +2013 13361 +2016 13359 +2018 13357 +2020 13355 +2022 13353 +2024 13351 +2026 13349 +2029 13347 +2031 13345 +2033 13342 +2035 13340 +2038 13338 +2040 13336 +2042 13334 +2044 13332 +2046 13330 +2049 13328 +2051 13325 +2053 13323 +2055 13321 +2058 13319 +2060 13317 +2062 13315 +2065 13313 +2067 13310 +2069 13308 +2071 13306 +2074 13304 +2076 13302 +2078 13300 +2081 13297 +2083 13295 +2085 13293 +2088 13291 +2090 13289 +2092 13287 +2095 13284 +2097 13282 +2099 13280 +2102 13278 +2104 13276 +2106 13273 +2109 13271 +2111 13269 +2114 13267 +2116 13265 +2118 13262 +2121 13260 +2123 13258 +2126 13256 +2128 13253 +2130 13251 +2133 13249 +2135 13247 +2138 13244 +2140 13242 +2143 13240 +2145 13238 +2147 13235 +2150 13233 +2152 13231 +2155 13229 +2157 13226 +2160 13224 +2162 13222 +2165 13219 +2167 13217 +2170 13215 +2172 13212 +2175 13210 +2177 13208 +2180 13206 +2182 13203 +2185 13201 +2188 13199 +2190 13196 +2193 13194 +2195 13192 +2198 13189 +2200 13187 +2203 13185 +2205 13182 +2208 13180 +2211 13178 +2213 13175 +2216 13173 +2218 13170 +2221 13168 +2224 13166 +2226 13163 +2229 13161 +2232 13159 +2234 13156 +2237 13154 +2240 13151 +2242 13149 +2245 13147 +2248 13144 +2250 13142 +2253 13139 +2256 13137 +2258 13134 +2261 13132 +2264 13130 +2266 13127 +2269 13125 +2272 13122 +2275 13120 +2277 13117 +2280 13115 +2283 13113 +2286 13110 +2288 13108 +2291 13105 +2294 13103 +2297 13100 +2300 13098 +2302 13095 +2305 13093 +2308 13090 +2311 13088 +2314 13085 +2316 13083 +2319 13080 +2322 13078 +2325 13075 +2328 13073 +2331 13070 +2333 13068 +2336 13065 +2339 13063 +2342 13060 +2345 13057 +2348 13055 +2351 13052 +2354 13050 +2356 13047 +2359 13045 +2362 13042 +2365 13040 +2368 13037 +2371 13034 +2374 13032 +2377 13029 +2380 13027 +2383 13024 +2386 13021 +2389 13019 +2392 13016 +2395 13014 +2398 13011 +2401 13008 +2404 13006 +2407 13003 +2410 13000 +2413 12998 +2416 12995 +2419 12992 +2422 12990 +2425 12987 +2428 12985 +2431 12982 +2434 12979 +2437 12977 +2440 12974 +2444 12971 +2447 12968 +2450 12966 +2453 12963 +2456 12960 +2459 12958 +2462 12955 +2465 12952 +2469 12950 +2472 12947 +2475 12944 +2478 12941 +2481 12939 +2484 12936 +2488 12933 +2491 12930 +2494 12928 +2497 12925 +2500 12922 +2504 12919 +2507 12917 +2510 12914 +2513 12911 +2516 12908 +2520 12905 +2523 12903 +2526 12900 +2530 12897 +2533 12894 +2536 12891 +2539 12889 +2543 12886 +2546 12883 +2549 12880 +2553 12877 +2556 12875 +2559 12872 +2563 12869 +2566 12866 +2569 12863 +2573 12860 +2576 12857 +2579 12855 +2583 12852 +2586 12849 +2590 12846 +2593 12843 +2596 12840 +2600 12837 +2603 12834 +2607 12831 +2610 12829 +2614 12826 +2617 12823 +2620 12820 +2624 12817 +2627 12814 +2631 12811 +2634 12808 +2638 12805 +2641 12802 +2645 12799 +2648 12796 +2652 12793 +2655 12790 +2659 12787 +2663 12784 +2666 12781 +2670 12778 +2673 12775 +2677 12772 +2680 12769 +2684 12766 +2688 12763 +2691 12760 +2695 12757 +2698 12754 +2702 12751 +2706 12748 +2709 12745 +2713 12742 +2717 12739 +2720 12736 +2724 12733 +2728 12730 +2731 12727 +2735 12724 +2739 12721 +2742 12717 +2746 12714 +2750 12711 +2754 12708 +2757 12705 +2761 12702 +2765 12699 +2769 12696 +2772 12693 +2776 12689 +2780 12686 +2784 12683 +2788 12680 +2791 12677 +2795 12674 +2799 12671 +2803 12667 +2807 12664 +2810 12661 +2814 12658 +2818 12655 +2822 12651 +2826 12648 +2830 12645 +2834 12642 +2838 12639 +2841 12635 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 diff --git a/Analysis/config/exclusionMask_541.txt.png b/Analysis/config/exclusionMask_541.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee73ce02e00f9dbc6ab91c4884e1e3307fcdaec GIT binary patch literal 213354 zcmce9dqB+D`~UNv=`z(wH%tlBy_8HBgfNs;Ldd;clak!WWo>MDmsAL$$bArUS#nvM zU2ETxgpkXU>yCSNExE1iT7J*;{cLld_xk(y{v+cx=lwd*dCqg5^PK0L_j?w__V3fg z(#aA4nnXp04+03)0Ql%+2*AGntIB}`hzjo+m%8EE?WpPQQ7e1*+`r?8PX&8gtgy9E zrj_(GcPt#B@A{AAVD{-xtqpe>FLjGg-~VlH{C~rz-(|T-$y_?y^Z@3+_%Ya3WkwJ_ z!lZ%#P@N+vDB@(RkP<}gCP^d!q=aka6j}QJPnb_P{%Md;v;Wf{A33l0tBlIhF5ho! z&`Gh^sGtS`OBS@3835h4pPqkg(3Unc+1Or<{L_3J{FA;n#ln zPfEmvhW-OE(ffXhfW~e9I>SE_+xGa6;hJ4v?1ThUZkOSI+F9`b>k@s-(L+(n>YN~~ zO{aXXYA6hF_1?Cr_%UL}LNAaeWUP4i46`lHDo1o?U#jUU}y6$u|D7sM?rS;}RD8{q@7Ctvp2c-jfGM8@9Le~092mqjiOfVXzW{^|L8}ESacqX!mllF6n>`uRvNaxSs$!VwaBRB zV|lRj+CK7KN|9jTR*gH)=k$J|1h$sX8SA$aQN}8UG^fJESpr z7>6_!MjFBQ_#JFiuW3cd`u+IEM(Jgn>#(YT(*7vrD5I3xWli5>wRdi#g$P58)s|L0 z`G81wf3y%i(}TJCJ@Zg4(w%4s+SQF+f}?qI5A39TH|*Jvo{c(&*Y)Kn{_vC7HgI+~ zir*})7R9f6{2ayXexVU1 z+VsIlmTI(aWv6KdIIAVXH|-mHP~F518>v@ZM?q&A?Sr3_kL3jO3ux`N(Tzg~3Z9x8 z+Tx7nQEjwsm=oIs$p?g>N6@u28sD3(I0L{uEe>$Cm$98G(cKaLaBd;~?Pruw*R2VP zg5%g3+f|)m?56sDkyx@6ot!SjNM5(95Yag^?2$adNUrzjjVy;BVa@zjqsa*44q}69 zE74-W)Tk`p{xCMEI@b;1#kP%Fffluf+H4I9b};q?JhwrIt!{b*t>&t(aWFweERH($ z>|}%=dNuYNn5}Aw@Yv=C*wYAmu0Yw;L$09`9(owzdyE}w zN!|ZQD8h`~W9$w6*nO4(X5?NO>xFPzPXo-zeQDt6Ou| zsYc7+Lx1?`a^r*mMP3Eyeg^DKMi(5~nkgUs5X;vsJB8&L5i{J;iPbIsP7dm+OyRO5 z1Dv}Z;ZY21U1We)6(HP~A&IzZfR`>pxD!))nS%qu*|QOLW8iCX1~@e|CK0Ct<4g>7 zv4c=8C;iMoCpAtrFnSjb+ms9L2nRFpMHvp;+N#zF`x!kmyvQ-YXPYDJ%9PHQ`5IiX zHNpx8ev)H=E6fnKVMwmaaO~HXe>4T18B_XQ3c@KT>rFw~je!#@4W&=KLbwA%66Vtm zOAF5sW@b(poP%)jg~tfHFeHPYBJ6!$kFX;{;v0#6rTE-Ugqt#@eRm=3efBECrVQLw zhVG`g@&dwLSa>|b-lr=O4rI8wN<-;WWe9sSrPIC9IeC{KL%1~qbBhozE<1v7bB1Kv zQ$y(!B?!xz(l4U|F!Z~)8(|3p&n`wd@-F$k6dsR&7GKJp*?hk8tF< zLWJEIc-K<{d?pX!77T0}8H6OK7b9%Nz+;OMj;zQ;xElkXlHus9ET4yP00VoBM>z6i zD#9KNyzq>n^oi*RJ2LPiAM_8A!X$*7FmS&dgmW$=Bg`@Ix3vh%&TA0v$iPja(1Yci z8;NjR2A;eNVcFR@gj+N4c?)!=IhFkpwqxMX!!0qmO)8RrYQYZ}+x_qQm^T#{EK3$z zf}s6v%)cnq_dLOYTKO0UR^l$BU()*+qK<(zJJ4Mh8E0_5(@cySGBt%rzL+8JzH&Ow zD4T~MxeY@e5_z5jA=wkj-?8M*y^*}-+HC;0S#sa-RybXaSt0>#HbcJe%2!Cf&;`lI zGUTOwZzB1ep*X=`FE&c9k$;8cOV;Yp3+oukSD3WM=_+YHlCNgSvsR@d`LeebAowuk zh2f8o+;s;A6{i`=HEp*bxo^!z^unyyp2?689rz7SS8LB8c^`&+c;7lC zkNFb=nHMa%`FSM2qF9e}!4^jH$#EEa#f+ICLFdenceI~}cEi*PYG(v!4J%|r0ze#00;VT;KagH zG7x@V;>E!_CI(hptV7s8$diM=jO$fU`y51g-vM{5l7)Mh8Q>B(4t5)tMi~CyM7V#D zD+kf6Qs%xw_`(4t2fqzsnqx1)$@)SG&cVA^GO(8u!q!2pk%TSXBM{+b2b__F)yd#q z2Dqdp2a8#F;z)$M2Rd=kmVsfq0WDE*a5mOhBm69jz`6VMmnPud>q}#&rEPKKK<|-g z3pxqYX>~1b;L`Hqh{XU;8N1?Jt3hrK7hLdj#H(OA$ey?tA8usxE zzi{C9GF>kWw6p^_u4>A`bVeyKE(MoG^(RA2ATpA%s|{Y*1^O1}Y(UwP5m8&P63Mei z$RID5vE|avTM0C+HqsuBC&|d=88+#+1#lVX2fe7E6|Me#{vt=t7b>Vo(z-+A2 zbaueMHfZ=a@x!=xX#Dyh23D;uG67xCM3gU-h0}K<+-@+!AuPP41YxhP2nVxp!4ZUA z+z?i=@aAI(x3ogokAX{a$`Ee$x+zYwES!H9;U-rQc4y&D)d-vah_I4{7hOY`+lH_+ z3$MD3aF-4$4s~2Eg zpbTAwFl*n{IT%GKJAQ+3ceZqH62gHK5$?#sD<&Z9HyB}m7A}~Auy+{3o-Dj}I>PQj z2)Ab8O&Am@Ten2GIRgt}$0cC1e4!brvYFwk?__EPYVXI6Ajn@C0}pd}ILP&EZ`L@BQ===9e8Cq;zM3WX@Ii8up-3LdlE1r(BO2+dgXm-$HEx9|&G;j-kOwm%xeml+&vV95(3pJSZ2 z{uGigW63+7Lh`uB%|UQv$(se^BEsHr6oyPJ`OtwsVfd0bfdglje0X#{l4oy3@?4g@ z^HU_34@GiYmb}|lBtN-!IKXC>{Lf22V}zQl;UJCe(3-xS6jb@MO`&cRL#_l{IS3&~ znt{-jC08b?0JaSu3b3CgcO6)UE@)8rA zjNa7@#&~QMGgoDLF5;l{sZK1q(|bAv`!4VRjhmto}gQJ_2EO z0O{=CB0M_A9dzuJqV6hj!r17U8)jvhs75`&4&nWK5oSHMIzt3932VF4+&q3M1pJgI92;H^| zqvW70;D2CCAKSVcBk-Vw!0#ro$6k!r4=w!`s9OR(Ofw{o({-h{xeXK;mZC>i(TSOGKL&T4$uIf zL*Omd$Q>{mcr7bnlG|kz@W26vaZnZ*2mD3?Z@-P)frEjcOW+;2Ro^Rc5b$5HaIo@M6!1VS@ZAXPqUT`i zfdRm`BCzW{4#phl5Bwum?wM})IdDDD4^IxTrDwW7K<<(l;I|Xl;~{dF^aXw)fju7~ zcS$txV_Ep{&d1nzRTS{OS-7tHH2|k@2f>bYrS;ic&7r0W7wV6hBmfA**0lzWZ=wkl z_G4yZ=&=iZ$8EmB`Ism2HYf(p*A$i-b z5=bRFPl&>~e&Svk=-Cx6Zn|1aFq7s2d<)hT^Z4#E2szIKY+}`-wzG5rz58npQi9WxVw1xKo(%hIO6kn9z3r}MuOQCKd6VgGT zy%h(2YA{lp%49$xa)7@Hq$Ok6{WD9hGdW=cb&GH{655(|3*O;@CU9&VuIUIG5sYXe zZVC~!Pe(+PeL(#+3l~RqMp)yCJ7)y$+tma@$GU?$k(JN8e>a2^TyaB31#sbap1{672rn4| zu(5av?l}JM(sc+xIJL$Es@Z%T++1ak4upd@E==b@>kL;AS_ALT$|3Y{?~O|V+~#P- zR><#927EVXzVv=ru56E|CThl+*kyHC_ng`yItYg|x6eQszLO(z(yA*~} z^>`DQocbrHYtA}D?b;d{%&B`UReP{KtZ#nQ7G~;Cne$zUt|n)=LGi_Jn!*sGD;M`Z z5WDeuYnYJ7EF9rM@iNTm4d@1ON>=cS%dI$dLhCtDIe-;BcW0=CKN$KN@-{FDSDcN~ zOrXQ;2b_`&dey2AGHCm%OsXEk3a;tM*}>HZbItj~L~uJNckr&jq+S%!_}I8eJTtbl z6$~O8@B1_lQ-Dpof;-Xp>cOs@+HuoYP#M8`>q)K=65i?9Q^;Gwj`^C%WhT%p^%AGF zXZ>u+TBQZNdc8@iCSzME>1PM-PXf$&KUQ$H!)|woJU`ePIungY&WwV*!fa;@y8m4*$ojJyFZUQA;d77a!DbyRcuqf^3A|7JnIpMk$m1g{ zVCBnHsd_l8aiv9p9TeSvYR2n`;I3UfAhP^q6L29KpHvhLc?*ZN1PRf&&iy5yVf(KmofSj|wO~96DJfplX*sSX01V`AA42GOp14`HS!7zzU0^;V4 zic^Pd8V5Detj6ePOO*1@4HwZwBun6*rc~`grV#l3mbjb;fD6b0NBgsW8jA?!JLrX zIuVAm$sW*6knrb2PvHh6E4RK)nJ3ho?ka~ptghgAUPn&w|1trFhq0}%TTp5Nf7Y2v z1qnOL@H^l1!@Q%p0(KAyj}>Vp{G-sHabuEH+HR>m_&t7U%I{*E4{dgB0FBRp_IM_U z^)?|9xP|b0>ermciPfzzZIBlXKefjSRuR!J*mmUvr@Qe`$ZkG@cHvzMXkMq62&rsW z@OvBvfNJBD<`6+dzwJ6l!XG{M9nArQzT*vhsCu~FlwV;KUHEk9>v3n6`bIgJ`iy)r z|NK7#cMo$4(Nu<4=#NX%0`^tS#K|tpraLEuKKH}pp={$6ef~CqRUrp)lbcnM#_z2? z#M~>FkWFsj9Zh0Ewdrq5kdihhuTXPB%06G%Ky0`$+SC-nI)4k=m4v)YD+iE2ERhI4 zL=}C#w_vzAt{L|1}|xs@#r8L|WLxm&DH8T?-}ryW4#s zmH5ZJrOB3XSL?^=JF_;gj@a!3sb_YX;D}>Vvk>)k2k1?+?O+q>xg^hx5`J8K6r_>> zBX47>CA6O5#_2giZt)kA3+X2OXd?Mo?_%6uePRpq2zmb2EfT(sb04UWU^_H+d9EcK zpYF)%tq6I)UTvZN^cN=lCt|slR|au{S07vWc$p16uI|_&;cxx%*j*xwCN^qjG6eLk zB|;m5db%U(Bw=&Mxcxb>8QeL#5J>1!8!^mOJ<-|)?y!>@bQv_1t1b+Z(V&_?Qa%J` zJ~ES#)GF}oRbJIccw;&@XAXiU|8p0|N72zBY5;Gq!wu%4^VKW^M`#&uL%UsyNo zFGwM7S{T;L7rIP)2Pve>gaLzm;V^M%FJ??W+(6osA=KWIDsS12aoj9_8s2ft0b1H~jeB8+J32aBiOgkq9< z5M*8aAbMI26!#^~Q4K;8?JxL%2!UhA_`z=N6)2|q)=u|>3EE3gO!e)MfdZE9$8#@= z0i>PI9}`>gmd8|5e)@+4#!W*I!q@r1EgXuHQfH&=m z4w)k$>G3iymfai#{+#zAh^%sy>eyXzfHjwsxxCeGW@-y|G=Trk3lgP|mz<`dVXyL{ zMCsK+&VL}j!_T!^qI7#_#XlpX-p~D#MA_!L6@Qz+d$%Lp>a-QjPQ%`vmnBNaomMnE z4duEk5@oY&E1I3g6VF#AO8c*^Xm%QN48KT}O@~?0>@=RHz9vyxb+V$_X$UVu*uu+- zpGlfyvi-V5X=-l8ClR^JJ!&LM={rlBw1#re8xm#r>y|WW4dq@pCCV<}ThgR86zLES z*=b3W)=;$NmP8qxZAp{XP_*^7M5!8QNt4!)!-F?^iBkE*g66Z~;m&&!rSoYEn$O0^ zpYBVPPTyP5d^Y5}Jdh}xud$%{Y{++gC{fydZNaA!A>+0oY%|V+PoZ$pBZ*Sp$ATYD zUK^YTBf_FTZXl%V?{8yxvZ+5(pXe`WS{18&*VXr!g#`K*z-+{o|RjFJ_#_v+Wk__;A z_g51A1bn*cI*BEO9&Ou$|Km5H?K~GVQjJ&Iw$~E`V=z zu5Ts!F8i#Z1zF$;L1VD^^wIy|1AklD5SHKh;Z`>o_HvLF?7RO04{bJHsr_D<3LehR zu#aSM_;(JYAznDj>1nfS!e5yRY5ST$i)uC&5Qc7T56LqoLLr$f;G4?hP;zzg>nDlE zx`i3vnfB8^&^%Y_@UGA%3+4L|7Arm|F-E|T|DlIrYdmIhPcQ%#kZOl!0DIvE>SIc>~ z+fCt5Law&43xGdobb}fadO&(kIW8>zpl8Ma%D37{HC{WVbZ-j}F!h)7UpQOCEMo8K z$mjr=rR@kcB;0|4QKz8h!Y_C%o!EQ0XLG5>x096bM7WC|~1$^*wM=Pm*m%${J-ozx6cHoAM&(tlTx1Ar} zC^|mZH0Mf*h*qkgmKN=s$J5E-B z>UOSyFmHNGs3)N?ysfN+`U_)0--Ebk@2EazLMLk}%zw(HaX{DB!x2V@SiuMC4a z;KPHqp#71o8+2XTIl`xD4nXy2J|Pgg-x<~e;CIT-aU5(uHbbK8Hoq0Cm=+m-`fzWl{;@Lzdl6s$rTbVYHG`ihcdLPpFojc-r5Myk4l%3VG?<>00X%u&oLeQ=wfy-5G$Bh}epqHQ{8WF( z+?%Y0@C1IaH09Cpzc?t2VV4?kY+Da}3i}%mm=N^BxMKo*zw%_5TYui1E{9;!_Kwn& zlLNY|zaZAAY5J3++Wy@yO+oPc%Xlaf9}jL3AW_@j_2yy=qVUXeiQ=35;iyiS_mGRGJPV31!r31hc*sl-+iD%) z>?>;vzDw0CmdBfOQujNqk;i7S>vK@_{T5$e9w~*oAZ4H6eX``#T>R`8y9!4MJr_hnMAZM z-JI06^QyU5RA<)*6><4mL(J#Aoyh6$ISwDd`r8#&9>gO%jTdhkJRVHz1I#OVx!iPWBQ814@aok{86Oujl_{>2&vjA{nkR5}#Qnnx z*;SbS>hC6b*~CI*5nIjjR*arXH+(=H>ePYHY?yf`6I)QLSDN#g$6860=7}wtYGu(o z%<`6wp2Ew?46csv(t%I$N?z|QVluKbBr1@t6y44)fp3omCony6!r;OWH1 zE3aTm6JCVo=H+ z3r*CTyAsYO7mr~YudB+@(wwvHgVo-IM<@TsNgX>+$GK92D)6p>UJXv?BmH7~jtBI5 z_0gPH`6V0K@nME0E!FCI8@R0`^n+%*YM|_>!Fhe8UwAC?gtC{<&3PL?+;DHabp9?a z)qd!_Y|@EoddGC(Y?cTLCAi_1cH`yUFO;)c-2R-@Z=4J372Ynof!kE*yndkuE48!a zLyo=SGyxBo)FoyFFPhn8wn-ALgw<^_I`RsG;p;jO3#rb@$EAIGfY2*~U6)obE|e%u zM(3LkuW)58c>K9g98p1Wi! zNDSf>{$OMQXnMvU_C0z15?Y|!#-qGf)~!xnJe zP9cp+*0XWlF&6QG66} zz3@%cUD($!@Aam=IBQiB#P`(5RSN@HEo*sQY z3(fHLSPo59`7bRQT$NGZMAI5=K|yC{Rc4G-(;Bs@&cZ`TjhCS8)r239ee$62+eQ#S zoQ&)M$@u0mdE#MKmvZ-OQ25ZG;&?XAB479ghVYkgUebqB(+s}cgujI1?{wV7@PPfI zPGDxQkZu^em6bBa<5y7pZV*3doQ{04bD>mGiQ-?N;#cjw2Z~7a3yUYS;%iNtnF}Fk z3PFDOAV%ZGPpl1rEY#Wt%^H?HzV{hUwOCN-L-sf{76#CF8EMV;Y-sIB!$ZxDm++m# zGzhEjY#Kr*K5gfI&H@-_xID7Y1o}Data%RAkX^I2kp(~a7I%RQs0QbRMXWq~xBLzv z{jgPhE4Ec7iw{cme%LAV3{OEfUP70f*8D0Y|M7;d5yTI^ySl;!Z0xhK>4bdy3kYc# zhwh2<*e^_!%cLO}P{*^Vj>C1YAf&%WUXuODg5kGWa6zh9p)}UByck&R5Z#pDhSJ;` zv5|pcT1q#tMprZ`c?Mf)_|`umBm&6`P7(4YccuEgsjJYx&U+A>@N|;23JF{_Gq=KEO>m&s-!51Z;@7ITcd8e7K&>qP@ zCU#=2G;WInmx@E6Zp|+;rg&`jP!qim4gs(AEIIJj4Nil7{t@S)F>hwTfO>4uA9MmK z?h_!X%R~{A+mSkp9J$mOja=i)qF0NYxYSyMhuoX&#eR{$eTy_zXpx)#oP>|?)%ZTp z+D0KgeTFN4Ss#2MNQJr$88+}Sb{%|+3hcPI zJ6qky%j;kx!v2#eJa9cMMcC7iz{fAIhq(y1e%6g8fzmse571eVsULbo_UDhFAm$aV z*}^~P^yl{>EZsrirj`Bq9SG0OqOipPeiOp8lLA zyZvdqTUN&MQ&6@MS*@6ouwFKhPeyo1GJ#u04dlN>cwhv9*Do5#4@J12CxIapZ3MrI9L%l=#7us z9AUe^*%T5!*?(8q0u&_eCLz0j1Zq;o9 z_bQgeA-q_1i@@Q#B@PHrP~Bu<(AcL3#AcAO%ul=8Ry|pgNXpgsPmt&1WYkv0CV@Z^ zT_TA?*ivLDW8Qo|GE4(RQs2VG``z*IEWwvN^|`>L75q^L zKXq?}+y74B8^M0+o(Q+QMd0^we(G)r`<`QAeB%msH7l4fqkF!2K6>qy2hXv1Y1T^9 zVvHOgCKV8iZPv~zHdjzYp7CU9^j#)2*EPo$uISCCIRMPjon=y|8XsLP$;avORlsRd zVPTPJvG`5SFT8Wpd*L|k()ilahFh)={tmv9<)%;aW0Nh07k+Bw?le5pSR`1*W(kVQ zQ*u^djqP}=SPZ!$Pss@EIL#^+gN4d+6BdSs1pq5;$)XoexDF^nxSUPYz-qfv+>pyn z53Op?7H?Q@6w$2}O^2mcvFTV>*`I8rq3Kp&70aV!Wv^Hmcw5wiHJx>O+57Z z_$C75m67)YJjDRfh~Ou~`?&MczbBaxo2(ksN({W*HPhL`HaWLVBM~kir)6OfRA^9i z1quhezvOSDWHYa^xemCL@+E&2VeJtDLoPa4RxEyF+R&zz7>AW7k7w($S@GO765-6` zaV(5)q(u<|sb}mNHJU$$b!j3y)6jYt~BN^7&((kv3;%%l-441zN@!j(3{N0z|nVr}YV zQd?AH0-uR+Xas>F21hI_Eq^j?a??tjLNgNwv32F;IGaTx9G)1}J zldvwoYwR)|ZlsLilMwbkLSU#uFDJ?o=jHjCRrVI?@d@fN4)0ga65WqJ`94hn=j8{R zRiY2bObBPC&08H}7Kw0pLN68u!BjjSu6yxB;3sI(Hjx;|Hj{t+FgZDD_w7PL5~YnUNbYxe~@Ky0x-bB>ghS%i}ukvN{aZnuIURsVOe(m5Iwyxsd z0n$u_S07x>!k`~1mbJ3ad%hM0h{kt8FxT7SWL}SOFMJmSd&P^EG!XV))y4)R_2H-S zg;W2#czk}KP1JOc&R{yD>hZ^6n?~fF-`93Wy{k#)-xg!cC$+|s_}HH`6K&$i0T0^C zb5Kbw=x7sxW73$g#(E9a^Q}vg=cD@Y=$a z;hN-Mu^aw1{NRAb{Q58!^d1l{4M+R$SmMOWTFvL0$;5sH@Ye{^-^PE*7Sx%#G_k>* z*WnjNva1=j{U9f$dwM|0y6x;j7@(s_D8YT+?^0I;myB4%%9Rq{UuuFb_S!)kwmJ}o zVjJkW0r<�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 RQL#ldq0gYWy|MbN{{xIu*ysQN literal 0 HcmV?d00001 diff --git a/Analysis/config/exclusionMask_550.txt b/Analysis/config/exclusionMask_550.txt new file mode 100644 index 00000000..7f3f1bf4 --- /dev/null +++ b/Analysis/config/exclusionMask_550.txt @@ -0,0 +1,14041 @@ +20544 14040 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +4447 16357 +4442 16362 +4438 16367 +4433 16372 +4428 16377 +4424 16382 +4419 16386 +4415 16391 +4410 16396 +4405 16401 +4401 16406 +4396 16410 +4392 16415 +4387 16420 +4383 16424 +4378 16429 +4374 16434 +4369 16439 +4365 16443 +4360 16448 +4356 16453 +4351 16457 +4347 16462 +4342 16467 +4338 16471 +4333 16476 +4329 16480 +4324 16485 +4320 16490 +4315 16494 +4311 16499 +4307 16503 +4302 16508 +4298 16512 +4293 16517 +4289 16521 +4285 16526 +4280 16530 +4276 16535 +4272 16539 +4267 16544 +4263 16548 +4258 16553 +4254 16557 +4250 16561 +4245 16566 +4241 16570 +4237 16575 +4233 16579 +4228 16583 +4224 16588 +4220 16592 +4215 16596 +4211 16601 +4207 16605 +4203 16609 +4198 16614 +4194 16618 +4190 16622 +4186 16627 +4182 16631 +4177 16635 +4173 16639 +4169 16644 +4165 16648 +4161 16652 +4156 16656 +4152 16660 +4148 16665 +4144 16669 +4140 16673 +4136 16677 +4131 16681 +4127 16685 +4123 16690 +4119 16694 +4115 16698 +4111 16702 +4107 16706 +4103 16710 +4099 16714 +4094 16718 +4090 16722 +4086 16726 +4082 16730 +4078 16734 +4074 16739 +4070 16743 +4066 16747 +4062 16751 +4058 16755 +4054 16759 +4050 16762 +4046 16766 +4042 16770 +4038 16774 +4034 16778 +4030 16782 +4026 16786 +4022 16790 +4018 16794 +4014 16798 +4010 16802 +4006 16806 +4003 16809 +3999 16813 +3995 16817 +3991 16821 +3987 16825 +3983 16829 +3979 16832 +3975 16836 +3971 16840 +3967 16844 +3964 16848 +3960 16851 +3956 16855 +3952 16859 +3948 16863 +3944 16866 +3940 16870 +3937 16874 +3933 16878 +3929 16881 +3925 16885 +3921 16889 +3918 16892 +3914 16896 +3910 16900 +3906 16903 +3903 16907 +3899 16911 +3895 16914 +3891 16918 +3888 16921 +3884 16925 +3880 16929 +3876 16932 +3873 16936 +3869 16939 +3865 16943 +3862 16946 +3858 16950 +3854 16954 +3850 16957 +3847 16961 +3843 16964 +3839 16968 +3836 16971 +3832 16975 +3828 16978 +3825 16982 +3821 16985 +3818 16988 +3814 16992 +3810 16995 +3807 16999 +3803 17002 +3800 17006 +3796 17009 +3792 17012 +3789 17016 +3785 17019 +3782 17023 +3778 17026 +3774 17029 +3771 17033 +3767 17036 +3764 17039 +3760 17043 +3757 17046 +3753 17049 +3750 17053 +3746 17056 +3743 17059 +3739 17063 +3736 17066 +3732 17069 +3729 17072 +3725 17076 +3722 17079 +3718 17082 +3715 17085 +3711 17089 +3708 17092 +3704 17095 +3701 17098 +3698 17101 +3694 17105 +3691 17108 +3687 17111 +3684 17114 +3681 17117 +3677 17120 +3674 17124 +3670 17127 +3667 17130 +3664 17133 +3660 17136 +3657 17139 +3653 17142 +3650 17145 +3647 17149 +3643 17152 +3640 17155 +3637 17158 +3633 17161 +3630 17164 +3627 17167 +3623 17170 +3620 17173 +3617 17176 +3614 17179 +3610 17182 +3607 17185 +3604 17188 +3600 17191 +3597 17194 +3594 17197 +3591 17200 +3587 17203 +3584 17206 +3581 17209 +3578 17212 +3574 17215 +3571 17218 +3568 17221 +3565 17223 +3561 17226 +3558 17229 +3555 17232 +3552 17235 +3549 17238 +3545 17241 +3542 17244 +3539 17247 +3536 17249 +3533 17252 +3530 17255 +3526 17258 +3523 17261 +3520 17264 +3517 17266 +3514 17269 +3511 17272 +3508 17275 +3504 17278 +3501 17280 +3498 17283 +3495 17286 +3492 17289 +3489 17292 +3486 17294 +3483 17297 +3480 17300 +3477 17303 +3473 17305 +3470 17308 +3467 17311 +3464 17313 +3461 17316 +3458 17319 +3455 17322 +3452 17324 +3449 17327 +3446 17330 +3443 17332 +3440 17335 +3437 17338 +3434 17340 +3431 17343 +3428 17346 +3425 17348 +3422 17351 +3419 17353 +3416 17356 +3413 17359 +3410 17361 +3407 17364 +3404 17367 +3401 17369 +3398 17372 +3395 17374 +3392 17377 +3389 17379 +3386 17382 +3383 17385 +3381 17387 +3378 17390 +3375 17392 +3372 17395 +3369 17397 +3366 17400 +3363 17402 +3360 17405 +3357 17407 +3354 17410 +3352 17412 +3349 17415 +3346 17417 +3343 17420 +3340 17422 +3337 17425 +3334 17427 +3331 17430 +3329 17432 +3326 17435 +3323 17437 +3320 17439 +3317 17442 +3314 17444 +3312 17447 +3309 17449 +3306 17451 +3303 17454 +3300 17456 +3298 17459 +3295 17461 +3292 17463 +3289 17466 +3286 17468 +3284 17471 +3281 17473 +3278 17475 +3275 17478 +3273 17480 +3270 17482 +3267 17485 +3264 17487 +3262 17489 +3259 17492 +3256 17494 +3253 17496 +3251 17499 +3248 17501 +3245 17503 +3243 17506 +3240 17508 +3237 17510 +3234 17512 +3232 17515 +3229 17517 +3226 17519 +3224 17521 +3221 17524 +3218 17526 +3216 17528 +3213 17531 +3210 17533 +3208 17535 +3205 17537 +3202 17539 +3200 17542 +3197 17544 +3194 17546 +3192 17548 +3189 17551 +3186 17553 +3184 17555 +3181 17557 +3179 17559 +3176 17561 +3173 17564 +3171 17566 +3168 17568 +3166 17570 +3163 17572 +3160 17574 +3158 17577 +3155 17579 +3153 17581 +3150 17583 +3148 17585 +3145 17587 +3142 17589 +3140 17592 +3137 17594 +3135 17596 +3132 17598 +3130 17600 +3127 17602 +3125 17604 +3122 17606 +3119 17608 +3117 17611 +3114 17613 +3112 17615 +3109 17617 +3107 17619 +3104 17621 +3102 17623 +3099 17625 +3097 17627 +3094 17629 +3092 17631 +3089 17633 +3087 17635 +3084 17637 +3082 17639 +3080 17641 +3077 17643 +3075 17645 +3072 17647 +3070 17649 +3067 17651 +3065 17653 +3062 17655 +3060 17657 +3057 17659 +3055 17661 +3053 17663 +3050 17665 +3048 17667 +3045 17669 +3043 17671 +3040 17673 +3038 17675 +3036 17677 +3033 17679 +3031 17681 +3028 17683 +3026 17685 +3024 17687 +3021 17689 +3019 17691 +3016 17693 +3014 17695 +3012 17697 +3009 17699 +3007 17701 +3005 17703 +3002 17705 +3000 17706 +2998 17708 +2995 17710 +2993 17712 +2990 17714 +2988 17716 +2986 17718 +2983 17720 +2981 17722 +2979 17724 +2976 17725 +2974 17727 +2972 17729 +2969 17731 +2967 17733 +2965 17735 +2963 17737 +2960 17739 +2958 17740 +2955 17742 +2953 17744 +2951 17745 +2948 17747 +2946 17749 +2943 17751 +2941 17753 +2938 17755 +2936 17756 +2934 17758 +2931 17760 +2929 17762 +2926 17763 +2924 17765 +2922 17767 +2919 17769 +2917 17771 +2915 17772 +2912 17774 +2910 17776 +2908 17778 +2905 17779 +2903 17781 +2900 17783 +2898 17785 +2896 17787 +2893 17788 +2891 17790 +2889 17792 +2886 17794 +2884 17795 +2882 17797 +2879 17799 +2877 17801 +2875 17803 +2872 17804 +2870 17806 +2868 17808 +2866 17810 +2863 17812 +2861 17813 +2859 17815 +2856 17817 +2854 17819 +2852 17820 +2849 17822 +2847 17824 +2845 17826 +2843 17828 +2840 17829 +2838 17831 +2836 17833 +2834 17835 +2831 17836 +2829 17838 +2827 17840 +2825 17842 +2822 17843 +2820 17845 +2818 17847 +2816 17849 +2814 17851 +2811 17852 +2809 17854 +2807 17856 +2805 17858 +2803 17859 +2800 17861 +2798 17863 +2796 17865 +2794 17866 +2792 17868 +2790 17870 +2787 17872 +2785 17873 +2783 17875 +2781 17877 +2779 17879 +2777 17881 +2774 17882 +2773 17884 +2771 17886 +2768 17888 +2766 17889 +2764 17891 +2762 17893 +2760 17895 +2758 17896 +2756 17898 +2754 17900 +2751 17902 +2749 17904 +2747 17905 +2745 17907 +2743 17909 +2741 17911 +2739 17912 +2737 17914 +2735 17916 +2733 17918 +2731 17920 +2729 17921 +2727 17923 +2725 17925 +2723 17927 +2721 17928 +2719 17930 +2717 17932 +2715 17934 +2713 17935 +2711 17937 +2709 17939 +2707 17941 +2705 17943 +2703 17944 +2701 17946 +2699 17948 +2697 17950 +2695 17951 +2693 17953 +2691 17955 +2689 17957 +2687 17958 +2685 17960 +2683 17962 +2682 17964 +2680 17966 +2678 17967 +2676 17969 +2674 17971 +2672 17973 +2670 17974 +2668 17976 +2666 17978 +2665 17980 +2663 17982 +2661 17983 +2659 17991 +2657 17993 +2655 17995 +2653 17997 +2651 17998 +2649 18000 +2647 18002 +2645 18004 +2643 18006 +2642 18008 +2640 18009 +2638 18011 +2636 18013 +2634 18015 +2632 18017 +2630 18019 +2628 18020 +2626 18022 +2624 18024 +2622 18026 +2620 18028 +2619 18029 +2617 18031 +2615 18033 +2613 18035 +2611 18037 +2609 18039 +2607 18040 +2605 18042 +2604 18044 +2602 18046 +2600 18048 +2599 18049 +2597 18051 +2595 18053 +2593 18055 +2591 18057 +2589 18058 +2587 18060 +2585 18062 +2583 18064 +2581 18066 +2580 18068 +2578 18069 +2576 18071 +2574 18073 +2572 18075 +2570 18077 +2569 18078 +2567 18080 +2565 18082 +2563 18084 +2562 18085 +2560 18087 +2558 18089 +2556 18091 +2555 18093 +2553 18094 +2551 18096 +2549 18098 +2547 18100 +2545 18101 +2543 18103 +2541 18105 +2539 18107 +2537 18108 +2535 18110 +2534 18112 +2532 18114 +2530 18115 +2528 18117 +2526 18119 +2524 18121 +2522 18122 +2520 18124 +2518 18126 +2517 18128 +2515 18129 +2513 18131 +2511 18133 +2509 18135 +2507 18136 +2505 18138 +2503 18140 +2502 18142 +2500 18143 +2498 18145 +2496 18147 +2494 18148 +2492 18150 +2490 18152 +2488 18154 +2486 18155 +2485 18157 +2483 18159 +2481 18160 +2479 18162 +2477 18164 +2475 18166 +2473 18167 +2471 18169 +2469 18171 +2468 18172 +2466 18174 +2464 18176 +2462 18177 +2460 18179 +2458 18181 +2456 18183 +2454 18184 +2452 18186 +2450 18188 +2448 18189 +2446 18191 +2445 18193 +2443 18194 +2441 18196 +2439 18198 +2437 18199 +2435 18201 +2433 18203 +2431 18204 +2429 18206 +2427 18208 +2425 18210 +2423 18212 +2421 18213 +2419 18215 +2417 18216 +2415 18218 +2413 18220 +2411 18221 +2410 18223 +2408 18225 +2406 18226 +2404 18228 +2402 18230 +2400 18231 +2398 18233 +2396 18235 +2394 18236 +2393 18238 +2391 18239 +2389 18241 +2387 18243 +2385 18244 +2383 18246 +2381 18248 +2379 18249 +2378 18251 +2376 18253 +2374 18254 +2372 18256 +2371 18257 +2369 18259 +2367 18261 +2365 18262 +2363 18264 +2361 18265 +2359 18267 +2358 18269 +2356 18270 +2354 18272 +2352 18273 +2350 18275 +2348 18277 +2346 18278 +2345 18280 +2343 18281 +2341 18283 +2339 18285 +2337 18286 +2335 18288 +2334 18289 +2332 18291 +2330 18293 +2328 18294 +2326 18296 +2325 18297 +2323 18299 +2321 18300 +2319 18302 +2317 18303 +2315 18305 +2314 18307 +2312 18308 +2310 18310 +2308 18311 +2306 18313 +2305 18314 +2303 18316 +2301 18317 +2300 18319 +2298 18320 +2296 18322 +2295 18324 +2293 18325 +2291 18327 +2289 18328 +2287 18330 +2286 18331 +2284 18333 +2282 18334 +2280 18336 +2278 18337 +2277 18339 +2275 18340 +2273 18342 +2271 18343 +2270 18345 +2268 18346 +2266 18348 +2264 18349 +2262 18351 +2261 18352 +2259 18354 +2257 18355 +2255 18357 +2254 18358 +2252 18360 +2250 18361 +2249 18363 +2247 18364 +2245 18365 +2243 18367 +2242 18368 +2240 18370 +2238 18371 +2236 18373 +2235 18374 +2233 18376 +2231 18377 +2230 18379 +2228 18380 +2226 18382 +2224 18383 +2223 18384 +2221 18386 +2219 18387 +2217 18389 +2216 18390 +2214 18392 +2212 18393 +2211 18394 +2209 18396 +2207 18397 +2205 18399 +2204 18400 +2202 18402 +2200 18403 +2199 18404 +2197 18406 +2195 18407 +2194 18409 +2192 18410 +2191 18411 +2189 18413 +2188 18414 +2186 18416 +2185 18417 +2183 18418 +2181 18420 +2180 18421 +2178 18423 +2177 18424 +2175 18425 +2173 18427 +2172 18428 +2170 18430 +2169 18431 +2167 18432 +2165 18434 +2164 18435 +2162 18436 +2161 18438 +2159 18439 +2157 18440 +2156 18442 +2154 18443 +2153 18445 +2151 18446 +2149 18447 +2148 18449 +2146 18450 +2145 18451 +2143 18453 +2142 18454 +2141 18455 +2139 18457 +2137 18458 +2136 18459 +2134 18461 +2132 18462 +2131 18463 +2129 18465 +2128 18466 +2126 18467 +2125 18469 +2123 18470 +2121 18471 +2120 18473 +2119 18479 +2117 18480 +2116 18481 +2114 18483 +2113 18484 +2111 18485 +2110 18487 +2108 18488 +2107 18489 +2105 18491 +2104 18492 +2102 18494 +2101 18495 +2099 18496 +2098 18498 +2096 18499 +2095 18500 +2093 18502 +2092 18503 +2090 18504 +2089 18506 +2087 18507 +2086 18508 +2084 18510 +2083 18511 +2082 18512 +2080 18513 +2078 18515 +2077 18516 +2076 18517 +2075 18519 +2073 18520 +2072 18521 +2070 18523 +2069 18524 +2067 18525 +2066 18527 +2065 18528 +2063 18529 +2062 18535 +2061 18536 +2059 18538 +2058 18539 +2057 18540 +2055 18542 +2053 18543 +2052 18544 +2051 18546 +2049 18547 +2048 18548 +2046 18550 +2045 18551 +2043 18552 +2041 18554 +2040 18555 +2039 18556 +2037 18558 +2036 18559 +2034 18560 +2033 18562 +2031 18563 +2030 18564 +2029 18565 +2028 18567 +2026 18568 +2025 18569 +2024 18571 +2022 18572 +2021 18573 +2019 18575 +2018 18576 +2017 18577 +2016 18578 +2014 18580 +2013 18581 +2012 18582 +2011 18584 +2009 18585 +2008 18586 +2006 18587 +2005 18589 +2004 18590 +2003 18591 +2001 18593 +2000 18594 +1998 18595 +1997 18596 +1995 18598 +1994 18599 +1993 18600 +1992 18601 +1990 18603 +1988 18604 +1987 18605 +1985 18606 +1984 18608 +1982 18609 +1981 18610 +1980 18611 +1979 18613 +1977 18614 +1976 18615 +1975 18616 +1973 18618 +1972 18619 +1970 18620 +1969 18621 +1968 18623 +1966 18624 +1965 18625 +1964 18626 +1962 18628 +1961 18629 +1959 18630 +1957 18631 +1956 18633 +1955 18634 +1954 18635 +1952 18636 +1951 18637 +1950 18639 +1948 18640 +1947 18641 +1945 18642 +1944 18644 +1943 18645 +1941 18646 +1940 18647 +1938 18648 +1937 18650 +1936 18651 +1935 18652 +1933 18653 +1932 18654 +1930 18656 +1929 18657 +1928 18658 +1927 18659 +1926 18660 +1924 18662 +1923 18663 +1922 18664 +1920 18665 +1919 18666 +1917 18668 +1916 18669 +1914 18670 +1913 18671 +1912 18672 +1911 18673 +1909 18675 +1908 18676 +1907 18677 +1905 18678 +1904 18679 +1902 18681 +1901 18682 +1899 18683 +1898 18684 +1897 18685 +1895 18686 +1894 18688 +1893 18689 +1891 18690 +1890 18691 +1888 18692 +1887 18693 +1886 18695 +1885 18696 +1883 18697 +1882 18698 +1881 18699 +1880 18700 +1879 18701 +1877 18703 +1876 18704 +1874 18705 +1873 18706 +1872 18707 +1871 18708 +1869 18710 +1868 18711 +1867 18712 +1866 18713 +1864 18714 +1863 18715 +1862 18716 +1860 18718 +1859 18719 +1857 18720 +1856 18721 +1855 18722 +1853 18723 +1852 18724 +1851 18725 +1849 18727 +1848 18728 +1847 18729 +1846 18730 +1844 18731 +1843 18732 +1842 18733 +1841 18734 +1840 18736 +1838 18737 +1837 18738 +1836 18739 +1835 18740 +1834 18741 +1833 18742 +1831 18743 +1830 18745 +1828 18746 +1827 18747 +1826 18748 +1824 18749 +1823 18750 +1822 18751 +1820 18752 +1819 18753 +1818 18755 +1817 18756 +1815 18757 +1814 18758 +1813 18759 +1812 18760 +1810 18761 +1809 18762 +1808 18763 +1807 18764 +1805 18766 +1804 18767 +1803 18768 +1802 18769 +1800 18770 +1799 18771 +1798 18772 +1796 18773 +1795 18774 +1793 18775 +1792 18776 +1791 18778 +1790 18779 +1789 18780 +1787 18781 +1786 18782 +1785 18783 +1783 18784 +1782 18785 +1781 18786 +1780 18787 +1779 18788 +1777 18789 +1776 18790 +1775 18792 +1773 18793 +1772 18794 +1771 18795 +1770 18796 +1768 18797 +1767 18798 +1766 18799 +1764 18800 +1763 18801 +1762 18802 +1761 18803 +1759 18804 +1758 18805 +1757 18806 +1756 18808 +1754 18809 +1753 18810 +1752 18811 +1750 18812 +1749 18813 +1748 18814 +1747 18815 +1746 18816 +1744 18817 +1743 18818 +1742 18819 +1741 18820 +1740 18821 +1738 18822 +1737 18823 +1736 18824 +1735 18825 +1734 18826 +1732 18828 +1731 18829 +1730 18830 +1728 18831 +1727 18832 +1726 18833 +1725 18834 +1724 18835 +1722 18836 +1721 18837 +1720 18838 +1718 18839 +1717 18840 +1716 18841 +1715 18842 +1714 18843 +1713 18845 +1712 18846 +1711 18847 +1710 18848 +1708 18849 +1707 18850 +1705 18851 +1704 18852 +1703 18853 +1702 18854 +1700 18855 +1699 18856 +1697 18857 +1696 18858 +1695 18859 +1694 18860 +1693 18861 +1692 18862 +1690 18863 +1689 18864 +1688 18865 +1687 18866 +1686 18867 +1685 18868 +1683 18869 +1682 18870 +1680 18871 +1679 18872 +1678 18873 +1677 18874 +1676 18875 +1675 18876 +1673 18877 +1672 18878 +1671 18879 +1669 18880 +1668 18881 +1667 18882 +1666 18883 +1665 18884 +1664 18885 +1662 18886 +1661 18887 +1660 18888 +1659 18889 +1657 18890 +1656 18891 +1655 18892 +1654 18893 +1652 18894 +1651 18895 +1650 18896 +1649 18897 +1647 18898 +1646 18899 +1645 18900 +1643 18901 +1642 18902 +1641 18903 +1640 18904 +1638 18905 +1637 18906 +1636 18907 +1635 18908 +1634 18909 +1632 18910 +1631 18911 +1630 18912 +1629 18913 +1627 18914 +1626 18915 +1625 18916 +1624 18917 +1623 18918 +1622 18919 +1620 18920 +1619 18921 +1618 18922 +1617 18923 +1616 18923 +1615 18924 +1614 18925 +1612 18926 +1611 18927 +1610 18928 +1608 18929 +1607 18930 +1606 18931 +1604 18932 +1603 18933 +1602 18934 +1601 18935 +1600 18936 +1599 18937 +1598 18938 +1596 18939 +1595 18940 +1594 18941 +1593 18942 +1591 18943 +1590 18944 +1589 18944 +1588 18945 +1587 18946 +1585 18947 +1584 18948 +1583 18949 +1582 18950 +1581 18951 +1580 18952 +1579 18953 +1577 18954 +1576 18955 +1575 18956 +1574 18957 +1572 18958 +1571 18959 +1570 18959 +1569 18960 +1568 18961 +1567 18962 +1565 18963 +1564 18964 +1563 18965 +1562 18966 +1561 18967 +1560 18968 +1558 18969 +1557 18970 +1556 18971 +1555 18972 +1554 18972 +1553 18973 +1552 18974 +1550 18975 +1549 18976 +1548 18977 +1547 18978 +1546 18979 +1545 18980 +1543 18981 +1542 18982 +1541 18982 +1539 18983 +1538 18984 +1537 18985 +1536 18986 +1534 18987 +1533 18988 +1532 18989 +1531 18990 +1530 18991 +1529 18992 +1528 18993 +1527 18994 +1525 18994 +1524 18995 +1523 18996 +1521 18997 +1520 18998 +1519 18999 +1517 19000 +1516 19001 +1515 19002 +1514 19002 +1513 19003 +1511 19004 +1510 19005 +1509 19006 +1508 19007 +1507 19008 +1506 19009 +1505 19010 +1504 19010 +1503 19011 +1502 19012 +1501 19013 +1499 19014 +1498 19015 +1497 19016 +1496 19017 +1495 19017 +1494 19018 +1493 19019 +1492 19020 +1491 19021 +1490 19022 +1489 19023 +1487 19024 +1486 19024 +1485 19025 +1484 19026 +1483 19027 +1481 19028 +1480 19029 +1479 19030 +1478 19030 +1476 19031 +1475 19032 +1474 19033 +1473 19034 +1472 19035 +1471 19035 +1470 19036 +1469 19037 +1467 19038 +1466 19039 +1465 19040 +1464 19041 +1463 19041 +1462 19042 +1460 19043 +1459 19044 +1458 19045 +1457 19046 +1456 19046 +1455 19047 +1454 19048 +1453 19049 +1451 19050 +1450 19051 +1449 19051 +1448 19052 +1447 19053 +1446 19054 +1445 19055 +1443 19056 +1442 19056 +1441 19057 +1439 19058 +1438 19059 +1437 19060 +1436 19061 +1435 19061 +1434 19062 +1432 19063 +1431 19064 +1430 19065 +1429 19065 +1428 19066 +1427 19067 +1425 19068 +1424 19069 +1423 19070 +1422 19070 +1421 19071 +1419 19072 +1418 19073 +1417 19074 +1416 19074 +1415 19075 +1414 19076 +1413 19077 +1412 19078 +1411 19078 +1410 19079 +1408 19080 +1407 19081 +1406 19082 +1405 19082 +1403 19083 +1402 19084 +1401 19085 +1400 19086 +1399 19086 +1397 19087 +1396 19088 +1395 19089 +1394 19090 +1393 19090 +1392 19091 +1391 19092 +1390 19093 +1389 19093 +1388 19094 +1386 19095 +1385 19096 +1384 19097 +1383 19097 +1382 19098 +1380 19099 +1379 19100 +1378 19100 +1377 19101 +1375 19102 +1374 19103 +1373 19104 +1372 19104 +1371 19105 +1370 19106 +1369 19107 +1367 19107 +1366 19108 +1365 19109 +1364 19110 +1363 19111 +1362 19111 +1360 19112 +1359 19113 +1358 19114 +1357 19114 +1356 19115 +1355 19116 +1353 19117 +1352 19117 +1351 19118 +1350 19119 +1349 19119 +1347 19120 +1346 19121 +1345 19122 +1344 19122 +1343 19123 +1342 19124 +1341 19125 +1340 19125 +1338 19126 +1337 19127 +1336 19128 +1335 19128 +1334 19129 +1333 19130 +1331 19131 +1330 19131 +1329 19132 +1328 19133 +1327 19134 +1326 19134 +1324 19135 +1323 19136 +1322 19137 +1321 19137 +1320 19138 +1319 19139 +1318 19139 +1316 19140 +1315 19141 +1314 19142 +1313 19142 +1312 19143 +1311 19144 +1310 19145 +1309 19145 +1308 19146 +1306 19147 +1305 19147 +1304 19148 +1303 19149 +1302 19149 +1301 19150 +1300 19151 +1299 19152 +1297 19152 +1296 19153 +1295 19154 +1294 19155 +1293 19155 +1292 19156 +1291 19157 +1290 19157 +1289 19158 +1288 19159 +1287 19160 +1286 19160 +1285 19161 +1284 19162 +1283 19163 +1282 19163 +1281 19164 +1280 19165 +1278 19165 +1277 19166 +1276 19167 +1274 19168 +1273 19168 +1272 19169 +1271 19170 +1270 19170 +1269 19171 +1268 19172 +1267 19173 +1265 19173 +1264 19174 +1263 19175 +1262 19175 +1261 19176 +1260 19177 +1259 19178 +1258 19178 +1257 19179 +1256 19180 +1255 19180 +1254 19181 +1253 19182 +1252 19183 +1251 19183 +1250 19184 +1248 19185 +1247 19185 +1246 19186 +1245 19187 +1244 19187 +1243 19188 +1242 19189 +1241 19190 +1240 19190 +1239 19191 +1238 19191 +1237 19192 +1236 19193 +1235 19193 +1234 19194 +1233 19195 +1232 19195 +1231 19196 +1230 19197 +1229 19198 +1228 19198 +1227 19199 +1226 19200 +1225 19200 +1224 19201 +1223 19202 +1222 19202 +1221 19203 +1220 19204 +1219 19205 +1218 19205 +1217 19206 +1216 19207 +1215 19207 +1214 19208 +1213 19209 +1212 19209 +1211 19210 +1210 19211 +1210 19211 +1209 19212 +1208 19213 +1207 19214 +1206 19214 +1205 19215 +1204 19216 +1203 19216 +1202 19217 +1200 19218 +1199 19218 +1198 19219 +1198 19220 +1197 19220 +1196 19221 +1195 19222 +1194 19222 +1193 19223 +1192 19224 +1191 19224 +1190 19225 +1190 19226 +1189 19226 +1188 19227 +1187 19228 +1186 19228 +1185 19229 +1184 19230 +1183 19230 +1182 19231 +1181 19232 +1180 19232 +1179 19233 +1178 19234 +1177 19234 +1176 19235 +1175 19236 +1175 19236 +1173 19237 +1172 19238 +1172 19238 +1171 19239 +1170 19240 +1169 19240 +1168 19241 +1167 19242 +1167 19242 +1166 19243 +1164 19244 +1164 19244 +1163 19245 +1162 19246 +1161 19246 +1160 19247 +1159 19248 +1158 19248 +1157 19249 +1156 19250 +1155 19250 +1154 19251 +1154 19252 +1153 19252 +1152 19253 +1151 19254 +1150 19254 +1149 19255 +1149 19255 +1148 19256 +1147 19257 +1146 19257 +1145 19258 +1145 19259 +1144 19259 +1143 19260 +1142 19261 +1141 19261 +1140 19262 +1140 19263 +1139 19263 +1138 19264 +1137 19265 +1136 19265 +1135 19266 +1134 19267 +1133 19267 +1133 19268 +1132 19269 +1131 19269 +1130 19270 +1130 19271 +1129 19271 +1128 19272 +1127 19273 +1126 19273 +1126 19274 +1125 19275 +1124 19275 +1123 19276 +1123 19277 +1122 19277 +1121 19278 +1120 19279 +1119 19279 +1119 19280 +1118 19281 +1117 19281 +1116 19282 +1116 19283 +1115 19283 +1114 19284 +1113 19285 +1112 19285 +1112 19286 +1111 19287 +1110 19287 +1109 19288 +1109 19288 +1107 19289 +1106 19290 +1106 19290 +1105 19291 +1104 19292 +1104 19292 +1102 19293 +1102 19294 +1101 19294 +1100 19295 +1099 19296 +1099 19296 +1098 19297 +1097 19298 +1097 19298 +1096 19299 +1095 19300 +1094 19300 +1093 19301 +1093 19302 +1092 19302 +1091 19303 +1090 19304 +1089 19304 +1088 19305 +1088 19306 +1087 19306 +1086 19307 +1085 19308 +1084 19308 +1083 19309 +1083 19309 +1082 19310 +1081 19311 +1080 19311 +1080 19312 +1079 19312 +1078 19313 +1077 19314 +1077 19314 +1076 19315 +1075 19316 +1075 19316 +1074 19317 +1073 19318 +1072 19318 +1071 19319 +1070 19319 +1069 19320 +1069 19321 +1068 19321 +1067 19322 +1066 19322 +1065 19323 +1065 19323 +1064 19324 +1064 19324 +1063 19325 +1062 19325 +1061 19326 +1060 19327 +1060 19327 +1059 19328 +1058 19329 +1057 19329 +1057 19330 +1056 19331 +1055 19331 +1054 19332 +1054 19333 +1053 19333 +1052 19334 +1051 19335 +1050 19335 +1049 19336 +1048 19337 +1047 19338 +1047 19338 +1046 19339 +1046 19340 +1045 19340 +1044 19341 +1043 19342 +1042 19342 +1041 19343 +1040 19344 +1039 19344 +1039 19345 +1038 19346 +1037 19346 +1037 19347 +1036 19348 +1035 19348 +1034 19349 +1034 19350 +1033 19350 +1032 19351 +1031 19352 +1030 19352 +1030 19353 +1029 19354 +1028 19354 +1028 19355 +1027 19356 +1026 19356 +1026 19357 +1025 19358 +1024 19358 +1023 19359 +1022 19360 +1021 19360 +1020 19361 +1020 19362 +1019 19362 +1018 19363 +1017 19364 +1016 19364 +1015 19365 +1014 19366 +1013 19366 +1012 19367 +1012 19368 +1011 19368 +1010 19369 +1009 19370 +1008 19370 +1007 19371 +1006 19372 +1005 19372 +1004 19373 +1004 19374 +1003 19374 +1002 19375 +1001 19376 +1000 19376 +1000 19377 +999 19378 +998 19378 +998 19379 +997 19380 +996 19380 +995 19381 +994 19382 +994 19382 +993 19383 +992 19384 +991 19384 +991 19385 +990 19386 +989 19386 +988 19387 +988 19388 +987 19388 +986 19389 +985 19390 +984 19390 +984 19391 +983 19392 +983 19392 +982 19393 +981 19393 +980 19394 +979 19395 +978 19396 +977 19396 +977 19397 +976 19398 +976 19398 +975 19399 +974 19400 +974 19400 +973 19401 +972 19402 +971 19402 +971 19403 +970 19404 +969 19404 +969 19405 +968 19406 +967 19406 +966 19407 +965 19408 +964 19408 +963 19409 +962 19410 +961 19410 +961 19411 +960 19412 +960 19412 +959 19413 +958 19414 +957 19414 +957 19415 +956 19416 +955 19416 +954 19417 +954 19418 +953 19418 +953 19419 +952 19420 +952 19420 +951 19421 +950 19422 +949 19422 +949 19423 +948 19423 +948 19424 +947 19425 +947 19425 +946 19426 +945 19427 +944 19427 +943 19428 +943 19429 +942 19429 +941 19430 +940 19431 +939 19431 +939 19432 +938 19433 +938 19433 +937 19434 +936 19434 +935 19435 +934 19436 +934 19436 +933 19437 +932 19437 +931 19438 +931 19439 +930 19439 +930 19440 +929 19441 +929 19441 +928 19442 +927 19443 +927 19443 +926 19444 +925 19445 +925 19445 +924 19446 +923 19447 +922 19447 +922 19448 +921 19449 +920 19449 +919 19450 +918 19450 +918 19451 +917 19452 +916 19452 +915 19453 +914 19454 +913 19454 +913 19455 +912 19456 +912 19456 +911 19457 +911 19458 +910 19458 +909 19459 +908 19460 +908 19460 +907 19461 +907 19462 +906 19462 +905 19463 +905 19464 +904 19464 +903 19465 +902 19466 +902 19466 +900 19467 +900 19468 +899 19468 +899 19469 +898 19470 +897 19470 +896 19471 +896 19472 +895 19472 +894 19473 +894 19474 +893 19474 +893 19475 +892 19476 +891 19476 +891 19477 +890 19478 +889 19478 +888 19479 +888 19479 +887 19480 +886 19481 +886 19481 +885 19482 +884 19483 +884 19483 +883 19484 +883 19485 +882 19485 +881 19486 +881 19486 +880 19487 +879 19488 +879 19488 +878 19489 +877 19490 +876 19490 +876 19491 +875 19492 +874 19492 +874 19493 +873 19494 +873 19494 +872 19495 +872 19496 +871 19496 +870 19497 +870 19498 +869 19498 +868 19499 +868 19500 +867 19500 +866 19501 +865 19501 +864 19502 +864 19503 +863 19503 +862 19504 +862 19505 +861 19505 +861 19506 +860 19507 +860 19507 +859 19508 +858 19509 +857 19509 +856 19510 +856 19511 +856 19511 +855 19512 +854 19512 +854 19513 +853 19514 +852 19514 +851 19515 +851 19516 +850 19516 +849 19517 +848 19518 +847 19518 +847 19519 +847 19520 +846 19520 +845 19521 +845 19521 +844 19522 +843 19523 +843 19523 +842 19524 +842 19525 +841 19525 +840 19526 +839 19526 +839 19527 +838 19528 +837 19528 +837 19529 +836 19530 +835 19530 +835 19531 +834 19531 +834 19532 +833 19533 +832 19533 +832 19534 +831 19535 +830 19535 +829 19536 +829 19536 +829 19537 +828 19538 +828 19538 +827 19539 +826 19539 +825 19540 +825 19540 +824 19541 +823 19542 +823 19542 +822 19543 +822 19543 +821 19544 +820 19545 +820 19545 +819 19546 +819 19547 +819 19547 +818 19548 +817 19549 +816 19549 +816 19550 +815 19550 +815 19551 +814 19552 +814 19552 +813 19553 +813 19554 +812 19554 +811 19555 +811 19555 +810 19556 +810 19557 +809 19557 +809 19558 +808 19559 +807 19559 +807 19560 +806 19560 +805 19561 +805 19562 +804 19562 +804 19563 +803 19563 +803 19564 +802 19564 +801 19565 +800 19565 +800 19566 +799 19566 +799 19567 +798 19567 +798 19568 +797 19569 +797 19569 +796 19570 +796 19570 +795 19571 +794 19572 +793 19572 +793 19573 +792 19574 +791 19574 +791 19574 +790 19575 +790 19576 +789 19576 +789 19577 +788 19578 +788 19578 +787 19579 +787 19580 +786 19580 +785 19581 +785 19581 +784 19582 +783 19582 +783 19583 +782 19584 +782 19584 +781 19585 +780 19585 +780 19586 +779 19587 +778 19587 +778 19588 +777 19588 +776 19589 +776 19590 +775 19590 +775 19591 +774 19592 +774 19592 +773 19593 +773 19593 +772 19594 +771 19595 +771 19595 +770 19596 +769 19596 +768 19597 +768 19598 +767 19598 +766 19599 +766 19599 +765 19600 +765 19600 +764 19601 +764 19602 +763 19602 +763 19602 +762 19603 +761 19604 +761 19604 +761 19605 +760 19606 +760 19606 +759 19607 +758 19607 +758 19608 +757 19609 +756 19609 +756 19610 +755 19610 +755 19611 +755 19612 +754 19612 +754 19613 +754 19613 +753 19614 +752 19615 +752 19615 +751 19616 +751 19616 +750 19617 +749 19617 +749 19618 +748 19619 +748 19619 +747 19620 +746 19620 +746 19621 +745 19622 +744 19622 +744 19623 +743 19623 +742 19624 +742 19625 +741 19625 +740 19626 +740 19626 +739 19627 +739 19627 +738 19628 +737 19629 +736 19629 +736 19630 +735 19630 +735 19631 +734 19632 +734 19632 +733 19633 +733 19633 +732 19634 +732 19634 +731 19635 +730 19636 +730 19636 +729 19637 +729 19637 +728 19638 +727 19638 +727 19639 +726 19640 +726 19640 +726 19641 +725 19641 +724 19642 +724 19642 +723 19643 +722 19643 +722 19644 +721 19645 +720 19645 +720 19646 +720 19646 +719 19647 +718 19647 +718 19648 +717 19648 +717 19649 +716 19650 +716 19650 +715 19651 +715 19651 +715 19652 +714 19652 +714 19653 +713 19653 +712 19654 +712 19654 +711 19655 +711 19656 +710 19656 +710 19657 +709 19657 +709 19658 +708 19658 +708 19659 +707 19659 +707 19660 +706 19660 +705 19661 +705 19662 +704 19662 +704 19663 +703 19663 +703 19664 +702 19664 +702 19664 +701 19665 +701 19665 +700 19666 +699 19667 +699 19667 +699 19668 +698 19668 +698 19669 +697 19669 +697 19670 +696 19670 +695 19671 +695 19671 +694 19672 +694 19672 +693 19673 +693 19673 +692 19674 +691 19674 +691 19675 +690 19675 +690 19676 +689 19677 +689 19677 +688 19678 +688 19678 +687 19678 +687 19679 +687 19679 +686 19680 +686 19680 +685 19681 +684 19681 +684 19682 +684 19682 +683 19683 +683 19683 +682 19683 +681 19684 +681 19684 +680 19685 +680 19685 +679 19686 +679 19686 +678 19687 +677 19687 +677 19688 +676 19688 +676 19689 +675 19689 +675 19690 +675 19690 +674 19691 +674 19691 +673 19692 +673 19693 +673 19693 +672 19694 +672 19694 +671 19695 +671 19695 +670 19696 +670 19696 +670 19697 +669 19697 +668 19698 +668 19698 +668 19699 +667 19699 +667 19700 +666 19700 +666 19701 +665 19701 +665 19702 +664 19702 +664 19703 +664 19703 +663 19704 +663 19704 +662 19705 +662 19705 +661 19706 +661 19706 +661 19706 +660 19707 +660 19707 +659 19708 +659 19708 +658 19709 +657 19709 +657 19710 +656 19710 +656 19711 +655 19712 +655 19712 +654 19712 +654 19713 +653 19713 +653 19714 +652 19714 +652 19715 +651 19715 +651 19716 +650 19716 +650 19717 +649 19717 +648 19718 +648 19718 +647 19719 +647 19719 +646 19719 +646 19720 +645 19720 +645 19721 +645 19721 +644 19721 +644 19722 +643 19722 +643 19723 +643 19723 +642 19723 +642 19723 +641 19724 +641 19724 +640 19725 +640 19725 +639 19726 +639 19726 +638 19727 +638 19727 +637 19728 +637 19728 +637 19729 +636 19729 +636 19730 +635 19730 +635 19731 +634 19731 +634 19731 +633 19731 +633 19732 +633 19732 +632 19733 +632 19733 +631 19734 +631 19734 +630 19734 +630 19735 +630 19735 +629 19736 +629 19736 +628 19737 +628 19737 +627 19737 +627 19738 +626 19738 +626 19739 +625 19739 +625 19739 +624 19739 +624 19740 +623 19740 +623 19741 +623 19741 +622 19741 +622 19742 +621 19742 +621 19743 +621 19743 +621 19744 +620 19744 +620 19745 +620 19745 +619 19746 +619 19746 +618 19746 +618 19747 +617 19747 +617 19747 +616 19748 +616 19748 +615 19749 +615 19749 +614 19750 +614 19750 +613 19750 +613 19751 +612 19751 +612 19752 +612 19752 +611 19753 +611 19753 +611 19754 +610 19754 +610 19755 +609 19755 +609 19756 +608 19756 +608 19757 +607 19757 +607 19757 +606 19758 +606 19758 +606 19759 +605 19759 +605 19760 +604 19760 +604 19760 +603 19761 +603 19761 +603 19762 +603 19762 +602 19763 +602 19763 +602 19764 +601 19764 +601 19764 +600 19765 +600 19765 +599 19766 +599 19766 +599 19767 +598 19767 +598 19767 +597 19768 +597 19768 +596 19768 +596 19769 +596 19769 +595 19770 +595 19770 +595 19771 +594 19771 +594 19772 +593 19772 +593 19772 +592 19773 +592 19773 +592 19774 +591 19774 +591 19775 +590 19775 +590 19776 +590 19776 +589 19776 +589 19777 +588 19777 +588 19778 +587 19778 +587 19779 +587 19779 +586 19779 +586 19780 +585 19780 +585 19781 +584 19781 +584 19781 +584 19782 +583 19782 +583 19783 +583 19783 +582 19784 +582 19784 +581 19785 +581 19785 +581 19785 +580 19786 +580 19786 +580 19787 +579 19787 +579 19788 +578 19788 +578 19789 +577 19789 +577 19789 +577 19790 +576 19790 +576 19791 +576 19791 +575 19792 +575 19792 +574 19792 +574 19793 +574 19793 +573 19794 +573 19794 +573 19794 +572 19795 +572 19795 +571 19795 +571 19796 +571 19796 +570 19796 +570 19797 +570 19797 +569 19797 +569 19797 +568 19798 +568 19798 +568 19799 +567 19799 +567 19799 +566 19800 +566 19800 +566 19800 +565 19801 +565 19801 +564 19802 +564 19802 +564 19803 +563 19803 +563 19803 +562 19804 +562 19804 +562 19805 +561 19805 +561 19806 +560 19806 +560 19806 +559 19807 +559 19807 +559 19808 +559 19808 +559 19809 +558 19809 +558 19810 +557 19810 +557 19811 +557 19811 +556 19811 +556 19811 +555 19812 +555 19812 +555 19813 +554 19813 +554 19813 +554 19814 +553 19815 +553 19815 +552 19816 +552 19816 +552 19816 +551 19816 +551 19817 +551 19817 +550 19818 +550 19819 +550 19819 +549 19820 +549 19820 +549 19821 +548 19822 +548 19822 +547 19823 +547 19823 +547 19824 +546 19824 +546 19824 +546 19825 +545 19826 +545 19826 +544 19827 +544 19827 +544 19828 +543 19828 +543 19829 +542 19829 +542 19829 +542 19830 +541 19830 +541 19831 +541 19831 +541 19832 +540 19832 +540 19833 +539 19833 +539 19833 +539 19834 +538 19834 +538 19835 +538 19835 +537 19836 +537 19836 +537 19837 +536 19837 +536 19837 +535 19838 +535 19838 +535 19839 +534 19839 +534 19839 +534 19840 +533 19840 +533 19840 +532 19841 +532 19841 +532 19842 +532 19842 +531 19842 +531 19843 +531 19843 +530 19843 +530 19844 +530 19844 +529 19845 +529 19845 +528 19845 +528 19846 +528 19846 +527 19847 +527 19847 +527 19848 +526 19848 +526 19849 +526 19849 +525 19850 +525 19850 +524 19851 +524 19851 +524 19852 +523 19852 +523 19853 +523 19853 +523 19854 +522 19854 +522 19854 +522 19855 +521 19855 +521 19856 +521 19856 +521 19856 +520 19857 +520 19857 +520 19858 +520 19858 +519 19859 +519 19859 +519 19860 +518 19860 +518 19860 +518 19861 +518 19861 +517 19862 +517 19862 +516 19862 +516 19863 +516 19863 +515 19864 +515 19864 +515 19864 +514 19865 +514 19865 +514 19866 +513 19866 +513 19867 +513 19867 +513 19868 +512 19868 +512 19869 +512 19869 +511 19870 +511 19871 +511 19871 +510 19872 +510 19872 +510 19873 +510 19873 +509 19874 +509 19874 +509 19875 +508 19875 +508 19876 +508 19876 +507 19877 +507 19877 +507 19878 +506 19878 +506 19879 +506 19879 +505 19880 +505 19880 +505 19881 +505 19881 +504 19882 +504 19882 +504 19882 +504 19883 +503 19883 +503 19884 +503 19884 +502 19885 +502 19885 +502 19886 +502 19886 +501 19887 +501 19887 +501 19888 +500 19888 +500 19889 +500 19889 +499 19890 +499 19890 +499 19891 +498 19891 +498 19891 +498 19892 +497 19892 +497 19893 +497 19893 +497 19893 +496 19894 +496 19895 +496 19895 +495 19896 +495 19896 +495 19896 +494 19897 +494 19897 +494 19898 +493 19898 +493 19899 +493 19899 +493 19900 +492 19900 +492 19901 +492 19902 +491 19902 +491 19903 +491 19903 +490 19904 +490 19904 +490 19905 +490 19905 +489 19906 +489 19906 +489 19906 +488 19907 +488 19907 +488 19908 +487 19908 +487 19908 +487 19909 +487 19909 +486 19910 +486 19910 +486 19911 +485 19911 +485 19912 +485 19912 +484 19913 +484 19913 +484 19914 +484 19914 +483 19915 +483 19915 +483 19915 +482 19916 +482 19916 +482 19916 +481 19917 +481 19917 +481 19918 +481 19918 +480 19918 +480 19919 +480 19919 +479 19920 +479 19920 +479 19921 +479 19921 +478 19921 +478 19922 +478 19922 +478 19923 +477 19923 +477 19924 +477 19924 +477 19925 +476 19925 +476 19926 +476 19926 +475 19927 +475 19927 +475 19928 +475 19928 +474 19929 +474 19929 +474 19930 +473 19930 +473 19931 +473 19931 +473 19932 +472 19932 +472 19932 +472 19933 +471 19933 +471 19934 +471 19934 +471 19934 +470 19935 +470 19936 +470 19936 +469 19937 +469 19937 +469 19938 +469 19938 +468 19938 +469 19939 +468 19939 +468 19940 +468 19940 +468 19940 +467 19941 +467 19942 +467 19942 +466 19943 +466 19943 +466 19943 +466 19944 +465 19944 +465 19945 +465 19945 +464 19946 +464 19946 +464 19947 +464 19947 +463 19948 +463 19948 +463 19949 +463 19949 +462 19949 +462 19950 +462 19950 +461 19951 +461 19951 +461 19951 +461 19952 +460 19952 +460 19953 +460 19953 +459 19953 +459 19954 +459 19954 +459 19955 +458 19955 +458 19956 +458 19956 +458 19956 +457 19957 +457 19957 +457 19958 +456 19958 +456 19959 +456 19959 +456 19960 +455 19960 +455 19960 +455 19961 +455 19961 +455 19961 +454 19962 +454 19962 +454 19963 +454 19963 +453 19964 +453 19964 +453 19965 +452 19965 +452 19966 +452 19966 +452 19967 +451 19967 +451 19967 +451 19968 +451 19968 +450 19969 +450 19969 +450 19969 +450 19970 +449 19970 +449 19971 +449 19971 +449 19972 +448 19972 +448 19973 +448 19973 +448 19974 +447 19974 +447 19975 +447 19975 +446 19976 +446 19976 +446 19977 +446 19977 +445 19977 +445 19978 +445 19978 +445 19979 +444 19979 +444 19980 +444 19980 +444 19980 +443 19981 +443 19981 +443 19981 +443 19982 +442 19983 +442 19983 +442 19983 +442 19984 +441 19984 +441 19985 +441 19985 +441 19985 +440 19986 +440 19986 +440 19987 +440 19987 +439 19988 +439 19988 +439 19989 +439 19989 +438 19990 +438 19991 +438 19991 +438 19992 +437 19993 +437 19993 +437 19994 +437 19994 +436 19994 +436 19995 +436 19995 +436 19996 +435 19996 +435 19997 +435 19997 +435 19997 +434 19998 +434 19998 +434 19999 +434 19999 +434 19999 +433 20000 +433 20001 +433 20001 +433 20002 +432 20002 +432 20003 +432 20003 +432 20004 +431 20004 +431 20004 +431 20005 +431 20005 +430 20006 +430 20007 +430 20007 +430 20008 +429 20008 +429 20009 +429 20009 +429 20010 +429 20010 +428 20010 +428 20011 +428 20012 +428 20012 +427 20012 +427 20013 +427 20013 +427 20013 +426 20014 +426 20014 +426 20015 +426 20015 +425 20016 +425 20016 +425 20017 +425 20017 +425 20018 +425 20018 +424 20019 +424 20019 +424 20020 +424 20020 +423 20021 +423 20021 +423 20022 +423 20022 +423 20022 +422 20023 +422 20023 +422 20024 +422 20024 +421 20025 +421 20025 +421 20025 +421 20026 +421 20026 +420 20027 +420 20027 +420 20028 +420 20028 +420 20028 +419 20029 +419 20029 +419 20030 +419 20030 +418 20031 +418 20031 +418 20031 +418 20032 +418 20032 +417 20033 +417 20033 +417 20033 +417 20034 +416 20034 +416 20035 +416 20035 +416 20036 +416 20036 +415 20036 +415 20037 +415 20037 +415 20038 +415 20038 +414 20039 +414 20039 +414 20039 +414 20040 +413 20040 +413 20041 +413 20041 +413 20041 +413 20042 +413 20042 +413 20042 +412 20043 +412 20043 +412 20043 +412 20044 +412 20044 +411 20044 +411 20045 +411 20045 +411 20045 +410 20046 +410 20046 +410 20046 +410 20047 +410 20047 +409 20047 +409 20048 +409 20048 +409 20049 +409 20049 +408 20049 +408 20050 +408 20050 +408 20050 +408 20051 +407 20051 +407 20052 +407 20052 +407 20052 +407 20052 +407 20053 +406 20053 +406 20054 +406 20054 +406 20054 +406 20054 +405 20055 +405 20055 +405 20056 +405 20056 +405 20056 +404 20056 +404 20057 +404 20057 +404 20057 +404 20058 +403 20058 +403 20059 +403 20059 +403 20059 +403 20060 +402 20060 +402 20060 +402 20061 +402 20061 +402 20061 +402 20062 +401 20062 +401 20062 +401 20063 +401 20063 +401 20063 +400 20064 +400 20064 +400 20064 +400 20065 +400 20065 +400 20065 +399 20066 +399 20066 +399 20066 +399 20067 +399 20067 +398 20067 +398 20067 +398 20068 +398 20068 +398 20068 +398 20069 +398 20069 +397 20069 +397 20070 +397 20070 +397 20070 +397 20071 +397 20071 +396 20071 +396 20071 +396 20072 +396 20072 +396 20073 +396 20073 +395 20073 +395 20074 +395 20074 +395 20075 +395 20075 +395 20075 +394 20075 +394 20076 +394 20076 +394 20077 +394 20077 +394 20078 +394 20078 +394 20078 +393 20079 +393 20079 +393 20079 +393 20079 +393 20080 +393 20080 +392 20080 +392 20080 +392 20081 +392 20081 +392 20081 +392 20082 +391 20082 +391 20082 +391 20082 +391 20083 +391 20083 +391 20083 +390 20083 +390 20084 +390 20084 +390 20084 +390 20084 +390 20085 +389 20085 +389 20085 +389 20086 +389 20086 +389 20086 +389 20086 +388 20087 +388 20087 +388 20087 +388 20087 +388 20088 +388 20088 +387 20088 +387 20088 +387 20088 +387 20089 +387 20089 +387 20090 +386 20090 +386 20090 +386 20090 +386 20091 +386 20091 +386 20091 +385 20091 +385 20092 +385 20092 +385 20092 +385 20093 +385 20093 +385 20093 +384 20093 +384 20094 +384 20094 +384 20094 +384 20095 +384 20095 +383 20095 +383 20095 +383 20096 +383 20096 +383 20096 +383 20097 +382 20097 +382 20097 +382 20097 +382 20098 +382 20098 +382 20098 +382 20099 +381 20099 +381 20099 +381 20099 +381 20100 +381 20100 +381 20101 +380 20101 +380 20101 +380 20101 +380 20102 +380 20102 +380 20102 +380 20103 +379 20103 +379 20103 +379 20103 +379 20103 +379 20104 +379 20104 +379 20104 +378 20105 +378 20105 +378 20105 +378 20105 +378 20106 +378 20106 +378 20106 +377 20107 +377 20107 +377 20107 +377 20107 +377 20108 +377 20108 +377 20108 +376 20109 +376 20109 +376 20109 +376 20109 +376 20110 +376 20111 +376 20111 +375 20112 +375 20112 +375 20112 +375 20112 +375 20113 +375 20113 +375 20113 +374 20114 +374 20114 +374 20114 +374 20114 +374 20114 +374 20115 +374 20115 +373 20115 +373 20115 +373 20116 +373 20116 +373 20117 +373 20117 +373 20117 +372 20118 +372 20118 +372 20118 +372 20118 +372 20119 +372 20119 +372 20119 +372 20119 +371 20119 +371 20120 +371 20120 +371 20120 +371 20120 +371 20121 +371 20121 +370 20121 +370 20121 +370 20121 +370 20122 +370 20122 +370 20122 +370 20123 +370 20123 +369 20123 +369 20123 +369 20124 +369 20124 +369 20124 +369 20124 +369 20125 +368 20125 +368 20125 +368 20125 +368 20125 +368 20125 +368 20126 +368 20126 +368 20126 +367 20127 +367 20127 +367 20127 +367 20127 +367 20127 +367 20127 +367 20128 +367 20128 +366 20129 +366 20129 +366 20129 +366 20129 +366 20129 +366 20130 +366 20130 +366 20130 +365 20130 +365 20131 +365 20131 +365 20131 +365 20131 +365 20132 +365 20132 +365 20132 +364 20132 +364 20133 +364 20133 +364 20133 +364 20133 +364 20133 +364 20134 +364 20134 +363 20134 +363 20134 +363 20135 +363 20135 +363 20135 +363 20136 +363 20136 +363 20136 +362 20136 +362 20136 +362 20137 +362 20137 +362 20137 +362 20137 +362 20138 +362 20138 +362 20138 +361 20138 +361 20138 +361 20138 +361 20138 +361 20139 +361 20139 +361 20139 +361 20139 +360 20140 +360 20140 +360 20140 +360 20140 +360 20141 +360 20141 +360 20141 +360 20141 +360 20142 +359 20142 +359 20142 +359 20142 +359 20142 +359 20143 +359 20143 +359 20143 +359 20143 +358 20144 +358 20144 +358 20144 +358 20145 +358 20145 +358 20146 +358 20146 +358 20146 +358 20146 +357 20147 +357 20147 +357 20147 +357 20147 +357 20148 +357 20148 +357 20148 +357 20148 +357 20149 +356 20149 +356 20149 +356 20149 +356 20150 +356 20150 +356 20150 +356 20150 +356 20151 +356 20151 +355 20151 +355 20151 +355 20151 +355 20152 +355 20152 +355 20152 +355 20152 +355 20152 +355 20152 +354 20153 +354 20153 +354 20153 +354 20154 +354 20154 +354 20154 +354 20154 +354 20155 +354 20155 +354 20155 +354 20155 +354 20155 +353 20155 +353 20156 +353 20156 +353 20156 +353 20157 +353 20157 +353 20157 +353 20157 +353 20157 +353 20157 +352 20158 +352 20158 +352 20158 +352 20158 +352 20158 +352 20159 +352 20159 +352 20159 +352 20159 +351 20160 +351 20160 +351 20160 +351 20160 +351 20161 +351 20161 +351 20161 +351 20161 +351 20161 +351 20162 +350 20162 +350 20162 +350 20162 +350 20162 +350 20163 +350 20163 +350 20163 +350 20163 +350 20163 +350 20164 +349 20164 +349 20164 +349 20164 +349 20164 +349 20164 +349 20165 +349 20165 +349 20165 +349 20166 +349 20166 +348 20166 +348 20166 +348 20167 +348 20167 +348 20167 +348 20168 +348 20168 +348 20168 +348 20168 +348 20168 +347 20169 +347 20169 +347 20169 +347 20170 +347 20170 +347 20170 +347 20170 +347 20171 +347 20171 +347 20171 +346 20171 +346 20172 +346 20172 +346 20172 +346 20172 +346 20172 +346 20172 +346 20172 +346 20173 +346 20173 +346 20173 +345 20173 +345 20174 +345 20174 +345 20174 +345 20174 +345 20174 +345 20174 +345 20175 +345 20175 +345 20175 +344 20176 +344 20176 +344 20176 +344 20176 +344 20176 +344 20177 +344 20177 +344 20177 +344 20177 +344 20178 +344 20178 +343 20178 +343 20178 +343 20179 +343 20179 +343 20179 +343 20179 +343 20179 +343 20180 +343 20180 +343 20180 +342 20181 +342 20181 +342 20181 +342 20181 +342 20181 +342 20181 +342 20182 +342 20182 +342 20182 +342 20182 +342 20182 +341 20182 +341 20182 +341 20182 +341 20183 +341 20183 +341 20183 +341 20183 +341 20183 +341 20183 +341 20183 +340 20183 +340 20184 +340 20184 +340 20184 +340 20184 +340 20184 +340 20184 +340 20185 +340 20185 +340 20185 +340 20185 +339 20185 +339 20185 +339 20185 +339 20186 +339 20186 +339 20186 +339 20186 +339 20186 +339 20186 +339 20186 +339 20186 +338 20186 +338 20187 +338 20187 +338 20187 +338 20187 +338 20187 +338 20187 +338 20187 +338 20188 +338 20188 +338 20188 +337 20188 +337 20188 +337 20189 +337 20189 +337 20189 +337 20189 +337 20189 +337 20189 +337 20190 +337 20190 +337 20190 +336 20190 +336 20190 +336 20190 +336 20190 +336 20191 +336 20191 +336 20191 +336 20191 +336 20191 +336 20191 +336 20191 +335 20191 +335 20191 +335 20192 +335 20192 +335 20192 +335 20192 +335 20192 +335 20192 +335 20192 +335 20193 +335 20193 +334 20193 +334 20193 +334 20193 +334 20194 +334 20194 +334 20194 +334 20194 +334 20194 +334 20195 +334 20195 +334 20195 +333 20195 +333 20195 +333 20195 +333 20196 +333 20196 +333 20196 +333 20196 +333 20196 +333 20196 +333 20196 +333 20197 +332 20197 +332 20197 +332 20198 +332 20198 +332 20198 +332 20198 +332 20198 +332 20198 +332 20198 +332 20199 +332 20199 +331 20199 +331 20199 +331 20199 +331 20199 +331 20200 +331 20200 +331 20200 +331 20200 +331 20200 +331 20200 +331 20200 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +330 20201 +329 20202 +329 20202 +329 20202 +329 20202 +329 20202 +329 20203 +329 20203 +329 20203 +329 20203 +329 20203 +329 20203 +328 20203 +328 20204 +328 20204 +328 20204 +327 20204 +327 20204 +327 20204 +327 20205 +327 20205 +327 20205 +327 20205 +327 20205 +327 20205 +327 20205 +326 20205 +326 20206 +326 20206 +326 20206 +326 20206 +326 20206 +326 20206 +326 20206 +326 20206 +326 20206 +326 20206 +325 20207 +325 20207 +325 20207 +325 20207 +325 20207 +325 20207 +325 20208 +325 20208 +325 20208 +325 20208 +324 20208 +324 20209 +324 20209 +324 20209 +324 20209 +324 20209 +324 20209 +324 20209 +324 20210 +324 20210 +323 20210 +323 20210 +323 20210 +323 20210 +323 20210 +323 20211 +323 20211 +323 20211 +323 20211 +323 20211 +323 20212 +322 20212 +322 20212 +322 20212 +322 20213 +322 20213 +322 20213 +322 20213 +322 20213 +322 20213 +322 20213 +321 20214 +321 20214 +321 20214 +321 20214 +321 20214 +321 20214 +321 20215 +321 20215 +321 20215 +321 20215 +320 20215 +320 20215 +320 20215 +320 20216 +320 20216 +320 20216 +320 20216 +320 20216 +320 20216 +320 20217 +319 20217 +319 20217 +319 20217 +319 20217 +319 20217 +319 20218 +319 20218 +319 20218 +319 20218 +319 20218 +319 20218 +318 20219 +318 20219 +318 20219 +318 20219 +318 20220 +318 20220 +318 20220 +318 20220 +318 20220 +318 20220 +318 20220 +318 20221 +318 20221 +318 20221 +317 20221 +317 20221 +317 20221 +317 20221 +317 20221 +317 20221 +317 20221 +317 20221 +317 20222 +317 20222 +316 20222 +316 20222 +316 20222 +316 20222 +316 20222 +316 20222 +316 20223 +316 20223 +316 20223 +315 20223 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20225 +315 20225 +315 20225 +314 20225 +314 20225 +314 20226 +314 20226 +314 20226 +314 20226 +314 20226 +314 20226 +314 20226 +314 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20227 +313 20227 +313 20227 +313 20227 +313 20227 +312 20227 +312 20227 +312 20227 +312 20227 +312 20228 +312 20228 +312 20228 +312 20228 +312 20228 +312 20228 +311 20229 +311 20229 +311 20229 +311 20229 +311 20229 +311 20229 +311 20229 +311 20229 +311 20229 +311 20229 +310 20229 +310 20229 +310 20229 +310 20230 +310 20230 +310 20230 +310 20230 +310 20230 +310 20230 +310 20230 +309 20230 +309 20231 +309 20231 +309 20231 +309 20231 +309 20231 +309 20231 +309 20231 +309 20232 +309 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20233 +307 20233 +307 20233 +307 20233 +307 20233 +307 20233 +307 20233 +307 20233 +307 20233 +307 20233 +307 20234 +306 20234 +306 20234 +306 20234 +306 20234 +306 20234 +306 20234 +306 20234 +306 20234 +306 20234 +306 20234 +305 20234 +305 20234 +305 20234 +305 20235 +305 20235 +305 20235 +305 20235 +305 20235 +305 20235 +305 20236 +304 20236 +304 20236 +304 20236 +304 20236 +304 20236 +304 20237 +304 20237 +304 20237 +304 20237 +304 20237 +303 20237 +303 20237 +303 20237 +303 20237 +303 20237 +303 20237 +303 20238 +303 20238 +303 20238 +303 20238 +302 20238 +302 20238 +302 20238 +302 20238 +302 20239 +302 20239 +302 20239 +302 20239 +302 20239 +302 20239 +301 20239 +301 20239 +301 20239 +301 20240 +301 20240 +301 20240 +301 20240 +301 20240 +301 20240 +301 20240 +300 20240 +300 20241 +300 20241 +300 20241 +300 20241 +300 20241 +300 20241 +300 20241 +300 20241 +300 20241 +299 20241 +299 20241 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20243 +298 20243 +298 20243 +298 20243 +298 20243 +298 20243 +298 20243 +298 20244 +298 20244 +298 20244 +298 20244 +297 20244 +297 20244 +297 20244 +297 20244 +297 20245 +297 20245 +297 20245 +297 20245 +297 20245 +297 20245 +296 20245 +296 20245 +296 20245 +296 20246 +296 20246 +296 20246 +296 20246 +296 20246 +296 20246 +296 20246 +295 20246 +295 20246 +295 20246 +295 20246 +295 20246 +294 20247 +294 20247 +294 20247 +294 20247 +294 20247 +294 20247 +294 20247 +294 20247 +294 20247 +294 20248 +293 20248 +293 20248 +293 20248 +293 20248 +293 20248 +293 20248 +293 20249 +293 20249 +293 20249 +292 20249 +292 20249 +292 20249 +292 20249 +292 20250 +292 20250 +292 20250 +292 20250 +292 20250 +292 20250 +291 20250 +291 20250 +291 20250 +291 20251 +291 20251 +291 20251 +291 20251 +291 20251 +291 20251 +291 20251 +290 20252 +290 20252 +290 20252 +290 20252 +290 20252 +290 20252 +290 20252 +290 20252 +290 20253 +290 20253 +289 20253 +289 20253 +289 20253 +289 20253 +289 20253 +289 20253 +289 20253 +289 20254 +289 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20256 +287 20256 +286 20256 +286 20256 +286 20256 +286 20256 +286 20256 +286 20257 +286 20257 +286 20257 +286 20257 +286 20257 +285 20257 +285 20257 +285 20257 +285 20257 +285 20257 +285 20258 +285 20258 +285 20258 +285 20258 +284 20258 +284 20258 +284 20258 +284 20258 +284 20259 +284 20259 +284 20259 +284 20259 +284 20259 +284 20259 +283 20259 +283 20259 +283 20259 +283 20259 +283 20260 +283 20260 +283 20260 +283 20260 +283 20260 +283 20260 +282 20260 +282 20260 +282 20260 +282 20261 +282 20261 +282 20261 +282 20261 +282 20261 +282 20261 +282 20261 +281 20261 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +280 20262 +280 20263 +280 20263 +280 20263 +280 20263 +280 20263 +280 20263 +280 20263 +280 20264 +280 20264 +279 20264 +279 20264 +279 20264 +279 20264 +279 20264 +279 20264 +279 20264 +279 20265 +279 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20266 +278 20266 +278 20266 +277 20266 +277 20266 +277 20266 +277 20266 +277 20266 +277 20267 +277 20267 +277 20267 +277 20267 +277 20267 +276 20267 +276 20267 +276 20268 +276 20268 +276 20268 +276 20268 +276 20268 +276 20268 +276 20268 +276 20268 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20272 +273 20272 +272 20272 +272 20272 +272 20272 +272 20272 +272 20272 +272 20272 +272 20272 +272 20273 +272 20273 +272 20273 +271 20273 +271 20273 +271 20273 +271 20273 +271 20274 +271 20274 +271 20274 +271 20274 +271 20274 +271 20274 +270 20274 +270 20274 +270 20274 +270 20275 +270 20275 +270 20275 +270 20275 +270 20275 +270 20275 +270 20275 +269 20275 +269 20275 +269 20276 +269 20276 +269 20276 +269 20276 +269 20276 +269 20276 +269 20276 +269 20276 +268 20277 +268 20277 +268 20277 +268 20277 +268 20277 +268 20277 +268 20277 +268 20277 +268 20277 +268 20278 +267 20278 +267 20278 +267 20278 +267 20278 +267 20278 +267 20278 +267 20278 +267 20278 +267 20278 +267 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20281 +265 20281 +265 20281 +264 20281 +264 20281 +264 20281 +264 20281 +264 20281 +264 20282 +264 20282 +264 20282 +264 20282 +264 20282 +263 20282 +263 20282 +263 20282 +263 20283 +263 20283 +263 20283 +263 20283 +263 20283 +263 20283 +263 20283 +262 20283 +262 20283 +262 20283 +262 20284 +262 20284 +262 20284 +262 20284 +262 20284 +262 20284 +262 20284 +261 20284 +261 20284 +261 20284 +261 20285 +261 20285 +261 20285 +261 20285 +261 20285 +261 20285 +261 20285 +260 20285 +260 20285 +260 20286 +260 20286 +260 20286 +260 20286 +260 20286 +260 20286 +260 20286 +260 20286 +259 20286 +259 20286 +259 20287 +259 20287 +259 20287 +259 20287 +259 20287 +259 20287 +259 20287 +259 20287 +258 20287 +258 20287 +258 20288 +258 20288 +258 20288 +258 20288 +258 20288 +258 20288 +258 20288 +258 20288 +257 20288 +257 20288 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +256 20289 +256 20289 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +255 20290 +255 20290 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +254 20291 +254 20291 +254 20291 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +253 20292 +253 20292 +253 20292 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +252 20293 +252 20293 +252 20294 +252 20294 +252 20294 +252 20294 +252 20294 +252 20294 +252 20294 +252 20294 +251 20294 +251 20294 +251 20294 +251 20294 +251 20295 +251 20295 +251 20295 +251 20295 +251 20295 +251 20295 +250 20295 +250 20295 +250 20295 +250 20296 +250 20296 +250 20296 +250 20296 +250 20296 +250 20296 +250 20296 +249 20296 +249 20296 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +248 20297 +248 20297 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +246 20299 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +245 20300 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +244 20301 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +243 20302 +243 20302 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +242 20303 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20307 +240 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20308 +239 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20309 +238 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20310 +237 20310 +237 20310 +237 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20311 +236 20311 +236 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20312 +235 20312 +235 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20313 +234 20313 +234 20313 +234 20313 +233 20313 +233 20313 +233 20313 +233 20313 +233 20313 +233 20313 +233 20314 +233 20314 +233 20314 +233 20314 +232 20314 +232 20314 +232 20314 +232 20314 +232 20314 +232 20314 +232 20314 +232 20315 +232 20315 +232 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20316 +231 20316 +231 20316 +231 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20317 +230 20317 +230 20317 +230 20317 +229 20317 +229 20317 +229 20317 +229 20317 +229 20317 +229 20318 +229 20318 +229 20318 +229 20318 +229 20318 +229 20318 +228 20318 +228 20318 +228 20318 +228 20318 +228 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20324 +222 20324 +223 20324 +223 20324 +222 20324 +222 20324 +222 20324 +222 20324 +222 20324 +222 20324 +222 20325 +222 20325 +222 20325 +221 20325 +221 20325 +221 20325 +221 20325 +221 20325 +221 20325 +221 20326 +221 20326 +221 20326 +221 20326 +220 20326 +220 20326 +220 20326 +220 20326 +220 20326 +220 20326 +220 20327 +220 20327 +220 20327 +220 20327 +219 20327 +219 20327 +219 20327 +219 20327 +219 20327 +219 20327 +219 20328 +219 20328 +219 20328 +218 20328 +218 20328 +218 20328 +218 20328 +218 20328 +218 20328 +218 20328 +218 20329 +218 20329 +218 20329 +217 20329 +217 20329 +217 20329 +217 20329 +217 20329 +217 20329 +217 20329 +217 20330 +217 20330 +217 20330 +216 20330 +216 20330 +216 20330 +216 20330 +216 20330 +216 20330 +216 20330 +216 20331 +216 20331 +215 20331 +215 20331 +215 20331 +215 20331 +215 20331 +215 20331 +215 20331 +215 20331 +215 20332 +215 20332 +214 20332 +214 20332 +214 20332 +214 20332 +214 20332 +214 20332 +214 20332 +214 20332 +214 20333 +214 20333 +213 20333 +213 20333 +213 20333 +213 20333 +213 20333 +213 20333 +213 20333 +213 20333 +213 20334 +212 20334 +212 20334 +212 20334 +212 20334 +212 20334 +212 20334 +212 20334 +212 20334 +212 20334 +212 20335 +211 20335 +211 20335 +211 20335 +211 20335 +211 20335 +211 20335 +211 20335 +211 20335 +211 20336 +211 20336 +210 20336 +210 20336 +210 20336 +210 20336 +210 20336 +210 20336 +210 20336 +210 20336 +210 20337 +210 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +201 20344 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +197 20348 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +196 20349 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +195 20350 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +194 20351 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +193 20352 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +191 20354 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +190 20355 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +189 20356 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +188 20357 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +187 20358 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +186 20359 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +185 20360 +185 20360 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +184 20361 +184 20361 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +183 20362 +183 20362 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +182 20363 +182 20363 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +181 20364 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +180 20365 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +179 20366 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +178 20367 +178 20368 +178 20368 +178 20368 +178 20368 +178 20368 +178 20368 +178 20368 +178 20368 +177 20368 +177 20368 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +176 20369 +176 20369 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +175 20370 +175 20370 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +174 20371 +174 20371 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +173 20372 +173 20372 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +172 20373 +172 20373 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +171 20374 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +170 20375 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +169 20376 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +168 20377 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +167 20378 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +166 20379 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +165 20380 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +164 20381 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +163 20382 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +162 20383 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +161 20384 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +160 20385 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +159 20386 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +158 20387 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +157 20388 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20392 +155 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20393 +154 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20394 +153 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20395 +152 20395 +151 20395 +151 20395 +151 20395 +151 20395 +151 20395 +151 20395 +151 20395 +151 20395 +151 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20402 +145 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20403 +144 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20404 +143 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20405 +142 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20406 +141 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20407 +140 20407 +140 20407 +139 20407 +139 20407 +139 20407 +139 20407 +139 20407 +139 20407 +139 20407 +139 20408 +139 20408 +139 20408 +138 20408 +138 20408 +138 20408 +138 20408 +138 20408 +138 20408 +138 20408 +138 20409 +138 20409 +138 20409 +137 20409 +137 20409 +137 20409 +137 20409 +137 20409 +137 20409 +137 20409 +137 20410 +137 20410 +137 20410 +136 20410 +136 20410 +136 20410 +136 20410 +136 20410 +136 20410 +136 20410 +136 20411 +136 20411 +136 20411 +135 20411 +135 20411 +135 20411 +135 20411 +135 20411 +135 20411 +135 20411 +135 20412 +135 20412 +135 20412 +134 20412 +134 20412 +134 20412 +134 20412 +134 20412 +134 20412 +134 20412 +134 20413 +134 20413 +134 20413 +133 20413 +133 20413 +133 20413 +133 20413 +133 20413 +133 20413 +133 20413 +133 20414 +133 20414 +133 20414 +132 20414 +132 20414 +132 20414 +132 20414 +132 20414 +132 20414 +132 20415 +132 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20416 +131 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20421 +126 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20422 +125 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20426 +121 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20438 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +98 20447 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +89 20458 +89 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20459 +88 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20474 +72 20474 +72 20474 +72 20474 +72 20474 +72 20474 +72 20474 +72 20474 +72 20475 +72 20475 +72 20475 +71 20475 +71 20475 +71 20475 +71 20475 +71 20475 +71 20475 +71 20475 +71 20476 +71 20476 +71 20476 +70 20476 +70 20476 +70 20476 +70 20476 +70 20476 +70 20476 +70 20476 +70 20477 +70 20477 +70 20477 +69 20477 +69 20477 +69 20477 +69 20477 +69 20477 +69 20477 +69 20477 +69 20478 +69 20478 +69 20478 +68 20478 +68 20478 +68 20478 +68 20478 +68 20478 +68 20478 +68 20478 +68 20479 +68 20479 +68 20479 +67 20479 +67 20479 +67 20479 +67 20479 +67 20479 +67 20479 +67 20479 +67 20480 +67 20480 +67 20480 +66 20480 +66 20480 +66 20480 +66 20480 +66 20480 +66 20480 +66 20480 +66 20481 +66 20481 +66 20481 +65 20481 +65 20481 +65 20481 +65 20481 +65 20481 +65 20481 +65 20481 +65 20482 +65 20482 +65 20482 +64 20482 +64 20482 +64 20482 +64 20482 +64 20482 +64 20482 +64 20482 +64 20483 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +59 20486 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +58 20487 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +56 20489 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +56 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20493 +53 20493 +53 20493 +53 20493 +53 20493 +53 20493 +53 20493 +53 20493 +53 20493 +53 20493 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +51 20495 +51 20495 +51 20495 +51 20495 +51 20495 +51 20495 +51 20495 +51 20495 +51 20495 +50 20495 +50 20496 +50 20496 +50 20496 +50 20496 +50 20496 +50 20496 +50 20496 +50 20496 +50 20496 +49 20496 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +48 20497 +48 20498 +48 20498 +48 20498 +48 20498 +48 20498 +48 20498 +48 20498 +48 20498 +47 20498 +47 20498 +47 20499 +47 20499 +47 20499 +47 20499 +47 20499 +47 20499 +47 20499 +47 20499 +46 20499 +46 20499 +46 20500 +46 20500 +46 20500 +46 20500 +46 20500 +46 20500 +46 20500 +46 20500 +45 20500 +45 20500 +45 20501 +45 20501 +45 20501 +45 20501 +45 20501 +45 20501 +45 20501 +45 20501 +44 20501 +44 20501 +44 20502 +44 20502 +44 20502 +44 20502 +44 20502 +44 20502 +44 20502 +43 20502 +43 20502 +43 20502 +43 20503 +43 20503 +43 20503 +43 20503 +43 20503 +43 20503 +43 20503 +43 20503 +42 20503 +42 20503 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +41 20504 +41 20504 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +40 20505 +40 20505 +40 20505 +40 20506 +40 20506 +40 20506 +40 20506 +40 20506 +40 20506 +40 20506 +39 20506 +39 20506 +39 20506 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +38 20507 +38 20507 +38 20507 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +37 20508 +37 20508 +37 20508 +37 20509 +37 20509 +37 20509 +37 20509 +37 20509 +37 20509 +37 20509 +37 20509 +36 20509 +36 20509 +36 20509 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +35 20510 +35 20510 +35 20510 +35 20510 +35 20511 +35 20511 +35 20511 +35 20511 +35 20511 +35 20511 +34 20511 +34 20511 +34 20511 +34 20511 +34 20511 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +33 20512 +33 20512 +33 20512 +33 20512 +33 20512 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +32 20513 +32 20513 +32 20513 +32 20513 +32 20513 +32 20513 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +31 20514 +31 20514 +31 20514 +31 20514 +31 20514 +31 20514 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +30 20515 +30 20515 +30 20515 +30 20515 +30 20515 +30 20515 +30 20515 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +29 20516 +29 20516 +29 20516 +29 20516 +29 20516 +29 20516 +29 20516 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +28 20517 +28 20517 +28 20517 +28 20517 +28 20517 +28 20517 +28 20517 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +27 20518 +27 20518 +27 20518 +27 20518 +27 20518 +27 20518 +27 20518 +27 20518 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +26 20519 +26 20519 +26 20519 +26 20519 +26 20519 +26 20519 +26 20519 +26 20519 +26 20519 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20520 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20521 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20523 +23 20522 +23 20522 +23 20522 +23 20522 +23 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +24 20522 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +25 20521 +26 20521 +26 20521 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20520 +26 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +27 20519 +28 20519 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +28 20518 +29 20518 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +29 20517 +30 20517 +30 20517 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +30 20516 +31 20516 +31 20516 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +31 20515 +32 20515 +32 20515 +32 20515 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +32 20514 +33 20514 +33 20514 +33 20514 +33 20514 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +33 20513 +34 20513 +34 20513 +34 20513 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +34 20512 +35 20512 +35 20512 +35 20512 +35 20512 +35 20511 +35 20511 +35 20511 +35 20511 +35 20511 +35 20511 +35 20511 +35 20511 +36 20511 +36 20511 +36 20511 +36 20511 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +36 20510 +37 20510 +37 20510 +37 20510 +37 20510 +37 20510 +37 20509 +37 20509 +37 20509 +37 20509 +37 20509 +37 20509 +38 20509 +38 20509 +38 20509 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +38 20508 +39 20508 +39 20508 +39 20508 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +39 20507 +40 20507 +40 20507 +40 20507 +40 20506 +40 20506 +40 20506 +40 20506 +40 20506 +40 20506 +40 20506 +41 20506 +41 20506 +41 20506 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +41 20505 +42 20505 +42 20505 +42 20505 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +42 20504 +43 20504 +43 20504 +43 20504 +43 20503 +43 20503 +43 20503 +43 20503 +43 20503 +43 20503 +44 20503 +44 20503 +44 20503 +44 20503 +44 20502 +44 20502 +44 20502 +44 20502 +44 20502 +44 20502 +45 20502 +45 20502 +45 20502 +45 20502 +45 20501 +45 20501 +45 20501 +45 20501 +45 20501 +45 20501 +46 20501 +46 20501 +46 20501 +46 20501 +46 20500 +46 20500 +46 20500 +46 20500 +46 20500 +46 20500 +47 20500 +47 20500 +47 20500 +47 20500 +47 20499 +47 20499 +47 20499 +47 20499 +47 20499 +48 20499 +48 20499 +48 20499 +48 20499 +48 20498 +48 20498 +48 20498 +48 20498 +48 20498 +48 20498 +49 20498 +49 20498 +49 20498 +49 20498 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +49 20497 +50 20497 +50 20497 +50 20497 +50 20497 +50 20496 +50 20496 +50 20496 +50 20496 +50 20496 +51 20496 +51 20496 +51 20496 +51 20496 +51 20496 +51 20495 +51 20495 +51 20495 +51 20495 +51 20495 +52 20495 +52 20495 +52 20495 +52 20495 +52 20494 +52 20494 +52 20494 +52 20494 +52 20494 +53 20494 +53 20494 +53 20494 +53 20494 +53 20494 +53 20493 +53 20493 +53 20493 +53 20493 +53 20493 +54 20493 +54 20493 +54 20493 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +54 20492 +55 20492 +55 20492 +55 20492 +55 20492 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +55 20491 +56 20491 +56 20491 +56 20491 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +56 20490 +57 20490 +57 20490 +57 20490 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +57 20489 +58 20489 +58 20489 +58 20489 +58 20489 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +58 20488 +59 20488 +59 20488 +59 20488 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +59 20487 +60 20487 +60 20487 +60 20487 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +60 20486 +61 20486 +61 20486 +61 20486 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +61 20485 +62 20485 +62 20485 +62 20485 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +62 20484 +63 20484 +63 20484 +63 20484 +63 20484 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +63 20483 +64 20483 +64 20483 +64 20483 +64 20482 +64 20482 +64 20482 +64 20482 +64 20482 +64 20482 +64 20482 +65 20482 +65 20482 +65 20482 +65 20481 +65 20481 +65 20481 +65 20481 +65 20481 +65 20481 +66 20481 +66 20481 +66 20481 +66 20481 +66 20480 +66 20480 +66 20480 +66 20480 +66 20480 +66 20480 +67 20480 +67 20480 +67 20480 +67 20479 +67 20479 +67 20479 +67 20479 +67 20479 +67 20479 +67 20479 +68 20479 +68 20479 +68 20479 +68 20478 +68 20478 +68 20478 +68 20478 +68 20478 +68 20478 +69 20478 +69 20478 +69 20478 +69 20478 +69 20477 +69 20477 +69 20477 +69 20477 +69 20477 +69 20477 +70 20477 +70 20477 +70 20477 +70 20476 +70 20476 +70 20476 +70 20476 +70 20476 +70 20476 +70 20476 +71 20476 +71 20476 +71 20476 +71 20475 +71 20475 +71 20475 +71 20475 +71 20475 +71 20475 +71 20475 +72 20475 +72 20475 +72 20475 +72 20474 +72 20474 +72 20474 +72 20474 +72 20474 +72 20474 +73 20474 +73 20474 +73 20474 +73 20474 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +73 20473 +74 20473 +74 20473 +74 20473 +74 20473 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +74 20472 +75 20472 +75 20472 +75 20472 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +75 20471 +76 20471 +76 20471 +76 20471 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +76 20470 +77 20470 +77 20470 +77 20470 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +77 20469 +78 20469 +78 20469 +78 20469 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +78 20468 +79 20468 +79 20468 +79 20468 +79 20468 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +79 20467 +80 20467 +80 20467 +80 20467 +80 20467 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +80 20466 +81 20466 +81 20466 +81 20466 +81 20466 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +81 20465 +82 20465 +82 20465 +82 20465 +82 20465 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +82 20464 +83 20464 +83 20464 +83 20464 +83 20464 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +83 20463 +84 20463 +84 20463 +84 20463 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +84 20462 +85 20462 +85 20462 +85 20462 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +85 20461 +86 20461 +86 20461 +86 20461 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +86 20460 +87 20460 +87 20460 +87 20460 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +87 20459 +88 20459 +88 20459 +88 20459 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +88 20458 +89 20458 +89 20458 +89 20458 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +89 20457 +90 20457 +90 20457 +90 20457 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +90 20456 +91 20456 +91 20456 +91 20456 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +91 20455 +92 20455 +92 20455 +92 20455 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +92 20454 +93 20454 +93 20454 +93 20454 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +93 20453 +94 20453 +94 20453 +94 20453 +94 20453 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +94 20452 +95 20452 +95 20452 +95 20452 +95 20452 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +95 20451 +96 20451 +96 20451 +96 20451 +96 20451 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +96 20450 +97 20450 +97 20450 +97 20450 +97 20450 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +97 20449 +98 20449 +98 20449 +98 20449 +98 20449 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +98 20448 +99 20448 +99 20448 +99 20448 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +99 20447 +100 20447 +100 20447 +100 20447 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +100 20446 +101 20446 +101 20446 +101 20446 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +101 20445 +102 20445 +102 20445 +102 20445 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +102 20444 +103 20444 +103 20444 +103 20444 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +103 20443 +104 20443 +104 20443 +104 20443 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +104 20442 +105 20442 +105 20442 +105 20442 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +105 20441 +106 20441 +106 20441 +106 20441 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +106 20440 +107 20440 +107 20440 +107 20440 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +107 20439 +108 20439 +108 20439 +108 20439 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +108 20438 +109 20438 +109 20438 +109 20438 +109 20438 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +109 20437 +110 20437 +110 20437 +110 20437 +110 20437 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +110 20436 +111 20436 +111 20436 +111 20436 +111 20436 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +111 20435 +112 20435 +112 20435 +112 20435 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +112 20434 +113 20434 +113 20434 +113 20434 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +113 20433 +114 20433 +114 20433 +114 20433 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +114 20432 +115 20432 +115 20432 +115 20432 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +115 20431 +116 20431 +116 20431 +116 20431 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +116 20430 +117 20430 +117 20430 +117 20430 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +117 20429 +118 20429 +118 20429 +118 20429 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +118 20428 +119 20428 +119 20428 +119 20428 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +119 20427 +120 20427 +120 20427 +120 20427 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +120 20426 +121 20426 +121 20426 +121 20426 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +121 20425 +122 20425 +122 20425 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +122 20424 +123 20424 +123 20424 +123 20424 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +123 20423 +124 20423 +124 20423 +124 20423 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +124 20422 +125 20422 +125 20422 +125 20422 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +125 20421 +126 20421 +126 20421 +126 20421 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +126 20420 +127 20420 +127 20420 +127 20420 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +127 20419 +128 20419 +128 20419 +128 20419 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +128 20418 +129 20418 +129 20418 +129 20418 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +129 20417 +130 20417 +130 20417 +130 20417 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +130 20416 +131 20416 +131 20416 +131 20416 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +131 20415 +132 20415 +132 20415 +132 20415 +132 20414 +132 20414 +132 20414 +132 20414 +132 20414 +132 20414 +132 20414 +133 20414 +133 20414 +133 20414 +133 20413 +133 20413 +133 20413 +133 20413 +133 20413 +133 20413 +133 20413 +134 20413 +134 20413 +134 20413 +134 20412 +134 20412 +134 20412 +134 20412 +134 20412 +134 20412 +134 20412 +135 20412 +135 20412 +135 20412 +135 20411 +135 20411 +135 20411 +135 20411 +135 20411 +135 20411 +135 20411 +136 20411 +136 20411 +136 20410 +136 20410 +136 20410 +136 20410 +136 20410 +136 20410 +136 20410 +137 20410 +137 20410 +137 20410 +137 20409 +137 20409 +137 20409 +137 20409 +137 20409 +137 20409 +137 20409 +138 20409 +138 20409 +138 20409 +138 20408 +138 20408 +138 20408 +138 20408 +138 20408 +138 20408 +138 20408 +139 20408 +139 20408 +139 20408 +139 20407 +139 20407 +139 20407 +139 20407 +139 20407 +139 20407 +139 20407 +140 20407 +140 20407 +140 20407 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +140 20406 +141 20406 +141 20406 +141 20406 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +141 20405 +142 20405 +142 20405 +142 20405 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +142 20404 +143 20404 +143 20404 +143 20404 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +143 20403 +144 20403 +144 20403 +144 20403 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +144 20402 +145 20402 +145 20402 +145 20402 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +145 20401 +146 20401 +146 20401 +146 20401 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +146 20400 +147 20400 +147 20400 +147 20400 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +147 20399 +148 20399 +148 20399 +148 20399 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +148 20398 +149 20398 +149 20398 +149 20398 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +149 20397 +150 20397 +150 20397 +150 20397 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +150 20396 +151 20396 +151 20396 +151 20396 +151 20395 +151 20395 +151 20395 +151 20395 +151 20395 +151 20395 +152 20395 +152 20395 +152 20395 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +152 20394 +153 20394 +153 20394 +153 20394 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +153 20393 +154 20393 +154 20393 +154 20393 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +154 20392 +155 20392 +155 20392 +155 20392 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +155 20391 +156 20391 +156 20391 +156 20391 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +156 20390 +157 20390 +157 20390 +157 20390 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +157 20389 +158 20389 +158 20389 +158 20389 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +158 20388 +159 20388 +159 20388 +159 20388 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +159 20387 +160 20387 +160 20387 +160 20387 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +160 20386 +161 20386 +161 20386 +161 20386 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +161 20385 +162 20385 +162 20385 +162 20385 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +162 20384 +163 20384 +163 20384 +163 20384 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +163 20383 +164 20383 +164 20383 +164 20383 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +164 20382 +165 20382 +165 20382 +165 20382 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +165 20381 +166 20381 +166 20381 +166 20381 +166 20381 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +166 20380 +167 20380 +167 20380 +167 20380 +167 20380 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +167 20379 +168 20379 +168 20379 +168 20379 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +168 20378 +169 20378 +169 20378 +169 20378 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +169 20377 +170 20377 +170 20377 +170 20377 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +170 20376 +171 20376 +171 20376 +171 20376 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +171 20375 +172 20375 +172 20375 +172 20375 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +172 20374 +173 20374 +173 20374 +173 20374 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +173 20373 +174 20373 +174 20373 +174 20373 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +174 20372 +175 20372 +175 20372 +175 20372 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +175 20371 +176 20371 +176 20371 +176 20371 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +176 20370 +177 20370 +177 20370 +177 20370 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +177 20369 +178 20369 +178 20369 +178 20369 +178 20368 +178 20368 +178 20368 +178 20368 +178 20368 +178 20368 +179 20368 +179 20368 +179 20368 +179 20368 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +179 20367 +180 20367 +180 20367 +180 20367 +180 20367 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +180 20366 +181 20366 +181 20366 +181 20366 +181 20366 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +181 20365 +182 20365 +182 20365 +182 20365 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +182 20364 +183 20364 +183 20364 +183 20364 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +183 20363 +184 20363 +184 20363 +184 20363 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +184 20362 +185 20362 +185 20362 +185 20362 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +185 20361 +186 20361 +186 20361 +186 20361 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +186 20360 +187 20360 +187 20360 +187 20360 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +187 20359 +188 20359 +188 20359 +188 20359 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +188 20358 +189 20358 +189 20358 +189 20358 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +189 20357 +190 20357 +190 20357 +190 20357 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +190 20356 +191 20356 +191 20356 +191 20356 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +191 20355 +192 20355 +192 20355 +192 20355 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +192 20354 +193 20354 +193 20354 +193 20354 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +193 20353 +194 20353 +194 20353 +194 20353 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +194 20352 +195 20352 +195 20352 +195 20352 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +195 20351 +196 20351 +196 20351 +196 20351 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +196 20350 +197 20350 +197 20350 +197 20350 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +197 20349 +198 20349 +198 20349 +198 20349 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +198 20348 +199 20348 +199 20348 +199 20348 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +199 20347 +200 20347 +200 20347 +200 20347 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +200 20346 +201 20346 +201 20346 +201 20346 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +201 20345 +202 20345 +202 20345 +202 20345 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +202 20344 +203 20344 +203 20344 +203 20344 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +203 20343 +204 20343 +204 20343 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +204 20342 +205 20342 +205 20342 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +205 20341 +206 20341 +206 20341 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +206 20340 +207 20340 +207 20340 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +207 20339 +208 20339 +208 20339 +208 20339 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +208 20338 +209 20338 +209 20338 +209 20338 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +209 20337 +210 20337 +210 20337 +210 20337 +210 20337 +210 20336 +210 20336 +210 20336 +210 20336 +210 20336 +210 20336 +211 20336 +211 20336 +211 20336 +211 20336 +211 20335 +211 20335 +211 20335 +211 20335 +211 20335 +211 20335 +212 20335 +212 20335 +212 20335 +212 20335 +212 20335 +212 20334 +212 20334 +212 20334 +212 20334 +212 20334 +213 20334 +213 20334 +213 20334 +213 20334 +213 20333 +213 20333 +213 20333 +213 20333 +213 20333 +213 20333 +214 20333 +214 20333 +214 20333 +214 20333 +214 20332 +214 20332 +214 20332 +214 20332 +214 20332 +214 20332 +215 20332 +215 20332 +215 20332 +215 20332 +215 20331 +215 20331 +215 20331 +215 20331 +215 20331 +215 20331 +216 20331 +216 20331 +216 20331 +216 20330 +216 20330 +216 20330 +216 20330 +216 20330 +216 20330 +216 20330 +217 20330 +217 20330 +217 20330 +217 20329 +217 20329 +217 20329 +217 20329 +217 20329 +217 20329 +217 20329 +218 20329 +218 20329 +218 20329 +218 20328 +218 20328 +218 20328 +218 20328 +218 20328 +218 20328 +218 20328 +219 20328 +219 20328 +219 20327 +219 20327 +219 20327 +219 20327 +219 20327 +219 20327 +219 20327 +219 20327 +220 20327 +220 20327 +220 20326 +220 20326 +220 20326 +220 20326 +220 20326 +220 20326 +220 20326 +220 20326 +221 20326 +221 20326 +221 20325 +221 20325 +221 20325 +221 20325 +221 20325 +221 20325 +221 20325 +221 20325 +222 20325 +222 20325 +222 20324 +222 20324 +222 20324 +222 20324 +222 20324 +222 20324 +222 20324 +222 20324 +223 20324 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +223 20323 +224 20323 +224 20323 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +224 20322 +225 20322 +225 20322 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +225 20321 +226 20321 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +226 20320 +227 20320 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +227 20319 +228 20319 +228 20318 +228 20318 +228 20318 +228 20318 +228 20318 +228 20318 +228 20318 +228 20318 +228 20318 +229 20318 +229 20317 +229 20317 +229 20317 +229 20317 +229 20317 +229 20317 +229 20317 +229 20317 +229 20317 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +230 20316 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +231 20315 +232 20315 +232 20315 +232 20315 +232 20314 +232 20314 +232 20314 +232 20314 +232 20314 +232 20314 +232 20314 +233 20314 +233 20314 +233 20313 +233 20313 +233 20313 +233 20313 +233 20313 +233 20313 +233 20313 +233 20313 +234 20313 +234 20313 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +234 20312 +235 20312 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +235 20311 +236 20311 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +236 20310 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +237 20309 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +238 20308 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +239 20307 +240 20307 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +240 20306 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +241 20305 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +242 20304 +243 20304 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +243 20303 +244 20303 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +244 20302 +245 20302 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +245 20301 +246 20301 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +246 20300 +247 20300 +247 20300 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +247 20299 +248 20299 +248 20299 +248 20299 +248 20299 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +248 20298 +249 20298 +249 20298 +249 20298 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +249 20297 +250 20297 +250 20297 +250 20296 +250 20296 +250 20296 +250 20296 +250 20296 +250 20296 +250 20296 +251 20296 +251 20296 +251 20296 +251 20296 +251 20295 +251 20295 +251 20295 +251 20295 +251 20295 +251 20295 +252 20295 +252 20295 +252 20295 +252 20295 +252 20294 +252 20294 +252 20294 +252 20294 +252 20294 +252 20294 +253 20294 +253 20294 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +253 20293 +254 20293 +254 20293 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +254 20292 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20291 +255 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20290 +256 20289 +256 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20289 +257 20288 +257 20288 +258 20288 +258 20288 +258 20288 +258 20288 +258 20288 +258 20288 +258 20287 +258 20287 +258 20287 +258 20287 +259 20287 +259 20287 +259 20287 +259 20287 +259 20287 +259 20287 +259 20286 +259 20286 +259 20286 +259 20286 +260 20286 +260 20286 +260 20286 +260 20286 +260 20286 +260 20285 +260 20285 +260 20285 +260 20285 +260 20285 +261 20285 +261 20285 +261 20285 +261 20284 +261 20284 +261 20284 +261 20284 +261 20284 +261 20284 +261 20284 +262 20284 +262 20283 +262 20283 +262 20283 +262 20283 +262 20283 +262 20283 +262 20283 +262 20283 +262 20283 +263 20282 +263 20282 +263 20282 +263 20282 +263 20282 +263 20282 +263 20282 +263 20282 +263 20281 +263 20281 +264 20281 +264 20281 +264 20281 +264 20281 +264 20281 +264 20281 +264 20280 +264 20280 +264 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20280 +265 20279 +265 20279 +265 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20279 +266 20278 +266 20278 +266 20278 +266 20278 +266 20278 +267 20278 +267 20278 +267 20278 +267 20277 +267 20277 +267 20277 +267 20277 +267 20277 +267 20277 +267 20277 +268 20277 +268 20277 +268 20277 +268 20277 +268 20276 +268 20276 +268 20276 +268 20276 +268 20276 +268 20276 +269 20276 +269 20276 +269 20276 +269 20276 +269 20276 +269 20276 +269 20275 +269 20275 +269 20275 +269 20275 +270 20275 +270 20275 +270 20275 +270 20275 +270 20274 +270 20274 +270 20274 +270 20274 +270 20274 +270 20274 +271 20274 +271 20274 +271 20273 +271 20273 +271 20273 +271 20273 +271 20273 +271 20273 +271 20273 +271 20273 +272 20273 +272 20272 +272 20273 +272 20272 +272 20272 +272 20272 +272 20272 +272 20272 +272 20272 +272 20272 +273 20271 +273 20272 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +273 20271 +274 20271 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +274 20270 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20269 +275 20268 +275 20268 +275 20268 +276 20268 +276 20268 +276 20268 +276 20268 +276 20268 +276 20267 +276 20267 +276 20267 +276 20267 +276 20267 +277 20267 +277 20267 +277 20267 +277 20266 +277 20266 +277 20266 +277 20266 +277 20266 +277 20266 +277 20266 +278 20266 +278 20266 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +278 20265 +279 20265 +279 20265 +279 20264 +279 20264 +279 20264 +279 20264 +279 20264 +279 20264 +279 20264 +280 20264 +280 20264 +280 20264 +280 20263 +280 20263 +280 20263 +280 20263 +280 20263 +280 20263 +280 20263 +281 20263 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20262 +281 20261 +282 20261 +282 20261 +282 20261 +282 20261 +282 20261 +282 20261 +282 20260 +282 20260 +282 20260 +282 20260 +283 20260 +283 20260 +283 20260 +283 20260 +283 20259 +283 20259 +283 20259 +283 20259 +283 20259 +283 20259 +284 20259 +284 20259 +284 20258 +284 20258 +284 20258 +284 20258 +284 20258 +284 20258 +284 20258 +284 20258 +285 20257 +285 20257 +285 20257 +285 20257 +285 20257 +285 20257 +285 20257 +285 20256 +285 20256 +285 20256 +286 20256 +286 20256 +286 20256 +286 20256 +286 20256 +286 20255 +286 20255 +286 20255 +286 20255 +286 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20255 +287 20254 +287 20254 +287 20254 +287 20254 +287 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20254 +288 20253 +288 20253 +288 20253 +288 20253 +289 20253 +289 20253 +289 20253 +289 20253 +289 20252 +289 20252 +289 20252 +289 20252 +289 20252 +289 20252 +290 20252 +290 20252 +290 20251 +290 20251 +290 20251 +290 20251 +290 20251 +290 20251 +290 20251 +290 20251 +291 20251 +291 20251 +291 20250 +291 20250 +291 20250 +291 20250 +291 20250 +291 20250 +291 20250 +291 20250 +292 20250 +292 20250 +292 20250 +292 20250 +292 20249 +292 20249 +292 20250 +292 20249 +292 20249 +292 20249 +293 20249 +293 20249 +293 20249 +293 20249 +293 20249 +293 20249 +293 20249 +293 20249 +293 20249 +293 20249 +294 20248 +294 20248 +294 20248 +294 20248 +294 20248 +294 20248 +294 20248 +294 20248 +294 20247 +294 20247 +295 20247 +295 20247 +295 20247 +295 20247 +295 20247 +295 20247 +295 20247 +295 20247 +295 20246 +296 20246 +296 20246 +296 20246 +296 20246 +296 20246 +296 20246 +296 20246 +296 20245 +296 20245 +296 20245 +297 20245 +297 20245 +297 20245 +297 20245 +297 20245 +297 20245 +297 20245 +297 20245 +297 20244 +297 20244 +298 20244 +298 20244 +298 20244 +298 20244 +298 20243 +298 20243 +298 20243 +298 20243 +298 20243 +298 20243 +299 20243 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20242 +299 20241 +299 20241 +300 20241 +300 20241 +300 20241 +300 20241 +300 20241 +300 20240 +300 20240 +300 20240 +300 20240 +300 20240 +301 20240 +301 20240 +301 20239 +301 20239 +301 20239 +301 20239 +301 20239 +301 20239 +301 20239 +301 20238 +302 20238 +302 20238 +302 20238 +302 20238 +302 20238 +302 20237 +302 20237 +302 20237 +302 20237 +302 20237 +303 20237 +303 20237 +303 20236 +303 20236 +303 20236 +303 20236 +303 20236 +303 20236 +303 20236 +303 20236 +304 20236 +304 20235 +304 20235 +304 20235 +304 20235 +304 20235 +304 20235 +304 20235 +304 20234 +304 20234 +305 20234 +305 20234 +305 20234 +305 20234 +305 20234 +305 20234 +305 20234 +305 20234 +305 20234 +305 20233 +306 20233 +306 20233 +306 20233 +306 20233 +306 20233 +306 20233 +306 20233 +306 20233 +306 20233 +306 20233 +307 20233 +307 20233 +307 20232 +307 20232 +307 20232 +307 20232 +307 20232 +307 20232 +307 20232 +307 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20232 +308 20231 +308 20231 +308 20231 +309 20231 +309 20231 +309 20231 +309 20231 +309 20231 +309 20230 +309 20230 +309 20230 +309 20230 +310 20230 +310 20230 +310 20230 +310 20230 +310 20230 +310 20229 +310 20229 +310 20229 +310 20229 +310 20229 +311 20229 +311 20229 +311 20228 +311 20228 +311 20228 +311 20228 +311 20228 +311 20228 +311 20228 +311 20228 +312 20228 +312 20228 +312 20228 +312 20227 +312 20227 +312 20227 +312 20227 +312 20227 +312 20227 +312 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +313 20226 +314 20225 +314 20225 +314 20225 +314 20225 +314 20225 +314 20225 +314 20225 +314 20225 +314 20224 +314 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +315 20224 +316 20224 +316 20223 +316 20224 +316 20223 +316 20223 +316 20223 +316 20223 +316 20223 +316 20223 +316 20223 +317 20223 +317 20223 +317 20223 +317 20223 +317 20223 +317 20223 +317 20223 +317 20223 +317 20223 +317 20223 +318 20223 +318 20223 +318 20223 +318 20223 +318 20223 +318 20222 +318 20222 +318 20222 +318 20222 +318 20222 +319 20222 +319 20222 +319 20221 +319 20221 +319 20221 +319 20221 +319 20221 +319 20220 +319 20220 +320 20220 +320 20220 +320 20220 +320 20219 +320 20219 +320 20219 +320 20219 +320 20219 +320 20219 +320 20218 +321 20218 +321 20218 +321 20218 +321 20218 +321 20218 +321 20218 +321 20217 +321 20217 +321 20217 +321 20217 +322 20217 +322 20217 +322 20217 +322 20216 +322 20216 +322 20216 +322 20216 +322 20216 +322 20216 +322 20216 +323 20216 +323 20216 +323 20216 +323 20216 +323 20216 +323 20216 +323 20215 +323 20215 +323 20215 +323 20215 +324 20215 +324 20215 +324 20215 +324 20215 +324 20215 +324 20215 +324 20214 +324 20214 +324 20214 +324 20214 +325 20214 +325 20214 +325 20214 +325 20213 +325 20213 +325 20213 +325 20213 +325 20213 +325 20213 +325 20213 +326 20213 +326 20213 +326 20213 +326 20212 +326 20212 +326 20212 +326 20212 +326 20212 +326 20212 +326 20212 +327 20212 +327 20212 +327 20211 +327 20211 +327 20211 +327 20211 +327 20211 +327 20211 +327 20210 +327 20210 +328 20210 +328 20210 +328 20210 +328 20210 +328 20210 +328 20209 +328 20209 +328 20209 +328 20209 +328 20209 +328 20209 +329 20209 +329 20209 +329 20209 +329 20209 +329 20209 +329 20208 +329 20208 +329 20208 +329 20208 +329 20208 +330 20208 +330 20208 +330 20207 +330 20207 +330 20207 +330 20207 +330 20206 +330 20206 +330 20206 +330 20206 +331 20206 +331 20206 +331 20206 +331 20206 +331 20206 +331 20206 +331 20206 +331 20206 +331 20206 +331 20206 +332 20206 +332 20206 +332 20206 +332 20206 +332 20206 +332 20205 +332 20205 +332 20205 +332 20205 +332 20204 +333 20204 +333 20204 +333 20204 +333 20204 +333 20204 +333 20204 +333 20204 +333 20204 +333 20204 +333 20204 +334 20203 +334 20203 +334 20203 +334 20203 +334 20203 +334 20203 +334 20202 +334 20202 +334 20202 +334 20202 +334 20202 +335 20202 +335 20202 +335 20201 +335 20201 +335 20201 +335 20201 +335 20201 +335 20200 +335 20200 +335 20200 +336 20200 +336 20199 +336 20199 +336 20199 +336 20199 +336 20199 +336 20199 +336 20199 +336 20199 +336 20199 +337 20198 +337 20198 +337 20198 +337 20198 +337 20198 +337 20198 +337 20198 +337 20197 +337 20197 +337 20197 +338 20197 +338 20197 +338 20196 +338 20196 +338 20196 +338 20196 +338 20196 +338 20195 +338 20195 +338 20195 +338 20195 +339 20195 +339 20195 +339 20195 +339 20194 +339 20194 +339 20194 +339 20194 +339 20194 +339 20194 +339 20194 +340 20193 +340 20193 +340 20193 +340 20193 +340 20193 +340 20193 +340 20193 +340 20192 +340 20192 +340 20192 +341 20192 +341 20191 +341 20191 +341 20191 +341 20191 +341 20191 +341 20190 +341 20190 +341 20190 +341 20190 +342 20190 +342 20189 +342 20189 +342 20189 +342 20189 +342 20189 +342 20189 +342 20188 +342 20188 +342 20188 +342 20188 +343 20187 +343 20187 +343 20187 +343 20187 +343 20187 +343 20187 +343 20187 +343 20186 +343 20186 +343 20186 +344 20185 +344 20185 +344 20185 +344 20184 +344 20184 +344 20184 +344 20183 +344 20183 +344 20183 +344 20183 +345 20182 +345 20182 +345 20182 +345 20182 +345 20182 +345 20182 +345 20181 +345 20181 +345 20181 +345 20180 +346 20180 +346 20180 +346 20179 +346 20179 +346 20179 +346 20178 +346 20178 +346 20178 +346 20177 +346 20177 +347 20177 +347 20177 +347 20177 +347 20176 +347 20176 +347 20176 +347 20176 +347 20176 +347 20176 +347 20175 +348 20175 +348 20175 +348 20175 +348 20175 +348 20174 +348 20174 +348 20174 +348 20173 +348 20173 +348 20173 +349 20173 +349 20173 +349 20172 +349 20172 +349 20172 +349 20172 +349 20172 +349 20172 +349 20171 +349 20171 +350 20171 +350 20170 +350 20170 +350 20170 +350 20170 +350 20170 +350 20169 +350 20169 +350 20169 +350 20168 +351 20168 +351 20168 +351 20168 +351 20168 +351 20167 +351 20167 +351 20167 +351 20166 +351 20166 +351 20166 +352 20166 +352 20166 +352 20166 +352 20165 +352 20165 +352 20165 +352 20165 +352 20164 +352 20164 +352 20163 +353 20163 +353 20163 +353 20162 +353 20162 +353 20162 +353 20161 +353 20161 +353 20161 +353 20160 +354 20160 +354 20159 +354 20159 +354 20159 +354 20159 +354 20158 +354 20158 +354 20158 +354 20158 +354 20157 +355 20157 +355 20157 +355 20156 +355 20156 +355 20156 +355 20156 +355 20155 +355 20155 +355 20155 +356 20155 +356 20154 +356 20154 +356 20154 +356 20153 +356 20153 +356 20153 +356 20152 +356 20152 +356 20152 +357 20151 +357 20151 +357 20151 +357 20151 +357 20151 +357 20150 +357 20150 +357 20150 +357 20150 +358 20150 +358 20149 +358 20149 +358 20149 +358 20148 +358 20148 +358 20148 +358 20147 +358 20147 +359 20146 +359 20146 +359 20146 +359 20146 +359 20146 +359 20145 +359 20145 +359 20145 +359 20144 +360 20144 +360 20144 +360 20144 +360 20143 +360 20143 +360 20142 +360 20142 +360 20142 +360 20141 +361 20141 +361 20141 +361 20141 +361 20140 +361 20140 +361 20139 +361 20139 +361 20139 +361 20138 +362 20138 +362 20138 +362 20138 +362 20138 +362 20137 +362 20137 +362 20137 +362 20136 +362 20136 +363 20136 +363 20135 +363 20135 +363 20135 +363 20134 +363 20134 +363 20134 +363 20134 +363 20133 +364 20133 +364 20133 +364 20132 +364 20132 +364 20131 +364 20131 +364 20130 +364 20130 +365 20130 +365 20129 +365 20129 +365 20129 +365 20128 +365 20128 +365 20127 +365 20127 +365 20127 +366 20126 +366 20126 +366 20125 +366 20125 +366 20125 +366 20124 +366 20124 +366 20123 +367 20123 +367 20123 +367 20122 +367 20122 +367 20122 +367 20121 +367 20121 +367 20121 +367 20120 +368 20120 +368 20120 +368 20119 +368 20119 +368 20118 +368 20118 +368 20118 +368 20117 +368 20117 +368 20116 +369 20116 +369 20116 +369 20116 +369 20115 +369 20115 +369 20115 +369 20114 +369 20114 +370 20114 +370 20113 +370 20113 +370 20113 +370 20112 +370 20112 +370 20112 +370 20111 +371 20111 +371 20110 +371 20110 +371 20110 +371 20109 +371 20109 +371 20109 +372 20108 +372 20108 +372 20108 +372 20107 +372 20107 +372 20107 +372 20107 +372 20106 +373 20106 +373 20105 +373 20105 +373 20104 +373 20104 +373 20104 +373 20103 +373 20103 +374 20103 +374 20103 +374 20102 +374 20102 +374 20102 +374 20101 +374 20101 +375 20100 +375 20100 +375 20100 +375 20099 +375 20099 +375 20099 +375 20098 +376 20097 +376 20097 +376 20097 +376 20097 +376 20096 +376 20096 +376 20096 +376 20096 +377 20095 +377 20095 +377 20095 +377 20095 +377 20094 +377 20094 +377 20093 +378 20093 +378 20093 +378 20093 +378 20092 +378 20092 +378 20092 +378 20091 +379 20091 +379 20091 +379 20090 +379 20090 +379 20090 +379 20090 +379 20089 +380 20089 +380 20089 +380 20088 +380 20088 +380 20088 +380 20087 +380 20087 +380 20087 +380 20086 +381 20086 +381 20085 +381 20085 +381 20085 +381 20084 +381 20084 +381 20084 +382 20084 +382 20083 +382 20083 +382 20082 +382 20082 +382 20082 +383 20082 +383 20081 +383 20081 +383 20081 +383 20080 +383 20080 +383 20080 +384 20079 +384 20079 +384 20079 +384 20078 +384 20078 +384 20078 +385 20077 +385 20077 +385 20076 +385 20076 +385 20076 +385 20075 +385 20075 +386 20075 +386 20074 +386 20074 +386 20073 +386 20073 +386 20073 +387 20072 +387 20072 +387 20071 +387 20071 +387 20071 +387 20070 +388 20070 +388 20069 +388 20069 +388 20068 +388 20068 +388 20068 +389 20067 +389 20067 +389 20066 +389 20066 +389 20066 +389 20066 +389 20065 +390 20065 +390 20065 +390 20064 +390 20063 +390 20063 +390 20062 +391 20062 +391 20062 +391 20061 +391 20060 +391 20060 +391 20060 +392 20059 +392 20059 +392 20058 +392 20058 +392 20057 +392 20057 +393 20057 +393 20056 +393 20056 +393 20056 +393 20055 +394 20055 +394 20055 +394 20054 +394 20054 +394 20053 +394 20053 +395 20052 +395 20052 +395 20051 +395 20051 +395 20051 +395 20050 +396 20050 +396 20049 +396 20049 +396 20049 +396 20049 +396 20048 +397 20048 +396 20048 +397 20047 +397 20047 +397 20046 +397 20046 +397 20046 +398 20045 +398 20045 +398 20045 +398 20044 +398 20044 +398 20044 +399 20043 +399 20043 +399 20043 +399 20042 +399 20042 +400 20042 +400 20041 +400 20041 +400 20041 +400 20040 +400 20040 +401 20040 +401 20039 +401 20039 +401 20038 +401 20037 +402 20037 +402 20037 +402 20036 +402 20036 +402 20035 +403 20035 +403 20035 +403 20034 +403 20034 +403 20033 +403 20033 +404 20033 +404 20033 +404 20032 +404 20032 +404 20032 +405 20031 +405 20031 +405 20030 +405 20030 +405 20030 +406 20030 +406 20029 +406 20029 +406 20028 +406 20028 +407 20027 +407 20027 +407 20027 +407 20027 +407 20026 +408 20026 +408 20025 +408 20025 +408 20025 +408 20024 +409 20024 +409 20023 +409 20023 +409 20023 +409 20023 +410 20022 +410 20022 +410 20021 +410 20021 +410 20021 +411 20020 +411 20020 +411 20020 +411 20020 +411 20019 +412 20019 +412 20019 +412 20018 +412 20018 +412 20017 +413 20017 +413 20017 +413 20016 +413 20016 +413 20015 +414 20015 +414 20015 +414 20014 +414 20014 +414 20014 +415 20014 +415 20013 +415 20013 +415 20012 +415 20012 +416 20012 +416 20011 +416 20011 +416 20011 +417 20010 +417 20010 +417 20010 +417 20009 +417 20009 +418 20009 +418 20008 +418 20008 +418 20007 +418 20007 +419 20007 +419 20006 +419 20006 +419 20005 +419 20005 +420 20005 +420 20004 +420 20004 +420 20003 +421 20003 +421 20003 +421 20002 +421 20002 +421 20002 +422 20001 +422 20001 +422 20001 +422 20001 +423 20000 +423 20000 +423 20000 +423 19999 +423 19999 +424 19999 +424 19998 +424 19998 +424 19998 +424 19997 +425 19997 +425 19997 +425 19996 +425 19996 +426 19996 +426 19995 +426 19995 +426 19995 +426 19994 +427 19994 +427 19994 +427 19993 +427 19993 +428 19992 +428 19992 +428 19991 +428 19991 +429 19991 +429 19990 +429 19990 +429 19990 +429 19989 +430 19989 +430 19989 +430 19988 +430 19988 +431 19988 +431 19987 +431 19987 +431 19986 +431 19986 +432 19986 +432 19985 +432 19985 +432 19985 +433 19984 +433 19984 +433 19984 +433 19983 +434 19983 +434 19982 +434 19982 +434 19981 +434 19981 +434 19981 +435 19981 +435 19980 +435 19980 +435 19980 +436 19979 +436 19978 +436 19978 +436 19977 +437 19977 +437 19976 +437 19976 +437 19976 +437 19975 +437 19975 +438 19975 +438 19974 +438 19974 +438 19974 +439 19973 +439 19973 +439 19972 +439 19972 +439 19972 +440 19971 +440 19971 +440 19970 +440 19969 +441 19969 +441 19968 +441 19968 +441 19967 +442 19967 +442 19967 +442 19967 +442 19966 +443 19966 +443 19966 +443 19965 +443 19965 +444 19965 +444 19965 +444 19964 +444 19964 +444 19964 +445 19963 +445 19963 +445 19963 +445 19962 +446 19962 +446 19962 +446 19961 +446 19961 +446 19961 +446 19961 +447 19960 +447 19960 +447 19960 +447 19960 +447 19959 +448 19959 +448 19959 +448 19959 +448 19958 +449 19958 +449 19958 +449 19958 +450 19957 +450 19957 +450 19957 +450 19956 +451 19956 +451 19956 +451 19955 +452 19955 +452 19954 +452 19954 +452 19953 +453 19953 +453 19953 +453 19953 +453 19952 +454 19952 +454 19951 +454 19951 +454 19951 +455 19950 +455 19950 +455 19950 +455 19949 +455 19949 +456 19949 +456 19949 +456 19948 +457 19948 +457 19948 +457 19948 +457 19947 +458 19947 +458 19947 +458 19946 +459 19946 +459 19946 +459 19945 +459 19945 +459 19945 +460 19944 +460 19944 +460 19944 +461 19944 +461 19943 +461 19943 +461 19942 +462 19942 +462 19942 +462 19942 +463 19941 +463 19941 +463 19940 +464 19940 +464 19940 +464 19940 +464 19939 +465 19939 +465 19939 +465 19939 +466 19938 +466 19938 +466 19938 +467 19938 +467 19937 +467 19937 +468 19937 +468 19936 +468 19936 +468 19936 +469 19935 +469 19935 +469 19935 +470 19935 +470 19934 +470 19934 +471 19933 +471 19933 +471 19933 +471 19933 +472 19932 +472 19932 +472 19932 +472 19931 +473 19931 +473 19930 +473 19930 +473 19930 +474 19930 +474 19929 +474 19929 +474 19928 +475 19928 +475 19928 +475 19927 +475 19927 +476 19926 +476 19926 +476 19925 +477 19925 +477 19924 +477 19924 +478 19924 +478 19923 +478 19923 +479 19923 +479 19923 +479 19923 +480 19922 +480 19922 +480 19922 +481 19921 +481 19921 +481 19920 +481 19920 +482 19920 +482 19919 +482 19919 +483 19918 +483 19918 +483 19918 +484 19917 +484 19917 +484 19917 +485 19916 +485 19916 +485 19915 +486 19915 +486 19915 +486 19914 +486 19914 +487 19914 +487 19913 +487 19913 +488 19913 +488 19912 +488 19912 +489 19912 +489 19911 +489 19911 +490 19911 +490 19911 +490 19910 +491 19910 +491 19909 +491 19909 +492 19909 +492 19908 +492 19908 +493 19908 +493 19907 +493 19907 +493 19907 +494 19906 +494 19906 +494 19905 +495 19905 +495 19904 +495 19904 +495 19903 +496 19903 +496 19903 +496 19902 +497 19902 +497 19902 +497 19901 +498 19901 +498 19900 +498 19900 +499 19899 +499 19899 +499 19898 +500 19898 +500 19898 +500 19897 +501 19897 +501 19897 +501 19896 +501 19896 +502 19895 +502 19895 +502 19894 +503 19894 +503 19893 +504 19893 +504 19892 +504 19892 +505 19891 +505 19891 +505 19891 +505 19891 +506 19890 +506 19890 +506 19889 +507 19889 +507 19889 +507 19888 +508 19888 +508 19887 +508 19887 +508 19886 +508 19886 +509 19886 +509 19885 +509 19885 +510 19884 +510 19884 +511 19883 +511 19883 +511 19882 +512 19882 +512 19881 +512 19881 +513 19881 +513 19880 +514 19880 +514 19879 +514 19879 +514 19878 +514 19878 +515 19877 +515 19877 +515 19877 +516 19876 +516 19876 +517 19876 +517 19875 +517 19875 +518 19874 +518 19874 +519 19874 +519 19874 +519 19873 +520 19873 +520 19872 +520 19872 +521 19871 +521 19871 +521 19871 +522 19870 +522 19870 +522 19869 +522 19869 +523 19869 +523 19868 +524 19868 +524 19868 +524 19868 +524 19867 +525 19867 +525 19866 +526 19866 +526 19866 +526 19865 +527 19865 +527 19864 +528 19864 +528 19863 +528 19863 +529 19862 +529 19862 +530 19861 +530 19861 +530 19861 +531 19860 +531 19860 +531 19859 +532 19859 +532 19858 +532 19858 +533 19857 +533 19857 +534 19856 +534 19856 +534 19855 +535 19855 +535 19855 +536 19854 +536 19854 +536 19853 +537 19852 +537 19852 +538 19851 +538 19851 +538 19851 +539 19850 +539 19849 +540 19849 +540 19848 +540 19848 +541 19848 +541 19847 +542 19847 +542 19846 +542 19846 +543 19845 +543 19845 +543 19844 +543 19844 +544 19844 +544 19843 +545 19843 +545 19842 +545 19842 +546 19841 +546 19840 +547 19839 +547 19839 +548 19838 +548 19838 +548 19837 +549 19837 +549 19836 +550 19836 +550 19836 +550 19835 +551 19835 +551 19835 +552 19834 +552 19834 +552 19834 +553 19833 +553 19833 +554 19833 +554 19832 +554 19832 +555 19831 +555 19831 +556 19831 +556 19830 +556 19830 +557 19829 +557 19828 +557 19828 +558 19828 +558 19827 +559 19827 +559 19826 +560 19826 +560 19825 +560 19825 +561 19824 +561 19824 +562 19823 +562 19823 +562 19822 +562 19822 +562 19822 +563 19821 +563 19821 +564 19820 +564 19820 +564 19819 +565 19819 +565 19818 +566 19818 +566 19817 +567 19817 +567 19816 +567 19816 +568 19815 +568 19814 +569 19814 +569 19813 +570 19812 +570 19812 +570 19812 +571 19811 +571 19811 +572 19810 +572 19810 +573 19809 +573 19809 +573 19808 +574 19808 +574 19807 +574 19807 +575 19806 +575 19806 +576 19805 +576 19805 +576 19804 +577 19804 +577 19803 +577 19803 +578 19803 +578 19802 +579 19802 +579 19801 +580 19800 +580 19800 +581 19799 +581 19799 +581 19799 +581 19798 +582 19798 +582 19797 +583 19797 +583 19796 +584 19796 +584 19795 +585 19794 +585 19794 +585 19793 +586 19793 +586 19792 +586 19791 +587 19791 +587 19790 +587 19790 +588 19789 +588 19789 +589 19788 +589 19788 +590 19787 +590 19787 +591 19786 +591 19785 +591 19785 +592 19785 +592 19785 +593 19784 +593 19783 +594 19783 +594 19782 +594 19782 +595 19781 +595 19781 +596 19781 +596 19780 +597 19780 +597 19780 +597 19779 +598 19779 +598 19778 +599 19778 +599 19777 +600 19777 +600 19776 +600 19776 +601 19776 +601 19775 +602 19774 +602 19774 +603 19774 +603 19773 +603 19772 +604 19772 +604 19771 +605 19771 +605 19770 +606 19770 +606 19770 +606 19769 +607 19769 +607 19768 +608 19768 +608 19767 +608 19767 +609 19766 +609 19766 +610 19765 +610 19765 +610 19764 +611 19764 +611 19763 +612 19763 +612 19762 +613 19762 +613 19761 +614 19761 +614 19760 +615 19759 +615 19759 +616 19758 +616 19758 +617 19757 +617 19757 +617 19757 +618 19756 +618 19755 +618 19755 +618 19754 +619 19754 +619 19753 +619 19753 +620 19752 +620 19751 +621 19751 +621 19751 +622 19750 +622 19750 +622 19749 +623 19749 +623 19749 +624 19748 +624 19747 +625 19747 +625 19746 +626 19746 +626 19745 +627 19745 +627 19744 +628 19744 +628 19743 +629 19743 +629 19743 +630 19742 +630 19741 +631 19741 +631 19741 +632 19740 +632 19740 +632 19739 +633 19739 +633 19738 +634 19738 +634 19737 +635 19737 +635 19736 +636 19735 +636 19735 +637 19734 +637 19734 +638 19733 +638 19732 +638 19732 +639 19732 +639 19731 +640 19730 +640 19730 +641 19730 +641 19729 +642 19728 +642 19728 +643 19727 +643 19727 +644 19726 +644 19725 +645 19725 +645 19724 +646 19724 +646 19723 +646 19723 +647 19722 +647 19722 +647 19721 +648 19721 +649 19720 +649 19720 +649 19719 +650 19719 +650 19718 +651 19718 +651 19717 +652 19716 +652 19716 +653 19715 +653 19715 +654 19714 +654 19714 +655 19713 +655 19712 +655 19712 +656 19711 +656 19711 +657 19710 +657 19709 +658 19709 +658 19708 +659 19708 +659 19707 +660 19706 +660 19706 +661 19705 +661 19705 +661 19704 +662 19704 +662 19703 +663 19703 +663 19702 +664 19702 +664 19701 +665 19701 +665 19700 +666 19700 +667 19699 +667 19699 +668 19698 +668 19698 +669 19697 +669 19697 +670 19696 +670 19696 +671 19695 +671 19695 +671 19694 +672 19694 +672 19693 +673 19692 +673 19692 +673 19691 +674 19691 +674 19690 +675 19689 +675 19689 +676 19688 +676 19688 +677 19687 +678 19686 +678 19686 +679 19685 +679 19685 +680 19685 +680 19684 +680 19683 +681 19683 +681 19682 +682 19682 +682 19681 +683 19681 +684 19680 +684 19680 +685 19679 +685 19679 +686 19678 +686 19678 +687 19677 +687 19677 +687 19676 +688 19675 +688 19675 +689 19674 +689 19674 +690 19673 +690 19673 +690 19672 +691 19672 +691 19671 +692 19671 +692 19670 +693 19670 +693 19669 +694 19668 +694 19668 +695 19667 +696 19667 +696 19666 +697 19665 +697 19665 +698 19664 +699 19664 +699 19663 +700 19662 +700 19662 +701 19661 +701 19660 +702 19660 +702 19659 +703 19659 +704 19658 +704 19657 +704 19657 +705 19656 +705 19655 +706 19655 +706 19654 +706 19654 +707 19653 +707 19653 +708 19652 +708 19651 +709 19651 +710 19650 +710 19650 +711 19649 +711 19649 +712 19649 +712 19648 +713 19647 +713 19647 +714 19646 +715 19646 +715 19645 +716 19645 +716 19645 +716 19644 +717 19643 +717 19643 +718 19642 +719 19641 +719 19640 +720 19640 +720 19639 +721 19639 +721 19638 +722 19638 +722 19637 +723 19636 +724 19636 +724 19635 +725 19635 +725 19634 +726 19634 +726 19633 +727 19633 +727 19632 +728 19632 +728 19631 +729 19631 +729 19630 +730 19630 +730 19629 +731 19629 +732 19628 +732 19628 +733 19627 +733 19626 +734 19626 +734 19625 +735 19625 +735 19624 +736 19624 +736 19623 +737 19622 +738 19622 +738 19621 +739 19621 +739 19620 +740 19620 +740 19619 +741 19618 +741 19618 +742 19617 +742 19617 +743 19617 +743 19616 +744 19616 +744 19615 +745 19614 +745 19614 +746 19613 +746 19613 +746 19612 +747 19611 +747 19611 +748 19610 +749 19610 +749 19609 +749 19608 +750 19607 +750 19607 +750 19606 +750 19606 +751 19605 +751 19604 +752 19604 +752 19603 +753 19603 +753 19602 +754 19601 +754 19601 +755 19600 +755 19599 +756 19599 +757 19598 +757 19598 +758 19597 +758 19597 +759 19596 +759 19595 +760 19595 +761 19595 +761 19594 +762 19593 +762 19593 +763 19592 +763 19592 +763 19591 +764 19591 +764 19590 +765 19589 +765 19589 +765 19588 +766 19587 +767 19587 +767 19586 +768 19586 +768 19585 +769 19584 +770 19584 +770 19583 +770 19583 +771 19582 +772 19581 +772 19581 +772 19580 +773 19579 +773 19579 +773 19578 +774 19578 +774 19577 +775 19577 +775 19576 +776 19575 +777 19575 +777 19574 +778 19574 +778 19573 +779 19573 +780 19572 +780 19572 +780 19571 +781 19571 +781 19570 +782 19570 +782 19569 +783 19568 +783 19568 +784 19567 +784 19567 +785 19566 +785 19566 +786 19565 +786 19565 +787 19564 +787 19564 +788 19563 +788 19562 +789 19562 +789 19561 +790 19561 +790 19560 +791 19560 +792 19559 +792 19559 +793 19558 +793 19557 +794 19557 +794 19556 +795 19556 +795 19556 +796 19555 +796 19555 +797 19554 +797 19554 +798 19553 +799 19553 +799 19552 +800 19552 +800 19551 +801 19550 +802 19550 +802 19549 +803 19548 +803 19548 +803 19547 +804 19547 +805 19546 +806 19545 +806 19545 +807 19545 +807 19544 +808 19544 +808 19544 +809 19543 +810 19542 +810 19542 +811 19541 +812 19541 +812 19540 +813 19540 +813 19539 +813 19539 +814 19538 +815 19538 +815 19537 +816 19537 +817 19536 +818 19536 +818 19535 +819 19534 +820 19534 +820 19534 +821 19533 +821 19533 +822 19532 +823 19532 +824 19531 +824 19531 +825 19530 +825 19529 +826 19529 +826 19528 +827 19528 +828 19527 +828 19527 +829 19526 +830 19525 +830 19525 +831 19524 +832 19524 +832 19524 +833 19523 +833 19522 +834 19522 +835 19521 +835 19521 +836 19520 +836 19520 +837 19519 +837 19519 +838 19518 +839 19518 +839 19517 +840 19517 +840 19516 +841 19516 +842 19515 +842 19515 +843 19514 +844 19513 +844 19513 +845 19512 +846 19512 +846 19511 +847 19511 +848 19510 +848 19510 +849 19509 +849 19508 +850 19508 +850 19507 +851 19507 +852 19506 +852 19506 +853 19505 +853 19505 +854 19504 +855 19503 +856 19503 +856 19502 +857 19502 +858 19501 +858 19501 +859 19500 +859 19499 +860 19499 +860 19498 +861 19498 +861 19497 +861 19497 +862 19496 +863 19496 +864 19495 +864 19495 +865 19494 +866 19494 +866 19493 +867 19492 +868 19492 +868 19491 +869 19491 +869 19490 +870 19490 +871 19489 +871 19488 +872 19488 +872 19487 +873 19487 +873 19486 +874 19486 +875 19485 +875 19485 +876 19484 +877 19483 +877 19483 +878 19482 +878 19482 +879 19481 +879 19481 +879 19480 +880 19479 +881 19479 +882 19478 +882 19478 +883 19478 +884 19477 +884 19476 +885 19476 +886 19475 +886 19475 +887 19474 +888 19474 +888 19473 +889 19472 +889 19472 +890 19471 +891 19471 +892 19470 +892 19470 +893 19469 +893 19468 +894 19468 +895 19467 +896 19467 +896 19466 +897 19466 +897 19465 +898 19465 +899 19464 +899 19463 +900 19463 +901 19462 +901 19462 +902 19461 +903 19461 +904 19460 +905 19459 +905 19459 +906 19458 +907 19458 +907 19457 +908 19457 +909 19456 +910 19455 +910 19455 +911 19454 +912 19454 +912 19453 +913 19453 +914 19452 +914 19451 +915 19451 +915 19450 +916 19450 +917 19449 +917 19449 +918 19448 +919 19447 +919 19447 +920 19446 +921 19446 +921 19445 +922 19445 +923 19444 +923 19443 +924 19443 +924 19442 +925 19442 +926 19441 +927 19441 +927 19440 +928 19439 +929 19439 +929 19438 +930 19438 +931 19437 +932 19437 +932 19436 +933 19435 +934 19435 +935 19434 +935 19434 +936 19433 +937 19433 +937 19432 +938 19431 +939 19431 +940 19430 +940 19430 +941 19429 +941 19429 +942 19428 +942 19428 +943 19427 +944 19427 +944 19426 +945 19425 +946 19425 +947 19424 +947 19424 +948 19423 +949 19423 +949 19422 +950 19421 +950 19421 +951 19420 +952 19420 +952 19419 +953 19419 +954 19418 +954 19417 +955 19417 +956 19416 +957 19416 +958 19415 +958 19415 +959 19414 +960 19413 +961 19413 +962 19412 +963 19412 +963 19411 +964 19411 +964 19410 +965 19409 +966 19409 +966 19408 +967 19408 +968 19407 +969 19407 +969 19406 +970 19405 +970 19405 +971 19404 +971 19404 +972 19403 +973 19403 +974 19402 +975 19401 +975 19401 +976 19400 +977 19400 +977 19399 +978 19398 +979 19398 +980 19397 +980 19397 +981 19396 +982 19396 +982 19395 +983 19394 +983 19394 +984 19393 +985 19393 +985 19392 +986 19392 +987 19391 +988 19390 +989 19390 +990 19389 +990 19389 +991 19388 +991 19387 +992 19387 +993 19386 +993 19386 +994 19385 +995 19385 +996 19384 +996 19383 +997 19383 +998 19382 +999 19382 +999 19381 +1000 19380 +1001 19380 +1001 19379 +1002 19379 +1003 19378 +1003 19378 +1004 19377 +1005 19376 +1006 19376 +1007 19375 +1007 19375 +1008 19374 +1009 19373 +1010 19373 +1010 19372 +1011 19372 +1012 19371 +1013 19371 +1014 19370 +1014 19369 +1015 19369 +1015 19368 +1016 19368 +1017 19367 +1018 19366 +1018 19366 +1019 19365 +1020 19365 +1021 19364 +1021 19363 +1022 19363 +1022 19362 +1023 19362 +1024 19361 +1025 19360 +1026 19360 +1026 19359 +1027 19359 +1027 19358 +1028 19358 +1029 19357 +1030 19356 +1031 19356 +1031 19355 +1032 19355 +1033 19354 +1034 19353 +1034 19353 +1035 19352 +1036 19352 +1037 19352 +1037 19351 +1038 19351 +1039 19350 +1039 19349 +1040 19349 +1041 19348 +1042 19348 +1042 19347 +1043 19346 +1044 19346 +1045 19345 +1045 19345 +1046 19344 +1047 19343 +1047 19343 +1048 19342 +1049 19342 +1050 19341 +1050 19340 +1051 19340 +1052 19339 +1053 19339 +1054 19338 +1054 19337 +1056 19337 +1056 19336 +1057 19336 +1058 19335 +1059 19334 +1059 19334 +1060 19333 +1061 19333 +1061 19332 +1062 19331 +1063 19331 +1064 19330 +1064 19330 +1065 19329 +1066 19328 +1067 19328 +1068 19328 +1069 19327 +1069 19326 +1070 19326 +1071 19325 +1072 19324 +1073 19324 +1073 19323 +1074 19323 +1075 19322 +1076 19321 +1077 19321 +1077 19320 +1078 19320 +1079 19319 +1080 19319 +1081 19318 +1081 19318 +1082 19317 +1082 19316 +1083 19316 +1084 19315 +1085 19314 +1085 19314 +1086 19313 +1087 19313 +1088 19312 +1088 19311 +1089 19311 +1090 19310 +1091 19310 +1092 19309 +1093 19308 +1093 19308 +1094 19307 +1094 19306 +1095 19306 +1096 19305 +1097 19305 +1098 19304 +1098 19303 +1099 19303 +1100 19302 +1101 19301 +1102 19301 +1102 19300 +1103 19300 +1104 19299 +1105 19298 +1106 19298 +1107 19297 +1107 19297 +1108 19296 +1109 19295 +1109 19295 +1110 19294 +1111 19294 +1112 19293 +1113 19293 +1113 19292 +1115 19291 +1115 19291 +1116 19290 +1117 19290 +1117 19289 +1118 19288 +1119 19288 +1120 19287 +1121 19287 +1121 19286 +1122 19285 +1123 19285 +1124 19284 +1124 19283 +1125 19283 +1126 19282 +1126 19282 +1127 19281 +1128 19280 +1129 19280 +1130 19279 +1131 19278 +1131 19278 +1132 19277 +1133 19277 +1133 19276 +1134 19275 +1135 19275 +1136 19274 +1137 19273 +1137 19273 +1138 19272 +1139 19272 +1139 19271 +1140 19270 +1141 19270 +1142 19269 +1143 19268 +1143 19268 +1144 19267 +1144 19267 +1145 19266 +1146 19266 +1147 19265 +1148 19264 +1148 19264 +1149 19263 +1150 19263 +1151 19262 +1152 19261 +1153 19261 +1154 19260 +1154 19260 +1155 19259 +1156 19258 +1157 19258 +1158 19257 +1159 19256 +1159 19256 +1160 19255 +1161 19254 +1162 19254 +1163 19253 +1163 19253 +1164 19252 +1164 19252 +1165 19251 +1166 19250 +1167 19250 +1167 19249 +1168 19248 +1169 19248 +1170 19247 +1171 19247 +1172 19246 +1173 19245 +1174 19245 +1175 19244 +1176 19243 +1177 19243 +1177 19242 +1178 19241 +1179 19241 +1180 19240 +1181 19239 +1181 19239 +1182 19238 +1183 19237 +1184 19237 +1185 19236 +1185 19235 +1186 19235 +1186 19234 +1187 19233 +1188 19233 +1189 19232 +1190 19232 +1191 19231 +1192 19230 +1192 19230 +1193 19229 +1193 19228 +1194 19228 +1195 19227 +1196 19226 +1197 19226 +1197 19225 +1198 19224 +1199 19224 +1200 19223 +1200 19222 +1201 19222 +1202 19221 +1203 19220 +1203 19220 +1204 19219 +1205 19218 +1206 19217 +1207 19217 +1209 19216 +1210 19215 +1210 19215 +1211 19214 +1212 19213 +1213 19213 +1214 19212 +1215 19211 +1215 19210 +1216 19210 +1217 19209 +1217 19208 +1218 19208 +1219 19207 +1220 19206 +1221 19206 +1221 19205 +1222 19204 +1223 19204 +1224 19203 +1225 19202 +1226 19201 +1227 19201 +1228 19200 +1228 19199 +1229 19199 +1230 19198 +1231 19197 +1232 19197 +1233 19196 +1234 19195 +1235 19194 +1236 19194 +1237 19193 +1237 19192 +1238 19192 +1239 19191 +1240 19190 +1241 19190 +1242 19189 +1243 19188 +1243 19187 +1244 19187 +1245 19186 +1246 19185 +1247 19185 +1248 19184 +1249 19183 +1250 19182 +1251 19182 +1251 19181 +1252 19180 +1253 19179 +1254 19179 +1254 19178 +1255 19177 +1256 19177 +1257 19176 +1258 19175 +1258 19174 +1259 19174 +1260 19173 +1261 19172 +1262 19172 +1263 19171 +1264 19170 +1265 19169 +1265 19169 +1266 19168 +1267 19167 +1268 19166 +1269 19166 +1270 19165 +1271 19164 +1272 19163 +1273 19163 +1274 19162 +1274 19161 +1275 19161 +1276 19160 +1276 19159 +1277 19158 +1278 19158 +1279 19157 +1280 19156 +1281 19155 +1281 19155 +1282 19154 +1283 19153 +1284 19152 +1284 19151 +1285 19151 +1286 19150 +1287 19149 +1288 19148 +1289 19148 +1290 19147 +1291 19146 +1292 19145 +1292 19145 +1293 19144 +1294 19143 +1295 19142 +1296 19142 +1297 19141 +1299 19140 +1300 19139 +1300 19139 +1301 19138 +1302 19137 +1302 19136 +1303 19135 +1305 19135 +1306 19134 +1307 19133 +1308 19132 +1309 19131 +1310 19131 +1310 19130 +1311 19129 +1312 19128 +1312 19128 +1313 19127 +1313 19126 +1314 19125 +1315 19124 +1316 19124 +1317 19123 +1318 19122 +1319 19121 +1319 19121 +1320 19120 +1321 19119 +1322 19118 +1322 19117 +1323 19117 +1324 19116 +1325 19115 +1326 19114 +1327 19113 +1328 19113 +1329 19112 +1330 19111 +1331 19110 +1332 19110 +1333 19109 +1333 19108 +1334 19107 +1335 19106 +1336 19106 +1337 19105 +1338 19104 +1339 19103 +1340 19102 +1341 19102 +1342 19101 +1343 19100 +1343 19099 +1344 19098 +1345 19098 +1346 19097 +1347 19096 +1348 19095 +1349 19094 +1350 19094 +1351 19093 +1352 19092 +1353 19092 +1353 19091 +1354 19090 +1355 19089 +1356 19088 +1357 19088 +1358 19087 +1359 19086 +1360 19085 +1360 19084 +1361 19084 +1362 19083 +1363 19082 +1365 19081 +1366 19080 +1366 19079 +1367 19079 +1368 19078 +1369 19077 +1370 19076 +1371 19075 +1372 19074 +1373 19074 +1374 19073 +1375 19072 +1376 19071 +1376 19070 +1377 19070 +1378 19069 +1379 19068 +1380 19067 +1380 19066 +1381 19065 +1382 19065 +1383 19064 +1384 19063 +1385 19062 +1386 19061 +1387 19060 +1388 19059 +1389 19058 +1390 19057 +1391 19057 +1392 19056 +1393 19055 +1393 19054 +1395 19053 +1395 19051 +1396 19050 +1397 19049 +1399 19049 +1399 19048 +1400 19047 +1401 19046 +1402 19045 +1403 19044 +1404 19044 +1405 19043 +1406 19042 +1408 19041 +1409 19040 +1410 19039 +1411 19038 +1412 19038 +1413 19037 +1414 19036 +1414 19035 +1415 19034 +1417 19033 +1417 19033 +1418 19032 +1419 19031 +1420 19030 +1421 19029 +1421 19028 +1422 19027 +1424 19027 +1424 19026 +1425 19025 +1426 19024 +1427 19023 +1428 19022 +1428 19022 +1429 19021 +1430 19020 +1431 19019 +1433 19018 +1434 19017 +1435 19016 +1436 19016 +1437 19015 +1438 19014 +1439 19013 +1440 19012 +1441 19011 +1442 19011 +1443 19010 +1444 19009 +1445 19008 +1446 19007 +1447 19006 +1448 19005 +1449 19004 +1451 19004 +1452 19003 +1453 19002 +1454 19001 +1455 19000 +1456 18999 +1457 18998 +1458 18998 +1458 18997 +1460 18996 +1460 18995 +1461 18994 +1462 18994 +1463 18993 +1463 18992 +1465 18991 +1466 18990 +1467 18989 +1468 18988 +1469 18987 +1470 18987 +1471 18986 +1472 18985 +1473 18984 +1474 18983 +1475 18982 +1476 18981 +1477 18980 +1479 18979 +1479 18979 +1481 18978 +1482 18977 +1483 18976 +1484 18975 +1485 18974 +1486 18973 +1488 18973 +1489 18972 +1489 18971 +1491 18970 +1492 18969 +1493 18968 +1494 18967 +1496 18966 +1496 18965 +1497 18964 +1499 18963 +1500 18963 +1501 18962 +1502 18961 +1503 18960 +1504 18959 +1505 18958 +1506 18957 +1507 18956 +1508 18956 +1509 18955 +1510 18954 +1511 18953 +1512 18952 +1513 18951 +1514 18950 +1515 18949 +1516 18948 +1517 18947 +1518 18946 +1519 18945 +1520 18944 +1521 18944 +1522 18943 +1524 18942 +1524 18941 +1525 18940 +1527 18939 +1527 18938 +1528 18937 +1530 18936 +1531 18935 +1532 18934 +1533 18933 +1534 18932 +1535 18931 +1536 18930 +1538 18929 +1538 18928 +1539 18927 +1541 18926 +1541 18925 +1542 18925 +1543 18924 +1544 18923 +1546 18922 +1547 18921 +1548 18920 +1549 18919 +1550 18918 +1552 18917 +1553 18916 +1554 18915 +1556 18914 +1557 18913 +1558 18912 +1559 18910 +1560 18909 +1561 18908 +1562 18907 +1563 18906 +1564 18905 +1565 18903 +1566 18902 +1566 18901 +1568 18900 +1568 18899 +1569 18897 +1571 18896 +1572 18895 +1573 18894 +1574 18893 +1575 18892 +1576 18891 +1577 18890 +1579 18889 +1579 18888 +1581 18887 +1582 18886 +1583 18885 +1584 18884 +1585 18883 +1586 18882 +1587 18881 +1589 18880 +1589 18879 +1590 18878 +1591 18877 +1593 18876 +1594 18875 +1595 18874 +1596 18873 +1597 18871 +1599 18870 +1600 18869 +1601 18868 +1603 18867 +1604 18866 +1605 18865 +1606 18864 +1607 18863 +1608 18862 +1609 18861 +1610 18860 +1612 18859 +1613 18858 +1614 18857 +1615 18856 +1616 18855 +1618 18854 +1619 18853 +1621 18852 +1621 18851 +1623 18849 +1624 18848 +1625 18847 +1626 18846 +1627 18845 +1629 18844 +1629 18843 +1630 18842 +1631 18841 +1633 18840 +1634 18839 +1635 18838 +1637 18837 +1638 18836 +1640 18835 +1641 18834 +1642 18833 +1643 18831 +1644 18830 +1645 18829 +1646 18828 +1647 18827 +1648 18826 +1650 18825 +1651 18824 +1653 18823 +1654 18822 +1655 18821 +1657 18819 +1658 18818 +1659 18817 +1660 18816 +1662 18815 +1663 18814 +1664 18813 +1666 18812 +1667 18811 +1668 18809 +1669 18808 +1671 18807 +1671 18806 +1672 18805 +1673 18804 +1674 18803 +1675 18802 +1677 18800 +1678 18799 +1679 18798 +1680 18797 +1682 18796 +1683 18795 +1683 18794 +1684 18792 +1685 18791 +1687 18790 +1688 18789 +1689 18788 +1691 18787 +1692 18785 +1693 18784 +1694 18783 +1695 18782 +1695 18781 +1697 18780 +1698 18778 +1699 18777 +1700 18776 +1702 18775 +1703 18774 +1703 18772 +1704 18771 +1705 18770 +1706 18769 +1708 18768 +1709 18766 +1711 18765 +1712 18764 +1712 18763 +1713 18762 +1714 18760 +1715 18759 +1717 18758 +1718 18757 +1719 18755 +1721 18754 +1722 18753 +1723 18752 +1724 18751 +1724 18749 +1726 18748 +1727 18747 +1728 18746 +1730 18744 +1732 18743 +1732 18742 +1733 18741 +1735 18739 +1736 18738 +1737 18737 +1739 18736 +1740 18734 +1741 18733 +1742 18732 +1742 18730 +1744 18729 +1746 18728 +1747 18727 +1748 18725 +1748 18724 +1749 18723 +1750 18721 +1752 18720 +1753 18719 +1754 18718 +1755 18716 +1756 18715 +1757 18714 +1759 18712 +1760 18711 +1762 18710 +1763 18708 +1764 18707 +1765 18706 +1766 18704 +1768 18703 +1769 18702 +1770 18700 +1772 18699 +1773 18698 +1774 18696 +1775 18695 +1776 18694 +1778 18692 +1779 18691 +1780 18690 +1781 18688 +1782 18687 +1784 18686 +1785 18684 +1787 18683 +1788 18682 +1789 18680 +1790 18679 +1791 18677 +1793 18676 +1794 18675 +1794 18673 +1795 18672 +1797 18671 +1799 18669 +1800 18668 +1802 18666 +1804 18665 +1804 18664 +1805 18662 +1807 18661 +1808 18659 +1809 18658 +1811 18657 +1812 18655 +1814 18654 +1815 18652 +1816 18651 +1818 18649 +1819 18648 +1821 18647 +1821 18645 +1823 18644 +1825 18642 +1826 18641 +1826 18639 +1828 18638 +1830 18637 +1831 18635 +1832 18634 +1833 18632 +1834 18631 +1836 18629 +1837 18628 +1839 18626 +1841 18625 +1842 18623 +1843 18622 +1844 18620 +1845 18619 +1846 18618 +1848 18616 +1849 18615 +1851 18613 +1852 18612 +1854 18610 +1856 18609 +1857 18607 +1859 18606 +1860 18604 +1862 18603 +1863 18601 +1865 18600 +1866 18598 +1868 18597 +1869 18595 +1871 18594 +1872 18592 +1873 18590 +1875 18589 +1877 18587 +1879 18586 +1880 18584 +1881 18583 +1882 18581 +1884 18580 +1885 18578 +1886 18577 +1888 18575 +1889 18574 +1891 18572 +1892 18570 +1893 18569 +1894 18567 +1895 18566 +1897 18564 +1898 18563 +1900 18561 +1901 18559 +1903 18557 +1904 18556 +1906 18554 +1907 18552 +1908 18551 +1909 18549 +1910 18548 +1912 18546 +1913 18545 +1915 18543 +1917 18541 +1919 18540 +1920 18538 +1921 18537 +1922 18535 +1923 18533 +1924 18532 +1926 18530 +1927 18529 +1929 18527 +1930 18525 +1932 18524 +1934 18522 +1935 18520 +1937 18519 +1938 18517 +1940 18516 +1941 18514 +1942 18512 +1943 18511 +1944 18509 +1945 18507 +1946 18506 +1948 18504 +1949 18502 +1952 18501 +1953 18499 +1955 18497 +1956 18496 +1958 18494 +1960 18492 +1962 18491 +1963 18489 +1964 18487 +1965 18486 +1966 18484 +1967 18482 +1969 18481 +1970 18479 +1971 18477 +1972 18476 +1973 18474 +1975 18472 +1976 18471 +1978 18469 +1980 18467 +1982 18465 +1983 18464 +1985 18462 +1986 18460 +1988 18459 +1991 18457 +1993 18455 +1994 18453 +1996 18452 +1998 18450 +2000 18448 +2001 18447 +2002 18445 +2003 18443 +2005 18441 +2006 18440 +2007 18438 +2008 18436 +2009 18435 +2010 18433 +2012 18431 +2013 18429 +2014 18428 +2015 18426 +2017 18424 +2018 18422 +2019 18421 +2020 18419 +2021 18417 +2022 18415 +2024 18414 +2025 18412 +2027 18410 +2030 18408 +2031 18406 +2032 18405 +2033 18403 +2035 18401 +2036 18399 +2037 18397 +2039 18396 +2040 18394 +2041 18392 +2043 18390 +2044 18388 +2045 18387 +2047 18385 +2048 18383 +2049 18381 +2051 18380 +2052 18378 +2053 18376 +2055 18374 +2056 18372 +2058 18371 +2059 18369 +2060 18367 +2062 18365 +2063 18363 +2064 18361 +2067 18360 +2068 18358 +2070 18356 +2071 18354 +2074 18352 +2076 18350 +2079 18349 +2080 18347 +2082 18345 +2083 18343 +2086 18341 +2088 18339 +2091 18337 +2092 18336 +2093 18334 +2094 18332 +2097 18330 +2099 18328 +2100 18326 +2101 18324 +2103 18323 +2104 18321 +2105 18319 +2107 18317 +2109 18315 +2111 18313 +2114 18311 +2116 18309 +2118 18307 +2120 18306 +2121 18304 +2124 18302 +2125 18300 +2127 18298 +2128 18296 +2129 18293 +2131 18292 +2132 18290 +2135 18287 +2136 18285 +2138 18283 +2141 18281 +2143 18279 +2145 18277 +2146 18276 +2148 18273 +2150 18271 +2153 18268 +2154 18266 +2156 18263 +2158 18261 +2160 18259 +2162 18257 +2164 18255 +2166 18254 +2168 18252 +2169 18250 +2171 18248 +2172 18246 +2175 18244 +2177 18242 +2179 18240 +2181 18238 +2182 18236 +2185 18235 +2186 18233 +2188 18231 +2189 18229 +2191 18227 +2194 18225 +2195 18223 +2198 18221 +2199 18219 +2202 18217 +2204 18215 +2206 18213 +2208 18212 +2210 18210 +2213 18208 +2214 18206 +2216 18204 +2217 18202 +2219 18200 +2222 18198 +2223 18196 +2225 18194 +2227 18192 +2229 18190 +2230 18188 +2233 18186 +2235 18184 +2237 18182 +2240 18180 +2241 18178 +2244 18176 +2246 18175 +2248 18173 +2251 18171 +2253 18169 +2256 18167 +2257 18165 +2260 18163 +2262 18161 +2264 18159 +2265 18157 +2267 18155 +2269 18153 +2272 18151 +2273 18149 +2275 18147 +2278 18145 +2280 18143 +2282 18141 +2285 18139 +2287 18137 +2289 18135 +2291 18133 +2293 18131 +2296 18129 +2298 18127 +2300 18125 +2302 18123 +2305 18121 +2306 18119 +2309 18117 +2311 18115 +2313 18113 +2315 18111 +2316 18109 +2319 18107 +2320 18105 +2322 18103 +2325 18101 +2326 18099 +2328 18097 +2331 18095 +2333 18093 +2335 18091 +2337 18089 +2339 18087 +2342 18085 +2343 18083 +2345 18081 +2348 18079 +2349 18077 +2352 18075 +2354 18073 +2356 18071 +2358 18069 +2360 18067 +2363 18065 +2364 18063 +2366 18061 +2368 18059 +2369 18057 +2372 18055 +2374 18053 +2376 18051 +2378 18048 +2380 18046 +2382 18044 +2385 18042 +2387 18040 +2390 18037 +2392 18035 +2394 18033 +2397 18031 +2398 18028 +2401 18026 +2403 18024 +2405 18022 +2407 18020 +2410 18018 +2412 18016 +2414 18014 +2417 18011 +2419 18010 +2421 18007 +2423 18005 +2425 18003 +2428 18001 +2430 17999 +2433 17997 +2435 17995 +2438 17993 +2440 17991 +2442 17989 +2444 17987 +2447 17985 +2449 17983 +2451 17981 +2454 17979 +2456 17977 +2458 17975 +2459 17973 +2462 17971 +2464 17969 +2466 17967 +2468 17965 +2471 17963 +2473 17961 +2475 17959 +2478 17957 +2480 17955 +2482 17953 +2483 17951 +2485 17949 +2487 17947 +2489 17945 +2491 17943 +2493 17941 +2495 17939 +2497 17937 +2499 17935 +2500 17933 +2503 17931 +2505 17929 +2506 17927 +2509 17925 +2510 17922 +2512 17920 +2515 17918 +2517 17916 +2519 17914 +2521 17912 +2523 17910 +2525 17908 +2527 17906 +2530 17904 +2531 17902 +2533 17900 +2535 17898 +2537 17896 +2539 17894 +2541 17892 +2542 17890 +2544 17888 +2546 17886 +2548 17884 +2551 17882 +2553 17880 +2555 17878 +2558 17876 +2560 17873 +2561 17871 +2563 17867 +2565 17865 +2566 17863 +2568 17860 +2570 17858 +2573 17856 +2575 17854 +2577 17852 +2580 17850 +2582 17848 +2585 17846 +2586 17843 +2588 17841 +2590 17839 +2592 17837 +2594 17835 +2596 17833 +2599 17831 +2601 17829 +2603 17826 +2605 17823 +2607 17821 +2609 17819 +2611 17817 +2613 17814 +2616 17812 +2618 17810 +2620 17808 +2622 17806 +2623 17804 +2625 17802 +2627 17799 +2630 17797 +2632 17795 +2634 17792 +2635 17790 +2637 17787 +2639 17784 +2641 17782 +2642 17780 +2644 17778 +2645 17776 +2647 17774 +2649 17771 +2652 17769 +2654 17767 +2656 17764 +2658 17762 +2660 17760 +2662 17757 +2665 17755 +2667 17753 +2669 17749 +2670 17747 +2671 17746 +2674 17742 +2676 17740 +2678 17738 +2680 17736 +2680 17733 +2683 17731 +2685 17729 +2687 17726 +2689 17723 +2691 17720 +2692 17718 +2694 17716 +2696 17713 +2698 17711 +2701 17709 +2701 17707 +2703 17704 +2705 17701 +2707 17699 +2708 17696 +2710 17694 +2712 17691 +2714 17689 +2716 17686 +2718 17684 +2720 17681 +2723 17679 +2725 17676 +2727 17674 +2729 17671 +2731 17669 +2732 17666 +2734 17664 +2736 17662 +2738 17660 +2740 17657 +2742 17654 +2744 17651 +2745 17648 +2747 17646 +2749 17643 +2752 17642 +2753 17640 +2756 17638 +2758 17636 +2760 17635 +2761 17633 +2763 17631 +2765 17628 +2765 17625 +2768 17622 +2770 17619 +2772 17616 +2774 17613 +2775 17610 +2776 17607 +2778 17604 +2780 17601 +2780 17598 +2783 17595 +2785 17594 +2785 17591 +2788 17589 +2790 17587 +2792 17586 +2794 17584 +2796 17582 +2796 17580 +2799 17578 +2801 17575 +2803 17572 +2805 17570 +2807 17567 +2809 17564 +2811 17561 +2813 17558 +2815 17555 +2820 17577 +2822 17574 +2824 17572 +2826 17570 +2828 17568 +2830 17566 +2832 17564 +2834 17561 +2836 17559 +2837 17557 +2839 17555 +2841 17553 +2843 17550 +2845 17548 +2847 17546 +2849 17544 +2851 17541 +2853 17539 +2855 17537 +2857 17535 +2859 17533 +2861 17530 +2863 17528 +2865 17526 +2867 17524 +2869 17521 +2871 17519 +2873 17517 +2875 17515 +2876 17513 +2878 17510 +2880 17508 +2882 17506 +2884 17504 +2886 17501 +2888 17499 +2890 17497 +2892 17494 +2894 17492 +2896 17490 +2898 17488 +2900 17485 +2902 17483 +2904 17481 +2906 17479 +2908 17476 +2910 17474 +2912 17472 +2914 17469 +2916 17467 +2918 17465 +2919 17463 +2921 17460 +2923 17458 +2925 17456 +2927 17453 +2929 17451 +2931 17449 +2933 17446 +2935 17444 +2937 17442 +2939 17440 +2941 17437 +2943 17435 +2945 17433 +2947 17430 +2949 17428 +2951 17426 +2953 17423 +2955 17421 +2957 17418 +2959 17416 +2961 17414 +2963 17411 +2965 17409 +2967 17407 +2968 17404 +2970 17402 +2972 17400 +2974 17397 +2976 17395 +2978 17393 +2980 17390 +2982 17388 +2984 17385 +2986 17383 +2988 17381 +2990 17378 +2992 17376 +2994 17373 +2996 17371 +2998 17369 +3000 17366 +3002 17364 +3004 17361 +3006 17359 +3008 17357 +3010 17354 +3012 17352 +3014 17349 +3016 17347 +3018 17344 +3020 17342 +3022 17339 +3024 17337 +3026 17335 +3028 17332 +3029 17330 +3031 17327 +3033 17325 +3035 17322 +3037 17320 +3039 17317 +3041 17315 +3043 17312 +3045 17310 +3047 17307 +3049 17305 +3051 17302 +3053 17300 +3055 17297 +3057 17295 +3059 17292 +3061 17290 +3063 17287 +3065 17285 +3067 17282 +3069 17280 +3071 17277 +3073 17275 +3075 17272 +3077 17270 +3079 17267 +3081 17265 +3083 17262 +3085 17260 +3087 17257 +3089 17255 +3091 17252 +3093 17249 +3095 17247 +3097 17244 +3099 17242 +3101 17239 +3103 17237 +3105 17234 +3107 17231 +3109 17229 +3111 17226 +3113 17224 +3115 17221 +3117 17218 +3119 17216 +3121 17213 +3123 17211 +3125 17208 +3127 17205 +3129 17203 +3131 17200 +3133 17197 +3135 17195 +3137 17192 +3139 17190 +3141 17187 +3143 17184 +3145 17182 +3147 17179 +3149 17176 +3151 17174 +3153 17171 +3155 17168 +3157 17166 +3159 17163 +3161 17160 +3163 17157 +3165 17155 +3167 17152 +3169 17149 +3171 17147 +3173 17144 +3175 17141 +3177 17139 +3179 17136 +3181 17133 +3183 17130 +3185 17128 +3187 17125 +3189 17122 +3191 17119 +3193 17117 +3195 17114 +3197 17111 +3199 17108 +3201 17106 +3203 17103 +3205 17100 +3207 17097 +3209 17094 +3211 17092 +3213 17089 +3215 17086 +3217 17083 +3219 17080 +3221 17078 +3223 17075 +3225 17072 +3227 17069 +3229 17066 +3231 17064 +3233 17061 +3235 17058 +3237 17055 +3239 17052 +3241 17049 +3243 17046 +3245 17044 +3247 17041 +3249 17038 +3251 17035 +3253 17032 +3255 17029 +3257 17026 +3259 17023 +3262 17021 +3264 17018 +3266 17015 +3268 17012 +3270 17009 +3272 17006 +3274 17003 +3276 17000 +3278 16997 +3280 16994 +3282 16991 +3284 16988 +3286 16985 +3288 16982 +3290 16980 +3292 16977 +3294 16974 +3296 16971 +3298 16968 +3300 16965 +3302 16962 +3304 16959 +3306 16956 +3308 16953 +3311 16950 +3313 16947 +3315 16944 +3317 16941 +3319 16938 +3321 16935 +3323 16931 +3325 16928 +3327 16925 +3329 16922 +3331 16919 +3333 16916 +3335 16913 +3337 16910 +3339 16907 +3341 16904 +3343 16901 +3346 16898 +3348 16895 +3350 16892 +3352 16888 +3354 16885 +3356 16882 +3358 16879 +3360 16876 +3362 16873 +3364 16870 +3366 16867 +3368 16863 +3370 16860 +3372 16857 +3374 16854 +3377 16851 +3379 16848 +3381 16844 +3383 16841 +3385 16838 +3387 16835 +3389 16832 +3391 16829 +3393 16825 +3395 16822 +3397 16819 +3399 16816 +3402 16812 +3404 16809 +3406 16806 +3408 16803 +3410 16800 +3412 16796 +3414 16793 +3416 16790 +3418 16786 +3420 16783 +3422 16780 +3425 16777 +3427 16773 +3429 16770 +3431 16767 +3433 16763 +3435 16760 +3437 16757 +3439 16753 +3441 16750 +3443 16747 +3446 16743 +3448 16740 +3450 16737 +3452 16733 +3454 16730 +3456 16727 +3458 16723 +3460 16720 +3462 16717 +3465 16713 +3467 16710 +3469 16706 +3471 16703 +3473 16700 +3475 16696 +3477 16693 +3479 16689 +3482 16686 +3484 16682 +3486 16679 +3488 16676 +3490 16672 +3492 16669 +3494 16665 +3496 16662 +3499 16658 +3501 16655 +3503 16651 +3505 16648 +3507 16644 +3509 16641 +3511 16637 +3513 16634 +3516 16630 +3518 16627 +3520 16623 +3522 16620 +3524 16616 +3526 16612 +3528 16609 +3531 16605 +3533 16602 +3535 16598 +3537 16595 +3539 16591 +3541 16587 +3543 16584 +3546 16580 +3548 16577 +3550 16573 +3552 16569 +3554 16566 +3556 16562 +3559 16558 +3561 16555 +3563 16551 +3565 16547 +3567 16544 +3569 16540 +3572 16536 +3574 16533 +3576 16529 +3578 16525 +3580 16522 +3582 16518 +3585 16514 +3587 16511 +3589 16507 +3591 16503 +3593 16499 +3595 16496 +3598 16492 +3600 16488 +3602 16484 +3604 16480 +3606 16477 +3609 16473 +3611 16469 +3613 16465 +3615 16462 +3617 16458 +3619 16454 +3622 16450 +3624 16446 +3626 16442 +3628 16439 +3630 16435 +3633 16431 +3635 16427 +3637 16423 +3639 16419 +3641 16415 +3644 16411 +3646 16408 +3648 16404 +3650 16400 +3652 16396 +3655 16392 +3657 16388 +3659 16384 +3661 16380 +3664 16376 +3666 16372 +3668 16368 +3670 16364 +3672 16360 +3675 16356 +3677 16352 +3679 16348 +3681 16344 +3683 16340 +3686 16336 +3688 16332 +3690 16328 +3692 16324 +3695 16320 +3697 16316 +3699 16312 +3701 16308 +3704 16304 +3706 16300 +3708 16296 +3710 16292 +3713 16288 +3715 16284 +3717 16279 +3719 16275 +3722 16271 +3724 16267 +3726 16263 +3728 16259 +3731 16255 +3733 16251 +3735 16246 +3737 16242 +3740 16238 +3742 16234 +3744 16230 +3744 16230 +3744 16230 +3744 16230 +3744 16230 +3744 16230 +3746 16225 diff --git a/Analysis/config/exclusionMask_550.txt.png b/Analysis/config/exclusionMask_550.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..1651eb69c8497444be0fd9d339aa5f08e43ea679 GIT binary patch literal 354139 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�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

q8no#fl=zwaQIoNrP;emZo1^c?S&@-D9> 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 LfT(?uhV1_b&di8u literal 0 HcmV?d00001 diff --git a/Analysis/config/exclusionMask_PQ.txt b/Analysis/config/exclusionMask_PQ.txt new file mode 100644 index 00000000..c668133c --- /dev/null +++ b/Analysis/config/exclusionMask_PQ.txt @@ -0,0 +1,21297 @@ +30912 21296 +6713 24680 +6707 24685 +6700 24689 +6693 24694 +6687 24699 +6680 24703 +6673 24708 +6667 24713 +6667 24713 +6667 24713 +6667 24713 +6667 24713 +6660 24717 +6654 24722 +6647 24726 +6640 24731 +6634 24736 +6627 24740 +6621 24745 +6614 24749 +6608 24754 +6601 24759 +6595 24763 +6588 24768 +6582 24772 +6576 24777 +6569 24781 +6563 24786 +6556 24790 +6550 24795 +6543 24799 +6537 24804 +6531 24808 +6524 24813 +6518 24817 +6512 24822 +6505 24826 +6499 24830 +6493 24835 +6487 24839 +6480 24844 +6474 24848 +6468 24852 +6462 24857 +6455 24861 +6449 24866 +6443 24870 +6437 24874 +6431 24879 +6425 24883 +6418 24887 +6412 24892 +6406 24896 +6400 24900 +6394 24905 +6388 24909 +6382 24913 +6376 24917 +6370 24922 +6364 24926 +6358 24930 +6352 24934 +6346 24939 +6340 24943 +6334 24947 +6328 24951 +6322 24956 +6316 24960 +6310 24964 +6304 24968 +6298 24972 +6292 24976 +6286 24981 +6280 24985 +6275 24989 +6269 24993 +6263 24997 +6257 25001 +6251 25005 +6245 25010 +6240 25014 +6234 25018 +6228 25022 +6222 25026 +6217 25030 +6211 25034 +6205 25038 +6199 25042 +6194 25046 +6188 25050 +6182 25054 +6177 25058 +6171 25062 +6165 25066 +6160 25070 +6154 25074 +6149 25078 +6143 25082 +6137 25086 +6132 25090 +6126 25094 +6121 25098 +6115 25102 +6110 25106 +6104 25110 +6099 25114 +6093 25118 +6088 25122 +6082 25126 +6077 25130 +6071 25134 +6066 25137 +6060 25141 +6055 25145 +6049 25149 +6044 25153 +6039 25157 +6033 25161 +6028 25164 +6023 25168 +6017 25172 +6012 25176 +6007 25180 +6001 25184 +5996 25187 +5991 25191 +5985 25195 +5980 25199 +5975 25202 +5970 25206 +5964 25210 +5959 25214 +5954 25217 +5949 25221 +5943 25225 +5938 25229 +5933 25232 +5928 25236 +5923 25240 +5918 25243 +5912 25247 +5907 25251 +5902 25255 +5897 25258 +5892 25262 +5887 25266 +5882 25269 +5877 25273 +5872 25276 +5867 25280 +5862 25284 +5857 25287 +5852 25291 +5847 25295 +5842 25298 +5837 25302 +5832 25305 +5827 25309 +5822 25313 +5817 25316 +5812 25320 +5807 25323 +5802 25327 +5797 25330 +5792 25334 +5787 25337 +5782 25341 +5778 25344 +5773 25348 +5768 25351 +5763 25355 +5758 25358 +5753 25362 +5749 25365 +5744 25369 +5739 25372 +5734 25376 +5730 25379 +5725 25383 +5720 25386 +5715 25390 +5711 25393 +5706 25396 +5701 25400 +5696 25403 +5692 25407 +5687 25410 +5682 25414 +5678 25417 +5673 25420 +5668 25424 +5664 25427 +5659 25430 +5655 25434 +5650 25437 +5645 25440 +5641 25444 +5636 25447 +5632 25451 +5627 25454 +5623 25457 +5618 25460 +5613 25464 +5609 25467 +5604 25470 +5600 25474 +5595 25477 +5591 25480 +5586 25484 +5582 25487 +5578 25490 +5573 25493 +5569 25497 +5564 25500 +5560 25503 +5555 25506 +5551 25510 +5547 25513 +5542 25516 +5538 25519 +5534 25522 +5529 25526 +5525 25529 +5520 25532 +5516 25535 +5512 25538 +5508 25542 +5503 25545 +5499 25548 +5495 25551 +5490 25554 +5486 25557 +5482 25560 +5478 25564 +5473 25567 +5469 25570 +5465 25573 +5461 25576 +5456 25579 +5452 25582 +5448 25585 +5444 25589 +5440 25592 +5436 25595 +5431 25598 +5427 25601 +5423 25604 +5419 25607 +5415 25610 +5411 25613 +5407 25616 +5403 25619 +5398 25622 +5394 25625 +5390 25628 +5386 25631 +5382 25634 +5378 25637 +5374 25640 +5370 25643 +5366 25646 +5362 25649 +5358 25652 +5354 25655 +5350 25658 +5346 25661 +5342 25664 +5338 25667 +5334 25670 +5330 25673 +5326 25676 +5322 25679 +5318 25682 +5315 25685 +5311 25688 +5307 25691 +5303 25694 +5299 25696 +5295 25699 +5291 25702 +5287 25705 +5284 25708 +5280 25711 +5276 25714 +5272 25717 +5268 25720 +5264 25722 +5261 25725 +5257 25728 +5253 25731 +5249 25734 +5246 25737 +5242 25740 +5238 25742 +5234 25745 +5230 25748 +5227 25751 +5223 25754 +5219 25757 +5216 25759 +5212 25762 +5208 25765 +5205 25768 +5201 25771 +5197 25773 +5194 25776 +5190 25779 +5186 25782 +5183 25784 +5179 25787 +5175 25790 +5172 25793 +5168 25795 +5164 25798 +5161 25801 +5157 25804 +5154 25806 +5150 25809 +5147 25812 +5143 25815 +5139 25817 +5136 25820 +5132 25823 +5129 25825 +5125 25828 +5122 25831 +5118 25834 +5115 25836 +5111 25839 +5108 25842 +5104 25844 +5101 25847 +5097 25850 +5094 25852 +5090 25855 +5087 25858 +5084 25860 +5080 25863 +5077 25865 +5073 25868 +5070 25871 +5066 25873 +5063 25876 +5060 25879 +5056 25881 +5053 25884 +5050 25886 +5046 25889 +5043 25892 +5039 25894 +5036 25897 +5033 25899 +5029 25902 +5026 25905 +5023 25907 +5020 25910 +5016 25912 +5013 25915 +5010 25917 +5006 25920 +5003 25923 +5000 25925 +4997 25928 +4993 25930 +4990 25933 +4987 25935 +4984 25938 +4980 25940 +4977 25943 +4974 25945 +4971 25948 +4967 25950 +4964 25953 +4961 25955 +4958 25958 +4955 25960 +4952 25963 +4948 25965 +4945 25968 +4942 25970 +4939 25973 +4936 25975 +4933 25978 +4929 25980 +4926 25983 +4923 25985 +4920 25988 +4917 25990 +4914 25992 +4911 25995 +4908 25997 +4905 26000 +4902 26002 +4898 26005 +4895 26007 +4892 26010 +4889 26012 +4886 26014 +4883 26017 +4880 26019 +4877 26022 +4874 26024 +4871 26026 +4868 26029 +4865 26031 +4862 26034 +4859 26036 +4856 26038 +4853 26041 +4850 26043 +4847 26046 +4844 26048 +4841 26050 +4838 26053 +4835 26055 +4832 26057 +4829 26060 +4827 26062 +4824 26064 +4821 26067 +4818 26069 +4815 26071 +4812 26074 +4809 26076 +4806 26078 +4803 26081 +4800 26083 +4798 26085 +4795 26088 +4792 26090 +4789 26092 +4786 26095 +4783 26097 +4780 26099 +4778 26102 +4775 26104 +4772 26106 +4769 26109 +4766 26111 +4763 26113 +4761 26115 +4758 26118 +4755 26120 +4752 26122 +4749 26125 +4747 26127 +4744 26129 +4741 26131 +4738 26134 +4736 26136 +4733 26138 +4730 26140 +4727 26143 +4725 26145 +4722 26147 +4719 26150 +4716 26151 +4713 26154 +4710 26156 +4707 26159 +4706 26160 +4703 26162 +4702 26163 +4699 26175 +4696 26178 +4693 26180 +4690 26182 +4687 26185 +4685 26187 +4682 26189 +4679 26191 +4676 26193 +4673 26196 +4670 26198 +4667 26200 +4665 26202 +4662 26204 +4659 26206 +4656 26208 +4653 26210 +4651 26213 +4648 26215 +4645 26217 +4642 26219 +4640 26221 +4637 26223 +4634 26225 +4631 26227 +4629 26229 +4626 26231 +4623 26233 +4620 26235 +4618 26237 +4615 26239 +4612 26241 +4609 26243 +4607 26246 +4604 26248 +4601 26250 +4599 26252 +4596 26254 +4593 26256 +4591 26258 +4588 26260 +4585 26262 +4583 26264 +4580 26266 +4577 26268 +4575 26270 +4572 26272 +4569 26274 +4567 26276 +4564 26279 +4562 26281 +4559 26283 +4556 26285 +4554 26287 +4551 26289 +4549 26291 +4546 26293 +4543 26295 +4541 26297 +4538 26299 +4536 26301 +4533 26303 +4531 26305 +4528 26307 +4526 26309 +4523 26311 +4520 26313 +4518 26315 +4515 26317 +4513 26319 +4510 26321 +4508 26323 +4505 26325 +4503 26327 +4500 26329 +4498 26331 +4496 26333 +4493 26335 +4491 26337 +4488 26339 +4486 26341 +4483 26343 +4481 26345 +4478 26347 +4476 26349 +4473 26351 +4471 26353 +4469 26355 +4466 26357 +4464 26359 +4461 26361 +4459 26363 +4457 26365 +4454 26367 +4452 26369 +4450 26371 +4447 26373 +4445 26375 +4443 26377 +4440 26379 +4438 26381 +4436 26383 +4433 26385 +4431 26387 +4429 26389 +4426 26391 +4424 26393 +4422 26395 +4419 26397 +4417 26400 +4415 26402 +4413 26404 +4410 26406 +4408 26408 +4406 26410 +4404 26412 +4401 26414 +4399 26416 +4397 26418 +4395 26420 +4392 26422 +4390 26424 +4388 26426 +4386 26428 +4384 26430 +4381 26432 +4379 26434 +4377 26436 +4375 26438 +4373 26440 +4370 26442 +4368 26444 +4366 26446 +4364 26448 +4362 26450 +4360 26452 +4358 26454 +4355 26456 +4353 26458 +4351 26460 +4349 26462 +4347 26464 +4345 26466 +4343 26468 +4341 26470 +4339 26472 +4336 26474 +4334 26476 +4332 26478 +4330 26480 +4328 26482 +4326 26484 +4324 26486 +4322 26489 +4320 26491 +4318 26493 +4316 26495 +4314 26497 +4312 26499 +4310 26501 +4308 26503 +4306 26505 +4303 26507 +4301 26509 +4299 26511 +4297 26513 +4295 26515 +4293 26517 +4291 26519 +4289 26521 +4287 26523 +4285 26525 +4283 26527 +4281 26529 +4279 26531 +4277 26533 +4275 26535 +4273 26537 +4271 26539 +4269 26541 +4267 26543 +4265 26545 +4263 26548 +4261 26550 +4260 26552 +4258 26554 +4256 26556 +4254 26558 +4252 26560 +4250 26562 +4248 26564 +4246 26566 +4244 26568 +4242 26570 +4240 26572 +4238 26574 +4236 26576 +4234 26578 +4232 26580 +4230 26582 +4228 26584 +4226 26586 +4224 26588 +4222 26590 +4220 26592 +4218 26594 +4216 26596 +4214 26598 +4212 26600 +4210 26602 +4208 26604 +4206 26606 +4204 26608 +4202 26610 +4200 26612 +4198 26614 +4196 26616 +4194 26619 +4192 26621 +4190 26623 +4188 26625 +4186 26627 +4184 26629 +4182 26631 +4180 26633 +4178 26635 +4176 26637 +4174 26639 +4172 26641 +4170 26643 +4168 26645 +4166 26647 +4164 26649 +4162 26651 +4160 26653 +4158 26655 +4156 26657 +4154 26659 +4152 26661 +4150 26663 +4148 26665 +4146 26667 +4144 26669 +4142 26671 +4140 26673 +4138 26675 +4136 26677 +4134 26679 +4132 26681 +4129 26683 +4127 26685 +4125 26687 +4123 26689 +4121 26691 +4119 26693 +4117 26695 +4115 26697 +4113 26699 +4111 26701 +4109 26703 +4107 26705 +4105 26707 +4102 26709 +4100 26711 +4098 26713 +4096 26715 +4094 26717 +4092 26719 +4090 26721 +4088 26723 +4086 26725 +4084 26727 +4082 26729 +4079 26731 +4077 26733 +4075 26735 +4073 26737 +4071 26739 +4069 26741 +4067 26743 +4065 26745 +4063 26747 +4061 26749 +4058 26751 +4056 26753 +4054 26755 +4052 26757 +4050 26759 +4048 26761 +4046 26763 +4044 26765 +4042 26768 +4039 26769 +4037 26771 +4035 26773 +4033 26775 +4031 26777 +4029 26779 +4027 26781 +4025 26783 +4022 26785 +4020 26787 +4018 26789 +4016 26791 +4014 26793 +4012 26795 +4010 26797 +4008 26799 +4006 26801 +4003 26803 +4001 26805 +3999 26807 +3997 26809 +3995 26811 +3993 26813 +3991 26815 +3989 26817 +3986 26819 +3984 26821 +3982 26823 +3980 26825 +3978 26827 +3976 26829 +3974 26831 +3972 26833 +3969 26835 +3967 26837 +3965 26839 +3963 26841 +3961 26843 +3959 26845 +3957 26847 +3955 26849 +3952 26851 +3950 26853 +3948 26855 +3946 26857 +3944 26859 +3942 26861 +3940 26863 +3938 26865 +3935 26867 +3933 26869 +3931 26871 +3929 26873 +3927 26875 +3925 26877 +3923 26879 +3921 26881 +3918 26883 +3916 26885 +3914 26886 +3912 26888 +3910 26890 +3908 26892 +3906 26894 +3904 26896 +3902 26898 +3899 26900 +3897 26902 +3895 26904 +3893 26906 +3891 26908 +3889 26910 +3887 26912 +3885 26914 +3883 26916 +3880 26918 +3878 26920 +3876 26922 +3874 26924 +3872 26926 +3870 26928 +3868 26930 +3866 26931 +3864 26933 +3861 26935 +3859 26937 +3857 26939 +3855 26941 +3853 26943 +3851 26945 +3849 26947 +3847 26949 +3845 26951 +3842 26953 +3840 26955 +3838 26957 +3836 26959 +3834 26961 +3832 26963 +3830 26965 +3828 26966 +3826 26968 +3824 26970 +3821 26972 +3819 26974 +3817 26976 +3815 26978 +3813 26980 +3811 26982 +3809 26984 +3807 26986 +3805 26988 +3803 26990 +3801 26992 +3799 26993 +3796 26995 +3794 26997 +3792 26999 +3790 27001 +3788 27003 +3786 27005 +3784 27007 +3782 27009 +3780 27011 +3778 27013 +3776 27015 +3774 27017 +3772 27018 +3769 27020 +3767 27022 +3765 27024 +3763 27026 +3761 27028 +3759 27030 +3757 27032 +3755 27034 +3753 27036 +3751 27038 +3749 27040 +3747 27041 +3745 27043 +3743 27045 +3741 27047 +3738 27049 +3736 27051 +3734 27053 +3732 27055 +3730 27057 +3728 27059 +3726 27060 +3724 27062 +3722 27064 +3720 27066 +3718 27068 +3716 27070 +3714 27072 +3712 27074 +3710 27076 +3708 27078 +3706 27079 +3704 27081 +3702 27083 +3699 27085 +3697 27087 +3695 27089 +3693 27091 +3691 27093 +3689 27095 +3687 27096 +3685 27098 +3683 27100 +3681 27102 +3679 27104 +3677 27106 +3675 27108 +3673 27110 +3671 27111 +3669 27113 +3667 27115 +3665 27117 +3663 27119 +3661 27121 +3659 27123 +3657 27125 +3655 27127 +3653 27128 +3651 27130 +3649 27132 +3647 27134 +3645 27136 +3643 27138 +3641 27140 +3639 27141 +3637 27143 +3635 27145 +3633 27147 +3631 27149 +3629 27151 +3627 27153 +3625 27155 +3623 27157 +3621 27158 +3619 27160 +3617 27162 +3615 27164 +3613 27166 +3611 27168 +3609 27170 +3607 27171 +3605 27173 +3603 27175 +3601 27177 +3599 27179 +3597 27181 +3595 27183 +3593 27184 +3591 27186 +3589 27188 +3587 27190 +3585 27192 +3583 27194 +3581 27195 +3579 27197 +3577 27199 +3575 27201 +3573 27203 +3571 27205 +3569 27207 +3567 27208 +3565 27210 +3563 27212 +3561 27214 +3559 27216 +3557 27218 +3556 27219 +3554 27221 +3552 27223 +3550 27225 +3548 27227 +3546 27229 +3544 27230 +3542 27232 +3540 27234 +3538 27236 +3536 27238 +3534 27240 +3532 27241 +3530 27243 +3528 27245 +3526 27247 +3524 27249 +3523 27251 +3521 27252 +3519 27254 +3517 27256 +3515 27258 +3513 27260 +3511 27262 +3509 27263 +3507 27265 +3505 27267 +3503 27269 +3501 27271 +3499 27272 +3497 27274 +3496 27276 +3494 27278 +3492 27280 +3490 27281 +3488 27283 +3486 27285 +3484 27287 +3482 27289 +3480 27290 +3478 27292 +3476 27294 +3475 27296 +3473 27298 +3471 27299 +3469 27301 +3467 27303 +3465 27305 +3463 27307 +3461 27308 +3459 27310 +3458 27312 +3456 27314 +3454 27316 +3452 27317 +3450 27319 +3448 27321 +3446 27323 +3445 27325 +3443 27326 +3441 27328 +3439 27330 +3437 27332 +3435 27333 +3433 27335 +3431 27337 +3430 27339 +3428 27341 +3426 27342 +3424 27344 +3422 27346 +3420 27348 +3418 27350 +3417 27351 +3415 27353 +3413 27355 +3411 27357 +3409 27358 +3407 27360 +3405 27362 +3404 27364 +3402 27366 +3400 27367 +3398 27369 +3396 27371 +3394 27373 +3393 27374 +3391 27376 +3389 27378 +3387 27380 +3385 27381 +3383 27383 +3382 27385 +3380 27387 +3378 27388 +3376 27390 +3374 27392 +3372 27394 +3371 27395 +3369 27397 +3367 27399 +3365 27401 +3363 27402 +3362 27404 +3360 27406 +3358 27407 +3356 27409 +3354 27411 +3353 27413 +3351 27414 +3349 27416 +3347 27418 +3345 27420 +3344 27421 +3342 27423 +3340 27425 +3338 27427 +3336 27428 +3335 27430 +3333 27432 +3331 27434 +3329 27435 +3328 27437 +3326 27439 +3324 27441 +3322 27442 +3320 27444 +3319 27446 +3317 27447 +3315 27449 +3313 27451 +3312 27452 +3310 27454 +3308 27456 +3306 27458 +3305 27459 +3303 27461 +3301 27463 +3299 27464 +3298 27466 +3296 27468 +3294 27469 +3292 27471 +3291 27473 +3289 27475 +3287 27476 +3285 27478 +3284 27480 +3282 27481 +3280 27483 +3278 27485 +3277 27487 +3275 27488 +3273 27490 +3271 27492 +3270 27493 +3268 27495 +3266 27497 +3265 27498 +3263 27500 +3261 27502 +3259 27503 +3258 27505 +3256 27507 +3254 27509 +3253 27510 +3251 27512 +3249 27514 +3247 27515 +3246 27517 +3244 27519 +3242 27520 +3241 27522 +3239 27524 +3237 27525 +3235 27527 +3234 27529 +3232 27530 +3230 27532 +3229 27534 +3227 27535 +3225 27537 +3223 27539 +3222 27540 +3220 27542 +3218 27544 +3217 27545 +3215 27547 +3213 27549 +3212 27550 +3210 27552 +3208 27554 +3207 27555 +3205 27557 +3203 27558 +3202 27560 +3200 27562 +3198 27563 +3197 27565 +3195 27567 +3193 27568 +3192 27570 +3190 27572 +3188 27573 +3187 27575 +3185 27577 +3183 27578 +3182 27580 +3180 27581 +3178 27583 +3177 27585 +3175 27586 +3173 27588 +3172 27590 +3170 27591 +3168 27593 +3167 27594 +3165 27596 +3164 27598 +3162 27599 +3160 27601 +3159 27603 +3157 27604 +3155 27606 +3154 27608 +3152 27609 +3150 27611 +3149 27612 +3147 27614 +3146 27616 +3144 27617 +3142 27619 +3141 27620 +3139 27622 +3137 27624 +3136 27625 +3134 27627 +3133 27628 +3131 27630 +3129 27631 +3128 27633 +3126 27635 +3125 27636 +3123 27638 +3121 27639 +3120 27641 +3118 27643 +3116 27644 +3115 27646 +3113 27647 +3112 27649 +3110 27650 +3109 27652 +3107 27654 +3105 27655 +3104 27657 +3102 27658 +3101 27660 +3099 27661 +3097 27663 +3096 27665 +3094 27666 +3093 27668 +3091 27669 +3090 27671 +3088 27672 +3086 27674 +3085 27676 +3083 27677 +3082 27679 +3080 27680 +3079 27682 +3077 27683 +3075 27685 +3074 27687 +3072 27688 +3071 27690 +3069 27691 +3068 27693 +3066 27694 +3064 27696 +3063 27697 +3061 27699 +3060 27700 +3058 27702 +3057 27704 +3055 27705 +3054 27707 +3052 27708 +3051 27710 +3049 27711 +3048 27713 +3046 27714 +3045 27716 +3043 27718 +3042 27719 +3040 27721 +3038 27722 +3037 27724 +3035 27725 +3034 27727 +3032 27728 +3031 27730 +3029 27731 +3028 27733 +3026 27734 +3025 27736 +3023 27737 +3022 27739 +3020 27740 +3019 27742 +3017 27743 +3016 27745 +3014 27746 +3013 27748 +3011 27749 +3010 27751 +3008 27752 +3007 27754 +3005 27755 +3004 27757 +3002 27758 +3001 27760 +3000 27761 +2998 27763 +2997 27764 +2995 27766 +2994 27767 +2992 27769 +2991 27770 +2989 27772 +2988 27773 +2986 27775 +2985 27776 +2984 27778 +2982 27779 +2981 27780 +2979 27782 +2978 27783 +2976 27785 +2975 27786 +2973 27788 +2972 27789 +2971 27791 +2969 27792 +2968 27794 +2966 27795 +2965 27796 +2963 27798 +2962 27799 +2961 27801 +2959 27802 +2958 27804 +2956 27805 +2955 27807 +2953 27808 +2952 27810 +2951 27811 +2949 27813 +2948 27814 +2946 27815 +2945 27817 +2944 27818 +2942 27820 +2941 27821 +2939 27823 +2938 27824 +2937 27826 +2935 27827 +2934 27829 +2932 27830 +2931 27831 +2930 27833 +2928 27834 +2927 27836 +2925 27837 +2924 27839 +2923 27840 +2921 27842 +2920 27843 +2919 27845 +2917 27846 +2916 27848 +2914 27849 +2913 27850 +2912 27852 +2910 27853 +2909 27855 +2908 27856 +2906 27858 +2905 27859 +2903 27861 +2902 27862 +2901 27863 +2899 27865 +2898 27866 +2897 27867 +2895 27869 +2894 27870 +2892 27872 +2891 27873 +2890 27875 +2888 27876 +2887 27877 +2886 27879 +2884 27880 +2883 27882 +2882 27883 +2880 27885 +2879 27886 +2878 27887 +2876 27889 +2875 27890 +2874 27892 +2872 27893 +2871 27894 +2870 27896 +2868 27897 +2867 27899 +2866 27900 +2864 27901 +2863 27903 +2862 27904 +2860 27905 +2859 27907 +2858 27908 +2856 27910 +2855 27911 +2854 27912 +2852 27914 +2851 27915 +2850 27917 +2848 27918 +2847 27919 +2846 27921 +2844 27922 +2843 27924 +2842 27925 +2841 27926 +2839 27928 +2838 27929 +2837 27930 +2835 27932 +2834 27934 +2833 27935 +2831 27937 +2830 27938 +2829 27939 +2828 27941 +2826 27942 +2825 27943 +2824 27945 +2822 27946 +2821 27947 +2820 27949 +2819 27950 +2817 27952 +2816 27953 +2815 27954 +2813 27956 +2812 27957 +2811 27959 +2810 27960 +2808 27961 +2807 27963 +2806 27964 +2805 27965 +2803 27967 +2802 27968 +2801 27969 +2799 27971 +2798 27972 +2797 27974 +2796 27975 +2794 27976 +2793 27978 +2792 27979 +2791 27980 +2789 27982 +2788 27983 +2787 27984 +2786 27986 +2784 27987 +2783 27988 +2782 27990 +2781 27991 +2779 27992 +2778 27994 +2777 27995 +2776 27996 +2774 27998 +2773 27999 +2772 28000 +2771 28002 +2769 28003 +2768 28004 +2767 28005 +2766 28007 +2764 28008 +2763 28009 +2762 28011 +2761 28012 +2760 28013 +2758 28015 +2757 28016 +2756 28017 +2755 28019 +2753 28020 +2752 28021 +2751 28023 +2750 28024 +2748 28025 +2747 28027 +2746 28028 +2745 28029 +2744 28031 +2742 28032 +2741 28033 +2740 28034 +2739 28036 +2737 28037 +2736 28038 +2735 28040 +2734 28041 +2733 28042 +2731 28044 +2730 28045 +2729 28046 +2728 28048 +2727 28049 +2725 28050 +2724 28052 +2723 28053 +2722 28054 +2721 28055 +2719 28057 +2718 28058 +2717 28059 +2716 28061 +2715 28062 +2713 28063 +2712 28064 +2711 28066 +2710 28067 +2709 28068 +2707 28070 +2706 28071 +2705 28072 +2704 28073 +2703 28074 +2702 28076 +2700 28077 +2699 28078 +2698 28079 +2697 28081 +2696 28082 +2695 28083 +2693 28085 +2692 28086 +2691 28087 +2690 28088 +2689 28090 +2687 28091 +2686 28092 +2685 28093 +2684 28095 +2683 28096 +2682 28097 +2680 28099 +2679 28100 +2678 28101 +2677 28102 +2676 28104 +2675 28105 +2673 28106 +2672 28107 +2671 28109 +2670 28110 +2669 28111 +2668 28112 +2667 28114 +2665 28115 +2664 28116 +2663 28118 +2662 28119 +2661 28120 +2660 28121 +2659 28123 +2657 28124 +2656 28125 +2655 28126 +2654 28128 +2653 28129 +2652 28130 +2651 28131 +2649 28133 +2648 28134 +2647 28135 +2646 28136 +2645 28138 +2644 28139 +2643 28140 +2642 28142 +2640 28143 +2639 28144 +2638 28145 +2637 28147 +2636 28148 +2635 28149 +2634 28150 +2632 28152 +2631 28153 +2630 28154 +2629 28155 +2628 28157 +2627 28158 +2626 28159 +2625 28160 +2624 28161 +2622 28163 +2621 28164 +2620 28165 +2619 28167 +2618 28168 +2617 28169 +2616 28170 +2615 28171 +2614 28173 +2612 28174 +2611 28175 +2610 28176 +2609 28178 +2608 28179 +2607 28180 +2606 28181 +2605 28183 +2604 28184 +2603 28185 +2601 28186 +2600 28187 +2599 28189 +2598 28190 +2597 28191 +2596 28192 +2595 28194 +2594 28195 +2593 28196 +2592 28197 +2591 28198 +2590 28200 +2588 28201 +2587 28202 +2586 28203 +2585 28205 +2584 28206 +2583 28207 +2582 28208 +2581 28209 +2580 28211 +2579 28212 +2578 28213 +2576 28214 +2575 28215 +2574 28217 +2573 28218 +2572 28219 +2571 28220 +2570 28222 +2569 28223 +2568 28224 +2567 28225 +2566 28226 +2565 28228 +2564 28229 +2563 28230 +2561 28231 +2560 28233 +2559 28234 +2558 28235 +2557 28236 +2556 28237 +2555 28239 +2554 28240 +2553 28241 +2552 28242 +2551 28243 +2550 28245 +2549 28246 +2548 28247 +2546 28248 +2545 28250 +2544 28251 +2543 28252 +2542 28253 +2541 28254 +2540 28255 +2539 28257 +2538 28258 +2537 28259 +2536 28260 +2535 28261 +2534 28263 +2533 28264 +2532 28265 +2531 28266 +2530 28267 +2529 28269 +2527 28270 +2526 28271 +2525 28272 +2524 28273 +2523 28275 +2522 28276 +2521 28277 +2520 28278 +2519 28279 +2518 28280 +2517 28282 +2516 28283 +2515 28284 +2514 28285 +2513 28286 +2512 28288 +2511 28289 +2510 28290 +2509 28291 +2508 28292 +2507 28293 +2506 28295 +2505 28296 +2504 28297 +2503 28298 +2501 28299 +2500 28301 +2499 28302 +2498 28303 +2497 28304 +2496 28305 +2495 28306 +2494 28308 +2493 28309 +2492 28310 +2491 28311 +2490 28312 +2489 28313 +2488 28315 +2487 28316 +2486 28317 +2485 28318 +2484 28319 +2483 28320 +2482 28322 +2481 28323 +2480 28324 +2479 28325 +2478 28326 +2477 28327 +2476 28328 +2475 28330 +2474 28331 +2473 28332 +2472 28333 +2471 28334 +2470 28335 +2469 28337 +2468 28338 +2467 28339 +2466 28340 +2465 28341 +2464 28342 +2463 28343 +2462 28345 +2461 28346 +2460 28347 +2459 28348 +2458 28349 +2457 28350 +2456 28351 +2455 28353 +2454 28354 +2453 28355 +2452 28356 +2451 28357 +2450 28358 +2449 28359 +2448 28361 +2447 28362 +2446 28363 +2445 28364 +2444 28365 +2443 28366 +2442 28368 +2441 28369 +2440 28370 +2439 28371 +2438 28372 +2437 28373 +2436 28374 +2435 28375 +2434 28377 +2433 28378 +2432 28379 +2431 28380 +2430 28381 +2429 28382 +2428 28383 +2427 28384 +2426 28386 +2425 28387 +2424 28388 +2423 28389 +2422 28390 +2421 28391 +2420 28392 +2419 28393 +2418 28394 +2417 28396 +2416 28397 +2415 28398 +2414 28399 +2413 28400 +2412 28401 +2411 28402 +2410 28403 +2409 28405 +2408 28406 +2407 28407 +2406 28408 +2405 28409 +2404 28410 +2403 28411 +2403 28412 +2402 28413 +2401 28414 +2400 28416 +2399 28417 +2398 28418 +2397 28419 +2396 28420 +2395 28421 +2394 28422 +2393 28423 +2392 28425 +2391 28426 +2390 28427 +2389 28428 +2388 28429 +2387 28430 +2386 28431 +2385 28432 +2384 28433 +2383 28435 +2382 28436 +2381 28437 +2380 28438 +2379 28439 +2378 28440 +2377 28441 +2377 28442 +2376 28443 +2375 28445 +2374 28446 +2373 28447 +2372 28448 +2371 28449 +2370 28450 +2369 28451 +2368 28452 +2367 28453 +2366 28454 +2365 28456 +2364 28457 +2363 28458 +2362 28459 +2361 28460 +2360 28461 +2359 28462 +2359 28463 +2358 28464 +2357 28465 +2356 28466 +2355 28468 +2354 28469 +2353 28470 +2352 28471 +2351 28472 +2350 28473 +2349 28474 +2348 28475 +2347 28476 +2346 28477 +2345 28478 +2344 28479 +2343 28481 +2342 28482 +2342 28483 +2341 28484 +2340 28485 +2339 28486 +2338 28487 +2337 28488 +2336 28489 +2335 28490 +2334 28491 +2333 28492 +2332 28493 +2331 28495 +2330 28496 +2329 28497 +2328 28498 +2327 28499 +2326 28500 +2326 28501 +2325 28502 +2324 28503 +2323 28504 +2322 28505 +2321 28506 +2320 28507 +2319 28508 +2318 28509 +2317 28510 +2316 28511 +2315 28513 +2314 28514 +2314 28515 +2313 28516 +2312 28517 +2311 28518 +2310 28519 +2309 28520 +2308 28521 +2307 28522 +2306 28523 +2305 28524 +2304 28525 +2303 28526 +2302 28527 +2302 28528 +2301 28529 +2300 28530 +2299 28531 +2298 28532 +2297 28533 +2296 28534 +2295 28536 +2294 28537 +2293 28538 +2292 28539 +2292 28540 +2291 28541 +2290 28542 +2289 28543 +2288 28544 +2287 28545 +2286 28546 +2285 28547 +2284 28548 +2283 28549 +2282 28550 +2281 28551 +2281 28552 +2280 28553 +2279 28554 +2278 28555 +2277 28556 +2276 28557 +2275 28558 +2274 28559 +2273 28560 +2272 28561 +2272 28562 +2271 28563 +2270 28564 +2269 28565 +2268 28566 +2267 28567 +2266 28568 +2265 28569 +2264 28570 +2263 28571 +2263 28572 +2262 28573 +2261 28574 +2260 28575 +2259 28576 +2258 28577 +2257 28578 +2256 28579 +2255 28580 +2254 28581 +2254 28582 +2253 28583 +2252 28584 +2251 28585 +2250 28586 +2249 28587 +2248 28588 +2247 28589 +2247 28590 +2246 28591 +2245 28592 +2244 28593 +2243 28594 +2242 28595 +2241 28596 +2240 28597 +2239 28598 +2239 28599 +2238 28600 +2237 28601 +2236 28602 +2235 28603 +2234 28604 +2233 28605 +2232 28606 +2232 28607 +2231 28608 +2230 28609 +2229 28610 +2228 28611 +2227 28612 +2226 28613 +2225 28614 +2224 28615 +2224 28616 +2223 28617 +2222 28618 +2221 28619 +2220 28620 +2219 28621 +2218 28622 +2217 28623 +2217 28624 +2216 28625 +2215 28625 +2214 28626 +2213 28627 +2212 28628 +2211 28629 +2211 28630 +2210 28631 +2209 28632 +2208 28633 +2207 28634 +2206 28635 +2205 28636 +2205 28637 +2204 28638 +2203 28639 +2202 28640 +2201 28641 +2200 28642 +2199 28643 +2199 28644 +2198 28645 +2197 28646 +2196 28647 +2195 28648 +2194 28649 +2193 28650 +2193 28651 +2192 28652 +2191 28653 +2190 28654 +2189 28655 +2188 28656 +2187 28657 +2187 28658 +2186 28659 +2185 28660 +2184 28660 +2183 28661 +2182 28662 +2181 28663 +2181 28664 +2180 28665 +2179 28666 +2178 28667 +2177 28668 +2176 28669 +2176 28670 +2175 28671 +2174 28672 +2173 28673 +2172 28674 +2171 28675 +2171 28676 +2170 28677 +2169 28677 +2168 28678 +2167 28679 +2166 28680 +2165 28681 +2165 28682 +2164 28683 +2163 28684 +2162 28685 +2161 28686 +2160 28687 +2160 28688 +2159 28689 +2158 28689 +2157 28690 +2156 28691 +2155 28692 +2155 28693 +2154 28694 +2153 28695 +2152 28696 +2151 28697 +2150 28698 +2149 28699 +2149 28700 +2148 28701 +2147 28701 +2146 28702 +2145 28703 +2144 28704 +2144 28705 +2143 28706 +2142 28707 +2141 28708 +2140 28709 +2139 28709 +2139 28710 +2138 28711 +2137 28712 +2136 28713 +2135 28714 +2135 28715 +2134 28716 +2133 28717 +2132 28718 +2131 28719 +2130 28719 +2130 28720 +2129 28721 +2128 28722 +2127 28723 +2126 28724 +2126 28725 +2125 28726 +2124 28727 +2123 28728 +2122 28728 +2122 28729 +2121 28730 +2120 28731 +2119 28732 +2118 28733 +2117 28733 +2117 28734 +2116 28735 +2115 28736 +2114 28737 +2113 28738 +2113 28739 +2112 28739 +2111 28740 +2110 28741 +2109 28742 +2109 28743 +2108 28744 +2107 28745 +2106 28746 +2105 28746 +2105 28747 +2104 28748 +2103 28749 +2102 28750 +2101 28751 +2101 28752 +2100 28752 +2099 28753 +2098 28754 +2098 28755 +2097 28756 +2096 28757 +2095 28758 +2094 28758 +2094 28759 +2093 28760 +2092 28761 +2091 28762 +2090 28763 +2090 28763 +2089 28764 +2088 28765 +2087 28766 +2086 28767 +2086 28768 +2085 28769 +2084 28769 +2083 28770 +2083 28771 +2082 28772 +2081 28773 +2080 28774 +2079 28774 +2079 28775 +2078 28776 +2077 28777 +2076 28778 +2076 28779 +2075 28779 +2074 28780 +2073 28781 +2072 28782 +2072 28783 +2071 28784 +2070 28784 +2069 28785 +2069 28786 +2068 28787 +2067 28788 +2066 28788 +2065 28789 +2065 28790 +2064 28791 +2063 28792 +2062 28793 +2062 28793 +2061 28794 +2060 28795 +2059 28796 +2058 28797 +2058 28797 +2057 28798 +2056 28799 +2055 28800 +2055 28801 +2054 28801 +2053 28802 +2052 28803 +2052 28804 +2051 28805 +2050 28805 +2049 28806 +2048 28807 +2048 28808 +2047 28809 +2046 28809 +2045 28810 +2045 28811 +2044 28812 +2043 28813 +2042 28813 +2042 28815 +2041 28815 +2040 28816 +2039 28817 +2039 28818 +2038 28819 +2037 28819 +2036 28820 +2036 28821 +2035 28822 +2034 28822 +2033 28823 +2033 28824 +2032 28825 +2031 28826 +2030 28827 +2029 28827 +2029 28828 +2028 28829 +2027 28830 +2026 28830 +2026 28831 +2025 28832 +2024 28833 +2023 28833 +2023 28834 +2022 28835 +2021 28836 +2020 28837 +2020 28837 +2019 28838 +2018 28839 +2017 28840 +2017 28840 +2016 28841 +2015 28842 +2014 28843 +2014 28843 +2013 28844 +2012 28845 +2011 28846 +2011 28846 +2010 28847 +2009 28848 +2008 28849 +2008 28849 +2007 28850 +2006 28851 +2006 28852 +2005 28853 +2004 28854 +2003 28854 +2003 28855 +2002 28856 +2001 28857 +2000 28858 +2000 28858 +1999 28859 +1998 28860 +1997 28861 +1997 28861 +1996 28862 +1995 28863 +1995 28864 +1994 28864 +1993 28865 +1992 28866 +1992 28867 +1991 28867 +1990 28868 +1989 28869 +1989 28870 +1988 28870 +1987 28871 +1986 28872 +1986 28873 +1985 28873 +1984 28874 +1983 28875 +1983 28876 +1982 28876 +1981 28877 +1981 28878 +1980 28879 +1979 28879 +1978 28880 +1978 28881 +1977 28882 +1976 28882 +1975 28883 +1975 28884 +1974 28885 +1973 28885 +1972 28886 +1972 28887 +1971 28888 +1970 28888 +1970 28889 +1969 28890 +1968 28891 +1967 28891 +1967 28892 +1966 28893 +1965 28894 +1964 28894 +1964 28895 +1963 28896 +1962 28897 +1962 28897 +1961 28898 +1960 28899 +1959 28900 +1959 28900 +1958 28901 +1957 28902 +1957 28903 +1956 28903 +1955 28904 +1954 28905 +1954 28906 +1953 28906 +1952 28907 +1952 28908 +1951 28908 +1950 28909 +1949 28910 +1949 28911 +1948 28911 +1947 28912 +1947 28913 +1946 28914 +1945 28914 +1944 28915 +1944 28916 +1943 28917 +1942 28917 +1942 28918 +1941 28919 +1940 28920 +1939 28920 +1939 28921 +1938 28922 +1937 28923 +1937 28923 +1936 28924 +1935 28925 +1934 28925 +1934 28926 +1933 28927 +1932 28928 +1932 28928 +1931 28929 +1930 28930 +1930 28931 +1929 28931 +1928 28932 +1927 28933 +1927 28934 +1926 28934 +1925 28935 +1925 28936 +1924 28937 +1923 28937 +1922 28938 +1922 28939 +1921 28939 +1920 28940 +1920 28941 +1919 28942 +1918 28942 +1918 28943 +1917 28944 +1916 28945 +1915 28945 +1915 28946 +1914 28947 +1913 28947 +1913 28948 +1912 28949 +1911 28949 +1911 28950 +1910 28951 +1909 28951 +1908 28952 +1908 28953 +1907 28954 +1906 28954 +1906 28955 +1905 28956 +1904 28956 +1904 28957 +1903 28958 +1902 28959 +1902 28959 +1901 28960 +1900 28961 +1899 28962 +1899 28962 +1898 28963 +1897 28964 +1897 28965 +1896 28966 +1895 28966 +1895 28967 +1894 28968 +1893 28968 +1893 28969 +1892 28970 +1891 28971 +1890 28971 +1890 28972 +1889 28973 +1888 28974 +1888 28974 +1887 28975 +1886 28976 +1886 28976 +1885 28977 +1884 28978 +1884 28979 +1883 28979 +1882 28980 +1882 28981 +1881 28982 +1880 28982 +1879 28983 +1879 28984 +1878 28984 +1877 28985 +1877 28986 +1876 28987 +1875 28987 +1875 28988 +1874 28989 +1873 28989 +1873 28990 +1872 28991 +1871 28992 +1871 28992 +1870 28993 +1869 28994 +1869 28994 +1868 28995 +1867 28996 +1867 28997 +1866 28997 +1865 28998 +1864 28999 +1864 28999 +1863 29000 +1862 29001 +1862 29002 +1861 29002 +1860 29003 +1860 29004 +1859 29004 +1858 29005 +1858 29006 +1857 29006 +1856 29007 +1856 29008 +1855 29009 +1854 29009 +1854 29010 +1853 29011 +1852 29011 +1852 29012 +1851 29013 +1850 29014 +1850 29014 +1849 29015 +1848 29016 +1848 29016 +1847 29017 +1846 29018 +1846 29018 +1845 29019 +1844 29020 +1844 29021 +1843 29021 +1842 29022 +1842 29023 +1841 29023 +1840 29024 +1840 29025 +1839 29025 +1838 29026 +1838 29027 +1837 29028 +1836 29028 +1836 29029 +1835 29030 +1834 29030 +1834 29031 +1833 29032 +1832 29032 +1832 29033 +1831 29034 +1830 29035 +1829 29035 +1829 29036 +1828 29037 +1827 29037 +1827 29038 +1826 29039 +1825 29039 +1825 29040 +1824 29041 +1823 29042 +1823 29042 +1822 29043 +1821 29044 +1821 29044 +1820 29045 +1819 29046 +1819 29047 +1818 29047 +1817 29048 +1817 29049 +1816 29049 +1815 29050 +1815 29051 +1814 29051 +1813 29052 +1813 29053 +1812 29053 +1811 29054 +1811 29055 +1810 29056 +1809 29056 +1809 29057 +1808 29058 +1807 29058 +1807 29059 +1806 29060 +1805 29060 +1805 29061 +1804 29062 +1803 29062 +1803 29063 +1802 29064 +1802 29064 +1801 29065 +1800 29066 +1800 29066 +1799 29067 +1798 29068 +1798 29069 +1797 29069 +1796 29070 +1796 29071 +1795 29071 +1794 29072 +1794 29073 +1793 29073 +1792 29074 +1792 29075 +1791 29075 +1790 29076 +1790 29077 +1789 29077 +1788 29078 +1788 29079 +1787 29079 +1786 29080 +1786 29081 +1785 29082 +1784 29082 +1784 29083 +1783 29084 +1782 29084 +1782 29085 +1781 29086 +1780 29086 +1780 29087 +1779 29088 +1778 29089 +1778 29089 +1777 29090 +1776 29091 +1776 29091 +1775 29092 +1775 29093 +1774 29093 +1773 29094 +1773 29095 +1772 29095 +1771 29096 +1771 29097 +1770 29098 +1769 29098 +1769 29099 +1768 29100 +1767 29100 +1767 29101 +1766 29102 +1765 29102 +1765 29103 +1764 29104 +1764 29104 +1763 29105 +1762 29106 +1762 29106 +1761 29107 +1760 29108 +1760 29108 +1759 29109 +1758 29110 +1758 29110 +1757 29111 +1756 29112 +1756 29112 +1755 29113 +1754 29114 +1754 29114 +1753 29115 +1753 29116 +1752 29117 +1751 29117 +1751 29118 +1750 29119 +1749 29119 +1749 29120 +1748 29121 +1747 29121 +1747 29122 +1746 29123 +1745 29123 +1745 29124 +1744 29125 +1743 29126 +1743 29126 +1742 29127 +1741 29128 +1741 29128 +1740 29129 +1740 29130 +1739 29130 +1738 29131 +1738 29132 +1737 29132 +1736 29133 +1736 29134 +1735 29134 +1734 29135 +1734 29136 +1733 29136 +1732 29137 +1732 29138 +1731 29138 +1730 29139 +1730 29140 +1729 29140 +1728 29141 +1728 29142 +1727 29143 +1726 29143 +1726 29144 +1725 29145 +1725 29145 +1724 29146 +1723 29147 +1723 29147 +1722 29148 +1721 29149 +1721 29149 +1720 29150 +1719 29151 +1719 29152 +1718 29152 +1717 29153 +1717 29154 +1716 29154 +1716 29155 +1715 29156 +1714 29156 +1714 29157 +1713 29158 +1712 29158 +1712 29159 +1711 29160 +1710 29160 +1710 29161 +1709 29162 +1708 29163 +1708 29163 +1707 29164 +1707 29165 +1706 29165 +1705 29166 +1705 29167 +1704 29167 +1703 29168 +1703 29169 +1702 29169 +1701 29170 +1701 29171 +1700 29171 +1699 29172 +1699 29173 +1698 29173 +1698 29174 +1697 29175 +1696 29175 +1696 29176 +1695 29177 +1694 29177 +1694 29178 +1693 29179 +1692 29179 +1692 29180 +1691 29181 +1691 29181 +1690 29182 +1689 29183 +1689 29183 +1688 29184 +1687 29185 +1687 29185 +1686 29186 +1685 29187 +1685 29187 +1684 29188 +1683 29189 +1683 29189 +1682 29190 +1681 29191 +1681 29192 +1680 29192 +1680 29193 +1679 29194 +1678 29194 +1678 29195 +1677 29196 +1676 29196 +1676 29197 +1675 29198 +1674 29198 +1674 29199 +1673 29200 +1673 29200 +1672 29201 +1671 29202 +1671 29202 +1670 29203 +1669 29203 +1669 29204 +1668 29205 +1667 29205 +1667 29206 +1666 29207 +1666 29207 +1665 29208 +1664 29209 +1664 29209 +1663 29210 +1662 29211 +1662 29211 +1661 29212 +1660 29213 +1660 29214 +1659 29214 +1659 29215 +1658 29216 +1657 29216 +1657 29217 +1656 29217 +1655 29218 +1655 29219 +1654 29219 +1654 29220 +1653 29221 +1652 29221 +1652 29222 +1651 29223 +1650 29223 +1650 29224 +1649 29224 +1649 29225 +1648 29226 +1647 29226 +1647 29227 +1646 29228 +1645 29228 +1645 29229 +1644 29230 +1643 29231 +1643 29231 +1642 29232 +1642 29233 +1641 29233 +1640 29234 +1640 29235 +1639 29235 +1638 29236 +1638 29237 +1637 29237 +1636 29238 +1636 29239 +1635 29239 +1635 29240 +1634 29241 +1633 29241 +1633 29242 +1632 29243 +1631 29243 +1631 29244 +1630 29245 +1630 29245 +1629 29246 +1628 29247 +1628 29247 +1627 29248 +1626 29249 +1626 29249 +1625 29250 +1624 29251 +1624 29251 +1623 29252 +1623 29253 +1622 29253 +1621 29254 +1621 29255 +1620 29256 +1619 29256 +1619 29257 +1618 29258 +1618 29258 +1617 29259 +1616 29260 +1616 29260 +1615 29261 +1614 29262 +1614 29262 +1613 29263 +1613 29264 +1612 29264 +1611 29265 +1611 29266 +1610 29267 +1609 29267 +1609 29268 +1608 29269 +1608 29269 +1607 29270 +1606 29271 +1606 29271 +1605 29272 +1604 29273 +1604 29273 +1603 29274 +1603 29275 +1602 29276 +1601 29276 +1601 29277 +1600 29278 +1599 29278 +1599 29279 +1598 29280 +1598 29280 +1597 29281 +1596 29282 +1596 29283 +1595 29283 +1594 29284 +1594 29285 +1593 29286 +1592 29286 +1592 29287 +1591 29288 +1591 29288 +1590 29289 +1589 29290 +1589 29290 +1588 29291 +1587 29292 +1587 29292 +1586 29293 +1586 29294 +1585 29294 +1584 29295 +1584 29296 +1583 29296 +1582 29297 +1582 29298 +1581 29298 +1581 29299 +1580 29300 +1579 29301 +1579 29301 +1578 29302 +1577 29303 +1577 29303 +1576 29304 +1576 29305 +1575 29305 +1574 29306 +1574 29307 +1573 29307 +1572 29308 +1572 29309 +1571 29309 +1571 29310 +1570 29311 +1569 29311 +1569 29312 +1568 29313 +1567 29313 +1567 29314 +1566 29315 +1566 29315 +1565 29316 +1564 29317 +1564 29317 +1563 29318 +1562 29319 +1562 29319 +1561 29320 +1561 29320 +1560 29321 +1559 29322 +1559 29322 +1558 29323 +1557 29324 +1557 29324 +1556 29325 +1556 29326 +1555 29326 +1554 29327 +1554 29328 +1553 29328 +1553 29329 +1552 29330 +1551 29330 +1551 29331 +1550 29332 +1549 29332 +1549 29333 +1548 29333 +1547 29334 +1547 29335 +1546 29335 +1546 29336 +1545 29337 +1544 29337 +1544 29338 +1543 29338 +1543 29339 +1542 29340 +1541 29340 +1541 29341 +1540 29342 +1539 29342 +1539 29343 +1538 29343 +1538 29344 +1537 29345 +1536 29345 +1536 29346 +1535 29347 +1535 29347 +1534 29348 +1533 29349 +1533 29349 +1532 29350 +1531 29351 +1531 29351 +1530 29352 +1530 29353 +1529 29353 +1528 29354 +1528 29354 +1527 29355 +1527 29356 +1526 29356 +1525 29357 +1525 29358 +1524 29358 +1524 29359 +1523 29360 +1522 29360 +1522 29361 +1521 29362 +1520 29362 +1520 29363 +1519 29363 +1519 29364 +1518 29365 +1517 29365 +1517 29366 +1516 29367 +1515 29367 +1515 29368 +1514 29369 +1514 29369 +1513 29370 +1512 29370 +1512 29371 +1511 29371 +1510 29372 +1510 29373 +1509 29373 +1509 29374 +1508 29375 +1507 29376 +1507 29376 +1506 29377 +1506 29378 +1505 29378 +1504 29379 +1504 29379 +1503 29380 +1502 29381 +1502 29381 +1501 29382 +1501 29383 +1500 29383 +1499 29384 +1499 29385 +1498 29385 +1497 29386 +1497 29387 +1496 29387 +1496 29388 +1495 29388 +1494 29389 +1494 29390 +1493 29390 +1493 29391 +1492 29392 +1491 29392 +1491 29393 +1490 29393 +1489 29394 +1489 29395 +1488 29395 +1488 29396 +1487 29397 +1486 29397 +1486 29398 +1485 29398 +1485 29399 +1484 29400 +1483 29400 +1483 29401 +1482 29402 +1481 29402 +1481 29403 +1480 29403 +1480 29404 +1479 29405 +1478 29405 +1478 29406 +1477 29407 +1477 29407 +1476 29408 +1475 29408 +1475 29409 +1474 29410 +1474 29410 +1473 29411 +1472 29412 +1472 29412 +1471 29413 +1470 29413 +1470 29414 +1469 29415 +1469 29415 +1468 29416 +1467 29417 +1467 29417 +1466 29418 +1466 29419 +1465 29419 +1464 29420 +1464 29420 +1463 29421 +1462 29422 +1462 29422 +1461 29423 +1461 29424 +1460 29424 +1459 29425 +1459 29425 +1458 29426 +1458 29427 +1457 29427 +1456 29428 +1456 29429 +1455 29429 +1454 29430 +1454 29430 +1453 29431 +1453 29432 +1452 29432 +1451 29433 +1451 29434 +1450 29434 +1450 29435 +1449 29435 +1448 29436 +1448 29437 +1447 29437 +1447 29438 +1446 29439 +1445 29439 +1445 29440 +1444 29440 +1443 29441 +1443 29442 +1442 29442 +1442 29443 +1441 29443 +1440 29444 +1440 29445 +1439 29445 +1439 29446 +1438 29447 +1437 29447 +1437 29448 +1436 29448 +1436 29449 +1435 29450 +1434 29450 +1434 29451 +1433 29452 +1432 29452 +1432 29453 +1431 29453 +1431 29454 +1430 29455 +1429 29455 +1429 29456 +1428 29456 +1428 29457 +1427 29458 +1426 29458 +1426 29459 +1425 29460 +1424 29460 +1424 29461 +1423 29461 +1423 29462 +1422 29462 +1421 29463 +1421 29463 +1420 29464 +1420 29464 +1419 29465 +1418 29465 +1418 29466 +1417 29467 +1417 29467 +1416 29468 +1415 29468 +1415 29469 +1414 29470 +1413 29470 +1413 29471 +1412 29471 +1412 29472 +1411 29473 +1410 29473 +1410 29474 +1409 29475 +1409 29475 +1408 29476 +1407 29476 +1407 29477 +1406 29478 +1406 29478 +1405 29479 +1404 29480 +1404 29480 +1403 29480 +1403 29481 +1402 29482 +1401 29482 +1401 29483 +1400 29484 +1400 29484 +1399 29485 +1398 29485 +1398 29486 +1397 29487 +1397 29487 +1396 29488 +1395 29488 +1395 29489 +1394 29490 +1394 29490 +1393 29491 +1392 29491 +1392 29492 +1391 29493 +1391 29493 +1390 29494 +1389 29495 +1389 29495 +1388 29496 +1388 29496 +1387 29497 +1386 29498 +1386 29498 +1385 29499 +1385 29499 +1384 29500 +1383 29501 +1383 29501 +1382 29502 +1382 29503 +1381 29503 +1380 29504 +1380 29504 +1379 29505 +1379 29506 +1378 29506 +1377 29507 +1377 29507 +1376 29508 +1376 29509 +1375 29509 +1375 29510 +1374 29510 +1373 29511 +1373 29512 +1372 29512 +1372 29513 +1371 29513 +1370 29514 +1370 29515 +1369 29515 +1369 29516 +1368 29516 +1367 29517 +1367 29518 +1366 29518 +1366 29519 +1365 29519 +1364 29520 +1364 29520 +1363 29521 +1363 29521 +1362 29522 +1362 29523 +1361 29523 +1360 29524 +1360 29524 +1359 29525 +1359 29525 +1358 29526 +1357 29526 +1357 29527 +1356 29527 +1356 29528 +1355 29529 +1354 29529 +1354 29530 +1353 29530 +1353 29531 +1352 29532 +1352 29532 +1351 29533 +1350 29533 +1350 29534 +1349 29535 +1349 29535 +1348 29536 +1347 29536 +1347 29537 +1346 29537 +1346 29538 +1345 29539 +1345 29539 +1344 29540 +1343 29540 +1343 29541 +1342 29541 +1342 29542 +1341 29543 +1340 29544 +1340 29544 +1339 29545 +1339 29545 +1338 29546 +1338 29547 +1337 29547 +1336 29548 +1336 29548 +1335 29549 +1335 29550 +1334 29550 +1333 29550 +1333 29551 +1332 29552 +1332 29552 +1331 29553 +1331 29553 +1330 29554 +1329 29555 +1329 29555 +1328 29556 +1328 29556 +1327 29557 +1327 29558 +1326 29558 +1325 29559 +1325 29559 +1324 29560 +1324 29560 +1323 29561 +1323 29562 +1322 29562 +1321 29563 +1321 29563 +1320 29564 +1320 29565 +1319 29565 +1319 29566 +1318 29566 +1317 29567 +1317 29568 +1316 29568 +1316 29569 +1315 29569 +1315 29570 +1314 29571 +1313 29571 +1313 29572 +1312 29572 +1312 29573 +1311 29574 +1311 29574 +1310 29575 +1309 29575 +1309 29576 +1308 29576 +1308 29577 +1307 29578 +1307 29578 +1306 29579 +1305 29579 +1305 29580 +1304 29581 +1304 29581 +1303 29582 +1303 29582 +1302 29583 +1301 29584 +1301 29584 +1300 29585 +1300 29585 +1299 29586 +1299 29587 +1298 29587 +1297 29588 +1297 29588 +1296 29589 +1296 29589 +1295 29590 +1295 29591 +1294 29591 +1294 29592 +1293 29592 +1292 29593 +1292 29593 +1291 29594 +1291 29595 +1290 29595 +1290 29596 +1289 29596 +1288 29597 +1288 29598 +1287 29598 +1287 29599 +1286 29599 +1286 29600 +1285 29600 +1285 29601 +1284 29602 +1283 29602 +1283 29603 +1282 29603 +1282 29604 +1281 29604 +1281 29605 +1280 29605 +1280 29606 +1279 29606 +1278 29607 +1278 29608 +1277 29608 +1277 29608 +1276 29609 +1276 29610 +1275 29610 +1274 29611 +1274 29611 +1273 29612 +1273 29612 +1272 29613 +1272 29614 +1271 29614 +1271 29615 +1270 29615 +1269 29616 +1269 29616 +1268 29617 +1268 29618 +1267 29618 +1267 29619 +1266 29619 +1266 29620 +1265 29620 +1265 29621 +1264 29622 +1263 29622 +1263 29623 +1262 29623 +1262 29624 +1261 29624 +1261 29625 +1260 29626 +1260 29626 +1259 29627 +1258 29627 +1258 29628 +1257 29628 +1257 29629 +1256 29629 +1256 29630 +1255 29631 +1255 29631 +1254 29632 +1254 29632 +1253 29633 +1252 29633 +1252 29634 +1251 29635 +1251 29635 +1250 29636 +1250 29636 +1249 29637 +1249 29637 +1248 29638 +1248 29638 +1247 29639 +1246 29640 +1246 29640 +1245 29641 +1245 29641 +1244 29642 +1244 29642 +1243 29643 +1243 29643 +1242 29644 +1242 29644 +1241 29645 +1240 29645 +1240 29646 +1239 29646 +1239 29647 +1238 29648 +1238 29648 +1237 29649 +1237 29649 +1236 29650 +1236 29650 +1235 29651 +1235 29651 +1234 29652 +1233 29652 +1233 29653 +1232 29653 +1232 29654 +1231 29654 +1231 29655 +1230 29656 +1230 29656 +1229 29657 +1229 29657 +1228 29658 +1228 29658 +1227 29659 +1226 29659 +1226 29660 +1225 29660 +1225 29661 +1224 29662 +1224 29662 +1223 29663 +1223 29663 +1222 29664 +1222 29664 +1221 29665 +1221 29665 +1220 29666 +1219 29666 +1219 29667 +1218 29668 +1218 29668 +1217 29669 +1217 29669 +1216 29670 +1216 29670 +1215 29671 +1215 29671 +1214 29672 +1214 29672 +1213 29673 +1213 29674 +1212 29674 +1212 29675 +1211 29675 +1210 29676 +1210 29676 +1209 29677 +1209 29677 +1208 29678 +1208 29678 +1207 29679 +1207 29679 +1206 29680 +1206 29680 +1205 29681 +1205 29682 +1204 29682 +1204 29683 +1203 29683 +1203 29684 +1202 29684 +1202 29685 +1201 29685 +1201 29686 +1200 29686 +1200 29687 +1199 29687 +1199 29688 +1198 29688 +1197 29689 +1197 29689 +1196 29690 +1196 29690 +1195 29691 +1195 29691 +1194 29692 +1194 29693 +1193 29693 +1193 29694 +1192 29694 +1192 29695 +1191 29695 +1191 29696 +1190 29696 +1190 29697 +1189 29697 +1189 29698 +1188 29698 +1188 29699 +1187 29699 +1187 29700 +1186 29700 +1186 29701 +1185 29701 +1185 29702 +1184 29702 +1184 29703 +1183 29703 +1183 29704 +1182 29704 +1182 29705 +1181 29705 +1181 29706 +1180 29706 +1180 29707 +1179 29707 +1179 29708 +1178 29708 +1178 29709 +1177 29709 +1177 29710 +1176 29710 +1176 29711 +1175 29711 +1175 29712 +1174 29712 +1174 29713 +1173 29713 +1173 29714 +1172 29714 +1172 29715 +1171 29715 +1171 29716 +1170 29716 +1170 29717 +1169 29717 +1169 29718 +1168 29718 +1168 29719 +1167 29719 +1167 29720 +1166 29720 +1166 29721 +1165 29721 +1165 29722 +1164 29722 +1164 29723 +1163 29723 +1163 29724 +1162 29724 +1162 29725 +1161 29725 +1161 29726 +1160 29726 +1160 29727 +1159 29727 +1159 29727 +1158 29728 +1158 29728 +1157 29729 +1157 29729 +1156 29730 +1156 29730 +1155 29731 +1155 29731 +1154 29732 +1154 29732 +1153 29733 +1153 29733 +1152 29734 +1152 29734 +1151 29735 +1151 29735 +1151 29736 +1150 29736 +1150 29737 +1149 29737 +1149 29737 +1148 29738 +1148 29738 +1147 29739 +1147 29739 +1146 29740 +1146 29740 +1145 29741 +1145 29741 +1144 29742 +1144 29742 +1143 29743 +1143 29743 +1142 29744 +1142 29744 +1141 29745 +1141 29745 +1140 29746 +1140 29746 +1139 29747 +1139 29747 +1139 29748 +1138 29748 +1138 29749 +1137 29749 +1137 29750 +1136 29750 +1136 29750 +1135 29751 +1135 29752 +1134 29752 +1134 29753 +1133 29753 +1133 29754 +1132 29754 +1132 29755 +1131 29755 +1131 29756 +1130 29756 +1130 29756 +1129 29757 +1129 29758 +1129 29758 +1128 29759 +1128 29759 +1127 29759 +1127 29760 +1126 29760 +1126 29761 +1125 29761 +1125 29762 +1124 29762 +1124 29763 +1123 29763 +1123 29764 +1122 29764 +1122 29765 +1122 29765 +1121 29765 +1121 29766 +1120 29766 +1120 29767 +1119 29767 +1119 29768 +1118 29768 +1118 29769 +1117 29769 +1117 29770 +1116 29770 +1116 29771 +1116 29771 +1115 29771 +1115 29772 +1114 29772 +1114 29773 +1113 29773 +1113 29774 +1112 29774 +1112 29775 +1111 29775 +1111 29776 +1111 29776 +1110 29777 +1110 29777 +1109 29778 +1109 29778 +1108 29779 +1108 29779 +1107 29779 +1107 29780 +1106 29780 +1106 29781 +1106 29781 +1105 29782 +1105 29782 +1104 29783 +1104 29783 +1103 29784 +1103 29784 +1102 29785 +1102 29785 +1101 29785 +1101 29786 +1101 29786 +1100 29787 +1100 29787 +1099 29788 +1099 29788 +1098 29789 +1098 29789 +1097 29790 +1097 29790 +1097 29791 +1096 29791 +1096 29791 +1095 29792 +1095 29792 +1094 29793 +1094 29793 +1093 29794 +1093 29794 +1093 29795 +1092 29795 +1092 29795 +1091 29796 +1091 29796 +1090 29797 +1090 29797 +1089 29798 +1089 29798 +1089 29799 +1088 29799 +1088 29799 +1087 29800 +1087 29800 +1086 29801 +1086 29801 +1085 29802 +1085 29802 +1085 29803 +1084 29803 +1084 29803 +1083 29804 +1083 29804 +1082 29805 +1082 29805 +1082 29806 +1081 29806 +1081 29807 +1080 29807 +1080 29807 +1079 29808 +1079 29808 +1079 29809 +1078 29809 +1078 29810 +1077 29810 +1077 29810 +1076 29811 +1076 29811 +1076 29811 +1075 29812 +1075 29812 +1074 29813 +1074 29813 +1073 29814 +1073 29814 +1073 29814 +1072 29815 +1072 29815 +1071 29816 +1071 29816 +1070 29817 +1070 29817 +1070 29818 +1069 29818 +1069 29818 +1068 29819 +1068 29819 +1068 29820 +1067 29820 +1067 29821 +1066 29821 +1066 29821 +1065 29822 +1065 29822 +1065 29823 +1064 29823 +1064 29823 +1063 29824 +1063 29824 +1062 29825 +1062 29825 +1062 29826 +1061 29826 +1061 29826 +1060 29827 +1060 29827 +1060 29828 +1059 29828 +1059 29828 +1058 29829 +1058 29829 +1057 29829 +1057 29830 +1057 29830 +1056 29831 +1056 29831 +1055 29831 +1055 29832 +1055 29832 +1054 29833 +1054 29833 +1053 29833 +1053 29834 +1053 29834 +1052 29835 +1052 29835 +1051 29836 +1051 29836 +1050 29836 +1050 29837 +1050 29837 +1049 29838 +1049 29838 +1048 29838 +1048 29839 +1048 29839 +1047 29839 +1047 29840 +1046 29840 +1046 29841 +1045 29841 +1045 29842 +1045 29842 +1044 29842 +1044 29843 +1043 29843 +1043 29844 +1043 29844 +1042 29844 +1042 29845 +1041 29845 +1041 29845 +1041 29846 +1040 29846 +1040 29846 +1039 29847 +1039 29847 +1038 29848 +1038 29848 +1038 29848 +1037 29849 +1037 29849 +1036 29850 +1036 29850 +1036 29851 +1035 29851 +1035 29851 +1034 29852 +1034 29853 +1034 29853 +1033 29853 +1033 29854 +1032 29854 +1032 29855 +1032 29855 +1031 29855 +1031 29856 +1030 29856 +1030 29857 +1030 29857 +1029 29858 +1029 29858 +1028 29858 +1028 29859 +1028 29859 +1027 29860 +1027 29860 +1026 29860 +1026 29861 +1026 29861 +1025 29862 +1025 29862 +1025 29862 +1024 29863 +1024 29863 +1023 29864 +1023 29864 +1023 29865 +1022 29865 +1022 29865 +1021 29866 +1021 29866 +1021 29867 +1020 29867 +1020 29867 +1019 29868 +1019 29868 +1019 29869 +1018 29869 +1018 29869 +1017 29870 +1017 29870 +1017 29871 +1016 29871 +1016 29871 +1016 29872 +1015 29872 +1015 29873 +1014 29873 +1014 29873 +1014 29874 +1013 29874 +1013 29875 +1012 29875 +1012 29875 +1012 29876 +1011 29876 +1011 29877 +1010 29877 +1010 29877 +1010 29878 +1009 29878 +1009 29879 +1009 29879 +1008 29879 +1008 29880 +1007 29880 +1007 29881 +1007 29881 +1006 29881 +1006 29882 +1006 29882 +1005 29882 +1005 29883 +1004 29883 +1004 29884 +1004 29884 +1003 29884 +1003 29885 +1003 29885 +1002 29885 +1002 29886 +1001 29886 +1001 29886 +1001 29887 +1000 29887 +1000 29888 +1000 29888 +999 29888 +999 29889 +998 29889 +998 29890 +998 29890 +997 29890 +997 29891 +997 29891 +996 29891 +996 29892 +995 29892 +995 29893 +995 29893 +994 29893 +994 29894 +994 29894 +993 29894 +993 29895 +992 29895 +992 29896 +992 29896 +991 29896 +991 29896 +991 29897 +990 29897 +990 29898 +989 29898 +989 29898 +989 29899 +988 29899 +988 29899 +988 29900 +987 29900 +987 29901 +986 29901 +986 29901 +986 29902 +985 29902 +985 29902 +985 29903 +984 29903 +984 29903 +984 29904 +983 29904 +983 29904 +982 29905 +982 29905 +982 29906 +981 29906 +981 29906 +981 29907 +980 29907 +980 29907 +980 29908 +979 29908 +979 29908 +979 29909 +978 29910 +978 29910 +977 29910 +977 29911 +977 29911 +976 29912 +976 29912 +976 29913 +975 29913 +975 29913 +975 29914 +974 29914 +974 29915 +974 29915 +973 29915 +973 29916 +972 29916 +972 29916 +972 29917 +971 29917 +971 29917 +971 29918 +970 29918 +970 29919 +970 29919 +969 29919 +969 29920 +969 29920 +968 29920 +968 29921 +967 29921 +967 29922 +967 29922 +966 29923 +966 29923 +966 29923 +965 29924 +965 29924 +965 29924 +964 29925 +964 29925 +964 29925 +963 29926 +963 29926 +963 29927 +962 29927 +962 29927 +962 29928 +961 29928 +961 29928 +961 29929 +960 29929 +960 29929 +959 29930 +959 29930 +959 29930 +958 29931 +958 29931 +958 29931 +957 29932 +957 29932 +957 29932 +956 29933 +956 29933 +956 29934 +955 29934 +955 29934 +955 29935 +954 29935 +954 29936 +954 29936 +953 29936 +953 29937 +953 29937 +952 29937 +952 29938 +952 29938 +951 29938 +951 29939 +951 29939 +950 29939 +950 29940 +950 29940 +949 29941 +949 29941 +949 29941 +948 29942 +948 29942 +948 29943 +947 29943 +947 29943 +947 29944 +946 29944 +946 29944 +946 29945 +945 29945 +945 29946 +945 29946 +944 29947 +944 29947 +944 29947 +943 29947 +943 29948 +943 29948 +942 29948 +942 29949 +942 29950 +941 29950 +941 29951 +941 29951 +940 29951 +940 29952 +940 29952 +939 29953 +939 29953 +939 29953 +938 29954 +938 29954 +938 29954 +937 29954 +937 29955 +937 29955 +936 29955 +936 29956 +936 29956 +935 29957 +935 29957 +934 29957 +934 29958 +934 29958 +933 29959 +933 29959 +933 29959 +932 29960 +932 29961 +932 29961 +931 29961 +931 29961 +931 29962 +930 29962 +930 29962 +930 29963 +929 29963 +929 29963 +929 29964 +928 29964 +928 29964 +928 29965 +928 29965 +927 29966 +927 29966 +927 29967 +926 29967 +926 29968 +926 29968 +925 29968 +925 29969 +925 29969 +924 29970 +924 29970 +924 29970 +923 29971 +923 29971 +923 29972 +922 29972 +922 29972 +922 29973 +921 29973 +921 29973 +921 29974 +920 29974 +920 29975 +920 29975 +919 29975 +919 29976 +919 29976 +918 29976 +918 29977 +918 29977 +917 29978 +917 29978 +917 29978 +916 29979 +916 29979 +916 29980 +915 29980 +915 29980 +915 29981 +914 29981 +914 29982 +914 29982 +913 29982 +913 29983 +913 29983 +912 29984 +912 29984 +912 29984 +911 29985 +911 29985 +911 29985 +910 29986 +910 29986 +910 29986 +910 29987 +909 29987 +909 29988 +909 29988 +908 29988 +908 29989 +908 29989 +907 29990 +907 29990 +907 29990 +906 29990 +906 29991 +906 29991 +905 29991 +905 29992 +905 29992 +904 29992 +904 29993 +904 29993 +903 29993 +903 29994 +903 29994 +902 29994 +902 29994 +902 29995 +902 29995 +901 29996 +901 29996 +901 29997 +900 29997 +900 29997 +900 29998 +899 29998 +899 29999 +899 29999 +898 29999 +898 30000 +898 30000 +897 30001 +897 30001 +897 30001 +896 30002 +896 30002 +896 30002 +896 30003 +895 30003 +895 30004 +895 30004 +894 30004 +894 30005 +894 30005 +893 30006 +893 30006 +893 30006 +892 30007 +892 30007 +892 30008 +891 30008 +891 30008 +891 30008 +890 30009 +890 30009 +890 30010 +889 30010 +889 30010 +889 30011 +889 30011 +888 30011 +888 30012 +888 30012 +887 30012 +887 30013 +887 30013 +886 30013 +886 30014 +886 30014 +885 30014 +885 30015 +885 30015 +884 30016 +884 30016 +884 30016 +884 30017 +883 30017 +883 30017 +883 30018 +882 30018 +882 30019 +882 30019 +881 30020 +881 30020 +881 30020 +880 30021 +880 30021 +880 30021 +879 30022 +879 30022 +879 30022 +879 30023 +878 30023 +878 30024 +878 30024 +877 30024 +877 30025 +877 30025 +876 30025 +876 30025 +876 30026 +875 30027 +875 30027 +875 30027 +875 30028 +874 30028 +874 30028 +874 30029 +873 30029 +873 30029 +873 30029 +872 30030 +872 30030 +872 30031 +871 30031 +871 30031 +871 30032 +871 30032 +870 30033 +870 30033 +870 30033 +869 30034 +869 30034 +869 30034 +868 30034 +868 30035 +868 30035 +868 30036 +867 30036 +867 30036 +867 30037 +866 30037 +866 30037 +866 30038 +865 30038 +865 30039 +865 30039 +865 30039 +864 30040 +864 30040 +864 30040 +863 30041 +863 30041 +863 30041 +862 30041 +862 30042 +862 30042 +862 30043 +861 30043 +861 30044 +861 30044 +860 30045 +860 30045 +860 30045 +860 30046 +859 30046 +859 30047 +859 30047 +858 30048 +858 30048 +858 30048 +857 30049 +857 30049 +857 30050 +857 30050 +856 30050 +856 30051 +856 30051 +855 30051 +855 30052 +855 30053 +854 30053 +854 30053 +854 30054 +854 30054 +853 30055 +853 30055 +853 30056 +852 30056 +852 30057 +852 30057 +851 30057 +851 30058 +851 30058 +851 30059 +850 30059 +850 30059 +850 30060 +849 30060 +849 30061 +849 30061 +848 30062 +848 30062 +848 30063 +848 30063 +847 30063 +847 30064 +847 30064 +846 30065 +846 30065 +846 30066 +846 30066 +845 30067 +845 30067 +845 30067 +844 30068 +844 30068 +844 30069 +843 30069 +843 30069 +843 30070 +843 30071 +842 30071 +842 30071 +842 30072 +841 30072 +841 30073 +841 30073 +841 30074 +840 30074 +840 30074 +840 30075 +839 30075 +839 30076 +839 30076 +838 30076 +838 30077 +838 30077 +838 30078 +837 30078 +837 30078 +837 30079 +836 30080 +836 30080 +836 30081 +835 30081 +835 30082 +835 30082 +835 30083 +834 30083 +834 30083 +834 30084 +833 30084 +833 30085 +833 30085 +833 30085 +832 30086 +832 30086 +832 30087 +831 30087 +831 30088 +831 30088 +831 30089 +830 30089 +830 30090 +830 30090 +829 30090 +829 30091 +829 30091 +828 30092 +828 30092 +828 30092 +828 30093 +827 30094 +827 30094 +827 30095 +826 30095 +826 30095 +826 30096 +826 30096 +825 30097 +825 30097 +825 30098 +824 30098 +824 30099 +824 30099 +824 30100 +823 30100 +823 30101 +823 30101 +822 30101 +822 30102 +822 30102 +822 30103 +821 30103 +821 30104 +821 30104 +820 30105 +820 30105 +820 30105 +820 30106 +819 30107 +819 30107 +819 30108 +818 30108 +818 30108 +818 30109 +818 30109 +817 30110 +817 30110 +817 30111 +816 30111 +816 30112 +816 30112 +816 30113 +815 30113 +815 30114 +815 30114 +814 30115 +814 30115 +814 30116 +814 30116 +813 30116 +813 30117 +813 30117 +812 30118 +812 30119 +812 30119 +812 30120 +811 30120 +811 30120 +811 30121 +810 30122 +810 30122 +810 30123 +810 30123 +809 30123 +809 30124 +809 30124 +808 30125 +808 30125 +808 30126 +808 30126 +807 30127 +807 30127 +807 30128 +807 30128 +806 30129 +806 30129 +806 30130 +805 30130 +805 30131 +805 30131 +805 30132 +804 30132 +804 30133 +804 30133 +803 30134 +803 30134 +803 30135 +803 30135 +802 30136 +802 30136 +802 30137 +802 30137 +801 30138 +801 30139 +801 30139 +800 30140 +800 30140 +800 30141 +800 30141 +799 30142 +799 30142 +799 30143 +798 30143 +798 30143 +798 30144 +798 30144 +797 30145 +797 30145 +797 30146 +797 30146 +796 30147 +796 30147 +796 30148 +796 30148 +795 30149 +795 30149 +795 30150 +794 30150 +794 30151 +794 30151 +794 30151 +793 30152 +793 30152 +793 30153 +793 30153 +792 30154 +792 30154 +792 30155 +791 30155 +791 30156 +791 30156 +791 30157 +790 30157 +790 30158 +790 30158 +790 30159 +789 30159 +789 30160 +789 30160 +788 30160 +788 30161 +788 30161 +788 30162 +787 30162 +787 30163 +787 30163 +787 30164 +786 30164 +786 30165 +786 30165 +785 30166 +785 30166 +785 30167 +785 30167 +784 30168 +784 30168 +784 30169 +784 30169 +783 30170 +783 30170 +783 30171 +783 30171 +782 30172 +782 30172 +782 30173 +781 30173 +781 30173 +781 30174 +781 30174 +780 30175 +780 30175 +780 30176 +780 30176 +779 30177 +779 30177 +779 30178 +779 30178 +778 30179 +778 30179 +778 30180 +777 30181 +777 30181 +777 30182 +777 30182 +776 30183 +776 30183 +776 30183 +776 30184 +775 30184 +775 30185 +775 30185 +775 30186 +774 30186 +774 30187 +774 30187 +773 30188 +773 30188 +773 30189 +773 30189 +772 30190 +772 30190 +772 30191 +772 30191 +771 30192 +771 30192 +771 30193 +771 30193 +770 30194 +770 30194 +770 30195 +770 30195 +769 30196 +769 30196 +769 30197 +768 30198 +768 30198 +768 30199 +768 30199 +767 30200 +767 30200 +767 30201 +767 30201 +766 30202 +766 30202 +766 30203 +766 30203 +765 30204 +765 30204 +765 30205 +765 30205 +764 30206 +764 30207 +764 30207 +763 30208 +763 30208 +763 30209 +763 30209 +762 30210 +762 30210 +762 30210 +762 30211 +761 30211 +761 30212 +761 30212 +761 30213 +760 30213 +760 30214 +760 30214 +760 30215 +759 30215 +759 30216 +759 30217 +759 30217 +758 30217 +758 30218 +758 30218 +758 30219 +757 30219 +757 30220 +757 30220 +757 30221 +756 30222 +756 30222 +756 30222 +756 30223 +755 30224 +755 30224 +755 30225 +755 30225 +754 30226 +754 30226 +754 30227 +754 30227 +753 30228 +753 30228 +753 30229 +753 30229 +752 30229 +752 30230 +752 30230 +752 30231 +751 30231 +751 30232 +751 30232 +750 30233 +750 30233 +750 30234 +750 30234 +750 30235 +749 30235 +749 30236 +749 30236 +749 30237 +748 30237 +748 30237 +748 30238 +748 30238 +747 30239 +747 30239 +747 30240 +747 30240 +746 30241 +746 30241 +746 30242 +746 30242 +745 30243 +745 30243 +745 30244 +745 30244 +744 30244 +744 30245 +744 30245 +744 30246 +743 30246 +743 30247 +743 30247 +743 30248 +742 30248 +742 30248 +742 30249 +742 30249 +741 30250 +741 30250 +741 30251 +741 30251 +741 30251 +740 30252 +740 30252 +740 30253 +740 30253 +739 30254 +739 30254 +739 30254 +739 30255 +738 30255 +738 30256 +738 30256 +738 30256 +737 30257 +737 30257 +737 30258 +737 30258 +737 30259 +736 30259 +736 30259 +736 30260 +736 30260 +735 30261 +735 30261 +735 30261 +735 30262 +734 30262 +734 30263 +734 30263 +734 30263 +733 30264 +733 30264 +733 30265 +733 30265 +733 30265 +732 30266 +732 30266 +732 30267 +732 30267 +731 30268 +731 30268 +731 30268 +731 30269 +730 30269 +730 30270 +730 30270 +730 30270 +730 30271 +729 30271 +729 30272 +729 30272 +729 30272 +728 30273 +728 30273 +728 30274 +728 30274 +727 30274 +727 30275 +727 30275 +727 30275 +727 30276 +726 30276 +726 30277 +726 30277 +726 30277 +725 30278 +725 30278 +725 30279 +725 30279 +725 30280 +724 30280 +724 30280 +724 30281 +724 30281 +723 30281 +723 30282 +723 30282 +723 30282 +723 30283 +722 30283 +722 30284 +722 30284 +722 30284 +721 30285 +721 30285 +721 30285 +721 30286 +721 30286 +720 30287 +720 30287 +720 30287 +720 30288 +720 30288 +719 30288 +719 30289 +719 30289 +719 30290 +718 30290 +718 30290 +718 30291 +718 30291 +718 30292 +717 30292 +717 30292 +717 30293 +717 30293 +716 30293 +716 30294 +716 30294 +716 30294 +716 30295 +715 30295 +715 30295 +715 30296 +715 30296 +715 30296 +714 30297 +714 30297 +714 30297 +714 30298 +713 30298 +713 30298 +713 30299 +713 30299 +713 30299 +712 30300 +712 30300 +712 30300 +712 30301 +712 30301 +711 30301 +711 30302 +711 30302 +711 30302 +711 30303 +710 30303 +710 30303 +710 30304 +710 30304 +710 30304 +709 30304 +709 30305 +709 30305 +709 30306 +708 30306 +708 30306 +708 30307 +708 30307 +708 30307 +707 30307 +707 30308 +707 30308 +707 30308 +707 30309 +706 30309 +706 30309 +706 30310 +706 30310 +706 30310 +705 30311 +705 30311 +705 30311 +705 30312 +705 30312 +704 30312 +704 30312 +704 30313 +704 30313 +704 30313 +703 30314 +703 30314 +703 30314 +703 30314 +703 30315 +702 30315 +702 30315 +702 30315 +702 30316 +702 30316 +701 30316 +701 30317 +701 30317 +701 30317 +701 30318 +700 30318 +700 30318 +700 30318 +700 30319 +700 30319 +699 30319 +699 30319 +699 30320 +699 30320 +699 30320 +698 30320 +698 30320 +698 30321 +698 30321 +698 30321 +697 30321 +697 30322 +697 30322 +697 30322 +697 30323 +696 30323 +696 30323 +696 30324 +696 30324 +696 30324 +696 30324 +695 30324 +695 30325 +695 30325 +695 30325 +695 30325 +694 30326 +694 30326 +694 30326 +694 30326 +694 30327 +693 30327 +693 30327 +693 30328 +693 30328 +693 30328 +692 30328 +692 30329 +692 30329 +692 30329 +692 30329 +692 30329 +691 30330 +691 30330 +691 30330 +691 30330 +691 30330 +690 30331 +690 30331 +690 30331 +690 30331 +690 30332 +690 30332 +689 30332 +689 30332 +689 30333 +689 30333 +689 30333 +688 30334 +688 30334 +688 30334 +688 30334 +688 30334 +688 30334 +687 30335 +687 30335 +687 30335 +687 30335 +687 30335 +687 30336 +686 30336 +686 30336 +686 30336 +686 30336 +686 30336 +686 30336 +685 30337 +685 30337 +685 30337 +685 30337 +685 30338 +685 30338 +684 30338 +684 30338 +684 30338 +684 30338 +684 30339 +683 30339 +683 30339 +683 30339 +683 30339 +683 30340 +683 30340 +682 30340 +682 30340 +682 30340 +682 30340 +682 30341 +682 30341 +682 30341 +681 30341 +681 30341 +681 30341 +681 30341 +681 30342 +681 30342 +680 30342 +680 30342 +680 30342 +680 30342 +680 30343 +680 30343 +679 30343 +679 30343 +679 30343 +679 30343 +679 30343 +679 30344 +678 30344 +678 30344 +678 30344 +678 30344 +678 30344 +678 30344 +677 30344 +677 30345 +677 30345 +677 30345 +677 30345 +677 30345 +677 30346 +676 30346 +676 30346 +676 30346 +676 30346 +676 30346 +676 30347 +675 30347 +675 30347 +675 30347 +675 30348 +675 30348 +675 30348 +675 30348 +674 30348 +674 30348 +674 30348 +674 30348 +674 30348 +674 30348 +674 30348 +673 30348 +673 30348 +673 30349 +673 30349 +673 30349 +673 30349 +672 30350 +672 30350 +672 30350 +672 30350 +672 30350 +672 30350 +672 30350 +671 30351 +671 30351 +671 30351 +671 30351 +671 30351 +671 30351 +671 30351 +670 30351 +670 30351 +670 30351 +670 30352 +670 30352 +670 30352 +670 30352 +669 30352 +669 30352 +669 30352 +669 30352 +669 30353 +669 30353 +669 30353 +668 30353 +668 30353 +668 30354 +668 30354 +668 30354 +668 30354 +667 30354 +667 30354 +667 30354 +667 30354 +667 30355 +667 30355 +667 30355 +666 30355 +666 30355 +666 30355 +666 30355 +666 30355 +666 30355 +666 30355 +666 30355 +665 30355 +665 30355 +665 30356 +665 30356 +665 30356 +665 30356 +665 30356 +664 30356 +664 30357 +664 30357 +664 30357 +664 30357 +664 30357 +664 30357 +663 30357 +663 30358 +663 30358 +663 30358 +663 30358 +663 30358 +663 30358 +663 30358 +662 30358 +662 30359 +662 30359 +662 30359 +662 30359 +662 30359 +662 30360 +661 30360 +661 30360 +661 30360 +661 30360 +661 30360 +661 30360 +661 30361 +661 30361 +660 30361 +660 30361 +660 30361 +660 30362 +660 30362 +660 30362 +660 30362 +660 30362 +659 30362 +659 30363 +659 30363 +659 30363 +659 30363 +659 30363 +659 30363 +658 30364 +658 30364 +658 30364 +658 30364 +658 30364 +658 30364 +658 30365 +658 30365 +657 30365 +657 30365 +657 30365 +657 30366 +657 30366 +657 30366 +657 30366 +657 30366 +656 30366 +656 30366 +656 30366 +656 30366 +656 30366 +656 30366 +656 30367 +656 30367 +655 30367 +655 30367 +655 30367 +655 30367 +655 30367 +655 30367 +655 30368 +654 30368 +654 30368 +654 30368 +654 30368 +654 30368 +654 30368 +654 30368 +654 30369 +653 30369 +653 30369 +653 30369 +653 30369 +653 30369 +653 30369 +653 30369 +653 30369 +653 30370 +652 30370 +652 30370 +652 30370 +652 30370 +652 30370 +652 30370 +652 30370 +652 30370 +651 30370 +651 30371 +651 30371 +651 30371 +651 30371 +651 30371 +651 30371 +651 30371 +650 30371 +650 30371 +650 30372 +650 30372 +650 30372 +650 30372 +650 30372 +650 30372 +649 30372 +649 30372 +649 30372 +649 30372 +649 30372 +649 30372 +649 30373 +649 30373 +648 30373 +648 30373 +648 30373 +648 30373 +648 30373 +648 30373 +648 30374 +648 30374 +647 30374 +647 30374 +647 30374 +647 30374 +647 30374 +647 30374 +647 30375 +647 30375 +647 30375 +646 30375 +646 30375 +646 30375 +646 30375 +646 30375 +646 30375 +646 30375 +646 30375 +645 30375 +645 30376 +645 30376 +645 30376 +645 30376 +645 30376 +645 30376 +645 30376 +644 30376 +644 30377 +644 30377 +644 30377 +644 30377 +644 30377 +644 30377 +644 30377 +644 30377 +643 30377 +643 30378 +643 30378 +643 30378 +643 30378 +643 30378 +643 30378 +643 30378 +642 30378 +642 30378 +642 30379 +642 30379 +642 30379 +642 30379 +642 30379 +642 30379 +642 30379 +641 30379 +641 30380 +641 30379 +641 30380 +641 30380 +641 30380 +641 30380 +641 30380 +640 30380 +640 30380 +640 30380 +640 30380 +640 30381 +640 30381 +640 30381 +640 30381 +640 30381 +639 30381 +639 30381 +639 30381 +639 30382 +639 30382 +639 30382 +639 30382 +639 30382 +638 30382 +638 30382 +638 30382 +638 30382 +638 30382 +638 30382 +638 30383 +638 30383 +638 30383 +637 30383 +637 30383 +637 30383 +637 30383 +637 30383 +637 30384 +637 30384 +637 30384 +637 30384 +636 30384 +636 30384 +636 30384 +636 30384 +636 30384 +636 30384 +636 30384 +636 30384 +636 30385 +636 30385 +635 30385 +635 30385 +635 30385 +635 30385 +635 30385 +635 30385 +635 30385 +635 30386 +635 30386 +634 30386 +634 30386 +634 30386 +634 30386 +634 30386 +634 30386 +634 30386 +634 30386 +634 30387 +633 30387 +633 30387 +633 30387 +633 30387 +633 30387 +633 30387 +633 30387 +633 30388 +633 30388 +633 30388 +632 30388 +632 30388 +632 30388 +632 30388 +632 30388 +632 30388 +632 30388 +632 30388 +632 30388 +631 30388 +631 30389 +631 30389 +631 30389 +631 30389 +631 30389 +631 30389 +631 30389 +631 30389 +630 30389 +630 30390 +630 30390 +630 30390 +630 30390 +630 30390 +630 30390 +630 30390 +630 30390 +629 30391 +629 30391 +629 30391 +629 30391 +629 30391 +629 30391 +629 30391 +629 30391 +629 30392 +628 30392 +628 30392 +628 30392 +628 30392 +628 30392 +628 30392 +628 30392 +628 30392 +628 30393 +628 30393 +627 30393 +627 30393 +627 30393 +627 30393 +627 30393 +627 30393 +627 30394 +627 30394 +627 30394 +626 30394 +626 30394 +626 30394 +626 30394 +626 30394 +626 30395 +626 30394 +626 30395 +626 30395 +625 30395 +625 30395 +625 30395 +625 30395 +625 30395 +625 30395 +625 30396 +625 30396 +625 30396 +624 30396 +624 30396 +624 30396 +624 30396 +624 30396 +624 30397 +624 30397 +624 30397 +624 30397 +623 30397 +623 30397 +623 30397 +623 30397 +623 30397 +623 30397 +623 30398 +623 30398 +623 30398 +622 30398 +622 30398 +622 30398 +622 30398 +622 30398 +622 30399 +622 30399 +622 30399 +622 30399 +621 30399 +621 30399 +621 30399 +621 30399 +621 30399 +621 30400 +621 30400 +621 30400 +621 30400 +620 30400 +620 30400 +620 30400 +620 30400 +620 30400 +620 30400 +620 30400 +620 30400 +620 30401 +619 30401 +619 30401 +619 30401 +619 30401 +619 30401 +619 30401 +619 30401 +619 30401 +619 30401 +619 30401 +618 30401 +618 30402 +618 30402 +618 30402 +618 30402 +618 30402 +618 30402 +618 30402 +617 30402 +617 30402 +617 30402 +617 30402 +617 30403 +617 30403 +617 30403 +617 30403 +617 30403 +616 30403 +616 30403 +616 30403 +616 30403 +616 30404 +616 30404 +616 30404 +616 30404 +616 30404 +615 30404 +615 30404 +615 30404 +615 30405 +615 30405 +615 30405 +615 30405 +615 30405 +615 30405 +614 30405 +614 30405 +614 30405 +614 30405 +614 30405 +614 30406 +614 30406 +614 30406 +614 30406 +613 30406 +613 30406 +613 30406 +613 30406 +613 30406 +613 30407 +613 30407 +613 30407 +613 30407 +612 30407 +612 30407 +612 30407 +612 30407 +612 30407 +612 30407 +612 30407 +612 30408 +611 30408 +611 30408 +611 30408 +611 30408 +611 30408 +611 30408 +611 30408 +611 30408 +611 30408 +610 30408 +610 30409 +610 30409 +610 30409 +610 30409 +610 30409 +610 30409 +610 30409 +610 30409 +609 30409 +609 30409 +609 30409 +609 30410 +609 30410 +609 30410 +609 30410 +609 30410 +609 30410 +608 30410 +608 30410 +608 30410 +608 30410 +608 30410 +608 30410 +608 30411 +608 30411 +607 30411 +607 30411 +607 30411 +607 30411 +607 30411 +607 30411 +607 30411 +607 30411 +607 30412 +606 30412 +606 30412 +606 30412 +606 30412 +606 30412 +606 30412 +606 30412 +606 30412 +606 30412 +605 30413 +605 30413 +605 30413 +605 30413 +605 30413 +605 30413 +605 30413 +605 30413 +604 30413 +604 30414 +604 30414 +604 30414 +604 30414 +604 30414 +604 30414 +604 30414 +603 30414 +603 30414 +603 30414 +603 30415 +603 30415 +603 30415 +603 30415 +603 30415 +603 30415 +602 30415 +602 30415 +602 30415 +602 30415 +602 30416 +602 30416 +602 30416 +602 30416 +602 30416 +601 30416 +601 30416 +601 30416 +601 30416 +601 30416 +601 30417 +601 30417 +601 30417 +600 30417 +600 30417 +600 30417 +600 30417 +600 30417 +600 30417 +600 30417 +600 30418 +600 30418 +599 30418 +599 30418 +599 30418 +599 30418 +599 30418 +599 30418 +599 30418 +599 30418 +599 30419 +598 30419 +598 30419 +598 30419 +598 30419 +598 30419 +598 30419 +598 30419 +598 30419 +597 30419 +597 30420 +597 30420 +597 30420 +597 30420 +597 30420 +597 30420 +597 30420 +597 30420 +596 30420 +596 30420 +596 30421 +596 30421 +596 30421 +596 30421 +596 30421 +596 30421 +595 30421 +595 30421 +595 30421 +595 30421 +595 30422 +595 30422 +595 30422 +595 30422 +595 30422 +594 30422 +594 30422 +594 30422 +594 30422 +594 30422 +594 30423 +594 30423 +594 30423 +594 30423 +593 30423 +593 30423 +593 30423 +593 30423 +593 30423 +593 30423 +593 30424 +593 30424 +592 30424 +592 30424 +592 30424 +592 30424 +592 30424 +592 30424 +592 30424 +592 30424 +592 30425 +591 30425 +591 30425 +591 30425 +591 30425 +591 30425 +591 30425 +591 30425 +591 30425 +590 30425 +590 30426 +590 30426 +590 30426 +590 30426 +590 30426 +590 30426 +590 30426 +590 30426 +589 30426 +589 30426 +589 30427 +589 30427 +589 30427 +589 30427 +589 30427 +589 30427 +588 30427 +588 30427 +588 30427 +588 30428 +588 30428 +588 30428 +588 30428 +588 30428 +587 30428 +587 30428 +587 30428 +587 30428 +587 30428 +587 30429 +587 30429 +587 30429 +586 30429 +586 30429 +586 30429 +586 30429 +586 30429 +586 30429 +586 30429 +586 30430 +586 30430 +585 30430 +585 30430 +585 30430 +585 30430 +585 30430 +585 30430 +585 30430 +585 30430 +584 30431 +584 30431 +584 30431 +584 30431 +584 30431 +584 30431 +584 30431 +584 30431 +583 30431 +583 30431 +583 30432 +583 30432 +583 30432 +583 30432 +583 30432 +583 30432 +582 30432 +582 30432 +582 30432 +582 30432 +582 30433 +582 30433 +582 30433 +582 30433 +581 30433 +581 30433 +581 30433 +581 30433 +581 30433 +581 30433 +581 30434 +581 30434 +580 30434 +580 30434 +580 30434 +580 30434 +580 30434 +580 30434 +580 30434 +580 30434 +579 30435 +579 30435 +579 30435 +579 30435 +579 30435 +579 30435 +579 30435 +579 30435 +578 30435 +578 30435 +578 30436 +578 30436 +578 30436 +578 30436 +578 30436 +578 30436 +577 30436 +577 30436 +577 30436 +577 30436 +577 30437 +577 30437 +577 30437 +577 30437 +576 30437 +576 30437 +576 30437 +576 30437 +576 30437 +576 30438 +576 30438 +576 30438 +575 30438 +575 30438 +575 30438 +575 30438 +575 30438 +575 30438 +575 30438 +575 30439 +574 30439 +574 30439 +574 30439 +574 30439 +574 30439 +574 30439 +574 30439 +574 30439 +573 30439 +573 30440 +573 30440 +573 30440 +573 30440 +573 30440 +573 30440 +573 30440 +572 30440 +572 30440 +572 30440 +572 30441 +572 30441 +572 30441 +572 30441 +572 30441 +571 30441 +571 30441 +571 30441 +571 30441 +571 30441 +571 30442 +571 30442 +571 30442 +571 30442 +570 30442 +570 30442 +570 30442 +570 30442 +570 30442 +570 30443 +570 30443 +570 30443 +569 30443 +569 30443 +569 30443 +569 30443 +569 30443 +569 30443 +569 30443 +568 30444 +568 30444 +568 30444 +568 30444 +568 30444 +568 30444 +568 30444 +568 30444 +568 30444 +567 30444 +567 30445 +567 30445 +567 30445 +567 30445 +567 30445 +567 30445 +567 30445 +566 30445 +566 30445 +566 30446 +566 30446 +566 30446 +566 30446 +566 30446 +566 30446 +565 30446 +565 30446 +565 30446 +565 30446 +565 30447 +565 30447 +565 30447 +564 30447 +564 30447 +564 30447 +564 30447 +564 30447 +564 30447 +564 30447 +564 30448 +564 30448 +563 30448 +563 30448 +563 30448 +563 30448 +563 30448 +563 30448 +563 30448 +563 30448 +562 30449 +562 30449 +562 30449 +562 30449 +562 30449 +562 30449 +562 30449 +562 30449 +561 30449 +561 30450 +561 30450 +561 30450 +561 30450 +561 30450 +561 30450 +561 30450 +560 30450 +560 30450 +560 30450 +560 30451 +560 30451 +560 30451 +560 30451 +560 30451 +559 30451 +559 30451 +559 30451 +559 30451 +559 30451 +559 30452 +559 30452 +559 30452 +558 30452 +558 30452 +558 30452 +558 30452 +558 30452 +558 30452 +558 30452 +558 30453 +557 30453 +557 30453 +557 30453 +557 30453 +557 30453 +557 30453 +557 30453 +557 30453 +556 30454 +556 30454 +556 30454 +556 30454 +556 30454 +556 30454 +556 30454 +556 30454 +555 30454 +555 30454 +555 30455 +555 30455 +555 30455 +555 30455 +555 30455 +555 30455 +554 30455 +554 30455 +554 30455 +554 30455 +554 30456 +554 30456 +554 30456 +554 30456 +553 30456 +553 30456 +553 30456 +553 30456 +553 30456 +553 30456 +553 30457 +553 30457 +552 30457 +552 30457 +552 30457 +552 30457 +552 30457 +552 30457 +552 30457 +551 30457 +551 30458 +551 30458 +551 30458 +551 30458 +551 30458 +551 30458 +551 30458 +551 30458 +550 30458 +550 30459 +550 30459 +550 30459 +550 30459 +550 30459 +550 30459 +550 30459 +549 30459 +549 30459 +549 30459 +549 30460 +549 30460 +549 30460 +549 30460 +548 30460 +548 30460 +548 30460 +548 30460 +548 30460 +548 30461 +548 30461 +548 30461 +547 30461 +547 30461 +547 30461 +547 30461 +547 30461 +547 30461 +547 30461 +547 30462 +546 30462 +546 30462 +546 30462 +546 30462 +546 30462 +546 30462 +546 30462 +545 30462 +545 30462 +545 30463 +545 30463 +545 30463 +545 30463 +545 30463 +545 30463 +544 30463 +544 30463 +544 30463 +544 30464 +544 30464 +544 30464 +544 30464 +544 30464 +543 30464 +543 30464 +543 30464 +543 30464 +543 30464 +543 30465 +543 30465 +542 30465 +542 30465 +542 30465 +542 30465 +542 30465 +542 30465 +542 30465 +542 30465 +541 30466 +541 30466 +541 30466 +541 30466 +541 30466 +541 30466 +541 30466 +541 30466 +540 30466 +540 30466 +540 30467 +540 30467 +540 30467 +540 30467 +540 30467 +540 30467 +539 30467 +539 30467 +539 30467 +539 30468 +539 30468 +539 30468 +539 30468 +539 30468 +538 30468 +538 30468 +538 30468 +538 30468 +538 30468 +538 30469 +538 30469 +538 30469 +537 30469 +537 30469 +537 30469 +537 30469 +537 30469 +537 30469 +537 30469 +537 30470 +536 30470 +536 30470 +536 30470 +536 30470 +536 30470 +536 30470 +536 30470 +536 30470 +536 30470 +535 30471 +535 30471 +535 30471 +535 30471 +535 30471 +535 30471 +535 30471 +535 30471 +534 30471 +534 30471 +534 30472 +534 30472 +534 30472 +534 30472 +534 30472 +534 30472 +533 30472 +533 30472 +533 30472 +533 30473 +533 30473 +533 30473 +533 30473 +533 30473 +532 30473 +532 30473 +532 30473 +532 30473 +532 30473 +532 30474 +532 30474 +532 30474 +531 30474 +531 30474 +531 30474 +531 30474 +531 30474 +531 30474 +531 30474 +531 30475 +530 30475 +530 30475 +530 30475 +530 30475 +530 30475 +530 30475 +530 30475 +530 30475 +529 30475 +529 30476 +529 30476 +529 30476 +529 30476 +529 30476 +529 30476 +529 30476 +529 30476 +528 30476 +528 30476 +528 30477 +528 30477 +528 30477 +528 30477 +528 30477 +528 30477 +527 30477 +527 30477 +527 30477 +527 30477 +527 30478 +527 30478 +527 30478 +527 30478 +526 30478 +526 30478 +526 30478 +526 30478 +526 30478 +526 30479 +526 30479 +526 30479 +525 30479 +525 30479 +525 30479 +525 30479 +525 30479 +525 30479 +525 30479 +525 30480 +524 30480 +524 30480 +524 30480 +524 30480 +524 30480 +524 30480 +524 30480 +524 30480 +523 30480 +523 30481 +523 30481 +523 30481 +523 30481 +523 30481 +523 30481 +523 30481 +522 30481 +522 30481 +522 30481 +522 30482 +522 30482 +522 30482 +522 30482 +522 30482 +521 30482 +521 30482 +521 30482 +521 30482 +521 30482 +521 30483 +521 30483 +521 30483 +520 30483 +520 30483 +520 30483 +520 30483 +520 30483 +520 30483 +520 30484 +520 30484 +519 30484 +519 30484 +519 30484 +519 30484 +519 30484 +519 30484 +519 30484 +519 30484 +518 30485 +518 30485 +518 30485 +518 30485 +518 30485 +518 30485 +518 30485 +518 30485 +517 30485 +517 30485 +517 30486 +517 30486 +517 30486 +517 30486 +517 30486 +517 30486 +516 30486 +516 30486 +516 30486 +516 30486 +516 30487 +516 30487 +516 30487 +516 30487 +515 30487 +515 30487 +515 30487 +515 30487 +515 30487 +515 30488 +515 30488 +515 30488 +514 30488 +514 30488 +514 30488 +514 30488 +514 30488 +514 30488 +514 30488 +514 30489 +513 30489 +513 30489 +513 30489 +513 30489 +513 30489 +513 30489 +513 30489 +512 30489 +512 30489 +512 30490 +512 30490 +512 30490 +512 30490 +512 30490 +512 30490 +511 30490 +511 30490 +511 30490 +511 30490 +511 30491 +511 30491 +511 30491 +511 30491 +510 30491 +510 30491 +510 30491 +510 30491 +510 30491 +510 30492 +510 30492 +510 30492 +509 30492 +509 30492 +509 30492 +509 30492 +509 30492 +509 30492 +509 30492 +508 30493 +508 30493 +508 30493 +508 30493 +508 30493 +508 30493 +508 30493 +508 30493 +507 30493 +507 30493 +507 30494 +507 30494 +507 30494 +507 30494 +507 30494 +507 30494 +506 30494 +506 30494 +506 30494 +506 30494 +506 30495 +506 30495 +506 30495 +506 30495 +505 30495 +505 30495 +505 30495 +505 30495 +505 30495 +505 30495 +505 30496 +504 30496 +504 30496 +504 30496 +504 30496 +504 30496 +504 30496 +504 30496 +504 30496 +503 30496 +503 30497 +503 30497 +503 30497 +503 30497 +503 30497 +503 30497 +502 30497 +502 30497 +502 30497 +502 30498 +502 30498 +502 30498 +502 30498 +502 30498 +501 30498 +501 30498 +501 30498 +501 30498 +501 30498 +501 30499 +501 30499 +501 30499 +500 30499 +500 30499 +500 30499 +500 30499 +500 30499 +500 30499 +500 30499 +500 30500 +499 30500 +499 30500 +499 30500 +499 30500 +499 30500 +499 30500 +499 30500 +498 30500 +498 30500 +498 30501 +498 30501 +498 30501 +498 30501 +498 30501 +498 30501 +497 30501 +497 30501 +497 30501 +497 30501 +497 30502 +497 30502 +497 30502 +497 30502 +496 30502 +496 30502 +496 30502 +496 30502 +496 30502 +496 30503 +496 30503 +496 30503 +495 30503 +495 30503 +495 30503 +495 30503 +495 30503 +495 30503 +495 30503 +494 30504 +494 30504 +494 30504 +494 30504 +494 30504 +494 30504 +494 30504 +494 30504 +493 30504 +493 30504 +493 30505 +493 30505 +493 30505 +493 30505 +493 30505 +493 30505 +492 30505 +492 30505 +492 30505 +492 30505 +492 30506 +492 30506 +492 30506 +492 30506 +491 30506 +491 30506 +491 30506 +491 30506 +491 30506 +491 30506 +491 30507 +491 30507 +490 30507 +490 30507 +490 30507 +490 30507 +490 30507 +490 30507 +490 30507 +489 30508 +489 30508 +489 30508 +489 30508 +489 30508 +489 30508 +489 30508 +489 30508 +488 30508 +488 30508 +488 30509 +488 30509 +488 30509 +488 30509 +488 30509 +488 30509 +487 30509 +487 30509 +487 30509 +487 30509 +487 30510 +487 30510 +487 30510 +486 30510 +486 30510 +486 30510 +486 30510 +486 30510 +486 30510 +486 30510 +486 30511 +485 30511 +485 30511 +485 30511 +485 30511 +485 30511 +485 30511 +485 30511 +484 30511 +484 30511 +484 30512 +484 30512 +484 30512 +484 30512 +484 30512 +483 30512 +483 30512 +483 30512 +483 30512 +483 30512 +483 30513 +483 30513 +483 30513 +482 30513 +482 30513 +482 30513 +482 30513 +482 30513 +482 30513 +482 30514 +481 30514 +481 30514 +481 30514 +481 30514 +481 30514 +481 30514 +481 30514 +480 30514 +480 30514 +480 30515 +480 30515 +480 30515 +480 30515 +480 30515 +479 30515 +479 30515 +479 30515 +479 30515 +479 30515 +479 30516 +479 30516 +478 30516 +478 30516 +478 30516 +478 30516 +478 30516 +478 30516 +478 30516 +477 30516 +477 30517 +477 30517 +477 30517 +477 30517 +477 30517 +477 30517 +477 30517 +476 30517 +476 30517 +476 30518 +476 30518 +476 30518 +476 30518 +476 30518 +475 30518 +475 30518 +475 30518 +475 30518 +475 30518 +475 30519 +475 30519 +474 30519 +474 30519 +474 30519 +474 30519 +474 30519 +474 30519 +474 30519 +474 30519 +473 30520 +473 30520 +473 30520 +473 30520 +473 30520 +473 30520 +473 30520 +472 30520 +472 30520 +472 30520 +472 30521 +472 30521 +472 30521 +472 30521 +471 30521 +471 30521 +471 30521 +471 30521 +471 30521 +471 30521 +471 30522 +471 30522 +470 30522 +470 30522 +470 30522 +470 30522 +470 30522 +470 30522 +470 30522 +469 30522 +469 30523 +469 30523 +469 30523 +469 30523 +469 30523 +469 30523 +468 30523 +468 30523 +468 30523 +468 30523 +468 30524 +468 30524 +468 30524 +467 30524 +467 30524 +467 30524 +467 30524 +467 30524 +467 30524 +467 30524 +467 30525 +466 30525 +466 30525 +466 30525 +466 30525 +466 30525 +466 30525 +466 30525 +465 30525 +465 30525 +465 30526 +465 30526 +465 30526 +465 30526 +465 30526 +465 30526 +464 30526 +464 30526 +464 30526 +464 30526 +464 30527 +464 30527 +464 30527 +463 30527 +463 30527 +463 30527 +463 30527 +463 30527 +463 30527 +463 30527 +463 30528 +462 30528 +462 30528 +462 30528 +462 30528 +462 30528 +462 30528 +462 30528 +462 30528 +461 30529 +461 30529 +461 30529 +461 30529 +461 30529 +461 30529 +461 30529 +461 30529 +460 30529 +460 30529 +460 30530 +460 30530 +460 30530 +460 30530 +460 30530 +460 30530 +459 30530 +459 30530 +459 30530 +459 30530 +459 30531 +459 30531 +459 30531 +459 30531 +458 30531 +458 30531 +458 30531 +458 30531 +458 30531 +458 30531 +458 30532 +458 30532 +457 30532 +457 30532 +457 30532 +457 30532 +457 30532 +457 30532 +457 30532 +456 30533 +456 30533 +456 30533 +456 30533 +456 30533 +456 30533 +456 30533 +456 30533 +455 30533 +455 30533 +455 30534 +455 30534 +455 30534 +455 30534 +455 30534 +455 30534 +454 30534 +454 30534 +454 30534 +454 30534 +454 30535 +454 30535 +454 30535 +454 30535 +453 30535 +453 30535 +453 30535 +453 30535 +453 30535 +453 30535 +453 30536 +453 30536 +452 30536 +452 30536 +452 30536 +452 30536 +452 30536 +452 30536 +452 30536 +452 30536 +451 30537 +451 30537 +451 30537 +451 30537 +451 30537 +451 30537 +451 30537 +451 30537 +450 30537 +450 30538 +450 30538 +450 30538 +450 30538 +450 30538 +450 30538 +450 30538 +449 30538 +449 30538 +449 30538 +449 30539 +449 30539 +449 30539 +449 30539 +448 30539 +448 30539 +448 30539 +448 30539 +448 30539 +448 30539 +448 30540 +448 30540 +447 30540 +447 30540 +447 30540 +447 30540 +447 30540 +447 30540 +447 30540 +447 30540 +446 30541 +446 30541 +446 30541 +446 30541 +446 30541 +446 30541 +446 30541 +446 30541 +445 30541 +445 30541 +445 30542 +445 30542 +445 30542 +445 30542 +445 30542 +445 30542 +444 30542 +444 30542 +444 30542 +444 30542 +444 30543 +444 30543 +444 30543 +444 30543 +443 30543 +443 30543 +443 30543 +443 30543 +443 30543 +443 30543 +443 30544 +443 30544 +442 30544 +442 30544 +442 30544 +442 30544 +442 30544 +442 30544 +442 30544 +442 30544 +442 30545 +441 30545 +441 30545 +441 30545 +441 30545 +441 30545 +441 30545 +441 30545 +441 30545 +440 30546 +440 30546 +440 30546 +440 30546 +440 30546 +440 30546 +440 30546 +440 30546 +439 30546 +439 30546 +439 30547 +439 30547 +439 30547 +439 30547 +439 30547 +439 30547 +438 30547 +438 30547 +438 30547 +438 30547 +438 30548 +438 30548 +438 30548 +438 30548 +437 30548 +437 30548 +437 30548 +437 30548 +437 30548 +437 30548 +437 30549 +437 30549 +436 30549 +436 30549 +436 30549 +436 30549 +436 30549 +436 30549 +436 30549 +436 30549 +436 30550 +435 30550 +435 30550 +435 30550 +435 30550 +435 30550 +435 30550 +435 30550 +434 30550 +434 30550 +434 30551 +434 30551 +434 30551 +434 30551 +434 30551 +434 30551 +434 30551 +433 30551 +433 30551 +433 30551 +433 30552 +433 30552 +433 30552 +433 30552 +433 30552 +432 30552 +432 30552 +432 30552 +432 30552 +432 30553 +432 30553 +432 30553 +432 30553 +431 30553 +431 30553 +431 30553 +431 30553 +431 30553 +431 30553 +431 30554 +431 30554 +430 30554 +430 30554 +430 30554 +430 30554 +430 30554 +430 30554 +430 30554 +430 30554 +429 30555 +429 30555 +429 30555 +429 30555 +429 30555 +429 30555 +429 30555 +428 30555 +428 30555 +428 30555 +428 30556 +428 30556 +428 30556 +428 30556 +428 30556 +427 30556 +427 30556 +427 30556 +427 30556 +427 30556 +427 30557 +427 30557 +426 30557 +426 30557 +426 30557 +426 30557 +426 30557 +426 30557 +426 30557 +425 30557 +425 30558 +425 30558 +425 30558 +425 30558 +425 30558 +425 30558 +424 30558 +424 30558 +424 30558 +424 30558 +424 30559 +424 30559 +424 30559 +423 30559 +423 30559 +423 30559 +423 30559 +423 30559 +423 30559 +423 30559 +422 30560 +422 30560 +422 30560 +422 30560 +422 30560 +422 30560 +422 30560 +422 30560 +421 30560 +421 30560 +421 30561 +421 30561 +421 30561 +421 30561 +421 30561 +421 30561 +421 30561 +420 30561 +420 30561 +420 30561 +420 30562 +420 30562 +420 30562 +420 30562 +420 30562 +420 30562 +419 30562 +419 30562 +419 30562 +419 30562 +419 30563 +419 30563 +419 30563 +419 30563 +419 30563 +418 30563 +418 30563 +418 30563 +418 30563 +418 30563 +418 30564 +418 30564 +418 30564 +418 30564 +418 30564 +417 30564 +417 30564 +417 30564 +417 30564 +417 30564 +417 30565 +417 30565 +417 30565 +417 30565 +416 30565 +416 30565 +416 30565 +416 30565 +416 30565 +416 30565 +416 30566 +416 30566 +416 30566 +415 30566 +415 30566 +415 30566 +415 30566 +415 30566 +415 30566 +415 30566 +415 30567 +415 30567 +415 30567 +414 30567 +414 30567 +414 30567 +414 30567 +414 30567 +414 30567 +414 30567 +414 30568 +413 30568 +413 30568 +413 30568 +413 30568 +413 30568 +413 30568 +413 30568 +413 30568 +413 30568 +412 30569 +412 30569 +412 30569 +412 30569 +412 30569 +412 30569 +412 30569 +412 30569 +412 30569 +411 30570 +411 30570 +411 30570 +411 30570 +411 30570 +411 30570 +411 30570 +411 30570 +411 30570 +410 30570 +410 30571 +410 30571 +410 30571 +410 30571 +410 30571 +410 30571 +410 30571 +409 30571 +409 30571 +409 30571 +409 30572 +409 30572 +409 30572 +409 30572 +409 30572 +409 30572 +408 30572 +408 30572 +408 30572 +408 30572 +408 30573 +408 30573 +408 30573 +408 30573 +407 30573 +407 30573 +407 30573 +407 30573 +407 30573 +407 30573 +407 30574 +407 30574 +407 30574 +406 30574 +406 30574 +406 30574 +406 30574 +406 30574 +406 30574 +406 30574 +406 30575 +405 30575 +405 30575 +405 30575 +405 30575 +405 30575 +405 30575 +405 30575 +405 30575 +405 30575 +404 30576 +404 30576 +404 30576 +404 30576 +404 30576 +404 30576 +404 30576 +404 30576 +403 30576 +403 30576 +403 30577 +403 30577 +403 30577 +403 30577 +403 30577 +403 30577 +402 30577 +402 30577 +402 30577 +402 30578 +402 30578 +402 30578 +402 30578 +402 30578 +401 30578 +401 30578 +401 30578 +401 30578 +401 30578 +401 30579 +401 30579 +401 30579 +401 30579 +400 30579 +400 30579 +400 30579 +400 30579 +400 30579 +400 30579 +400 30580 +400 30580 +399 30580 +399 30580 +399 30580 +399 30580 +399 30580 +399 30580 +399 30580 +399 30580 +398 30581 +398 30581 +398 30581 +398 30581 +398 30581 +398 30581 +398 30581 +398 30581 +397 30581 +397 30581 +397 30582 +397 30582 +397 30582 +397 30582 +397 30582 +397 30582 +396 30582 +396 30582 +396 30582 +396 30582 +396 30583 +396 30583 +396 30583 +396 30583 +395 30583 +395 30583 +395 30583 +395 30583 +395 30583 +395 30583 +395 30584 +395 30584 +394 30584 +394 30584 +394 30584 +394 30584 +394 30584 +394 30584 +394 30584 +394 30584 +393 30585 +393 30585 +393 30585 +393 30585 +393 30585 +393 30585 +393 30585 +393 30585 +392 30585 +392 30585 +392 30586 +392 30586 +392 30586 +392 30586 +392 30586 +392 30586 +391 30586 +391 30586 +391 30586 +391 30586 +391 30587 +391 30587 +391 30587 +391 30587 +390 30587 +390 30587 +390 30587 +390 30587 +390 30587 +390 30587 +390 30588 +389 30588 +389 30588 +389 30588 +389 30588 +389 30588 +389 30588 +389 30588 +389 30588 +388 30588 +388 30589 +388 30589 +388 30589 +388 30589 +388 30589 +388 30589 +388 30589 +387 30589 +387 30589 +387 30589 +387 30590 +387 30590 +387 30590 +387 30590 +386 30590 +386 30590 +386 30590 +386 30590 +386 30590 +386 30590 +386 30591 +386 30591 +385 30591 +385 30591 +385 30591 +385 30591 +385 30591 +385 30591 +385 30591 +385 30592 +384 30592 +384 30592 +384 30592 +384 30592 +384 30592 +384 30592 +384 30592 +383 30592 +383 30592 +383 30593 +383 30593 +383 30593 +383 30593 +383 30593 +383 30593 +382 30593 +382 30593 +382 30593 +382 30593 +382 30594 +382 30594 +382 30594 +381 30594 +381 30594 +381 30594 +381 30594 +381 30594 +381 30594 +381 30594 +381 30595 +380 30595 +380 30595 +380 30595 +380 30595 +380 30595 +380 30595 +380 30595 +379 30595 +379 30595 +379 30596 +379 30596 +379 30596 +379 30596 +379 30596 +378 30596 +378 30596 +378 30596 +378 30596 +378 30596 +378 30597 +378 30597 +378 30597 +377 30597 +377 30597 +377 30597 +377 30597 +377 30597 +377 30597 +377 30597 +376 30598 +376 30598 +376 30598 +376 30598 +376 30598 +376 30598 +376 30598 +375 30598 +375 30598 +375 30598 +375 30599 +375 30599 +375 30599 +375 30599 +374 30599 +374 30599 +374 30599 +374 30599 +374 30599 +374 30599 +374 30600 +373 30600 +373 30600 +373 30600 +373 30600 +373 30600 +373 30600 +373 30600 +372 30600 +372 30600 +372 30601 +372 30601 +372 30601 +372 30601 +372 30601 +371 30601 +371 30601 +371 30601 +371 30601 +371 30601 +371 30602 +371 30602 +370 30602 +370 30602 +370 30602 +370 30602 +370 30602 +370 30602 +370 30602 +369 30603 +369 30603 +369 30603 +369 30603 +369 30603 +369 30603 +369 30603 +368 30603 +368 30603 +368 30603 +368 30604 +368 30604 +368 30604 +368 30604 +367 30604 +367 30604 +367 30604 +367 30604 +367 30604 +367 30604 +367 30605 +366 30605 +366 30605 +366 30605 +366 30605 +366 30605 +366 30605 +366 30605 +365 30605 +365 30605 +365 30606 +365 30606 +365 30606 +365 30606 +365 30606 +364 30606 +364 30606 +364 30606 +364 30606 +364 30606 +364 30607 +364 30607 +363 30607 +363 30607 +363 30607 +363 30607 +363 30607 +363 30607 +363 30607 +362 30607 +362 30608 +362 30608 +362 30608 +362 30608 +362 30608 +362 30608 +361 30608 +361 30608 +361 30608 +361 30609 +361 30609 +361 30609 +361 30609 +360 30609 +360 30609 +360 30609 +360 30609 +360 30609 +360 30609 +360 30610 +359 30610 +359 30610 +359 30610 +359 30610 +359 30610 +359 30610 +359 30610 +358 30610 +358 30610 +358 30611 +358 30611 +358 30611 +358 30611 +358 30611 +357 30611 +357 30611 +357 30611 +357 30611 +357 30611 +357 30612 +357 30612 +356 30612 +356 30612 +356 30612 +356 30612 +356 30612 +356 30612 +356 30612 +355 30612 +355 30613 +355 30613 +355 30613 +355 30613 +355 30613 +355 30613 +354 30613 +354 30613 +354 30613 +354 30613 +354 30614 +354 30614 +354 30614 +354 30614 +353 30614 +353 30614 +353 30614 +353 30614 +353 30614 +353 30614 +353 30615 +352 30615 +352 30615 +352 30615 +352 30615 +352 30615 +352 30615 +352 30615 +351 30615 +351 30615 +351 30616 +351 30616 +351 30616 +351 30616 +351 30616 +350 30616 +350 30616 +350 30616 +350 30616 +350 30616 +350 30617 +350 30617 +350 30617 +349 30617 +349 30617 +349 30617 +349 30617 +349 30617 +349 30617 +349 30617 +348 30618 +348 30618 +348 30618 +348 30618 +348 30618 +348 30618 +348 30618 +347 30618 +347 30618 +347 30619 +347 30619 +347 30619 +347 30619 +347 30619 +346 30619 +346 30619 +346 30619 +346 30619 +346 30619 +346 30620 +346 30620 +345 30620 +345 30620 +345 30620 +345 30620 +345 30620 +345 30620 +345 30620 +344 30620 +344 30621 +344 30621 +344 30621 +344 30621 +344 30621 +344 30621 +343 30621 +343 30621 +343 30621 +343 30621 +343 30622 +343 30622 +343 30622 +342 30622 +342 30622 +342 30622 +342 30622 +342 30622 +342 30622 +342 30622 +341 30623 +341 30623 +341 30623 +341 30623 +341 30623 +341 30623 +341 30623 +340 30623 +340 30623 +340 30623 +340 30624 +340 30624 +340 30624 +339 30624 +339 30624 +339 30624 +339 30624 +339 30624 +339 30624 +339 30624 +338 30625 +338 30625 +338 30625 +338 30625 +338 30625 +338 30625 +338 30625 +337 30625 +337 30625 +337 30626 +337 30626 +337 30626 +337 30626 +337 30626 +336 30626 +336 30626 +336 30626 +336 30626 +336 30626 +336 30627 +336 30627 +335 30627 +335 30627 +335 30627 +335 30627 +335 30627 +335 30627 +335 30627 +334 30627 +334 30628 +334 30628 +334 30628 +334 30628 +334 30628 +334 30628 +333 30628 +333 30628 +333 30628 +333 30628 +333 30629 +333 30629 +333 30629 +332 30629 +332 30629 +332 30629 +332 30629 +332 30629 +332 30629 +332 30629 +331 30630 +331 30630 +331 30630 +331 30630 +331 30630 +331 30630 +331 30630 +330 30630 +330 30630 +330 30630 +330 30631 +330 30631 +330 30631 +330 30631 +329 30631 +329 30631 +329 30631 +329 30631 +329 30631 +329 30631 +328 30632 +328 30632 +328 30632 +328 30632 +328 30632 +328 30632 +328 30632 +327 30632 +327 30632 +327 30633 +327 30633 +327 30633 +327 30633 +327 30633 +326 30633 +326 30633 +326 30633 +326 30633 +326 30633 +326 30634 +326 30634 +325 30634 +325 30634 +325 30634 +325 30634 +325 30634 +325 30634 +325 30634 +324 30634 +324 30635 +324 30635 +324 30635 +324 30635 +324 30635 +324 30635 +323 30635 +323 30635 +323 30635 +323 30635 +323 30636 +323 30636 +323 30636 +322 30636 +322 30636 +322 30636 +322 30636 +322 30636 +322 30636 +322 30636 +322 30637 +321 30637 +321 30637 +321 30637 +321 30637 +321 30637 +321 30637 +321 30637 +320 30637 +320 30637 +320 30638 +320 30638 +320 30638 +320 30638 +320 30638 +319 30638 +319 30638 +319 30638 +319 30638 +319 30638 +319 30639 +319 30639 +318 30639 +318 30639 +318 30639 +318 30639 +318 30639 +318 30639 +318 30639 +317 30639 +317 30640 +317 30640 +317 30640 +317 30640 +317 30640 +317 30640 +316 30640 +316 30640 +316 30640 +316 30640 +316 30641 +316 30641 +316 30641 +315 30641 +315 30641 +315 30641 +315 30641 +315 30641 +315 30641 +315 30641 +314 30642 +314 30642 +314 30642 +314 30642 +314 30642 +314 30642 +314 30642 +314 30642 +313 30642 +313 30643 +313 30643 +313 30643 +313 30643 +313 30643 +313 30643 +312 30643 +312 30643 +312 30643 +312 30643 +312 30644 +312 30644 +312 30644 +311 30644 +311 30644 +311 30644 +311 30644 +311 30644 +311 30644 +311 30644 +310 30645 +310 30645 +310 30645 +310 30645 +310 30645 +310 30645 +310 30645 +310 30645 +309 30645 +309 30645 +309 30646 +309 30646 +309 30646 +309 30646 +309 30646 +309 30646 +308 30646 +308 30646 +308 30646 +308 30646 +308 30647 +308 30647 +308 30647 +308 30647 +308 30647 +307 30647 +307 30647 +307 30647 +307 30647 +307 30647 +307 30648 +307 30648 +307 30648 +307 30648 +307 30648 +306 30648 +306 30648 +306 30648 +306 30648 +306 30648 +306 30649 +306 30649 +306 30649 +306 30649 +306 30649 +305 30649 +305 30649 +305 30649 +305 30649 +305 30649 +305 30650 +305 30650 +305 30650 +305 30650 +305 30650 +304 30650 +304 30650 +304 30650 +304 30650 +304 30650 +304 30651 +304 30651 +304 30651 +304 30651 +304 30651 +303 30651 +303 30651 +303 30651 +303 30651 +303 30651 +303 30652 +303 30652 +303 30652 +303 30652 +302 30652 +302 30652 +302 30652 +302 30652 +302 30652 +302 30653 +302 30653 +302 30653 +302 30653 +301 30653 +301 30653 +301 30653 +301 30653 +301 30653 +301 30653 +301 30654 +301 30654 +301 30654 +300 30654 +300 30654 +300 30654 +300 30654 +300 30654 +300 30654 +300 30654 +300 30655 +300 30655 +299 30655 +299 30655 +299 30655 +299 30655 +299 30655 +299 30655 +299 30655 +299 30655 +299 30656 +298 30656 +298 30656 +298 30656 +298 30656 +298 30656 +298 30656 +298 30656 +298 30656 +298 30656 +297 30657 +297 30657 +297 30657 +297 30657 +297 30657 +297 30657 +297 30657 +297 30657 +296 30657 +296 30657 +296 30658 +296 30658 +296 30658 +296 30658 +296 30658 +296 30658 +296 30658 +295 30658 +295 30658 +295 30658 +295 30659 +295 30659 +295 30659 +295 30659 +295 30659 +295 30659 +294 30659 +294 30659 +294 30659 +294 30659 +294 30660 +294 30660 +294 30660 +294 30660 +294 30660 +293 30660 +293 30660 +293 30660 +293 30660 +293 30661 +293 30661 +293 30661 +293 30661 +293 30661 +293 30661 +292 30661 +292 30661 +292 30661 +292 30661 +292 30662 +292 30662 +292 30662 +292 30662 +292 30662 +291 30662 +291 30662 +291 30662 +291 30662 +291 30662 +291 30663 +291 30663 +291 30663 +291 30663 +291 30663 +290 30663 +290 30663 +290 30663 +290 30663 +290 30663 +290 30664 +290 30664 +290 30664 +290 30664 +290 30664 +289 30664 +289 30664 +289 30664 +289 30664 +289 30664 +289 30665 +289 30665 +289 30665 +289 30665 +289 30665 +288 30665 +288 30665 +288 30665 +288 30665 +288 30665 +288 30666 +288 30666 +288 30666 +288 30666 +287 30666 +287 30666 +287 30666 +287 30666 +287 30666 +287 30666 +287 30667 +287 30667 +287 30667 +287 30667 +286 30667 +286 30667 +286 30667 +286 30667 +286 30667 +286 30667 +286 30668 +286 30668 +286 30668 +286 30668 +285 30668 +285 30668 +285 30668 +285 30668 +285 30668 +285 30668 +285 30669 +285 30669 +285 30669 +285 30669 +284 30669 +284 30669 +284 30669 +284 30669 +284 30669 +284 30669 +284 30670 +284 30670 +284 30670 +284 30670 +283 30670 +283 30670 +283 30670 +283 30670 +283 30670 +283 30671 +283 30671 +283 30671 +283 30671 +283 30671 +283 30671 +282 30671 +282 30671 +282 30671 +282 30671 +282 30672 +282 30672 +282 30672 +282 30672 +282 30672 +282 30672 +281 30672 +281 30672 +281 30672 +281 30672 +281 30673 +281 30673 +281 30673 +281 30673 +281 30673 +280 30673 +280 30673 +280 30673 +280 30673 +280 30673 +280 30674 +280 30674 +280 30674 +280 30674 +280 30674 +279 30674 +279 30674 +279 30674 +279 30674 +279 30674 +279 30675 +279 30675 +279 30675 +279 30675 +278 30675 +278 30675 +278 30675 +278 30675 +278 30675 +278 30675 +278 30676 +278 30676 +278 30676 +278 30676 +278 30676 +277 30676 +277 30676 +277 30676 +277 30676 +277 30676 +277 30677 +277 30677 +277 30677 +277 30677 +277 30677 +277 30677 +276 30677 +276 30677 +276 30677 +276 30677 +276 30678 +276 30678 +276 30678 +276 30678 +276 30678 +276 30678 +276 30678 +276 30678 +275 30678 +275 30679 +275 30679 +275 30679 +275 30679 +275 30679 +275 30679 +275 30679 +275 30679 +275 30679 +275 30679 +275 30680 +274 30680 +274 30680 +274 30680 +274 30680 +274 30680 +274 30680 +274 30680 +274 30680 +274 30680 +274 30681 +274 30681 +273 30681 +273 30681 +273 30681 +273 30681 +273 30681 +273 30681 +273 30681 +273 30681 +273 30682 +273 30682 +273 30682 +273 30682 +272 30682 +272 30682 +272 30682 +272 30682 +272 30682 +272 30682 +272 30683 +272 30683 +272 30683 +272 30683 +272 30683 +271 30683 +271 30683 +271 30683 +271 30683 +271 30683 +271 30684 +271 30684 +271 30684 +271 30684 +271 30684 +271 30684 +271 30684 +270 30684 +270 30684 +270 30684 +270 30685 +270 30685 +270 30685 +270 30685 +270 30685 +270 30685 +270 30685 +270 30685 +270 30685 +270 30686 +269 30686 +269 30686 +269 30686 +269 30686 +269 30686 +269 30686 +269 30686 +269 30686 +269 30686 +269 30687 +269 30687 +269 30687 +269 30687 +268 30687 +268 30687 +268 30687 +268 30687 +268 30687 +268 30687 +268 30688 +268 30688 +268 30688 +268 30688 +268 30688 +268 30688 +268 30688 +267 30688 +267 30688 +267 30688 +267 30689 +267 30689 +267 30689 +267 30689 +267 30689 +267 30689 +267 30689 +267 30689 +267 30689 +267 30689 +267 30690 +266 30690 +266 30690 +266 30690 +266 30690 +266 30690 +266 30690 +266 30690 +266 30690 +266 30691 +266 30691 +266 30691 +266 30691 +266 30691 +266 30691 +265 30691 +265 30691 +265 30691 +265 30691 +265 30692 +265 30692 +265 30692 +265 30692 +265 30692 +265 30692 +265 30692 +265 30692 +265 30692 +265 30692 +264 30693 +264 30693 +264 30693 +264 30693 +264 30693 +264 30693 +264 30693 +264 30693 +264 30693 +264 30693 +264 30694 +264 30694 +264 30694 +264 30694 +264 30694 +263 30694 +263 30694 +263 30694 +263 30694 +263 30694 +263 30695 +263 30695 +263 30695 +263 30695 +263 30695 +263 30695 +263 30695 +263 30695 +263 30695 +262 30695 +262 30696 +262 30696 +262 30696 +262 30696 +262 30696 +262 30696 +262 30696 +262 30696 +262 30696 +262 30697 +262 30697 +262 30697 +262 30697 +262 30697 +261 30697 +261 30697 +261 30697 +261 30697 +261 30697 +261 30698 +261 30698 +261 30698 +261 30698 +261 30698 +261 30698 +261 30698 +261 30698 +261 30698 +261 30698 +261 30699 +260 30699 +260 30699 +260 30699 +260 30699 +260 30699 +260 30699 +260 30699 +260 30699 +260 30699 +260 30700 +260 30700 +260 30700 +260 30700 +260 30700 +260 30700 +260 30700 +259 30700 +259 30700 +259 30700 +259 30701 +259 30701 +259 30701 +259 30701 +259 30701 +259 30701 +259 30701 +259 30701 +259 30701 +259 30701 +259 30702 +259 30702 +259 30702 +258 30702 +258 30702 +258 30702 +258 30702 +258 30702 +258 30702 +258 30703 +258 30703 +258 30703 +258 30703 +258 30703 +258 30703 +258 30703 +258 30703 +258 30703 +258 30703 +257 30704 +257 30704 +257 30704 +257 30704 +257 30704 +257 30704 +257 30704 +257 30704 +257 30704 +257 30704 +257 30705 +257 30705 +257 30705 +257 30705 +257 30705 +257 30705 +256 30705 +256 30705 +256 30705 +256 30705 +256 30706 +256 30706 +256 30706 +256 30706 +256 30706 +256 30706 +256 30706 +256 30706 +256 30706 +256 30706 +256 30707 +256 30707 +255 30707 +255 30707 +255 30707 +255 30707 +255 30707 +255 30707 +255 30707 +255 30707 +255 30708 +255 30708 +255 30708 +255 30708 +255 30708 +255 30708 +255 30708 +254 30708 +254 30708 +254 30708 +254 30709 +254 30709 +254 30709 +254 30709 +254 30709 +254 30709 +254 30709 +254 30709 +254 30709 +254 30710 +254 30710 +254 30710 +254 30710 +253 30710 +253 30710 +253 30710 +253 30710 +253 30710 +253 30710 +253 30711 +253 30711 +253 30711 +253 30711 +253 30711 +253 30711 +253 30711 +253 30711 +253 30711 +253 30711 +252 30712 +252 30712 +252 30712 +252 30712 +252 30712 +252 30712 +252 30712 +252 30712 +252 30712 +252 30712 +252 30713 +252 30713 +252 30713 +252 30713 +252 30713 +252 30713 +252 30713 +252 30713 +251 30713 +251 30713 +251 30714 +251 30714 +251 30714 +251 30714 +251 30714 +251 30714 +251 30714 +251 30714 +251 30714 +251 30714 +251 30715 +251 30715 +251 30715 +251 30715 +251 30715 +251 30715 +250 30715 +250 30715 +250 30715 +250 30716 +250 30716 +250 30716 +250 30716 +250 30716 +250 30716 +250 30716 +250 30716 +250 30716 +250 30716 +250 30717 +250 30717 +250 30717 +250 30717 +249 30717 +249 30717 +249 30717 +249 30717 +249 30717 +249 30717 +249 30718 +249 30718 +249 30718 +249 30718 +249 30718 +249 30718 +249 30718 +249 30718 +249 30718 +249 30718 +249 30719 +249 30719 +248 30719 +248 30719 +248 30719 +248 30719 +248 30719 +248 30719 +248 30719 +248 30719 +248 30720 +248 30720 +248 30720 +248 30720 +248 30720 +248 30720 +248 30720 +248 30720 +248 30720 +248 30720 +247 30721 +247 30721 +247 30721 +247 30721 +247 30721 +247 30721 +247 30721 +247 30721 +247 30721 +247 30721 +247 30722 +247 30722 +247 30722 +247 30722 +247 30722 +247 30722 +247 30722 +247 30722 +247 30722 +247 30723 +246 30723 +246 30723 +246 30723 +246 30723 +246 30723 +246 30723 +246 30723 +246 30723 +246 30723 +246 30724 +246 30724 +246 30724 +246 30724 +246 30724 +246 30724 +246 30724 +246 30724 +246 30724 +246 30724 +245 30725 +245 30725 +245 30725 +245 30725 +245 30725 +245 30725 +245 30725 +245 30725 +245 30725 +245 30725 +245 30726 +245 30726 +245 30726 +245 30726 +245 30726 +245 30726 +245 30726 +245 30726 +245 30726 +245 30726 +244 30727 +244 30727 +244 30727 +244 30727 +244 30727 +244 30727 +244 30727 +244 30727 +244 30727 +244 30728 +244 30728 +244 30728 +244 30728 +244 30728 +244 30728 +244 30728 +244 30728 +244 30728 +243 30728 +243 30729 +243 30729 +243 30729 +243 30729 +243 30729 +243 30729 +243 30729 +243 30729 +243 30729 +243 30729 +243 30730 +243 30730 +243 30730 +243 30730 +243 30730 +243 30730 +243 30730 +242 30730 +242 30730 +242 30730 +242 30731 +242 30731 +242 30731 +242 30731 +242 30731 +242 30731 +242 30731 +242 30731 +242 30731 +242 30731 +242 30732 +242 30732 +242 30732 +242 30732 +242 30732 +242 30732 +241 30732 +241 30732 +241 30732 +241 30732 +241 30733 +241 30733 +241 30733 +241 30733 +241 30733 +241 30733 +241 30733 +241 30733 +241 30733 +241 30733 +241 30734 +241 30734 +241 30734 +241 30734 +240 30734 +240 30734 +240 30734 +240 30734 +240 30734 +240 30734 +240 30735 +240 30735 +240 30735 +240 30735 +240 30735 +240 30735 +240 30735 +240 30735 +240 30735 +240 30735 +240 30736 +240 30736 +239 30736 +239 30736 +239 30736 +239 30736 +239 30736 +239 30736 +239 30736 +239 30737 +239 30737 +239 30737 +239 30737 +239 30737 +239 30737 +239 30737 +239 30737 +239 30737 +239 30737 +239 30738 +238 30738 +238 30738 +238 30738 +238 30738 +238 30738 +238 30738 +238 30738 +238 30738 +238 30738 +238 30739 +238 30739 +238 30739 +238 30739 +238 30739 +238 30739 +238 30739 +238 30739 +238 30739 +238 30739 +237 30740 +237 30740 +237 30740 +237 30740 +237 30740 +237 30740 +237 30740 +237 30740 +237 30740 +237 30740 +237 30741 +237 30741 +237 30741 +237 30741 +237 30741 +237 30741 +237 30741 +237 30741 +237 30741 +236 30741 +236 30742 +236 30742 +236 30742 +236 30742 +236 30742 +236 30742 +236 30742 +236 30742 +236 30742 +236 30742 +236 30743 +236 30743 +236 30743 +236 30743 +236 30743 +236 30743 +236 30743 +235 30743 +235 30743 +235 30743 +235 30744 +235 30744 +235 30744 +235 30744 +235 30744 +235 30744 +235 30744 +235 30744 +235 30744 +235 30745 +235 30745 +235 30745 +235 30745 +235 30745 +234 30745 +234 30745 +234 30745 +234 30745 +234 30745 +234 30746 +234 30746 +234 30746 +234 30746 +234 30746 +234 30746 +234 30746 +234 30746 +234 30746 +234 30746 +234 30747 +234 30747 +234 30747 +233 30747 +233 30747 +233 30747 +233 30747 +233 30747 +233 30747 +233 30747 +233 30748 +233 30748 +233 30748 +233 30748 +233 30748 +233 30748 +233 30748 +233 30748 +233 30748 +233 30748 +232 30749 +232 30749 +232 30749 +232 30749 +232 30749 +232 30749 +232 30749 +232 30749 +232 30749 +232 30749 +232 30750 +232 30750 +232 30750 +232 30750 +232 30750 +232 30750 +232 30750 +231 30750 +231 30750 +231 30750 +231 30751 +231 30751 +231 30751 +231 30751 +231 30751 +231 30751 +231 30751 +231 30751 +231 30751 +231 30751 +231 30752 +231 30752 +231 30752 +230 30752 +230 30752 +230 30752 +230 30752 +230 30752 +230 30752 +230 30752 +230 30753 +230 30753 +230 30753 +230 30753 +230 30753 +230 30753 +230 30753 +230 30753 +229 30753 +229 30753 +229 30754 +229 30754 +229 30754 +229 30754 +229 30754 +229 30754 +229 30754 +229 30754 +229 30754 +229 30754 +229 30755 +229 30755 +229 30755 +228 30755 +228 30755 +228 30755 +228 30755 +228 30755 +228 30755 +228 30756 +228 30756 +228 30756 +228 30756 +228 30756 +228 30756 +228 30756 +228 30756 +228 30756 +227 30756 +227 30757 +227 30757 +227 30757 +227 30757 +227 30757 +227 30757 +227 30757 +227 30757 +227 30757 +227 30757 +227 30758 +227 30758 +227 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30759 +226 30759 +226 30759 +226 30759 +226 30759 +226 30759 +226 30759 +225 30759 +225 30759 +225 30759 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30761 +225 30761 +224 30761 +224 30761 +224 30761 +224 30761 +224 30761 +224 30761 +224 30761 +224 30761 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +223 30762 +223 30762 +223 30762 +223 30762 +223 30763 +223 30763 +223 30763 +223 30763 +223 30763 +223 30763 +223 30763 +223 30763 +223 30763 +223 30763 +223 30764 +223 30764 +222 30764 +222 30764 +222 30764 +222 30764 +222 30764 +222 30764 +222 30764 +222 30764 +222 30765 +222 30765 +222 30765 +222 30765 +222 30765 +222 30765 +222 30765 +222 30765 +222 30765 +222 30765 +222 30766 +221 30766 +221 30766 +221 30766 +221 30766 +221 30766 +221 30766 +221 30766 +221 30766 +221 30766 +221 30767 +221 30767 +221 30767 +221 30767 +221 30767 +221 30767 +221 30767 +221 30767 +221 30767 +220 30768 +220 30768 +220 30768 +220 30768 +220 30768 +220 30768 +220 30768 +220 30768 +220 30768 +220 30768 +220 30769 +220 30769 +220 30769 +220 30769 +220 30769 +220 30769 +219 30769 +219 30769 +219 30769 +219 30769 +219 30770 +219 30770 +219 30770 +219 30770 +219 30770 +219 30770 +219 30770 +219 30770 +219 30770 +219 30770 +219 30771 +218 30771 +218 30771 +218 30771 +218 30771 +218 30771 +218 30771 +218 30771 +218 30771 +218 30771 +218 30772 +218 30772 +218 30772 +218 30772 +218 30772 +218 30772 +217 30772 +217 30772 +217 30772 +217 30772 +217 30773 +217 30773 +217 30773 +217 30773 +217 30773 +217 30773 +217 30773 +217 30773 +217 30773 +217 30773 +217 30774 +217 30774 +216 30774 +216 30774 +216 30774 +216 30774 +216 30774 +216 30774 +216 30774 +216 30774 +216 30775 +216 30775 +216 30775 +216 30775 +216 30775 +216 30775 +216 30775 +216 30775 +215 30775 +215 30775 +215 30776 +215 30776 +215 30776 +215 30776 +215 30776 +215 30776 +215 30776 +215 30776 +215 30776 +215 30776 +215 30777 +215 30777 +215 30777 +214 30777 +214 30777 +214 30777 +214 30777 +214 30777 +214 30777 +214 30777 +214 30778 +214 30778 +214 30778 +214 30778 +214 30778 +214 30778 +214 30778 +213 30778 +213 30778 +213 30778 +213 30779 +213 30779 +213 30779 +213 30779 +213 30779 +213 30779 +213 30779 +213 30779 +213 30779 +213 30779 +213 30780 +213 30780 +212 30780 +212 30780 +212 30780 +212 30780 +212 30780 +212 30780 +212 30780 +212 30780 +212 30781 +212 30781 +212 30781 +212 30781 +212 30781 +212 30781 +211 30781 +211 30781 +211 30781 +211 30781 +211 30782 +211 30782 +211 30782 +211 30782 +211 30782 +211 30782 +211 30782 +211 30782 +211 30782 +211 30782 +210 30783 +210 30783 +210 30783 +210 30783 +210 30783 +210 30783 +210 30783 +210 30783 +210 30783 +210 30783 +210 30784 +210 30784 +210 30784 +210 30784 +209 30784 +209 30784 +209 30784 +209 30784 +209 30784 +209 30784 +209 30785 +209 30785 +209 30785 +209 30785 +209 30785 +209 30785 +209 30785 +209 30785 +208 30785 +208 30785 +208 30786 +208 30786 +208 30786 +208 30786 +208 30786 +208 30786 +208 30786 +208 30786 +208 30786 +208 30786 +208 30787 +208 30787 +207 30787 +207 30787 +207 30787 +207 30787 +207 30787 +207 30787 +207 30787 +207 30787 +207 30788 +207 30788 +207 30788 +207 30788 +207 30788 +207 30788 +206 30788 +206 30788 +206 30788 +206 30788 +206 30789 +206 30789 +206 30789 +206 30789 +206 30789 +206 30789 +206 30789 +206 30789 +206 30789 +205 30789 +205 30790 +205 30790 +205 30790 +205 30790 +205 30790 +205 30790 +205 30790 +205 30790 +205 30790 +205 30790 +205 30791 +205 30791 +205 30791 +204 30791 +204 30791 +204 30791 +204 30791 +204 30791 +204 30791 +204 30791 +204 30792 +204 30792 +204 30792 +204 30792 +204 30792 +204 30792 +203 30792 +203 30792 +203 30792 +203 30792 +203 30793 +203 30793 +203 30793 +203 30793 +203 30793 +203 30793 +203 30793 +203 30793 +203 30793 +203 30793 +202 30794 +202 30794 +202 30794 +202 30794 +202 30794 +202 30794 +202 30794 +202 30794 +202 30794 +202 30794 +202 30795 +202 30795 +202 30795 +201 30795 +201 30795 +201 30795 +201 30795 +201 30795 +201 30795 +201 30795 +201 30796 +201 30796 +201 30796 +201 30796 +201 30796 +200 30796 +200 30796 +200 30796 +200 30796 +200 30796 +200 30797 +200 30797 +200 30797 +200 30797 +200 30797 +200 30797 +200 30797 +200 30797 +199 30797 +199 30797 +199 30798 +199 30798 +199 30798 +199 30798 +199 30798 +199 30798 +199 30798 +199 30798 +199 30798 +199 30798 +199 30799 +198 30799 +198 30799 +198 30799 +198 30799 +198 30799 +198 30799 +198 30799 +198 30799 +198 30799 +198 30800 +198 30800 +198 30800 +198 30800 +197 30800 +197 30800 +197 30800 +197 30800 +197 30800 +197 30800 +197 30801 +197 30801 +197 30801 +197 30801 +197 30801 +197 30801 +196 30801 +196 30801 +196 30801 +196 30801 +196 30802 +196 30802 +196 30802 +196 30802 +196 30802 +196 30802 +196 30802 +196 30802 +196 30802 +195 30802 +195 30803 +195 30803 +195 30803 +195 30803 +195 30803 +195 30803 +195 30803 +195 30803 +195 30803 +195 30803 +195 30804 +194 30804 +194 30804 +194 30804 +194 30804 +194 30804 +194 30804 +194 30804 +194 30804 +194 30804 +194 30805 +194 30805 +194 30805 +193 30805 +193 30805 +193 30805 +193 30805 +193 30805 +193 30805 +193 30805 +193 30806 +193 30806 +193 30806 +193 30806 +193 30806 +192 30806 +192 30806 +192 30806 +192 30806 +192 30806 +192 30807 +192 30807 +192 30807 +192 30807 +192 30807 +192 30807 +192 30807 +192 30807 +191 30807 +191 30807 +191 30808 +191 30808 +191 30808 +191 30808 +191 30808 +191 30808 +191 30808 +191 30808 +191 30808 +191 30808 +190 30809 +190 30809 +190 30809 +190 30809 +190 30809 +190 30809 +190 30809 +190 30809 +190 30809 +190 30809 +190 30810 +190 30810 +190 30810 +190 30810 +189 30810 +189 30810 +189 30810 +189 30810 +189 30810 +189 30810 +189 30811 +189 30811 +189 30811 +189 30811 +189 30811 +189 30811 +188 30811 +188 30811 +188 30811 +188 30811 +188 30812 +188 30812 +188 30812 +188 30812 +188 30812 +188 30812 +188 30812 +188 30812 +188 30812 +187 30812 +187 30813 +187 30813 +187 30813 +187 30813 +187 30813 +187 30813 +187 30813 +187 30813 +187 30813 +187 30813 +187 30814 +187 30814 +187 30814 +186 30814 +186 30814 +186 30814 +186 30814 +186 30814 +186 30814 +186 30814 +186 30815 +186 30815 +186 30815 +186 30815 +186 30815 +186 30815 +185 30815 +185 30815 +185 30815 +185 30815 +185 30816 +185 30816 +185 30816 +185 30816 +185 30816 +185 30816 +185 30816 +185 30816 +185 30816 +185 30817 +185 30817 +184 30817 +184 30817 +184 30817 +184 30817 +184 30817 +184 30817 +184 30817 +184 30817 +184 30818 +184 30818 +184 30818 +184 30818 +184 30818 +184 30818 +184 30818 +183 30818 +183 30818 +183 30818 +183 30819 +183 30819 +183 30819 +183 30819 +183 30819 +183 30819 +183 30819 +183 30819 +183 30819 +183 30819 +183 30820 +183 30820 +182 30820 +182 30820 +182 30820 +182 30820 +182 30820 +182 30820 +182 30820 +182 30820 +182 30821 +182 30821 +182 30821 +182 30821 +182 30821 +182 30821 +182 30821 +182 30821 +182 30821 +182 30821 +182 30822 +181 30822 +181 30822 +181 30822 +181 30822 +181 30822 +181 30822 +181 30822 +181 30822 +181 30822 +181 30823 +181 30823 +181 30823 +181 30823 +181 30823 +181 30823 +181 30823 +181 30823 +181 30823 +181 30823 +180 30824 +180 30824 +180 30824 +180 30824 +180 30824 +180 30824 +180 30824 +180 30824 +180 30824 +180 30825 +180 30825 +180 30825 +180 30825 +180 30825 +180 30825 +180 30825 +180 30825 +180 30825 +180 30825 +180 30826 +180 30826 +180 30826 +179 30826 +179 30826 +179 30826 +179 30826 +179 30826 +179 30826 +179 30826 +179 30827 +179 30827 +179 30827 +179 30827 +179 30827 +179 30827 +179 30827 +179 30827 +179 30827 +179 30827 +179 30828 +179 30828 +179 30828 +179 30828 +179 30828 +179 30828 +179 30828 +179 30828 +179 30828 +179 30828 +179 30829 +178 30829 +178 30829 +178 30829 +178 30829 +178 30829 +178 30829 +178 30829 +178 30829 +178 30830 +178 30830 +178 30830 +178 30830 +178 30830 +178 30830 +178 30830 +178 30830 +178 30830 +178 30830 +178 30831 +178 30831 +178 30831 +178 30831 +178 30831 +178 30831 +178 30831 +178 30831 +178 30831 +178 30831 +178 30832 +178 30832 +178 30832 +178 30832 +178 30832 +178 30832 +177 30832 +177 30832 +177 30832 +177 30832 +177 30833 +177 30833 +177 30833 +177 30833 +177 30833 +177 30833 +177 30833 +177 30833 +177 30833 +177 30834 +177 30834 +177 30834 +177 30834 +177 30834 +177 30834 +177 30834 +177 30834 +177 30834 +177 30834 +177 30835 +177 30835 +177 30835 +177 30835 +177 30835 +177 30835 +177 30835 +177 30835 +177 30835 +177 30835 +177 30836 +177 30836 +177 30836 +177 30836 +177 30836 +177 30836 +177 30836 +177 30836 +177 30836 +177 30836 +177 30837 +177 30837 +177 30837 +177 30837 +177 30837 +177 30837 +177 30837 +177 30837 +177 30837 +177 30837 +177 30838 +177 30838 +177 30838 +177 30838 +177 30838 +177 30838 +177 30838 +177 30838 +177 30838 +177 30838 +177 30839 +177 30839 +177 30839 +177 30839 +177 30839 +177 30839 +177 30839 +177 30839 +177 30839 +177 30839 +177 30840 +177 30840 +177 30840 +177 30840 +177 30840 +177 30840 +177 30840 +177 30840 +177 30840 +177 30841 +177 30841 +177 30841 +177 30841 +177 30841 +177 30841 +177 30841 +177 30841 +177 30841 +177 30841 +177 30842 +177 30842 +177 30842 +177 30842 +177 30842 +177 30842 +177 30842 +177 30842 +177 30842 +177 30842 +177 30843 +177 30843 +177 30843 +177 30843 +177 30843 +177 30843 +177 30843 +177 30843 +177 30843 +177 30843 +177 30844 +177 30844 +177 30844 +177 30844 +177 30844 +177 30844 +177 30844 +177 30844 +177 30844 +177 30844 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30845 +177 30846 +177 30846 +177 30846 +177 30846 +178 30846 +178 30846 +178 30846 +178 30846 +178 30846 +178 30846 +178 30847 +178 30847 +178 30847 +178 30847 +178 30847 +178 30847 +178 30847 +178 30847 +178 30847 +178 30847 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30848 +178 30849 +178 30849 +178 30849 +178 30849 +178 30849 +178 30849 +178 30849 +178 30849 +179 30849 +179 30849 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30850 +179 30851 +179 30851 +179 30851 +179 30851 +179 30851 +179 30851 +179 30851 +179 30851 +179 30851 +179 30851 +179 30852 +179 30852 +179 30852 +179 30852 +179 30852 +179 30852 +180 30852 +180 30852 +180 30852 +180 30852 +180 30852 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30853 +180 30854 +180 30854 +180 30854 +180 30854 +180 30854 +180 30854 +180 30854 +180 30854 +180 30854 +181 30854 +181 30854 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30855 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30856 +181 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30857 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30858 +182 30859 +182 30859 +182 30859 +182 30859 +182 30859 +182 30859 +183 30859 +183 30859 +183 30859 +183 30859 +183 30859 +183 30859 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30860 +183 30861 +183 30861 +183 30861 +183 30861 +183 30861 +183 30861 +183 30861 +183 30861 +183 30861 +183 30861 +184 30861 +184 30861 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30862 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +184 30863 +185 30863 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30864 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30865 +185 30866 +185 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30866 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30867 +186 30868 +186 30868 +186 30868 +186 30868 +186 30868 +187 30868 +187 30868 +187 30868 +187 30868 +187 30868 +187 30868 +187 30868 +187 30868 +187 30868 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30869 +187 30870 +187 30870 +187 30870 +187 30870 +187 30870 +187 30870 +187 30870 +187 30870 +188 30870 +188 30870 +188 30870 +188 30870 +188 30870 +188 30870 +188 30870 +188 30870 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30871 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +188 30872 +189 30872 +189 30872 +189 30872 +189 30872 +189 30872 +189 30872 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30873 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +189 30874 +190 30874 +190 30874 +190 30874 +190 30874 +190 30874 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30875 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +190 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30876 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +191 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30877 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +192 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +193 30878 +194 30878 +194 30878 +194 30878 +194 30878 +194 30878 +194 30878 +194 30878 +194 30878 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +194 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +195 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +196 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +197 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +198 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +199 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +200 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +201 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +202 30879 +203 30879 +203 30879 +203 30879 +203 30879 +203 30879 +203 30879 +203 30879 +203 30879 +203 30879 +203 30879 +204 30879 +204 30879 +204 30879 +204 30879 +204 30879 +204 30879 +204 30879 +204 30879 +204 30879 +204 30879 +205 30879 +205 30879 +205 30879 +205 30879 +205 30878 +205 30878 +205 30878 +205 30878 +205 30878 +205 30878 +206 30878 +206 30878 +206 30878 +206 30878 +206 30878 +206 30878 +206 30878 +206 30878 +206 30878 +206 30878 +207 30878 +207 30878 +207 30878 +207 30878 +207 30878 +207 30878 +207 30878 +207 30878 +207 30878 +208 30878 +208 30878 +208 30878 +208 30878 +208 30878 +208 30878 +208 30878 +208 30878 +208 30878 +208 30878 +209 30878 +209 30878 +209 30878 +209 30878 +209 30878 +209 30878 +209 30878 +209 30878 +209 30878 +210 30878 +210 30878 +210 30878 +210 30878 +210 30878 +210 30877 +210 30877 +210 30877 +210 30877 +211 30877 +211 30877 +211 30877 +211 30877 +211 30877 +211 30877 +211 30877 +211 30877 +211 30877 +212 30877 +212 30877 +212 30877 +212 30877 +212 30877 +212 30877 +212 30877 +212 30877 +212 30877 +213 30877 +213 30877 +213 30877 +213 30877 +213 30877 +213 30877 +213 30877 +213 30877 +213 30877 +214 30877 +214 30877 +214 30876 +214 30876 +214 30876 +214 30876 +214 30876 +214 30876 +214 30876 +215 30876 +215 30876 +215 30876 +215 30876 +215 30876 +215 30876 +215 30876 +215 30876 +216 30876 +216 30876 +216 30876 +216 30876 +216 30876 +216 30876 +216 30876 +216 30876 +216 30876 +217 30876 +217 30876 +217 30876 +217 30876 +217 30875 +217 30875 +217 30875 +217 30875 +217 30875 +218 30875 +218 30875 +218 30875 +218 30875 +218 30875 +218 30875 +218 30875 +218 30875 +219 30875 +219 30875 +219 30875 +219 30875 +219 30875 +219 30875 +219 30875 +219 30875 +219 30875 +220 30875 +220 30875 +220 30874 +220 30874 +220 30874 +220 30874 +220 30874 +220 30874 +220 30874 +221 30874 +221 30874 +221 30874 +221 30874 +221 30874 +221 30874 +221 30874 +221 30874 +221 30874 +222 30874 +222 30874 +222 30874 +222 30874 +222 30874 +222 30873 +222 30873 +222 30873 +222 30873 +223 30873 +223 30873 +223 30873 +223 30873 +223 30873 +223 30873 +223 30873 +223 30873 +223 30873 +224 30873 +224 30873 +224 30873 +224 30873 +224 30873 +224 30873 +224 30873 +224 30872 +224 30872 +224 30872 +225 30872 +225 30872 +225 30872 +225 30872 +225 30872 +225 30872 +225 30872 +225 30872 +225 30872 +225 30872 +226 30872 +226 30872 +226 30872 +226 30872 +226 30872 +226 30871 +226 30871 +226 30871 +226 30871 +226 30871 +227 30871 +227 30871 +227 30871 +227 30871 +227 30871 +227 30871 +227 30871 +227 30871 +227 30871 +227 30871 +228 30871 +228 30871 +228 30870 +228 30870 +228 30870 +228 30870 +228 30870 +228 30870 +228 30870 +228 30870 +228 30870 +229 30870 +229 30870 +229 30870 +229 30870 +229 30870 +229 30870 +229 30870 +229 30869 +229 30869 +229 30869 +229 30869 +229 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30869 +230 30868 +230 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30868 +231 30867 +231 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30867 +232 30866 +232 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30866 +233 30865 +233 30865 +233 30865 +233 30865 +233 30865 +233 30865 +234 30865 +234 30865 +234 30865 +234 30865 +234 30865 +234 30865 +234 30865 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30864 +234 30863 +234 30863 +234 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30863 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30862 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30861 +235 30860 +235 30860 +235 30860 +235 30860 +235 30860 +235 30860 +235 30860 +235 30860 +236 30860 +236 30860 +236 30860 +236 30860 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30859 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30858 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30857 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30856 +236 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30855 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30854 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30853 +235 30852 +235 30852 +235 30852 +235 30852 +235 30852 +235 30852 +235 30852 +235 30852 +235 30852 +235 30852 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30851 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30850 +234 30849 +234 30849 +234 30849 +234 30849 +233 30849 +233 30849 +233 30849 +233 30849 +233 30849 +233 30849 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30848 +233 30847 +233 30847 +233 30847 +233 30847 +232 30847 +232 30847 +232 30847 +232 30847 +232 30847 +232 30847 +232 30846 +232 30846 +232 30846 +232 30846 +232 30846 +232 30846 +232 30846 +232 30846 +232 30846 +232 30846 +232 30845 +232 30845 +231 30845 +231 30845 +231 30845 +231 30845 +231 30845 +231 30845 +231 30845 +231 30845 +231 30844 +231 30844 +231 30844 +231 30844 +231 30844 +231 30844 +231 30844 +231 30844 +231 30844 +230 30844 +230 30844 +230 30843 +230 30843 +230 30843 +230 30843 +230 30843 +230 30843 +230 30843 +230 30843 +230 30843 +230 30843 +230 30842 +230 30842 +230 30842 +230 30842 +229 30842 +229 30842 +229 30842 +229 30842 +229 30842 +229 30842 +229 30841 +229 30841 +229 30841 +229 30841 +229 30841 +229 30841 +229 30841 +229 30841 +229 30841 +228 30841 +228 30840 +228 30840 +228 30840 +228 30840 +228 30840 +228 30840 +228 30840 +228 30840 +228 30840 +228 30840 +228 30839 +228 30839 +228 30839 +228 30839 +227 30839 +227 30839 +227 30839 +227 30839 +227 30839 +227 30839 +227 30838 +227 30838 +227 30838 +227 30838 +227 30838 +227 30838 +227 30838 +227 30838 +226 30838 +226 30837 +226 30837 +226 30837 +226 30837 +226 30837 +226 30837 +226 30837 +226 30837 +226 30837 +226 30837 +226 30836 +226 30836 +226 30836 +225 30836 +225 30836 +225 30836 +225 30836 +225 30836 +225 30836 +225 30836 +225 30835 +225 30835 +225 30835 +225 30835 +225 30835 +225 30835 +225 30835 +224 30835 +224 30835 +224 30835 +224 30834 +224 30834 +224 30834 +224 30834 +224 30834 +224 30834 +224 30834 +224 30834 +224 30834 +224 30834 +224 30833 +223 30833 +223 30833 +223 30833 +223 30833 +223 30833 +223 30833 +223 30833 +223 30833 +223 30832 +223 30832 +223 30832 +223 30832 +223 30832 +223 30832 +222 30832 +222 30832 +222 30832 +222 30832 +222 30831 +222 30831 +222 30831 +222 30831 +222 30831 +222 30831 +222 30831 +222 30831 +222 30831 +222 30831 +221 30830 +221 30830 +221 30830 +221 30830 +221 30830 +221 30830 +221 30830 +221 30830 +221 30830 +221 30830 +221 30829 +221 30829 +221 30829 +221 30829 +220 30829 +220 30829 +220 30829 +220 30829 +220 30829 +220 30828 +220 30828 +220 30828 +220 30828 +220 30828 +220 30828 +220 30828 +220 30828 +220 30828 +219 30828 +219 30827 +219 30827 +219 30827 +219 30827 +219 30827 +219 30827 +219 30827 +219 30827 +219 30827 +219 30827 +219 30826 +219 30826 +219 30826 +219 30826 +219 30826 +218 30826 +218 30826 +218 30826 +218 30826 +218 30826 +218 30825 +218 30825 +218 30825 +218 30825 +218 30825 +218 30825 +218 30825 +218 30825 +218 30825 +218 30824 +217 30824 +217 30824 +217 30824 +217 30824 +217 30824 +217 30824 +217 30824 +217 30824 +217 30824 +217 30823 +217 30823 +217 30823 +217 30823 +217 30823 +217 30823 +217 30823 +217 30823 +216 30823 +216 30823 +216 30822 +216 30822 +216 30822 +216 30822 +216 30822 +216 30822 +216 30822 +216 30822 +216 30822 +216 30822 +216 30821 +216 30821 +216 30821 +216 30821 +216 30821 +215 30821 +215 30821 +215 30821 +215 30821 +215 30821 +215 30820 +215 30820 +215 30820 +215 30820 +215 30820 +215 30820 +215 30820 +215 30820 +215 30820 +215 30820 +215 30819 +215 30819 +215 30819 +215 30819 +215 30819 +214 30819 +214 30819 +214 30819 +214 30819 +214 30818 +214 30818 +214 30818 +214 30818 +214 30818 +214 30818 +214 30818 +214 30818 +214 30818 +214 30818 +214 30817 +214 30817 +214 30817 +214 30817 +214 30817 +214 30817 +214 30817 +213 30817 +213 30817 +213 30817 +213 30816 +213 30816 +213 30816 +213 30816 +213 30816 +213 30816 +213 30816 +213 30816 +213 30816 +213 30816 +213 30815 +213 30815 +213 30815 +213 30815 +213 30815 +213 30815 +213 30815 +213 30815 +213 30815 +213 30815 +213 30814 +213 30814 +213 30814 +212 30814 +212 30814 +212 30814 +212 30814 +212 30814 +212 30814 +212 30814 +212 30813 +212 30813 +212 30813 +212 30813 +212 30813 +212 30813 +212 30813 +212 30813 +212 30813 +212 30812 +212 30812 +212 30812 +212 30812 +212 30812 +212 30812 +212 30812 +212 30812 +212 30812 +212 30812 +212 30811 +212 30811 +212 30811 +212 30811 +212 30811 +212 30811 +212 30811 +212 30811 +212 30811 +212 30811 +212 30810 +212 30810 +212 30810 +212 30810 +212 30810 +212 30810 +211 30810 +211 30810 +211 30810 +211 30810 +211 30809 +211 30809 +211 30809 +211 30809 +211 30809 +211 30809 +211 30809 +211 30809 +211 30809 +211 30809 +211 30808 +211 30808 +211 30808 +211 30808 +211 30808 +211 30808 +211 30808 +211 30808 +211 30808 +211 30808 +211 30807 +211 30807 +211 30807 +211 30807 +211 30807 +211 30807 +211 30807 +211 30807 +211 30807 +211 30807 +211 30806 +211 30806 +211 30806 +211 30806 +211 30806 +211 30806 +211 30806 +211 30806 +211 30806 +211 30806 +211 30805 +211 30805 +211 30805 +211 30805 +211 30805 +211 30805 +211 30805 +211 30805 +211 30805 +211 30805 +211 30804 +211 30804 +211 30804 +211 30804 +211 30804 +211 30804 +211 30804 +211 30804 +211 30804 +211 30803 +211 30803 +211 30803 +211 30803 +211 30803 +211 30803 +211 30803 +211 30803 +211 30803 +211 30803 +211 30802 +211 30802 +211 30802 +211 30802 +211 30802 +211 30802 +211 30802 +211 30802 +211 30802 +211 30802 +211 30801 +211 30801 +211 30801 +211 30801 +211 30801 +211 30801 +211 30801 +211 30801 +211 30801 +211 30801 +211 30800 +211 30800 +211 30800 +211 30800 +211 30800 +211 30800 +211 30800 +211 30800 +211 30800 +211 30800 +211 30799 +211 30799 +211 30799 +211 30799 +211 30799 +211 30799 +211 30799 +211 30799 +211 30799 +211 30799 +211 30798 +211 30798 +211 30798 +211 30798 +211 30798 +212 30798 +212 30798 +212 30798 +212 30798 +212 30798 +212 30797 +212 30797 +212 30797 +212 30797 +212 30797 +212 30797 +212 30797 +212 30797 +212 30797 +212 30797 +212 30796 +212 30796 +212 30796 +212 30796 +212 30796 +212 30796 +212 30796 +212 30796 +212 30796 +212 30796 +212 30795 +212 30795 +212 30795 +212 30795 +212 30795 +212 30795 +212 30795 +212 30795 +212 30795 +212 30795 +212 30794 +212 30794 +212 30794 +212 30794 +212 30794 +212 30794 +212 30794 +212 30794 +212 30794 +212 30793 +212 30793 +212 30793 +212 30793 +212 30793 +212 30793 +213 30793 +213 30793 +213 30793 +213 30793 +213 30792 +213 30792 +213 30792 +213 30792 +213 30792 +213 30792 +213 30792 +213 30792 +213 30792 +213 30792 +213 30791 +213 30791 +213 30791 +213 30791 +213 30791 +213 30791 +213 30791 +213 30791 +213 30791 +213 30791 +213 30790 +213 30790 +213 30790 +213 30790 +213 30790 +213 30790 +213 30790 +213 30790 +213 30790 +213 30790 +213 30789 +213 30789 +213 30789 +214 30789 +214 30789 +214 30789 +214 30789 +214 30789 +214 30789 +214 30789 +214 30788 +214 30788 +214 30788 +214 30788 +214 30788 +214 30788 +214 30788 +214 30788 +214 30788 +214 30788 +214 30787 +214 30787 +214 30787 +214 30787 +214 30787 +214 30787 +214 30787 +214 30787 +214 30787 +214 30787 +214 30786 +214 30786 +214 30786 +214 30786 +215 30786 +215 30786 +215 30786 +215 30786 +215 30786 +215 30785 +215 30785 +215 30785 +215 30785 +215 30785 +215 30785 +215 30785 +215 30785 +215 30785 +215 30785 +215 30784 +215 30784 +215 30784 +215 30784 +215 30784 +215 30784 +215 30784 +215 30784 +215 30784 +215 30784 +215 30783 +215 30783 +215 30783 +215 30783 +216 30783 +216 30783 +216 30783 +216 30783 +216 30783 +216 30783 +216 30782 +216 30782 +216 30782 +216 30782 +216 30782 +216 30782 +216 30782 +216 30782 +216 30782 +216 30782 +216 30781 +216 30781 +216 30781 +216 30781 +216 30781 +216 30781 +216 30781 +216 30781 +216 30781 +216 30781 +217 30780 +217 30780 +217 30780 +217 30780 +217 30780 +217 30780 +217 30780 +217 30780 +217 30780 +217 30780 +217 30779 +217 30779 +217 30779 +217 30779 +217 30779 +217 30779 +217 30779 +217 30779 +217 30779 +217 30779 +217 30778 +217 30778 +217 30778 +217 30778 +217 30778 +218 30778 +218 30778 +218 30778 +218 30778 +218 30778 +218 30777 +218 30777 +218 30777 +218 30777 +218 30777 +218 30777 +218 30777 +218 30777 +218 30777 +218 30777 +218 30776 +218 30776 +218 30776 +218 30776 +218 30776 +218 30776 +218 30776 +218 30776 +219 30776 +219 30776 +219 30775 +219 30775 +219 30775 +219 30775 +219 30775 +219 30775 +219 30775 +219 30775 +219 30775 +219 30774 +219 30774 +219 30774 +219 30774 +219 30774 +219 30774 +219 30774 +219 30774 +219 30774 +219 30774 +219 30773 +219 30773 +220 30773 +220 30773 +220 30773 +220 30773 +220 30773 +220 30773 +220 30773 +220 30773 +220 30772 +220 30772 +220 30772 +220 30772 +220 30772 +220 30772 +220 30772 +220 30772 +220 30772 +220 30772 +220 30771 +220 30771 +220 30771 +220 30771 +220 30771 +220 30771 +220 30771 +221 30771 +221 30771 +221 30771 +221 30770 +221 30770 +221 30770 +221 30770 +221 30770 +221 30770 +221 30770 +221 30770 +221 30770 +221 30770 +221 30769 +221 30769 +221 30769 +221 30769 +221 30769 +221 30769 +221 30769 +221 30769 +221 30769 +221 30769 +221 30768 +221 30768 +222 30768 +222 30768 +222 30768 +222 30768 +222 30768 +222 30768 +222 30768 +222 30768 +222 30767 +222 30767 +222 30767 +222 30767 +222 30767 +222 30767 +222 30767 +222 30767 +222 30767 +222 30767 +222 30766 +222 30766 +222 30766 +222 30766 +222 30766 +223 30766 +223 30766 +223 30766 +223 30766 +223 30766 +223 30765 +223 30765 +223 30765 +223 30765 +223 30765 +223 30765 +223 30765 +223 30765 +223 30765 +223 30765 +223 30764 +223 30764 +223 30764 +223 30764 +223 30764 +223 30764 +223 30764 +224 30764 +224 30764 +224 30764 +224 30763 +224 30763 +224 30763 +224 30763 +224 30763 +224 30763 +224 30763 +224 30763 +224 30763 +224 30763 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30762 +224 30761 +224 30761 +225 30761 +225 30761 +225 30761 +225 30761 +225 30761 +225 30761 +225 30761 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30760 +225 30759 +225 30759 +225 30759 +225 30759 +225 30759 +225 30759 +226 30759 +226 30759 +226 30759 +226 30759 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30758 +226 30757 +226 30757 +226 30757 +226 30757 +226 30757 +226 30757 +226 30757 +226 30757 +226 30757 +226 30757 +226 30756 +226 30756 +227 30756 +227 30756 +227 30756 +227 30756 +227 30756 +227 30756 +227 30756 +227 30756 +227 30755 +227 30755 +227 30755 +227 30755 +227 30755 +227 30755 +227 30755 +227 30755 +227 30755 +227 30755 +227 30754 +227 30754 +227 30754 +227 30754 +227 30754 +227 30754 +227 30754 +227 30754 +227 30754 +228 30754 +228 30753 +228 30753 +228 30753 +228 30753 +228 30753 +228 30753 +228 30753 +228 30753 +228 30753 +228 30753 +228 30752 +228 30752 +228 30752 +228 30752 +228 30752 +228 30752 +228 30752 +228 30752 +228 30752 +228 30752 +228 30751 +228 30751 +228 30751 +228 30751 +228 30751 +228 30751 +228 30751 +228 30751 +228 30751 +229 30750 +229 30750 +229 30750 +229 30750 +229 30750 +229 30750 +229 30750 +229 30750 +229 30750 +229 30750 +229 30749 +229 30749 +229 30749 +229 30749 +229 30749 +229 30749 +229 30749 +229 30749 +229 30749 +229 30749 +229 30748 +229 30748 +229 30748 +229 30748 +229 30748 +229 30748 +229 30748 +229 30748 +229 30748 +229 30748 +229 30747 +229 30747 +229 30747 +229 30747 +229 30747 +229 30747 +229 30747 +229 30747 +229 30747 +229 30747 +229 30746 +230 30746 +230 30746 +230 30746 +230 30746 +230 30746 +230 30746 +230 30746 +230 30746 +230 30746 +230 30745 +230 30745 +230 30745 +230 30745 +230 30745 +230 30745 +230 30745 +230 30745 +230 30745 +230 30745 +230 30744 +230 30744 +230 30744 +230 30744 +230 30744 +230 30744 +230 30744 +230 30744 +230 30744 +230 30744 +230 30743 +230 30743 +230 30743 +230 30743 +230 30743 +230 30743 +230 30743 +230 30743 +230 30743 +230 30743 +230 30742 +230 30742 +230 30742 +230 30742 +230 30742 +230 30742 +230 30742 +230 30742 +230 30742 +230 30742 +230 30741 +231 30741 +231 30741 +231 30741 +231 30741 +231 30741 +231 30741 +231 30741 +231 30741 +231 30741 +231 30740 +231 30740 +231 30740 +231 30740 +231 30740 +231 30740 +231 30740 +231 30740 +231 30740 +231 30740 +231 30739 +231 30739 +231 30739 +231 30739 +231 30739 +231 30739 +231 30739 +231 30739 +231 30739 +231 30739 +231 30738 +231 30738 +231 30738 +231 30738 +231 30738 +231 30738 +231 30738 +231 30738 +231 30738 +231 30737 +231 30737 +231 30737 +231 30737 +232 30737 +232 30737 +232 30737 +232 30737 +232 30737 +232 30737 +232 30736 +232 30736 +232 30736 +232 30736 +232 30736 +232 30736 +232 30736 +232 30736 +232 30736 +232 30736 +232 30735 +232 30735 +232 30735 +232 30735 +232 30735 +232 30735 +232 30735 +232 30735 +232 30735 +232 30735 +232 30734 +232 30734 +232 30734 +232 30734 +232 30734 +232 30734 +232 30734 +232 30734 +232 30734 +232 30734 +233 30733 +233 30733 +233 30733 +233 30733 +233 30733 +233 30733 +233 30733 +233 30733 +233 30733 +233 30733 +233 30732 +233 30732 +233 30732 +233 30732 +233 30732 +233 30732 +233 30732 +233 30732 +233 30732 +233 30732 +233 30731 +233 30731 +233 30731 +233 30731 +233 30731 +233 30731 +233 30731 +233 30731 +233 30731 +233 30731 +233 30730 +233 30730 +233 30730 +233 30730 +234 30730 +234 30730 +234 30730 +234 30730 +234 30730 +234 30730 +234 30729 +234 30729 +234 30729 +234 30729 +234 30729 +234 30729 +234 30729 +234 30729 +234 30729 +234 30728 +234 30728 +234 30728 +234 30728 +234 30728 +234 30728 +234 30728 +234 30728 +234 30728 +234 30728 +234 30727 +234 30727 +235 30727 +235 30727 +235 30727 +235 30727 +235 30727 +235 30727 +235 30727 +235 30727 +235 30726 +235 30726 +235 30726 +235 30726 +235 30726 +235 30726 +235 30726 +235 30726 +235 30726 +235 30726 +235 30725 +235 30725 +235 30725 +235 30725 +235 30725 +236 30725 +236 30725 +236 30725 +236 30725 +236 30725 +236 30724 +236 30724 +236 30724 +236 30724 +236 30724 +236 30724 +236 30724 +236 30724 +236 30724 +236 30724 +236 30723 +236 30723 +236 30723 +236 30723 +236 30723 +236 30723 +236 30723 +236 30723 +236 30723 +236 30723 +237 30722 +237 30722 +237 30722 +237 30722 +237 30722 +237 30722 +237 30722 +237 30722 +237 30722 +237 30722 +237 30721 +237 30721 +237 30721 +237 30721 +237 30721 +237 30721 +237 30721 +237 30721 +237 30721 +237 30721 +237 30720 +237 30720 +237 30720 +238 30720 +238 30720 +238 30720 +238 30720 +238 30720 +238 30720 +238 30719 +238 30719 +238 30719 +238 30719 +238 30719 +238 30719 +238 30719 +238 30719 +238 30719 +238 30719 +238 30718 +238 30718 +238 30718 +238 30718 +239 30718 +239 30718 +239 30718 +239 30718 +239 30718 +239 30718 +239 30717 +239 30717 +239 30717 +239 30717 +239 30717 +239 30717 +239 30717 +239 30717 +239 30717 +239 30717 +239 30716 +239 30716 +239 30716 +240 30716 +240 30716 +240 30716 +240 30716 +240 30716 +240 30716 +240 30716 +240 30715 +240 30715 +240 30715 +240 30715 +240 30715 +240 30715 +240 30715 +240 30715 +240 30715 +240 30715 +240 30714 +240 30714 +240 30714 +241 30714 +241 30714 +241 30714 +241 30714 +241 30714 +241 30714 +241 30714 +241 30713 +241 30713 +241 30713 +241 30713 +241 30713 +241 30713 +241 30713 +241 30713 +241 30713 +241 30713 +242 30712 +242 30712 +242 30712 +242 30712 +242 30712 +242 30712 +242 30712 +242 30712 +242 30712 +242 30711 +242 30711 +242 30711 +242 30711 +242 30711 +242 30711 +242 30711 +242 30711 +243 30711 +243 30711 +243 30710 +243 30710 +243 30710 +243 30710 +243 30710 +243 30710 +243 30710 +243 30710 +243 30710 +243 30710 +243 30709 +243 30709 +243 30709 +243 30709 +243 30709 +244 30709 +244 30709 +244 30709 +244 30709 +244 30709 +244 30708 +244 30708 +244 30708 +244 30708 +244 30708 +244 30708 +244 30708 +244 30708 +244 30708 +244 30708 +244 30707 +244 30707 +245 30707 +245 30707 +245 30707 +245 30707 +245 30707 +245 30707 +245 30707 +245 30707 +245 30706 +245 30706 +245 30706 +245 30706 +245 30706 +245 30706 +246 30706 +246 30706 +246 30706 +246 30706 +246 30705 +246 30705 +246 30705 +246 30705 +246 30705 +246 30705 +246 30705 +246 30705 +246 30705 +246 30705 +247 30704 +247 30704 +247 30704 +247 30704 +247 30704 +247 30704 +247 30704 +247 30704 +247 30704 +247 30703 +247 30703 +247 30703 +247 30703 +247 30703 +248 30703 +248 30703 +248 30703 +248 30703 +248 30703 +248 30702 +248 30702 +248 30702 +248 30702 +248 30702 +248 30702 +248 30702 +248 30702 +249 30702 +249 30702 +249 30701 +249 30701 +249 30701 +249 30701 +249 30701 +249 30701 +249 30701 +249 30701 +249 30701 +249 30701 +249 30700 +249 30700 +250 30700 +250 30700 +250 30700 +250 30700 +250 30700 +250 30700 +250 30700 +250 30700 +250 30699 +250 30699 +250 30699 +250 30699 +250 30699 +251 30699 +251 30699 +251 30699 +251 30699 +251 30699 +251 30698 +251 30698 +251 30698 +251 30698 +251 30698 +251 30698 +251 30698 +251 30698 +252 30698 +252 30698 +252 30697 +252 30697 +252 30697 +252 30697 +252 30697 +252 30697 +252 30697 +252 30697 +252 30697 +252 30697 +252 30696 +253 30696 +253 30696 +253 30696 +253 30696 +253 30696 +253 30696 +253 30696 +253 30696 +253 30695 +253 30695 +253 30695 +253 30695 +254 30695 +254 30695 +254 30695 +254 30695 +254 30695 +254 30695 +254 30694 +254 30694 +254 30694 +254 30694 +254 30694 +254 30694 +255 30694 +255 30694 +255 30694 +255 30694 +255 30693 +255 30693 +255 30693 +255 30693 +255 30693 +255 30693 +255 30693 +255 30693 +256 30693 +256 30693 +256 30692 +256 30692 +256 30692 +256 30692 +256 30692 +256 30692 +256 30692 +256 30692 +256 30692 +257 30692 +257 30691 +257 30691 +257 30691 +257 30691 +257 30691 +257 30691 +257 30691 +257 30691 +257 30691 +257 30691 +258 30690 +258 30690 +258 30690 +258 30690 +258 30690 +258 30690 +258 30690 +258 30690 +258 30690 +258 30690 +258 30689 +259 30689 +259 30689 +259 30689 +259 30689 +259 30689 +259 30689 +259 30689 +259 30689 +259 30689 +259 30688 +259 30688 +259 30688 +260 30688 +260 30688 +260 30688 +260 30688 +260 30688 +260 30688 +260 30687 +260 30687 +260 30687 +260 30687 +260 30687 +261 30687 +261 30687 +261 30687 +261 30687 +261 30687 +261 30686 +261 30686 +261 30686 +261 30686 +261 30686 +261 30686 +262 30686 +262 30686 +262 30686 +262 30686 +262 30685 +262 30685 +262 30685 +262 30685 +262 30685 +262 30685 +262 30685 +263 30685 +263 30685 +263 30685 +263 30684 +263 30684 +263 30684 +263 30684 +263 30684 +263 30684 +263 30684 +263 30684 +264 30684 +264 30684 +264 30683 +264 30683 +264 30683 +264 30683 +264 30683 +264 30683 +264 30683 +264 30683 +264 30683 +265 30683 +265 30682 +265 30682 +265 30682 +265 30682 +265 30682 +265 30682 +265 30682 +265 30682 +265 30682 +265 30682 +266 30681 +266 30681 +266 30681 +266 30681 +266 30681 +266 30681 +266 30681 +266 30681 +266 30681 +266 30681 +267 30680 +267 30680 +267 30680 +267 30680 +267 30680 +267 30680 +267 30680 +267 30680 +267 30680 +267 30680 +268 30679 +268 30679 +268 30679 +268 30679 +268 30679 +268 30679 +268 30679 +268 30679 +268 30679 +268 30678 +269 30678 +269 30678 +269 30678 +269 30678 +269 30678 +269 30678 +269 30678 +269 30678 +269 30678 +269 30677 +270 30677 +270 30677 +270 30677 +270 30677 +270 30677 +270 30677 +270 30677 +270 30677 +270 30677 +270 30676 +270 30676 +271 30676 +271 30676 +271 30676 +271 30676 +271 30676 +271 30676 +271 30676 +271 30676 +271 30675 +271 30675 +272 30675 +272 30675 +272 30675 +272 30675 +272 30675 +272 30675 +272 30675 +272 30675 +272 30674 +273 30674 +273 30674 +273 30674 +273 30674 +273 30674 +273 30674 +273 30674 +273 30674 +273 30674 +273 30673 +274 30673 +274 30673 +274 30673 +274 30673 +274 30673 +274 30673 +274 30673 +274 30673 +274 30673 +275 30672 +275 30672 +275 30672 +275 30672 +275 30672 +275 30672 +275 30672 +275 30672 +275 30672 +276 30672 +276 30671 +276 30671 +276 30671 +276 30671 +276 30671 +276 30671 +276 30671 +276 30671 +277 30671 +277 30671 +277 30670 +277 30670 +277 30670 +277 30670 +277 30670 +277 30670 +277 30670 +277 30670 +278 30670 +278 30670 +278 30669 +278 30669 +278 30669 +278 30669 +278 30669 +278 30669 +278 30669 +278 30669 +279 30669 +279 30668 +279 30668 +279 30668 +279 30668 +279 30668 +279 30668 +279 30668 +279 30668 +280 30668 +280 30668 +280 30667 +280 30667 +280 30667 +280 30667 +280 30667 +280 30667 +280 30667 +280 30667 +281 30667 +281 30667 +281 30666 +281 30666 +281 30666 +281 30666 +281 30666 +281 30666 +281 30666 +282 30666 +282 30666 +282 30666 +282 30665 +282 30665 +282 30665 +282 30665 +282 30665 +282 30665 +283 30665 +283 30665 +283 30665 +283 30665 +283 30664 +283 30664 +283 30664 +283 30664 +283 30664 +284 30664 +284 30664 +284 30664 +284 30664 +284 30664 +284 30663 +284 30663 +284 30663 +284 30663 +284 30663 +285 30663 +285 30663 +285 30663 +285 30663 +285 30663 +285 30662 +285 30662 +285 30662 +285 30662 +286 30662 +286 30662 +286 30662 +286 30662 +286 30662 +286 30662 +286 30661 +286 30661 +286 30661 +286 30661 +287 30661 +287 30661 +287 30661 +287 30661 +287 30661 +287 30661 +287 30660 +287 30660 +287 30660 +287 30660 +288 30660 +288 30660 +288 30660 +288 30660 +288 30660 +288 30660 +288 30659 +288 30659 +288 30659 +289 30659 +289 30659 +289 30659 +289 30659 +289 30659 +289 30659 +289 30659 +289 30658 +289 30658 +290 30658 +290 30658 +290 30658 +290 30658 +290 30658 +290 30658 +290 30658 +290 30658 +290 30657 +291 30657 +291 30657 +291 30657 +291 30657 +291 30657 +291 30657 +291 30657 +291 30657 +291 30657 +292 30656 +292 30656 +292 30656 +292 30656 +292 30656 +292 30656 +292 30656 +292 30656 +292 30656 +293 30655 +293 30655 +293 30655 +293 30655 +293 30655 +293 30655 +293 30655 +293 30655 +294 30655 +294 30655 +294 30654 +294 30654 +294 30654 +294 30654 +294 30654 +294 30654 +294 30654 +295 30654 +295 30654 +295 30654 +295 30653 +295 30653 +295 30653 +295 30653 +295 30653 +296 30653 +296 30653 +296 30653 +296 30653 +296 30653 +296 30652 +296 30652 +296 30652 +297 30652 +297 30652 +297 30652 +297 30652 +297 30652 +297 30652 +297 30652 +297 30651 +297 30651 +298 30651 +298 30651 +298 30651 +298 30651 +298 30651 +298 30651 +298 30651 +298 30651 +299 30650 +299 30650 +299 30650 +299 30650 +299 30650 +299 30650 +299 30650 +299 30650 +299 30650 +300 30650 +300 30649 +300 30649 +300 30649 +300 30649 +300 30649 +300 30649 +300 30649 +301 30649 +301 30649 +301 30649 +301 30648 +301 30648 +301 30648 +301 30648 +301 30648 +301 30648 +302 30648 +302 30648 +302 30648 +302 30648 +302 30647 +302 30647 +302 30647 +302 30647 +303 30647 +303 30647 +303 30647 +303 30647 +303 30647 +303 30647 +303 30646 +303 30646 +303 30646 +304 30646 +304 30646 +304 30646 +304 30646 +304 30646 +304 30646 +304 30646 +304 30645 +305 30645 +305 30645 +305 30645 +305 30645 +305 30645 +305 30645 +305 30645 +305 30645 +306 30645 +306 30644 +306 30644 +306 30644 +306 30644 +306 30644 +306 30644 +306 30644 +306 30644 +307 30644 +307 30644 +307 30643 +307 30643 +307 30643 +307 30643 +307 30643 +308 30643 +308 30643 +308 30643 +308 30643 +308 30642 +308 30642 +308 30642 +308 30642 +309 30642 +309 30642 +309 30642 +309 30642 +309 30642 +309 30642 +309 30641 +309 30641 +309 30641 +310 30641 +310 30641 +310 30641 +310 30641 +310 30641 +310 30641 +310 30641 +310 30640 +311 30640 +311 30640 +311 30640 +311 30640 +311 30640 +311 30640 +311 30640 +311 30640 +312 30640 +312 30639 +312 30639 +312 30639 +312 30639 +312 30639 +312 30639 +312 30639 +313 30639 +313 30639 +313 30639 +313 30638 +313 30638 +313 30638 +313 30638 +313 30638 +314 30638 +314 30638 +314 30638 +314 30638 +314 30638 +314 30637 +314 30637 +314 30637 +314 30637 +315 30637 +315 30637 +315 30637 +315 30637 +315 30637 +315 30637 +315 30636 +315 30636 +316 30636 +316 30636 +316 30636 +316 30636 +316 30636 +316 30636 +316 30636 +316 30636 +316 30635 +317 30635 +317 30635 +317 30635 +317 30635 +317 30635 +317 30635 +317 30635 +317 30635 +317 30635 +318 30634 +318 30634 +318 30634 +318 30634 +318 30634 +318 30634 +318 30634 +318 30634 +319 30634 +319 30634 +319 30633 +319 30633 +319 30633 +319 30633 +319 30633 +319 30633 +320 30633 +320 30633 +320 30633 +320 30632 +320 30632 +320 30632 +320 30632 +320 30632 +321 30632 +321 30632 +321 30632 +321 30632 +321 30632 +321 30631 +321 30631 +321 30631 +322 30631 +322 30631 +322 30631 +322 30631 +322 30631 +322 30631 +322 30631 +322 30630 +323 30630 +323 30630 +323 30630 +323 30630 +323 30630 +323 30630 +323 30630 +323 30630 +323 30630 +324 30629 +324 30629 +324 30629 +324 30629 +324 30629 +324 30629 +324 30629 +324 30629 +325 30629 +325 30629 +325 30628 +325 30628 +325 30628 +325 30628 +325 30628 +325 30628 +326 30628 +326 30628 +326 30628 +326 30628 +326 30627 +326 30627 +326 30627 +326 30627 +327 30627 +327 30627 +327 30627 +327 30627 +327 30627 +327 30627 +327 30626 +327 30626 +328 30626 +328 30626 +328 30626 +328 30626 +328 30626 +328 30626 +328 30626 +328 30626 +329 30625 +329 30625 +329 30625 +329 30625 +329 30625 +329 30625 +329 30625 +329 30625 +330 30625 +330 30624 +330 30624 +330 30624 +330 30624 +330 30624 +330 30624 +331 30624 +331 30624 +331 30624 +331 30624 +331 30623 +331 30623 +331 30623 +331 30623 +332 30623 +332 30623 +332 30623 +332 30623 +332 30623 +332 30623 +332 30622 +332 30622 +333 30622 +333 30622 +333 30622 +333 30622 +333 30622 +333 30622 +333 30622 +333 30622 +334 30621 +334 30621 +334 30621 +334 30621 +334 30621 +334 30621 +334 30621 +334 30621 +335 30621 +335 30621 +335 30620 +335 30620 +335 30620 +335 30620 +335 30620 +335 30620 +336 30620 +336 30620 +336 30620 +336 30620 +336 30619 +336 30619 +336 30619 +337 30619 +337 30619 +337 30619 +337 30619 +337 30619 +337 30619 +337 30619 +337 30618 +338 30618 +338 30618 +338 30618 +338 30618 +338 30618 +338 30618 +338 30618 +338 30618 +339 30617 +339 30617 +339 30617 +339 30617 +339 30617 +339 30617 +339 30617 +340 30617 +340 30617 +340 30617 +340 30616 +340 30616 +340 30616 +340 30616 +340 30616 +341 30616 +341 30616 +341 30616 +341 30616 +341 30616 +341 30615 +341 30615 +342 30615 +342 30615 +342 30615 +342 30615 +342 30615 +342 30615 +342 30615 +342 30615 +343 30614 +343 30614 +343 30614 +343 30614 +343 30614 +343 30614 +343 30614 +343 30614 +344 30614 +344 30614 +344 30613 +344 30613 +344 30613 +344 30613 +344 30613 +344 30613 +345 30613 +345 30613 +345 30613 +345 30613 +345 30612 +345 30612 +345 30612 +346 30612 +346 30612 +346 30612 +346 30612 +346 30612 +346 30612 +346 30612 +346 30611 +347 30611 +347 30611 +347 30611 +347 30611 +347 30611 +347 30611 +347 30611 +347 30611 +348 30611 +348 30610 +348 30610 +348 30610 +348 30610 +348 30610 +348 30610 +348 30610 +349 30610 +349 30610 +349 30610 +349 30609 +349 30609 +349 30609 +349 30609 +350 30609 +350 30609 +350 30609 +350 30609 +350 30609 +350 30609 +350 30608 +350 30608 +351 30608 +351 30608 +351 30608 +351 30608 +351 30608 +351 30608 +351 30608 +351 30608 +352 30607 +352 30607 +352 30607 +352 30607 +352 30607 +352 30607 +352 30607 +353 30607 +353 30607 +353 30607 +353 30606 +353 30606 +353 30606 +353 30606 +353 30606 +354 30606 +354 30606 +354 30606 +354 30606 +354 30605 +354 30605 +354 30605 +354 30605 +355 30605 +355 30605 +355 30605 +355 30605 +355 30605 +355 30605 +355 30604 +356 30604 +356 30604 +356 30604 +356 30604 +356 30604 +356 30604 +356 30604 +356 30604 +357 30604 +357 30603 +357 30603 +357 30603 +357 30603 +357 30603 +357 30603 +357 30603 +358 30603 +358 30603 +358 30603 +358 30602 +358 30602 +358 30602 +358 30602 +358 30602 +359 30602 +359 30602 +359 30602 +359 30602 +359 30602 +359 30601 +359 30601 +359 30601 +360 30601 +360 30601 +360 30601 +360 30601 +360 30601 +360 30601 +360 30601 +360 30600 +361 30600 +361 30600 +361 30600 +361 30600 +361 30600 +361 30600 +361 30600 +361 30600 +362 30600 +362 30599 +362 30599 +362 30599 +362 30599 +362 30599 +362 30599 +362 30599 +363 30599 +363 30599 +363 30598 +363 30598 +363 30598 +363 30598 +363 30598 +363 30598 +364 30598 +364 30598 +364 30598 +364 30598 +364 30597 +364 30597 +364 30597 +364 30597 +365 30597 +365 30597 +365 30597 +365 30597 +365 30597 +365 30597 +365 30596 +365 30596 +366 30596 +366 30596 +366 30596 +366 30596 +366 30596 +366 30596 +366 30596 +367 30596 +367 30595 +367 30595 +367 30595 +367 30595 +367 30595 +367 30595 +367 30595 +368 30595 +368 30595 +368 30595 +368 30594 +368 30594 +368 30594 +368 30594 +368 30594 +369 30594 +369 30594 +369 30594 +369 30594 +369 30594 +369 30593 +369 30593 +369 30593 +370 30593 +370 30593 +370 30593 +370 30593 +370 30593 +370 30593 +370 30593 +371 30592 +371 30592 +371 30592 +371 30592 +371 30592 +371 30592 +371 30592 +371 30592 +372 30592 +372 30591 +372 30591 +372 30591 +372 30591 +372 30591 +372 30591 +372 30591 +373 30591 +373 30591 +373 30591 +373 30590 +373 30590 +373 30590 +373 30590 +373 30590 +374 30590 +374 30590 +374 30590 +374 30590 +374 30590 +374 30589 +374 30589 +374 30589 +375 30589 +375 30589 +375 30589 +375 30589 +375 30589 +375 30589 +375 30589 +375 30588 +376 30588 +376 30588 +376 30588 +376 30588 +376 30588 +376 30588 +376 30588 +377 30588 +377 30588 +377 30587 +377 30587 +377 30587 +377 30587 +377 30587 +377 30587 +378 30587 +378 30587 +378 30587 +378 30587 +378 30586 +378 30586 +378 30586 +379 30586 +379 30586 +379 30586 +379 30586 +379 30586 +379 30586 +379 30585 +380 30585 +380 30585 +380 30585 +380 30585 +380 30585 +380 30585 +380 30585 +380 30585 +381 30585 +381 30584 +381 30584 +381 30584 +381 30584 +381 30584 +381 30584 +381 30584 +382 30584 +382 30584 +382 30584 +382 30583 +382 30583 +382 30583 +382 30583 +383 30583 +383 30583 +383 30583 +383 30583 +383 30583 +383 30583 +383 30582 +383 30582 +384 30582 +384 30582 +384 30582 +384 30582 +384 30582 +384 30582 +384 30582 +384 30582 +385 30581 +385 30581 +385 30581 +385 30581 +385 30581 +385 30581 +385 30581 +385 30581 +385 30581 +386 30581 +386 30580 +386 30580 +386 30580 +386 30580 +386 30580 +386 30580 +386 30580 +387 30580 +387 30580 +387 30580 +387 30579 +387 30579 +387 30579 +387 30579 +387 30579 +388 30579 +388 30579 +388 30579 +388 30579 +388 30579 +388 30578 +388 30578 +388 30578 +389 30578 +389 30578 +389 30578 +389 30578 +389 30578 +389 30578 +389 30578 +390 30577 +390 30577 +390 30577 +390 30577 +390 30577 +390 30577 +390 30577 +390 30577 +391 30577 +391 30577 +391 30576 +391 30576 +391 30576 +391 30576 +391 30576 +391 30576 +392 30576 +392 30576 +392 30576 +392 30576 +392 30575 +392 30575 +392 30575 +392 30575 +393 30575 +393 30575 +393 30575 +393 30575 +393 30575 +393 30575 +393 30574 +393 30574 +394 30574 +394 30574 +394 30574 +394 30574 +394 30574 +394 30574 +394 30574 +394 30574 +395 30573 +395 30573 +395 30573 +395 30573 +395 30573 +395 30573 +395 30573 +395 30573 +396 30573 +396 30572 +396 30572 +396 30572 +396 30572 +396 30572 +396 30572 +397 30572 +397 30572 +397 30572 +397 30572 +397 30571 +397 30571 +397 30571 +397 30571 +398 30571 +398 30571 +398 30571 +398 30571 +398 30571 +398 30571 +398 30570 +398 30570 +399 30570 +399 30570 +399 30570 +399 30570 +399 30570 +399 30570 +399 30570 +399 30570 +399 30569 +400 30569 +400 30569 +400 30569 +400 30569 +400 30569 +400 30569 +400 30569 +400 30569 +401 30569 +401 30568 +401 30568 +401 30568 +401 30568 +401 30568 +401 30568 +401 30568 +401 30568 +402 30568 +402 30567 +402 30567 +402 30567 +402 30567 +402 30567 +402 30567 +402 30567 +402 30567 +402 30567 +403 30567 +403 30566 +403 30566 +403 30566 +403 30566 +403 30566 +403 30566 +403 30566 +403 30566 +404 30566 +404 30566 +404 30565 +404 30565 +404 30565 +404 30565 +404 30565 +404 30565 +404 30565 +405 30565 +405 30565 +405 30565 +405 30564 +405 30564 +405 30564 +405 30564 +405 30564 +406 30564 +406 30564 +406 30564 +406 30564 +406 30564 +406 30563 +406 30563 +407 30563 +407 30563 +407 30563 +407 30563 +407 30563 +407 30563 +407 30563 +407 30563 +408 30562 +408 30562 +408 30562 +408 30562 +408 30562 +408 30562 +408 30562 +409 30562 +409 30562 +409 30562 +409 30561 +409 30561 +409 30561 +409 30561 +409 30561 +410 30561 +410 30561 +410 30561 +410 30561 +410 30561 +410 30560 +410 30560 +411 30560 +411 30560 +411 30560 +411 30560 +411 30560 +411 30560 +411 30560 +412 30560 +412 30559 +412 30559 +412 30559 +412 30559 +412 30559 +412 30559 +413 30559 +413 30559 +413 30559 +413 30558 +413 30558 +413 30558 +413 30558 +414 30558 +414 30558 +414 30558 +414 30558 +414 30558 +414 30558 +414 30557 +414 30557 +415 30557 +415 30557 +415 30557 +415 30557 +415 30557 +415 30557 +415 30557 +416 30557 +416 30556 +416 30556 +416 30556 +416 30556 +416 30556 +416 30556 +417 30556 +417 30556 +417 30556 +417 30556 +417 30555 +417 30555 +417 30555 +418 30555 +418 30555 +418 30555 +418 30555 +418 30555 +418 30555 +418 30554 +418 30554 +419 30554 +419 30554 +419 30554 +419 30554 +419 30554 +419 30554 +419 30554 +420 30554 +420 30553 +420 30553 +420 30553 +420 30553 +420 30553 +420 30553 +421 30553 +421 30553 +421 30553 +421 30553 +421 30552 +421 30552 +421 30552 +421 30552 +422 30552 +422 30552 +422 30552 +422 30552 +422 30552 +422 30552 +422 30551 +422 30551 +423 30551 +423 30551 +423 30551 +423 30551 +423 30551 +423 30551 +423 30551 +424 30550 +424 30550 +424 30550 +424 30550 +424 30550 +424 30550 +424 30550 +424 30550 +425 30550 +425 30550 +425 30549 +425 30549 +425 30549 +425 30549 +425 30549 +426 30549 +426 30549 +426 30549 +426 30549 +426 30549 +426 30548 +426 30548 +426 30548 +427 30548 +427 30548 +427 30548 +427 30548 +427 30548 +427 30548 +427 30548 +428 30547 +428 30547 +428 30547 +428 30547 +428 30547 +428 30547 +428 30547 +428 30547 +429 30547 +429 30547 +429 30547 +429 30546 +429 30546 +429 30546 +429 30546 +430 30546 +430 30546 +430 30546 +430 30546 +430 30546 +430 30546 +430 30545 +430 30545 +431 30545 +431 30545 +431 30545 +431 30545 +431 30545 +431 30545 +431 30545 +431 30545 +432 30545 +432 30544 +432 30544 +432 30544 +432 30544 +432 30544 +432 30544 +433 30544 +433 30544 +433 30544 +433 30543 +433 30543 +433 30543 +433 30543 +433 30543 +434 30543 +434 30543 +434 30543 +434 30543 +434 30542 +434 30542 +434 30542 +434 30542 +435 30542 +435 30542 +435 30542 +435 30542 +435 30542 +435 30542 +435 30541 +435 30541 +436 30541 +436 30541 +436 30541 +436 30541 +436 30541 +436 30541 +436 30541 +437 30541 +437 30541 +437 30540 +437 30540 +437 30540 +437 30540 +437 30540 +437 30540 +438 30540 +438 30540 +438 30540 +438 30540 +438 30539 +438 30539 +438 30539 +439 30539 +439 30539 +439 30539 +439 30539 +439 30539 +439 30539 +439 30538 +440 30538 +440 30538 +440 30538 +440 30538 +440 30538 +440 30538 +440 30538 +440 30538 +441 30538 +441 30538 +441 30537 +441 30537 +441 30537 +441 30537 +441 30537 +442 30537 +442 30537 +442 30537 +442 30537 +442 30536 +442 30536 +442 30536 +442 30536 +443 30536 +443 30536 +443 30536 +443 30536 +443 30536 +443 30535 +443 30535 +444 30535 +444 30535 +444 30535 +444 30535 +444 30535 +444 30535 +444 30535 +444 30534 +445 30534 +445 30534 +445 30534 +445 30534 +445 30534 +445 30534 +445 30534 +445 30534 +446 30533 +446 30533 +446 30533 +446 30533 +446 30533 +446 30533 +446 30533 +446 30533 +447 30533 +447 30532 +447 30532 +447 30532 +447 30532 +447 30532 +447 30532 +448 30532 +448 30532 +448 30532 +448 30531 +448 30531 +448 30531 +448 30531 +448 30531 +449 30531 +449 30531 +449 30531 +449 30531 +449 30530 +449 30530 +449 30530 +449 30530 +450 30530 +450 30530 +450 30530 +450 30530 +450 30530 +450 30530 +450 30529 +450 30529 +451 30529 +451 30529 +451 30529 +451 30529 +451 30529 +451 30529 +451 30529 +451 30528 +452 30529 +452 30528 +452 30528 +452 30528 +452 30528 +452 30528 +452 30528 +453 30528 +453 30528 +453 30528 +453 30527 +453 30527 +453 30527 +453 30527 +453 30527 +454 30527 +454 30527 +454 30527 +454 30526 +454 30526 +454 30526 +454 30526 +454 30526 +455 30526 +455 30526 +455 30526 +455 30526 +455 30526 +455 30526 +455 30526 +455 30526 +456 30526 +456 30525 +456 30525 +456 30525 +456 30525 +456 30525 +456 30525 +456 30525 +457 30525 +457 30525 +457 30525 +457 30524 +457 30524 +457 30524 +457 30524 +458 30524 +458 30524 +458 30524 +458 30524 +458 30523 +458 30523 +458 30523 +458 30523 +459 30523 +459 30523 +459 30523 +459 30523 +459 30523 +459 30523 +459 30523 +459 30523 +460 30523 +460 30522 +460 30523 +460 30523 +460 30523 +460 30522 +460 30522 +460 30522 +461 30522 +461 30522 +461 30522 +461 30522 +461 30522 +461 30522 +461 30522 +461 30522 +462 30521 +462 30521 +462 30521 +462 30521 +462 30521 +462 30521 +462 30521 +463 30521 +463 30521 +463 30521 +463 30521 +463 30521 +463 30520 +463 30520 +463 30520 +464 30520 +464 30520 +464 30520 +464 30520 +464 30519 +464 30519 +464 30519 +464 30519 +465 30519 +465 30519 +465 30519 +465 30519 +465 30518 +465 30518 +465 30518 +465 30518 +466 30518 +466 30518 +466 30518 +466 30518 +466 30517 +466 30517 +466 30517 +466 30517 +467 30517 +467 30517 +467 30517 +467 30517 +467 30517 +467 30517 +467 30516 +467 30516 +468 30516 +468 30516 +468 30516 +468 30516 +468 30516 +468 30516 +468 30515 +468 30515 +469 30515 +469 30515 +469 30515 +469 30515 +469 30515 +469 30515 +469 30514 +469 30514 +470 30514 +470 30514 +470 30514 +470 30514 +470 30514 +470 30514 +470 30513 +470 30513 +471 30513 +471 30513 +471 30513 +471 30513 +471 30513 +471 30512 +471 30512 +471 30512 +472 30512 +472 30512 +472 30512 +472 30511 +472 30511 +472 30511 +472 30511 +472 30511 +473 30511 +473 30511 +473 30510 +473 30510 +473 30510 +473 30510 +473 30510 +473 30510 +474 30510 +474 30510 +474 30510 +474 30509 +474 30509 +474 30509 +474 30509 +474 30509 +475 30509 +475 30509 +475 30508 +475 30508 +475 30508 +475 30508 +475 30508 +475 30508 +476 30508 +476 30508 +476 30508 +476 30507 +476 30507 +476 30507 +476 30507 +476 30507 +477 30507 +477 30507 +477 30507 +477 30507 +477 30507 +477 30507 +477 30507 +477 30507 +478 30507 +478 30507 +478 30507 +478 30507 +478 30507 +478 30507 +478 30507 +478 30507 +479 30507 +479 30507 +479 30507 +479 30507 +479 30507 +479 30506 +479 30506 +479 30506 +480 30506 +480 30506 +480 30506 +480 30506 +480 30506 +480 30506 +480 30505 +480 30505 +481 30505 +481 30505 +481 30505 +481 30505 +481 30505 +481 30505 +481 30505 +481 30505 +482 30504 +482 30504 +482 30504 +482 30504 +482 30504 +482 30503 +482 30503 +482 30503 +483 30503 +483 30503 +483 30503 +483 30503 +483 30503 +483 30503 +483 30503 +483 30502 +484 30502 +484 30502 +484 30502 +484 30502 +484 30502 +484 30502 +484 30502 +484 30501 +485 30501 +485 30501 +485 30501 +485 30501 +485 30501 +485 30500 +485 30500 +485 30500 +486 30500 +486 30500 +486 30500 +486 30500 +486 30500 +486 30500 +486 30500 +486 30499 +487 30499 +487 30499 +487 30499 +487 30499 +487 30499 +487 30498 +487 30498 +487 30498 +488 30498 +488 30498 +488 30498 +488 30498 +488 30497 +488 30497 +488 30497 +488 30497 +489 30497 +489 30497 +489 30496 +489 30496 +489 30496 +489 30496 +489 30496 +489 30496 +490 30496 +490 30496 +490 30495 +490 30495 +490 30495 +490 30495 +490 30495 +490 30495 +490 30495 +491 30495 +491 30495 +491 30495 +491 30495 +491 30494 +491 30494 +491 30494 +491 30494 +492 30494 +492 30494 +492 30494 +492 30493 +492 30493 +492 30493 +492 30493 +492 30493 +493 30493 +493 30492 +493 30492 +493 30492 +493 30492 +493 30491 +493 30491 +493 30491 +494 30491 +494 30491 +494 30490 +494 30490 +494 30490 +494 30490 +494 30490 +494 30490 +495 30489 +495 30489 +495 30489 +495 30489 +495 30489 +495 30489 +495 30489 +495 30489 +496 30489 +496 30488 +496 30488 +496 30488 +496 30488 +496 30488 +496 30488 +496 30488 +497 30487 +497 30487 +497 30487 +497 30487 +497 30487 +497 30486 +497 30486 +497 30486 +497 30486 +498 30485 +498 30485 +498 30485 +498 30485 +498 30485 +498 30485 +498 30484 +498 30484 +499 30484 +499 30484 +499 30484 +499 30484 +499 30484 +499 30484 +499 30483 +499 30483 +500 30483 +500 30483 +500 30483 +500 30482 +500 30482 +500 30482 +500 30482 +501 30482 +501 30481 +501 30481 +501 30481 +501 30480 +501 30480 +501 30480 +501 30480 +501 30480 +502 30480 +502 30479 +502 30479 +502 30479 +502 30478 +502 30478 +502 30478 +502 30478 +503 30478 +503 30478 +503 30478 +503 30477 +503 30477 +503 30477 +503 30477 +503 30477 +504 30477 +504 30477 +504 30477 +504 30476 +504 30476 +504 30476 +504 30475 +504 30475 +505 30475 +505 30475 +505 30475 +505 30475 +505 30474 +505 30474 +505 30474 +505 30474 +506 30473 +506 30473 +506 30473 +506 30473 +506 30472 +506 30472 +506 30472 +506 30472 +506 30472 +507 30472 +507 30472 +507 30471 +507 30471 +507 30471 +507 30471 +507 30471 +507 30471 +508 30470 +508 30470 +508 30470 +508 30470 +508 30470 +508 30470 +508 30470 +508 30469 +509 30469 +509 30469 +509 30469 +509 30469 +509 30468 +509 30468 +509 30468 +509 30468 +510 30468 +510 30467 +510 30467 +510 30467 +510 30467 +510 30466 +510 30466 +510 30466 +511 30465 +511 30465 +511 30465 +511 30465 +511 30464 +511 30464 +511 30464 +511 30464 +511 30464 +512 30463 +512 30463 +512 30463 +512 30463 +512 30463 +512 30463 +512 30462 +512 30462 +513 30462 +513 30462 +513 30461 +513 30461 +513 30461 +513 30461 +513 30460 +513 30460 +514 30460 +514 30460 +514 30460 +514 30459 +514 30459 +514 30459 +514 30459 +514 30458 +515 30458 +515 30458 +515 30458 +515 30458 +515 30457 +515 30457 +515 30457 +516 30456 +516 30456 +516 30456 +516 30456 +516 30456 +516 30455 +516 30455 +517 30455 +517 30455 +517 30454 +517 30454 +517 30454 +517 30454 +518 30453 +518 30453 +518 30453 +518 30453 +518 30453 +518 30452 +519 30452 +519 30452 +519 30452 +519 30452 +519 30451 +519 30451 +519 30451 +520 30451 +520 30451 +520 30450 +520 30450 +520 30450 +520 30450 +521 30449 +521 30449 +521 30449 +521 30449 +521 30448 +521 30448 +521 30448 +522 30448 +522 30447 +522 30447 +522 30447 +522 30446 +522 30446 +522 30446 +523 30446 +523 30445 +523 30445 +523 30445 +523 30444 +523 30444 +523 30444 +524 30444 +524 30443 +524 30443 +524 30443 +524 30443 +524 30443 +524 30442 +525 30442 +525 30442 +525 30442 +525 30441 +525 30441 +525 30441 +525 30441 +526 30441 +526 30440 +526 30440 +526 30440 +526 30439 +526 30439 +526 30439 +527 30438 +527 30438 +527 30438 +527 30438 +527 30437 +527 30437 +527 30437 +528 30436 +528 30436 +528 30436 +528 30436 +528 30436 +528 30436 +528 30435 +528 30435 +529 30435 +529 30434 +529 30434 +529 30434 +529 30434 +529 30434 +529 30433 +530 30433 +530 30433 +530 30433 +530 30433 +530 30433 +530 30432 +530 30432 +531 30432 +531 30432 +531 30432 +531 30432 +531 30432 +531 30432 +531 30431 +531 30431 +532 30431 +532 30431 +532 30431 +532 30430 +532 30430 +532 30430 +532 30430 +533 30430 +533 30430 +533 30429 +533 30429 +533 30429 +533 30429 +533 30428 +534 30428 +534 30428 +534 30428 +534 30427 +534 30427 +534 30427 +534 30427 +534 30426 +535 30426 +535 30426 +535 30426 +535 30426 +535 30426 +535 30425 +535 30425 +536 30425 +536 30425 +536 30425 +536 30425 +536 30424 +536 30424 +536 30424 +537 30424 +537 30423 +537 30423 +537 30423 +537 30423 +537 30423 +537 30423 +537 30422 +538 30422 +538 30422 +538 30422 +538 30422 +538 30421 +538 30421 +538 30421 +539 30421 +539 30420 +539 30420 +539 30420 +539 30420 +539 30420 +539 30420 +540 30419 +540 30419 +540 30419 +540 30419 +540 30418 +540 30418 +540 30418 +540 30418 +541 30417 +541 30417 +541 30417 +541 30417 +541 30417 +541 30417 +541 30417 +542 30417 +542 30416 +542 30416 +542 30416 +542 30416 +542 30416 +542 30416 +543 30416 +543 30415 +543 30415 +543 30415 +543 30415 +543 30414 +543 30414 +543 30414 +544 30414 +544 30414 +544 30414 +544 30413 +544 30413 +544 30413 +544 30413 +545 30412 +545 30412 +545 30412 +545 30412 +545 30411 +545 30411 +545 30411 +545 30411 +546 30410 +546 30410 +546 30410 +546 30410 +546 30410 +546 30409 +546 30409 +547 30409 +547 30409 +547 30409 +547 30409 +547 30409 +547 30408 +547 30408 +547 30408 +548 30408 +548 30408 +548 30408 +548 30407 +548 30407 +548 30407 +548 30406 +549 30406 +549 30406 +549 30406 +549 30406 +549 30405 +549 30405 +549 30405 +549 30404 +550 30404 +550 30404 +550 30404 +550 30404 +550 30403 +550 30403 +550 30403 +551 30403 +551 30403 +551 30403 +551 30402 +551 30402 +551 30402 +551 30402 +552 30401 +552 30401 +552 30401 +552 30401 +552 30401 +552 30400 +552 30400 +552 30400 +553 30400 +553 30400 +553 30400 +553 30400 +553 30399 +553 30399 +553 30399 +554 30399 +554 30399 +554 30399 +554 30399 +554 30399 +554 30398 +554 30398 +555 30398 +555 30398 +555 30398 +555 30397 +555 30397 +555 30397 +555 30397 +555 30397 +556 30397 +556 30396 +556 30396 +556 30396 +556 30396 +556 30396 +556 30396 +557 30395 +557 30395 +557 30395 +557 30395 +557 30395 +557 30395 +557 30395 +557 30394 +558 30394 +558 30394 +558 30394 +558 30394 +558 30393 +558 30393 +558 30393 +559 30393 +559 30393 +559 30393 +559 30393 +559 30393 +559 30392 +559 30392 +560 30392 +560 30392 +560 30392 +560 30392 +560 30392 +560 30391 +560 30391 +560 30391 +561 30391 +561 30391 +561 30391 +561 30390 +561 30390 +561 30390 +561 30390 +561 30390 +562 30389 +562 30389 +562 30389 +562 30389 +562 30389 +562 30389 +562 30389 +562 30389 +563 30388 +563 30388 +563 30388 +563 30388 +563 30388 +563 30388 +563 30388 +564 30388 +564 30387 +564 30387 +564 30387 +564 30387 +564 30387 +564 30387 +564 30387 +565 30387 +565 30387 +565 30386 +565 30386 +565 30386 +565 30386 +565 30386 +565 30386 +566 30386 +566 30385 +566 30385 +566 30385 +566 30385 +566 30385 +566 30385 +566 30384 +567 30384 +567 30384 +567 30384 +567 30384 +567 30384 +567 30384 +567 30384 +567 30384 +568 30383 +568 30383 +568 30383 +568 30382 +568 30382 +568 30382 +568 30382 +568 30382 +569 30382 +569 30381 +569 30381 +569 30381 +569 30381 +569 30380 +569 30380 +570 30380 +570 30380 +570 30380 +570 30379 +570 30379 +570 30379 +570 30379 +570 30378 +571 30378 +571 30378 +571 30378 +571 30378 +571 30377 +571 30377 +571 30377 +571 30377 +572 30377 +572 30377 +572 30376 +572 30376 +572 30376 +572 30376 +572 30376 +572 30375 +573 30375 +573 30375 +573 30375 +573 30375 +573 30374 +573 30374 +573 30374 +574 30374 +574 30374 +574 30374 +574 30374 +574 30374 +574 30374 +574 30374 +574 30374 +575 30373 +575 30373 +575 30373 +575 30373 +575 30373 +575 30373 +575 30373 +575 30373 +576 30373 +576 30373 +576 30373 +576 30372 +576 30372 +576 30372 +576 30372 +576 30372 +577 30372 +577 30371 +577 30371 +577 30371 +577 30371 +577 30371 +577 30371 +577 30370 +578 30370 +578 30370 +578 30370 +578 30370 +578 30370 +578 30370 +578 30370 +578 30369 +579 30369 +579 30369 +579 30369 +579 30369 +579 30368 +579 30368 +579 30368 +579 30368 +579 30368 +580 30368 +580 30367 +580 30367 +580 30367 +580 30367 +580 30367 +580 30367 +580 30367 +581 30367 +581 30367 +581 30367 +581 30366 +581 30366 +581 30366 +581 30366 +581 30366 +582 30365 +582 30365 +582 30365 +582 30365 +582 30365 +582 30364 +582 30364 +582 30364 +583 30364 +583 30363 +583 30363 +583 30363 +583 30363 +583 30363 +583 30363 +583 30363 +584 30363 +584 30363 +584 30362 +584 30362 +584 30362 +584 30362 +584 30362 +584 30362 +585 30362 +585 30362 +585 30362 +585 30361 +585 30361 +585 30361 +585 30361 +585 30361 +586 30361 +586 30361 +586 30361 +586 30360 +586 30360 +586 30360 +586 30360 +586 30360 +587 30360 +587 30360 +587 30360 +587 30360 +587 30360 +587 30360 +587 30360 +587 30359 +588 30359 +588 30359 +588 30359 +588 30359 +588 30359 +588 30359 +588 30359 +588 30359 +589 30359 +589 30359 +589 30359 +589 30359 +589 30359 +589 30359 +589 30359 +589 30358 +590 30358 +590 30358 +590 30358 +590 30358 +590 30358 +590 30358 +590 30358 +590 30358 +590 30357 +591 30357 +591 30357 +591 30357 +591 30357 +591 30357 +591 30357 +591 30357 +591 30357 +592 30357 +592 30356 +592 30356 +592 30356 +592 30356 +592 30356 +592 30356 +592 30356 +593 30356 +593 30356 +593 30356 +593 30356 +593 30356 +593 30355 +593 30355 +593 30355 +593 30355 +594 30355 +594 30355 +594 30354 +594 30354 +594 30354 +594 30354 +594 30354 +594 30353 +595 30353 +595 30353 +595 30353 +595 30352 +595 30352 +595 30352 +595 30351 +595 30351 +595 30351 +596 30351 +596 30351 +596 30350 +596 30350 +596 30350 +596 30350 +596 30350 +596 30350 +597 30350 +597 30350 +597 30350 +597 30349 +597 30349 +597 30349 +597 30349 +597 30349 +597 30349 +598 30348 +598 30348 +598 30348 +598 30348 +598 30348 +598 30348 +598 30348 +598 30348 +599 30348 +599 30348 +599 30348 +599 30348 +599 30348 +599 30348 +599 30347 +599 30347 +599 30347 +600 30347 +600 30347 +600 30346 +600 30346 +600 30346 +600 30346 +600 30346 +600 30346 +600 30346 +601 30346 +601 30345 +601 30345 +601 30345 +601 30345 +601 30345 +601 30345 +601 30345 +602 30345 +602 30345 +602 30344 +602 30344 +602 30344 +602 30344 +602 30343 +602 30343 +602 30343 +603 30343 +603 30343 +603 30343 +603 30342 +603 30342 +603 30342 +603 30342 +603 30342 +603 30342 +604 30342 +604 30342 +604 30342 +604 30342 +604 30341 +604 30341 +604 30341 +604 30341 +604 30341 +605 30341 +605 30341 +605 30341 +605 30341 +605 30340 +605 30340 +605 30340 +605 30340 +606 30340 +606 30340 +606 30339 +606 30339 +606 30339 +606 30339 +606 30339 +606 30338 +606 30338 +607 30338 +607 30338 +607 30338 +607 30338 +607 30337 +607 30337 +607 30336 +607 30336 +607 30336 +608 30336 +608 30336 +608 30335 +608 30335 +608 30335 +608 30335 +608 30335 +608 30334 +608 30334 +609 30334 +609 30334 +609 30334 +609 30334 +609 30334 +609 30334 +609 30334 +609 30334 +610 30334 +610 30334 +610 30333 +610 30333 +610 30333 +610 30333 +610 30332 +610 30332 +610 30332 +611 30332 +611 30332 +611 30331 +611 30331 +611 30331 +611 30331 +611 30330 +611 30330 +611 30330 +612 30330 +612 30329 +612 30329 +612 30329 +612 30329 +612 30329 +612 30329 +612 30329 +612 30329 +613 30328 +613 30328 +613 30328 +613 30328 +613 30328 +613 30327 +613 30327 +613 30327 +613 30327 +614 30327 +614 30326 +614 30326 +614 30326 +614 30326 +614 30325 +614 30325 +614 30325 +614 30325 +615 30325 +615 30324 +615 30324 +615 30324 +615 30324 +615 30324 +615 30324 +615 30324 +615 30324 +615 30324 +616 30323 +616 30323 +616 30323 +616 30323 +616 30323 +616 30323 +616 30322 +616 30322 +616 30322 +617 30322 +617 30322 +617 30322 +617 30322 +617 30322 +617 30321 +617 30321 +617 30321 +617 30321 +618 30321 +618 30321 +618 30320 +618 30320 +618 30320 +618 30319 +618 30319 +618 30319 +618 30319 +619 30319 +619 30319 +619 30318 +619 30318 +619 30318 +619 30318 +619 30317 +619 30317 +619 30317 +620 30317 +620 30317 +620 30317 +620 30316 +620 30316 +620 30316 +620 30316 +620 30316 +620 30315 +621 30315 +621 30315 +621 30315 +621 30315 +621 30314 +621 30314 +621 30314 +621 30313 +621 30313 +621 30313 +622 30312 +622 30312 +622 30312 +622 30312 +622 30311 +622 30311 +622 30311 +622 30311 +622 30311 +623 30310 +623 30310 +623 30310 +623 30310 +623 30310 +623 30309 +623 30309 +623 30309 +623 30309 +623 30308 +624 30308 +624 30308 +624 30308 +624 30308 +624 30308 +624 30308 +624 30307 +624 30307 +624 30307 +625 30307 +625 30307 +625 30307 +625 30306 +625 30306 +625 30306 +625 30306 +625 30305 +625 30305 +626 30305 +626 30304 +626 30304 +626 30304 +626 30304 +626 30304 +626 30304 +626 30303 +626 30303 +627 30303 +627 30303 +627 30302 +627 30302 +627 30302 +627 30302 +627 30302 +627 30301 +627 30301 +628 30301 +628 30300 +628 30300 +628 30300 +628 30300 +628 30300 +628 30299 +628 30299 +628 30299 +629 30299 +629 30299 +629 30299 +629 30298 +629 30298 +629 30298 +629 30298 +629 30297 +629 30297 +630 30297 +630 30297 +630 30297 +630 30296 +630 30296 +630 30296 +630 30296 +630 30295 +630 30295 +631 30295 +631 30295 +631 30294 +631 30294 +631 30294 +631 30294 +631 30293 +631 30293 +631 30293 +632 30292 +632 30292 +632 30292 +632 30292 +632 30292 +632 30292 +632 30292 +632 30291 +632 30291 +633 30291 +633 30291 +633 30291 +633 30290 +633 30290 +633 30290 +633 30290 +633 30289 +633 30289 +634 30289 +634 30289 +634 30289 +634 30289 +634 30288 +634 30288 +634 30288 +634 30288 +634 30288 +635 30287 +635 30287 +635 30287 +635 30287 +635 30286 +635 30286 +635 30286 +635 30286 +635 30286 +636 30286 +636 30285 +636 30285 +636 30285 +636 30285 +636 30285 +636 30285 +636 30284 +636 30284 +637 30284 +637 30284 +637 30284 +637 30284 +637 30284 +637 30284 +637 30283 +637 30283 +637 30283 +638 30283 +638 30283 +638 30283 +638 30283 +638 30282 +638 30282 +638 30282 +638 30282 +638 30281 +639 30281 +639 30281 +639 30281 +639 30281 +639 30280 +639 30280 +639 30280 +639 30280 +639 30280 +640 30279 +640 30279 +640 30279 +640 30279 +640 30278 +640 30278 +640 30278 +640 30278 +640 30277 +641 30277 +641 30277 +641 30276 +641 30276 +641 30276 +641 30276 +641 30276 +641 30275 +642 30275 +642 30274 +642 30274 +642 30274 +642 30274 +642 30274 +642 30273 +642 30273 +642 30273 +643 30273 +643 30273 +643 30273 +643 30273 +643 30273 +643 30272 +643 30272 +643 30271 +644 30271 +644 30271 +644 30271 +644 30271 +644 30270 +644 30270 +644 30270 +644 30269 +644 30269 +645 30269 +645 30269 +645 30269 +645 30269 +645 30268 +645 30268 +645 30268 +645 30268 +645 30267 +646 30267 +646 30267 +646 30266 +646 30266 +646 30266 +646 30265 +646 30265 +646 30265 +647 30265 +647 30265 +647 30265 +647 30264 +647 30264 +647 30264 +647 30263 +647 30263 +647 30263 +648 30263 +648 30263 +648 30262 +648 30262 +648 30262 +648 30262 +648 30262 +648 30261 +649 30261 +649 30261 +649 30261 +649 30261 +649 30260 +649 30260 +649 30260 +649 30259 +649 30259 +650 30259 +650 30258 +650 30258 +650 30258 +650 30258 +650 30257 +650 30257 +650 30257 +651 30256 +651 30256 +651 30256 +651 30256 +651 30256 +651 30255 +651 30255 +651 30255 +651 30254 +652 30254 +652 30254 +652 30254 +652 30254 +652 30254 +652 30253 +652 30253 +652 30253 +653 30253 +653 30252 +653 30252 +653 30252 +653 30252 +653 30252 +653 30251 +653 30251 +653 30251 +654 30251 +654 30251 +654 30250 +654 30250 +654 30250 +654 30249 +654 30249 +654 30249 +655 30248 +655 30248 +655 30248 +655 30247 +655 30247 +655 30247 +655 30247 +655 30246 +656 30246 +656 30246 +656 30246 +656 30246 +656 30245 +656 30245 +656 30245 +656 30245 +657 30245 +657 30245 +657 30244 +657 30244 +657 30244 +657 30244 +657 30244 +657 30243 +657 30243 +658 30243 +658 30242 +658 30242 +658 30242 +658 30242 +658 30241 +658 30241 +658 30241 +659 30240 +659 30240 +659 30240 +659 30240 +659 30240 +659 30239 +659 30239 +659 30239 +659 30238 +660 30238 +660 30238 +660 30237 +660 30237 +660 30237 +660 30236 +660 30236 +660 30236 +661 30235 +661 30235 +661 30235 +661 30235 +661 30235 +661 30234 +661 30234 +661 30234 +662 30233 +662 30233 +662 30233 +662 30232 +662 30232 +662 30232 +662 30232 +662 30231 +663 30231 +663 30231 +663 30230 +663 30230 +663 30230 +663 30229 +663 30229 +663 30228 +664 30228 +664 30228 +664 30228 +664 30228 +664 30228 +664 30227 +664 30227 +664 30226 +665 30226 +665 30226 +665 30225 +665 30225 +665 30225 +665 30225 +665 30224 +665 30224 +666 30224 +666 30223 +666 30223 +666 30222 +666 30222 +666 30222 +666 30221 +666 30221 +667 30221 +667 30220 +667 30220 +667 30220 +667 30219 +667 30219 +667 30219 +668 30219 +668 30219 +668 30218 +668 30217 +668 30217 +668 30216 +668 30216 +668 30216 +669 30215 +669 30215 +669 30215 +669 30214 +669 30214 +669 30214 +669 30214 +670 30213 +670 30213 +670 30213 +670 30212 +670 30212 +670 30211 +670 30210 +671 30210 +671 30210 +671 30209 +671 30209 +671 30209 +671 30209 +671 30208 +671 30208 +672 30208 +672 30207 +672 30207 +672 30207 +672 30206 +672 30206 +672 30206 +673 30206 +673 30205 +673 30205 +673 30204 +673 30204 +673 30204 +673 30204 +674 30203 +674 30203 +674 30202 +674 30202 +674 30202 +674 30202 +675 30202 +675 30202 +675 30201 +675 30201 +675 30201 +675 30200 +675 30200 +676 30200 +676 30200 +676 30200 +676 30199 +676 30199 +676 30199 +676 30198 +677 30198 +677 30198 +677 30198 +677 30197 +677 30197 +677 30197 +677 30197 +678 30196 +678 30196 +678 30196 +678 30196 +678 30196 +678 30196 +679 30195 +679 30195 +679 30195 +679 30194 +679 30194 +679 30194 +679 30194 +680 30193 +680 30193 +680 30192 +680 30192 +680 30192 +680 30192 +681 30192 +681 30191 +681 30191 +681 30191 +681 30191 +681 30190 +681 30190 +682 30190 +682 30189 +682 30189 +682 30189 +682 30189 +682 30188 +683 30188 +683 30188 +683 30187 +683 30187 +683 30187 +683 30187 +684 30186 +684 30186 +684 30186 +684 30185 +684 30185 +684 30185 +684 30184 +685 30184 +685 30184 +685 30183 +685 30183 +685 30183 +685 30183 +686 30182 +686 30182 +686 30181 +686 30181 +686 30181 +686 30180 +687 30180 +687 30180 +687 30179 +687 30179 +687 30178 +687 30178 +688 30178 +688 30178 +688 30177 +688 30177 +688 30177 +688 30177 +689 30176 +689 30176 +689 30176 +689 30175 +689 30175 +689 30175 +690 30175 +690 30174 +690 30174 +690 30173 +690 30173 +690 30173 +691 30172 +691 30172 +691 30172 +691 30171 +691 30171 +691 30171 +692 30171 +692 30170 +692 30170 +692 30170 +692 30170 +693 30170 +693 30169 +693 30169 +693 30169 +693 30169 +693 30169 +694 30168 +694 30168 +694 30168 +694 30168 +694 30167 +694 30167 +695 30167 +695 30166 +695 30166 +695 30166 +695 30165 +696 30165 +696 30165 +696 30165 +696 30165 +696 30164 +696 30164 +697 30164 +697 30163 +697 30163 +697 30163 +697 30162 +697 30162 +698 30162 +698 30162 +698 30161 +698 30161 +698 30161 +699 30160 +699 30160 +699 30160 +699 30159 +699 30159 +699 30159 +700 30159 +700 30158 +700 30158 +700 30158 +700 30158 +701 30157 +701 30157 +701 30157 +701 30156 +701 30156 +701 30156 +702 30155 +702 30155 +702 30155 +702 30155 +702 30154 +703 30154 +703 30154 +703 30153 +703 30153 +703 30152 +704 30152 +704 30152 +704 30152 +704 30152 +704 30151 +705 30151 +705 30151 +705 30150 +705 30150 +705 30150 +706 30150 +706 30149 +706 30149 +706 30149 +706 30149 +707 30148 +707 30148 +707 30148 +707 30147 +707 30147 +708 30147 +708 30147 +708 30146 +708 30146 +708 30146 +709 30146 +709 30145 +709 30145 +709 30145 +709 30144 +710 30144 +710 30144 +710 30144 +710 30143 +710 30143 +711 30143 +711 30142 +711 30142 +711 30142 +711 30141 +712 30141 +712 30141 +712 30141 +712 30140 +712 30140 +713 30140 +713 30140 +713 30139 +713 30139 +713 30139 +714 30139 +714 30139 +714 30138 +714 30138 +714 30137 +715 30137 +715 30137 +715 30136 +715 30136 +716 30136 +716 30136 +716 30135 +716 30135 +716 30135 +717 30134 +717 30134 +717 30134 +717 30133 +717 30133 +718 30133 +718 30132 +718 30132 +718 30132 +719 30131 +719 30131 +719 30131 +719 30130 +719 30130 +720 30130 +720 30130 +720 30129 +720 30129 +721 30129 +721 30128 +721 30128 +721 30128 +721 30127 +722 30127 +722 30127 +722 30126 +722 30126 +723 30126 +723 30126 +723 30125 +723 30125 +723 30125 +724 30125 +724 30124 +724 30124 +724 30123 +725 30123 +725 30123 +725 30122 +725 30122 +725 30122 +726 30121 +726 30121 +726 30121 +726 30120 +727 30120 +727 30120 +727 30120 +727 30119 +728 30119 +728 30119 +728 30118 +728 30118 +728 30118 +729 30117 +729 30117 +729 30117 +729 30116 +730 30116 +730 30116 +730 30115 +730 30115 +731 30115 +731 30114 +731 30114 +731 30114 +732 30113 +732 30113 +732 30113 +732 30112 +732 30112 +733 30112 +733 30111 +733 30111 +733 30111 +734 30111 +734 30110 +734 30110 +734 30110 +735 30109 +735 30109 +735 30109 +735 30108 +736 30108 +736 30108 +736 30107 +736 30107 +737 30107 +737 30107 +737 30107 +737 30106 +738 30106 +738 30106 +738 30106 +738 30105 +739 30105 +739 30105 +739 30105 +739 30104 +740 30104 +740 30104 +740 30103 +740 30103 +741 30103 +741 30103 +741 30102 +741 30102 +741 30102 +742 30101 +742 30101 +742 30101 +742 30101 +743 30100 +743 30100 +743 30100 +743 30099 +744 30099 +744 30099 +744 30098 +745 30098 +745 30098 +745 30098 +745 30097 +746 30097 +746 30097 +746 30097 +746 30096 +747 30096 +747 30096 +747 30095 +747 30095 +748 30095 +748 30094 +748 30094 +748 30094 +749 30094 +749 30093 +749 30093 +749 30093 +750 30092 +750 30092 +750 30092 +750 30092 +751 30091 +751 30091 +751 30091 +751 30090 +752 30090 +752 30090 +752 30090 +753 30089 +753 30089 +753 30089 +753 30088 +754 30088 +754 30088 +754 30087 +754 30087 +755 30087 +755 30087 +755 30086 +755 30086 +756 30086 +756 30085 +756 30085 +757 30085 +757 30084 +757 30084 +757 30084 +758 30083 +758 30083 +758 30083 +758 30082 +759 30082 +759 30082 +759 30081 +760 30081 +760 30081 +760 30080 +760 30080 +761 30080 +761 30079 +761 30079 +761 30079 +762 30079 +762 30078 +762 30078 +763 30078 +763 30077 +763 30077 +763 30077 +764 30076 +764 30076 +764 30076 +765 30076 +765 30076 +765 30075 +765 30075 +766 30075 +766 30074 +766 30074 +766 30074 +767 30073 +767 30073 +767 30073 +768 30072 +768 30072 +768 30071 +768 30071 +769 30071 +769 30071 +769 30070 +770 30070 +770 30070 +770 30070 +770 30069 +771 30069 +771 30069 +771 30069 +772 30068 +772 30068 +772 30068 +772 30067 +773 30067 +773 30067 +773 30066 +774 30066 +774 30066 +774 30065 +775 30065 +775 30065 +775 30064 +775 30064 +776 30064 +776 30064 +776 30063 +777 30063 +777 30063 +777 30062 +777 30062 +778 30061 +778 30061 +778 30061 +779 30060 +779 30060 +779 30060 +780 30059 +780 30059 +780 30059 +780 30058 +781 30058 +781 30058 +781 30058 +782 30057 +782 30057 +782 30057 +782 30056 +783 30056 +783 30056 +783 30056 +784 30055 +784 30055 +784 30055 +785 30054 +785 30054 +785 30054 +786 30053 +786 30053 +786 30053 +786 30052 +787 30052 +787 30052 +787 30052 +788 30051 +788 30051 +788 30051 +789 30050 +789 30050 +789 30050 +790 30049 +790 30049 +790 30049 +790 30048 +791 30048 +791 30048 +791 30048 +792 30047 +792 30047 +792 30047 +793 30046 +793 30046 +793 30046 +794 30045 +794 30045 +794 30045 +794 30044 +795 30044 +795 30044 +795 30044 +796 30043 +796 30043 +796 30043 +797 30042 +797 30042 +797 30042 +798 30041 +798 30041 +798 30041 +798 30040 +799 30040 +799 30040 +799 30039 +800 30039 +800 30039 +800 30038 +801 30038 +801 30038 +801 30038 +802 30037 +802 30037 +802 30037 +803 30036 +803 30036 +803 30036 +804 30036 +804 30036 +804 30035 +804 30035 +805 30035 +805 30035 +805 30034 +806 30034 +806 30034 +806 30033 +807 30033 +807 30033 +807 30033 +808 30033 +808 30032 +808 30032 +809 30032 +809 30031 +809 30031 +810 30031 +810 30030 +810 30030 +811 30030 +811 30030 +811 30029 +812 30029 +812 30029 +812 30028 +812 30028 +813 30028 +813 30027 +813 30027 +814 30027 +814 30027 +814 30026 +815 30026 +815 30026 +815 30025 +816 30025 +816 30025 +816 30024 +817 30024 +817 30024 +817 30024 +818 30023 +818 30023 +818 30023 +819 30023 +819 30022 +819 30022 +820 30022 +820 30021 +820 30021 +821 30021 +821 30020 +821 30020 +822 30020 +822 30020 +822 30019 +823 30019 +823 30019 +823 30018 +824 30018 +824 30018 +824 30017 +825 30017 +825 30017 +825 30016 +826 30016 +826 30016 +826 30016 +827 30015 +827 30015 +827 30015 +828 30015 +828 30014 +828 30014 +829 30014 +829 30013 +829 30013 +830 30013 +830 30013 +830 30012 +831 30012 +831 30012 +831 30011 +832 30011 +832 30011 +832 30010 +833 30010 +833 30010 +833 30010 +834 30009 +834 30009 +834 30009 +835 30009 +835 30008 +835 30008 +836 30007 +836 30007 +836 30007 +837 30006 +837 30006 +837 30006 +838 30005 +838 30005 +838 30005 +839 30005 +839 30004 +839 30004 +840 30004 +840 30003 +840 30003 +841 30003 +841 30003 +841 30002 +842 30002 +842 30002 +842 30002 +843 30001 +843 30001 +843 30001 +844 30000 +844 30000 +844 30000 +845 29999 +845 29999 +845 29999 +846 29999 +846 29998 +846 29998 +847 29998 +847 29997 +847 29997 +848 29996 +848 29996 +848 29996 +849 29996 +849 29995 +849 29995 +850 29994 +850 29994 +850 29994 +851 29993 +851 29993 +851 29993 +852 29992 +852 29992 +852 29992 +853 29992 +853 29991 +853 29991 +854 29991 +854 29990 +854 29990 +855 29990 +855 29989 +855 29989 +856 29989 +856 29989 +856 29988 +857 29988 +857 29988 +858 29987 +858 29987 +858 29987 +859 29986 +859 29986 +859 29986 +860 29986 +860 29985 +860 29985 +861 29985 +861 29984 +861 29984 +862 29984 +862 29983 +862 29983 +863 29983 +863 29982 +863 29982 +864 29982 +864 29982 +864 29981 +865 29981 +865 29981 +865 29980 +866 29980 +866 29980 +866 29979 +867 29979 +867 29979 +867 29979 +868 29978 +868 29978 +869 29978 +869 29977 +869 29977 +870 29977 +870 29976 +870 29976 +871 29976 +871 29975 +871 29975 +872 29975 +872 29974 +872 29974 +873 29974 +873 29974 +873 29973 +874 29973 +874 29973 +874 29972 +875 29972 +875 29972 +875 29971 +876 29971 +876 29971 +877 29971 +877 29970 +877 29970 +878 29970 +878 29969 +878 29969 +879 29969 +879 29968 +879 29968 +880 29968 +880 29967 +880 29967 +881 29967 +881 29966 +881 29966 +882 29966 +882 29966 +883 29965 +883 29965 +883 29965 +884 29964 +884 29964 +884 29964 +885 29963 +885 29963 +885 29963 +886 29962 +886 29962 +886 29962 +887 29961 +887 29961 +887 29961 +888 29960 +888 29960 +888 29960 +889 29960 +889 29959 +890 29959 +890 29959 +890 29958 +891 29958 +891 29957 +891 29957 +892 29957 +892 29957 +892 29956 +893 29956 +893 29956 +893 29955 +894 29955 +894 29955 +895 29954 +895 29954 +895 29954 +896 29953 +896 29953 +896 29953 +897 29952 +897 29952 +897 29952 +898 29952 +898 29952 +898 29951 +899 29951 +899 29951 +899 29950 +900 29950 +900 29949 +901 29949 +901 29949 +901 29948 +902 29948 +902 29948 +902 29948 +903 29948 +903 29948 +903 29948 +904 29947 +904 29947 +904 29947 +905 29946 +905 29946 +906 29946 +906 29945 +906 29945 +907 29945 +907 29944 +907 29944 +908 29944 +908 29944 +908 29944 +909 29944 +909 29943 +909 29943 +910 29943 +910 29942 +911 29942 +911 29942 +911 29941 +912 29941 +912 29941 +912 29941 +913 29940 +913 29940 +913 29940 +914 29940 +914 29939 +914 29939 +915 29938 +915 29938 +916 29938 +916 29937 +916 29937 +917 29937 +917 29936 +917 29936 +918 29935 +918 29935 +918 29935 +919 29935 +919 29934 +920 29934 +920 29934 +920 29933 +921 29933 +921 29933 +921 29932 +922 29932 +922 29932 +922 29931 +923 29931 +923 29931 +924 29931 +924 29930 +924 29930 +925 29930 +925 29930 +925 29929 +926 29929 +926 29929 +926 29928 +927 29928 +927 29928 +927 29927 +928 29927 +928 29927 +929 29926 +929 29926 +929 29926 +930 29926 +930 29925 +930 29925 +931 29925 +931 29924 +931 29924 +932 29924 +932 29924 +933 29923 +933 29923 +933 29923 +934 29923 +934 29922 +934 29922 +935 29922 +935 29921 +935 29921 +936 29921 +936 29920 +937 29920 +937 29920 +937 29919 +938 29919 +938 29919 +938 29918 +939 29918 +939 29917 +939 29917 +940 29917 +940 29917 +941 29916 +941 29916 +941 29916 +942 29915 +942 29915 +942 29915 +943 29914 +943 29914 +943 29914 +944 29913 +944 29913 +945 29913 +945 29913 +945 29912 +946 29912 +946 29912 +946 29912 +947 29911 +947 29911 +948 29911 +948 29910 +948 29910 +949 29910 +949 29909 +949 29909 +950 29909 +950 29908 +951 29908 +951 29908 +951 29907 +952 29907 +952 29907 +952 29907 +953 29906 +953 29906 +953 29906 +954 29905 +954 29905 +955 29905 +955 29904 +955 29904 +956 29904 +956 29903 +956 29903 +957 29903 +957 29903 +958 29902 +958 29902 +958 29902 +959 29901 +959 29901 +959 29901 +960 29900 +960 29900 +960 29899 +961 29899 +961 29899 +962 29898 +962 29898 +962 29898 +963 29897 +963 29897 +963 29897 +964 29897 +964 29896 +965 29896 +965 29896 +965 29895 +966 29895 +966 29895 +966 29894 +967 29894 +967 29894 +968 29893 +968 29893 +968 29893 +969 29892 +969 29892 +969 29892 +970 29891 +970 29891 +971 29891 +971 29890 +971 29890 +972 29890 +972 29889 +972 29889 +973 29889 +973 29888 +974 29888 +974 29888 +974 29887 +975 29887 +975 29887 +976 29886 +976 29886 +976 29886 +977 29885 +977 29885 +977 29885 +978 29884 +978 29884 +979 29884 +979 29883 +979 29883 +980 29883 +980 29882 +980 29882 +981 29882 +981 29881 +982 29881 +982 29881 +982 29880 +983 29880 +983 29880 +983 29880 +984 29879 +984 29879 +985 29879 +985 29878 +985 29878 +986 29878 +986 29877 +987 29877 +987 29877 +987 29876 +988 29876 +988 29876 +988 29875 +989 29875 +989 29875 +990 29874 +990 29874 +990 29874 +991 29873 +991 29873 +992 29873 +992 29872 +992 29872 +993 29872 +993 29871 +993 29871 +994 29870 +994 29870 +995 29870 +995 29869 +995 29869 +996 29869 +996 29868 +997 29868 +997 29868 +997 29867 +998 29867 +998 29867 +998 29866 +999 29866 +999 29865 +1000 29865 +1000 29865 +1000 29864 +1001 29864 +1001 29864 +1002 29863 +1002 29863 +1002 29863 +1003 29862 +1003 29862 +1004 29862 +1004 29862 +1004 29861 +1005 29861 +1005 29860 +1005 29860 +1006 29860 +1006 29859 +1007 29859 +1007 29859 +1007 29858 +1008 29858 +1008 29858 +1009 29857 +1009 29857 +1009 29856 +1010 29856 +1010 29856 +1011 29855 +1011 29855 +1011 29855 +1012 29854 +1012 29854 +1013 29854 +1013 29853 +1013 29853 +1014 29852 +1014 29852 +1014 29852 +1015 29851 +1015 29851 +1016 29851 +1016 29850 +1016 29850 +1017 29849 +1017 29849 +1018 29849 +1018 29848 +1018 29848 +1019 29848 +1019 29847 +1020 29847 +1020 29846 +1020 29846 +1021 29846 +1021 29845 +1022 29845 +1022 29845 +1022 29845 +1023 29844 +1023 29844 +1023 29843 +1024 29843 +1024 29843 +1025 29842 +1025 29842 +1025 29842 +1026 29841 +1026 29841 +1027 29840 +1027 29840 +1027 29840 +1028 29839 +1028 29839 +1029 29838 +1029 29838 +1029 29838 +1030 29837 +1030 29837 +1031 29837 +1031 29836 +1031 29835 +1032 29835 +1032 29835 +1033 29834 +1033 29834 +1033 29834 +1034 29833 +1034 29833 +1035 29832 +1035 29832 +1035 29832 +1036 29831 +1036 29831 +1037 29831 +1037 29830 +1037 29830 +1038 29829 +1038 29829 +1039 29829 +1039 29828 +1039 29828 +1040 29827 +1040 29827 +1041 29827 +1041 29826 +1041 29826 +1042 29826 +1042 29825 +1043 29825 +1043 29824 +1043 29824 +1044 29824 +1044 29823 +1045 29823 +1045 29822 +1045 29822 +1046 29822 +1046 29821 +1047 29821 +1047 29820 +1048 29820 +1048 29820 +1048 29819 +1049 29819 +1049 29818 +1050 29818 +1050 29818 +1050 29817 +1051 29817 +1051 29816 +1052 29816 +1052 29816 +1052 29815 +1053 29815 +1053 29814 +1054 29814 +1054 29814 +1054 29813 +1055 29813 +1055 29812 +1056 29812 +1056 29812 +1057 29811 +1057 29811 +1057 29810 +1058 29810 +1058 29809 +1059 29809 +1059 29809 +1059 29808 +1060 29808 +1060 29807 +1061 29807 +1061 29807 +1062 29806 +1062 29806 +1062 29805 +1063 29805 +1063 29804 +1064 29804 +1064 29804 +1064 29803 +1065 29803 +1065 29802 +1066 29802 +1066 29802 +1067 29801 +1067 29801 +1067 29800 +1068 29800 +1068 29799 +1069 29799 +1069 29799 +1070 29798 +1070 29798 +1070 29797 +1071 29797 +1071 29796 +1072 29796 +1072 29795 +1072 29795 +1073 29794 +1073 29794 +1074 29793 +1074 29793 +1075 29793 +1075 29792 +1075 29792 +1076 29791 +1076 29791 +1077 29790 +1077 29790 +1078 29790 +1078 29789 +1078 29789 +1079 29788 +1079 29788 +1080 29787 +1080 29787 +1081 29787 +1081 29786 +1081 29786 +1082 29785 +1082 29785 +1083 29784 +1083 29784 +1084 29783 +1084 29783 +1084 29783 +1085 29782 +1085 29782 +1086 29781 +1086 29781 +1087 29780 +1087 29780 +1087 29779 +1088 29779 +1088 29779 +1089 29778 +1089 29778 +1090 29777 +1090 29777 +1091 29776 +1091 29776 +1091 29775 +1092 29775 +1092 29775 +1093 29774 +1093 29774 +1094 29773 +1094 29773 +1094 29773 +1095 29772 +1095 29772 +1096 29771 +1096 29771 +1097 29770 +1097 29770 +1098 29769 +1098 29769 +1098 29768 +1099 29768 +1099 29768 +1100 29767 +1100 29767 +1101 29766 +1101 29766 +1102 29765 +1102 29765 +1102 29764 +1103 29764 +1103 29763 +1104 29763 +1104 29762 +1105 29762 +1105 29761 +1106 29761 +1106 29761 +1106 29760 +1107 29760 +1107 29759 +1108 29759 +1108 29759 +1109 29758 +1109 29758 +1110 29757 +1110 29757 +1110 29756 +1111 29756 +1111 29755 +1112 29755 +1112 29754 +1113 29754 +1113 29753 +1114 29753 +1114 29753 +1115 29752 +1115 29752 +1115 29751 +1116 29751 +1116 29750 +1117 29750 +1117 29749 +1118 29749 +1118 29748 +1119 29748 +1119 29747 +1120 29747 +1120 29746 +1120 29746 +1121 29745 +1121 29745 +1122 29744 +1122 29744 +1123 29744 +1123 29743 +1124 29743 +1124 29742 +1125 29742 +1125 29742 +1125 29741 +1126 29741 +1126 29740 +1127 29740 +1127 29739 +1128 29739 +1128 29738 +1129 29737 +1129 29737 +1130 29737 +1130 29736 +1130 29735 +1131 29735 +1131 29734 +1132 29734 +1132 29733 +1133 29733 +1133 29733 +1134 29732 +1134 29732 +1135 29731 +1135 29731 +1136 29730 +1136 29730 +1137 29730 +1137 29729 +1137 29729 +1138 29728 +1138 29728 +1139 29727 +1139 29727 +1140 29726 +1140 29726 +1141 29725 +1141 29725 +1142 29724 +1142 29724 +1143 29723 +1143 29723 +1144 29723 +1144 29722 +1144 29722 +1145 29721 +1145 29721 +1146 29720 +1146 29720 +1147 29719 +1147 29719 +1148 29718 +1148 29718 +1149 29717 +1149 29717 +1150 29716 +1150 29716 +1151 29715 +1151 29715 +1152 29714 +1152 29714 +1153 29713 +1153 29713 +1153 29712 +1154 29712 +1154 29711 +1155 29711 +1155 29711 +1156 29710 +1156 29710 +1157 29709 +1157 29709 +1158 29708 +1158 29708 +1159 29707 +1159 29707 +1160 29706 +1160 29706 +1161 29705 +1161 29705 +1162 29704 +1162 29704 +1163 29703 +1163 29703 +1164 29702 +1164 29702 +1164 29701 +1165 29701 +1165 29700 +1166 29700 +1166 29699 +1167 29699 +1167 29698 +1168 29698 +1168 29697 +1169 29697 +1169 29696 +1170 29696 +1170 29695 +1171 29695 +1171 29694 +1172 29694 +1172 29693 +1173 29693 +1173 29692 +1174 29692 +1174 29691 +1175 29691 +1175 29690 +1176 29690 +1176 29689 +1177 29689 +1177 29688 +1178 29688 +1178 29687 +1179 29687 +1179 29686 +1180 29685 +1180 29685 +1181 29684 +1181 29684 +1182 29683 +1182 29683 +1183 29682 +1183 29682 +1184 29681 +1184 29681 +1185 29680 +1185 29680 +1186 29679 +1186 29679 +1187 29678 +1187 29678 +1188 29677 +1188 29677 +1189 29676 +1189 29676 +1190 29675 +1190 29675 +1191 29674 +1191 29674 +1192 29673 +1192 29672 +1193 29672 +1193 29671 +1194 29671 +1194 29670 +1195 29670 +1195 29670 +1196 29669 +1196 29669 +1197 29668 +1197 29668 +1198 29667 +1198 29667 +1199 29666 +1199 29665 +1200 29665 +1200 29664 +1201 29664 +1201 29663 +1202 29663 +1202 29662 +1203 29662 +1203 29661 +1204 29661 +1204 29660 +1205 29660 +1205 29659 +1206 29659 +1206 29658 +1207 29658 +1207 29657 +1208 29656 +1208 29656 +1209 29655 +1209 29655 +1210 29654 +1210 29654 +1211 29653 +1211 29653 +1212 29652 +1212 29652 +1213 29652 +1213 29651 +1214 29650 +1214 29650 +1215 29649 +1215 29649 +1216 29649 +1217 29648 +1217 29648 +1218 29647 +1218 29647 +1219 29646 +1219 29646 +1220 29645 +1220 29644 +1221 29644 +1221 29643 +1222 29643 +1222 29642 +1223 29642 +1223 29641 +1224 29641 +1224 29640 +1225 29640 +1225 29639 +1226 29639 +1226 29638 +1227 29637 +1227 29637 +1228 29636 +1229 29636 +1229 29635 +1230 29635 +1230 29634 +1231 29634 +1231 29633 +1232 29633 +1232 29632 +1233 29632 +1233 29631 +1234 29630 +1234 29630 +1235 29629 +1235 29629 +1236 29628 +1236 29628 +1237 29627 +1238 29627 +1238 29626 +1239 29626 +1239 29625 +1240 29624 +1240 29624 +1241 29623 +1241 29623 +1242 29622 +1242 29622 +1243 29621 +1243 29621 +1244 29620 +1244 29619 +1245 29619 +1246 29618 +1246 29618 +1247 29617 +1247 29617 +1248 29616 +1248 29616 +1249 29615 +1249 29614 +1250 29614 +1250 29613 +1251 29613 +1251 29612 +1252 29612 +1253 29611 +1253 29610 +1254 29610 +1254 29609 +1255 29609 +1255 29608 +1256 29608 +1256 29607 +1257 29607 +1257 29606 +1258 29605 +1258 29605 +1259 29604 +1260 29604 +1260 29603 +1261 29603 +1261 29602 +1262 29601 +1262 29601 +1263 29600 +1263 29600 +1264 29599 +1264 29599 +1265 29598 +1266 29597 +1266 29597 +1267 29596 +1267 29596 +1268 29595 +1268 29595 +1269 29594 +1269 29593 +1270 29593 +1271 29592 +1271 29592 +1272 29591 +1272 29591 +1273 29590 +1273 29589 +1274 29589 +1274 29588 +1275 29588 +1275 29587 +1276 29587 +1277 29586 +1277 29585 +1278 29585 +1278 29584 +1279 29584 +1279 29584 +1280 29583 +1280 29582 +1281 29582 +1282 29581 +1282 29581 +1283 29580 +1283 29580 +1284 29579 +1284 29578 +1285 29578 +1285 29577 +1286 29577 +1287 29576 +1287 29575 +1288 29575 +1288 29574 +1289 29574 +1289 29573 +1290 29573 +1291 29572 +1291 29571 +1292 29571 +1292 29570 +1293 29570 +1293 29569 +1294 29569 +1294 29568 +1295 29568 +1296 29567 +1296 29567 +1297 29566 +1297 29565 +1298 29565 +1298 29564 +1299 29564 +1300 29563 +1300 29562 +1301 29562 +1301 29561 +1302 29561 +1302 29560 +1303 29560 +1303 29559 +1304 29558 +1305 29558 +1305 29557 +1306 29557 +1306 29556 +1307 29556 +1307 29555 +1308 29554 +1309 29554 +1309 29553 +1310 29553 +1310 29552 +1311 29551 +1311 29551 +1312 29550 +1313 29550 +1313 29549 +1314 29549 +1314 29548 +1315 29547 +1315 29547 +1316 29546 +1317 29546 +1317 29545 +1318 29545 +1318 29544 +1319 29544 +1319 29543 +1320 29542 +1321 29542 +1321 29541 +1322 29541 +1322 29540 +1323 29540 +1323 29539 +1324 29539 +1325 29538 +1325 29538 +1326 29537 +1326 29536 +1327 29536 +1328 29535 +1328 29535 +1329 29534 +1329 29533 +1330 29533 +1330 29532 +1331 29532 +1332 29531 +1332 29531 +1333 29530 +1333 29529 +1334 29529 +1335 29528 +1335 29528 +1336 29527 +1336 29527 +1337 29526 +1337 29526 +1338 29525 +1339 29524 +1339 29524 +1340 29523 +1340 29523 +1341 29522 +1342 29522 +1342 29521 +1343 29520 +1343 29520 +1344 29519 +1344 29519 +1345 29518 +1346 29517 +1346 29517 +1347 29516 +1347 29516 +1348 29515 +1349 29515 +1349 29514 +1350 29513 +1350 29513 +1351 29512 +1352 29512 +1352 29511 +1353 29510 +1353 29510 +1354 29509 +1354 29509 +1355 29508 +1356 29508 +1356 29507 +1357 29507 +1357 29506 +1358 29505 +1359 29505 +1359 29504 +1360 29504 +1360 29503 +1361 29502 +1362 29502 +1362 29501 +1363 29501 +1363 29500 +1364 29500 +1365 29499 +1365 29498 +1366 29498 +1366 29497 +1367 29497 +1368 29496 +1368 29495 +1369 29495 +1369 29494 +1370 29494 +1371 29493 +1371 29492 +1372 29492 +1372 29491 +1373 29491 +1374 29490 +1374 29489 +1375 29489 +1375 29488 +1376 29488 +1377 29487 +1377 29486 +1378 29486 +1378 29485 +1379 29485 +1380 29484 +1380 29483 +1381 29483 +1381 29482 +1382 29482 +1383 29481 +1383 29480 +1384 29480 +1384 29480 +1385 29479 +1386 29478 +1386 29478 +1387 29477 +1387 29477 +1388 29476 +1389 29475 +1389 29475 +1390 29474 +1390 29474 +1391 29473 +1392 29473 +1392 29472 +1393 29472 +1393 29471 +1394 29470 +1395 29470 +1395 29469 +1396 29469 +1396 29468 +1397 29467 +1398 29467 +1398 29466 +1399 29466 +1399 29465 +1400 29465 +1401 29464 +1401 29463 +1402 29463 +1402 29462 +1403 29462 +1404 29461 +1404 29460 +1405 29460 +1405 29459 +1406 29459 +1407 29458 +1407 29457 +1408 29457 +1408 29456 +1409 29456 +1410 29455 +1410 29454 +1411 29454 +1411 29453 +1412 29453 +1413 29452 +1413 29451 +1414 29451 +1415 29450 +1415 29450 +1416 29449 +1416 29448 +1417 29448 +1418 29447 +1418 29447 +1419 29446 +1419 29445 +1420 29445 +1421 29444 +1421 29443 +1422 29443 +1422 29442 +1423 29442 +1424 29441 +1424 29440 +1425 29440 +1426 29439 +1426 29439 +1427 29438 +1427 29437 +1428 29437 +1429 29436 +1429 29436 +1430 29435 +1430 29434 +1431 29434 +1432 29433 +1432 29433 +1433 29432 +1434 29431 +1434 29431 +1435 29430 +1435 29430 +1436 29429 +1437 29428 +1437 29428 +1438 29427 +1438 29426 +1439 29426 +1440 29425 +1440 29425 +1441 29424 +1441 29423 +1442 29423 +1443 29422 +1443 29422 +1444 29421 +1445 29420 +1445 29420 +1446 29419 +1446 29419 +1447 29418 +1448 29417 +1448 29417 +1449 29416 +1449 29415 +1450 29415 +1451 29414 +1451 29414 +1452 29413 +1453 29412 +1453 29412 +1454 29411 +1454 29411 +1455 29410 +1456 29409 +1456 29409 +1457 29408 +1458 29408 +1458 29407 +1459 29406 +1459 29406 +1460 29405 +1461 29404 +1461 29404 +1462 29403 +1462 29403 +1463 29402 +1464 29401 +1464 29401 +1465 29400 +1466 29399 +1466 29399 +1467 29398 +1467 29398 +1468 29397 +1469 29396 +1469 29396 +1470 29395 +1470 29394 +1471 29394 +1472 29393 +1472 29393 +1473 29392 +1474 29391 +1474 29391 +1475 29390 +1475 29390 +1476 29389 +1477 29388 +1477 29388 +1478 29387 +1479 29386 +1479 29386 +1480 29385 +1480 29385 +1481 29384 +1482 29383 +1482 29383 +1483 29382 +1484 29381 +1484 29381 +1485 29380 +1485 29380 +1486 29379 +1487 29378 +1487 29378 +1488 29377 +1489 29376 +1489 29376 +1490 29375 +1490 29374 +1491 29374 +1492 29373 +1492 29373 +1493 29372 +1494 29371 +1494 29371 +1495 29370 +1495 29369 +1496 29369 +1497 29368 +1497 29368 +1498 29367 +1499 29366 +1499 29366 +1500 29365 +1500 29365 +1501 29364 +1502 29364 +1502 29363 +1503 29362 +1503 29362 +1504 29361 +1505 29361 +1505 29360 +1506 29360 +1507 29359 +1507 29358 +1508 29358 +1508 29357 +1509 29356 +1510 29356 +1510 29355 +1511 29355 +1512 29354 +1512 29353 +1513 29353 +1514 29352 +1514 29351 +1515 29351 +1515 29350 +1516 29350 +1517 29349 +1517 29348 +1518 29348 +1519 29347 +1519 29346 +1520 29346 +1520 29345 +1521 29344 +1522 29344 +1522 29343 +1523 29343 +1524 29342 +1524 29341 +1525 29341 +1525 29340 +1526 29339 +1527 29339 +1527 29338 +1528 29338 +1529 29337 +1529 29336 +1530 29336 +1530 29335 +1531 29334 +1532 29334 +1532 29333 +1533 29332 +1534 29332 +1534 29331 +1535 29331 +1536 29330 +1536 29329 +1537 29329 +1537 29328 +1538 29327 +1539 29327 +1539 29326 +1540 29325 +1541 29325 +1541 29324 +1542 29324 +1542 29323 +1543 29322 +1544 29322 +1544 29321 +1545 29320 +1546 29320 +1546 29319 +1547 29319 +1548 29318 +1548 29317 +1549 29317 +1549 29316 +1550 29315 +1551 29315 +1551 29314 +1552 29313 +1553 29313 +1553 29312 +1554 29312 +1554 29311 +1555 29310 +1556 29310 +1556 29309 +1557 29308 +1558 29308 +1558 29307 +1559 29306 +1560 29306 +1560 29305 +1561 29305 +1561 29304 +1562 29303 +1563 29303 +1563 29302 +1564 29301 +1565 29301 +1565 29300 +1566 29299 +1567 29299 +1567 29298 +1568 29298 +1568 29297 +1569 29297 +1570 29296 +1570 29295 +1571 29295 +1572 29294 +1572 29293 +1573 29293 +1574 29292 +1574 29291 +1575 29291 +1575 29290 +1576 29290 +1577 29289 +1577 29288 +1578 29288 +1579 29287 +1579 29286 +1580 29286 +1581 29285 +1581 29284 +1582 29284 +1582 29283 +1583 29282 +1584 29282 +1584 29281 +1585 29281 +1586 29280 +1586 29279 +1587 29279 +1588 29278 +1588 29277 +1589 29277 +1589 29276 +1590 29275 +1591 29275 +1591 29274 +1592 29273 +1593 29273 +1593 29272 +1594 29272 +1595 29271 +1595 29270 +1596 29270 +1597 29269 +1597 29268 +1598 29268 +1598 29267 +1599 29266 +1600 29266 +1600 29265 +1601 29264 +1602 29264 +1602 29263 +1603 29263 +1604 29262 +1604 29261 +1605 29261 +1605 29260 +1606 29259 +1607 29259 +1607 29258 +1608 29257 +1609 29257 +1609 29256 +1610 29255 +1611 29255 +1611 29254 +1612 29254 +1612 29253 +1613 29252 +1614 29252 +1614 29251 +1615 29250 +1616 29250 +1616 29249 +1617 29248 +1618 29248 +1618 29247 +1619 29246 +1620 29246 +1620 29245 +1621 29244 +1621 29244 +1622 29243 +1623 29243 +1623 29242 +1624 29241 +1625 29241 +1625 29240 +1626 29239 +1627 29239 +1627 29238 +1628 29237 +1629 29237 +1629 29236 +1630 29235 +1630 29235 +1631 29234 +1632 29233 +1632 29233 +1633 29232 +1634 29231 +1634 29231 +1635 29230 +1636 29229 +1636 29229 +1637 29228 +1638 29227 +1638 29227 +1639 29226 +1639 29225 +1640 29225 +1641 29224 +1641 29224 +1642 29223 +1643 29222 +1643 29222 +1644 29221 +1645 29220 +1645 29220 +1646 29219 +1647 29218 +1647 29218 +1648 29217 +1649 29216 +1649 29216 +1650 29215 +1650 29215 +1651 29214 +1652 29214 +1652 29213 +1653 29213 +1654 29212 +1654 29211 +1655 29211 +1656 29210 +1656 29209 +1657 29209 +1658 29208 +1658 29207 +1659 29207 +1660 29206 +1660 29205 +1661 29205 +1661 29204 +1662 29203 +1663 29203 +1663 29202 +1664 29201 +1665 29201 +1665 29200 +1666 29199 +1667 29198 +1667 29198 +1668 29197 +1669 29197 +1669 29196 +1670 29195 +1671 29195 +1671 29194 +1672 29193 +1672 29193 +1673 29192 +1674 29191 +1674 29191 +1675 29190 +1676 29189 +1676 29189 +1677 29188 +1678 29187 +1678 29187 +1679 29186 +1680 29185 +1680 29185 +1681 29184 +1682 29183 +1682 29183 +1683 29182 +1684 29181 +1684 29181 +1685 29180 +1685 29179 +1686 29179 +1687 29178 +1687 29177 +1688 29177 +1689 29176 +1689 29175 +1690 29175 +1691 29174 +1691 29174 +1692 29173 +1693 29172 +1693 29172 +1694 29171 +1695 29170 +1695 29170 +1696 29169 +1697 29168 +1697 29168 +1698 29167 +1699 29166 +1699 29166 +1700 29165 +1701 29164 +1701 29164 +1702 29163 +1702 29162 +1703 29162 +1704 29161 +1704 29160 +1705 29160 +1706 29159 +1706 29158 +1707 29158 +1708 29157 +1708 29156 +1709 29156 +1710 29155 +1710 29154 +1711 29154 +1712 29153 +1712 29152 +1713 29152 +1714 29151 +1714 29150 +1715 29150 +1716 29149 +1716 29149 +1717 29148 +1718 29147 +1718 29147 +1719 29146 +1720 29145 +1720 29144 +1721 29144 +1722 29143 +1722 29143 +1723 29142 +1723 29141 +1724 29141 +1725 29140 +1725 29139 +1726 29139 +1727 29138 +1727 29137 +1728 29137 +1729 29136 +1729 29135 +1730 29135 +1731 29134 +1731 29133 +1732 29133 +1733 29132 +1733 29131 +1734 29131 +1735 29130 +1735 29129 +1736 29128 +1737 29128 +1737 29127 +1738 29126 +1739 29126 +1739 29125 +1740 29124 +1741 29124 +1741 29123 +1742 29122 +1743 29122 +1743 29121 +1744 29120 +1745 29120 +1745 29119 +1746 29118 +1747 29118 +1747 29117 +1748 29116 +1749 29116 +1749 29115 +1750 29114 +1751 29114 +1751 29113 +1752 29112 +1753 29111 +1753 29111 +1754 29110 +1755 29109 +1755 29109 +1756 29108 +1757 29107 +1757 29107 +1758 29106 +1759 29105 +1759 29105 +1760 29104 +1760 29103 +1761 29103 +1762 29102 +1762 29101 +1763 29101 +1764 29100 +1764 29099 +1765 29098 +1766 29098 +1766 29097 +1767 29096 +1768 29096 +1768 29095 +1769 29094 +1770 29094 +1770 29093 +1771 29092 +1772 29092 +1772 29091 +1773 29090 +1774 29090 +1774 29089 +1775 29088 +1776 29088 +1776 29087 +1777 29086 +1778 29085 +1778 29085 +1779 29084 +1780 29083 +1780 29083 +1781 29082 +1782 29081 +1782 29081 +1783 29080 +1784 29079 +1784 29079 +1785 29078 +1786 29077 +1786 29077 +1787 29076 +1788 29075 +1788 29074 +1789 29074 +1790 29073 +1790 29072 +1791 29072 +1792 29071 +1792 29070 +1793 29070 +1794 29069 +1794 29068 +1795 29068 +1796 29067 +1796 29066 +1797 29066 +1798 29065 +1798 29064 +1799 29063 +1800 29063 +1800 29062 +1801 29061 +1802 29061 +1802 29060 +1803 29059 +1804 29059 +1804 29058 +1805 29057 +1806 29056 +1806 29056 +1807 29055 +1808 29054 +1808 29054 +1809 29053 +1810 29052 +1810 29052 +1811 29051 +1812 29050 +1812 29049 +1813 29049 +1814 29048 +1814 29047 +1815 29047 +1816 29046 +1816 29045 +1817 29045 +1818 29044 +1818 29043 +1819 29042 +1820 29042 +1820 29041 +1821 29040 +1822 29040 +1822 29039 +1823 29038 +1824 29038 +1824 29037 +1825 29036 +1826 29035 +1826 29035 +1827 29034 +1828 29033 +1828 29033 +1829 29032 +1830 29031 +1831 29031 +1831 29030 +1832 29029 +1833 29028 +1833 29028 +1834 29027 +1835 29026 +1835 29026 +1836 29025 +1837 29024 +1837 29024 +1838 29023 +1839 29022 +1839 29021 +1840 29021 +1841 29020 +1841 29019 +1842 29019 +1843 29018 +1843 29017 +1844 29016 +1845 29016 +1845 29015 +1846 29014 +1847 29014 +1847 29013 +1848 29012 +1849 29012 +1850 29011 +1850 29010 +1851 29009 +1852 29009 +1852 29008 +1853 29007 +1854 29007 +1854 29006 +1855 29005 +1856 29004 +1856 29004 +1857 29003 +1858 29002 +1858 29002 +1859 29001 +1860 29000 +1860 28999 +1861 28999 +1862 28998 +1862 28997 +1863 28997 +1864 28996 +1865 28995 +1865 28994 +1866 28994 +1867 28993 +1867 28992 +1868 28992 +1869 28991 +1869 28990 +1870 28989 +1871 28989 +1871 28988 +1872 28987 +1873 28987 +1873 28986 +1874 28985 +1875 28984 +1876 28984 +1876 28983 +1877 28982 +1878 28982 +1878 28981 +1879 28980 +1880 28979 +1880 28979 +1881 28978 +1882 28977 +1882 28977 +1883 28976 +1884 28975 +1885 28974 +1885 28974 +1886 28973 +1887 28972 +1887 28971 +1888 28970 +1889 28970 +1889 28969 +1890 28968 +1891 28967 +1892 28967 +1892 28966 +1893 28965 +1894 28965 +1894 28964 +1895 28963 +1896 28962 +1897 28962 +1897 28961 +1898 28960 +1899 28959 +1899 28959 +1900 28958 +1901 28957 +1901 28957 +1902 28956 +1903 28955 +1904 28954 +1904 28954 +1905 28953 +1906 28952 +1906 28951 +1907 28951 +1908 28950 +1908 28949 +1909 28948 +1910 28948 +1911 28947 +1911 28946 +1912 28945 +1913 28945 +1913 28944 +1914 28943 +1915 28942 +1915 28942 +1916 28941 +1917 28940 +1918 28939 +1918 28939 +1919 28938 +1920 28937 +1920 28936 +1921 28936 +1922 28935 +1923 28934 +1923 28933 +1924 28933 +1925 28932 +1925 28931 +1926 28930 +1927 28930 +1927 28929 +1928 28928 +1929 28927 +1930 28927 +1930 28926 +1931 28925 +1932 28924 +1932 28924 +1933 28923 +1934 28922 +1935 28921 +1935 28921 +1936 28920 +1937 28919 +1937 28918 +1938 28918 +1939 28917 +1940 28916 +1940 28915 +1941 28915 +1942 28914 +1942 28913 +1943 28912 +1944 28911 +1945 28911 +1945 28910 +1946 28909 +1947 28908 +1947 28908 +1948 28907 +1949 28906 +1950 28905 +1950 28905 +1951 28904 +1952 28903 +1953 28902 +1953 28902 +1954 28901 +1955 28900 +1955 28899 +1956 28898 +1957 28898 +1958 28897 +1958 28896 +1959 28895 +1960 28895 +1960 28894 +1961 28893 +1962 28892 +1963 28892 +1963 28891 +1964 28890 +1965 28889 +1966 28888 +1966 28888 +1967 28887 +1968 28886 +1969 28885 +1969 28885 +1970 28884 +1971 28883 +1971 28882 +1972 28882 +1973 28881 +1974 28880 +1974 28879 +1975 28879 +1976 28878 +1977 28877 +1977 28876 +1978 28875 +1979 28875 +1980 28874 +1980 28873 +1981 28872 +1982 28872 +1983 28871 +1983 28870 +1984 28869 +1985 28868 +1985 28868 +1986 28867 +1987 28866 +1988 28865 +1988 28865 +1989 28864 +1990 28863 +1991 28862 +1991 28861 +1992 28861 +1993 28860 +1993 28859 +1994 28858 +1995 28858 +1996 28857 +1996 28856 +1997 28855 +1998 28854 +1999 28854 +1999 28853 +2000 28852 +2001 28851 +2002 28850 +2002 28850 +2003 28849 +2004 28848 +2004 28847 +2005 28847 +2006 28846 +2007 28845 +2007 28844 +2008 28843 +2009 28842 +2010 28842 +2010 28841 +2011 28840 +2012 28839 +2013 28839 +2013 28838 +2014 28837 +2015 28836 +2016 28835 +2016 28835 +2017 28834 +2018 28833 +2019 28832 +2019 28831 +2020 28831 +2021 28830 +2022 28829 +2022 28828 +2023 28827 +2024 28827 +2025 28826 +2025 28825 +2026 28824 +2027 28823 +2028 28822 +2029 28822 +2029 28821 +2030 28820 +2031 28819 +2032 28818 +2032 28818 +2033 28817 +2034 28816 +2035 28815 +2035 28814 +2036 28814 +2037 28813 +2038 28812 +2038 28811 +2039 28810 +2040 28809 +2041 28809 +2042 28808 +2042 28807 +2043 28806 +2044 28805 +2045 28805 +2045 28804 +2046 28803 +2047 28802 +2048 28801 +2049 28800 +2049 28800 +2050 28799 +2051 28798 +2052 28797 +2052 28796 +2053 28795 +2054 28795 +2055 28794 +2056 28793 +2056 28792 +2057 28791 +2058 28790 +2059 28790 +2060 28789 +2060 28788 +2061 28787 +2062 28786 +2063 28785 +2064 28785 +2064 28784 +2065 28783 +2066 28782 +2067 28781 +2068 28780 +2068 28780 +2069 28779 +2070 28778 +2071 28777 +2072 28776 +2072 28775 +2073 28774 +2074 28774 +2075 28773 +2076 28772 +2076 28771 +2077 28770 +2078 28769 +2079 28769 +2080 28768 +2080 28767 +2081 28766 +2082 28765 +2083 28764 +2084 28763 +2085 28763 +2085 28762 +2086 28761 +2087 28760 +2088 28759 +2089 28758 +2090 28757 +2090 28757 +2091 28756 +2092 28755 +2093 28754 +2094 28753 +2094 28752 +2095 28751 +2096 28751 +2097 28750 +2098 28749 +2099 28748 +2099 28747 +2100 28746 +2101 28745 +2102 28744 +2103 28744 +2104 28743 +2104 28742 +2105 28741 +2106 28740 +2107 28739 +2108 28738 +2109 28738 +2110 28737 +2110 28736 +2111 28735 +2112 28734 +2113 28733 +2114 28732 +2115 28731 +2115 28731 +2116 28730 +2117 28729 +2118 28728 +2119 28727 +2120 28726 +2121 28725 +2121 28724 +2122 28724 +2123 28723 +2124 28722 +2125 28721 +2126 28720 +2127 28719 +2127 28718 +2128 28717 +2129 28716 +2130 28716 +2131 28715 +2132 28714 +2133 28713 +2133 28712 +2134 28711 +2135 28710 +2136 28709 +2137 28708 +2138 28708 +2139 28707 +2140 28706 +2140 28705 +2141 28704 +2142 28703 +2143 28702 +2144 28701 +2145 28700 +2146 28699 +2147 28699 +2147 28698 +2148 28697 +2149 28696 +2150 28695 +2151 28694 +2152 28693 +2153 28692 +2154 28691 +2155 28690 +2155 28690 +2156 28689 +2157 28688 +2158 28687 +2159 28686 +2160 28685 +2161 28684 +2162 28683 +2163 28682 +2163 28681 +2164 28680 +2165 28680 +2166 28679 +2167 28678 +2168 28677 +2169 28676 +2170 28675 +2171 28674 +2172 28673 +2172 28672 +2173 28671 +2174 28670 +2175 28669 +2176 28669 +2177 28668 +2178 28667 +2179 28666 +2180 28665 +2181 28664 +2182 28663 +2182 28662 +2183 28661 +2184 28660 +2185 28659 +2186 28658 +2187 28657 +2188 28656 +2189 28656 +2190 28655 +2191 28654 +2192 28653 +2193 28652 +2194 28651 +2194 28650 +2195 28649 +2196 28648 +2197 28647 +2198 28646 +2199 28645 +2200 28644 +2201 28643 +2202 28643 +2203 28642 +2204 28641 +2205 28640 +2206 28639 +2207 28638 +2208 28637 +2208 28636 +2209 28635 +2210 28634 +2211 28633 +2212 28632 +2213 28631 +2214 28630 +2215 28630 +2216 28629 +2217 28628 +2218 28627 +2219 28626 +2220 28625 +2221 28624 +2222 28623 +2223 28622 +2224 28621 +2225 28620 +2226 28619 +2227 28618 +2228 28617 +2229 28616 +2230 28616 +2231 28615 +2232 28614 +2233 28613 +2234 28612 +2235 28611 +2236 28610 +2237 28609 +2237 28608 +2238 28607 +2239 28606 +2240 28605 +2241 28604 +2242 28603 +2243 28602 +2244 28601 +2245 28600 +2246 28599 +2247 28599 +2248 28598 +2249 28597 +2250 28596 +2251 28595 +2252 28594 +2253 28593 +2254 28592 +2255 28591 +2256 28590 +2257 28589 +2258 28588 +2259 28587 +2260 28586 +2261 28585 +2262 28584 +2263 28583 +2264 28582 +2265 28581 +2266 28580 +2267 28579 +2268 28578 +2269 28577 +2270 28577 +2271 28576 +2272 28575 +2273 28574 +2274 28573 +2275 28572 +2276 28571 +2277 28570 +2278 28569 +2279 28568 +2280 28567 +2281 28566 +2282 28565 +2283 28564 +2284 28563 +2285 28562 +2286 28561 +2287 28560 +2288 28559 +2289 28558 +2290 28557 +2291 28556 +2292 28555 +2293 28554 +2294 28553 +2295 28552 +2296 28552 +2297 28551 +2299 28550 +2300 28549 +2301 28548 +2302 28547 +2303 28546 +2304 28545 +2305 28544 +2306 28543 +2307 28542 +2308 28541 +2309 28540 +2310 28539 +2311 28538 +2312 28537 +2313 28536 +2314 28535 +2315 28534 +2316 28533 +2317 28532 +2318 28531 +2319 28530 +2320 28529 +2321 28528 +2322 28527 +2323 28526 +2324 28525 +2325 28524 +2326 28523 +2327 28522 +2328 28521 +2329 28520 +2330 28519 +2331 28518 +2333 28517 +2334 28516 +2335 28515 +2336 28514 +2337 28513 +2338 28512 +2339 28511 +2340 28510 +2341 28509 +2342 28508 +2343 28507 +2344 28506 +2345 28505 +2346 28504 +2347 28503 +2348 28502 +2349 28501 +2350 28500 +2351 28499 +2352 28498 +2353 28497 +2355 28496 +2356 28495 +2357 28494 +2358 28493 +2359 28492 +2360 28491 +2361 28490 +2362 28489 +2363 28488 +2364 28487 +2365 28486 +2366 28485 +2367 28484 +2368 28483 +2369 28482 +2370 28481 +2372 28481 +2373 28480 +2374 28479 +2375 28478 +2376 28477 +2377 28476 +2378 28475 +2379 28473 +2380 28472 +2381 28471 +2382 28470 +2383 28469 +2384 28468 +2386 28467 +2387 28466 +2388 28465 +2389 28464 +2390 28463 +2391 28462 +2392 28461 +2393 28460 +2394 28459 +2395 28458 +2396 28457 +2397 28456 +2398 28455 +2400 28454 +2401 28453 +2402 28452 +2403 28451 +2404 28450 +2405 28449 +2406 28448 +2407 28447 +2408 28446 +2409 28445 +2410 28444 +2412 28443 +2413 28442 +2414 28441 +2415 28440 +2416 28439 +2417 28438 +2418 28437 +2419 28436 +2420 28435 +2421 28434 +2423 28433 +2424 28432 +2425 28431 +2426 28430 +2427 28429 +2428 28428 +2429 28427 +2430 28426 +2431 28425 +2432 28423 +2434 28422 +2435 28421 +2436 28420 +2437 28419 +2438 28418 +2439 28417 +2440 28416 +2441 28415 +2442 28414 +2444 28413 +2445 28412 +2446 28411 +2447 28410 +2448 28409 +2449 28408 +2450 28407 +2451 28406 +2452 28404 +2454 28403 +2455 28402 +2456 28401 +2457 28400 +2458 28399 +2459 28398 +2460 28397 +2461 28396 +2463 28395 +2464 28394 +2465 28393 +2466 28392 +2467 28391 +2468 28390 +2469 28389 +2470 28388 +2472 28386 +2473 28385 +2474 28384 +2475 28383 +2476 28382 +2477 28381 +2478 28380 +2479 28379 +2481 28378 +2482 28377 +2483 28376 +2484 28375 +2485 28374 +2486 28373 +2487 28371 +2489 28370 +2490 28369 +2491 28368 +2492 28367 +2493 28366 +2494 28365 +2495 28364 +2497 28363 +2498 28362 +2499 28361 +2500 28360 +2501 28359 +2502 28358 +2503 28357 +2505 28356 +2506 28354 +2507 28353 +2508 28352 +2509 28351 +2510 28350 +2511 28349 +2513 28348 +2514 28347 +2515 28346 +2516 28345 +2517 28344 +2518 28343 +2520 28341 +2521 28340 +2522 28339 +2523 28338 +2524 28337 +2525 28336 +2527 28335 +2528 28334 +2529 28333 +2530 28332 +2531 28331 +2532 28329 +2533 28328 +2535 28327 +2536 28326 +2537 28325 +2538 28324 +2539 28323 +2540 28322 +2542 28321 +2543 28320 +2544 28318 +2545 28317 +2546 28316 +2548 28315 +2549 28314 +2550 28313 +2551 28312 +2552 28311 +2553 28310 +2555 28308 +2556 28307 +2557 28306 +2558 28305 +2559 28304 +2561 28303 +2562 28302 +2563 28301 +2564 28300 +2565 28298 +2566 28297 +2568 28296 +2569 28295 +2570 28294 +2571 28293 +2572 28292 +2574 28290 +2575 28289 +2576 28288 +2577 28287 +2578 28286 +2580 28285 +2581 28284 +2582 28283 +2583 28281 +2584 28280 +2586 28279 +2587 28278 +2588 28277 +2589 28276 +2590 28274 +2592 28273 +2593 28272 +2594 28271 +2595 28270 +2596 28269 +2598 28267 +2599 28266 +2600 28265 +2601 28264 +2602 28263 +2604 28262 +2605 28260 +2606 28259 +2607 28258 +2608 28257 +2610 28256 +2611 28255 +2612 28253 +2613 28252 +2615 28251 +2616 28250 +2617 28249 +2618 28248 +2619 28246 +2621 28245 +2622 28244 +2623 28243 +2624 28242 +2626 28241 +2627 28239 +2628 28238 +2629 28237 +2630 28236 +2632 28235 +2633 28233 +2634 28232 +2635 28231 +2637 28230 +2638 28229 +2639 28228 +2640 28226 +2642 28225 +2643 28224 +2644 28223 +2645 28222 +2647 28220 +2648 28219 +2649 28218 +2650 28217 +2651 28216 +2653 28214 +2654 28213 +2655 28212 +2656 28211 +2658 28209 +2659 28208 +2660 28207 +2661 28206 +2663 28205 +2664 28203 +2665 28202 +2666 28201 +2668 28200 +2669 28198 +2670 28197 +2672 28196 +2673 28195 +2674 28194 +2675 28192 +2677 28191 +2678 28190 +2679 28189 +2680 28187 +2682 28186 +2683 28185 +2684 28184 +2685 28182 +2687 28181 +2688 28180 +2689 28179 +2690 28177 +2692 28176 +2693 28175 +2694 28174 +2696 28172 +2697 28171 +2698 28170 +2699 28169 +2701 28167 +2702 28166 +2703 28165 +2705 28163 +2706 28162 +2707 28161 +2708 28160 +2710 28158 +2711 28157 +2712 28156 +2714 28155 +2715 28153 +2716 28152 +2717 28151 +2719 28149 +2720 28148 +2721 28147 +2722 28146 +2724 28144 +2725 28143 +2726 28142 +2728 28140 +2729 28139 +2730 28138 +2731 28137 +2733 28135 +2734 28134 +2735 28133 +2737 28131 +2738 28130 +2739 28129 +2741 28128 +2742 28126 +2743 28125 +2744 28124 +2746 28122 +2747 28121 +2748 28120 +2750 28118 +2751 28117 +2752 28116 +2754 28114 +2755 28113 +2756 28112 +2758 28110 +2759 28109 +2760 28108 +2762 28107 +2763 28105 +2764 28104 +2766 28103 +2767 28102 +2768 28100 +2770 28099 +2771 28098 +2772 28096 +2774 28095 +2775 28093 +2776 28092 +2778 28091 +2779 28089 +2780 28088 +2782 28087 +2783 28085 +2784 28084 +2786 28083 +2787 28081 +2789 28080 +2790 28079 +2791 28077 +2793 28076 +2794 28075 +2795 28073 +2797 28072 +2798 28071 +2799 28069 +2801 28068 +2802 28066 +2803 28065 +2805 28064 +2806 28062 +2808 28061 +2809 28060 +2810 28058 +2812 28057 +2813 28056 +2814 28054 +2816 28053 +2817 28052 +2819 28050 +2820 28049 +2821 28048 +2823 28046 +2824 28045 +2825 28043 +2827 28042 +2828 28041 +2830 28039 +2831 28038 +2832 28037 +2834 28035 +2835 28034 +2837 28032 +2838 28031 +2839 28030 +2841 28028 +2842 28027 +2844 28025 +2845 28024 +2846 28023 +2848 28021 +2849 28020 +2851 28018 +2852 28017 +2853 28016 +2855 28014 +2856 28013 +2858 28011 +2859 28010 +2861 28009 +2862 28007 +2863 28006 +2865 28004 +2866 28003 +2868 28001 +2869 28000 +2870 27999 +2872 27997 +2873 27996 +2875 27994 +2876 27993 +2878 27992 +2879 27990 +2880 27989 +2882 27987 +2883 27986 +2885 27984 +2886 27983 +2888 27982 +2889 27980 +2891 27979 +2892 27977 +2893 27976 +2895 27974 +2896 27973 +2898 27971 +2899 27970 +2901 27969 +2902 27967 +2904 27966 +2905 27964 +2906 27963 +2908 27961 +2909 27960 +2911 27958 +2912 27957 +2914 27955 +2915 27954 +2916 27952 +2918 27951 +2919 27950 +2921 27948 +2922 27947 +2924 27945 +2925 27944 +2927 27942 +2928 27941 +2930 27939 +2931 27938 +2933 27936 +2934 27935 +2936 27933 +2937 27932 +2939 27930 +2940 27929 +2941 27927 +2943 27926 +2944 27924 +2946 27923 +2947 27921 +2949 27920 +2950 27918 +2952 27917 +2953 27916 +2955 27914 +2956 27913 +2958 27911 +2959 27909 +2961 27908 +2963 27906 +2964 27905 +2966 27903 +2967 27902 +2969 27900 +2970 27899 +2972 27897 +2973 27896 +2975 27894 +2976 27893 +2978 27891 +2979 27890 +2981 27888 +2982 27887 +2984 27885 +2986 27884 +2987 27882 +2989 27881 +2990 27879 +2992 27878 +2993 27876 +2995 27875 +2996 27873 +2998 27871 +3000 27870 +3001 27868 +3003 27867 +3004 27865 +3006 27864 +3007 27862 +3009 27861 +3010 27859 +3012 27858 +3014 27856 +3015 27855 +3017 27853 +3018 27852 +3020 27850 +3021 27848 +3023 27847 +3025 27845 +3026 27844 +3028 27842 +3029 27841 +3031 27839 +3033 27837 +3034 27836 +3036 27834 +3037 27833 +3039 27831 +3041 27830 +3042 27828 +3044 27826 +3045 27825 +3047 27823 +3049 27822 +3050 27820 +3052 27819 +3053 27817 +3055 27815 +3057 27814 +3058 27812 +3060 27811 +3061 27809 +3063 27808 +3065 27806 +3066 27804 +3068 27803 +3070 27801 +3071 27800 +3073 27798 +3074 27796 +3076 27795 +3078 27793 +3079 27792 +3081 27790 +3083 27788 +3084 27787 +3086 27785 +3087 27784 +3089 27782 +3091 27780 +3092 27779 +3094 27777 +3096 27775 +3097 27774 +3099 27772 +3101 27771 +3102 27769 +3104 27767 +3105 27766 +3107 27764 +3109 27763 +3110 27761 +3112 27759 +3114 27758 +3115 27756 +3117 27755 +3119 27753 +3120 27751 +3122 27750 +3124 27748 +3125 27746 +3127 27745 +3129 27743 +3130 27741 +3132 27740 +3134 27738 +3135 27737 +3137 27735 +3139 27733 +3140 27732 +3142 27730 +3144 27728 +3145 27727 +3147 27725 +3149 27723 +3150 27722 +3152 27720 +3154 27718 +3156 27717 +3157 27715 +3159 27713 +3161 27712 +3162 27710 +3164 27708 +3166 27707 +3167 27705 +3169 27704 +3171 27702 +3173 27700 +3174 27699 +3176 27697 +3178 27695 +3179 27694 +3181 27692 +3183 27690 +3185 27689 +3186 27687 +3188 27685 +3190 27684 +3191 27682 +3193 27680 +3195 27679 +3197 27677 +3198 27675 +3200 27674 +3202 27672 +3203 27670 +3205 27668 +3207 27667 +3209 27665 +3210 27663 +3212 27662 +3214 27660 +3216 27658 +3217 27657 +3219 27655 +3221 27653 +3223 27652 +3224 27650 +3226 27648 +3228 27646 +3230 27645 +3231 27643 +3233 27641 +3235 27640 +3237 27638 +3238 27636 +3240 27634 +3242 27633 +3244 27631 +3245 27629 +3247 27628 +3249 27626 +3251 27624 +3252 27622 +3254 27621 +3256 27619 +3258 27617 +3260 27616 +3261 27614 +3263 27612 +3265 27610 +3267 27609 +3268 27607 +3270 27605 +3272 27604 +3274 27602 +3276 27600 +3277 27598 +3279 27597 +3281 27595 +3283 27593 +3285 27591 +3286 27590 +3288 27588 +3290 27586 +3292 27584 +3293 27583 +3295 27581 +3297 27579 +3299 27577 +3301 27576 +3302 27574 +3304 27572 +3306 27570 +3308 27569 +3310 27567 +3311 27565 +3313 27563 +3315 27562 +3317 27560 +3319 27558 +3321 27557 +3322 27555 +3324 27553 +3326 27551 +3328 27550 +3330 27548 +3331 27546 +3333 27544 +3335 27542 +3337 27541 +3339 27539 +3341 27537 +3342 27535 +3344 27534 +3346 27532 +3348 27530 +3350 27528 +3352 27527 +3353 27525 +3355 27523 +3357 27521 +3359 27520 +3361 27518 +3363 27516 +3364 27514 +3366 27512 +3368 27511 +3370 27509 +3372 27507 +3374 27505 +3376 27503 +3377 27502 +3379 27500 +3381 27498 +3383 27496 +3385 27495 +3387 27493 +3389 27491 +3390 27489 +3392 27487 +3394 27486 +3396 27484 +3398 27482 +3400 27480 +3402 27479 +3403 27477 +3405 27475 +3407 27473 +3409 27471 +3411 27470 +3413 27468 +3415 27466 +3417 27464 +3418 27462 +3420 27461 +3422 27459 +3424 27457 +3426 27455 +3428 27453 +3430 27452 +3432 27450 +3433 27448 +3435 27446 +3437 27444 +3439 27442 +3441 27441 +3443 27439 +3445 27437 +3447 27435 +3449 27433 +3450 27431 +3452 27430 +3454 27428 +3456 27426 +3458 27424 +3460 27422 +3462 27421 +3464 27419 +3465 27417 +3467 27415 +3469 27413 +3471 27411 +3473 27409 +3475 27408 +3477 27406 +3479 27404 +3481 27402 +3482 27400 +3484 27398 +3486 27397 +3488 27395 +3490 27393 +3492 27391 +3494 27389 +3496 27387 +3498 27385 +3499 27384 +3501 27382 +3503 27380 +3505 27378 +3507 27376 +3509 27374 +3511 27373 +3513 27371 +3515 27369 +3516 27367 +3518 27365 +3520 27363 +3522 27361 +3524 27360 +3526 27358 +3528 27356 +3530 27354 +3532 27352 +3534 27350 +3536 27348 +3537 27346 +3539 27345 +3541 27343 +3543 27341 +3545 27339 +3547 27337 +3549 27335 +3551 27333 +3553 27332 +3555 27330 +3557 27328 +3559 27326 +3561 27324 +3563 27322 +3565 27320 +3566 27318 +3568 27316 +3570 27315 +3572 27313 +3574 27311 +3576 27309 +3578 27307 +3580 27305 +3582 27303 +3584 27301 +3586 27299 +3588 27297 +3590 27295 +3592 27294 +3594 27292 +3596 27290 +3598 27288 +3600 27286 +3602 27284 +3604 27282 +3606 27280 +3607 27278 +3609 27276 +3611 27275 +3613 27273 +3615 27271 +3617 27269 +3619 27267 +3621 27265 +3623 27263 +3625 27261 +3627 27259 +3629 27257 +3631 27255 +3633 27254 +3635 27252 +3637 27250 +3639 27248 +3641 27246 +3643 27244 +3645 27242 +3647 27240 +3649 27238 +3651 27236 +3653 27234 +3655 27232 +3657 27231 +3659 27229 +3661 27227 +3663 27225 +3665 27223 +3667 27221 +3669 27219 +3671 27217 +3673 27215 +3675 27213 +3677 27211 +3679 27209 +3681 27207 +3683 27205 +3685 27203 +3687 27201 +3689 27199 +3691 27197 +3693 27195 +3695 27193 +3697 27191 +3699 27190 +3701 27188 +3703 27186 +3705 27184 +3707 27182 +3709 27180 +3711 27178 +3713 27176 +3715 27174 +3717 27172 +3719 27170 +3721 27168 +3723 27166 +3725 27164 +3727 27162 +3729 27160 +3731 27158 +3733 27156 +3735 27154 +3737 27152 +3739 27150 +3741 27148 +3743 27146 +3745 27144 +3747 27142 +3749 27141 +3751 27139 +3753 27137 +3755 27135 +3757 27133 +3759 27131 +3761 27129 +3763 27127 +3765 27125 +3767 27123 +3769 27121 +3771 27119 +3774 27117 +3776 27115 +3778 27113 +3780 27111 +3782 27109 +3784 27107 +3786 27105 +3788 27103 +3790 27101 +3792 27099 +3794 27097 +3796 27095 +3798 27093 +3800 27091 +3802 27089 +3804 27087 +3806 27085 +3808 27083 +3810 27081 +3812 27079 +3814 27077 +3816 27075 +3819 27073 +3821 27071 +3823 27069 +3825 27067 +3827 27065 +3829 27063 +3831 27061 +3833 27059 +3835 27057 +3837 27055 +3839 27053 +3841 27051 +3843 27049 +3845 27047 +3848 27045 +3850 27043 +3852 27041 +3854 27039 +3856 27037 +3858 27035 +3860 27033 +3862 27031 +3864 27029 +3866 27027 +3868 27025 +3870 27023 +3872 27021 +3875 27019 +3877 27017 +3879 27015 +3881 27013 +3883 27011 +3885 27009 +3887 27007 +3889 27005 +3891 27002 +3893 27000 +3896 26998 +3898 26996 +3900 26994 +3902 26992 +3904 26990 +3906 26988 +3908 26986 +3910 26984 +3912 26982 +3914 26980 +3917 26978 +3919 26976 +3921 26974 +3923 26972 +3925 26970 +3927 26968 +3929 26966 +3931 26963 +3933 26961 +3936 26959 +3938 26957 +3940 26955 +3942 26953 +3944 26951 +3946 26949 +3948 26947 +3950 26945 +3953 26943 +3955 26941 +3957 26939 +3959 26937 +3961 26935 +3963 26933 +3965 26930 +3967 26928 +3970 26926 +3972 26924 +3974 26922 +3976 26920 +3978 26918 +3980 26916 +3982 26914 +3985 26912 +3987 26910 +3989 26907 +3991 26905 +3993 26903 +3995 26901 +3997 26899 +4000 26897 +4002 26895 +4004 26893 +4006 26891 +4008 26889 +4010 26886 +4013 26884 +4015 26882 +4017 26880 +4019 26878 +4021 26876 +4023 26874 +4025 26872 +4028 26870 +4030 26868 +4032 26865 +4034 26863 +4036 26861 +4038 26859 +4041 26857 +4043 26855 +4045 26853 +4047 26851 +4049 26849 +4051 26846 +4054 26844 +4056 26842 +4058 26840 +4060 26838 +4062 26836 +4064 26834 +4067 26832 +4069 26829 +4071 26827 +4073 26825 +4075 26823 +4078 26821 +4080 26819 +4082 26817 +4084 26815 +4086 26813 +4088 26810 +4091 26808 +4093 26806 +4095 26804 +4097 26802 +4099 26800 +4102 26798 +4104 26795 +4106 26793 +4108 26791 +4110 26789 +4113 26787 +4115 26785 +4117 26783 +4119 26781 +4121 26778 +4124 26776 +4126 26774 +4128 26772 +4130 26770 +4132 26768 +4135 26766 +4137 26764 +4139 26761 +4141 26759 +4143 26757 +4146 26755 +4148 26753 +4150 26751 +4152 26748 +4155 26746 +4157 26744 +4159 26742 +4161 26740 +4163 26738 +4166 26736 +4168 26734 +4170 26731 +4172 26729 +4174 26727 +4177 26725 +4179 26723 +4181 26721 +4183 26719 +4186 26716 +4188 26714 +4190 26712 +4192 26710 +4195 26708 +4197 26706 +4199 26703 +4201 26701 +4203 26699 +4206 26697 +4208 26695 +4210 26693 +4212 26690 +4215 26688 +4217 26686 +4219 26684 +4221 26682 +4224 26680 +4226 26678 +4228 26675 +4230 26673 +4233 26671 +4235 26669 +4237 26667 +4239 26665 +4242 26663 +4244 26660 +4246 26658 +4248 26656 +4251 26654 +4253 26652 +4255 26650 +4257 26648 +4260 26645 +4262 26643 +4264 26641 +4266 26639 +4269 26637 +4271 26635 +4273 26633 +4275 26630 +4278 26628 +4280 26626 +4282 26624 +4284 26622 +4287 26619 +4289 26617 +4291 26615 +4294 26613 +4296 26611 +4298 26609 +4300 26607 +4303 26604 +4305 26602 +4307 26600 +4309 26598 +4312 26596 +4314 26594 +4316 26592 +4319 26589 +4321 26587 +4323 26585 +4325 26583 +4328 26581 +4330 26579 +4332 26577 +4335 26575 +4337 26572 +4339 26570 +4341 26568 +4344 26566 +4346 26564 +4348 26562 +4351 26559 +4353 26557 +4355 26555 +4357 26553 +4360 26551 +4362 26549 +4364 26547 +4367 26545 +4369 26542 +4371 26540 +4373 26538 +4376 26536 +4378 26534 +4380 26532 +4383 26530 +4385 26528 +4387 26525 +4390 26523 +4392 26521 +4394 26519 +4396 26517 +4399 26515 +4401 26513 +4403 26511 +4406 26509 +4408 26506 +4410 26504 +4412 26502 +4415 26500 +4417 26498 +4419 26496 +4422 26494 +4424 26492 +4426 26489 +4429 26487 +4431 26485 +4433 26483 +4435 26481 +4438 26479 +4440 26476 +4442 26474 +4445 26472 +4447 26470 +4449 26468 +4451 26466 +4454 26464 +4456 26461 +4458 26459 +4461 26457 +4463 26455 +4465 26453 +4467 26451 +4470 26449 +4472 26446 +4474 26444 +4477 26442 +4479 26440 +4481 26438 +4483 26436 +4486 26434 +4488 26431 +4490 26429 +4493 26427 +4495 26425 +4497 26423 +4500 26420 +4502 26418 +4504 26416 +4506 26414 +4509 26412 +4511 26410 +4513 26407 +4515 26405 +4518 26403 +4520 26401 +4522 26398 +4525 26396 +4527 26394 +4529 26392 +4531 26390 +4534 26387 +4536 26385 +4538 26383 +4540 26381 +4543 26378 +4545 26376 +4547 26374 +4549 26372 +4551 26369 +4554 26367 +4556 26365 +4558 26363 +4560 26360 +4563 26358 +4565 26356 +4567 26353 +4569 26351 +4572 26349 +4574 26346 +4576 26344 +4578 26342 +4581 26340 +4583 26337 +4585 26335 +4587 26333 +4590 26330 +4592 26328 +4594 26326 +4596 26323 +4599 26321 +4601 26319 +4603 26316 +4605 26314 +4607 26312 +4610 26309 +4612 26307 +4614 26304 +4616 26302 +4618 26300 +4621 26297 +4623 26295 +4625 26292 +4627 26290 +4630 26288 +4632 26285 +4634 26283 +4636 26280 +4639 26278 +4641 26276 +4643 26273 +4645 26271 +4648 26268 +4650 26266 +4652 26263 +4654 26261 +4657 26258 +4659 26256 +4661 26254 +4664 26251 +4666 26249 +4668 26246 +4670 26244 +4673 26241 +4675 26239 +4677 26236 +4680 26234 +4682 26231 +4684 26229 +4687 26226 +4689 26224 +4691 26221 +4694 26218 +4696 26216 +4699 26213 +4701 26211 +4703 26208 +4706 26206 +4708 26203 +4711 26201 +4713 26198 +4715 26196 +4718 26193 +4720 26190 +4723 26188 +4725 26185 +4727 26183 +4730 26180 +4732 26177 +4735 26175 +4737 26172 +4740 26170 +4742 26167 +4745 26164 +4747 26162 +4750 26159 +4752 26156 +4755 26154 +4757 26151 +4760 26148 +4762 26146 +4765 26143 +4767 26140 +4770 26138 +4772 26135 +4775 26133 +4778 26130 +4780 26127 +4783 26125 +4785 26122 +4788 26119 +4790 26117 +4793 26114 +4795 26111 +4798 26109 +4801 26106 +4803 26103 +4806 26101 +4808 26098 +4811 26095 +4814 26092 +4816 26090 +4819 26087 +4821 26084 +4824 26081 +4827 26079 +4829 26076 +4832 26073 +4834 26070 +4837 26068 +4840 26065 +4842 26062 +4845 26060 +4848 26057 +4850 26054 +4853 26051 +4855 26049 +4858 26046 +4861 26043 +4863 26040 +4866 26037 +4869 26034 +4872 26031 +4875 26028 +4878 26025 +4880 26022 +4883 26019 +4886 26017 +4889 26014 +4892 26011 +4895 26008 +4898 26006 +4901 26003 +4904 26000 +4907 25998 +4910 25995 +4912 25992 +4887 25989 +4885 26009 +4862 26006 +4864 26001 +4866 25999 +4868 25996 +4870 25994 +4872 25991 +4874 25988 +4876 25986 +4878 25983 +4880 25980 +4883 25978 +4885 25975 +4887 25972 +4889 25970 +4891 25967 +4893 25964 +4896 25961 +4898 25959 +4900 25956 +4902 25953 +4904 25951 +4907 25948 +4909 25945 +4911 25942 +4913 25940 +4916 25937 +4918 25934 +4920 25931 +4923 25928 +4925 25926 +4927 25923 +4930 25920 +4932 25917 +4934 25914 +4937 25912 +4939 25909 +4941 25906 +4944 25903 +4946 25900 +4949 25897 +4951 25894 +4954 25891 +4956 25889 +4959 25886 +4961 25883 +4964 25880 +4966 25877 +4969 25874 +4971 25871 +4974 25868 +4976 25865 +4979 25862 +4981 25859 +4984 25856 +4987 25853 +4989 25850 +4992 25847 +4995 25844 +4997 25841 +5000 25838 +5003 25835 +5005 25832 +5008 25829 +5011 25826 +5014 25823 +5016 25819 +5019 25816 +5022 25813 +5025 25810 +5028 25807 +5030 25804 +5033 25801 +5036 25797 +5039 25794 +5042 25791 +5045 25788 +5048 25785 +5051 25781 +5054 25778 +5057 25775 +5059 25772 +5062 25769 +5065 25765 +5069 25762 +5072 25759 +5075 25755 +5078 25752 +5081 25749 +5084 25746 +5087 25742 +5090 25739 +5093 25736 +5096 25732 +5100 25729 +5103 25725 +5106 25722 +5109 25719 +5112 25715 +5116 25712 +5119 25708 +5122 25705 +5126 25702 +5129 25698 +5132 25695 +5136 25691 +5139 25688 +5142 25684 +5146 25681 +5149 25677 +5153 25674 +5156 25670 +5159 25667 +5163 25663 +5166 25659 +5170 25656 +5174 25652 +5177 25649 +5181 25645 +5184 25641 +5188 25638 +5191 25634 +5195 25630 +5199 25627 +5202 25623 +5206 25619 +5210 25616 +5214 25612 +5217 25608 +5221 25605 +5225 25601 +5229 25597 +5233 25593 +5236 25589 +5240 25586 +5244 25582 +5248 25578 +5252 25574 +5256 25570 +5260 25567 +5264 25563 +5268 25559 +5272 25555 +5276 25551 +5280 25547 +5284 25543 +5288 25539 +5292 25535 +5296 25531 +5301 25527 +5305 25523 +5309 25519 +5313 25515 +5317 25511 +5322 25507 +5326 25503 +5330 25499 +5335 25495 +5339 25491 +5343 25487 +5348 25483 +5352 25479 +5356 25475 +5361 25471 +5365 25466 +5370 25462 +5374 25458 +5379 25454 +5383 25450 +5388 25445 +5393 25441 +5397 25437 +5402 25433 +5406 25428 +5411 25424 +5416 25420 +5421 25416 +5425 25411 +5430 25407 +5435 25403 +5440 25398 +5445 25394 +5449 25389 +5454 25385 +5459 25381 +5464 25376 +5469 25372 +5474 25367 +5479 25363 +5484 25358 +5489 25354 +5494 25349 +5499 25345 +5504 25340 +5509 25336 +5515 25331 +5520 25327 +5525 25322 +5530 25317 +5535 25313 +5541 25308 +5546 25304 +5551 25299 +5557 25294 +5562 25289 +5567 25285 +5573 25280 +5578 25275 +5584 25271 +5589 25266 +5595 25261 +5600 25256 +5606 25251 +5611 25247 +5617 25242 +5623 25237 +5628 25232 +5634 25227 +5640 25222 +5645 25217 +5651 25213 +5657 25208 +5663 25203 +5669 25198 +5675 25193 +5680 25188 +5686 25183 +5692 25178 +5698 25173 +5704 25168 +5710 25163 +5716 25157 +5722 25152 +5728 25147 +5735 25142 +5741 25137 +5747 25132 +5753 25127 +5759 25122 +5766 25116 +5772 25111 +5778 25106 +5785 25101 +5791 25095 +5797 25090 +5804 25085 +5810 25079 +5817 25074 +5823 25069 +5830 25063 +5836 25058 +5843 25053 +5849 25047 +5856 25042 +5863 25036 +5869 25031 +5876 25025 +5883 25020 +5890 25015 +5897 25009 +5903 25003 +5910 24998 +5917 24992 +5924 24987 +5931 24981 +5938 24976 +5945 24970 +5952 24964 +5959 24959 +5966 24953 +5973 24947 +5980 24942 +5988 24936 +5995 24930 +6002 24924 +6009 24918 +6017 24913 +6024 24907 +6031 24901 +6039 24895 +6046 24889 +6054 24883 +6061 24878 +6069 24872 +6076 24866 +6084 24860 +6091 24854 +6099 24848 +6107 24842 +6114 24836 +6122 24830 +6130 24824 +6138 24818 +6145 24812 +6153 24805 +6161 24799 +6169 24793 +6177 24787 +6185 24781 +6193 24775 +6201 24768 +6209 24762 +6217 24756 +6225 24750 +6233 24743 +6242 24737 +6250 24731 +6258 24725 +6266 24718 +6275 24712 +6283 24705 +6291 24699 +6300 24693 +6308 24686 +6317 24680 +6325 24673 +6334 24667 +6342 24660 +6351 24654 +6360 24647 +6368 24641 +6377 24634 +6386 24627 +6395 24621 +6403 24614 +6412 24607 +6421 24601 +6430 24594 +6439 24587 +6448 24581 +6457 24574 +6466 24567 +6475 24560 +6484 24553 +6493 24547 +6503 24540 +6512 24533 +6521 24526 +6530 24519 +6540 24512 +6549 24505 +6558 24498 +6568 24491 +6577 24484 +6587 24477 +6596 24470 +6606 24463 +6615 24456 +6625 24449 +6635 24442 +6644 24435 +6654 24428 +6664 24420 +6674 24413 +6684 24406 +6693 24399 +6703 24392 +6713 24384 +6723 24377 +6733 24370 +6743 24362 +6753 24355 +6764 24348 +6774 24340 +6784 24333 +6794 24325 +6804 24318 +6815 24310 +6825 24303 +6835 24295 +6846 24288 +6856 24280 +6867 24273 +6877 24265 +6888 24258 +6899 24250 +6909 24242 +6920 24235 +6931 24227 +6941 24219 +6952 24211 +6963 24204 +6974 24196 +6985 24188 +6996 24180 +7007 24172 +7018 24165 +7029 24157 +7040 24149 +7051 24141 +7062 24133 +7073 24125 +7085 24117 +7096 24109 +7107 24101 +7119 24093 +7130 24085 +7141 24077 +7153 24069 +7164 24060 +7176 24052 +7188 24044 +7199 24036 +7211 24028 +7223 24019 +7234 24011 +7246 24003 +7258 23995 +7270 23986 +7282 23978 +7294 23969 +7306 23961 +7318 23953 +7330 23944 +7342 23936 +7354 23927 +7366 23919 +7378 23910 +7391 23902 +7403 23893 +7415 23884 +7428 23876 +7440 23867 +7453 23859 +7465 23850 +7478 23841 +7490 23832 +7503 23824 +7516 23815 +7528 23806 +7541 23797 +7554 23788 +7567 23780 +7580 23771 +7593 23762 +7606 23753 +7619 23744 +7632 23735 +7645 23726 +7658 23717 +7671 23708 +7684 23699 +7697 23690 +7711 23681 +7724 23672 +7738 23662 +7751 23653 +7764 23644 +7778 23635 +7778 23635 +7778 23635 +7778 23635 +7778 23635 +7778 23635 +7792 23626 +7792 23626 +7805 23616 +7805 23616 +7819 23607 +7819 23607 +7832 23598 diff --git a/Analysis/config/exclusionMask_p1.0.19.txt.png b/Analysis/config/exclusionMask_p1.0.19.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..1087a8636f9e0eeb2151302b4fd83ee8a2dca4c7 GIT binary patch literal 65738 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

ue?$&k9m1tC8+6+QkFUF5>xr4#9lk z`~LCmZgJ$eZweGN+P7OX$$5RK<*jOQ;NH_;r+m%+>uDg$sQ6r?h){R@Y2Xx?LGjYr z-&XgYFL0zZT0hCmEr)zJ5v_M+c}xGbEtp>1=L8i){9f~4hn;r?H$OuH?-;f8UuW`~ zpTQger`}iYKN~cpqynKmiq_x}H>KGgI-2iCGuuac`l;I#Llr|=@t%LrWB>&RDJUx( z3SUkt$himyRawFRzW6L;C;)}$4u-GG043;zi;KO|5n`6Mi2Lg6re<`1A&gPHeYHn+ z(6}jUhcQ|HemDTJI(BT#4ieI8DE;Rf{i+zBf!(6`aj)roTVSEEMC(70(5#qp1&c-J zLR{ltb$;WqC3}%SR8c5*LLtc6v%9r6~qa(bSVq<8J$&lucEaffxOZaOieRmGjv3UC(m&6zjTC%LIQ=> zi(5Q1nEA+HqM&G<5-&HN92qRvd5%QBV(U|6FteymDMweg_%J-%AeYF zXP;JBdeak~2$eA<`zx3Wt1RSU$iLMUuKqW>30HKO+Ebs_!qm|0D}@D7;bpFKf)~Im zN22!>lD|VblB*Sczr&H@gfidC-7AP!Wh+wg_m6Io-bev$>6;r=pB0vGA3UA(f&PK2 z4Q;&>&b2Xl9WVDl=asylWwp#Rq$!VLz`r1^)-`lRRgKWRZqn07rfc{RY^}tGKN{OM z^|2nTKstF`o5hprh?1^)zy{a+%x#jcxI*NNqFtGn!OSHKMB~tAm2-$co2L{c2sy|4 zP&9nwCbjtYX+0=MYo7S4wuV=H(^gcyE*_c5t0sEEYABS94UF ziqO5B??uhy$8%;jU3 z(BgVXXD!mDKcZf`sMs_M`L=6&Fq31>WAUN$>I`zUGrYuXDYOLN;F)p9$24+38Z>S* ztlOEbIxFNIGJ!D))FZJ~(pM=U)OhoNN}SWJA0+b58UdHWr&UT#gce~3U@XN~I5C&V zeG5^$uCs~J9^;wC-ihS-ZZ~%QFd}c`^GNmxKYV`km;5yyV;QLb;*)xvlCCj%jNZc# z_i7%$>RO|7VzN?_ zzsJH2aX`9?uf?*rM(3ph=!l|jdAO?Z zzjFfh5~^a`I}tujOUT~yIZqocud;Lx*NYq&W+;AEjj{O5=JNcmVkcu%a&hJvd)6%` z&-kgSDogRt%{%k^h0gaV{AiwiVJDM!@98>-S2Y)qc6O;i6%>!2VIS3UB*YV0*Sm14 zEH`=EViXDaE!c9YLlPu{^a_ zdY+Eo4NXZbooI+sxhrdZnu zVlzyyqxAKU36&ny^`Hhq$#yAYk@>t#0Zd==ZoJleuZvyVG*r_iVg24PJwsY=fCP8m zvyHmR3Ux#)e!D1AR%4z29TNP9tb`kwsQXVQp>j`BT;EFerT-#t7UpEV7Z=Q=`M6Hb;CLBZ_( zY{pml289h0>~F=g@ERl*Ai*LR_L=m7t${wGkpRH!!npm{Uel?O28-r*?S7^C2E~Ka z5U=z2V9E?~1gDW-KV(iO#=9~_!O%n^{YK5f zD|5gs8O#C)_Imv7YosdU%@eC!=C!Lz$2MW=p*Wxf9WgpO|aM2kaQ}u*O$oFU|~ph(BYS$AlZ!1Mbv-xS}Od<(pjjD50=a6HR6S!1vzD2W&q(_ zslhIRS*zSpMz2X<3Jj5vC+SPJUdApV#s9hHPvrP%vXMv5I6MQhyRG;sP?N4b`Sg_e zIiMcW@~3I*OG-at3}myfEPrC=Crx%Olr&)TC;0s#mrp~)h3n}(qHQ1mCJ*1>k)C9S zJuMxrN-t_6G5IV@hKh@^V-HyO(3bB47a6}hlU^*^1FpeE@dexUc!g_d2hpzPr*>b$ z-%nd6gOV%i?}N}}7k(@v#^dS589#z#7BbS3Q|-jC`ea#EYQK(u+q!+9ii;U`e+h-vkn~D=g{9 z(w>7gNbR6Gx=j_0^ciUSt!H@>i$Ced?|_oS>RZ9sq{;jMNZhoaL4Pg&7cik@V%8U( zrkzX%)#s`+J&0MA?08i5k2yAj3lBs2sff7jFB(H+g6&4cHf1{O+@*|rC;~sca^stg zF^aE+S>9oH7L;~J^Xbr4yn-%TSeggs2Gu4cE;?-4eTENmm2I>uUwD?&RRD>L>wg5| zT1w6Kgw>{mo=bzPfXI zEXHDx+Me^ZWkvhQ=>jIscxPw6*Ml)#Rgk#Zx&eIdPT?}`ppTkc<(V!{OWVGs#99=TVpho$QS#f9r$`@&513r@@G!< zz^*rSf#2?^YEPZjNyd>&vGhT-bK2)*&-Z*_L9By{_*j>NkKI`yE>qQ@;%g&l73t@( zAh-&?_zNq(lBuagG2>fg%U`h8l0YV?Xeb7klUISah`7*|c5mTb47D@9-dP<% zi0KeE*-2Mu-7pmSLpX!u4{A@3$NJ(SJZ24DVL!o0WCP)dazF6LNqZ~(U*8v$Km_OFr16T-36y@CE|FU*e(VJBz0LU%4lWD4Qn+nyly zls|SBs-`e8q$`3ha70re{2MtipEx-MlS0)LrXOhvuL;InBNdE)I;evOW+mVfXqHEJ z+`zI^QP_OQEHm;9IM?aUWC@ugFZWZOokniT}E%jf;=Xz>OjC_hl~RLF!EG z4ph`B@-Q&#n9pnvhs3(2G*Qq66RtHR9x&F%osZ4Nk3r%)WsV@@q!^nHZPXsI7s#(w zGTY-+#9WrrUware)Z5Xu-mW5|DKH3iC)bB-VDL ziL9rY*6|?m&DAXYvg>#FvoKXCtTnU-4^D5uCP51&#cT!LtzVd#p(=$S-GvR8`-RZ888KWXr1fRsX`hn&E3Z53GfoH$Y z0OW+KJrZ?<_z{gSs`LRDj}qiZ&}`=n(nWLW%AF84ol4`oJn#dZM^x|=XBKcy{u4CY z&_+%gee5hjZ^kaVvQl+*m-IUjpSMk)Sm-h`?(!47Fut!}Akc)o*R<2Y%!f))Q?DXU zKco#R7T%{z_Ns_wJIp{=!9D}R7ZR_w0Q}I(DPSU$jOF~#pi4d;18?9mLzLIYgNqtK z_kfC7*0Y!?VBTOd5Lv1gDl+#N#ImovrGvC0mF9^#Ck=?e&5l6MSJ}m^Gy!|R&CB;8~0zEJG1(6q% ve|)`>JvaOx{P*ZQ`RudmI2M5a$A9m(nB)nA?t~W#NBBmE$A+B<5lR0K@Y9G) literal 0 HcmV?d00001 diff --git a/Analysis/config/exclusionMask_p1.0.20.txt b/Analysis/config/exclusionMask_p1.0.20.txt index adc173d9..476150cf 100644 --- a/Analysis/config/exclusionMask_p1.0.20.txt +++ b/Analysis/config/exclusionMask_p1.0.20.txt @@ -1,5313 +1,5313 @@ -7680 5312 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -1380 1920 -1376 6289 -1373 6294 -1369 6295 -1365 6298 -1362 6303 -1358 6307 -1354 6312 -1350 6317 -1347 6321 -1343 6326 -1339 6331 -1336 6336 -1332 6340 -1328 6345 -1325 6350 -1321 6354 -1317 6359 -1314 6364 -1310 6368 -1306 6373 -1302 6378 -1299 6382 -1295 6387 -1291 6391 -1288 6395 -1284 6398 -1280 6402 -1277 6405 -1273 6409 -1269 6412 -1266 6416 -1262 6420 -1258 6423 -1255 6427 -1252 6430 -1249 6434 -1246 6437 -1242 6441 -1239 6445 -1236 6448 -1233 6452 -1230 6455 -1227 6459 -1223 6462 -1220 6466 -1217 6470 -1214 6473 -1211 6477 -1208 6480 -1204 6484 -1201 6487 -1198 6491 -1195 6495 -1192 6498 -1189 6502 -1185 6505 -1182 6509 -1179 6512 -1176 6516 -1173 6520 -1170 6523 -1166 6527 -1163 6530 -1160 6534 -1157 6537 -1154 6540 -1151 6542 -1147 6545 -1144 6547 -1141 6549 -1138 6552 -1135 6554 -1131 6556 -1129 6559 -1126 6561 -1123 6563 -1121 6566 -1118 6568 -1116 6570 -1113 6573 -1110 6575 -1108 6577 -1105 6580 -1103 6582 -1100 6584 -1098 6587 -1095 6589 -1092 6591 -1090 6594 -1087 6596 -1085 6598 -1082 6601 -1079 6603 -1077 6605 -1074 6608 -1072 6610 -1069 6612 -1066 6615 -1064 6617 -1061 6619 -1059 6622 -1056 6624 -1053 6626 -1051 6628 -1048 6631 -1046 6633 -1043 6635 -1041 6638 -1038 6640 -1035 6642 -1033 6645 -1030 6647 -1028 6649 -1025 6652 -1022 6654 -1020 6656 -1017 6659 -1015 6661 -1012 6663 -1009 6666 -1007 6668 -1005 6670 -1003 6673 -1001 6675 -999 6677 -997 6680 -995 6682 -993 6684 -991 6687 -990 6689 -988 6691 -986 6692 -984 6694 -982 6696 -980 6697 -978 6699 -976 6701 -974 6703 -972 6704 -971 6706 -969 6708 -967 6709 -965 6711 -963 6713 -961 6715 -959 6716 -957 6718 -955 6720 -954 6721 -952 6723 -950 6725 -948 6727 -946 6728 -944 6730 -942 6732 -940 6733 -938 6735 -936 6737 -935 6739 -933 6740 -931 6742 -929 6744 -927 6745 -925 6747 -923 6749 -921 6751 -919 6752 -917 6754 -916 6756 -914 6757 -912 6759 -910 6761 -908 6762 -906 6764 -904 6766 -902 6768 -900 6769 -898 6771 -897 6773 -895 6774 -893 6776 -891 6778 -889 6780 -887 6781 -885 6783 -883 6785 -881 6786 -879 6788 -878 6790 -876 6792 -874 6793 -872 6795 -871 6797 -869 6798 -868 6800 -866 6802 -864 6804 -863 6805 -861 6807 -860 6809 -858 6810 -856 6812 -855 6814 -853 6816 -852 6817 -850 6819 -848 6821 -847 6822 -845 6824 -844 6826 -842 6828 -840 6829 -839 6831 -837 6833 -836 6834 -834 6836 -832 6838 -831 6840 -829 6841 -828 6843 -826 6845 -824 6846 -823 6848 -821 6850 -820 6852 -818 6853 -816 6855 -815 6857 -813 6858 -812 6860 -810 6862 -808 6864 -807 6865 -805 6867 -804 6869 -802 6870 -800 6872 -799 6874 -797 6876 -796 6877 -794 6879 -792 6880 -791 6882 -789 6883 -788 6884 -786 6886 -784 6887 -783 6888 -781 6890 -780 6891 -778 6892 -776 6894 -775 6895 -773 6896 -772 6898 -770 6899 -768 6900 -767 6902 -765 6903 -764 6905 -762 6906 -760 6907 -759 6909 -757 6910 -756 6911 -754 6913 -752 6914 -751 6915 -749 6917 -748 6918 -746 6919 -744 6921 -743 6922 -741 6923 -740 6925 -738 6926 -736 6927 -735 6929 -733 6930 -732 6931 -731 6933 -729 6934 -728 6935 -727 6937 -726 6938 -724 6940 -723 6941 -722 6942 -720 6944 -719 6945 -718 6946 -717 6948 -715 6949 -714 6950 -713 6952 -712 6953 -710 6954 -709 6956 -708 6957 -706 6958 -705 6960 -704 6961 -703 6962 -701 6964 -700 6965 -699 6966 -698 6968 -696 6969 -695 6970 -694 6972 -692 6973 -691 6974 -690 6976 -689 6977 -687 6979 -686 6980 -685 6981 -684 6983 -682 6984 -681 6985 -680 6987 -678 6988 -677 6989 -676 6991 -675 6992 -673 6993 -672 6995 -671 6996 -670 6997 -668 6999 -667 7000 -666 7001 -664 7003 -663 7004 -662 7005 -661 7007 -659 7008 -658 7009 -657 7011 -656 7012 -654 7014 -653 7015 -652 7016 -650 7018 -649 7019 -648 7020 -647 7022 -645 7023 -644 7024 -643 7026 -642 7027 -640 7028 -639 7030 -638 7031 -636 7032 -635 7034 -634 7035 -633 7036 -632 7038 -631 7039 -630 7040 -629 7042 -628 7043 -627 7044 -626 7045 -625 7046 -624 7046 -623 7047 -622 7048 -621 7049 -620 7050 -618 7051 -617 7052 -616 7053 -615 7054 -614 7055 -613 7056 -612 7057 -611 7058 -610 7059 -609 7060 -608 7060 -607 7061 -606 7062 -605 7063 -604 7064 -603 7065 -602 7066 -601 7067 -600 7068 -599 7069 -598 7070 -597 7071 -596 7072 -595 7073 -594 7073 -593 7074 -592 7075 -591 7076 -590 7077 -589 7078 -588 7079 -586 7080 -585 7081 -584 7082 -583 7083 -582 7084 -581 7085 -580 7086 -579 7087 -578 7087 -577 7088 -576 7089 -575 7090 -574 7091 -573 7092 -572 7093 -571 7094 -570 7095 -569 7096 -568 7097 -567 7098 -566 7099 -565 7100 -564 7101 -563 7101 -562 7102 -561 7103 -560 7104 -559 7105 -558 7106 -557 7107 -555 7108 -554 7109 -553 7110 -552 7111 -551 7112 -550 7113 -549 7114 -548 7115 -547 7115 -546 7116 -545 7117 -544 7118 -543 7119 -542 7120 -541 7121 -540 7122 -539 7123 -538 7124 -537 7125 -536 7126 -535 7127 -534 7128 -533 7128 -532 7129 -531 7130 -530 7131 -529 7132 -528 7133 -527 7134 -526 7135 -525 7136 -523 7137 -522 7138 -521 7139 -520 7140 -520 7141 -519 7142 -518 7142 -517 7143 -516 7144 -515 7145 -514 7146 -513 7147 -512 7148 -511 7149 -510 7150 -510 7151 -509 7152 -508 7153 -507 7154 -506 7155 -505 7156 -504 7156 -503 7157 -502 7158 -501 7159 -500 7160 -499 7161 -499 7162 -498 7163 -497 7164 -496 7165 -495 7166 -494 7167 -493 7168 -492 7169 -491 7170 -490 7170 -489 7171 -489 7172 -488 7173 -487 7174 -486 7175 -485 7176 -484 7177 -483 7178 -482 7179 -481 7180 -480 7181 -479 7182 -479 7183 -478 7183 -477 7184 -476 7185 -475 7186 -474 7187 -473 7188 -472 7189 -471 7190 -470 7191 -469 7192 -469 7193 -468 7194 -467 7195 -466 7196 -465 7197 -464 7197 -463 7198 -462 7199 -461 7200 -460 7201 -459 7202 -458 7203 -458 7204 -457 7205 -456 7206 -455 7207 -454 7208 -453 7209 -452 7210 -451 7211 -450 7211 -449 7212 -448 7213 -448 7214 -447 7215 -446 7216 -445 7217 -444 7218 -443 7219 -442 7219 -441 7220 -440 7221 -439 7222 -438 7222 -438 7223 -437 7224 -436 7224 -435 7225 -434 7226 -433 7227 -432 7227 -431 7228 -430 7229 -430 7230 -429 7230 -428 7231 -427 7232 -427 7232 -426 7233 -425 7234 -424 7235 -424 7235 -423 7236 -422 7237 -422 7238 -421 7238 -420 7239 -419 7240 -419 7240 -418 7241 -417 7242 -416 7243 -416 7243 -415 7244 -414 7245 -413 7246 -413 7246 -412 7247 -411 7248 -411 7248 -410 7249 -409 7250 -408 7251 -408 7251 -407 7252 -406 7253 -405 7253 -405 7254 -404 7255 -403 7256 -402 7256 -402 7257 -401 7258 -400 7259 -400 7259 -399 7260 -398 7261 -397 7261 -397 7262 -396 7263 -395 7264 -394 7264 -394 7265 -393 7266 -392 7267 -391 7267 -391 7268 -390 7269 -389 7269 -389 7270 -388 7271 -387 7272 -386 7272 -386 7273 -385 7274 -384 7275 -383 7275 -383 7276 -382 7277 -381 7277 -380 7278 -380 7279 -379 7280 -378 7280 -377 7281 -377 7282 -376 7283 -375 7283 -375 7284 -374 7285 -373 7285 -372 7286 -372 7287 -371 7288 -370 7288 -369 7289 -369 7290 -368 7291 -367 7291 -366 7292 -366 7293 -365 7293 -364 7294 -364 7295 -363 7296 -362 7296 -361 7297 -361 7298 -360 7298 -359 7299 -358 7300 -358 7301 -357 7301 -356 7302 -355 7303 -355 7304 -354 7304 -353 7305 -353 7306 -352 7306 -351 7307 -350 7308 -350 7309 -349 7309 -348 7310 -347 7311 -347 7312 -346 7312 -345 7313 -344 7313 -344 7314 -343 7314 -342 7315 -342 7315 -341 7316 -340 7316 -339 7317 -339 7317 -338 7318 -337 7318 -336 7319 -336 7319 -335 7320 -334 7320 -333 7321 -333 7321 -332 7322 -332 7322 -331 7323 -331 7323 -330 7324 -330 7324 -329 7325 -329 7325 -328 7326 -327 7326 -327 7327 -326 7327 -326 7328 -325 7329 -325 7329 -324 7330 -324 7330 -323 7331 -323 7331 -322 7332 -321 7332 -321 7333 -320 7333 -320 7334 -319 7334 -319 7335 -318 7335 -318 7336 -317 7336 -317 7337 -316 7337 -316 7338 -315 7338 -314 7339 -314 7339 -313 7340 -313 7340 -312 7341 -312 7341 -311 7342 -311 7342 -310 7343 -310 7343 -309 7344 -308 7345 -308 7345 -307 7346 -307 7346 -306 7347 -306 7347 -305 7348 -305 7348 -304 7349 -304 7349 -303 7350 -303 7350 -302 7351 -301 7351 -301 7352 -300 7352 -300 7353 -299 7353 -299 7354 -298 7354 -298 7355 -297 7355 -297 7356 -296 7356 -295 7357 -295 7357 -294 7358 -294 7358 -293 7359 -293 7359 -292 7360 -292 7361 -291 7361 -291 7362 -290 7362 -289 7363 -289 7363 -288 7364 -288 7364 -287 7365 -287 7365 -286 7366 -286 7366 -285 7367 -285 7367 -284 7368 -284 7368 -283 7369 -282 7369 -282 7370 -281 7370 -281 7371 -280 7371 -280 7372 -279 7372 -279 7373 -278 7373 -278 7374 -277 7374 -276 7375 -276 7375 -275 7376 -275 7377 -274 7377 -274 7378 -273 7378 -273 7379 -272 7379 -272 7380 -271 7380 -271 7381 -270 7381 -269 7382 -269 7382 -268 7383 -268 7383 -267 7384 -267 7384 -266 7385 -266 7385 -265 7386 -265 7386 -264 7387 -263 7387 -263 7388 -262 7388 -262 7389 -261 7389 -261 7390 -260 7390 -260 7391 -259 7391 -259 7392 -258 7393 -257 7393 -257 7394 -256 7394 -256 7395 -255 7395 -255 7396 -254 7396 -254 7397 -253 7397 -253 7398 -252 7398 -252 7399 -251 7399 -250 7400 -250 7400 -249 7401 -249 7401 -248 7402 -248 7402 -247 7403 -247 7403 -246 7404 -246 7404 -245 7405 -245 7405 -244 7406 -244 7406 -243 7407 -243 7407 -243 7408 -242 7409 -242 7409 -241 7410 -241 7410 -241 7411 -240 7411 -240 7412 -239 7412 -239 7412 -239 7413 -238 7413 -238 7413 -237 7414 -237 7414 -236 7415 -236 7415 -236 7415 -235 7416 -235 7416 -234 7416 -234 7417 -234 7417 -233 7418 -233 7418 -232 7418 -232 7419 -232 7419 -231 7419 -231 7420 -230 7420 -230 7421 -230 7421 -229 7421 -229 7422 -228 7422 -228 7422 -228 7423 -227 7423 -227 7424 -226 7424 -226 7424 -226 7425 -225 7425 -225 7426 -224 7426 -224 7426 -224 7427 -223 7427 -223 7427 -222 7428 -222 7428 -222 7429 -221 7429 -221 7429 -220 7430 -220 7430 -220 7430 -219 7431 -219 7431 -218 7432 -218 7432 -218 7432 -217 7433 -217 7433 -216 7433 -216 7434 -215 7434 -215 7435 -215 7435 -214 7435 -214 7436 -213 7436 -213 7436 -213 7437 -212 7437 -212 7438 -211 7438 -211 7438 -211 7439 -210 7439 -210 7439 -209 7440 -209 7440 -209 7441 -208 7441 -208 7441 -207 7442 -207 7442 -207 7442 -206 7443 -206 7443 -205 7444 -205 7444 -205 7444 -204 7445 -204 7445 -203 7446 -203 7446 -203 7446 -202 7447 -202 7447 -201 7447 -201 7448 -201 7448 -200 7449 -200 7449 -199 7449 -199 7450 -199 7450 -198 7450 -198 7451 -197 7451 -197 7452 -197 7452 -196 7452 -196 7453 -195 7453 -195 7453 -194 7454 -194 7454 -194 7455 -193 7455 -193 7455 -192 7456 -192 7456 -192 7456 -191 7457 -191 7457 -190 7458 -190 7458 -190 7458 -189 7459 -189 7459 -189 7459 -188 7460 -188 7460 -188 7461 -187 7461 -187 7461 -187 7462 -187 7462 -186 7463 -186 7463 -186 7463 -185 7464 -185 7464 -185 7464 -184 7465 -184 7465 -184 7466 -183 7466 -183 7466 -183 7466 -182 7467 -182 7467 -182 7467 -181 7467 -181 7468 -181 7468 -180 7468 -180 7468 -180 7468 -180 7469 -179 7469 -179 7469 -179 7469 -178 7470 -178 7470 -178 7470 -177 7470 -177 7470 -177 7471 -176 7471 -176 7471 -176 7471 -175 7471 -175 7472 -175 7472 -174 7472 -174 7472 -174 7473 -173 7473 -173 7473 -173 7473 -173 7473 -172 7474 -172 7474 -172 7474 -171 7474 -171 7475 -171 7475 -170 7475 -170 7475 -170 7475 -169 7476 -169 7476 -169 7476 -168 7476 -168 7477 -168 7477 -167 7477 -167 7477 -167 7477 -167 7478 -166 7478 -166 7478 -166 7478 -165 7478 -165 7479 -165 7479 -164 7479 -164 7479 -164 7480 -163 7480 -163 7480 -163 7480 -162 7480 -162 7481 -162 7481 -161 7481 -161 7481 -161 7482 -160 7482 -160 7482 -160 7482 -160 7482 -159 7483 -159 7483 -159 7483 -158 7483 -158 7484 -158 7484 -157 7484 -157 7484 -157 7484 -156 7485 -156 7485 -156 7485 -155 7485 -155 7485 -155 7486 -154 7486 -154 7486 -154 7486 -154 7487 -154 7487 -153 7487 -153 7487 -153 7487 -153 7488 -153 7488 -152 7488 -152 7488 -152 7489 -152 7489 -152 7489 -151 7489 -151 7489 -151 7490 -151 7490 -151 7490 -151 7490 -150 7491 -150 7491 -150 7491 -150 7491 -150 7491 -149 7492 -149 7492 -149 7492 -149 7492 -149 7492 -148 7493 -148 7493 -148 7493 -148 7493 -148 7494 -147 7494 -147 7494 -147 7494 -147 7494 -147 7495 -146 7495 -146 7495 -146 7495 -146 7496 -146 7496 -145 7496 -145 7496 -145 7496 -145 7497 -145 7497 -144 7497 -144 7497 -144 7498 -144 7498 -144 7498 -143 7498 -143 7498 -143 7499 -143 7499 -143 7499 -142 7499 -142 7499 -142 7500 -142 7500 -142 7500 -141 7500 -141 7501 -141 7501 -141 7501 -141 7501 -141 7501 -140 7502 -140 7502 -140 7502 -140 7502 -140 7502 -139 7502 -139 7502 -139 7503 -139 7503 -139 7503 -138 7503 -138 7503 -138 7503 -138 7503 -138 7503 -137 7503 -137 7504 -137 7504 -137 7504 -137 7504 -136 7504 -136 7504 -136 7504 -136 7504 -136 7504 -135 7505 -135 7505 -135 7505 -135 7505 -135 7505 -134 7505 -134 7505 -134 7505 -134 7505 -134 7506 -133 7506 -133 7506 -133 7506 -133 7506 -133 7506 -132 7506 -132 7506 -132 7506 -132 7507 -132 7507 -131 7507 -131 7507 -131 7507 -131 7507 -131 7507 -131 7507 -130 7507 -130 7508 -130 7508 -130 7508 -130 7508 -129 7508 -129 7508 -129 7508 -129 7508 -129 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7511 -128 7511 -128 7511 -127 7511 -127 7511 -127 7511 -127 7511 -127 7511 -127 7511 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7515 -127 7515 -126 7515 -126 7515 -126 7515 -126 7515 -126 7515 -126 7515 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -124 7516 -124 7516 -124 7516 -124 7516 -124 7516 -124 7516 -124 7516 -124 7517 -124 7517 -123 7517 -123 7517 -123 7517 -123 7517 -123 7517 -123 7518 -123 7518 -123 7518 -123 7518 -123 7518 -122 7518 -122 7518 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -121 7520 -121 7520 -121 7520 -121 7520 -121 7520 -121 7520 -121 7520 -121 7521 -121 7521 -120 7521 -120 7521 -120 7521 -120 7521 -120 7521 -120 7522 -120 7522 -120 7522 -120 7522 -120 7522 -119 7522 -119 7522 -119 7522 -119 7523 -119 7523 -119 7523 -119 7523 -119 7523 -119 7523 -119 7523 -118 7524 -118 7524 -118 7524 -118 7524 -118 7524 -118 7524 -118 7524 -118 7525 -118 7525 -117 7525 -117 7525 -117 7525 -117 7525 -117 7525 -117 7526 -117 7526 -117 7526 -117 7526 -117 7526 -116 7526 -116 7526 -116 7526 -116 7527 -116 7527 -116 7527 -116 7527 -116 7527 -116 7527 -116 7527 -115 7528 -115 7528 -115 7528 -115 7528 -115 7528 -115 7528 -115 7528 -115 7529 -115 7529 -114 7529 -114 7529 -114 7529 -114 7529 -114 7529 -114 7529 -114 7530 -114 7530 -114 7530 -114 7530 -113 7530 -113 7530 -113 7530 -113 7531 -113 7531 -113 7531 -113 7531 -112 7531 -112 7531 -112 7531 -112 7532 -112 7532 -112 7532 -112 7532 -111 7532 -111 7532 -111 7532 -111 7533 -111 7533 -111 7533 -110 7533 -110 7533 -110 7533 -110 7533 -110 7533 -110 7534 -110 7534 -109 7534 -109 7534 -109 7534 -109 7534 -109 7534 -109 7535 -109 7535 -108 7535 -108 7535 -108 7535 -108 7535 -108 7535 -108 7536 -108 7536 -107 7536 -107 7536 -107 7536 -107 7536 -107 7536 -107 7536 -106 7537 -106 7537 -106 7537 -106 7537 -106 7537 -106 7537 -106 7537 -105 7538 -105 7538 -105 7538 -105 7538 -105 7538 -105 7538 -105 7538 -104 7539 -104 7539 -104 7539 -104 7539 -104 7539 -104 7539 -104 7539 -103 7540 -103 7540 -103 7540 -103 7540 -103 7540 -103 7540 -102 7540 -102 7540 -102 7541 -102 7541 -102 7541 -102 7541 -102 7541 -101 7541 -101 7541 -101 7542 -101 7542 -101 7542 -101 7542 -101 7542 -100 7542 -100 7542 -100 7543 -100 7543 -100 7543 -100 7543 -100 7543 -99 7543 -99 7543 -99 7543 -99 7544 -99 7544 -99 7544 -98 7544 -98 7544 -98 7544 -98 7544 -98 7545 -98 7545 -98 7545 -97 7545 -97 7545 -97 7545 -97 7545 -97 7546 -97 7546 -97 7546 -96 7546 -96 7546 -96 7546 -96 7546 -96 7546 -96 7547 -96 7547 -95 7547 -95 7547 -95 7547 -95 7547 -95 7547 -95 7548 -94 7548 -94 7548 -94 7548 -94 7548 -94 7548 -94 7548 -94 7549 -93 7549 -93 7549 -93 7549 -93 7549 -93 7549 -93 7549 -93 7550 -92 7550 -92 7550 -92 7550 -92 7550 -92 7550 -92 7550 -92 7550 -91 7550 -91 7550 -91 7551 -91 7551 -91 7551 -91 7551 -91 7551 -91 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7552 -90 7552 -90 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -87 7553 -87 7553 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -86 7554 -86 7554 -86 7554 -86 7554 -86 7554 -86 7554 -86 7555 -86 7555 -86 7555 -86 7555 -86 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7556 -85 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -83 7556 -83 7556 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -82 7557 -82 7557 -82 7557 -82 7557 -82 7557 -82 7558 -82 7558 -82 7558 -82 7558 -82 7558 -82 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7559 -81 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -79 7559 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -78 7560 -78 7560 -78 7560 -78 7560 -78 7561 -78 7561 -78 7561 -78 7561 -78 7561 -78 7561 -78 7561 -77 7561 -77 7561 -77 7561 -77 7561 -77 7561 -77 7561 -77 7561 -77 7562 -77 7562 -77 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -74 7563 -74 7563 -74 7563 -74 7563 -74 7564 -74 7564 -74 7564 -74 7564 -74 7564 -74 7564 -74 7564 -73 7564 -73 7564 -73 7564 -73 7564 -73 7564 -73 7564 -73 7565 -73 7565 -73 7565 -73 7565 -73 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -70 7566 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7571 -67 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7572 -66 7572 -66 7572 -65 7572 -65 7572 -65 7572 -65 7572 -65 7572 -65 7572 -65 7572 -65 7573 -65 7573 -65 7573 -65 7573 -64 7573 -64 7573 -64 7573 -64 7573 -64 7573 -64 7573 -64 7574 -64 7574 -64 7574 -64 7574 -63 7574 -63 7574 -63 7574 -63 7574 -63 7574 -63 7574 -63 7575 -63 7575 -63 7575 -63 7575 -63 7575 -62 7575 -62 7575 -62 7575 -62 7575 -62 7575 -62 7576 -62 7576 -62 7576 -62 7576 -62 7576 -61 7576 -61 7576 -61 7576 -61 7576 -61 7576 -61 7577 -61 7577 -61 7577 -61 7577 -61 7577 -61 7577 -60 7577 -60 7577 -60 7577 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -59 7578 -59 7578 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -58 7579 -58 7579 -58 7580 -58 7580 -58 7580 -58 7580 -58 7580 -58 7580 -58 7580 -57 7580 -57 7580 -57 7580 -57 7581 -57 7581 -57 7581 -57 7581 -57 7581 -57 7581 -57 7581 -56 7581 -56 7581 -56 7581 -56 7582 -56 7582 -56 7582 -56 7582 -56 7582 -56 7582 -55 7582 -55 7582 -55 7582 -55 7582 -55 7583 -55 7583 -55 7583 -55 7583 -55 7583 -54 7583 -54 7583 -54 7583 -54 7583 -54 7583 -54 7584 -54 7584 -54 7584 -54 7584 -53 7584 -53 7584 -53 7584 -53 7584 -53 7584 -53 7584 -53 7585 -53 7585 -53 7585 -53 7585 -52 7585 -52 7585 -52 7585 -52 7585 -52 7585 -52 7586 -52 7586 -52 7586 -52 7586 -51 7586 -51 7586 -51 7586 -51 7586 -51 7586 -51 7586 -51 7587 -51 7587 -51 7587 -50 7587 -50 7587 -50 7587 -50 7587 -50 7587 -50 7587 -50 7587 -50 7588 -50 7588 -50 7588 -49 7588 -49 7588 -49 7588 -49 7588 -49 7588 -49 7588 -49 7588 -49 7589 -49 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -42 7594 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -41 7595 -41 7595 -41 7596 -41 7596 -41 7596 -41 7596 -41 7596 -41 7596 -41 7596 -40 7596 -40 7596 -40 7596 -40 7597 -40 7597 -40 7597 -40 7597 -40 7597 -40 7597 -39 7597 -39 7597 -39 7597 -39 7597 -39 7598 -39 7598 -39 7598 -39 7598 -38 7598 -38 7598 -38 7598 -38 7598 -38 7598 -38 7598 -38 7599 -38 7599 -38 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -36 7599 -36 7599 -36 7599 -36 7599 -36 7599 -36 7599 -36 7599 -36 7600 -36 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -33 7600 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -31 7601 -31 7601 -31 7601 -31 7601 -31 7602 -31 7602 -31 7602 -31 7602 -31 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -29 7602 -29 7602 -29 7602 -29 7602 -29 7602 -29 7602 -29 7602 -29 7603 -29 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -26 7603 -26 7604 -26 7604 -26 7604 -26 7604 -26 7604 -26 7604 -26 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -24 7604 -24 7604 -24 7604 -24 7604 -24 7605 -24 7605 -24 7605 -24 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -22 7605 -22 7605 -22 7605 -22 7605 -22 7605 -22 7605 -22 7605 -22 7606 -22 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -19 7606 -19 7607 -19 7607 -19 7607 -19 7607 -19 7607 -19 7607 -19 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -17 7607 -17 7607 -17 7607 -17 7607 -17 7608 -17 7608 -17 7608 -17 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -15 7608 -15 7608 -15 7608 -15 7608 -15 7608 -15 7608 -15 7609 -15 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -12 7609 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -10 7610 -10 7610 -10 7610 -10 7611 -10 7611 -10 7611 -10 7611 -10 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -8 7611 -8 7611 -8 7611 -8 7611 -8 7611 -8 7611 -8 7612 -8 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7617 -4 7617 -4 7617 -4 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7624 -3 7624 -3 7624 -3 7624 -3 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -2 7624 -3 7624 -3 7624 -3 7624 -3 7624 -3 7624 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7623 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7622 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7621 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7620 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7619 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7618 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -3 7617 -4 7617 -4 7617 -4 7617 -4 7617 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7616 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7615 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7614 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -4 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -5 7613 -6 7613 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -6 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -7 7612 -8 7612 -8 7612 -8 7611 -8 7611 -8 7611 -8 7611 -8 7611 -8 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -9 7611 -10 7611 -10 7611 -10 7611 -10 7611 -10 7611 -10 7610 -10 7610 -10 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -11 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7610 -12 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -13 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -14 7609 -15 7609 -15 7609 -15 7608 -15 7608 -15 7608 -15 7608 -15 7608 -15 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -16 7608 -17 7608 -17 7608 -17 7608 -17 7608 -17 7607 -17 7607 -17 7607 -17 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -18 7607 -19 7607 -19 7607 -19 7607 -19 7607 -19 7607 -19 7607 -19 7607 -19 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -20 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -21 7606 -22 7606 -22 7606 -22 7605 -22 7605 -22 7605 -22 7605 -22 7605 -22 7605 -22 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -23 7605 -24 7605 -24 7605 -24 7605 -24 7605 -24 7604 -24 7604 -24 7604 -24 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -25 7604 -26 7604 -26 7604 -26 7604 -26 7604 -26 7604 -26 7604 -26 7604 -26 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -27 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -28 7603 -29 7603 -29 7603 -29 7602 -29 7602 -29 7602 -29 7602 -29 7602 -29 7602 -29 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -30 7602 -31 7602 -31 7602 -31 7602 -31 7602 -31 7602 -31 7601 -31 7601 -31 7601 -31 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -32 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7601 -33 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -34 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -35 7600 -36 7600 -36 7600 -36 7599 -36 7599 -36 7599 -36 7599 -36 7599 -36 7599 -36 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -37 7599 -38 7599 -38 7599 -38 7599 -38 7598 -38 7598 -38 7598 -38 7598 -38 7598 -38 7598 -39 7598 -39 7598 -39 7598 -39 7598 -39 7597 -39 7597 -39 7597 -39 7597 -40 7597 -40 7597 -40 7597 -40 7597 -40 7597 -40 7597 -40 7596 -40 7596 -40 7596 -41 7596 -41 7596 -41 7596 -41 7596 -41 7596 -41 7596 -41 7596 -41 7595 -41 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7595 -42 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -43 7594 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -44 7593 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -45 7592 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -46 7591 -47 7591 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -47 7590 -48 7590 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -48 7589 -49 7589 -49 7589 -49 7588 -49 7588 -49 7588 -49 7588 -49 7588 -49 7588 -49 7588 -50 7588 -50 7588 -50 7588 -50 7587 -50 7587 -50 7587 -50 7587 -50 7587 -50 7587 -50 7587 -51 7587 -51 7587 -51 7587 -51 7586 -51 7586 -51 7586 -51 7586 -51 7586 -51 7586 -52 7586 -52 7586 -52 7586 -52 7586 -52 7585 -52 7585 -52 7585 -52 7585 -52 7585 -53 7585 -53 7585 -53 7585 -53 7585 -53 7584 -53 7584 -53 7584 -53 7584 -53 7584 -53 7584 -54 7584 -54 7584 -54 7584 -54 7584 -54 7583 -54 7583 -54 7583 -54 7583 -54 7583 -55 7583 -55 7583 -55 7583 -55 7583 -55 7583 -55 7582 -55 7582 -55 7582 -55 7582 -56 7582 -56 7582 -56 7582 -56 7582 -56 7582 -56 7582 -56 7581 -56 7581 -56 7581 -57 7581 -57 7581 -57 7581 -57 7581 -57 7581 -57 7581 -57 7581 -57 7580 -57 7580 -57 7580 -58 7580 -58 7580 -58 7580 -58 7580 -58 7580 -58 7580 -58 7580 -58 7579 -58 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7579 -59 7578 -59 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7578 -60 7577 -60 7577 -60 7577 -61 7577 -61 7577 -61 7577 -61 7577 -61 7577 -61 7577 -61 7576 -61 7576 -61 7576 -61 7576 -61 7576 -62 7576 -62 7576 -62 7576 -62 7576 -62 7576 -62 7575 -62 7575 -62 7575 -62 7575 -62 7575 -63 7575 -63 7575 -63 7575 -63 7575 -63 7575 -63 7574 -63 7574 -63 7574 -63 7574 -63 7574 -63 7574 -64 7574 -64 7574 -64 7574 -64 7574 -64 7573 -64 7573 -64 7573 -64 7573 -64 7573 -64 7573 -65 7573 -65 7573 -65 7573 -65 7573 -65 7572 -65 7572 -65 7572 -65 7572 -65 7572 -65 7572 -65 7572 -66 7572 -66 7572 -66 7572 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -66 7571 -67 7571 -67 7571 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -67 7570 -68 7570 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -68 7569 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -69 7568 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7567 -70 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -71 7566 -72 7566 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -72 7565 -73 7565 -73 7565 -73 7565 -73 7565 -73 7565 -73 7564 -73 7564 -73 7564 -73 7564 -73 7564 -73 7564 -74 7564 -74 7564 -74 7564 -74 7564 -74 7564 -74 7564 -74 7564 -74 7563 -74 7563 -74 7563 -74 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -75 7563 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -76 7562 -77 7562 -77 7562 -77 7562 -77 7561 -77 7561 -77 7561 -77 7561 -77 7561 -77 7561 -77 7561 -78 7561 -78 7561 -78 7561 -78 7561 -78 7561 -78 7561 -78 7561 -78 7560 -78 7560 -78 7560 -78 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7560 -79 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -80 7559 -81 7559 -81 7559 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -81 7558 -82 7558 -82 7558 -82 7558 -82 7558 -82 7558 -82 7558 -82 7557 -82 7557 -82 7557 -82 7557 -82 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7557 -83 7556 -83 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -84 7556 -85 7556 -85 7556 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -85 7555 -86 7555 -86 7555 -86 7555 -86 7555 -86 7555 -86 7554 -86 7554 -86 7554 -86 7554 -86 7554 -86 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7554 -87 7553 -87 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -88 7553 -89 7553 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -89 7552 -90 7552 -90 7552 -90 7552 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -90 7551 -91 7551 -91 7551 -91 7551 -91 7551 -91 7551 -91 7551 -91 7550 -91 7550 -92 7550 -92 7550 -92 7550 -92 7550 -92 7550 -92 7550 -92 7550 -93 7550 -93 7549 -93 7549 -93 7549 -93 7549 -93 7549 -93 7549 -94 7549 -94 7548 -94 7548 -94 7548 -94 7548 -94 7548 -94 7548 -95 7548 -95 7547 -95 7547 -95 7547 -95 7547 -95 7547 -96 7547 -96 7547 -96 7546 -96 7546 -96 7546 -96 7546 -96 7546 -97 7546 -97 7546 -97 7546 -97 7545 -97 7545 -97 7545 -97 7545 -98 7545 -98 7545 -98 7545 -98 7544 -98 7544 -98 7544 -98 7544 -99 7544 -99 7544 -99 7544 -99 7543 -99 7543 -99 7543 -100 7543 -100 7543 -100 7543 -100 7543 -100 7543 -100 7542 -100 7542 -101 7542 -101 7542 -101 7542 -101 7542 -101 7542 -101 7541 -101 7541 -102 7541 -102 7541 -102 7541 -102 7541 -102 7541 -102 7540 -102 7540 -103 7540 -103 7540 -103 7540 -103 7540 -103 7540 -103 7540 -104 7539 -104 7539 -104 7539 -104 7539 -104 7539 -104 7539 -104 7539 -105 7538 -105 7538 -105 7538 -105 7538 -105 7538 -105 7538 -105 7538 -106 7537 -106 7537 -106 7537 -106 7537 -106 7537 -106 7537 -106 7537 -107 7536 -107 7536 -107 7536 -107 7536 -107 7536 -107 7536 -108 7536 -108 7536 -108 7535 -108 7535 -108 7535 -108 7535 -108 7535 -109 7535 -109 7535 -109 7534 -109 7534 -109 7534 -109 7534 -109 7534 -110 7534 -110 7534 -110 7533 -110 7533 -110 7533 -110 7533 -110 7533 -111 7533 -111 7533 -111 7533 -111 7532 -111 7532 -111 7532 -112 7532 -112 7532 -112 7532 -112 7532 -112 7531 -112 7531 -112 7531 -113 7531 -113 7531 -113 7531 -113 7531 -113 7530 -113 7530 -113 7530 -114 7530 -114 7530 -114 7530 -114 7530 -114 7529 -114 7529 -114 7529 -114 7529 -114 7529 -114 7529 -115 7529 -115 7529 -115 7528 -115 7528 -115 7528 -115 7528 -115 7528 -115 7528 -115 7528 -116 7527 -116 7527 -116 7527 -116 7527 -116 7527 -116 7527 -116 7527 -116 7526 -116 7526 -116 7526 -117 7526 -117 7526 -117 7526 -117 7526 -117 7526 -117 7525 -117 7525 -117 7525 -117 7525 -117 7525 -118 7525 -118 7525 -118 7524 -118 7524 -118 7524 -118 7524 -118 7524 -118 7524 -118 7524 -119 7523 -119 7523 -119 7523 -119 7523 -119 7523 -119 7523 -119 7523 -119 7522 -119 7522 -119 7522 -120 7522 -120 7522 -120 7522 -120 7522 -120 7522 -120 7521 -120 7521 -120 7521 -120 7521 -120 7521 -121 7521 -121 7521 -121 7520 -121 7520 -121 7520 -121 7520 -121 7520 -121 7520 -121 7520 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7519 -122 7518 -122 7518 -123 7518 -123 7518 -123 7518 -123 7518 -123 7518 -123 7517 -123 7517 -123 7517 -123 7517 -123 7517 -124 7517 -124 7517 -124 7516 -124 7516 -124 7516 -124 7516 -124 7516 -124 7516 -124 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -125 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7516 -126 7515 -126 7515 -126 7515 -126 7515 -126 7515 -126 7515 -127 7515 -127 7515 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7514 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7513 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7512 -127 7511 -127 7511 -127 7511 -127 7511 -127 7511 -127 7511 -128 7511 -128 7511 -128 7511 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7510 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -128 7509 -129 7509 -129 7508 -129 7508 -129 7508 -129 7508 -130 7508 -130 7508 -130 7508 -130 7508 -130 7507 -131 7507 -131 7507 -131 7507 -131 7507 -131 7507 -131 7507 -132 7507 -132 7507 -132 7506 -132 7506 -132 7506 -133 7506 -133 7506 -133 7506 -133 7506 -133 7506 -134 7506 -134 7505 -134 7505 -134 7505 -134 7505 -135 7505 -135 7505 -135 7505 -135 7505 -135 7505 -136 7504 -136 7504 -136 7504 -136 7504 -136 7504 -137 7504 -137 7504 -137 7504 -137 7504 -137 7503 -138 7503 -138 7503 -138 7503 -138 7503 -138 7503 -139 7503 -139 7503 -139 7503 -139 7502 -139 7502 -140 7502 -140 7502 -140 7502 -140 7502 -140 7502 -141 7501 -141 7501 -141 7501 -141 7501 -141 7501 -141 7500 -142 7500 -142 7500 -142 7500 -142 7499 -142 7499 -143 7499 -143 7499 -143 7499 -143 7498 -143 7498 -144 7498 -144 7498 -144 7498 -144 7497 -144 7497 -145 7497 -145 7497 -145 7496 -145 7496 -145 7496 -146 7496 -146 7496 -146 7495 -146 7495 -146 7495 -147 7495 -147 7494 -147 7494 -147 7494 -147 7494 -148 7494 -148 7493 -148 7493 -148 7493 -148 7493 -149 7492 -149 7492 -149 7492 -149 7492 -149 7492 -150 7491 -150 7491 -150 7491 -150 7491 -150 7491 -151 7490 -151 7490 -151 7490 -151 7490 -151 7489 -151 7489 -152 7489 -152 7489 -152 7489 -152 7488 -152 7488 -153 7488 -153 7488 -153 7487 -153 7487 -153 7487 -154 7487 -154 7487 -154 7486 -154 7486 -154 7486 -155 7486 -155 7485 -155 7485 -156 7485 -156 7485 -156 7485 -157 7484 -157 7484 -157 7484 -158 7484 -158 7484 -158 7483 -159 7483 -159 7483 -159 7483 -160 7482 -160 7482 -160 7482 -160 7482 -161 7482 -161 7481 -161 7481 -162 7481 -162 7481 -162 7480 -163 7480 -163 7480 -163 7480 -164 7480 -164 7479 -164 7479 -165 7479 -165 7479 -165 7478 -166 7478 -166 7478 -166 7478 -167 7478 -167 7477 -167 7477 -167 7477 -168 7477 -168 7477 -168 7476 -169 7476 -169 7476 -169 7476 -170 7475 -170 7475 -170 7475 -171 7475 -171 7475 -171 7474 -172 7474 -172 7474 -172 7474 -173 7473 -173 7473 -173 7473 -173 7473 -174 7473 -174 7472 -174 7472 -175 7472 -175 7472 -175 7471 -176 7471 -176 7471 -176 7471 -177 7471 -177 7470 -177 7470 -178 7470 -178 7470 -178 7470 -179 7469 -179 7469 -179 7469 -180 7469 -180 7468 -180 7468 -180 7468 -181 7468 -181 7468 -181 7467 -182 7467 -182 7467 -182 7467 -183 7466 -183 7466 -183 7466 -184 7466 -184 7465 -184 7465 -185 7464 -185 7464 -185 7464 -186 7463 -186 7463 -186 7463 -187 7462 -187 7462 -187 7461 -187 7461 -188 7461 -188 7460 -188 7460 -189 7459 -189 7459 -189 7459 -190 7458 -190 7458 -190 7458 -191 7457 -191 7457 -192 7456 -192 7456 -192 7456 -193 7455 -193 7455 -194 7455 -194 7454 -194 7454 -195 7453 -195 7453 -196 7453 -196 7452 -197 7452 -197 7452 -197 7451 -198 7451 -198 7450 -199 7450 -199 7450 -199 7449 -200 7449 -200 7449 -201 7448 -201 7448 -201 7447 -202 7447 -202 7447 -203 7446 -203 7446 -203 7446 -204 7445 -204 7445 -205 7444 -205 7444 -205 7444 -206 7443 -206 7443 -207 7442 -207 7442 -207 7442 -208 7441 -208 7441 -209 7441 -209 7440 -209 7440 -210 7439 -210 7439 -211 7439 -211 7438 -211 7438 -212 7438 -212 7437 -213 7437 -213 7436 -213 7436 -214 7436 -214 7435 -215 7435 -215 7435 -215 7434 -216 7434 -216 7433 -217 7433 -217 7433 -218 7432 -218 7432 -218 7432 -219 7431 -219 7431 -220 7430 -220 7430 -220 7430 -221 7429 -221 7429 -222 7429 -222 7428 -222 7428 -223 7427 -223 7427 -224 7427 -224 7426 -224 7426 -225 7426 -225 7425 -226 7425 -226 7424 -226 7424 -227 7424 -227 7423 -228 7423 -228 7422 -228 7422 -229 7422 -229 7421 -230 7421 -230 7421 -230 7420 -231 7420 -231 7419 -232 7419 -232 7419 -232 7418 -233 7418 -233 7418 -234 7417 -234 7417 -234 7416 -235 7416 -235 7416 -236 7415 -236 7415 -236 7415 -237 7414 -237 7414 -238 7413 -238 7413 -239 7413 -239 7412 -239 7412 -240 7412 -240 7411 -241 7411 -241 7410 -241 7410 -242 7409 -242 7409 -243 7408 -243 7407 -243 7407 -244 7406 -244 7406 -245 7405 -245 7405 -246 7404 -246 7404 -247 7403 -247 7403 -248 7402 -248 7402 -249 7401 -249 7401 -250 7400 -250 7400 -251 7399 -252 7399 -252 7398 -253 7398 -253 7397 -254 7397 -254 7396 -255 7396 -255 7395 -256 7395 -256 7394 -257 7394 -257 7393 -258 7393 -259 7392 -259 7391 -260 7391 -260 7390 -261 7390 -261 7389 -262 7389 -262 7388 -263 7388 -263 7387 -264 7387 -265 7386 -265 7386 -266 7385 -266 7385 -267 7384 -267 7384 -268 7383 -268 7383 -269 7382 -269 7382 -270 7381 -271 7381 -271 7380 -272 7380 -272 7379 -273 7379 -273 7378 -274 7378 -274 7377 -275 7377 -275 7376 -276 7375 -276 7375 -277 7374 -278 7374 -278 7373 -279 7373 -279 7372 -280 7372 -280 7371 -281 7371 -281 7370 -282 7370 -282 7369 -283 7369 -284 7368 -284 7368 -285 7367 -285 7367 -286 7366 -286 7366 -287 7365 -287 7365 -288 7364 -288 7364 -289 7363 -289 7363 -290 7362 -291 7362 -291 7361 -292 7361 -292 7360 -293 7359 -293 7359 -294 7358 -294 7358 -295 7357 -295 7357 -296 7356 -297 7356 -297 7355 -298 7355 -298 7354 -299 7354 -299 7353 -300 7353 -300 7352 -301 7352 -301 7351 -302 7351 -303 7350 -303 7350 -304 7349 -304 7349 -305 7348 -305 7348 -306 7347 -306 7347 -307 7346 -307 7346 -308 7345 -308 7345 -309 7344 -310 7343 -310 7343 -311 7342 -311 7342 -312 7341 -312 7341 -313 7340 -313 7340 -314 7339 -314 7339 -315 7338 -316 7338 -316 7337 -317 7337 -317 7336 -318 7336 -318 7335 -319 7335 -319 7334 -320 7334 -320 7333 -321 7333 -321 7332 -322 7332 -323 7331 -323 7331 -324 7330 -324 7330 -325 7329 -325 7329 -326 7328 -326 7327 -327 7327 -327 7326 -328 7326 -329 7325 -329 7325 -330 7324 -330 7324 -331 7323 -331 7323 -332 7322 -332 7322 -333 7321 -333 7321 -334 7320 -335 7320 -336 7319 -336 7319 -337 7318 -338 7318 -339 7317 -339 7317 -340 7316 -341 7316 -342 7315 -342 7315 -343 7314 -344 7314 -344 7313 -345 7313 -346 7312 -347 7312 -347 7311 -348 7310 -349 7309 -350 7309 -350 7308 -351 7307 -352 7306 -353 7306 -353 7305 -354 7304 -355 7304 -355 7303 -356 7302 -357 7301 -358 7301 -358 7300 -359 7299 -360 7298 -361 7298 -361 7297 -362 7296 -363 7296 -364 7295 -364 7294 -365 7293 -366 7293 -366 7292 -367 7291 -368 7291 -369 7290 -369 7289 -370 7288 -371 7288 -372 7287 -372 7286 -373 7285 -374 7285 -375 7284 -375 7283 -376 7283 -377 7282 -377 7281 -378 7280 -379 7280 -380 7279 -380 7278 -381 7277 -382 7277 -383 7276 -383 7275 -384 7275 -385 7274 -386 7273 -386 7272 -387 7272 -388 7271 -389 7270 -389 7269 -390 7269 -391 7268 -391 7267 -392 7267 -393 7266 -394 7265 -394 7264 -395 7264 -396 7263 -397 7262 -397 7261 -398 7261 -399 7260 -400 7259 -400 7259 -401 7258 -402 7257 -402 7256 -403 7256 -404 7255 -405 7254 -405 7253 -406 7253 -407 7252 -408 7251 -408 7251 -409 7250 -410 7249 -411 7248 -411 7248 -412 7247 -413 7246 -413 7246 -414 7245 -415 7244 -416 7243 -416 7243 -417 7242 -418 7241 -419 7240 -419 7240 -420 7239 -421 7238 -422 7238 -422 7237 -423 7236 -424 7235 -424 7235 -425 7234 -426 7233 -427 7232 -427 7232 -428 7231 -429 7230 -430 7230 -430 7229 -431 7228 -432 7227 -433 7227 -434 7226 -435 7225 -436 7224 -437 7224 -438 7223 -438 7222 -439 7222 -440 7221 -441 7220 -442 7219 -443 7219 -444 7218 -445 7217 -446 7216 -447 7215 -448 7214 -448 7213 -449 7212 -450 7211 -451 7211 -452 7210 -453 7209 -454 7208 -455 7207 -456 7206 -457 7205 -458 7204 -458 7203 -459 7202 -460 7201 -461 7200 -462 7199 -463 7198 -464 7197 -465 7197 -466 7196 -467 7195 -468 7194 -469 7193 -469 7192 -470 7191 -471 7190 -472 7189 -473 7188 -474 7187 -475 7186 -476 7185 -477 7184 -478 7183 -479 7183 -479 7182 -480 7181 -481 7180 -482 7179 -483 7178 -484 7177 -485 7176 -486 7175 -487 7174 -488 7173 -489 7172 -489 7171 -490 7170 -491 7170 -492 7169 -493 7168 -494 7167 -495 7166 -496 7165 -497 7164 -498 7163 -499 7162 -499 7161 -500 7160 -501 7159 -502 7158 -503 7157 -504 7156 -505 7156 -506 7155 -507 7154 -508 7153 -509 7152 -510 7151 -510 7150 -511 7149 -512 7148 -513 7147 -514 7146 -515 7145 -516 7144 -517 7143 -518 7142 -519 7142 -520 7141 -520 7140 -521 7139 -522 7138 -523 7137 -525 7136 -526 7135 -527 7134 -528 7133 -529 7132 -530 7131 -531 7130 -532 7129 -533 7128 -534 7128 -535 7127 -536 7126 -537 7125 -538 7124 -539 7123 -540 7122 -541 7121 -542 7120 -543 7119 -544 7118 -545 7117 -546 7116 -547 7115 -548 7115 -549 7114 -550 7113 -551 7112 -552 7111 -553 7110 -554 7109 -555 7108 -557 7107 -558 7106 -559 7105 -560 7104 -561 7103 -562 7102 -563 7101 -564 7101 -565 7100 -566 7099 -567 7098 -568 7097 -569 7096 -570 7095 -571 7094 -572 7093 -573 7092 -574 7091 -575 7090 -576 7089 -577 7088 -578 7087 -579 7087 -580 7086 -581 7085 -582 7084 -583 7083 -584 7082 -585 7081 -586 7080 -588 7079 -589 7078 -590 7077 -591 7076 -592 7075 -593 7074 -594 7073 -595 7073 -596 7072 -597 7071 -598 7070 -599 7069 -600 7068 -601 7067 -602 7066 -603 7065 -604 7064 -605 7063 -606 7062 -607 7061 -608 7060 -609 7060 -610 7059 -611 7058 -612 7057 -613 7056 -614 7055 -615 7054 -616 7053 -617 7052 -618 7051 -620 7050 -621 7049 -622 7048 -623 7047 -624 7046 -625 7046 -626 7045 -627 7044 -628 7043 -629 7042 -630 7040 -631 7039 -632 7038 -633 7036 -634 7035 -635 7034 -636 7032 -638 7031 -639 7030 -640 7028 -642 7027 -643 7026 -644 7024 -645 7023 -647 7022 -648 7020 -649 7019 -650 7018 -652 7016 -653 7015 -654 7014 -656 7012 -657 7011 -658 7009 -659 7008 -661 7007 -662 7005 -663 7004 -664 7003 -666 7001 -667 7000 -668 6999 -670 6997 -671 6996 -672 6995 -673 6993 -675 6992 -676 6991 -677 6989 -678 6988 -680 6987 -681 6985 -682 6984 -684 6983 -685 6981 -686 6980 -687 6979 -689 6977 -690 6976 -691 6974 -692 6973 -694 6972 -695 6970 -696 6969 -698 6968 -699 6966 -700 6965 -701 6964 -703 6962 -704 6961 -705 6960 -706 6958 -708 6957 -709 6956 -710 6954 -712 6953 -713 6952 -714 6950 -715 6949 -717 6948 -718 6946 -719 6945 -720 6944 -722 6942 -723 6941 -724 6940 -726 6938 -727 6937 -728 6935 -729 6934 -731 6933 -732 6931 -733 6930 -735 6929 -736 6927 -738 6926 -740 6925 -741 6923 -743 6922 -744 6921 -746 6919 -748 6918 -749 6917 -751 6915 -752 6914 -754 6913 -756 6911 -757 6910 -759 6909 -760 6907 -762 6906 -764 6905 -765 6903 -767 6902 -768 6900 -770 6899 -772 6898 -773 6896 -775 6895 -776 6894 -778 6892 -780 6891 -781 6890 -783 6888 -784 6887 -786 6886 -788 6884 -789 6883 -791 6882 -792 6880 -794 6879 -796 6877 -797 6876 -799 6874 -800 6872 -802 6870 -804 6869 -805 6867 -807 6865 -808 6864 -810 6862 -812 6860 -813 6858 -815 6857 -816 6855 -818 6853 -820 6852 -821 6850 -823 6848 -824 6846 -826 6845 -828 6843 -829 6841 -831 6840 -832 6838 -834 6836 -836 6834 -837 6833 -839 6831 -840 6829 -842 6828 -844 6826 -845 6824 -847 6822 -848 6821 -850 6819 -852 6817 -853 6816 -855 6814 -856 6812 -858 6810 -860 6809 -861 6807 -863 6805 -864 6804 -866 6802 -868 6800 -869 6798 -871 6797 -872 6795 -874 6793 -876 6792 -878 6790 -879 6788 -881 6786 -883 6785 -885 6783 -887 6781 -889 6780 -891 6778 -893 6776 -895 6774 -897 6773 -898 6771 -900 6769 -902 6768 -904 6766 -906 6764 -908 6762 -910 6761 -912 6759 -914 6757 -916 6756 -917 6754 -919 6752 -921 6751 -923 6749 -925 6747 -927 6745 -929 6744 -931 6742 -933 6740 -935 6739 -936 6737 -938 6735 -940 6733 -942 6732 -944 6730 -946 6728 -948 6727 -950 6725 -952 6723 -954 6721 -955 6720 -957 6718 -959 6716 -961 6715 -963 6713 -965 6711 -967 6709 -969 6708 -971 6706 -972 6704 -974 6703 -976 6701 -978 6699 -980 6697 -982 6696 -984 6694 -986 6692 -988 6691 -990 6689 -991 6687 -993 6684 -995 6682 -997 6680 -999 6677 -1001 6675 -1003 6673 -1005 6670 -1007 6668 -1009 6666 -1012 6663 -1015 6661 -1017 6659 -1020 6656 -1022 6654 -1025 6652 -1028 6649 -1030 6647 -1033 6645 -1035 6642 -1038 6640 -1041 6638 -1043 6635 -1046 6633 -1048 6631 -1051 6628 -1053 6626 -1056 6624 -1059 6622 -1061 6619 -1064 6617 -1066 6615 -1069 6612 -1072 6610 -1074 6608 -1077 6605 -1079 6603 -1082 6601 -1085 6598 -1087 6596 -1090 6594 -1092 6591 -1095 6589 -1098 6587 -1100 6584 -1103 6582 -1105 6580 -1108 6577 -1110 6575 -1113 6573 -1116 6570 -1118 6568 -1121 6566 -1123 6563 -1126 6561 -1129 6559 -1131 6556 -1135 6554 -1138 6552 -1141 6549 -1144 6547 -1147 6545 -1151 6542 -1154 6540 -1157 6537 -1160 6534 -1163 6530 -1166 6527 -1170 6523 -1173 6520 -1176 6516 -1179 6512 -1182 6509 -1185 6505 -1189 6502 -1192 6498 -1195 6495 -1198 6491 -1201 6487 -1204 6484 -1208 6480 -1211 6477 -1214 6473 -1217 6470 -1220 6466 -1223 6462 -1227 6459 -1230 6455 -1233 6452 -1236 6448 -1239 6445 -1242 6441 -1246 6437 -1249 6434 -1252 6430 -1255 6427 -1258 6423 -1262 6420 -1266 6416 -1269 6412 -1273 6409 -1277 6405 -1280 6402 -1284 6398 -1288 6395 -1291 6391 -1295 6387 -1299 6382 -1302 6378 -1306 6373 -1310 6368 -1314 6364 -1317 6359 -1321 6354 -1325 6350 -1328 6345 -1332 6340 -1336 6336 -1339 6331 -1343 6326 -1347 6321 -1350 6317 -1354 6312 -1358 6307 -1362 6303 -1365 6298 -1369 6295 -1373 6294 -1376 6289 -1380 1920 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 +7680 5312 +0 0 +0 0 +0 0 +0 0 +1383 6306 +1380 6309 +1377 6312 +1374 6315 +1371 6318 +1367 6322 +1364 6325 +1361 6328 +1358 6331 +1355 6334 +1352 6337 +1349 6340 +1345 6343 +1342 6346 +1339 6349 +1336 6351 +1333 6354 +1330 6357 +1327 6360 +1324 6363 +1321 6366 +1318 6369 +1315 6372 +1312 6375 +1309 6378 +1306 6381 +1303 6384 +1300 6387 +1297 6389 +1294 6392 +1291 6395 +1288 6398 +1285 6401 +1282 6404 +1279 6407 +1276 6409 +1273 6412 +1270 6415 +1267 6418 +1264 6421 +1261 6423 +1258 6426 +1256 6429 +1253 6432 +1250 6435 +1247 6437 +1244 6440 +1241 6443 +1238 6446 +1236 6448 +1233 6451 +1230 6454 +1227 6456 +1224 6459 +1221 6462 +1219 6465 +1216 6467 +1213 6470 +1210 6473 +1207 6475 +1205 6478 +1202 6481 +1199 6483 +1196 6486 +1194 6489 +1191 6491 +1188 6494 +1185 6496 +1183 6499 +1180 6502 +1177 6504 +1175 6507 +1172 6509 +1169 6512 +1167 6515 +1164 6517 +1161 6520 +1159 6522 +1156 6525 +1153 6527 +1151 6530 +1148 6533 +1145 6535 +1143 6538 +1140 6540 +1138 6543 +1135 6545 +1132 6548 +1130 6550 +1127 6553 +1125 6555 +1122 6558 +1119 6560 +1117 6562 +1114 6565 +1112 6567 +1109 6570 +1107 6572 +1104 6575 +1102 6577 +1099 6580 +1097 6582 +1094 6584 +1092 6587 +1089 6589 +1087 6592 +1084 6594 +1082 6596 +1079 6599 +1077 6601 +1074 6603 +1072 6606 +1070 6608 +1067 6610 +1065 6613 +1062 6615 +1060 6617 +1057 6620 +1055 6622 +1053 6624 +1050 6627 +1048 6629 +1046 6631 +1043 6634 +1041 6636 +1038 6638 +1036 6640 +1034 6643 +1031 6645 +1029 6647 +1027 6649 +1024 6652 +1022 6654 +1020 6656 +1018 6658 +1015 6660 +1013 6663 +1011 6665 +1008 6667 +1006 6669 +1004 6671 +1002 6674 +999 6676 +997 6678 +995 6680 +993 6682 +990 6684 +988 6687 +986 6689 +984 6691 +982 6693 +979 6695 +977 6697 +975 6699 +973 6701 +971 6704 +968 6706 +966 6708 +964 6710 +962 6712 +960 6714 +958 6716 +956 6718 +953 6720 +951 6722 +949 6724 +947 6726 +945 6728 +943 6730 +941 6732 +939 6734 +937 6737 +934 6739 +932 6741 +930 6743 +928 6745 +926 6747 +924 6748 +922 6750 +920 6752 +918 6754 +916 6756 +914 6758 +912 6760 +910 6762 +908 6764 +906 6766 +904 6768 +902 6770 +900 6772 +898 6774 +896 6776 +894 6778 +892 6780 +890 6781 +888 6783 +886 6785 +884 6787 +882 6789 +880 6791 +878 6793 +876 6795 +875 6796 +873 6798 +871 6800 +869 6802 +867 6804 +865 6806 +863 6807 +861 6809 +859 6811 +858 6813 +856 6815 +854 6817 +852 6818 +850 6820 +848 6822 +846 6824 +845 6825 +843 6827 +841 6829 +839 6831 +837 6833 +835 6834 +834 6836 +832 6838 +830 6840 +828 6841 +826 6843 +825 6845 +823 6846 +821 6848 +819 6850 +818 6852 +816 6853 +814 6855 +812 6857 +811 6858 +809 6860 +807 6862 +805 6863 +804 6865 +802 6867 +800 6868 +799 6870 +797 6872 +795 6873 +793 6875 +792 6877 +790 6878 +788 6880 +787 6882 +785 6883 +783 6885 +782 6886 +780 6888 +778 6890 +777 6891 +775 6893 +774 6894 +772 6896 +770 6898 +769 6899 +767 6901 +765 6902 +764 6904 +762 6905 +761 6907 +759 6908 +757 6910 +756 6912 +754 6913 +753 6915 +751 6916 +750 6918 +748 6919 +746 6921 +745 6922 +743 6924 +742 6925 +740 6927 +739 6928 +737 6930 +736 6931 +734 6933 +733 6934 +731 6936 +730 6937 +728 6939 +727 6940 +725 6941 +724 6943 +722 6944 +721 6946 +719 6947 +718 6949 +716 6950 +715 6952 +713 6953 +712 6954 +710 6956 +709 6957 +707 6959 +706 6960 +705 6961 +703 6963 +702 6964 +700 6966 +699 6967 +697 6968 +696 6970 +695 6971 +693 6972 +692 6974 +690 6975 +689 6976 +688 6978 +686 6979 +685 6981 +683 6982 +682 6983 +681 6985 +679 6986 +678 6987 +677 6989 +675 6990 +674 6991 +673 6992 +671 6994 +670 6995 +669 6996 +667 6998 +666 6999 +665 7000 +663 7002 +662 7003 +661 7004 +659 7005 +658 7007 +657 7008 +655 7009 +654 7010 +653 7012 +652 7013 +650 7014 +649 7015 +648 7017 +647 7018 +645 7019 +644 7020 +643 7022 +641 7023 +640 7024 +639 7025 +638 7026 +636 7028 +635 7029 +634 7030 +633 7031 +632 7032 +630 7034 +629 7035 +628 7036 +627 7037 +626 7038 +624 7040 +623 7041 +622 7042 +621 7043 +620 7044 +618 7045 +617 7046 +616 7048 +615 7049 +614 7050 +612 7051 +611 7052 +610 7053 +609 7054 +608 7056 +607 7057 +606 7058 +604 7059 +603 7060 +602 7061 +601 7062 +600 7063 +599 7064 +598 7066 +596 7067 +595 7068 +594 7069 +593 7070 +592 7071 +591 7072 +590 7073 +589 7074 +588 7075 +587 7076 +585 7077 +584 7079 +583 7080 +582 7081 +581 7082 +580 7083 +579 7084 +578 7085 +577 7086 +576 7087 +575 7088 +574 7089 +573 7090 +572 7091 +570 7092 +569 7093 +568 7094 +567 7095 +566 7096 +565 7097 +564 7098 +563 7099 +562 7100 +561 7101 +560 7102 +559 7103 +558 7104 +557 7105 +556 7106 +555 7107 +554 7108 +553 7109 +552 7110 +551 7111 +550 7112 +549 7113 +548 7114 +547 7115 +546 7116 +545 7117 +544 7118 +543 7119 +542 7119 +541 7120 +540 7121 +539 7122 +539 7123 +538 7124 +537 7125 +536 7126 +535 7127 +534 7128 +533 7129 +532 7130 +531 7130 +530 7131 +529 7132 +529 7133 +528 7134 +527 7135 +526 7136 +525 7137 +524 7137 +523 7138 +523 7139 +522 7140 +521 7141 +520 7142 +519 7142 +518 7143 +517 7144 +517 7145 +516 7146 +515 7147 +514 7147 +513 7148 +512 7149 +511 7150 +511 7151 +510 7151 +509 7152 +508 7153 +507 7154 +506 7155 +505 7156 +505 7156 +504 7157 +503 7158 +502 7159 +501 7159 +500 7160 +500 7161 +499 7162 +498 7163 +497 7163 +496 7164 +495 7165 +495 7166 +494 7167 +493 7167 +492 7168 +491 7169 +490 7170 +489 7171 +489 7171 +488 7172 +487 7173 +486 7174 +485 7175 +484 7175 +484 7176 +483 7177 +482 7178 +481 7179 +480 7179 +479 7180 +479 7181 +478 7182 +477 7183 +476 7184 +475 7184 +474 7185 +474 7186 +473 7187 +472 7188 +471 7188 +470 7189 +470 7190 +469 7191 +468 7192 +467 7193 +466 7193 +465 7194 +465 7195 +464 7196 +463 7197 +462 7198 +461 7198 +460 7199 +460 7200 +459 7201 +458 7202 +457 7202 +456 7203 +455 7204 +455 7205 +454 7206 +453 7207 +452 7207 +451 7208 +451 7209 +450 7210 +449 7211 +448 7211 +447 7212 +446 7213 +446 7214 +445 7215 +444 7215 +443 7216 +442 7217 +442 7218 +441 7218 +440 7219 +439 7220 +438 7221 +438 7222 +437 7222 +436 7223 +435 7224 +434 7225 +434 7225 +433 7226 +432 7227 +431 7228 +430 7229 +430 7229 +429 7230 +428 7231 +427 7232 +426 7232 +426 7233 +425 7234 +424 7235 +423 7235 +422 7236 +422 7237 +421 7238 +420 7239 +419 7239 +418 7240 +418 7241 +417 7242 +416 7242 +415 7243 +415 7244 +414 7245 +413 7245 +412 7246 +411 7247 +411 7248 +410 7248 +409 7249 +408 7250 +408 7251 +407 7251 +406 7252 +405 7253 +405 7254 +404 7254 +403 7255 +402 7256 +402 7257 +401 7257 +400 7258 +399 7259 +399 7259 +398 7260 +397 7261 +396 7262 +396 7262 +395 7263 +394 7264 +393 7264 +393 7265 +392 7266 +391 7267 +391 7267 +390 7268 +389 7269 +388 7269 +388 7270 +387 7271 +386 7271 +386 7272 +385 7273 +384 7273 +383 7274 +383 7275 +382 7276 +381 7276 +381 7277 +380 7278 +379 7278 +379 7279 +378 7280 +377 7280 +376 7281 +376 7282 +375 7282 +374 7283 +374 7284 +373 7284 +372 7285 +372 7286 +371 7286 +370 7287 +370 7287 +369 7288 +368 7289 +368 7289 +367 7290 +366 7291 +366 7291 +365 7292 +364 7293 +364 7293 +363 7294 +362 7294 +362 7295 +361 7296 +360 7296 +360 7297 +359 7298 +358 7298 +358 7299 +357 7299 +357 7300 +356 7301 +355 7301 +355 7302 +354 7303 +353 7303 +353 7304 +352 7304 +352 7305 +351 7306 +350 7306 +350 7307 +349 7307 +348 7308 +348 7309 +347 7309 +347 7310 +346 7310 +345 7311 +345 7312 +344 7312 +343 7313 +343 7313 +342 7314 +342 7315 +341 7315 +340 7316 +340 7316 +339 7317 +339 7318 +338 7318 +337 7319 +337 7319 +336 7320 +336 7320 +335 7321 +334 7322 +334 7322 +333 7323 +333 7323 +332 7324 +331 7325 +331 7325 +330 7326 +330 7326 +329 7327 +328 7327 +328 7328 +327 7329 +327 7329 +326 7330 +325 7330 +325 7331 +324 7331 +324 7332 +323 7333 +322 7333 +322 7334 +321 7334 +321 7335 +320 7335 +319 7336 +319 7336 +318 7337 +318 7338 +317 7338 +317 7339 +316 7339 +315 7340 +315 7340 +314 7341 +314 7341 +313 7342 +312 7343 +312 7343 +311 7344 +311 7344 +310 7345 +310 7345 +309 7346 +308 7346 +308 7347 +307 7347 +307 7348 +306 7349 +306 7349 +305 7350 +304 7350 +304 7351 +303 7351 +303 7352 +302 7352 +302 7353 +301 7353 +300 7354 +300 7354 +299 7355 +299 7355 +298 7356 +298 7357 +297 7357 +297 7358 +296 7358 +295 7359 +295 7359 +294 7360 +294 7360 +293 7361 +293 7361 +292 7362 +292 7362 +291 7363 +291 7363 +290 7364 +289 7364 +289 7365 +288 7365 +288 7366 +287 7366 +287 7367 +286 7367 +286 7368 +285 7368 +285 7369 +284 7369 +283 7370 +283 7370 +282 7371 +282 7371 +281 7372 +281 7372 +280 7373 +280 7373 +279 7374 +279 7374 +278 7375 +278 7375 +277 7376 +277 7376 +276 7377 +276 7377 +275 7378 +274 7378 +274 7379 +273 7379 +273 7380 +272 7380 +272 7381 +271 7381 +271 7382 +270 7382 +270 7383 +269 7383 +269 7384 +268 7384 +268 7385 +267 7385 +267 7386 +266 7386 +266 7387 +265 7387 +265 7388 +264 7388 +264 7388 +263 7389 +262 7389 +262 7390 +261 7390 +261 7391 +260 7391 +260 7392 +259 7392 +259 7393 +258 7393 +258 7394 +257 7394 +257 7395 +256 7395 +256 7395 +255 7396 +255 7396 +254 7397 +254 7397 +253 7398 +253 7398 +252 7399 +252 7399 +251 7400 +251 7400 +250 7400 +250 7401 +249 7401 +249 7402 +248 7402 +248 7403 +247 7403 +247 7404 +246 7404 +246 7404 +245 7405 +245 7405 +244 7406 +244 7406 +243 7407 +243 7407 +242 7408 +242 7408 +242 7408 +241 7409 +241 7409 +240 7410 +240 7410 +239 7411 +239 7411 +238 7411 +238 7412 +237 7412 +237 7413 +236 7413 +236 7414 +235 7414 +235 7414 +234 7415 +234 7415 +234 7416 +233 7416 +233 7417 +232 7417 +232 7417 +231 7418 +231 7418 +230 7419 +230 7419 +229 7419 +229 7420 +229 7420 +228 7421 +228 7421 +227 7421 +227 7422 +226 7422 +226 7423 +225 7423 +225 7423 +225 7424 +224 7424 +224 7425 +223 7425 +223 7425 +222 7426 +222 7426 +222 7427 +221 7427 +221 7427 +220 7428 +220 7428 +219 7429 +219 7429 +219 7429 +218 7430 +218 7430 +217 7431 +217 7431 +216 7431 +216 7432 +216 7432 +215 7433 +215 7433 +214 7433 +214 7434 +214 7434 +213 7434 +213 7435 +212 7435 +212 7436 +212 7436 +211 7436 +211 7437 +210 7437 +210 7437 +210 7438 +209 7438 +209 7439 +208 7439 +208 7439 +208 7440 +207 7440 +207 7440 +206 7441 +206 7441 +206 7441 +205 7442 +205 7442 +204 7442 +204 7443 +204 7443 +203 7444 +203 7444 +203 7444 +202 7445 +202 7445 +201 7445 +201 7446 +201 7446 +200 7446 +200 7447 +199 7447 +199 7447 +199 7448 +198 7448 +198 7448 +198 7449 +197 7449 +197 7449 +197 7450 +196 7450 +196 7451 +195 7451 +195 7451 +195 7452 +194 7452 +194 7452 +194 7453 +193 7453 +193 7453 +193 7454 +192 7454 +192 7454 +192 7455 +191 7455 +191 7455 +190 7456 +190 7456 +190 7456 +189 7457 +189 7457 +189 7457 +188 7457 +188 7458 +188 7458 +187 7458 +187 7459 +187 7459 +186 7459 +186 7460 +186 7460 +185 7460 +185 7461 +185 7461 +184 7461 +184 7462 +184 7462 +183 7462 +183 7463 +183 7463 +182 7463 +182 7464 +182 7464 +181 7464 +181 7464 +181 7465 +180 7465 +180 7465 +180 7466 +180 7466 +179 7466 +179 7467 +179 7467 +178 7467 +178 7467 +178 7468 +177 7468 +177 7468 +177 7469 +176 7469 +176 7469 +176 7470 +175 7470 +175 7470 +175 7470 +175 7471 +174 7471 +174 7471 +174 7472 +173 7472 +173 7472 +173 7472 +172 7473 +172 7473 +172 7473 +172 7474 +171 7474 +171 7474 +171 7474 +170 7475 +170 7475 +170 7475 +170 7476 +169 7476 +169 7476 +169 7476 +168 7477 +168 7477 +168 7477 +168 7478 +167 7478 +167 7478 +167 7478 +166 7479 +166 7479 +166 7479 +166 7479 +165 7480 +165 7480 +165 7480 +165 7481 +164 7481 +164 7481 +164 7481 +163 7482 +163 7482 +163 7482 +163 7482 +162 7483 +162 7483 +162 7483 +162 7483 +161 7484 +161 7484 +161 7484 +161 7485 +160 7485 +160 7485 +160 7485 +160 7486 +159 7486 +159 7486 +159 7486 +159 7487 +158 7487 +158 7487 +158 7487 +158 7488 +157 7488 +157 7488 +157 7488 +157 7489 +156 7489 +156 7489 +156 7489 +156 7490 +155 7490 +155 7490 +155 7490 +155 7491 +154 7491 +154 7491 +154 7491 +154 7492 +153 7492 +153 7492 +153 7492 +153 7493 +153 7493 +152 7493 +152 7493 +152 7494 +152 7494 +151 7494 +151 7494 +151 7495 +151 7495 +151 7495 +150 7495 +150 7496 +150 7496 +150 7496 +149 7496 +149 7497 +149 7497 +149 7497 +149 7497 +148 7497 +148 7498 +148 7498 +148 7498 +147 7498 +147 7499 +147 7499 +147 7499 +147 7499 +146 7500 +146 7500 +146 7500 +146 7500 +145 7500 +145 7501 +145 7501 +145 7501 +145 7501 +144 7502 +144 7502 +144 7502 +144 7502 +144 7502 +143 7503 +143 7503 +143 7503 +143 7503 +143 7504 +142 7504 +142 7504 +142 7504 +142 7504 +142 7505 +141 7505 +141 7505 +141 7505 +141 7506 +141 7506 +140 7506 +140 7506 +140 7506 +140 7507 +140 7507 +139 7507 +139 7507 +139 7507 +139 7508 +139 7508 +139 7508 +138 7508 +138 7508 +138 7509 +138 7509 +138 7509 +137 7509 +137 7510 +137 7510 +137 7510 +137 7510 +137 7510 +136 7511 +136 7511 +136 7511 +136 7511 +136 7511 +135 7512 +135 7512 +135 7512 +135 7512 +135 7512 +135 7513 +134 7513 +134 7513 +134 7513 +134 7513 +134 7514 +134 7514 +133 7514 +133 7514 +133 7514 +133 7515 +133 7515 +132 7515 +132 7515 +132 7515 +132 7515 +132 7516 +132 7516 +131 7516 +131 7516 +131 7516 +131 7517 +131 7517 +131 7517 +131 7517 +130 7517 +130 7518 +130 7518 +130 7518 +130 7518 +130 7518 +129 7518 +129 7519 +129 7519 +129 7519 +129 7519 +129 7519 +128 7520 +128 7520 +128 7520 +128 7520 +128 7520 +128 7521 +127 7521 +127 7521 +127 7521 +127 7521 +127 7521 +127 7522 +127 7522 +126 7522 +126 7522 +126 7522 +126 7522 +126 7523 +126 7523 +126 7523 +125 7523 +125 7523 +125 7524 +125 7524 +125 7524 +125 7524 +124 7524 +124 7524 +124 7525 +124 7525 +124 7525 +124 7525 +124 7525 +123 7525 +123 7526 +123 7526 +123 7526 +123 7526 +123 7526 +123 7526 +122 7527 +122 7527 +122 7527 +122 7527 +122 7527 +122 7527 +122 7528 +122 7528 +121 7528 +121 7528 +121 7528 +121 7528 +121 7529 +121 7529 +121 7529 +120 7529 +120 7529 +120 7529 +120 7530 +120 7530 +120 7530 +120 7530 +120 7530 +119 7530 +119 7531 +119 7531 +119 7531 +119 7531 +119 7531 +119 7531 +118 7532 +118 7532 +118 7532 +118 7532 +118 7532 +118 7532 +118 7533 +118 7533 +117 7533 +117 7533 +117 7533 +117 7533 +117 7534 +117 7534 +117 7534 +117 7534 +116 7534 +116 7534 +116 7534 +116 7535 +116 7535 +116 7535 +116 7535 +116 7535 +115 7535 +115 7536 +115 7536 +115 7536 +115 7536 +115 7536 +115 7536 +115 7536 +115 7537 +114 7537 +114 7537 +114 7537 +114 7537 +114 7537 +114 7538 +114 7538 +114 7538 +113 7538 +113 7538 +113 7538 +113 7538 +113 7539 +113 7539 +113 7539 +113 7539 +113 7539 +112 7539 +112 7539 +112 7540 +112 7540 +112 7540 +112 7540 +112 7540 +112 7540 +112 7541 +111 7541 +111 7541 +111 7541 +111 7541 +111 7541 +111 7541 +111 7542 +111 7542 +111 7542 +110 7542 +110 7542 +110 7542 +110 7542 +110 7543 +110 7543 +110 7543 +110 7543 +110 7543 +110 7543 +109 7543 +109 7543 +109 7544 +109 7544 +109 7544 +109 7544 +109 7544 +109 7544 +109 7544 +108 7545 +108 7545 +108 7545 +108 7545 +108 7545 +108 7545 +108 7545 +108 7546 +108 7546 +108 7546 +107 7546 +107 7546 +107 7546 +107 7546 +107 7547 +107 7547 +107 7547 +107 7547 +107 7547 +107 7547 +106 7547 +106 7548 +106 7548 +106 7548 +106 7548 +106 7548 +106 7548 +106 7548 +106 7549 +106 7549 +106 7549 +105 7549 +105 7549 +105 7549 +105 7549 +105 7549 +105 7550 +105 7550 +105 7550 +105 7550 +105 7550 +104 7550 +104 7550 +104 7551 +104 7551 +104 7551 +104 7551 +104 7551 +104 7551 +104 7551 +104 7552 +104 7552 +103 7552 +103 7552 +103 7552 +103 7552 +103 7552 +103 7553 +103 7553 +103 7553 +103 7553 +103 7553 +102 7553 +102 7553 +102 7554 +102 7554 +102 7554 +102 7554 +102 7554 +102 7554 +102 7554 +102 7555 +102 7555 +101 7555 +101 7555 +101 7555 +101 7555 +101 7555 +101 7555 +101 7556 +101 7556 +101 7556 +101 7556 +101 7556 +101 7556 +100 7556 +100 7557 +100 7557 +100 7557 +100 7557 +100 7557 +100 7557 +100 7557 +100 7557 +100 7558 +100 7558 +99 7558 +99 7558 +99 7558 +99 7558 +99 7558 +99 7558 +99 7559 +99 7559 +99 7559 +99 7559 +99 7559 +98 7559 +98 7559 +98 7560 +98 7560 +98 7560 +98 7560 +98 7560 +98 7560 +98 7560 +98 7560 +98 7561 +97 7561 +97 7561 +97 7561 +97 7561 +97 7561 +97 7561 +97 7561 +97 7561 +97 7562 +97 7562 +97 7562 +96 7562 +96 7562 +96 7562 +96 7562 +96 7562 +96 7563 +96 7563 +96 7563 +96 7563 +96 7563 +96 7563 +95 7563 +95 7563 +95 7564 +95 7564 +95 7564 +95 7564 +95 7564 +95 7564 +95 7564 +95 7564 +94 7564 +94 7565 +94 7565 +94 7565 +94 7565 +94 7565 +94 7565 +94 7565 +94 7565 +94 7566 +93 7566 +93 7566 +93 7566 +93 7566 +93 7566 +93 7566 +93 7566 +93 7566 +93 7567 +93 7567 +93 7567 +92 7567 +92 7567 +92 7567 +92 7567 +92 7567 +92 7567 +92 7568 +92 7568 +92 7568 +92 7568 +91 7568 +91 7568 +91 7568 +91 7568 +91 7568 +91 7569 +91 7569 +91 7569 +91 7569 +91 7569 +90 7569 +90 7569 +90 7569 +90 7569 +90 7570 +90 7570 +90 7570 +90 7570 +90 7570 +90 7570 +90 7570 +89 7570 +89 7570 +89 7571 +89 7571 +89 7571 +89 7571 +89 7571 +89 7571 +89 7571 +89 7571 +88 7571 +88 7572 +88 7572 +88 7572 +88 7572 +88 7572 +88 7572 +88 7572 +88 7572 +88 7572 +87 7573 +87 7573 +87 7573 +87 7573 +87 7573 +87 7573 +87 7573 +87 7573 +87 7573 +87 7574 +87 7574 +86 7574 +86 7574 +86 7574 +86 7574 +86 7574 +86 7574 +86 7574 +86 7575 +86 7575 +86 7575 +85 7575 +85 7575 +85 7575 +85 7575 +85 7575 +85 7575 +85 7575 +85 7576 +85 7576 +85 7576 +84 7576 +84 7576 +84 7576 +84 7576 +84 7576 +84 7576 +84 7577 +84 7577 +84 7577 +84 7577 +83 7577 +83 7577 +83 7577 +83 7577 +83 7577 +83 7577 +83 7578 +83 7578 +83 7578 +82 7578 +82 7578 +82 7578 +82 7578 +82 7578 +82 7578 +82 7579 +82 7579 +82 7579 +82 7579 +81 7579 +81 7579 +81 7579 +81 7579 +81 7579 +81 7579 +81 7580 +81 7580 +81 7580 +80 7580 +80 7580 +80 7580 +80 7580 +80 7580 +80 7580 +80 7581 +80 7581 +80 7581 +79 7581 +79 7581 +79 7581 +79 7581 +79 7581 +79 7581 +79 7581 +79 7582 +79 7582 +79 7582 +78 7582 +78 7582 +78 7582 +78 7582 +78 7582 +78 7582 +78 7582 +78 7583 +78 7583 +78 7583 +77 7583 +77 7583 +77 7583 +77 7583 +77 7583 +77 7583 +77 7584 +77 7584 +77 7584 +77 7584 +76 7584 +76 7584 +76 7584 +76 7584 +76 7584 +76 7584 +76 7585 +76 7585 +76 7585 +76 7585 +75 7585 +75 7585 +75 7585 +75 7585 +75 7585 +75 7586 +75 7586 +75 7586 +75 7586 +75 7586 +74 7586 +74 7586 +74 7586 +74 7586 +74 7586 +74 7587 +74 7587 +74 7587 +74 7587 +74 7587 +74 7587 +73 7587 +73 7587 +73 7587 +73 7587 +73 7588 +73 7588 +73 7588 +73 7588 +73 7588 +73 7588 +72 7588 +72 7588 +72 7588 +72 7588 +72 7589 +72 7589 +72 7589 +72 7589 +72 7589 +72 7589 +71 7589 +71 7589 +71 7589 +71 7590 +71 7590 +71 7590 +71 7590 +71 7590 +71 7590 +71 7590 +70 7590 +70 7590 +70 7590 +70 7591 +70 7591 +70 7591 +70 7591 +70 7591 +70 7591 +70 7591 +69 7591 +69 7591 +69 7591 +69 7591 +69 7592 +69 7592 +69 7592 +69 7592 +69 7592 +69 7592 +68 7592 +68 7592 +68 7592 +68 7592 +68 7593 +68 7593 +68 7593 +68 7593 +68 7593 +68 7593 +67 7593 +67 7593 +67 7593 +67 7593 +67 7594 +67 7594 +67 7594 +67 7594 +67 7594 +67 7594 +66 7594 +66 7594 +66 7594 +66 7594 +66 7594 +66 7595 +66 7595 +66 7595 +66 7595 +66 7595 +65 7595 +65 7595 +65 7595 +65 7595 +65 7595 +65 7596 +65 7596 +65 7596 +65 7596 +65 7596 +64 7596 +64 7596 +64 7596 +64 7596 +64 7596 +64 7597 +64 7597 +64 7597 +64 7597 +64 7597 +63 7597 +63 7597 +63 7597 +63 7597 +63 7597 +63 7597 +63 7598 +63 7598 +63 7598 +63 7598 +62 7598 +62 7598 +62 7598 +62 7598 +62 7598 +62 7598 +62 7599 +62 7599 +62 7599 +62 7599 +62 7599 +61 7599 +61 7599 +61 7599 +61 7599 +61 7599 +61 7600 +61 7600 +61 7600 +61 7600 +61 7600 +60 7600 +60 7600 +60 7600 +60 7600 +60 7600 +60 7601 +60 7601 +60 7601 +60 7601 +60 7601 +59 7601 +59 7601 +59 7601 +59 7601 +59 7601 +59 7602 +59 7602 +59 7602 +59 7602 +59 7602 +59 7602 +58 7602 +58 7602 +58 7602 +58 7603 +58 7603 +58 7603 +58 7603 +58 7603 +58 7603 +58 7603 +57 7603 +57 7603 +57 7603 +57 7604 +57 7604 +57 7604 +57 7604 +57 7604 +57 7604 +57 7604 +56 7604 +56 7604 +56 7604 +56 7605 +56 7605 +56 7605 +56 7605 +56 7605 +56 7605 +56 7605 +56 7605 +55 7605 +55 7605 +55 7606 +55 7606 +55 7606 +55 7606 +55 7606 +55 7606 +55 7606 +55 7606 +54 7606 +54 7607 +54 7607 +54 7607 +54 7607 +54 7607 +54 7607 +54 7607 +54 7607 +54 7607 +54 7607 +53 7608 +53 7608 +53 7608 +53 7608 +53 7608 +53 7608 +53 7608 +53 7608 +53 7608 +53 7609 +52 7609 +52 7609 +52 7609 +52 7609 +52 7609 +52 7609 +52 7609 +52 7609 +52 7610 +51 7610 +51 7610 +51 7610 +51 7610 +51 7610 +51 7610 +51 7610 +51 7610 +51 7611 +51 7611 +50 7611 +50 7611 +50 7611 +50 7611 +50 7611 +50 7611 +50 7611 +50 7612 +50 7612 +49 7612 +49 7612 +49 7612 +49 7612 +49 7612 +49 7612 +49 7613 +49 7613 +49 7613 +48 7613 +48 7613 +48 7613 +48 7613 +48 7613 +48 7613 +48 7614 +48 7614 +47 7614 +47 7614 +47 7614 +47 7614 +47 7614 +47 7614 +47 7615 +47 7615 +46 7615 +46 7615 +46 7615 +46 7615 +46 7615 +46 7615 +46 7615 +46 7616 +46 7616 +45 7616 +45 7616 +45 7616 +45 7616 +45 7616 +45 7616 +45 7616 +45 7617 +44 7617 +44 7617 +44 7617 +44 7617 +44 7617 +44 7617 +44 7617 +44 7617 +43 7618 +43 7618 +43 7618 +43 7618 +43 7618 +43 7618 +43 7618 +43 7618 +42 7618 +42 7619 +42 7619 +42 7619 +42 7619 +42 7619 +42 7619 +42 7619 +41 7619 +41 7619 +41 7620 +41 7620 +41 7620 +41 7620 +41 7620 +41 7620 +40 7620 +40 7620 +40 7620 +40 7620 +40 7621 +40 7621 +40 7621 +40 7621 +39 7621 +39 7621 +39 7621 +39 7621 +39 7621 +39 7621 +39 7622 +39 7622 +38 7622 +38 7622 +38 7622 +38 7622 +38 7622 +38 7622 +38 7622 +38 7622 +38 7623 +37 7623 +37 7623 +37 7623 +37 7623 +37 7623 +37 7623 +37 7623 +37 7623 +36 7623 +36 7624 +36 7624 +36 7624 +36 7624 +36 7624 +36 7624 +36 7624 +35 7624 +35 7624 +35 7624 +35 7624 +35 7625 +35 7625 +35 7625 +35 7625 +34 7625 +34 7625 +34 7625 +34 7625 +34 7625 +34 7625 +34 7626 +34 7626 +33 7626 +33 7626 +33 7626 +33 7626 +33 7626 +33 7626 +33 7626 +33 7626 +33 7626 +32 7626 +32 7627 +32 7627 +32 7627 +32 7627 +32 7627 +32 7627 +32 7627 +31 7627 +31 7627 +31 7627 +31 7627 +31 7628 +31 7628 +31 7628 +31 7628 +30 7628 +30 7628 +30 7628 +30 7628 +30 7628 +30 7628 +30 7628 +30 7628 +30 7628 +29 7629 +29 7629 +29 7629 +29 7629 +29 7629 +29 7629 +29 7629 +29 7629 +29 7629 +28 7629 +28 7629 +28 7629 +28 7629 +28 7630 +28 7630 +28 7630 +28 7630 +27 7630 +27 7630 +27 7630 +27 7630 +27 7630 +27 7630 +27 7630 +27 7630 +27 7630 +26 7630 +26 7631 +26 7631 +26 7631 +26 7631 +26 7631 +26 7631 +26 7631 +26 7631 +25 7631 +25 7631 +25 7631 +25 7631 +25 7631 +25 7631 +25 7631 +25 7632 +25 7632 +24 7632 +24 7632 +24 7632 +24 7632 +24 7632 +24 7632 +24 7632 +24 7632 +24 7632 +23 7632 +23 7632 +23 7632 +23 7632 +23 7633 +23 7633 +23 7633 +23 7633 +23 7633 +22 7633 +22 7633 +22 7633 +22 7633 +22 7633 +22 7633 +22 7633 +22 7633 +22 7633 +21 7633 +21 7633 +21 7633 +21 7634 +21 7634 +21 7634 +21 7634 +21 7634 +21 7634 +21 7634 +20 7634 +20 7634 +20 7634 +20 7634 +20 7634 +20 7634 +20 7634 +20 7634 +20 7634 +19 7634 +19 7635 +19 7635 +19 7635 +19 7635 +19 7635 +19 7635 +19 7635 +19 7635 +19 7635 +18 7635 +18 7635 +18 7635 +18 7635 +18 7635 +18 7635 +18 7635 +18 7635 +18 7635 +18 7635 +17 7635 +17 7635 +17 7636 +17 7636 +17 7636 +17 7636 +17 7636 +17 7636 +17 7636 +17 7636 +16 7636 +16 7636 +16 7636 +16 7636 +16 7636 +16 7636 +16 7636 +16 7636 +16 7636 +16 7636 +15 7636 +15 7636 +15 7636 +15 7636 +15 7636 +15 7636 +15 7637 +15 7637 +15 7637 +15 7637 +15 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +14 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +13 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7638 +12 7638 +12 7638 +12 7638 +12 7638 +12 7638 +12 7638 +12 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +11 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +6 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +5 7639 +4 7639 +4 7639 +4 7639 +4 7639 +4 7639 +4 7639 +4 7639 +4 7639 +4 7639 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7639 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +0 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +1 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +2 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +3 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +4 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +5 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +6 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +7 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +8 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +9 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7638 +10 7637 +10 7637 +10 7637 +10 7637 +10 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +11 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +12 7637 +13 7637 +13 7637 +13 7636 +13 7636 +13 7636 +13 7636 +13 7636 +13 7636 +13 7636 +13 7636 +13 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +14 7636 +15 7636 +15 7636 +15 7636 +15 7636 +15 7635 +15 7635 +15 7635 +15 7635 +15 7635 +15 7635 +15 7635 +16 7635 +16 7635 +16 7635 +16 7635 +16 7635 +16 7635 +16 7635 +16 7635 +16 7635 +16 7635 +17 7635 +17 7635 +17 7635 +17 7635 +17 7634 +17 7634 +17 7634 +17 7634 +17 7634 +17 7634 +18 7634 +18 7634 +18 7634 +18 7634 +18 7634 +18 7634 +18 7634 +18 7634 +18 7634 +18 7634 +19 7634 +19 7634 +19 7634 +19 7633 +19 7633 +19 7633 +19 7633 +19 7633 +19 7633 +19 7633 +20 7633 +20 7633 +20 7633 +20 7633 +20 7633 +20 7633 +20 7633 +20 7633 +20 7633 +21 7633 +21 7633 +21 7632 +21 7632 +21 7632 +21 7632 +21 7632 +21 7632 +21 7632 +21 7632 +22 7632 +22 7632 +22 7632 +22 7632 +22 7632 +22 7632 +22 7632 +22 7631 +22 7631 +23 7631 +23 7631 +23 7631 +23 7631 +23 7631 +23 7631 +23 7631 +23 7631 +23 7631 +24 7631 +24 7631 +24 7631 +24 7631 +24 7630 +24 7630 +24 7630 +24 7630 +24 7630 +25 7630 +25 7630 +25 7630 +25 7630 +25 7630 +25 7630 +25 7630 +25 7630 +25 7630 +26 7629 +26 7629 +26 7629 +26 7629 +26 7629 +26 7629 +26 7629 +26 7629 +26 7629 +27 7629 +27 7629 +27 7629 +27 7629 +27 7628 +27 7628 +27 7628 +27 7628 +27 7628 +28 7628 +28 7628 +28 7628 +28 7628 +28 7628 +28 7628 +28 7628 +28 7628 +29 7627 +29 7627 +29 7627 +29 7627 +29 7627 +29 7627 +29 7627 +29 7627 +29 7627 +30 7627 +30 7627 +30 7627 +30 7626 +30 7626 +30 7626 +30 7626 +30 7626 +30 7626 +31 7626 +31 7626 +31 7626 +31 7626 +31 7626 +31 7626 +31 7625 +31 7625 +32 7625 +32 7625 +32 7625 +32 7625 +32 7625 +32 7625 +32 7625 +32 7625 +33 7625 +33 7624 +33 7624 +33 7624 +33 7624 +33 7624 +33 7624 +33 7624 +33 7624 +34 7624 +34 7624 +34 7624 +34 7623 +34 7623 +34 7623 +34 7623 +34 7623 +35 7623 +35 7623 +35 7623 +35 7623 +35 7623 +35 7623 +35 7622 +35 7622 +36 7622 +36 7622 +36 7622 +36 7622 +36 7622 +36 7622 +36 7622 +36 7622 +37 7621 +37 7621 +37 7621 +37 7621 +37 7621 +37 7621 +37 7621 +37 7621 +38 7621 +38 7621 +38 7620 +38 7620 +38 7620 +38 7620 +38 7620 +38 7620 +38 7620 +39 7620 +39 7620 +39 7620 +39 7619 +39 7619 +39 7619 +39 7619 +39 7619 +40 7619 +40 7619 +40 7619 +40 7619 +40 7618 +40 7618 +40 7618 +40 7618 +41 7618 +41 7618 +41 7618 +41 7618 +41 7618 +41 7618 +41 7617 +41 7617 +42 7617 +42 7617 +42 7617 +42 7617 +42 7617 +42 7617 +42 7617 +42 7616 +43 7616 +43 7616 +43 7616 +43 7616 +43 7616 +43 7616 +43 7616 +43 7616 +44 7615 +44 7615 +44 7615 +44 7615 +44 7615 +44 7615 +44 7615 +44 7615 +45 7615 +45 7614 +45 7614 +45 7614 +45 7614 +45 7614 +45 7614 +45 7614 +46 7614 +46 7614 +46 7613 +46 7613 +46 7613 +46 7613 +46 7613 +46 7613 +46 7613 +47 7613 +47 7613 +47 7612 +47 7612 +47 7612 +47 7612 +47 7612 +47 7612 +48 7612 +48 7612 +48 7612 +48 7611 +48 7611 +48 7611 +48 7611 +48 7611 +49 7611 +49 7611 +49 7611 +49 7611 +49 7610 +49 7610 +49 7610 +49 7610 +49 7610 +50 7610 +50 7610 +50 7610 +50 7610 +50 7609 +50 7609 +50 7609 +50 7609 +50 7609 +51 7609 +51 7609 +51 7609 +51 7609 +51 7609 +51 7608 +51 7608 +51 7608 +51 7608 +51 7608 +52 7608 +52 7608 +52 7608 +52 7608 +52 7607 +52 7607 +52 7607 +52 7607 +52 7607 +53 7607 +53 7607 +53 7607 +53 7607 +53 7607 +53 7606 +53 7606 +53 7606 +53 7606 +53 7606 +54 7606 +54 7606 +54 7606 +54 7606 +54 7605 +54 7605 +54 7605 +54 7605 +54 7605 +54 7605 +54 7605 +55 7605 +55 7605 +55 7605 +55 7604 +55 7604 +55 7604 +55 7604 +55 7604 +55 7604 +55 7604 +56 7604 +56 7604 +56 7603 +56 7603 +56 7603 +56 7603 +56 7603 +56 7603 +56 7603 +56 7603 +56 7603 +57 7602 +57 7602 +57 7602 +57 7602 +57 7602 +57 7602 +57 7602 +57 7602 +57 7602 +57 7602 +58 7601 +58 7601 +58 7601 +58 7601 +58 7601 +58 7601 +58 7601 +58 7601 +58 7601 +58 7601 +59 7600 +59 7600 +59 7600 +59 7600 +59 7600 +59 7600 +59 7600 +59 7600 +59 7600 +59 7600 +59 7599 +60 7599 +60 7599 +60 7599 +60 7599 +60 7599 +60 7599 +60 7599 +60 7599 +60 7598 +60 7598 +61 7598 +61 7598 +61 7598 +61 7598 +61 7598 +61 7598 +61 7598 +61 7598 +61 7597 +61 7597 +62 7597 +62 7597 +62 7597 +62 7597 +62 7597 +62 7597 +62 7597 +62 7597 +62 7596 +62 7596 +62 7596 +63 7596 +63 7596 +63 7596 +63 7596 +63 7596 +63 7596 +63 7596 +63 7595 +63 7595 +63 7595 +64 7595 +64 7595 +64 7595 +64 7595 +64 7595 +64 7595 +64 7595 +64 7594 +64 7594 +64 7594 +65 7594 +65 7594 +65 7594 +65 7594 +65 7594 +65 7594 +65 7594 +65 7593 +65 7593 +65 7593 +66 7593 +66 7593 +66 7593 +66 7593 +66 7593 +66 7593 +66 7592 +66 7592 +66 7592 +66 7592 +67 7592 +67 7592 +67 7592 +67 7592 +67 7592 +67 7592 +67 7591 +67 7591 +67 7591 +67 7591 +68 7591 +68 7591 +68 7591 +68 7591 +68 7591 +68 7591 +68 7590 +68 7590 +68 7590 +68 7590 +69 7590 +69 7590 +69 7590 +69 7590 +69 7590 +69 7590 +69 7589 +69 7589 +69 7589 +69 7589 +70 7589 +70 7589 +70 7589 +70 7589 +70 7589 +70 7588 +70 7588 +70 7588 +70 7588 +70 7588 +71 7588 +71 7588 +71 7588 +71 7588 +71 7588 +71 7587 +71 7587 +71 7587 +71 7587 +71 7587 +72 7587 +72 7587 +72 7587 +72 7587 +72 7586 +72 7586 +72 7586 +72 7586 +72 7586 +72 7586 +73 7586 +73 7586 +73 7586 +73 7585 +73 7585 +73 7585 +73 7585 +73 7585 +73 7585 +73 7585 +74 7585 +74 7585 +74 7585 +74 7584 +74 7584 +74 7584 +74 7584 +74 7584 +74 7584 +74 7584 +74 7584 +75 7584 +75 7583 +75 7583 +75 7583 +75 7583 +75 7583 +75 7583 +75 7583 +75 7583 +75 7583 +76 7582 +76 7582 +76 7582 +76 7582 +76 7582 +76 7582 +76 7582 +76 7582 +76 7582 +76 7581 +77 7581 +77 7581 +77 7581 +77 7581 +77 7581 +77 7581 +77 7581 +77 7581 +77 7580 +77 7580 +78 7580 +78 7580 +78 7580 +78 7580 +78 7580 +78 7580 +78 7580 +78 7579 +78 7579 +78 7579 +79 7579 +79 7579 +79 7579 +79 7579 +79 7579 +79 7578 +79 7578 +79 7578 +79 7578 +79 7578 +80 7578 +80 7578 +80 7578 +80 7578 +80 7577 +80 7577 +80 7577 +80 7577 +80 7577 +81 7577 +81 7577 +81 7577 +81 7577 +81 7576 +81 7576 +81 7576 +81 7576 +81 7576 +82 7576 +82 7576 +82 7576 +82 7576 +82 7575 +82 7575 +82 7575 +82 7575 +82 7575 +82 7575 +83 7575 +83 7575 +83 7574 +83 7574 +83 7574 +83 7574 +83 7574 +83 7574 +83 7574 +84 7574 +84 7574 +84 7573 +84 7573 +84 7573 +84 7573 +84 7573 +84 7573 +84 7573 +84 7573 +85 7572 +85 7572 +85 7572 +85 7572 +85 7572 +85 7572 +85 7572 +85 7572 +85 7572 +85 7571 +86 7571 +86 7571 +86 7571 +86 7571 +86 7571 +86 7571 +86 7571 +86 7570 +86 7570 +86 7570 +87 7570 +87 7570 +87 7570 +87 7570 +87 7570 +87 7570 +87 7569 +87 7569 +87 7569 +87 7569 +87 7569 +88 7569 +88 7569 +88 7569 +88 7568 +88 7568 +88 7568 +88 7568 +88 7568 +88 7568 +88 7568 +89 7568 +89 7567 +89 7567 +89 7567 +89 7567 +89 7567 +89 7567 +89 7567 +89 7567 +89 7566 +90 7566 +90 7566 +90 7566 +90 7566 +90 7566 +90 7566 +90 7566 +90 7565 +90 7565 +90 7565 +90 7565 +91 7565 +91 7565 +91 7565 +91 7565 +91 7564 +91 7564 +91 7564 +91 7564 +91 7564 +91 7564 +92 7564 +92 7564 +92 7564 +92 7563 +92 7563 +92 7563 +92 7563 +92 7563 +92 7563 +92 7563 +93 7562 +93 7562 +93 7562 +93 7562 +93 7562 +93 7562 +93 7562 +93 7562 +93 7561 +93 7561 +93 7561 +94 7561 +94 7561 +94 7561 +94 7561 +94 7561 +94 7560 +94 7560 +94 7560 +94 7560 +94 7560 +95 7560 +95 7560 +95 7560 +95 7559 +95 7559 +95 7559 +95 7559 +95 7559 +95 7559 +95 7559 +96 7559 +96 7558 +96 7558 +96 7558 +96 7558 +96 7558 +96 7558 +96 7558 +96 7557 +96 7557 +96 7557 +97 7557 +97 7557 +97 7557 +97 7557 +97 7557 +97 7556 +97 7556 +97 7556 +97 7556 +97 7556 +97 7556 +98 7556 +98 7556 +98 7555 +98 7555 +98 7555 +98 7555 +98 7555 +98 7555 +98 7555 +98 7554 +98 7554 +99 7554 +99 7554 +99 7554 +99 7554 +99 7554 +99 7553 +99 7553 +99 7553 +99 7553 +99 7553 +99 7553 +100 7553 +100 7553 +100 7552 +100 7552 +100 7552 +100 7552 +100 7552 +100 7552 +100 7552 +100 7551 +100 7551 +101 7551 +101 7551 +101 7551 +101 7551 +101 7551 +101 7550 +101 7550 +101 7550 +101 7550 +101 7550 +101 7550 +101 7550 +102 7549 +102 7549 +102 7549 +102 7549 +102 7549 +102 7549 +102 7549 +102 7548 +102 7548 +102 7548 +102 7548 +103 7548 +103 7548 +103 7548 +103 7547 +103 7547 +103 7547 +103 7547 +103 7547 +103 7547 +103 7547 +104 7546 +104 7546 +104 7546 +104 7546 +104 7546 +104 7546 +104 7546 +104 7545 +104 7545 +104 7545 +104 7545 +105 7545 +105 7545 +105 7545 +105 7544 +105 7544 +105 7544 +105 7544 +105 7544 +105 7544 +105 7544 +106 7543 +106 7543 +106 7543 +106 7543 +106 7543 +106 7543 +106 7543 +106 7543 +106 7542 +106 7542 +106 7542 +107 7542 +107 7542 +107 7542 +107 7542 +107 7541 +107 7541 +107 7541 +107 7541 +107 7541 +107 7541 +108 7541 +108 7541 +108 7540 +108 7540 +108 7540 +108 7540 +108 7540 +108 7540 +108 7540 +108 7539 +109 7539 +109 7539 +109 7539 +109 7539 +109 7539 +109 7539 +109 7538 +109 7538 +109 7538 +110 7538 +110 7538 +110 7538 +110 7538 +110 7538 +110 7537 +110 7537 +110 7537 +110 7537 +110 7537 +111 7537 +111 7537 +111 7536 +111 7536 +111 7536 +111 7536 +111 7536 +111 7536 +111 7536 +112 7535 +112 7535 +112 7535 +112 7535 +112 7535 +112 7535 +112 7535 +112 7535 +112 7534 +113 7534 +113 7534 +113 7534 +113 7534 +113 7534 +113 7534 +113 7533 +113 7533 +113 7533 +114 7533 +114 7533 +114 7533 +114 7533 +114 7532 +114 7532 +114 7532 +114 7532 +115 7532 +115 7532 +115 7532 +115 7531 +115 7531 +115 7531 +115 7531 +115 7531 +115 7531 +116 7530 +116 7530 +116 7530 +116 7530 +116 7530 +116 7530 +116 7530 +116 7529 +117 7529 +117 7529 +117 7529 +117 7529 +117 7529 +117 7529 +117 7528 +117 7528 +118 7528 +118 7528 +118 7528 +118 7528 +118 7527 +118 7527 +118 7527 +118 7527 +119 7527 +119 7527 +119 7527 +119 7526 +119 7526 +119 7526 +119 7526 +120 7526 +120 7526 +120 7526 +120 7525 +120 7525 +120 7525 +120 7525 +120 7525 +121 7525 +121 7524 +121 7524 +121 7524 +121 7524 +121 7524 +121 7524 +122 7523 +122 7523 +122 7523 +122 7523 +122 7523 +122 7523 +122 7523 +122 7522 +123 7522 +123 7522 +123 7522 +123 7522 +123 7522 +123 7521 +123 7521 +124 7521 +124 7521 +124 7521 +124 7521 +124 7520 +124 7520 +124 7520 +125 7520 +125 7520 +125 7520 +125 7519 +125 7519 +125 7519 +126 7519 +126 7519 +126 7519 +126 7519 +126 7518 +126 7518 +126 7518 +127 7518 +127 7518 +127 7518 +127 7517 +127 7517 +127 7517 +127 7517 +128 7517 +128 7516 +128 7516 +128 7516 +128 7516 +128 7516 +129 7516 +129 7515 +129 7515 +129 7515 +129 7515 +129 7515 +130 7515 +130 7514 +130 7514 +130 7514 +130 7514 +130 7514 +131 7514 +131 7513 +131 7513 +131 7513 +131 7513 +131 7513 +131 7513 +132 7512 +132 7512 +132 7512 +132 7512 +132 7512 +132 7511 +133 7511 +133 7511 +133 7511 +133 7511 +133 7511 +134 7510 +134 7510 +134 7510 +134 7510 +134 7510 +134 7509 +135 7509 +135 7509 +135 7509 +135 7509 +135 7509 +135 7508 +136 7508 +136 7508 +136 7508 +136 7508 +136 7507 +137 7507 +137 7507 +137 7507 +137 7507 +137 7506 +137 7506 +138 7506 +138 7506 +138 7506 +138 7505 +138 7505 +139 7505 +139 7505 +139 7505 +139 7505 +139 7504 +139 7504 +140 7504 +140 7504 +140 7504 +140 7503 +140 7503 +141 7503 +141 7503 +141 7503 +141 7502 +141 7502 +142 7502 +142 7502 +142 7502 +142 7501 +142 7501 +143 7501 +143 7501 +143 7500 +143 7500 +143 7500 +144 7500 +144 7500 +144 7499 +144 7499 +144 7499 +145 7499 +145 7499 +145 7498 +145 7498 +145 7498 +146 7498 +146 7498 +146 7497 +146 7497 +147 7497 +147 7497 +147 7496 +147 7496 +147 7496 +148 7496 +148 7496 +148 7495 +148 7495 +149 7495 +149 7495 +149 7495 +149 7494 +149 7494 +150 7494 +150 7494 +150 7493 +150 7493 +151 7493 +151 7493 +151 7492 +151 7492 +151 7492 +152 7492 +152 7492 +152 7491 +152 7491 +153 7491 +153 7491 +153 7490 +153 7490 +153 7490 +154 7490 +154 7489 +154 7489 +154 7489 +155 7489 +155 7488 +155 7488 +155 7488 +156 7488 +156 7488 +156 7487 +156 7487 +157 7487 +157 7487 +157 7486 +157 7486 +158 7486 +158 7486 +158 7485 +158 7485 +159 7485 +159 7485 +159 7484 +159 7484 +160 7484 +160 7484 +160 7483 +160 7483 +161 7483 +161 7483 +161 7482 +161 7482 +162 7482 +162 7482 +162 7481 +162 7481 +163 7481 +163 7481 +163 7480 +163 7480 +164 7480 +164 7480 +164 7479 +165 7479 +165 7479 +165 7479 +165 7478 +166 7478 +166 7478 +166 7478 +166 7477 +167 7477 +167 7477 +167 7477 +168 7476 +168 7476 +168 7476 +168 7475 +169 7475 +169 7475 +169 7475 +170 7474 +170 7474 +170 7474 +170 7474 +171 7473 +171 7473 +171 7473 +172 7473 +172 7472 +172 7472 +172 7472 +173 7471 +173 7471 +173 7471 +174 7471 +174 7470 +174 7470 +175 7470 +175 7469 +175 7469 +175 7469 +176 7469 +176 7468 +176 7468 +177 7468 +177 7467 +177 7467 +178 7467 +178 7467 +178 7466 +179 7466 +179 7466 +179 7465 +180 7465 +180 7465 +180 7465 +180 7464 +181 7464 +181 7464 +181 7463 +182 7463 +182 7463 +182 7463 +183 7462 +183 7462 +183 7462 +184 7461 +184 7461 +184 7461 +185 7460 +185 7460 +185 7460 +186 7460 +186 7459 +186 7459 +187 7459 +187 7458 +187 7458 +188 7458 +188 7457 +188 7457 +189 7457 +189 7456 +189 7456 +190 7456 +190 7455 +190 7455 +191 7455 +191 7455 +192 7454 +192 7454 +192 7454 +193 7453 +193 7453 +193 7453 +194 7452 +194 7452 +194 7452 +195 7451 +195 7451 +195 7451 +196 7450 +196 7450 +197 7450 +197 7449 +197 7449 +198 7449 +198 7448 +198 7448 +199 7448 +199 7447 +199 7447 +200 7447 +200 7446 +201 7446 +201 7446 +201 7445 +202 7445 +202 7445 +203 7444 +203 7444 +203 7444 +204 7443 +204 7443 +204 7443 +205 7442 +205 7442 +206 7442 +206 7441 +206 7441 +207 7441 +207 7440 +208 7440 +208 7439 +208 7439 +209 7439 +209 7438 +210 7438 +210 7438 +210 7437 +211 7437 +211 7437 +212 7436 +212 7436 +212 7436 +213 7435 +213 7435 +214 7434 +214 7434 +214 7434 +215 7433 +215 7433 +216 7433 +216 7432 +216 7432 +217 7432 +217 7431 +218 7431 +218 7430 +219 7430 +219 7430 +219 7429 +220 7429 +220 7429 +221 7428 +221 7428 +222 7427 +222 7427 +222 7427 +223 7426 +223 7426 +224 7425 +224 7425 +225 7425 +225 7424 +225 7424 +226 7423 +226 7423 +227 7423 +227 7422 +228 7422 +228 7421 +229 7421 +229 7421 +229 7420 +230 7420 +230 7419 +231 7419 +231 7419 +232 7418 +232 7418 +233 7417 +233 7417 +234 7417 +234 7416 +234 7416 +235 7415 +235 7415 +236 7414 +236 7414 +237 7413 +237 7413 +238 7412 +238 7412 +239 7411 +239 7411 +240 7411 +240 7410 +241 7410 +241 7409 +241 7409 +242 7408 +242 7408 +243 7407 +243 7407 +244 7406 +244 7406 +245 7405 +245 7405 +246 7404 +246 7404 +247 7403 +247 7403 +248 7403 +248 7402 +249 7402 +249 7401 +250 7401 +250 7400 +251 7400 +251 7399 +252 7399 +252 7398 +253 7398 +253 7397 +254 7397 +254 7396 +255 7396 +255 7395 +256 7395 +256 7394 +257 7394 +257 7393 +258 7393 +258 7392 +259 7392 +259 7391 +260 7391 +260 7390 +261 7390 +261 7389 +262 7389 +262 7388 +263 7388 +263 7387 +264 7387 +264 7386 +265 7386 +265 7385 +266 7385 +266 7384 +267 7384 +267 7383 +268 7383 +269 7382 +269 7382 +270 7381 +270 7381 +271 7380 +271 7380 +272 7379 +272 7379 +273 7378 +273 7378 +274 7377 +274 7377 +275 7376 +275 7376 +276 7375 +276 7374 +277 7374 +277 7373 +278 7373 +278 7372 +279 7372 +280 7371 +280 7371 +281 7370 +281 7370 +282 7369 +282 7369 +283 7368 +283 7368 +284 7367 +284 7367 +285 7366 +285 7366 +286 7365 +286 7365 +287 7364 +288 7364 +288 7363 +289 7362 +289 7362 +290 7361 +290 7361 +291 7360 +291 7360 +292 7359 +292 7359 +293 7358 +294 7358 +294 7357 +295 7357 +295 7356 +296 7356 +296 7355 +297 7354 +297 7354 +298 7353 +299 7353 +299 7352 +300 7352 +300 7351 +301 7351 +301 7350 +302 7350 +302 7349 +303 7349 +304 7348 +304 7347 +305 7347 +305 7346 +306 7346 +306 7345 +307 7345 +308 7344 +308 7344 +309 7343 +309 7343 +310 7342 +310 7342 +311 7341 +312 7340 +312 7340 +313 7339 +313 7339 +314 7338 +315 7338 +315 7337 +316 7337 +316 7336 +317 7335 +317 7335 +318 7334 +319 7334 +319 7333 +320 7333 +320 7332 +321 7332 +322 7331 +322 7331 +323 7330 +323 7329 +324 7329 +325 7328 +325 7328 +326 7327 +326 7327 +327 7326 +328 7326 +328 7325 +329 7324 +329 7324 +330 7323 +331 7323 +331 7322 +332 7322 +332 7321 +333 7321 +334 7320 +334 7319 +335 7319 +335 7318 +336 7318 +337 7317 +337 7317 +338 7316 +338 7316 +339 7315 +340 7314 +340 7314 +341 7313 +342 7313 +342 7312 +343 7312 +343 7311 +344 7310 +345 7310 +345 7309 +346 7309 +347 7308 +347 7308 +348 7307 +348 7307 +349 7306 +350 7305 +350 7305 +351 7304 +352 7304 +352 7303 +353 7303 +353 7302 +354 7301 +355 7301 +355 7300 +356 7300 +357 7299 +357 7299 +358 7298 +359 7297 +359 7297 +360 7296 +361 7296 +361 7295 +362 7295 +363 7294 +363 7293 +364 7293 +364 7292 +365 7292 +366 7291 +367 7291 +367 7290 +368 7289 +369 7289 +369 7288 +370 7288 +371 7287 +371 7287 +372 7286 +373 7285 +373 7285 +374 7284 +375 7284 +375 7283 +376 7282 +377 7282 +377 7281 +378 7281 +379 7280 +380 7279 +380 7279 +381 7278 +382 7278 +382 7277 +383 7276 +384 7276 +385 7275 +385 7275 +386 7274 +387 7273 +387 7273 +388 7272 +389 7272 +390 7271 +390 7270 +391 7269 +392 7269 +392 7268 +393 7267 +394 7267 +395 7266 +395 7266 +396 7265 +397 7264 +398 7264 +398 7263 +399 7262 +400 7262 +401 7261 +401 7261 +402 7260 +403 7259 +404 7259 +404 7258 +405 7257 +406 7257 +407 7256 +407 7255 +408 7254 +409 7254 +410 7253 +410 7252 +411 7252 +412 7251 +413 7250 +414 7249 +414 7248 +415 7248 +416 7247 +417 7246 +417 7246 +418 7245 +419 7244 +420 7244 +421 7243 +421 7242 +422 7242 +423 7241 +424 7240 +425 7240 +425 7239 +426 7238 +427 7237 +428 7237 +429 7236 +429 7235 +430 7235 +431 7234 +432 7233 +433 7233 +433 7232 +434 7231 +435 7230 +436 7230 +437 7229 +437 7228 +438 7227 +439 7227 +440 7226 +441 7225 +441 7225 +442 7224 +443 7223 +444 7222 +445 7221 +445 7220 +446 7219 +447 7218 +448 7218 +449 7217 +450 7216 +450 7216 +451 7215 +452 7214 +453 7213 +454 7212 +454 7212 +455 7211 +456 7210 +457 7209 +458 7208 +459 7207 +459 7206 +460 7205 +461 7204 +462 7203 +463 7202 +463 7201 +464 7200 +465 7199 +466 7198 +467 7197 +468 7196 +468 7195 +469 7194 +470 7193 +471 7192 +472 7191 +473 7190 +473 7189 +474 7188 +475 7187 +476 7186 +477 7185 +477 7184 +478 7182 +479 7182 +480 7180 +481 7180 +482 7178 +482 7177 +483 7176 +484 7176 +485 7175 +486 7174 +487 7174 +487 7173 +488 7172 +489 7172 +490 7171 +491 7170 +492 7169 +492 7168 +493 7167 +494 7166 +495 7164 +496 7163 +497 7162 +497 7161 +498 7160 +499 7159 +500 7158 +501 7157 +502 7156 +502 7155 +503 7154 +504 7153 +505 7152 +506 7151 +507 7150 +508 7150 +508 7149 +509 7148 +510 7148 +511 7147 +512 7145 +513 7144 +514 7143 +514 7142 +515 7141 +516 7140 +517 7139 +518 7138 +519 7137 +520 7136 +521 7135 +521 7135 +522 7134 +523 7133 +524 7133 +525 7133 +526 7132 +527 7132 +528 7132 +529 7132 +530 7132 +530 7131 +531 7131 +532 7131 +533 7129 +534 7129 +536 7129 +539 7128 +544 7128 +545 7127 +547 7126 +548 7126 +549 7125 +550 7124 +551 7123 +552 7122 +553 7121 +554 7120 +555 7119 +556 7118 +557 7117 +558 7117 +559 7116 +560 7115 +561 7114 +562 7113 +563 7112 +565 7111 +566 7110 +567 7109 +568 7108 +569 7107 +570 7107 +571 7106 +572 7105 +573 7104 +574 7103 +575 7102 +577 7101 +578 7100 +579 7099 +580 7098 +581 7097 +582 7096 +583 7095 +584 7094 +586 7093 +587 7092 +588 7091 +589 7091 +590 7090 +591 7089 +592 7088 +593 7087 +595 7086 +596 7085 +597 7084 +598 7083 +599 7082 +600 7081 +602 7080 +603 7079 +604 7078 +605 7077 +606 7076 +607 7075 +609 7074 +610 7073 +611 7072 +612 7071 +613 7070 +614 7069 +616 7068 +617 7067 +618 7066 +619 7065 +620 7064 +622 7063 +623 7062 +624 7061 +625 7060 +627 7059 +628 7058 +629 7057 +630 7056 +631 7055 +633 7054 +634 7053 +635 7052 +636 7051 +638 7050 +639 7049 +640 7048 +641 7047 +643 7046 +644 7045 +645 7043 +646 7042 +648 7041 +649 7040 +650 7039 +651 7038 +653 7037 +654 7036 +655 7035 +656 7034 +658 7033 +659 7032 +660 7031 +662 7030 +663 7029 +664 7028 +665 7026 +667 7025 +668 7024 +669 7023 +671 7022 +672 7021 +673 7020 +675 7019 +676 7018 +677 7017 +679 7015 +680 7014 +681 7013 +683 7012 +684 7011 +685 7010 +687 7009 +688 7008 +689 7007 +691 7005 +692 7004 +693 7003 +695 7002 +696 7001 +697 7000 +699 6999 +700 6998 +702 6996 +703 6995 +704 6994 +706 6993 +707 6992 +708 6991 +710 6989 +711 6988 +713 6987 +714 6986 +715 6985 +717 6984 +718 6982 +720 6981 +721 6980 +722 6979 +724 6978 +725 6977 +727 6975 +728 6974 +730 6973 +731 6972 +732 6971 +734 6969 +735 6968 +737 6967 +738 6966 +740 6965 +741 6963 +743 6962 +744 6961 +746 6960 +747 6959 +748 6957 +750 6956 +751 6955 +753 6954 +754 6952 +756 6951 +757 6950 +759 6949 +760 6948 +762 6946 +763 6945 +765 6944 +766 6943 +768 6941 +769 6940 +771 6939 +772 6937 +774 6936 +775 6935 +777 6934 +779 6932 +780 6931 +782 6930 +783 6929 +785 6927 +786 6926 +788 6925 +789 6923 +791 6922 +792 6921 +794 6920 +796 6918 +797 6917 +799 6916 +800 6914 +802 6913 +803 6912 +805 6910 +807 6909 +808 6908 +810 6906 +811 6905 +813 6904 +815 6903 +816 6901 +818 6900 +819 6899 +821 6897 +823 6896 +824 6894 +826 6893 +828 6892 +829 6890 +831 6889 +833 6888 +834 6886 +836 6885 +837 6884 +839 6882 +841 6881 +842 6879 +844 6878 +846 6877 +847 6875 +849 6874 +851 6873 +852 6871 +854 6870 +856 6868 +858 6867 +859 6866 +861 6864 +863 6863 +864 6861 +866 6860 +868 6859 +869 6857 +871 6856 +873 6854 +875 6853 +876 6851 +878 6850 +880 6849 +882 6847 +883 6846 +885 6844 +887 6843 +889 6841 +890 6840 +892 6838 +894 6837 +896 6835 +897 6834 +899 6833 +901 6831 +903 6830 +904 6828 +906 6827 +908 6825 +910 6824 +912 6822 +913 6821 +915 6819 +917 6818 +919 6816 +921 6815 +923 6813 +924 6812 +926 6810 +928 6809 +930 6807 +932 6806 +934 6804 +935 6803 +937 6801 +939 6800 +941 6798 +943 6796 +945 6795 +946 6793 +948 6792 +950 6790 +952 6789 +954 6787 +956 6786 +958 6784 +960 6783 +962 6781 +963 6779 +965 6778 +967 6776 +969 6775 +971 6773 +973 6771 +975 6770 +977 6768 +979 6767 +981 6765 +983 6763 +985 6762 +987 6760 +988 6759 +990 6757 +992 6755 +994 6754 +996 6752 +998 6751 +1000 6749 +1002 6747 +1004 6746 +1006 6744 +1008 6742 +1010 6741 +1012 6739 +1014 6737 +1016 6736 +1018 6734 +1020 6732 +1022 6731 +1024 6729 +1026 6727 +1028 6726 +1030 6724 +1032 6722 +1034 6721 +1036 6719 +1038 6717 +1040 6716 +1042 6714 +1044 6712 +1047 6711 +1049 6709 +1051 6707 +1053 6705 +1055 6704 +1057 6702 +1059 6700 +1061 6699 +1063 6697 +1065 6695 +1067 6693 +1069 6692 +1071 6690 +1074 6688 +1076 6686 +1078 6685 +1080 6683 +1082 6681 +1084 6679 +1086 6678 +1088 6676 +1091 6674 +1093 6672 +1095 6671 +1097 6669 +1099 6667 +1101 6665 +1103 6663 +1106 6662 +1108 6660 +1110 6658 +1112 6656 +1114 6654 +1116 6653 +1119 6651 +1121 6649 +1123 6647 +1125 6645 +1127 6644 +1130 6642 +1132 6640 +1134 6638 +1136 6636 +1139 6634 +1141 6632 +1143 6631 +1145 6629 +1147 6627 +1150 6625 +1152 6623 +1154 6621 +1156 6619 +1159 6618 +1161 6616 +1163 6614 +1166 6612 +1168 6610 +1170 6608 +1172 6606 +1175 6604 +1177 6602 +1179 6601 +1182 6599 +1184 6597 +1186 6595 +1189 6593 +1191 6591 +1193 6589 +1195 6587 +1198 6585 +1200 6583 +1202 6581 +1205 6579 +1207 6577 +1210 6575 +1212 6573 +1214 6572 +1217 6570 +1219 6568 +1221 6566 +1224 6564 +1226 6562 +1228 6560 +1231 6558 +1233 6556 +1236 6554 +1238 6552 +1240 6550 +1243 6548 +1245 6546 +1248 6544 +1250 6542 +1253 6540 +1255 6538 +1257 6536 +1260 6534 +1262 6532 +1265 6530 +1267 6527 +1270 6525 +1272 6523 +1275 6521 +1277 6519 +1280 6517 +1282 6515 +1285 6513 +1287 6511 +1290 6509 +1292 6507 +1295 6505 +1297 6503 +1300 6501 +1302 6499 +1305 6496 +1307 6494 +1310 6492 +1312 6490 +0 0 +0 0 diff --git a/Analysis/config/exclusionMask_p1.0.20.txt.png b/Analysis/config/exclusionMask_p1.0.20.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..02336cdae2ae252fdcdb4501d98174e2e59f205c GIT binary patch literal 64577 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 literal 0 HcmV?d00001 diff --git a/Analysis/config/exclusionMask_p1.1.17.txt b/Analysis/config/exclusionMask_p1.1.17.txt index 12c125d3..f7b97bd1 100644 --- a/Analysis/config/exclusionMask_p1.1.17.txt +++ b/Analysis/config/exclusionMask_p1.1.17.txt @@ -1,10657 +1,10657 @@ 15456 10656 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -4898 5073 -4853 10304 -4808 10304 -4764 11047 -3850 11204 -3694 11603 -3598 11608 -3564 11763 -3516 11780 -3467 11797 -3418 11813 -3399 11830 -3392 11847 -3386 11864 -3380 11881 -3373 11897 -3367 11914 -3361 11931 -3355 11944 -3348 11953 -3342 11963 -3336 11972 -3330 11981 -3323 11990 -3317 12000 -3311 12009 -3305 12018 -3298 12028 -3292 12037 -3286 12046 -3280 12055 -3273 12065 -3267 12074 -3261 12083 -3254 12089 -3249 12096 -3244 12102 -3240 12109 -3235 12115 -3231 12122 -3227 12128 -3222 12134 -3218 12141 -3213 12147 -3209 12154 -3204 12160 -3200 12167 -3196 12173 -3191 12180 -3187 12186 -3182 12192 -3178 12199 -3173 12205 -3169 12212 -3165 12218 -3160 12225 -3156 12231 -3151 12237 -3147 12244 -3143 12250 -3138 12257 -3134 12263 -3129 12270 -3125 12276 -3120 12283 -3116 12289 -3112 12295 -3107 12302 -3103 12308 -3098 12315 -3094 12321 -3089 12328 -3085 12334 -3081 12340 -3077 12344 -3073 12349 -3068 12354 -3064 12359 -3060 12363 -3056 12368 -3052 12373 -3048 12378 -3044 12382 -3040 12387 -3035 12392 -3031 12397 -3027 12401 -3023 12406 -3019 12411 -3015 12416 -3011 12420 -3007 12425 -3002 12430 -2998 12435 -2994 12439 -2990 12444 -2986 12449 -2982 12454 -2978 12458 -2974 12463 -2969 12468 -2965 12473 -2961 12477 -2957 12482 -2953 12487 -2949 12491 -2945 12496 -2941 12501 -2936 12505 -2932 12509 -2928 12513 -2924 12517 -2920 12521 -2916 12524 -2912 12528 -2908 12532 -2903 12536 -2899 12540 -2895 12544 -2891 12547 -2887 12551 -2883 12555 -2879 12559 -2875 12563 -2871 12566 -2866 12570 -2861 12574 -2857 12578 -2852 12582 -2847 12585 -2842 12589 -2838 12593 -2833 12597 -2828 12601 -2824 12604 -2819 12608 -2814 12612 -2810 12616 -2805 12620 -2800 12623 -2796 12627 -2791 12631 -2786 12635 -2782 12639 -2777 12642 -2772 12645 -2768 12648 -2763 12651 -2758 12654 -2754 12657 -2749 12660 -2744 12663 -2739 12666 -2735 12669 -2730 12672 -2725 12675 -2721 12678 -2716 12681 -2711 12684 -2708 12687 -2705 12690 -2702 12694 -2699 12697 -2696 12700 -2693 12703 -2690 12706 -2687 12709 -2684 12712 -2681 12715 -2678 12718 -2675 12721 -2672 12724 -2669 12727 -2666 12730 -2663 12733 -2660 12736 -2657 12739 -2654 12742 -2651 12745 -2648 12748 -2645 12752 -2642 12755 -2639 12758 -2636 12761 -2633 12764 -2630 12767 -2627 12769 -2624 12772 -2621 12775 -2618 12777 -2615 12780 -2612 12783 -2609 12786 -2606 12788 -2603 12791 -2600 12794 -2597 12796 -2594 12799 -2591 12802 -2588 12804 -2585 12807 -2574 12810 -2569 12812 -2564 12815 -2560 12818 -2555 12821 -2550 12823 -2545 12826 -2542 12829 -2539 12831 -2537 12834 -2534 12837 -2531 12839 -2528 12842 -2525 12845 -2522 12847 -2520 12850 -2517 12853 -2514 12856 -2511 12858 -2508 12861 -2505 12864 -2503 12866 -2500 12869 -2497 12872 -2494 12874 -2492 12877 -2490 12880 -2488 12880 -2486 12880 -2484 12881 -2482 12884 -2480 12887 -2478 12890 -2476 12893 -2474 12896 -2472 12900 -2470 12903 -2468 12906 -2466 12909 -2464 12912 -2462 12915 -2460 12918 -2458 12921 -2456 12924 -2453 12927 -2451 12930 -2449 12933 -2447 12936 -2445 12939 -2443 12943 -2441 12946 -2439 12949 -2437 12952 -2435 12955 -2433 12958 -2431 12961 -2429 12964 -2427 12967 -2425 12970 -2423 12972 -2421 12975 -2419 12977 -2417 12980 -2415 12982 -2413 12984 -2411 12987 -2409 12989 -2407 12992 -2405 12994 -2403 12996 -2401 12999 -2399 13001 -2397 13004 -2395 13006 -2393 13008 -2391 13011 -2389 13013 -2387 13016 -2385 13018 -2383 13020 -2381 13023 -2379 13025 -2377 13028 -2375 13030 -2373 13032 -2371 13035 -2369 13037 -2367 13040 -2365 13042 -2363 13044 -2361 13047 -2359 13049 -2357 13052 -2355 13054 -2353 13056 -2351 13059 -2349 13061 -2347 13064 -2345 13066 -2343 13068 -2341 13071 -2339 13073 -2337 13076 -2335 13078 -2333 13080 -2331 13083 -2329 13085 -2327 13088 -2325 13090 -2323 13092 -2321 13095 -2319 13097 -2317 13100 -2315 13102 -2313 13104 -2311 13107 -2309 13109 -2307 13112 -2305 13114 -2303 13116 -2301 13119 -2298 13121 -2296 13124 -2294 13126 -2292 13128 -2290 13131 -2288 13133 -2286 13136 -2283 13138 -2281 13140 -2279 13143 -2277 13145 -2275 13148 -2273 13150 -2271 13152 -2268 13155 -2266 13157 -2264 13160 -2262 13162 -2260 13164 -2258 13167 -2256 13169 -2253 13172 -2251 13174 -2249 13176 -2247 13179 -2245 13181 -2243 13184 -2241 13186 -2238 13188 -2236 13191 -2234 13193 -2232 13195 -2230 13197 -2228 13199 -2226 13201 -2223 13203 -2221 13206 -2219 13208 -2217 13210 -2215 13212 -2213 13214 -2211 13216 -2208 13219 -2206 13221 -2204 13223 -2202 13225 -2200 13227 -2198 13229 -2196 13232 -2193 13234 -2191 13236 -2189 13238 -2187 13240 -2185 13242 -2183 13245 -2181 13247 -2179 13249 -2176 13251 -2174 13253 -2172 13255 -2170 13257 -2168 13260 -2166 13262 -2164 13264 -2161 13266 -2159 13268 -2157 13270 -2155 13273 -2153 13275 -2151 13277 -2149 13279 -2146 13281 -2144 13283 -2142 13286 -2140 13288 -2138 13290 -2136 13292 -2134 13294 -2131 13296 -2129 13298 -2127 13301 -2125 13303 -2123 13305 -2121 13307 -2119 13309 -2116 13311 -2114 13314 -2112 13316 -2110 13318 -2108 13320 -2106 13322 -2104 13324 -2101 13327 -2099 13329 -2097 13331 -2095 13333 -2093 13335 -2091 13337 -2089 13339 -2086 13342 -2084 13344 -2082 13346 -2080 13348 -2078 13350 -2076 13352 -2074 13355 -2071 13357 -2069 13359 -2067 13361 -2065 13363 -2063 13365 -2061 13368 -2059 13370 -2056 13372 -2054 13374 -2052 13376 -2050 13378 -2048 13381 -2046 13383 -2044 13385 -2041 13387 -2039 13389 -2037 13391 -2035 13393 -2033 13396 -2031 13398 -2029 13400 -2026 13402 -2024 13405 -2022 13407 -2020 13409 -2018 13412 -2016 13414 -2014 13416 -2011 13419 -2009 13421 -2007 13423 -2005 13425 -2003 13428 -2001 13430 -1999 13432 -1996 13435 -1994 13437 -1992 13439 -1990 13442 -1988 13444 -1986 13446 -1984 13448 -1981 13451 -1979 13453 -1977 13455 -1975 13458 -1973 13460 -1971 13462 -1969 13465 -1966 13467 -1964 13469 -1962 13471 -1960 13474 -1958 13476 -1956 13478 -1954 13481 -1952 13483 -1949 13485 -1947 13488 -1945 13490 -1943 13492 -1941 13494 -1939 13497 -1937 13499 -1934 13501 -1932 13504 -1930 13506 -1928 13508 -1926 13511 -1924 13513 -1922 13515 -1919 13517 -1917 13520 -1915 13522 -1913 13524 -1911 13527 -1909 13529 -1907 13531 -1904 13534 -1902 13536 -1900 13538 -1898 13540 -1896 13543 -1894 13545 -1892 13547 -1889 13550 -1887 13552 -1885 13555 -1883 13557 -1881 13559 -1879 13562 -1877 13564 -1874 13567 -1872 13569 -1870 13572 -1868 13574 -1866 13576 -1864 13579 -1862 13581 -1860 13584 -1858 13586 -1856 13588 -1854 13591 -1852 13593 -1850 13596 -1849 13598 -1847 13601 -1845 13603 -1843 13605 -1841 13608 -1839 13610 -1837 13613 -1835 13615 -1833 13618 -1831 13620 -1830 13622 -1828 13625 -1826 13627 -1824 13630 -1822 13632 -1820 13634 -1818 13637 -1816 13639 -1814 13642 -1813 13644 -1811 13647 -1809 13649 -1807 13651 -1805 13654 -1803 13656 -1801 13659 -1799 13661 -1797 13663 -1795 13666 -1794 13668 -1792 13671 -1790 13673 -1788 13676 -1786 13678 -1784 13680 -1782 13683 -1780 13685 -1778 13688 -1777 13690 -1775 13692 -1773 13695 -1771 13697 -1769 13700 -1767 13702 -1765 13705 -1763 13707 -1761 13709 -1759 13711 -1758 13712 -1756 13714 -1754 13715 -1752 13717 -1750 13719 -1748 13720 -1746 13722 -1744 13724 -1742 13725 -1741 13727 -1739 13729 -1737 13730 -1735 13732 -1733 13734 -1731 13735 -1729 13737 -1727 13739 -1725 13740 -1724 13742 -1722 13743 -1720 13745 -1718 13747 -1716 13748 -1714 13750 -1712 13752 -1710 13753 -1708 13755 -1706 13757 -1705 13758 -1703 13760 -1701 13762 -1699 13763 -1697 13765 -1695 13767 -1693 13768 -1691 13770 -1689 13771 -1688 13773 -1686 13775 -1684 13776 -1682 13778 -1680 13780 -1678 13781 -1676 13783 -1674 13785 -1672 13786 -1670 13788 -1669 13790 -1667 13791 -1665 13793 -1663 13795 -1661 13796 -1660 13798 -1658 13799 -1656 13801 -1654 13803 -1653 13804 -1651 13806 -1649 13808 -1647 13809 -1646 13811 -1644 13813 -1642 13814 -1640 13816 -1639 13818 -1637 13819 -1635 13821 -1633 13823 -1632 13824 -1630 13826 -1628 13827 -1626 13829 -1625 13831 -1623 13832 -1621 13834 -1619 13836 -1618 13837 -1616 13839 -1614 13841 -1612 13842 -1611 13844 -1609 13846 -1607 13847 -1605 13849 -1604 13851 -1602 13852 -1600 13854 -1598 13855 -1597 13857 -1595 13859 -1593 13860 -1591 13862 -1590 13864 -1588 13865 -1586 13867 -1584 13869 -1583 13870 -1581 13872 -1579 13874 -1577 13875 -1576 13877 -1574 13879 -1572 13880 -1570 13882 -1569 13883 -1567 13885 -1565 13887 -1563 13888 -1562 13890 -1560 13891 -1558 13893 -1556 13894 -1555 13896 -1553 13897 -1551 13898 -1549 13900 -1548 13901 -1546 13903 -1544 13904 -1542 13905 -1541 13907 -1539 13908 -1537 13910 -1535 13911 -1534 13912 -1532 13914 -1530 13915 -1528 13917 -1527 13918 -1525 13920 -1523 13921 -1521 13922 -1520 13924 -1518 13925 -1516 13927 -1514 13928 -1513 13929 -1511 13931 -1509 13932 -1507 13934 -1506 13935 -1505 13937 -1503 13938 -1502 13939 -1501 13941 -1499 13942 -1498 13944 -1497 13945 -1495 13946 -1494 13948 -1493 13949 -1491 13951 -1490 13952 -1489 13953 -1487 13955 -1486 13956 -1484 13958 -1483 13959 -1482 13961 -1480 13962 -1479 13963 -1478 13965 -1476 13966 -1475 13968 -1474 13969 -1472 13970 -1471 13972 -1470 13973 -1468 13975 -1467 13976 -1466 13977 -1464 13979 -1463 13980 -1462 13982 -1460 13983 -1459 13985 -1457 13986 -1456 13987 -1455 13989 -1453 13990 -1452 13992 -1451 13993 -1449 13994 -1448 13996 -1447 13997 -1445 13999 -1444 14000 -1443 14001 -1441 14003 -1440 14004 -1439 14006 -1437 14007 -1436 14009 -1434 14010 -1433 14011 -1432 14013 -1430 14014 -1429 14016 -1428 14017 -1426 14018 -1425 14020 -1424 14021 -1422 14023 -1421 14024 -1420 14025 -1418 14027 -1417 14028 -1416 14030 -1414 14031 -1413 14032 -1412 14033 -1410 14034 -1409 14035 -1407 14036 -1406 14038 -1405 14039 -1403 14040 -1402 14041 -1401 14042 -1399 14043 -1398 14044 -1397 14045 -1395 14047 -1394 14048 -1393 14049 -1391 14050 -1390 14051 -1389 14052 -1387 14053 -1386 14055 -1385 14056 -1384 14057 -1383 14058 -1382 14059 -1381 14060 -1380 14061 -1379 14062 -1378 14064 -1376 14065 -1375 14066 -1374 14067 -1373 14068 -1372 14069 -1371 14070 -1370 14072 -1369 14073 -1368 14074 -1366 14075 -1365 14076 -1364 14077 -1363 14078 -1362 14079 -1361 14081 -1360 14082 -1359 14083 -1358 14084 -1356 14085 -1355 14086 -1354 14087 -1353 14088 -1352 14090 -1351 14091 -1350 14092 -1349 14093 -1348 14094 -1347 14095 -1345 14096 -1344 14098 -1343 14099 -1342 14100 -1341 14101 -1340 14102 -1339 14103 -1338 14104 -1337 14105 -1335 14107 -1334 14108 -1333 14109 -1332 14110 -1331 14111 -1330 14112 -1329 14113 -1328 14115 -1327 14116 -1325 14117 -1324 14118 -1323 14119 -1322 14120 -1321 14121 -1320 14122 -1319 14123 -1318 14124 -1317 14125 -1316 14126 -1314 14127 -1313 14128 -1312 14128 -1311 14129 -1310 14130 -1309 14131 -1308 14132 -1307 14133 -1306 14134 -1304 14134 -1303 14135 -1302 14136 -1301 14137 -1300 14138 -1299 14139 -1298 14140 -1297 14141 -1296 14141 -1294 14142 -1293 14143 -1292 14144 -1291 14145 -1288 14146 -1287 14147 -1286 14147 -1285 14148 -1285 14149 -1284 14150 -1283 14151 -1282 14152 -1281 14153 -1280 14154 -1279 14154 -1278 14155 -1277 14156 -1276 14157 -1275 14158 -1274 14159 -1273 14160 -1272 14161 -1271 14161 -1270 14162 -1269 14165 -1268 14169 -1267 14170 -1266 14170 -1265 14171 -1264 14172 -1263 14173 -1262 14174 -1261 14174 -1260 14175 -1259 14176 -1258 14177 -1257 14178 -1256 14179 -1255 14179 -1254 14180 -1253 14181 -1252 14182 -1251 14183 -1250 14183 -1249 14184 -1248 14185 -1247 14186 -1246 14187 -1245 14187 -1244 14188 -1243 14189 -1242 14190 -1241 14191 -1240 14192 -1239 14192 -1238 14193 -1238 14194 -1237 14195 -1236 14196 -1235 14196 -1234 14197 -1233 14198 -1232 14199 -1231 14200 -1230 14201 -1229 14201 -1228 14202 -1228 14203 -1227 14204 -1226 14205 -1225 14205 -1224 14206 -1223 14207 -1222 14208 -1222 14209 -1221 14209 -1220 14210 -1219 14211 -1218 14212 -1217 14213 -1216 14214 -1215 14214 -1215 14215 -1214 14216 -1213 14217 -1212 14218 -1211 14218 -1210 14219 -1209 14220 -1209 14221 -1208 14222 -1207 14222 -1206 14223 -1205 14224 -1204 14225 -1203 14225 -1203 14226 -1202 14227 -1201 14227 -1200 14228 -1199 14229 -1198 14229 -1197 14230 -1196 14231 -1196 14231 -1195 14232 -1194 14233 -1193 14233 -1192 14234 -1191 14235 -1190 14235 -1190 14236 -1189 14237 -1188 14237 -1187 14238 -1186 14239 -1185 14239 -1184 14240 -1184 14241 -1183 14241 -1182 14242 -1181 14243 -1180 14243 -1179 14244 -1178 14245 -1177 14245 -1177 14246 -1176 14247 -1175 14247 -1174 14248 -1173 14249 -1172 14249 -1171 14250 -1171 14251 -1170 14251 -1169 14252 -1168 14253 -1167 14253 -1167 14254 -1166 14255 -1165 14255 -1164 14256 -1164 14257 -1163 14257 -1162 14258 -1162 14259 -1161 14259 -1160 14260 -1159 14261 -1159 14261 -1158 14262 -1157 14263 -1156 14263 -1156 14264 -1155 14265 -1154 14265 -1154 14266 -1153 14267 -1152 14267 -1151 14268 -1151 14269 -1150 14269 -1149 14270 -1148 14271 -1148 14271 -1147 14272 -1146 14273 -1146 14273 -1145 14274 -1144 14275 -1143 14275 -1143 14276 -1142 14277 -1141 14277 -1140 14278 -1140 14279 -1139 14279 -1138 14280 -1137 14281 -1137 14281 -1136 14282 -1135 14283 -1135 14283 -1134 14284 -1133 14284 -1132 14285 -1132 14286 -1131 14286 -1130 14287 -1129 14288 -1129 14288 -1128 14289 -1127 14290 -1127 14290 -1126 14291 -1125 14292 -1124 14292 -1124 14293 -1123 14294 -1122 14294 -1121 14295 -1121 14296 -1120 14296 -1119 14297 -1119 14298 -1118 14298 -1117 14299 -1116 14300 -1116 14300 -1115 14301 -1114 14302 -1113 14302 -1113 14303 -1112 14304 -1111 14304 -1111 14305 -1110 14306 -1109 14306 -1108 14307 -1108 14308 -1107 14308 -1106 14309 -1105 14310 -1105 14310 -1104 14311 -1103 14312 -1102 14312 -1102 14313 -1101 14314 -1100 14314 -1100 14315 -1099 14316 -1098 14316 -1097 14317 -1097 14318 -1096 14318 -1095 14319 -1094 14320 -1094 14320 -1093 14321 -1092 14322 -1092 14322 -1091 14323 -1090 14324 -1089 14324 -1089 14325 -1088 14326 -1087 14326 -1086 14327 -1086 14328 -1085 14328 -1084 14329 -1084 14330 -1083 14330 -1082 14331 -1081 14332 -1081 14332 -1080 14333 -1079 14334 -1079 14334 -1078 14335 -1077 14336 -1077 14336 -1076 14337 -1075 14338 -1075 14338 -1074 14339 -1073 14340 -1073 14340 -1072 14341 -1071 14342 -1071 14342 -1070 14343 -1069 14343 -1069 14344 -1068 14345 -1067 14345 -1067 14346 -1066 14347 -1065 14347 -1065 14348 -1064 14349 -1063 14349 -1062 14350 -1062 14350 -1061 14351 -1060 14352 -1060 14352 -1059 14353 -1058 14354 -1058 14354 -1057 14355 -1056 14355 -1056 14356 -1055 14357 -1054 14357 -1054 14358 -1053 14359 -1052 14359 -1052 14360 -1051 14360 -1050 14361 -1050 14362 -1049 14362 -1048 14363 -1048 14364 -1047 14364 -1046 14365 -1046 14366 -1045 14366 -1044 14367 -1044 14367 -1043 14368 -1042 14369 -1041 14369 -1041 14370 -1040 14371 -1039 14371 -1039 14372 -1038 14372 -1037 14373 -1037 14374 -1036 14374 -1035 14375 -1035 14376 -1034 14376 -1033 14377 -1033 14377 -1032 14378 -1031 14379 -1031 14379 -1030 14380 -1029 14381 -1029 14381 -1028 14382 -1027 14382 -1027 14383 -1026 14384 -1025 14384 -1025 14385 -1024 14386 -1023 14386 -1023 14387 -1022 14388 -1021 14388 -1020 14389 -1020 14389 -1019 14390 -1019 14391 -1018 14391 -1018 14392 -1017 14393 -1016 14393 -1016 14394 -1015 14394 -1015 14395 -1014 14396 -1013 14396 -1013 14397 -1012 14398 -1012 14398 -1011 14399 -1010 14399 -1010 14400 -1009 14401 -1009 14401 -1008 14402 -1008 14403 -1007 14403 -1006 14404 -1006 14405 -1005 14405 -1005 14406 -1004 14406 -1003 14407 -1003 14408 -1002 14408 -1002 14409 -1001 14410 -1001 14410 -1000 14411 -999 14411 -999 14412 -998 14413 -998 14413 -997 14414 -996 14415 -996 14415 -995 14416 -995 14416 -994 14417 -994 14418 -993 14418 -992 14419 -992 14420 -991 14420 -991 14421 -990 14422 -989 14422 -989 14423 -988 14423 -988 14424 -987 14425 -987 14425 -986 14426 -985 14427 -985 14427 -984 14428 -984 14428 -983 14429 -982 14430 -982 14430 -981 14431 -981 14432 -980 14432 -979 14433 -979 14434 -978 14434 -978 14435 -977 14435 -977 14436 -976 14437 -975 14437 -975 14429 -975 14430 -975 14430 -975 14431 -975 14432 -975 14432 -974 14433 -974 14434 -974 14435 -973 14435 -973 14436 -972 14437 -972 14437 -971 14438 -971 14439 -970 14439 -970 14440 -969 14441 -968 14441 -968 14442 -967 14443 -967 14443 -966 14444 -966 14445 -965 14445 -965 14446 -964 14447 -964 14447 -963 14448 -963 14449 -962 14450 -962 14450 -961 14451 -960 14452 -960 14452 -959 14453 -959 14454 -958 14454 -958 14455 -957 14456 -957 14456 -956 14457 -956 14458 -955 14458 -955 14459 -954 14460 -953 14460 -953 14461 -952 14462 -952 14463 -951 14463 -951 14464 -950 14465 -950 14465 -949 14466 -949 14467 -948 14467 -948 14468 -947 14469 -947 14469 -946 14470 -945 14471 -945 14471 -944 14472 -944 14473 -943 14473 -943 14474 -942 14475 -942 14476 -941 14476 -941 14477 -940 14478 -940 14478 -939 14479 -938 14480 -938 14480 -937 14481 -937 14482 -936 14482 -936 14483 -935 14484 -935 14484 -934 14485 -934 14486 -933 14486 -933 14487 -932 14488 -931 14489 -931 14489 -930 14490 -930 14491 -929 14491 -929 14492 -928 14493 -928 14493 -927 14494 -927 14495 -926 14495 -926 14496 -925 14497 -925 14497 -924 14498 -923 14499 -923 14499 -922 14500 -922 14501 -921 14502 -921 14502 -920 14503 -920 14504 -919 14504 -919 14505 -918 14506 -918 14506 -917 14507 -916 14508 -916 14508 -915 14509 -915 14510 -914 14510 -914 14511 -913 14512 -913 14512 -912 14513 -912 14514 -911 14515 -911 14515 -910 14516 -910 14517 -909 14517 -908 14518 -908 14519 -907 14519 -907 14520 -906 14521 -906 14521 -905 14522 -905 14523 -904 14523 -904 14524 -903 14525 -903 14525 -902 14526 -901 14527 -901 14527 -900 14528 -900 14529 -899 14530 -899 14530 -898 14531 -898 14532 -897 14532 -897 14533 -896 14534 -896 14534 -895 14535 -895 14536 -894 14536 -893 14537 -893 14538 -892 14538 -892 14539 -891 14540 -891 14540 -890 14541 -890 14542 -889 14543 -889 14543 -888 14544 -888 14545 -887 14545 -886 14546 -886 14546 -885 14547 -885 14547 -884 14548 -884 14549 -883 14549 -883 14550 -882 14550 -882 14551 -881 14551 -881 14552 -880 14553 -879 14553 -879 14554 -878 14554 -878 14555 -877 14555 -877 14556 -876 14556 -876 14557 -875 14558 -875 14558 -874 14559 -874 14559 -873 14560 -873 14560 -872 14561 -871 14562 -871 14562 -870 14563 -870 14563 -869 14564 -869 14564 -868 14565 -868 14565 -867 14566 -867 14567 -866 14567 -866 14568 -865 14568 -864 14569 -864 14569 -863 14570 -863 14571 -862 14571 -862 14572 -861 14572 -861 14573 -860 14573 -860 14574 -859 14575 -859 14575 -858 14576 -857 14576 -857 14577 -856 14577 -856 14578 -855 14578 -855 14579 -854 14580 -854 14580 -853 14581 -853 14581 -852 14582 -851 14582 -851 14583 -850 14584 -850 14584 -849 14585 -849 14585 -848 14586 -848 14586 -847 14587 -847 14587 -846 14588 -846 14589 -845 14589 -844 14590 -844 14590 -843 14591 -843 14591 -842 14592 -842 14593 -841 14593 -841 14594 -840 14594 -840 14595 -839 14595 -838 14596 -838 14596 -837 14597 -837 14598 -836 14598 -836 14599 -835 14599 -835 14600 -834 14600 -834 14601 -833 14602 -833 14602 -832 14603 -831 14603 -831 14604 -830 14604 -830 14605 -829 14605 -829 14606 -828 14607 -828 14607 -827 14608 -827 14608 -826 14609 -826 14609 -825 14610 -824 14611 -824 14611 -823 14612 -823 14612 -822 14613 -822 14613 -821 14614 -821 14614 -820 14615 -820 14616 -819 14616 -818 14617 -818 14617 -817 14618 -817 14618 -816 14619 -816 14620 -815 14620 -815 14621 -814 14621 -814 14622 -813 14622 -813 14623 -812 14624 -811 14624 -811 14625 -810 14625 -810 14626 -809 14626 -809 14627 -808 14627 -808 14628 -807 14629 -807 14629 -806 14630 -805 14630 -805 14631 -804 14631 -804 14632 -803 14633 -803 14633 -802 14634 -802 14634 -801 14635 -801 14635 -800 14636 -800 14636 -799 14637 -798 14638 -798 14638 -797 14639 -797 14639 -796 14640 -796 14640 -795 14641 -795 14642 -794 14642 -794 14643 -793 14643 -792 14644 -792 14644 -791 14645 -791 14645 -790 14646 -790 14647 -789 14647 -789 14648 -788 14648 -788 14649 -787 14650 -787 14650 -786 14651 -785 14651 -785 14652 -784 14652 -784 14653 -783 14654 -783 14654 -782 14655 -782 14655 -781 14656 -781 14656 -780 14657 -780 14658 -779 14658 -778 14659 -778 14659 -777 14660 -777 14660 -776 14661 -776 14662 -775 14662 -775 14663 -774 14663 -774 14664 -773 14664 -772 14665 -772 14666 -771 14666 -771 14667 -770 14667 -770 14668 -769 14669 -769 14669 -768 14670 -768 14670 -767 14671 -767 14671 -766 14672 -765 14673 -765 14673 -764 14674 -764 14674 -763 14675 -763 14675 -762 14676 -762 14677 -761 14677 -761 14678 -760 14678 -759 14679 -759 14679 -758 14680 -758 14681 -757 14681 -757 14682 -756 14682 -756 14683 -755 14683 -755 14684 -754 14685 -754 14685 -753 14686 -752 14686 -752 14687 -751 14688 -751 14688 -750 14689 -750 14689 -749 14690 -749 14690 -748 14691 -748 14692 -747 14692 -747 14693 -746 14693 -746 14694 -745 14694 -745 14695 -744 14696 -744 14696 -743 14697 -743 14697 -742 14698 -742 14698 -741 14699 -741 14700 -740 14700 -740 14701 -739 14701 -739 14702 -738 14702 -738 14703 -737 14704 -737 14704 -736 14705 -736 14705 -735 14706 -735 14707 -734 14707 -734 14708 -733 14708 -732 14709 -732 14709 -731 14710 -731 14711 -730 14711 -730 14712 -729 14712 -729 14713 -728 14713 -728 14714 -727 14714 -727 14715 -726 14715 -726 14716 -725 14716 -725 14717 -724 14717 -724 14717 -723 14718 -723 14718 -722 14719 -722 14719 -721 14720 -721 14720 -720 14721 -720 14721 -719 14721 -719 14722 -718 14722 -718 14723 -717 14723 -717 14724 -716 14724 -716 14724 -715 14725 -715 14725 -714 14726 -714 14726 -713 14727 -713 14727 -712 14728 -712 14728 -711 14728 -711 14729 -710 14729 -710 14730 -709 14730 -709 14731 -708 14731 -708 14731 -707 14732 -707 14732 -706 14733 -706 14733 -705 14734 -705 14734 -704 14735 -704 14735 -703 14735 -703 14736 -702 14736 -701 14737 -701 14737 -700 14738 -700 14738 -699 14738 -699 14739 -698 14739 -698 14740 -697 14740 -697 14741 -696 14741 -696 14741 -695 14742 -695 14742 -695 14743 -694 14743 -694 14744 -693 14744 -693 14745 -692 14745 -692 14745 -691 14746 -691 14746 -690 14747 -690 14747 -689 14748 -689 14748 -689 14748 -688 14749 -688 14749 -687 14750 -687 14750 -686 14751 -686 14751 -685 14752 -685 14752 -684 14752 -684 14753 -683 14753 -683 14754 -682 14754 -682 14755 -682 14755 -681 14755 -681 14756 -680 14756 -680 14757 -679 14757 -679 14758 -678 14758 -678 14759 -677 14759 -677 14759 -676 14760 -676 14760 -675 14761 -675 14761 -675 14762 -674 14762 -674 14762 -673 14763 -673 14763 -672 14764 -672 14764 -671 14765 -671 14765 -670 14765 -670 14766 -669 14766 -669 14767 -669 14767 -668 14768 -668 14768 -667 14769 -667 14769 -666 14769 -666 14770 -665 14770 -665 14771 -664 14771 -664 14772 -663 14772 -663 14772 -662 14773 -662 14773 -662 14774 -661 14774 -661 14775 -660 14775 -660 14776 -659 14776 -659 14776 -659 14777 -658 14777 -658 14778 -658 14778 -657 14778 -657 14779 -657 14779 -656 14780 -656 14780 -656 14780 -655 14781 -655 14781 -654 14781 -654 14782 -654 14782 -653 14783 -653 14783 -653 14783 -652 14784 -652 14784 -652 14784 -651 14785 -651 14785 -651 14786 -650 14786 -650 14786 -650 14787 -649 14787 -649 14788 -649 14788 -648 14788 -648 14789 -648 14789 -647 14789 -647 14790 -647 14790 -646 14791 -646 14791 -646 14791 -645 14792 -645 14792 -644 14793 -644 14793 -644 14793 -643 14794 -643 14794 -643 14794 -642 14795 -642 14795 -642 14796 -641 14796 -641 14796 -641 14797 -640 14797 -640 14797 -640 14798 -639 14798 -639 14799 -639 14799 -638 14799 -638 14800 -638 14800 -637 14801 -637 14801 -637 14801 -636 14802 -636 14802 -636 14802 -635 14803 -635 14803 -634 14804 -634 14804 -634 14804 -633 14805 -633 14805 -633 14806 -632 14806 -632 14806 -632 14807 -631 14807 -631 14807 -631 14808 -630 14808 -630 14809 -630 14809 -629 14809 -629 14810 -629 14810 -628 14810 -628 14811 -628 14811 -627 14812 -627 14812 -627 14812 -626 14813 -626 14813 -625 14814 -625 14814 -625 14814 -624 14815 -624 14815 -624 14815 -623 14816 -623 14816 -623 14817 -622 14817 -622 14817 -622 14818 -621 14818 -621 14819 -621 14819 -620 14819 -620 14820 -620 14820 -620 14820 -619 14821 -619 14821 -619 14822 -618 14822 -618 14822 -618 14823 -618 14823 -617 14823 -617 14824 -617 14824 -616 14825 -616 14825 -616 14825 -616 14826 -615 14826 -615 14827 -615 14827 -614 14827 -614 14828 -614 14828 -614 14828 -613 14829 -613 14829 -613 14830 -613 14830 -612 14830 -612 14831 -612 14831 -611 14832 -611 14832 -611 14832 -611 14833 -610 14833 -610 14833 -610 14834 -609 14834 -609 14835 -609 14835 -609 14835 -608 14836 -608 14836 -608 14837 -607 14837 -607 14837 -607 14838 -607 14838 -606 14839 -606 14839 -606 14839 -605 14840 -605 14840 -605 14840 -605 14841 -604 14841 -604 14842 -604 14842 -603 14842 -603 14843 -603 14843 -603 14844 -602 14844 -602 14844 -602 14845 -602 14845 -601 14846 -601 14846 -601 14846 -600 14847 -600 14847 -600 14848 -600 14848 -599 14848 -599 14849 -599 14849 -598 14849 -598 14850 -598 14850 -598 14851 -597 14851 -597 14851 -597 14852 -596 14852 -596 14853 -596 14853 -596 14853 -595 14854 -595 14854 -595 14855 -594 14855 -594 14855 -594 14856 -594 14856 -593 14857 -593 14857 -593 14857 -593 14858 -592 14858 -592 14858 -592 14859 -591 14859 -591 14860 -591 14860 -591 14860 -590 14861 -590 14861 -590 14862 -589 14862 -589 14862 -589 14863 -589 14863 -588 14864 -588 14864 -588 14864 -587 14865 -587 14865 -587 14866 -587 14866 -586 14866 -586 14867 -586 14867 -585 14867 -585 14868 -585 14868 -585 14869 -584 14869 -584 14869 -584 14870 -583 14870 -583 14871 -583 14871 -583 14871 -582 14872 -582 14872 -582 14873 -582 14873 -581 14873 -581 14874 -581 14874 -580 14874 -580 14875 -580 14875 -580 14876 -579 14876 -579 14876 -579 14877 -578 14877 -578 14878 -578 14878 -578 14878 -577 14879 -577 14879 -577 14880 -576 14880 -576 14880 -576 14881 -576 14881 -575 14882 -575 14882 -575 14882 -574 14883 -574 14883 -574 14883 -574 14884 -573 14884 -573 14885 -573 14885 -572 14885 -572 14886 -572 14886 -572 14887 -571 14887 -571 14887 -571 14888 -571 14888 -570 14889 -570 14889 -570 14889 -569 14890 -569 14890 -569 14891 -569 14891 -568 14891 -568 14892 -568 14892 -567 14893 -567 14893 -567 14893 -567 14894 -566 14894 -566 14895 -566 14895 -565 14896 -565 14896 -565 14896 -565 14897 -564 14897 -564 14898 -564 14898 -563 14899 -563 14899 -563 14899 -563 14900 -562 14900 -562 14901 -562 14901 -562 14902 -561 14902 -561 14902 -561 14903 -560 14903 -560 14904 -560 14904 -560 14905 -559 14905 -559 14906 -559 14906 -558 14906 -558 14907 -558 14907 -558 14908 -557 14908 -557 14909 -557 14909 -556 14909 -556 14910 -556 14910 -556 14911 -555 14911 -555 14912 -555 14912 -554 14912 -554 14913 -554 14913 -554 14914 -553 14914 -553 14915 -553 14915 -553 14916 -552 14916 -552 14916 -552 14917 -551 14917 -551 14918 -551 14918 -551 14919 -550 14919 -550 14919 -550 14920 -549 14920 -549 14921 -549 14921 -549 14922 -548 14922 -548 14922 -548 14923 -547 14923 -547 14924 -547 14924 -547 14925 -546 14925 -546 14925 -546 14926 -545 14926 -545 14927 -545 14927 -545 14928 -544 14928 -544 14929 -544 14929 -544 14929 -543 14930 -543 14930 -543 14931 -542 14931 -542 14932 -542 14932 -542 14932 -541 14933 -541 14933 -541 14934 -540 14934 -540 14935 -540 14935 -540 14935 -539 14936 -539 14936 -539 14937 -538 14937 -538 14938 -538 14938 -538 14939 -537 14939 -537 14939 -537 14940 -537 14940 -536 14941 -536 14941 -536 14942 -535 14942 -535 14942 -535 14943 -535 14943 -534 14944 -534 14944 -534 14944 -533 14944 -533 14945 -533 14945 -533 14945 -532 14945 -532 14945 -532 14946 -531 14946 -531 14946 -531 14946 -531 14947 -530 14947 -530 14947 -530 14947 -529 14947 -529 14948 -529 14948 -529 14948 -528 14948 -528 14949 -528 14949 -528 14949 -527 14949 -527 14949 -527 14950 -526 14950 -526 14950 -526 14950 -525 14951 -525 14951 -525 14951 -525 14951 -524 14951 -524 14952 -524 14952 -523 14952 -523 14952 -523 14952 -522 14953 -522 14953 -522 14953 -521 14953 -521 14954 -521 14954 -520 14954 -520 14954 -520 14954 -519 14955 -519 14955 -519 14955 -518 14955 -518 14956 -518 14956 -518 14956 -517 14956 -517 14956 -517 14957 -516 14957 -516 14957 -516 14957 -515 14957 -515 14958 -515 14958 -514 14958 -514 14958 -514 14959 -513 14959 -513 14959 -513 14959 -512 14959 -512 14960 -512 14960 -511 14960 -511 14960 -511 14961 -510 14961 -510 14961 -510 14961 -510 14961 -509 14962 -509 14962 -509 14962 -508 14962 -508 14962 -508 14963 -507 14963 -507 14963 -507 14963 -506 14964 -506 14964 -506 14964 -505 14964 -505 14964 -505 14965 -504 14965 -504 14965 -504 14965 -503 14966 -503 14966 -503 14966 -503 14966 -502 14966 -502 14967 -502 14967 -501 14967 -501 14967 -501 14967 -500 14968 -500 14968 -500 14968 -499 14968 -499 14969 -499 14969 -498 14969 -498 14969 -498 14969 -498 14970 -498 14970 -497 14970 -497 14970 -497 14971 -497 14971 -496 14971 -496 14971 -496 14971 -496 14971 -495 14972 -495 14972 -495 14972 -495 14972 -495 14972 -494 14972 -494 14972 -494 14973 -494 14973 -493 14973 -493 14973 -493 14973 -493 14973 -492 14973 -492 14974 -492 14974 -492 14974 -492 14974 -491 14974 -491 14974 -491 14974 -491 14975 -490 14975 -490 14975 -490 14975 -490 14975 -489 14975 -489 14976 -489 14976 -489 14976 -488 14976 -488 14976 -488 14976 -488 14976 -488 14977 -487 14977 -487 14977 -487 14977 -487 14977 -486 14977 -486 14977 -486 14978 -486 14978 -485 14978 -485 14978 -485 14978 -485 14978 -485 14979 -484 14979 -484 14979 -484 14979 -484 14979 -483 14979 -483 14979 -483 14980 -483 14980 -482 14980 -482 14980 -482 14980 -482 14980 -482 14980 -481 14981 -481 14981 -481 14981 -481 14981 -480 14981 -480 14981 -480 14981 -480 14982 -479 14982 -479 14982 -479 14982 -479 14982 -478 14982 -478 14983 -478 14983 -478 14983 -478 14983 -477 14983 -477 14983 -477 14983 -477 14984 -476 14984 -476 14984 -476 14984 -476 14984 -475 14984 -475 14984 -475 14985 -475 14985 -475 14985 -474 14985 -474 14985 -474 14985 -474 14986 -473 14986 -473 14986 -473 14986 -473 14986 -472 14986 -472 14986 -472 14987 -472 14987 -472 14987 -471 14987 -471 14987 -471 14987 -471 14987 -471 14988 -470 14988 -470 14988 -470 14988 -470 14988 -470 14988 -470 14988 -469 14989 -469 14989 -469 14989 -469 14989 -469 14989 -468 14989 -468 14990 -468 14990 -468 14990 -468 14990 -468 14990 -467 14990 -467 14990 -467 14991 -467 14991 -467 14991 -466 14991 -466 14991 -466 14991 -466 14991 -466 14992 -466 14992 -465 14992 -465 14992 -465 14992 -465 14992 -465 14993 -464 14993 -464 14993 -464 14993 -464 14993 -464 14993 -464 14993 -463 14994 -463 14994 -463 14994 -463 14994 -463 14994 -462 14994 -462 14994 -462 14995 -462 14995 -462 14995 -462 14995 -461 14995 -461 14995 -461 14995 -461 14996 -461 14996 -460 14996 -460 14996 -460 14996 -460 14996 -460 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -459 14998 -459 14998 -459 14998 -459 14998 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 14999 -459 15000 -459 15000 -459 15000 -459 15000 -458 15000 -458 15000 -458 15000 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15001 -458 15002 -458 15002 -458 15002 -457 15002 -457 15002 -457 15002 -457 15003 -457 15003 -457 15003 -457 15003 -457 15003 -456 15003 -456 15003 -456 15004 -456 15004 -456 15004 -456 15004 -456 15004 -455 15004 -455 15004 -455 15005 -455 15005 -455 15005 -455 15005 -455 15005 -454 15005 -454 15006 -454 15006 -454 15006 -454 15006 -454 15006 -454 15006 -453 15006 -453 15007 -453 15007 -453 15007 -453 15007 -453 15007 -453 15007 -452 15007 -452 15008 -452 15008 -452 15008 -452 15008 -452 15008 -452 15008 -451 15008 -451 15009 -451 15009 -451 15009 -451 15009 -451 15009 -451 15009 -451 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15010 -450 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15011 -449 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15012 -448 15013 -447 15013 -447 15013 -447 15013 -447 15013 -447 15013 -447 15014 -447 15014 -446 15014 -446 15014 -446 15014 -446 15014 -446 15014 -446 15015 -446 15015 -445 15015 -445 15015 -445 15015 -445 15015 -445 15015 -445 15016 -445 15016 -445 15016 -444 15016 -444 15016 -444 15016 -444 15016 -444 15017 -444 15017 -444 15017 -443 15017 -443 15017 -443 15017 -443 15018 -443 15018 -443 15018 -443 15018 -442 15018 -442 15018 -442 15018 -442 15019 -442 15019 -442 15019 -442 15019 -441 15019 -441 15019 -441 15019 -441 15020 -441 15020 -441 15020 -441 15020 -440 15020 -440 15020 -440 15021 -440 15021 -440 15021 -440 15021 -440 15021 -439 15021 -439 15021 -439 15022 -439 15022 -439 15022 -439 15022 -439 15022 -439 15022 -438 15022 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -437 15023 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -435 15026 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -434 15027 -433 15028 -433 15028 -433 15028 -433 15028 -433 15028 -433 15028 -433 15029 -433 15029 -432 15029 -432 15029 -432 15029 -432 15029 -432 15029 -432 15030 -432 15030 -431 15030 -431 15030 -431 15030 -431 15030 -431 15030 -431 15031 -431 15031 -430 15031 -430 15031 -430 15031 -430 15031 -430 15032 -430 15032 -430 15032 -429 15032 -429 15032 -429 15032 -429 15032 -429 15033 -429 15033 -429 15033 -428 15033 -428 15033 -428 15033 -428 15033 -428 15034 -428 15034 -428 15034 -427 15034 -427 15034 -427 15034 -427 15034 -427 15035 -427 15035 -427 15035 -427 15035 -426 15035 -426 15035 -426 15036 -426 15036 -426 15036 -426 15036 -426 15036 -425 15036 -425 15036 -425 15037 -425 15037 -425 15037 -425 15037 -425 15037 -424 15037 -424 15037 -424 15038 -424 15038 -424 15038 -424 15038 -424 15038 -423 15038 -423 15038 -423 15039 -423 15039 -423 15039 -423 15039 -423 15039 -422 15039 -422 15040 -422 15040 -422 15040 -422 15040 -422 15040 -422 15040 -421 15040 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -421 15041 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -420 15042 -419 15043 -419 15043 -419 15043 -419 15043 -419 15043 -419 15043 -419 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15044 -418 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15045 -417 15046 -416 15046 -416 15046 -416 15046 -416 15046 -416 15046 -416 15047 -416 15047 -415 15047 -415 15047 -415 15047 -415 15047 -415 15048 -415 15048 -415 15048 -415 15048 -414 15048 -414 15048 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -413 15049 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -412 15050 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15054 -410 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15055 -409 15055 -409 15055 -408 15055 -408 15055 -408 15055 -408 15056 -408 15056 -408 15056 -408 15056 -407 15056 -407 15056 -407 15057 -407 15057 -407 15057 -407 15057 -407 15057 -406 15057 -406 15058 -406 15058 -406 15058 -406 15058 -406 15058 -406 15058 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -405 15059 -404 15060 -404 15060 -404 15060 -404 15060 -404 15060 -404 15060 -404 15061 -403 15061 -403 15061 -403 15061 -403 15061 -403 15061 -403 15062 -403 15062 -403 15062 -402 15062 -402 15062 -402 15062 -402 15063 -402 15063 -402 15063 -402 15063 -402 15063 -401 15063 -401 15063 -401 15064 -401 15064 -401 15064 -401 15064 -401 15064 -401 15064 -401 15065 -400 15065 -400 15065 -400 15065 -400 15065 -400 15065 -400 15066 -400 15066 -400 15066 -399 15066 -399 15066 -399 15066 -399 15067 -399 15067 -399 15067 -399 15067 -399 15067 -399 15067 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15068 -398 15069 -397 15069 -397 15069 -397 15069 -397 15069 -397 15069 -397 15070 -397 15070 -397 15070 -397 15070 -396 15070 -396 15070 -396 15071 -396 15071 -396 15071 -396 15071 -396 15071 -396 15071 -396 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15072 -395 15073 -395 15073 -394 15073 -394 15073 -394 15073 -394 15073 -394 15074 -394 15074 -394 15074 -394 15074 -394 15074 -393 15074 -393 15075 -393 15075 -393 15075 -393 15075 -393 15075 -393 15075 -393 15076 -392 15076 -392 15076 -392 15076 -392 15076 -392 15076 -392 15077 -392 15077 -392 15077 -392 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15077 -391 15078 -391 15078 -391 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15078 -390 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -389 15079 -388 15079 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -388 15080 -387 15080 -387 15080 -387 15080 -387 15080 -387 15081 -387 15081 -387 15081 -387 15081 -387 15081 -386 15081 -386 15081 -386 15081 -386 15081 -386 15081 -386 15082 -386 15082 -386 15082 -386 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15082 -385 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -384 15083 -383 15083 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -383 15084 -382 15084 -382 15084 -382 15084 -382 15085 -382 15085 -382 15085 -382 15085 -382 15085 -382 15085 -381 15085 -381 15085 -381 15085 -381 15085 -381 15085 -381 15086 -381 15086 -381 15086 -381 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15086 -380 15087 -380 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -379 15087 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -378 15088 -377 15088 -377 15088 -377 15088 -377 15089 -377 15089 -377 15089 -377 15089 -377 15089 -377 15089 -376 15089 -376 15089 -376 15089 -376 15089 -376 15090 -376 15090 -376 15090 -376 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15090 -375 15091 -375 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -374 15091 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -373 15092 -372 15092 -372 15092 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -372 15093 -371 15093 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -371 15094 -370 15094 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -370 15095 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15096 -369 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15097 -368 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15098 -367 15099 -367 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15099 -366 15100 -366 15100 -365 15100 -365 15100 -365 15100 -365 15100 -365 15100 -365 15101 -365 15101 -365 15101 -365 15101 -364 15101 -364 15101 -364 15101 -364 15101 -364 15102 -364 15102 -363 15102 -363 15102 -363 15102 -363 15102 -363 15102 -362 15102 -362 15103 -362 15103 -362 15103 -362 15103 -362 15103 -361 15103 -361 15103 -361 15103 -361 15104 -361 15104 -360 15104 -360 15104 -360 15104 -360 15104 -360 15104 -360 15104 -359 15105 -359 15105 -359 15105 -359 15105 -359 15105 -358 15105 -358 15105 -358 15105 -358 15106 -358 15106 -357 15106 -357 15106 -357 15106 -357 15106 -357 15106 -357 15106 -356 15107 -356 15107 -356 15107 -356 15107 -356 15107 -355 15107 -355 15107 -355 15107 -355 15108 -355 15108 -355 15108 -354 15108 -354 15108 -354 15108 -354 15108 -354 15109 -353 15109 -353 15109 -353 15109 -353 15109 -353 15109 -353 15109 -352 15109 -352 15110 -352 15110 -352 15110 -352 15110 -351 15110 -351 15110 -351 15110 -351 15110 -351 15111 -350 15111 -350 15111 -350 15111 -350 15111 -350 15111 -350 15111 -349 15111 -349 15112 -349 15112 -349 15112 -349 15112 -348 15112 -348 15112 -348 15112 -348 15112 -348 15113 -348 15113 -347 15113 -347 15113 -347 15113 -347 15113 -347 15113 -346 15113 -346 15114 -346 15114 -346 15114 -346 15114 -346 15114 -345 15114 -345 15114 -345 15114 -345 15115 -345 15115 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -342 15116 -342 15116 -342 15117 -342 15117 -342 15117 -341 15117 -341 15117 -341 15117 -341 15117 -341 15117 -341 15118 -340 15118 -340 15118 -340 15118 -340 15118 -340 15118 -339 15118 -339 15119 -339 15119 -339 15119 -339 15119 -339 15119 -338 15119 -338 15120 -338 15120 -338 15120 -338 15120 -337 15120 -337 15120 -337 15121 -337 15121 -337 15121 -336 15121 -336 15121 -336 15121 -336 15122 -336 15122 -336 15122 -336 15122 -336 15122 -335 15122 -335 15123 -335 15123 -335 15123 -335 15123 -335 15123 -335 15123 -335 15124 -335 15124 -335 15124 -335 15124 -335 15124 -334 15124 -334 15125 -334 15125 -334 15125 -334 15125 -334 15125 -334 15125 -334 15126 -334 15126 -334 15126 -334 15126 -334 15126 -334 15126 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15128 -333 15128 -333 15128 -333 15128 -333 15128 -333 15128 -332 15129 -332 15129 -332 15129 -332 15129 -332 15129 -332 15129 -332 15130 -332 15130 -332 15130 -332 15130 -332 15130 -332 15130 -332 15131 -331 15131 -331 15131 -331 15131 -331 15131 -331 15131 -331 15132 -331 15132 -331 15132 -331 15132 -331 15132 -331 15132 -331 15133 -330 15133 -330 15133 -330 15133 -330 15133 -330 15133 -330 15134 -330 15134 -330 15134 -330 15134 -330 15134 -330 15134 -330 15135 -330 15135 -329 15135 -329 15135 -329 15135 -329 15135 -329 15136 -329 15136 -329 15136 -329 15136 -329 15136 -329 15136 -329 15137 -329 15137 -328 15137 -328 15137 -328 15137 -328 15137 -328 15138 -328 15138 -328 15138 -328 15138 -328 15138 -328 15138 -328 15139 -328 15139 -328 15139 -327 15139 -327 15139 -327 15139 -327 15140 -327 15140 -327 15140 -327 15140 -327 15140 -327 15140 -327 15141 -327 15141 -327 15141 -326 15141 -326 15141 -326 15141 -326 15142 -326 15142 -326 15142 -326 15142 -326 15142 -326 15142 -326 15143 -326 15143 -326 15143 -325 15143 -325 15143 -325 15143 -325 15144 -325 15144 -325 15144 -325 15144 -325 15144 -325 15144 -325 15145 -325 15145 -325 15145 -325 15145 -324 15145 -324 15145 -324 15146 -324 15146 -324 15146 -324 15146 -324 15146 -324 15146 -324 15147 -324 15147 -324 15147 -324 15147 -323 15147 -323 15147 -323 15148 -323 15148 -323 15148 -323 15148 -323 15148 -323 15148 -323 15149 -323 15149 -323 15149 -323 15149 -323 15149 -322 15149 -322 15150 -322 15150 -322 15150 -322 15150 -322 15150 -322 15150 -322 15151 -322 15151 -322 15151 -322 15151 -322 15151 -321 15151 -321 15151 -321 15152 -321 15152 -321 15152 -321 15152 -321 15152 -321 15152 -321 15153 -321 15153 -321 15153 -321 15153 -321 15153 -320 15153 -320 15153 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15154 -320 15155 -319 15155 -319 15155 -319 15155 -319 15155 -319 15155 -319 15156 -319 15156 -319 15156 -319 15156 -318 15156 -318 15156 -318 15156 -318 15157 -318 15157 -318 15157 -318 15157 -318 15157 -317 15157 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15158 -317 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15159 -316 15160 -316 15160 -315 15160 -315 15160 -315 15160 -315 15160 -315 15161 -315 15161 -315 15161 -315 15161 -315 15161 -314 15161 -314 15161 -314 15162 -314 15162 -314 15162 -314 15162 -314 15162 -314 15162 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15163 -313 15164 -313 15164 -312 15164 -312 15164 -312 15164 -312 15164 -312 15164 -312 15165 -312 15165 -312 15165 -311 15165 -311 15165 -311 15165 -311 15166 -311 15166 -311 15166 -311 15166 -311 15166 -311 15166 -310 15166 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15167 -310 15168 -309 15168 -309 15168 -309 15168 -309 15168 -309 15168 -309 15169 -309 15169 -309 15169 -308 15169 -308 15169 -308 15169 -308 15169 -308 15170 -308 15170 -308 15170 -308 15170 -308 15170 -307 15170 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -307 15171 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15172 -306 15173 -306 15173 -305 15173 -305 15173 -305 15173 -305 15173 -305 15174 -305 15174 -305 15174 -305 15174 -304 15174 -304 15174 -304 15174 -304 15175 -304 15175 -304 15175 -304 15175 -304 15175 -304 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -303 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -302 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -301 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -300 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -299 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -298 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -297 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -296 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -295 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -294 15175 -293 15175 -293 15175 -293 15175 -293 15175 -293 15178 -292 15178 -288 15178 -288 15178 -288 15178 -288 15178 -288 15178 -288 15178 -287 15178 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -287 15179 -286 15179 -286 15179 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -286 15180 -285 15180 -285 15180 -285 15180 -285 15181 -285 15181 -285 15181 -285 15181 -285 15181 -285 15181 -284 15181 -284 15181 -284 15181 -284 15181 -284 15181 -284 15182 -284 15182 -284 15182 -284 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15182 -283 15183 -283 15183 -283 15183 -283 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15183 -282 15184 -282 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -281 15184 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -280 15185 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -279 15186 -278 15186 -278 15186 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -278 15187 -277 15187 -277 15187 -277 15187 -277 15187 -277 15188 -277 15188 -277 15188 -277 15188 -277 15188 -277 15188 -276 15188 -276 15188 -276 15188 -276 15188 -276 15189 -276 15189 -276 15189 -276 15189 -276 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15189 -275 15190 -275 15190 -275 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15190 -274 15191 -274 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15191 -273 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -272 15192 -271 15192 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -271 15193 -270 15193 -270 15193 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -270 15194 -269 15194 -269 15194 -269 15194 -269 15195 -269 15195 -269 15195 -269 15195 -269 15195 -269 15195 -268 15195 -268 15195 -268 15195 -268 15195 -268 15196 -268 15196 -268 15196 -268 15196 -268 15196 -268 15196 -267 15196 -267 15196 -267 15196 -267 15197 -267 15197 -267 15197 -267 15197 -267 15197 -267 15197 -266 15197 -266 15197 -266 15197 -266 15198 -266 15198 -266 15198 -266 15198 -266 15198 -266 15198 -265 15198 -265 15198 -265 15198 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -265 15199 -264 15199 -264 15199 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -264 15200 -263 15200 -263 15200 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -263 15201 -262 15201 -262 15201 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -262 15202 -261 15202 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -261 15203 -260 15203 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -260 15204 -259 15204 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -259 15205 -258 15205 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -258 15206 -257 15206 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -257 15207 -256 15207 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -256 15208 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -255 15209 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -254 15210 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15211 -253 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15212 -252 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15213 -251 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15214 -250 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15215 -249 15216 -249 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15216 -248 15217 -248 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15217 -247 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15218 -246 15219 -246 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15219 -245 15220 -245 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15220 -244 15221 -244 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15221 -243 15222 -243 15222 -243 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15222 -242 15223 -242 15223 -242 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15223 -241 15224 -241 15224 -241 15224 -240 15224 -240 15224 -240 15224 -240 15224 -240 15224 -240 15224 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -239 15225 -238 15225 -238 15225 -238 15226 -238 15226 -238 15226 -238 15226 -237 15226 -237 15226 -237 15226 -237 15226 -237 15226 -237 15227 -236 15227 -236 15227 -236 15227 -236 15227 -236 15227 -236 15227 -235 15227 -235 15227 -235 15228 -235 15228 -235 15228 -235 15228 -234 15228 -234 15228 -234 15228 -234 15228 -234 15228 -234 15229 -234 15229 -233 15229 -233 15229 -233 15229 -233 15229 -233 15229 -233 15229 -232 15229 -232 15229 -232 15230 -232 15230 -232 15230 -232 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -228 15232 -228 15232 -228 15232 -228 15233 -228 15233 -228 15233 -227 15233 -227 15233 -227 15233 -227 15233 -227 15233 -227 15233 -226 15234 -226 15234 -226 15234 -226 15234 -226 15234 -226 15234 -225 15234 -225 15234 -225 15234 -225 15235 -225 15235 -225 15235 -225 15235 -224 15235 -224 15235 -224 15235 -224 15235 -224 15236 -224 15236 -223 15236 -223 15236 -223 15236 -223 15236 -223 15236 -223 15236 -222 15236 -222 15237 -222 15237 -222 15237 -222 15237 -222 15237 -221 15237 -221 15237 -221 15237 -221 15237 -221 15238 -221 15238 -221 15238 -220 15238 -220 15238 -220 15238 -220 15238 -220 15238 -220 15239 -219 15239 -219 15239 -219 15239 -219 15239 -219 15239 -219 15239 -218 15239 -218 15239 -218 15240 -218 15240 -218 15240 -218 15240 -217 15240 -217 15240 -217 15240 -217 15240 -217 15240 -217 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -216 15241 -215 15241 -215 15242 -215 15242 -215 15242 -215 15242 -215 15242 -214 15242 -214 15242 -214 15242 -214 15243 -214 15243 -214 15243 -213 15243 -213 15243 -213 15243 -213 15243 -213 15243 -213 15243 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -212 15244 -211 15244 -211 15244 -211 15245 -211 15245 -211 15245 -211 15245 -210 15245 -210 15245 -210 15245 -210 15245 -210 15245 -210 15246 -209 15246 -209 15246 -209 15246 -209 15246 -209 15246 -209 15246 -208 15246 -208 15247 -208 15247 -208 15247 -208 15247 -207 15247 -207 15247 -207 15247 -207 15247 -207 15247 -207 15248 -206 15248 -206 15248 -206 15248 -206 15248 -206 15248 -206 15248 -205 15248 -205 15248 -205 15249 -205 15249 -205 15249 -204 15249 -204 15249 -204 15249 -204 15249 -204 15249 -204 15250 -203 15250 -203 15250 -203 15250 -203 15250 -203 15250 -202 15250 -202 15250 -202 15250 -202 15251 -202 15251 -202 15251 -201 15251 -201 15251 -201 15251 -201 15251 -201 15251 -200 15251 -200 15252 -200 15252 -200 15252 -200 15252 -200 15252 -199 15252 -199 15252 -199 15252 -199 15252 -199 15253 -199 15253 -198 15253 -198 15253 -198 15253 -198 15253 -198 15253 -197 15253 -197 15254 -197 15254 -197 15254 -197 15254 -197 15254 -196 15254 -196 15254 -196 15254 -196 15254 -196 15255 -195 15255 -195 15255 -195 15255 -195 15255 -195 15255 -195 15255 -194 15255 -194 15255 -194 15256 -194 15256 -194 15256 -193 15256 -193 15256 -193 15256 -193 15256 -193 15256 -193 15256 -192 15257 -192 15257 -192 15257 -192 15257 -192 15257 -191 15257 -191 15257 -191 15257 -191 15258 -191 15258 -191 15258 -190 15258 -190 15258 -190 15258 -190 15258 -190 15258 -190 15258 -189 15259 -189 15259 -189 15259 -189 15259 -189 15259 -188 15259 -188 15259 -188 15259 -188 15259 -188 15260 -188 15260 -187 15260 -187 15260 -187 15260 -187 15260 -187 15260 -186 15260 -186 15261 -186 15261 -186 15261 -186 15261 -186 15261 -185 15261 -185 15261 -185 15261 -185 15261 -185 15261 -184 15262 -184 15262 -184 15262 -184 15262 -184 15262 -184 15262 -183 15262 -183 15262 -183 15262 -183 15262 -183 15262 -183 15262 -182 15262 -182 15263 -182 15263 -182 15263 -182 15263 -181 15263 -181 15263 -181 15263 -181 15263 -181 15263 -181 15263 -180 15263 -180 15263 -180 15263 -180 15264 -180 15264 -179 15264 -179 15264 -179 15264 -179 15264 -179 15264 -179 15264 -178 15264 -178 15264 -178 15264 -178 15264 -178 15264 -177 15264 -177 15265 -177 15265 -177 15265 -177 15265 -177 15265 -176 15265 -176 15265 -176 15265 -176 15265 -176 15265 -176 15265 -175 15265 -175 15265 -175 15266 -175 15266 -175 15266 -174 15266 -174 15266 -174 15266 -174 15266 -174 15266 -174 15266 -173 15266 -173 15266 -173 15266 -173 15266 -173 15267 -172 15267 -172 15267 -172 15267 -172 15267 -172 15267 -172 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -171 15267 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -170 15268 -169 15268 -169 15268 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -169 15269 -168 15269 -168 15269 -168 15269 -168 15269 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -168 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15270 -167 15271 -167 15271 -167 15271 -167 15271 -167 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15271 -166 15272 -166 15272 -166 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15272 -165 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -164 15273 -163 15273 -163 15273 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -163 15274 -162 15274 -162 15274 -162 15274 -162 15274 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -162 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15275 -161 15276 -161 15276 -161 15276 -161 15276 -161 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15276 -160 15277 -160 15277 -160 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -159 15277 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -158 15278 -157 15278 -157 15278 -157 15278 -157 15278 -157 15278 -157 15279 -157 15279 -157 15279 -157 15279 -157 15279 -157 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15279 -156 15280 -156 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -155 15280 -154 15280 -154 15280 -154 15280 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -154 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15281 -153 15282 -153 15282 -153 15282 -153 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -152 15282 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -151 15283 -150 15283 -150 15283 -150 15283 -150 15283 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -150 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15284 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15285 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15286 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -149 15287 -148 15288 -148 15288 -147 15288 -147 15288 -147 15288 -146 15288 -146 15288 -146 15288 -145 15288 -145 15288 -145 15288 -144 15288 -144 15288 -144 15288 -143 15288 -143 15289 -142 15289 -142 15289 -142 15289 -141 15289 -141 15289 -141 15289 -140 15289 -140 15289 -140 15289 -139 15289 -139 15289 -139 15289 -138 15290 -138 15290 -137 15290 -137 15290 -137 15290 -136 15290 -136 15290 -136 15290 -135 15290 -135 15290 -135 15290 -134 15290 -134 15291 -134 15291 -133 15291 -133 15291 -133 15291 -132 15291 -132 15291 -131 15291 -131 15291 -131 15291 -130 15291 -130 15292 -130 15292 -129 15292 -129 15292 -129 15292 -128 15292 -128 15292 -128 15292 -127 15292 -127 15292 -126 15292 -126 15292 -126 15293 -125 15293 -125 15293 -125 15293 -124 15293 -124 15293 -124 15293 -123 15293 -123 15293 -123 15293 -122 15293 -122 15293 -121 15294 -121 15294 -121 15294 -120 15294 -120 15294 -120 15294 -119 15294 -119 15294 -119 15294 -118 15294 -118 15294 -118 15294 -117 15295 -117 15295 -117 15295 -117 15295 -117 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15295 -116 15296 -116 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -115 15296 -114 15296 -114 15296 -114 15296 -114 15297 -114 15297 -114 15297 -114 15297 -114 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15297 -113 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -112 15298 -111 15298 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -111 15299 -110 15299 -110 15299 -110 15300 -110 15300 -110 15300 -110 15300 -110 15300 -110 15300 -109 15300 -109 15300 -109 15300 -109 15301 -109 15301 -109 15301 -109 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15301 -108 15302 -108 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15302 -107 15303 -106 15303 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -106 15302 -105 15302 -105 15302 -105 15301 -105 15301 -105 15301 -105 15301 -105 15301 -104 15301 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -104 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -105 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -106 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -107 15303 -108 15303 -108 15303 -108 15303 -108 15303 -108 15303 -109 15303 -109 15303 -109 15303 -109 15303 -109 15303 -109 15303 -110 15303 -110 15303 -110 15303 -110 15303 -110 15303 -111 15303 -111 15303 -111 15303 -111 15303 -111 15303 -111 15303 -112 15303 -112 15303 -112 15303 -112 15303 -112 15303 -113 15303 -113 15303 -113 15303 -113 15303 -113 15303 -113 15303 -114 15303 -114 15303 -114 15303 -114 15303 -114 15303 -114 15303 -115 15303 -115 15303 -115 15303 -115 15303 -115 15303 -116 15303 -116 15303 -116 15303 -116 15303 -116 15303 -116 15303 -117 15303 -117 15303 -117 15303 -117 15303 -117 15303 -118 15303 -118 15302 -118 15302 -118 15302 -118 15302 -119 15302 -119 15302 -119 15302 -119 15302 -119 15302 -120 15302 -120 15302 -120 15302 -120 15302 -121 15302 -121 15302 -121 15302 -121 15302 -121 15302 -122 15302 -122 15302 -122 15302 -122 15302 -123 15302 -123 15302 -123 15302 -123 15302 -123 15302 -124 15302 -124 15302 -124 15302 -124 15302 -125 15301 -125 15301 -125 15301 -125 15301 -125 15301 -126 15301 -126 15301 -126 15301 -126 15301 -127 15301 -127 15301 -127 15301 -127 15301 -127 15301 -128 15301 -128 15301 -128 15301 -128 15301 -129 15301 -129 15301 -129 15301 -129 15301 -129 15301 -130 15301 -130 15301 -130 15301 -130 15301 -131 15301 -131 15301 -131 15301 -131 15300 -132 15300 -132 15300 -132 15300 -132 15300 -132 15300 -133 15300 -133 15300 -133 15300 -133 15300 -134 15300 -134 15300 -134 15300 -134 15300 -134 15300 -135 15300 -135 15300 -135 15300 -135 15300 -136 15300 -136 15300 -136 15300 -136 15300 -136 15300 -137 15300 -137 15300 -137 15300 -137 15300 -138 15300 -138 15300 -138 15300 -138 15299 -138 15299 -139 15299 -139 15299 -139 15299 -139 15299 -140 15299 -140 15299 -140 15299 -140 15299 -140 15299 -141 15299 -141 15299 -141 15298 -141 15298 -142 15298 -142 15298 -142 15298 -142 15298 -142 15298 -143 15298 -143 15298 -143 15298 -143 15298 -144 15297 -144 15297 -144 15297 -144 15297 -144 15297 -145 15297 -145 15297 -145 15297 -145 15297 -146 15297 -146 15297 -146 15296 -146 15296 -147 15296 -147 15296 -147 15296 -147 15296 -147 15296 -148 15296 -148 15296 -148 15296 -148 15296 -149 15295 -149 15295 -149 15295 -149 15295 -149 15295 -150 15295 -150 15295 -150 15295 -150 15295 -150 15295 -151 15295 -151 15295 -151 15294 -151 15294 -151 15294 -151 15294 -152 15294 -152 15294 -152 15294 -152 15294 -152 15294 -152 15294 -153 15294 -153 15293 -153 15293 -153 15293 -153 15293 -154 15293 -154 15293 -154 15293 -154 15293 -154 15293 -154 15293 -155 15293 -155 15292 -155 15292 -155 15292 -155 15292 -155 15292 -156 15292 -156 15292 -156 15292 -156 15292 -156 15292 -157 15292 -157 15291 -157 15291 -157 15291 -157 15291 -157 15291 -158 15291 -158 15291 -158 15291 -158 15291 -158 15291 -158 15291 -159 15290 -159 15290 -159 15290 -159 15290 -159 15290 -159 15290 -160 15290 -160 15290 -160 15290 -160 15290 -160 15290 -161 15290 -161 15289 -161 15289 -161 15289 -161 15289 -161 15289 -162 15289 -162 15289 -162 15289 -162 15289 -162 15289 -162 15289 -163 15288 -163 15288 -163 15288 -163 15288 -163 15288 -164 15288 -164 15288 -164 15288 -164 15288 -164 15288 -164 15288 -165 15287 -165 15287 -165 15287 -165 15287 -165 15287 -165 15287 -166 15287 -166 15287 -166 15287 -166 15287 -166 15287 -167 15286 -167 15286 -167 15286 -167 15286 -167 15286 -167 15286 -168 15286 -168 15286 -168 15286 -168 15286 -168 15286 -168 15285 -169 15285 -169 15285 -169 15285 -169 15285 -169 15285 -170 15285 -170 15285 -170 15285 -170 15285 -170 15285 -170 15285 -171 15284 -171 15284 -171 15284 -171 15284 -171 15284 -171 15284 -172 15284 -172 15284 -172 15284 -172 15284 -172 15284 -173 15283 -173 15283 -173 15283 -173 15283 -173 15283 -173 15283 -174 15283 -174 15283 -174 15283 -174 15283 -174 15283 -174 15283 -175 15282 -175 15282 -175 15282 -175 15282 -175 15282 -176 15282 -176 15282 -176 15282 -176 15282 -176 15282 -176 15282 -177 15282 -177 15281 -177 15281 -177 15281 -177 15281 -177 15281 -178 15281 -178 15281 -178 15281 -178 15281 -178 15281 -179 15281 -179 15281 -179 15280 -179 15280 -179 15280 -179 15280 -180 15280 -180 15280 -180 15280 -180 15280 -180 15280 -180 15280 -181 15280 -181 15279 -181 15279 -181 15279 -181 15279 -182 15279 -182 15279 -182 15279 -182 15279 -182 15279 -182 15279 -183 15279 -183 15279 -183 15278 -183 15278 -183 15278 -183 15278 -184 15278 -184 15278 -184 15278 -184 15278 -184 15278 -184 15278 -185 15278 -185 15278 -185 15277 -185 15277 -185 15277 -186 15277 -186 15277 -186 15277 -186 15277 -186 15277 -186 15277 -187 15277 -187 15277 -187 15276 -187 15276 -187 15276 -187 15276 -188 15276 -188 15276 -188 15276 -188 15276 -188 15276 -189 15276 -189 15276 -189 15276 -189 15275 -189 15275 -189 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -190 15275 -191 15275 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -191 15274 -192 15274 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15273 -192 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15272 -193 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15271 -194 15270 -194 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15270 -195 15269 -195 15269 -195 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15269 -196 15268 -196 15268 -196 15268 -196 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15268 -197 15267 -197 15267 -197 15267 -197 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15267 -198 15266 -198 15266 -198 15266 -198 15266 -198 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15266 -199 15265 -199 15265 -199 15265 -199 15265 -199 15265 -199 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15265 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -200 15264 -201 15264 -201 15264 -201 15264 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15263 -201 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15262 -202 15261 -202 15261 -202 15261 -202 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15261 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -203 15260 -204 15260 -204 15260 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15259 -204 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15258 -205 15257 -205 15257 -205 15257 -205 15257 -205 15257 -206 15257 -206 15257 -206 15257 -206 15257 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -206 15256 -207 15256 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15255 -207 15254 -207 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15254 -208 15253 -208 15253 -208 15253 -208 15253 -208 15253 -209 15253 -209 15253 -209 15253 -209 15253 -209 15252 -209 15252 -209 15252 -209 15252 -209 15252 -209 15252 -210 15252 -210 15252 -210 15252 -210 15251 -210 15251 -210 15251 -210 15251 -210 15251 -210 15251 -211 15251 -211 15251 -211 15251 -211 15250 -211 15250 -211 15250 -211 15250 -211 15250 -211 15250 -212 15250 -212 15250 -212 15250 -212 15250 -212 15249 -212 15249 -212 15249 -212 15249 -212 15249 -213 15249 -213 15249 -213 15249 -213 15249 -213 15248 -213 15248 -213 15248 -213 15248 -213 15248 -214 15248 -214 15248 -214 15248 -214 15248 -214 15247 -214 15247 -214 15247 -214 15247 -214 15247 -215 15247 -215 15247 -215 15247 -215 15247 -215 15246 -215 15246 -215 15246 -215 15246 -215 15246 -216 15246 -216 15246 -216 15246 -216 15246 -216 15245 -216 15245 -216 15245 -216 15245 -216 15245 -217 15245 -217 15245 -217 15245 -217 15245 -217 15245 -217 15244 -217 15244 -217 15244 -217 15244 -217 15244 -218 15244 -218 15244 -218 15244 -218 15244 -218 15243 -218 15243 -218 15243 -218 15243 -218 15243 -219 15243 -219 15243 -219 15243 -219 15243 -219 15242 -219 15242 -219 15242 -219 15242 -219 15242 -220 15242 -220 15242 -220 15242 -220 15242 -220 15241 -220 15241 -220 15241 -220 15241 -220 15241 -221 15241 -221 15241 -221 15241 -221 15241 -221 15240 -221 15240 -221 15240 -221 15240 -221 15240 -222 15240 -222 15240 -222 15240 -222 15240 -222 15240 -222 15239 -222 15239 -222 15239 -222 15239 -223 15239 -223 15239 -223 15239 -223 15239 -223 15239 -223 15238 -223 15238 -223 15238 -223 15238 -224 15238 -224 15238 -224 15238 -224 15238 -224 15238 -224 15237 -224 15237 -224 15237 -224 15237 -225 15237 -225 15237 -225 15237 -225 15237 -225 15237 -225 15236 -225 15236 -225 15236 -225 15236 -225 15236 -226 15236 -226 15236 -226 15236 -226 15236 -226 15235 -226 15235 -226 15235 -226 15235 -226 15235 -227 15235 -227 15235 -227 15235 -227 15235 -227 15235 -227 15234 -227 15234 -227 15234 -227 15234 -228 15234 -228 15234 -228 15234 -228 15234 -228 15234 -228 15233 -228 15233 -228 15233 -228 15233 -228 15233 -229 15233 -229 15233 -229 15233 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -229 15232 -230 15232 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15231 -230 15230 -230 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15230 -231 15229 -231 15229 -231 15229 -231 15229 -232 15229 -232 15229 -232 15229 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -232 15228 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15227 -233 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15226 -234 15225 -234 15225 -234 15225 -235 15225 -235 15225 -235 15225 -235 15225 -235 15224 -235 15224 -235 15224 -235 15224 -235 15224 -235 15224 -236 15224 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15223 -236 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15222 -237 15221 -237 15221 -237 15221 -237 15221 -238 15221 -238 15221 -238 15221 -238 15220 -238 15220 -238 15220 -238 15220 -238 15220 -238 15220 -239 15220 -239 15220 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15219 -239 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15218 -240 15217 -240 15217 -240 15217 -241 15217 -241 15217 -241 15217 -241 15217 -241 15216 -241 15216 -241 15216 -241 15216 -241 15216 -241 15216 -242 15216 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15215 -242 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15214 -243 15213 -243 15213 -244 15213 -244 15213 -244 15213 -244 15213 -244 15213 -244 15212 -244 15212 -244 15212 -244 15212 -244 15212 -245 15212 -245 15212 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -245 15211 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15210 -246 15209 -246 15209 -246 15209 -247 15209 -247 15209 -247 15209 -247 15209 -247 15208 -247 15208 -247 15208 -247 15208 -247 15208 -248 15208 -248 15208 -248 15208 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -248 15207 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15206 -249 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15205 -250 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15204 -251 15203 -251 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15203 -252 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15202 -253 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -254 15201 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -255 15200 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -256 15199 -257 15199 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -257 15198 -258 15198 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -258 15197 -259 15197 -259 15197 -259 15196 -259 15196 -259 15196 -259 15196 -259 15196 -260 15196 -260 15196 -260 15196 -260 15195 -260 15195 -260 15195 -260 15195 -260 15195 -261 15195 -261 15195 -261 15195 -261 15195 -261 15194 -261 15194 -261 15194 -261 15194 -262 15194 -262 15194 -262 15194 -262 15194 -262 15193 -262 15193 -262 15193 -262 15193 -263 15193 -263 15193 -263 15193 -263 15193 -263 15193 -263 15192 -263 15192 -263 15192 -264 15192 -264 15192 -264 15192 -264 15192 -264 15192 -264 15191 -264 15191 -264 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15191 -265 15190 -265 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15190 -266 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -267 15189 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -268 15188 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -269 15187 -270 15187 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -270 15186 -271 15186 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -271 15185 -272 15185 -272 15185 -272 15184 -272 15184 -272 15184 -272 15184 -272 15184 -272 15184 -273 15184 -273 15184 -273 15183 -273 15183 -273 15183 -273 15183 -273 15183 -274 15183 -274 15183 -274 15183 -274 15183 -274 15182 -274 15182 -274 15182 -274 15182 -275 15182 -275 15182 -275 15182 -275 15182 -275 15181 -275 15181 -275 15181 -275 15181 -276 15181 -276 15181 -276 15181 -276 15181 -276 15181 -276 15180 -276 15180 -276 15180 -277 15180 -277 15180 -277 15180 -277 15180 -277 15180 -277 15179 -277 15179 -277 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15179 -278 15178 -278 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15178 -279 15177 -279 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15177 -280 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -281 15176 -282 15175 -282 15175 -282 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15175 -283 15174 -283 15174 -283 15174 -283 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15174 -284 15173 -284 15173 -284 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15173 -285 15172 -285 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15172 -286 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15171 -287 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15170 -288 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -289 15169 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -290 15168 -291 15168 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -291 15167 -292 15167 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -292 15166 -293 15166 -293 15166 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -293 15165 -294 15165 -294 15165 -294 15165 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -294 15164 -295 15164 -295 15164 -295 15164 -295 15164 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -295 15163 -296 15163 -296 15163 -296 15163 -296 15163 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -296 15162 -297 15162 -297 15162 -297 15162 -297 15162 -297 15162 -297 15161 -297 15161 -297 15161 -297 15161 -297 15161 -297 15161 -298 15161 -298 15161 -298 15161 -298 15161 -298 15161 -298 15160 -298 15160 -298 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15160 -299 15159 -299 15159 -299 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15159 -300 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15158 -301 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -302 15157 -303 15157 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -303 15156 -304 15156 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -304 15155 -305 15155 -305 15155 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -305 15154 -306 15154 -306 15154 -306 15154 -306 15153 -306 15153 -306 15153 -306 15153 -306 15153 -307 15153 -307 15153 -307 15153 -307 15153 -307 15152 -307 15152 -307 15152 -307 15152 -307 15152 -308 15152 -308 15152 -308 15152 -308 15152 -308 15152 -308 15151 -308 15151 -308 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15151 -309 15150 -309 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15150 -310 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -311 15149 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -312 15148 -313 15148 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -313 15147 -314 15147 -314 15147 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -314 15146 -315 15146 -315 15146 -315 15145 -315 15145 -315 15145 -315 15145 -315 15145 -315 15145 -316 15145 -316 15145 -316 15145 -316 15145 -316 15144 -316 15144 -316 15144 -316 15144 -316 15144 -317 15144 -317 15144 -317 15144 -317 15144 -317 15143 -317 15143 -317 15143 -317 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15143 -318 15142 -318 15142 -318 15142 -319 15142 -319 15142 -319 15142 -319 15142 -319 15142 -319 15141 -319 15141 -319 15141 -320 15141 -320 15141 -320 15141 -320 15141 -320 15141 -320 15140 -320 15140 -320 15140 -320 15140 -321 15140 -321 15140 -321 15140 -321 15140 -321 15139 -321 15139 -321 15139 -321 15139 -322 15139 -322 15139 -322 15139 -322 15139 -322 15138 -322 15138 -322 15138 -322 15138 -322 15138 -323 15138 -323 15138 -323 15137 -323 15137 -323 15137 -323 15137 -323 15137 -323 15137 -324 15137 -324 15137 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -324 15136 -325 15136 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -325 15135 -326 15135 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -326 15134 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -327 15133 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15132 -328 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15131 -329 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15130 -330 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15129 -331 15128 -331 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15128 -332 15127 -332 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15127 -333 15126 -333 15126 -333 15126 -333 15126 -334 15126 -334 15126 -334 15126 -334 15126 -334 15125 -334 15125 -334 15125 -334 15125 -335 15125 -335 15125 -335 15125 -335 15125 -335 15124 -335 15124 -335 15124 -335 15124 -335 15124 -336 15124 -336 15124 -336 15124 -336 15123 -336 15123 -336 15123 -336 15123 -336 15123 -337 15123 -337 15123 -337 15123 -337 15122 -337 15122 -337 15122 -337 15122 -337 15122 -337 15122 -338 15122 -338 15122 -338 15121 -338 15121 -338 15121 -338 15121 -338 15121 -338 15121 -339 15121 -339 15121 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -339 15120 -340 15120 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -340 15119 -341 15119 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -341 15118 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -342 15117 -343 15117 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -343 15116 -344 15116 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -344 15115 -345 15115 -345 15115 -345 15114 -345 15114 -345 15114 -345 15114 -345 15114 -345 15114 -346 15114 -346 15114 -346 15113 -346 15113 -346 15113 -346 15113 -346 15113 -346 15113 -347 15113 -347 15113 -347 15112 -347 15112 -347 15112 -347 15112 -347 15112 -348 15112 -348 15112 -348 15112 -348 15111 -348 15111 -348 15111 -348 15111 -348 15111 -349 15111 -349 15111 -349 15111 -349 15110 -349 15110 -349 15110 -349 15110 -350 15110 -350 15110 -350 15110 -350 15110 -350 15109 -350 15109 -350 15109 -350 15109 -351 15109 -351 15109 -351 15109 -351 15109 -351 15108 -351 15108 -351 15108 -352 15108 -352 15108 -352 15108 -352 15108 -352 15108 -352 15107 -352 15107 -352 15107 -353 15107 -353 15107 -353 15107 -353 15107 -353 15107 -353 15106 -353 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15106 -354 15105 -354 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15105 -355 15104 -355 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15104 -356 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15103 -357 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -358 15102 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -359 15101 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -360 15100 -361 15100 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -361 15099 -362 15099 -362 15098 -362 15098 -362 15098 -362 15098 -362 15098 -362 15098 -363 15098 -363 15098 -363 15097 -363 15097 -363 15097 -363 15097 -363 15097 -363 15097 -364 15097 -364 15097 -364 15096 -364 15096 -364 15096 -364 15096 -364 15096 -365 15096 -365 15096 -365 15096 -365 15095 -365 15095 -365 15095 -365 15095 -365 15095 -366 15095 -366 15095 -366 15095 -366 15094 -366 15094 -366 15094 -366 15094 -366 15094 -367 15094 -367 15094 -367 15094 -367 15093 -367 15093 -367 15093 -367 15093 -368 15093 -368 15093 -368 15093 -368 15093 -368 15092 -368 15092 -368 15092 -368 15092 -369 15092 -369 15092 -369 15092 -369 15092 -369 15091 -369 15091 -369 15091 -370 15091 -370 15091 -370 15091 -370 15091 -370 15091 -370 15090 -370 15090 -370 15090 -371 15090 -371 15090 -371 15090 -371 15090 -371 15090 -371 15089 -371 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15089 -372 15088 -372 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15088 -373 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15087 -374 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15086 -375 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -376 15085 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -377 15084 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -378 15083 -379 15083 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -379 15082 -380 15082 -380 15081 -380 15081 -380 15081 -380 15081 -380 15081 -380 15081 -381 15081 -381 15081 -381 15080 -381 15080 -381 15080 -381 15080 -381 15080 -382 15080 -382 15080 -382 15080 -382 15079 -382 15079 -382 15079 -383 15079 -383 15079 -383 15079 -383 15079 -383 15079 -383 15078 -383 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15078 -384 15077 -385 15077 -385 15077 -385 15077 -385 15077 -385 15077 -385 15077 -386 15077 -386 15076 -386 15076 -386 15076 -386 15076 -386 15076 -386 15076 -387 15076 -387 15075 -387 15075 -387 15075 -387 15075 -387 15075 -388 15075 -388 15075 -388 15074 -388 15074 -388 15074 -388 15074 -388 15074 -389 15074 -389 15074 -389 15073 -389 15073 -389 15073 -389 15073 -389 15073 -390 15073 -390 15073 -390 15072 -390 15072 -390 15072 -390 15072 -391 15072 -391 15072 -391 15071 -391 15071 -391 15071 -391 15071 -391 15071 -392 15071 -392 15071 -392 15070 -392 15070 -392 15070 -392 15070 -393 15070 -393 15070 -393 15070 -393 15069 -393 15069 -393 15069 -393 15069 -394 15069 -394 15069 -394 15069 -394 15068 -394 15068 -394 15068 -394 15068 -395 15068 -395 15068 -395 15068 -395 15067 -395 15067 -395 15067 -396 15067 -396 15067 -396 15067 -396 15066 -396 15066 -396 15066 -396 15066 -397 15066 -397 15066 -397 15066 -397 15065 -397 15065 -397 15065 -398 15065 -398 15065 -398 15065 -398 15065 -398 15064 -398 15064 -398 15064 -399 15064 -399 15064 -399 15064 -399 15064 -399 15063 -399 15063 -399 15063 -400 15063 -400 15063 -400 15063 -400 15062 -400 15062 -400 15062 -401 15062 -401 15062 -401 15062 -401 15062 -401 15061 -401 15061 -401 15061 -402 15061 -402 15061 -402 15061 -402 15061 -402 15060 -402 15060 -403 15060 -403 15060 -403 15060 -403 15060 -403 15060 -403 15059 -403 15059 -404 15059 -404 15059 -404 15059 -404 15059 -404 15059 -404 15058 -404 15058 -405 15058 -405 15058 -405 15058 -405 15058 -405 15057 -405 15057 -406 15057 -406 15057 -406 15057 -406 15057 -406 15057 -406 15056 -406 15056 -407 15056 -407 15056 -407 15056 -407 15056 -407 15056 -407 15055 -407 15055 -408 15055 -408 15055 -408 15055 -408 15055 -408 15055 -408 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15054 -409 15053 -409 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15053 -410 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15052 -411 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15051 -412 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -413 15050 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -414 15049 -414 15048 -415 15048 -415 15048 -415 15048 -415 15048 -415 15048 -415 15048 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -416 15047 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -417 15046 -418 15045 -418 15045 -418 15045 -418 15045 -418 15045 -418 15045 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -419 15044 -420 15043 -420 15043 -420 15043 -420 15043 -420 15043 -420 15043 -421 15043 -421 15042 -421 15042 -421 15042 -421 15042 -421 15042 -421 15042 -422 15042 -422 15041 -422 15041 -422 15041 -422 15041 -422 15041 -422 15041 -423 15040 -423 15040 -423 15040 -423 15040 -423 15040 -423 15040 -424 15040 -424 15039 -424 15039 -424 15039 -424 15039 -424 15039 -424 15039 -425 15039 -425 15038 -425 15038 -425 15038 -425 15038 -425 15038 -426 15038 -426 15037 -426 15037 -426 15037 -426 15037 -426 15037 -426 15037 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -427 15036 -428 15035 -428 15035 -428 15035 -428 15035 -428 15035 -428 15035 -428 15034 -429 15034 -429 15034 -429 15034 -429 15034 -429 15034 -429 15033 -429 15033 -430 15033 -430 15033 -430 15033 -430 15033 -430 15033 -430 15032 -430 15032 -430 15032 -431 15032 -431 15032 -431 15032 -431 15031 -431 15031 -431 15031 -431 15031 -431 15031 -432 15031 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -432 15030 -433 15029 -433 15029 -433 15029 -433 15029 -433 15029 -433 15029 -433 15028 -434 15028 -434 15028 -434 15028 -434 15028 -434 15028 -434 15027 -434 15027 -434 15027 -435 15027 -435 15027 -435 15027 -435 15027 -435 15026 -435 15026 -435 15026 -435 15026 -436 15026 -436 15026 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -436 15025 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15024 -437 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15023 -438 15022 -438 15022 -439 15022 -439 15022 -439 15022 -439 15022 -439 15021 -439 15021 -439 15021 -439 15021 -440 15021 -440 15021 -440 15021 -440 15020 -440 15020 -440 15020 -440 15020 -440 15020 -441 15020 -441 15019 -441 15019 -441 15019 -441 15019 -441 15019 -441 15019 -441 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15018 -442 15017 -443 15017 -443 15017 -443 15017 -443 15017 -443 15017 -443 15016 -443 15016 -443 15016 -444 15016 -444 15016 -444 15016 -444 15015 -444 15015 -444 15015 -444 15015 -444 15015 -445 15015 -445 15015 -445 15014 -445 15014 -445 15014 -445 15014 -445 15014 -445 15014 -446 15013 -446 15013 -446 15013 -446 15013 -446 15013 -446 15013 -446 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15012 -447 15011 -447 15011 -448 15011 -448 15011 -448 15011 -448 15011 -448 15010 -448 15010 -448 15010 -448 15010 -449 15010 -449 15010 -449 15009 -449 15009 -449 15009 -449 15009 -449 15009 -449 15009 -450 15009 -450 15008 -450 15008 -450 15008 -450 15008 -450 15008 -450 15008 -451 15007 -451 15007 -451 15007 -451 15007 -451 15007 -451 15007 -451 15006 -451 15006 -452 15006 -452 15006 -452 15006 -453 15003 -454 15003 -455 15003 -455 15002 -455 15002 -455 15002 -455 15002 -455 15002 -455 15002 -456 15001 -456 15001 -456 15001 -456 15001 -456 15001 -456 15000 -457 15000 -457 15000 -457 15000 -457 15000 -457 14999 -457 14999 -457 14999 -458 14999 -458 14999 -458 14999 -458 14998 -458 14998 -458 14998 -459 14998 -459 14998 -459 14997 -459 14997 -459 14997 -459 14997 -459 14997 -460 14997 -460 14996 -460 14996 -460 14996 -460 14996 -460 14996 -461 14995 -461 14995 -461 14995 -461 14995 -461 14995 -461 14995 -461 14994 -462 14994 -462 14994 -462 14994 -462 14994 -462 14993 -462 14993 -463 14993 -463 14993 -463 14993 -463 14992 -463 14992 -463 14992 -463 14992 -464 14992 -464 14992 -464 14991 -464 14991 -464 14991 -464 14991 -464 14991 -465 14990 -465 14990 -465 14990 -465 14990 -465 14990 -465 14990 -466 14989 -466 14989 -466 14989 -466 14989 -466 14989 -466 14988 -466 14988 -467 14988 -467 14988 -467 14988 -467 14988 -467 14987 -468 14987 -468 14987 -468 14987 -468 14987 -468 14986 -469 14986 -469 14986 -469 14986 -469 14986 -469 14985 -470 14985 -470 14985 -470 14985 -470 14985 -471 14985 -471 14984 -471 14984 -471 14984 -471 14984 -472 14984 -472 14983 -472 14983 -472 14983 -472 14983 -473 14983 -473 14983 -473 14982 -473 14982 -474 14982 -474 14982 -474 14982 -474 14981 -474 14981 -475 14981 -475 14981 -475 14981 -475 14980 -475 14980 -476 14980 -476 14980 -476 14980 -476 14980 -477 14979 -477 14979 -477 14979 -477 14979 -477 14979 -478 14978 -478 14978 -478 14978 -478 14978 -478 14978 -479 14978 -479 14977 -479 14977 -479 14977 -479 14977 -480 14977 -480 14976 -480 14976 -480 14976 -481 14976 -481 14976 -481 14976 -481 14975 -481 14975 -482 14975 -482 14975 -482 14975 -482 14974 -482 14974 -483 14974 -483 14974 -483 14974 -483 14973 -484 14973 -484 14973 -484 14973 -484 14973 -484 14973 -485 14972 -485 14972 -485 14972 -485 14972 -485 14972 -486 14971 -486 14971 -486 14971 -486 14971 -487 14971 -487 14971 -487 14970 -487 14970 -487 14970 -488 14970 -488 14970 -488 14969 -488 14969 -488 14969 -489 14969 -489 14969 -489 14968 -489 14968 -490 14968 -490 14968 -490 14968 -490 14968 -490 14967 -491 14967 -491 14967 -491 14967 -491 14967 -491 14966 -492 14966 -492 14966 -492 14966 -492 14966 -492 14966 -493 14965 -493 14965 -493 14965 -493 14965 -494 14965 -494 14964 -494 14964 -494 14964 -494 14964 -495 14964 -495 14964 -495 14963 -495 14963 -495 14963 -496 14963 -496 14962 -496 14962 -496 14962 -497 14961 -497 14961 -497 14961 -497 14961 -497 14960 -498 14960 -498 14960 -498 14959 -498 14959 -498 14959 -499 14959 -499 14958 -499 14958 -499 14958 -500 14957 -500 14957 -500 14957 -500 14956 -500 14956 -501 14956 -501 14956 -501 14955 -501 14955 -501 14955 -502 14954 -502 14954 -502 14954 -502 14953 -503 14953 -503 14953 -504 14953 -504 14952 -504 14952 -505 14952 -505 14951 -505 14951 -506 14951 -506 14951 -506 14950 -507 14950 -507 14950 -507 14949 -508 14949 -508 14949 -508 14948 -509 14948 -509 14948 -509 14948 -510 14947 -510 14947 -510 14947 -511 14946 -511 14946 -511 14946 -512 14945 -512 14945 -513 14945 -513 14945 -513 14944 -514 14944 -514 14944 -514 14943 -515 14943 -515 14943 -515 14943 -516 14942 -516 14942 -516 14942 -517 14941 -517 14941 -517 14941 -518 14940 -518 14940 -518 14940 -519 14940 -519 14939 -519 14939 -520 14939 -520 14938 -520 14938 -521 14938 -521 14938 -522 14937 -522 14937 -522 14937 -523 14936 -523 14936 -523 14936 -524 14935 -524 14935 -524 14935 -525 14935 -525 14934 -525 14934 -526 14934 -526 14933 -526 14933 -527 14933 -527 14932 -527 14932 -528 14932 -528 14932 -528 14931 -529 14931 -529 14931 -529 14930 -530 14930 -530 14930 -531 14930 -531 14929 -531 14929 -532 14929 -532 14928 -532 14928 -533 14928 -533 14927 -533 14927 -534 14927 -534 14927 -534 14926 -535 14926 -535 14926 -535 14925 -536 14925 -536 14925 -536 14924 -537 14924 -537 14924 -537 14924 -538 14923 -538 14923 -538 14923 -539 14922 -539 14922 -540 14922 -540 14922 -540 14921 -541 14921 -541 14921 -541 14920 -542 14920 -542 14920 -542 14919 -543 14919 -543 14919 -543 14919 -544 14918 -544 14918 -544 14918 -545 14917 -545 14917 -545 14917 -546 14917 -546 14916 -546 14916 -547 14916 -547 14915 -547 14915 -548 14915 -548 14914 -549 14914 -549 14914 -549 14914 -550 14913 -550 14913 -550 14913 -551 14912 -551 14912 -551 14912 -552 14911 -552 14911 -552 14911 -553 14911 -553 14910 -553 14910 -554 14910 -554 14909 -554 14909 -555 14909 -555 14909 -555 14908 -556 14908 -556 14908 -556 14907 -557 14907 -557 14907 -558 14906 -558 14906 -558 14906 -559 14906 -559 14905 -559 14905 -560 14905 -560 14904 -560 14904 -561 14904 -561 14903 -561 14903 -562 14903 -562 14903 -562 14902 -563 14902 -563 14902 -563 14901 -564 14901 -564 14901 -564 14901 -565 14900 -565 14900 -565 14900 -566 14899 -566 14899 -567 14899 -567 14898 -567 14898 -568 14898 -568 14898 -568 14897 -569 14897 -569 14897 -569 14896 -570 14896 -570 14896 -570 14896 -571 14895 -571 14895 -571 14895 -572 14894 -572 14894 -572 14894 -573 14893 -573 14893 -573 14893 -574 14893 -574 14892 -574 14892 -575 14892 -575 14891 -576 14891 -576 14891 -576 14890 -577 14890 -577 14890 -577 14890 -578 14889 -578 14889 -578 14889 -579 14888 -579 14888 -579 14888 -580 14888 -580 14887 -580 14887 -581 14887 -581 14886 -581 14886 -582 14886 -582 14885 -582 14885 -583 14885 -583 14885 -583 14884 -584 14884 -584 14884 -585 14883 -585 14883 -585 14883 -586 14883 -586 14882 -586 14882 -587 14882 -587 14881 -587 14881 -588 14881 -588 14880 -588 14880 -589 14880 -589 14880 -589 14879 -590 14879 -590 14879 -590 14878 -591 14878 -591 14878 -591 14877 -592 14877 -592 14877 -592 14876 -593 14876 -593 14876 -594 14875 -594 14875 -594 14875 -595 14874 -595 14874 -595 14874 -596 14873 -596 14873 -596 14873 -597 14872 -597 14872 -597 14871 -598 14871 -598 14871 -598 14870 -599 14870 -599 14870 -599 14869 -600 14869 -600 14869 -600 14868 -601 14868 -601 14868 -601 14867 -602 14867 -602 14866 -603 14866 -603 14866 -603 14865 -604 14865 -604 14865 -604 14864 -605 14864 -605 14864 -606 14863 -606 14863 -606 14863 -607 14862 -607 14862 -607 14861 -608 14861 -608 14861 -609 14860 -609 14860 -609 14860 -610 14859 -610 14859 -610 14859 -611 14858 -611 14858 -611 14858 -612 14857 -612 14857 -613 14856 -613 14856 -613 14856 -614 14855 -614 14855 -614 14855 -615 14854 -615 14854 -616 14854 -616 14853 -616 14853 -617 14853 -617 14852 -617 14852 -618 14851 -618 14851 -619 14851 -619 14850 -619 14850 -620 14850 -620 14849 -620 14849 -621 14849 -621 14848 -622 14848 -622 14848 -622 14847 -623 14847 -623 14846 -623 14846 -624 14846 -624 14845 -625 14845 -625 14845 -625 14844 -626 14844 -626 14844 -626 14843 -627 14843 -627 14843 -627 14842 -628 14842 -628 14841 -629 14841 -629 14841 -629 14840 -630 14840 -630 14840 -630 14839 -631 14839 -631 14839 -632 14838 -632 14838 -632 14838 -633 14837 -633 14837 -633 14836 -634 14836 -634 14836 -635 14835 -635 14835 -635 14835 -636 14834 -636 14834 -636 14834 -637 14833 -637 14833 -638 14833 -638 14832 -638 14832 -639 14831 -639 14831 -639 14831 -640 14830 -640 14830 -641 14830 -641 14829 -641 14829 -642 14829 -642 14828 -642 14828 -643 14827 -643 14827 -643 14827 -644 14826 -644 14826 -645 14826 -645 14825 -645 14825 -646 14825 -646 14824 -646 14824 -647 14824 -647 14823 -648 14823 -648 14822 -648 14822 -649 14822 -649 14821 -649 14821 -650 14821 -650 14820 -651 14820 -651 14820 -651 14819 -652 14819 -652 14819 -652 14818 -653 14818 -653 14817 -654 14817 -654 14817 -654 14816 -655 14816 -655 14816 -655 14815 -656 14815 -656 14815 -656 14814 -657 14814 -657 14814 -658 14813 -658 14813 -658 14812 -659 14812 -659 14812 -659 14811 -660 14811 -660 14811 -661 14810 -661 14810 -661 14810 -662 14809 -662 14809 -662 14809 -663 14808 -663 14808 -664 14807 -664 14807 -664 14807 -665 14806 -665 14806 -665 14806 -666 14805 -666 14805 -667 14805 -667 14804 -667 14804 -668 14804 -668 14803 -668 14803 -669 14802 -669 14802 -670 14802 -670 14801 -670 14801 -671 14801 -671 14800 -671 14800 -672 14800 -672 14799 -672 14799 -673 14799 -673 14798 -674 14798 -674 14797 -675 14797 -675 14797 -675 14796 -676 14796 -676 14795 -677 14794 -677 14794 -678 14793 -678 14793 -679 14792 -679 14792 -679 14791 -680 14791 -680 14790 -681 14789 -681 14789 -682 14788 -682 14788 -683 14787 -683 14787 -684 14786 -684 14786 -684 14785 -685 14784 -685 14784 -686 14783 -686 14783 -687 14782 -687 14782 -688 14781 -688 14781 -688 14780 -689 14779 -689 14779 -690 14778 -690 14778 -691 14777 -691 14777 -692 14776 -692 14776 -693 14775 -693 14774 -693 14774 -694 14773 -694 14773 -695 14772 -695 14772 -696 14771 -696 14771 -697 14770 -697 14769 -697 14769 -698 14768 -698 14768 -699 14767 -699 14767 -700 14766 -700 14766 -701 14765 -701 14764 -702 14764 -702 14763 -702 14763 -703 14762 -703 14762 -704 14761 -704 14761 -705 14760 -705 14759 -706 14759 -706 14758 -706 14758 -707 14757 -707 14757 -708 14756 -708 14756 -709 14755 -709 14754 -710 14754 -710 14753 -711 14753 -711 14752 -711 14752 -712 14751 -712 14751 -713 14750 -713 14749 -714 14749 -714 14748 -715 14748 -715 14747 -715 14747 -716 14746 -716 14746 -717 14745 -717 14744 -718 14744 -718 14743 -719 14743 -719 14742 -720 14742 -720 14741 -720 14741 -721 14740 -721 14739 -722 14739 -722 14738 -723 14738 -723 14737 -724 14737 -724 14736 -724 14736 -725 14735 -725 14734 -726 14734 -726 14733 -727 14733 -727 14732 -728 14732 -728 14731 -729 14731 -729 14730 -729 14729 -730 14729 -730 14728 -731 14728 -731 14727 -732 14727 -732 14726 -733 14726 -733 14725 -733 14724 -734 14724 -734 14723 -735 14723 -735 14722 -736 14722 -736 14721 -737 14721 -737 14720 -738 14719 -738 14719 -738 14718 -739 14718 -739 14717 -740 14717 -740 14716 -741 14716 -741 14715 -742 14714 -742 14714 -742 14713 -743 14713 -743 14712 -744 14712 -744 14711 -745 14711 -745 14710 -746 14709 -746 14709 -747 14708 -747 14708 -747 14707 -748 14707 -748 14706 -749 14706 -749 14705 -750 14704 -750 14704 -751 14703 -751 14703 -751 14702 -752 14702 -752 14701 -753 14701 -753 14700 -754 14699 -754 14699 -755 14698 -755 14698 -756 14697 -756 14697 -756 14696 -757 14696 -757 14695 -758 14694 -758 14694 -759 14693 -759 14693 -760 14692 -760 14692 -760 14691 -761 14691 -761 14690 -762 14689 -762 14689 -763 14688 -763 14688 -764 14687 -764 14687 -765 14686 -765 14686 -765 14685 -766 14684 -767 14684 -767 14683 -768 14683 -768 14682 -769 14682 -770 14681 -770 14681 -771 14680 -771 14679 -772 14679 -773 14678 -773 14678 -774 14677 -774 14677 -775 14676 -776 14676 -776 14675 -777 14674 -777 14674 -778 14673 -779 14673 -779 14672 -780 14672 -780 14671 -781 14671 -782 14670 -782 14669 -783 14669 -783 14668 -784 14668 -785 14667 -785 14667 -786 14666 -786 14666 -787 14665 -788 14664 -788 14664 -789 14663 -789 14663 -790 14662 -791 14662 -791 14661 -792 14661 -792 14660 -793 14659 -794 14659 -794 14658 -795 14658 -795 14657 -796 14657 -797 14656 -797 14656 -798 14655 -798 14654 -799 14654 -800 14653 -800 14653 -801 14652 -801 14652 -802 14651 -803 14651 -803 14650 -804 14649 -804 14649 -805 14648 -806 14648 -806 14647 -807 14647 -807 14646 -808 14646 -809 14645 -809 14644 -810 14644 -810 14643 -811 14643 -812 14642 -812 14642 -813 14641 -813 14641 -814 14640 -815 14639 -815 14639 -816 14638 -816 14638 -817 14637 -818 14637 -818 14636 -819 14636 -819 14635 -820 14634 -821 14634 -821 14633 -822 14633 -822 14632 -823 14632 -824 14631 -824 14631 -825 14630 -825 14629 -826 14629 -827 14628 -827 14628 -828 14627 -828 14627 -829 14626 -830 14626 -830 14625 -831 14624 -831 14624 -832 14623 -833 14623 -833 14622 -834 14622 -834 14621 -835 14621 -836 14620 -836 14619 -837 14619 -837 14618 -838 14618 -839 14617 -839 14617 -840 14616 -840 14615 -841 14615 -842 14614 -842 14614 -843 14613 -843 14612 -844 14612 -845 14611 -845 14611 -846 14610 -846 14609 -847 14609 -848 14608 -848 14608 -849 14607 -849 14606 -850 14606 -851 14605 -851 14605 -852 14604 -852 14603 -853 14603 -854 14602 -854 14602 -855 14601 -855 14600 -856 14600 -857 14599 -857 14599 -858 14598 -858 14597 -859 14597 -860 14596 -860 14596 -861 14595 -861 14594 -862 14594 -863 14593 -863 14593 -864 14592 -864 14591 -865 14591 -866 14590 -866 14589 -867 14589 -867 14588 -868 14588 -869 14587 -869 14586 -870 14586 -870 14585 -871 14585 -872 14584 -872 14583 -873 14583 -873 14582 -874 14582 -875 14581 -875 14580 -876 14580 -876 14579 -877 14579 -878 14578 -878 14577 -879 14577 -879 14576 -880 14576 -881 14575 -881 14574 -882 14574 -882 14573 -883 14573 -884 14572 -884 14571 -885 14571 -885 14570 -886 14570 -887 14569 -887 14568 -888 14568 -888 14567 -889 14567 -890 14566 -890 14565 -891 14565 -891 14564 -892 14564 -893 14563 -893 14562 -894 14562 -895 14561 -895 14561 -896 14560 -896 14559 -897 14559 -898 14558 -898 14557 -899 14557 -900 14556 -900 14556 -901 14555 -902 14554 -902 14554 -903 14553 -904 14553 -904 14552 -905 14551 -906 14551 -906 14550 -907 14550 -908 14549 -908 14548 -909 14548 -910 14547 -910 14547 -911 14546 -912 14545 -912 14545 -913 14544 -913 14544 -914 14543 -915 14542 -915 14542 -916 14541 -917 14541 -917 14540 -918 14539 -919 14539 -919 14538 -920 14538 -921 14537 -921 14536 -922 14536 -923 14535 -923 14535 -924 14534 -925 14533 -925 14533 -926 14532 -927 14532 -927 14531 -928 14530 -929 14530 -929 14529 -930 14528 -930 14528 -931 14527 -932 14527 -932 14526 -933 14525 -934 14525 -934 14524 -935 14524 -936 14523 -936 14522 -937 14522 -938 14521 -938 14521 -939 14520 -940 14519 -940 14519 -941 14518 -942 14518 -942 14517 -943 14516 -944 14516 -944 14515 -945 14515 -946 14514 -946 14513 -947 14513 -947 14512 -948 14512 -949 14511 -949 14510 -950 14510 -951 14509 -951 14509 -952 14508 -953 14507 -953 14507 -954 14506 -955 14506 -955 14505 -956 14504 -957 14504 -957 14503 -958 14503 -959 14502 -959 14501 -960 14501 -961 14500 -961 14500 -962 14499 -963 14498 -963 14498 -964 14497 -964 14496 -965 14496 -966 14495 -966 14495 -967 14494 -968 14493 -968 14493 -969 14492 -970 14492 -970 14491 -971 14490 -972 14490 -972 14489 -973 14489 -974 14488 -974 14487 -975 14487 -976 14486 -976 14486 -977 14485 -978 14484 -978 14484 -979 14483 -980 14483 -980 14482 -981 14481 -981 14481 -982 14480 -983 14480 -983 14479 -984 14478 -985 14478 -985 14477 -986 14477 -987 14476 -987 14475 -988 14475 -989 14474 -989 14474 -990 14473 -991 14472 -991 14472 -992 14471 -993 14471 -993 14470 -994 14469 -995 14469 -995 14468 -996 14467 -997 14467 -997 14466 -998 14466 -998 14465 -999 14464 -1000 14464 -1000 14463 -1001 14463 -1002 14462 -1002 14461 -1003 14461 -1004 14460 -1004 14460 -1005 14459 -1006 14459 -1006 14458 -1007 14458 -1008 14457 -1008 14457 -1009 14456 -1010 14456 -1010 14455 -1011 14455 -1012 14454 -1012 14454 -1013 14453 -1014 14453 -1014 14452 -1014 14453 -1014 14453 -1015 14452 -1015 14451 -1016 14451 -1017 14450 -1017 14450 -1018 14449 -1018 14448 -1019 14448 -1019 14447 -1020 14446 -1021 14446 -1021 14445 -1022 14444 -1022 14444 -1023 14443 -1023 14443 -1024 14442 -1025 14441 -1025 14441 -1026 14440 -1026 14439 -1027 14439 -1027 14438 -1028 14437 -1029 14437 -1029 14436 -1030 14436 -1030 14435 -1031 14434 -1031 14434 -1032 14433 -1033 14432 -1033 14432 -1034 14431 -1034 14430 -1035 14430 -1035 14429 -1036 14429 -1037 14428 -1037 14427 -1038 14427 -1038 14426 -1039 14425 -1039 14425 -1040 14424 -1041 14423 -1041 14423 -1042 14422 -1042 14422 -1043 14421 -1043 14420 -1044 14420 -1045 14419 -1045 14418 -1046 14418 -1046 14417 -1047 14416 -1047 14416 -1048 14415 -1049 14415 -1049 14414 -1050 14413 -1050 14413 -1051 14412 -1051 14411 -1052 14411 -1053 14410 -1053 14409 -1054 14409 -1054 14408 -1055 14408 -1055 14407 -1056 14406 -1056 14406 -1057 14405 -1058 14404 -1058 14404 -1059 14403 -1059 14402 -1060 14402 -1060 14401 -1061 14401 -1062 14400 -1062 14399 -1063 14399 -1063 14398 -1064 14397 -1064 14397 -1065 14396 -1066 14395 -1066 14395 -1067 14394 -1067 14394 -1068 14393 -1069 14392 -1069 14392 -1070 14391 -1071 14390 -1071 14390 -1072 14389 -1073 14388 -1073 14388 -1074 14387 -1075 14387 -1076 14386 -1076 14385 -1077 14385 -1078 14384 -1078 14383 -1079 14383 -1080 14382 -1080 14381 -1081 14381 -1082 14380 -1083 14380 -1083 14379 -1084 14378 -1085 14378 -1085 14377 -1086 14376 -1087 14376 -1087 14375 -1088 14374 -1089 14374 -1090 14373 -1090 14373 -1091 14372 -1092 14371 -1092 14371 -1093 14370 -1094 14369 -1094 14369 -1095 14368 -1096 14367 -1097 14367 -1097 14366 -1098 14366 -1099 14365 -1099 14364 -1100 14364 -1101 14363 -1101 14362 -1102 14362 -1103 14361 -1104 14360 -1104 14360 -1105 14359 -1106 14359 -1106 14358 -1107 14357 -1108 14356 -1108 14356 -1109 14355 -1110 14354 -1111 14353 -1111 14353 -1112 14352 -1113 14351 -1113 14351 -1114 14350 -1115 14349 -1115 14348 -1116 14348 -1117 14347 -1118 14346 -1118 14345 -1119 14345 -1120 14344 -1120 14343 -1121 14342 -1122 14342 -1122 14341 -1123 14340 -1124 14339 -1125 14339 -1125 14338 -1126 14337 -1127 14336 -1127 14336 -1128 14335 -1129 14334 -1129 14334 -1130 14333 -1131 14332 -1132 14331 -1132 14331 -1133 14330 -1134 14329 -1134 14328 -1135 14328 -1136 14327 -1136 14326 -1137 14325 -1138 14325 -1139 14324 -1139 14323 -1140 14322 -1141 14322 -1141 14321 -1142 14320 -1143 14319 -1143 14319 -1144 14318 -1145 14317 -1146 14316 -1146 14316 -1147 14315 -1148 14314 -1148 14314 -1149 14313 -1150 14312 -1150 14311 -1151 14311 -1152 14310 -1153 14309 -1154 14308 -1155 14308 -1156 14307 -1156 14306 -1157 14305 -1158 14305 -1159 14304 -1160 14303 -1161 14302 -1162 14302 -1162 14301 -1163 14300 -1164 14299 -1165 14299 -1166 14298 -1167 14297 -1168 14297 -1169 14296 -1169 14295 -1170 14294 -1171 14294 -1172 14293 -1173 14292 -1174 14291 -1175 14291 -1175 14290 -1176 14289 -1177 14288 -1178 14288 -1179 14287 -1180 14286 -1181 14285 -1182 14285 -1182 14284 -1183 14283 -1184 14282 -1185 14282 -1186 14281 -1187 14280 -1188 14280 -1189 14279 -1189 14278 -1190 14277 -1191 14277 -1192 14276 -1193 14275 -1194 14274 -1195 14274 -1195 14273 -1196 14272 -1197 14271 -1198 14271 -1199 14270 -1200 14269 -1201 14268 -1202 14268 -1202 14267 -1203 14266 -1204 14265 -1205 14265 -1206 14264 -1207 14263 -1208 14263 -1209 14262 -1209 14261 -1210 14260 -1211 14260 -1212 14259 -1213 14258 -1214 14257 -1215 14257 -1215 14256 -1216 14255 -1217 14254 -1218 14254 -1219 14253 -1220 14252 -1221 14251 -1222 14251 -1222 14250 -1223 14249 -1224 14248 -1225 14248 -1226 14247 -1227 14246 -1228 14246 -1228 14245 -1229 14244 -1230 14243 -1231 14242 -1232 14242 -1233 14241 -1234 14240 -1235 14239 -1235 14238 -1236 14237 -1237 14236 -1238 14235 -1239 14234 -1240 14234 -1241 14233 -1242 14232 -1242 14231 -1243 14230 -1244 14229 -1245 14228 -1246 14227 -1247 14226 -1248 14225 -1248 14225 -1249 14224 -1250 14223 -1251 14222 -1252 14221 -1253 14220 -1254 14219 -1255 14218 -1255 14217 -1256 14217 -1257 14216 -1258 14215 -1259 14214 -1260 14213 -1261 14212 -1261 14211 -1262 14210 -1263 14209 -1264 14208 -1265 14208 -1266 14207 -1267 14206 -1268 14205 -1268 14204 -1269 14203 -1270 14202 -1271 14201 -1272 14200 -1273 14200 -1274 14199 -1275 14198 -1275 14197 -1276 14196 -1277 14195 -1278 14194 -1279 14193 -1280 14192 -1281 14191 -1281 14191 -1282 14190 -1283 14189 -1284 14188 -1285 14187 -1286 14186 -1287 14185 -1288 14184 -1289 14183 -1290 14183 -1291 14182 -1292 14181 -1293 14180 -1294 14179 -1295 14178 -1296 14177 -1297 14176 -1298 14175 -1299 14174 -1300 14174 -1301 14173 -1302 14172 -1303 14171 -1304 14170 -1305 14169 -1306 14168 -1307 14168 -1308 14167 -1309 14166 -1310 14165 -1311 14164 -1312 14163 -1313 14162 -1314 14161 -1315 14160 -1316 14159 -1317 14158 -1318 14157 -1319 14156 -1320 14155 -1321 14154 -1322 14153 -1324 14152 -1325 14151 -1326 14150 -1327 14149 -1328 14148 -1329 14147 -1330 14146 -1331 14145 -1332 14144 -1333 14143 -1334 14142 -1335 14141 -1336 14140 -1337 14139 -1338 14138 -1339 14137 -1340 14136 -1341 14135 -1342 14134 -1343 14133 -1344 14132 -1345 14131 -1346 14130 -1347 14129 -1348 14128 -1349 14127 -1350 14126 -1351 14125 -1352 14124 -1353 14123 -1354 14121 -1355 14120 -1356 14119 -1357 14118 -1358 14117 -1359 14116 -1360 14114 -1361 14113 -1362 14112 -1363 14111 -1364 14110 -1365 14109 -1366 14107 -1367 14106 -1368 14105 -1369 14104 -1370 14103 -1371 14102 -1372 14100 -1373 14099 -1374 14098 -1375 14097 -1376 14096 -1378 14095 -1379 14093 -1380 14092 -1381 14091 -1382 14090 -1383 14089 -1384 14088 -1385 14086 -1386 14085 -1387 14084 -1388 14083 -1389 14082 -1390 14081 -1391 14079 -1392 14078 -1393 14077 -1394 14076 -1395 14075 -1396 14074 -1397 14073 -1398 14071 -1400 14070 -1401 14069 -1402 14068 -1403 14067 -1404 14066 -1405 14064 -1407 14063 -1408 14062 -1409 14061 -1410 14060 -1411 14059 -1412 14057 -1413 14056 -1415 14055 -1416 14054 -1417 14053 -1418 14052 -1419 14050 -1420 14049 -1421 14048 -1423 14047 -1424 14046 -1425 14045 -1426 14043 -1427 14042 -1428 14041 -1430 14040 -1431 14039 -1432 14038 -1433 14036 -1434 14035 -1435 14034 -1436 14033 -1438 14032 -1439 14031 -1440 14030 -1441 14028 -1442 14027 -1443 14026 -1444 14025 -1446 14024 -1447 14023 -1448 14021 -1449 14020 -1450 14019 -1451 14018 -1453 14017 -1454 14016 -1455 14014 -1456 14013 -1457 14012 -1458 14011 -1459 14010 -1461 14009 -1462 14007 -1463 14006 -1464 14005 -1465 14004 -1466 14003 -1467 14002 -1469 14000 -1470 13999 -1471 13998 -1472 13997 -1473 13996 -1474 13995 -1476 13993 -1477 13992 -1478 13991 -1479 13990 -1480 13989 -1481 13988 -1482 13986 -1484 13985 -1485 13984 -1486 13983 -1487 13982 -1488 13981 -1489 13980 -1491 13978 -1492 13977 -1493 13976 -1494 13975 -1495 13974 -1496 13973 -1497 13971 -1499 13970 -1500 13969 -1501 13968 -1502 13966 -1503 13965 -1504 13964 -1505 13962 -1507 13961 -1508 13960 -1509 13958 -1510 13957 -1511 13955 -1512 13954 -1514 13953 -1515 13951 -1516 13950 -1518 13949 -1519 13947 -1520 13946 -1522 13944 -1523 13943 -1525 13942 -1526 13940 -1527 13939 -1529 13938 -1530 13936 -1531 13935 -1533 13934 -1534 13932 -1536 13931 -1537 13929 -1538 13928 -1540 13927 -1541 13925 -1543 13924 -1544 13923 -1545 13921 -1547 13920 -1548 13919 -1550 13917 -1551 13916 -1552 13914 -1554 13913 -1555 13912 -1557 13910 -1558 13909 -1559 13908 -1561 13906 -1562 13905 -1563 13903 -1565 13902 -1566 13901 -1568 13899 -1569 13898 -1570 13897 -1572 13895 -1573 13894 -1575 13893 -1576 13891 -1577 13890 -1579 13888 -1580 13887 -1582 13886 -1583 13884 -1584 13883 -1586 13882 -1587 13880 -1589 13879 -1590 13877 -1591 13876 -1593 13875 -1594 13873 -1596 13872 -1597 13871 -1598 13869 -1600 13868 -1601 13867 -1602 13865 -1604 13864 -1605 13862 -1607 13861 -1608 13860 -1609 13858 -1611 13857 -1612 13856 -1614 13854 -1615 13853 -1616 13851 -1618 13850 -1619 13849 -1621 13847 -1622 13846 -1623 13845 -1625 13843 -1626 13842 -1628 13841 -1629 13839 -1630 13838 -1632 13836 -1633 13835 -1634 13834 -1636 13832 -1637 13831 -1639 13830 -1640 13828 -1641 13827 -1643 13825 -1644 13824 -1646 13823 -1647 13821 -1648 13820 -1650 13819 -1651 13817 -1653 13816 -1654 13815 -1655 13813 -1657 13812 -1658 13810 -1660 13809 -1661 13808 -1662 13806 -1664 13805 -1665 13804 -1666 13802 -1668 13801 -1669 13800 -1671 13798 -1672 13797 -1673 13795 -1675 13794 -1676 13793 -1678 13791 -1679 13790 -1680 13789 -1682 13787 -1683 13786 -1685 13784 -1686 13783 -1687 13781 -1689 13780 -1690 13778 -1692 13776 -1693 13774 -1694 13773 -1696 13771 -1697 13769 -1698 13767 -1700 13766 -1701 13764 -1703 13762 -1704 13760 -1706 13759 -1708 13757 -1709 13755 -1711 13754 -1713 13752 -1715 13750 -1716 13748 -1718 13747 -1720 13745 -1722 13743 -1723 13741 -1725 13740 -1727 13738 -1729 13736 -1731 13734 -1732 13733 -1734 13731 -1736 13729 -1738 13728 -1739 13726 -1741 13724 -1743 13722 -1745 13721 -1746 13719 -1748 13717 -1750 13715 -1752 13714 -1753 13712 -1755 13710 -1757 13709 -1759 13707 -1761 13705 -1762 13703 -1764 13702 -1766 13700 -1768 13698 -1769 13696 -1771 13695 -1773 13693 -1775 13691 -1776 13689 -1778 13688 -1780 13686 -1782 13684 -1783 13683 -1785 13681 -1787 13679 -1789 13677 -1791 13676 -1792 13674 -1794 13672 -1796 13670 -1798 13669 -1799 13667 -1801 13665 -1803 13664 -1805 13662 -1806 13660 -1808 13658 -1810 13657 -1812 13655 -1813 13653 -1815 13651 -1817 13650 -1819 13648 -1821 13646 -1822 13644 -1824 13643 -1826 13641 -1828 13639 -1829 13638 -1831 13636 -1833 13634 -1835 13632 -1836 13631 -1838 13629 -1840 13627 -1842 13625 -1843 13624 -1845 13622 -1847 13620 -1849 13618 -1851 13617 -1852 13615 -1854 13613 -1856 13612 -1858 13610 -1859 13608 -1861 13606 -1863 13605 -1865 13603 -1866 13601 -1868 13599 -1870 13598 -1872 13596 -1873 13594 -1875 13593 -1877 13591 -1879 13589 -1881 13587 -1882 13586 -1884 13584 -1886 13582 -1888 13580 -1889 13579 -1891 13577 -1893 13575 -1895 13573 -1897 13572 -1899 13570 -1901 13568 -1903 13567 -1905 13565 -1907 13563 -1909 13561 -1911 13560 -1913 13558 -1915 13556 -1917 13554 -1919 13553 -1921 13551 -1923 13549 -1925 13547 -1927 13545 -1929 13543 -1931 13540 -1933 13538 -1935 13536 -1937 13534 -1939 13531 -1941 13529 -1943 13527 -1945 13525 -1947 13522 -1949 13520 -1951 13518 -1953 13516 -1955 13514 -1957 13511 -1959 13509 -1961 13507 -1963 13505 -1965 13502 -1967 13500 -1969 13498 -1971 13496 -1973 13493 -1975 13491 -1977 13489 -1979 13487 -1981 13484 -1983 13482 -1985 13480 -1987 13478 -1989 13475 -1991 13473 -1993 13471 -1995 13469 -1997 13466 -1999 13464 -2001 13462 -2003 13460 -2005 13458 -2007 13455 -2009 13453 -2011 13451 -2013 13449 -2016 13446 -2018 13444 -2020 13442 -2022 13440 -2024 13437 -2026 13435 -2028 13433 -2030 13431 -2032 13428 -2034 13426 -2036 13424 -2038 13422 -2040 13419 -2042 13417 -2044 13415 -2046 13413 -2048 13410 -2050 13408 -2052 13406 -2054 13404 -2056 13401 -2058 13399 -2060 13397 -2062 13395 -2064 13393 -2066 13390 -2068 13388 -2070 13386 -2072 13384 -2074 13381 -2076 13379 -2078 13377 -2080 13375 -2082 13372 -2084 13370 -2086 13368 -2088 13366 -2090 13363 -2092 13361 -2094 13359 -2096 13357 -2098 13354 -2100 13352 -2102 13350 -2104 13347 -2106 13345 -2108 13343 -2110 13340 -2112 13338 -2114 13336 -2116 13333 -2118 13331 -2120 13329 -2122 13327 -2124 13324 -2126 13322 -2128 13320 -2130 13317 -2132 13315 -2134 13313 -2136 13310 -2137 13308 -2139 13306 -2141 13303 -2143 13301 -2145 13299 -2147 13296 -2149 13294 -2151 13292 -2153 13289 -2155 13287 -2157 13285 -2159 13283 -2161 13280 -2163 13278 -2165 13276 -2167 13273 -2169 13271 -2170 13269 -2172 13266 -2174 13264 -2176 13262 -2178 13259 -2180 13257 -2182 13255 -2184 13252 -2186 13250 -2188 13248 -2190 13245 -2192 13243 -2194 13241 -2196 13238 -2198 13236 -2200 13234 -2201 13232 -2203 13229 -2205 13227 -2207 13225 -2209 13222 -2211 13220 -2213 13218 -2215 13215 -2217 13213 -2219 13211 -2221 13208 -2223 13206 -2225 13204 -2227 13201 -2229 13199 -2231 13197 -2233 13194 -2234 13192 -2236 13190 -2238 13187 -2240 13185 -2242 13183 -2244 13181 -2246 13178 -2248 13176 -2250 13174 -2252 13171 -2254 13169 -2256 13167 -2258 13164 -2260 13162 -2262 13160 -2264 13157 -2265 13155 -2267 13153 -2269 13150 -2271 13148 -2273 13146 -2275 13143 -2277 13141 -2279 13138 -2281 13136 -2283 13133 -2285 13131 -2287 13128 -2289 13126 -2291 13123 -2293 13121 -2295 13118 -2297 13115 -2299 13113 -2301 13110 -2303 13108 -2305 13105 -2307 13103 -2309 13100 -2311 13098 -2313 13095 -2315 13092 -2317 13090 -2319 13087 -2322 13085 -2324 13082 -2326 13080 -2328 13077 -2330 13075 -2332 13072 -2334 13070 -2336 13067 -2338 13064 -2340 13062 -2342 13059 -2344 13057 -2346 13054 -2348 13052 -2350 13049 -2352 13047 -2354 13044 -2356 13041 -2359 13039 -2361 13036 -2363 13034 -2365 13031 -2367 13029 -2369 13026 -2371 13024 -2373 13021 -2375 13018 -2377 13016 -2379 13013 -2381 13011 -2383 13008 -2385 13006 -2387 13003 -2389 13001 -2391 12998 -2393 12995 -2395 12993 -2398 12990 -2400 12988 -2402 12985 -2404 12983 -2406 12981 -2408 12979 -2410 12976 -2412 12974 -2414 12972 -2416 12970 -2418 12967 -2420 12965 -2422 12963 -2424 12961 -2426 12958 -2428 12956 -2430 12954 -2432 12952 -2435 12949 -2438 12947 -2440 12945 -2443 12942 -2446 12940 -2448 12938 -2451 12936 -2454 12933 -2456 12931 -2459 12929 -2462 12927 -2464 12924 -2467 12922 -2470 12920 -2472 12918 -2475 12915 -2478 12913 -2480 12911 -2483 12909 -2486 12906 -2488 12904 -2491 12902 -2494 12900 -2496 12897 -2499 12895 -2502 12893 -2505 12891 -2507 12888 -2510 12886 -2513 12884 -2515 12882 -2518 12880 -2521 12878 -2523 12875 -2526 12873 -2529 12871 -2531 12869 -2534 12866 -2537 12864 -2539 12862 -2542 12860 -2545 12857 -2547 12855 -2550 12853 -2553 12851 -2555 12849 -2558 12846 -2561 12844 -2563 12842 -2566 12840 -2569 12837 -2571 12835 -2574 12833 -2582 12831 -2585 12828 -2588 12826 -2590 12824 -2593 12822 -2596 12819 -2599 12817 -2602 12815 -2605 12813 -2608 12810 -2611 12808 -2614 12806 -2617 12804 -2620 12802 -2623 12799 -2626 12797 -2629 12795 -2632 12793 -2635 12790 -2638 12788 -2641 12786 -2643 12784 -2646 12781 -2649 12779 -2652 12777 -2655 12775 -2658 12772 -2661 12770 -2664 12767 -2667 12763 -2670 12760 -2673 12756 -2676 12753 -2679 12749 -2682 12745 -2685 12742 -2688 12738 -2691 12735 -2693 12731 -2696 12727 -2700 12724 -2703 12720 -2707 12717 -2710 12713 -2714 12709 -2717 12706 -2721 12702 -2724 12699 -2728 12695 -2731 12691 -2735 12688 -2739 12684 -2742 12681 -2746 12677 -2749 12673 -2753 12670 -2756 12666 -2760 12663 -2763 12659 -2767 12655 -2770 12652 -2774 12648 -2777 12645 -2781 12641 -2784 12637 -2788 12634 -2791 12630 -2795 12627 -2798 12623 -2802 12619 -2805 12616 -2809 12612 -2812 12609 -2816 12605 -2820 12601 -2823 12598 -2827 12594 -2830 12590 -2834 12586 -2837 12581 -2841 12577 -2844 12573 -2848 12568 -2851 12564 -2855 12559 -2858 12555 -2862 12550 -2865 12546 -2869 12541 -2873 12537 -2877 12533 -2882 12528 -2886 12524 -2891 12519 -2895 12515 -2900 12510 -2905 12506 -2909 12501 -2914 12497 -2918 12493 -2923 12488 -2927 12484 -2932 12479 -2937 12475 -2941 12470 -2946 12466 -2950 12461 -2955 12457 -2959 12453 -2964 12448 -2968 12444 -2973 12439 -2978 12435 -2982 12430 -2987 12426 -2991 12421 -2996 12417 -3000 12411 -3005 12405 -3010 12399 -3014 12393 -3019 12387 -3025 12381 -3031 12375 -3036 12369 -3042 12363 -3047 12357 -3053 12351 -3058 12345 -3064 12339 -3070 12333 -3075 12327 -3081 12321 -3086 12315 -3092 12308 -3097 12302 -3103 12296 -3109 12290 -3114 12284 -3120 12278 -3125 12272 -3131 12266 -3136 12260 -3142 12254 -3148 12248 -3153 12242 -3159 12236 -3164 12230 -3170 12224 -3175 12218 -3181 12211 -3187 12204 -3192 12196 -3198 12188 -3203 12181 -3209 12173 -3215 12166 -3220 12158 -3227 12150 -3242 12143 -3257 12135 -3272 12128 -3287 12120 -3302 12112 -3317 12105 -3332 12097 -3348 12087 -3363 12070 -3402 12053 -3498 12036 -3611 12019 -3764 12003 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 -0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +3043 12432 +3039 12436 +3035 12440 +3031 12444 +3027 12448 +3023 12452 +3019 12456 +3015 12460 +3011 12464 +3007 12468 +3003 12472 +2999 12476 +2996 12480 +2992 12484 +2988 12488 +2984 12492 +2980 12495 +2976 12499 +2972 12503 +2969 12507 +2965 12511 +2961 12515 +2957 12519 +2953 12522 +2950 12526 +2946 12530 +2942 12534 +2938 12538 +2934 12542 +2931 12545 +2927 12549 +2923 12553 +2919 12557 +2916 12560 +2912 12564 +2908 12568 +2904 12572 +2901 12575 +2897 12579 +2893 12583 +2890 12586 +2886 12590 +2882 12594 +2879 12598 +2875 12601 +2871 12605 +2868 12609 +2864 12612 +2860 12616 +2857 12619 +2853 12623 +2850 12627 +2846 12630 +2842 12634 +2839 12638 +2835 12641 +2832 12645 +2828 12648 +2824 12652 +2821 12655 +2817 12659 +2814 12663 +2810 12666 +2807 12670 +2803 12673 +2800 12677 +2796 12680 +2793 12684 +2789 12687 +2786 12691 +2782 12694 +2779 12698 +2775 12701 +2772 12705 +2768 12708 +2765 12711 +2761 12715 +2758 12718 +2755 12722 +2751 12725 +2748 12729 +2744 12732 +2741 12735 +2738 12739 +2734 12742 +2731 12745 +2727 12749 +2724 12752 +2721 12756 +2717 12759 +2714 12762 +2711 12766 +2707 12769 +2704 12772 +2701 12776 +2697 12779 +2694 12782 +2691 12785 +2687 12789 +2684 12792 +2681 12795 +2678 12799 +2674 12802 +2671 12805 +2668 12808 +2664 12812 +2661 12815 +2658 12818 +2655 12821 +2652 12824 +2648 12828 +2645 12831 +2642 12834 +2639 12837 +2635 12840 +2632 12843 +2629 12847 +2626 12850 +2623 12853 +2620 12856 +2616 12859 +2613 12862 +2610 12865 +2607 12869 +2604 12872 +2601 12875 +2598 12878 +2594 12881 +2591 12884 +2588 12887 +2585 12890 +2582 12893 +2579 12896 +2576 12899 +2573 12902 +2570 12905 +2567 12908 +2564 12912 +2561 12915 +2558 12918 +2554 12921 +2551 12924 +2548 12927 +2545 12930 +2542 12932 +2539 12935 +2536 12938 +2533 12941 +2530 12944 +2527 12947 +2524 12950 +2521 12953 +2518 12956 +2516 12959 +2513 12962 +2510 12965 +2507 12968 +2504 12971 +2501 12975 +2498 12978 +2495 12981 +2492 12984 +2489 12987 +2486 12991 +2483 12994 +2480 12997 +2478 13000 +2475 13003 +2472 13006 +2469 13009 +2466 13013 +2463 13016 +2460 13019 +2457 13022 +2455 13025 +2452 13028 +2449 13031 +2446 13034 +2443 13037 +2441 13040 +2438 13043 +2435 13046 +2432 13049 +2429 13052 +2427 13055 +2424 13058 +2421 13061 +2418 13064 +2415 13067 +2413 13070 +2410 13073 +2407 13076 +2404 13079 +2402 13082 +2399 13085 +2396 13088 +2394 13091 +2391 13094 +2388 13096 +2385 13099 +2383 13102 +2380 13105 +2377 13108 +2375 13111 +2372 13114 +2369 13116 +2367 13119 +2364 13122 +2361 13125 +2359 13128 +2356 13130 +2353 13133 +2351 13136 +2348 13139 +2345 13142 +2343 13144 +2340 13147 +2338 13150 +2335 13153 +2332 13155 +2330 13158 +2327 13161 +2325 13163 +2322 13166 +2319 13169 +2317 13171 +2314 13174 +2312 13177 +2309 13179 +2307 13182 +2304 13185 +2302 13187 +2299 13190 +2297 13193 +2294 13195 +2291 13198 +2289 13201 +2286 13203 +2284 13206 +2281 13208 +2279 13211 +2276 13213 +2274 13216 +2271 13219 +2269 13221 +2267 13224 +2264 13226 +2262 13229 +2259 13231 +2257 13234 +2254 13236 +2252 13239 +2249 13241 +2247 13244 +2245 13246 +2242 13249 +2240 13251 +2237 13254 +2235 13256 +2232 13259 +2230 13261 +2228 13264 +2225 13266 +2223 13268 +2221 13271 +2218 13273 +2216 13276 +2213 13278 +2211 13280 +2209 13283 +2206 13285 +2204 13288 +2202 13290 +2199 13292 +2197 13295 +2195 13297 +2192 13299 +2190 13302 +2188 13304 +2185 13306 +2183 13309 +2181 13311 +2178 13313 +2176 13316 +2174 13318 +2172 13320 +2169 13322 +2167 13325 +2165 13327 +2163 13329 +2160 13331 +2158 13334 +2156 13336 +2154 13338 +2151 13340 +2149 13343 +2147 13345 +2145 13347 +2142 13349 +2140 13352 +2138 13354 +2136 13356 +2133 13358 +2131 13360 +2129 13362 +2127 13365 +2125 13367 +2122 13369 +2120 13371 +2118 13373 +2116 13375 +2114 13378 +2112 13380 +2109 13382 +2107 13384 +2105 13386 +2103 13388 +2101 13390 +2099 13392 +2097 13394 +2094 13397 +2092 13399 +2090 13401 +2088 13403 +2086 13405 +2084 13407 +2082 13409 +2080 13411 +2077 13413 +2075 13415 +2073 13417 +2071 13419 +2069 13421 +2067 13423 +2065 13425 +2063 13427 +2061 13429 +2059 13431 +2057 13433 +2055 13435 +2053 13437 +2051 13439 +2048 13441 +2046 13443 +2044 13445 +2042 13447 +2040 13449 +2038 13451 +2036 13453 +2034 13455 +2032 13457 +2030 13459 +2028 13461 +2026 13463 +2024 13465 +2022 13466 +2020 13468 +2018 13470 +2016 13472 +2014 13474 +2012 13476 +2010 13478 +2008 13480 +2006 13482 +2004 13483 +2002 13485 +2000 13487 +1999 13489 +1997 13491 +1995 13493 +1993 13495 +1991 13496 +1989 13498 +1987 13500 +1985 13502 +1983 13504 +1981 13506 +1979 13507 +1977 13509 +1975 13511 +1973 13513 +1972 13515 +1970 13516 +1968 13518 +1966 13520 +1964 13522 +1962 13524 +1960 13525 +1958 13527 +1956 13529 +1955 13531 +1953 13532 +1951 13534 +1949 13536 +1947 13538 +1945 13539 +1943 13541 +1942 13543 +1940 13545 +1938 13546 +1936 13548 +1934 13550 +1932 13551 +1931 13553 +1929 13555 +1927 13557 +1925 13558 +1923 13560 +1921 13562 +1920 13563 +1918 13565 +1916 13567 +1914 13568 +1912 13570 +1911 13572 +1909 13573 +1907 13575 +1905 13577 +1903 13578 +1902 13580 +1900 13582 +1898 13583 +1896 13585 +1895 13587 +1893 13588 +1891 13590 +1889 13592 +1888 13593 +1886 13595 +1884 13596 +1882 13598 +1881 13600 +1879 13601 +1877 13603 +1875 13604 +1874 13606 +1872 13608 +1870 13609 +1868 13611 +1867 13612 +1865 13614 +1863 13616 +1862 13617 +1860 13619 +1858 13620 +1856 13622 +1855 13623 +1853 13625 +1851 13627 +1850 13628 +1848 13630 +1846 13631 +1845 13633 +1843 13634 +1841 13636 +1839 13637 +1838 13639 +1836 13640 +1834 13642 +1833 13643 +1831 13645 +1829 13646 +1828 13648 +1826 13649 +1824 13651 +1823 13652 +1821 13654 +1819 13655 +1818 13657 +1816 13658 +1814 13660 +1813 13661 +1811 13663 +1810 13664 +1808 13666 +1806 13667 +1805 13668 +1803 13670 +1801 13671 +1800 13673 +1798 13674 +1796 13676 +1795 13677 +1793 13679 +1792 13680 +1790 13682 +1788 13683 +1787 13684 +1785 13686 +1784 13687 +1782 13689 +1780 13690 +1779 13692 +1777 13693 +1776 13694 +1774 13696 +1772 13697 +1771 13699 +1769 13700 +1768 13701 +1766 13703 +1765 13704 +1763 13706 +1761 13707 +1760 13709 +1758 13710 +1757 13711 +1755 13713 +1754 13714 +1752 13716 +1750 13717 +1749 13719 +1747 13720 +1746 13721 +1744 13723 +1743 13724 +1741 13726 +1739 13727 +1738 13729 +1736 13730 +1735 13732 +1733 13733 +1732 13735 +1730 13737 +1729 13738 +1727 13740 +1725 13741 +1724 13743 +1722 13744 +1721 13746 +1719 13747 +1718 13749 +1716 13751 +1715 13752 +1713 13754 +1711 13755 +1710 13757 +1708 13759 +1707 13760 +1705 13762 +1704 13763 +1702 13765 +1701 13767 +1699 13768 +1698 13770 +1696 13771 +1695 13773 +1693 13775 +1692 13776 +1690 13778 +1688 13780 +1687 13781 +1685 13783 +1684 13784 +1682 13786 +1681 13788 +1679 13789 +1678 13791 +1676 13792 +1675 13794 +1673 13796 +1672 13797 +1670 13799 +1669 13801 +1667 13802 +1666 13804 +1664 13805 +1663 13807 +1661 13809 +1660 13810 +1658 13812 +1657 13814 +1655 13815 +1654 13817 +1652 13818 +1651 13820 +1649 13822 +1648 13823 +1646 13825 +1645 13826 +1643 13828 +1642 13830 +1640 13831 +1639 13833 +1637 13834 +1636 13836 +1634 13838 +1633 13839 +1631 13841 +1630 13842 +1628 13844 +1627 13846 +1625 13847 +1624 13849 +1622 13850 +1621 13852 +1619 13854 +1618 13855 +1616 13857 +1615 13858 +1613 13860 +1612 13862 +1611 13863 +1609 13865 +1608 13866 +1606 13868 +1605 13870 +1603 13871 +1602 13873 +1600 13874 +1599 13876 +1597 13878 +1596 13879 +1594 13881 +1593 13882 +1591 13884 +1590 13885 +1588 13887 +1587 13889 +1586 13890 +1584 13892 +1583 13893 +1581 13895 +1580 13896 +1578 13898 +1577 13900 +1575 13901 +1574 13903 +1572 13904 +1571 13906 +1570 13907 +1568 13909 +1567 13910 +1565 13912 +1564 13914 +1562 13915 +1561 13917 +1559 13918 +1558 13920 +1557 13921 +1555 13923 +1554 13924 +1552 13926 +1551 13927 +1549 13929 +1548 13930 +1547 13932 +1545 13933 +1544 13935 +1542 13936 +1541 13938 +1539 13939 +1538 13941 +1537 13942 +1535 13944 +1534 13945 +1532 13947 +1531 13948 +1530 13950 +1528 13951 +1527 13953 +1525 13954 +1524 13956 +1523 13957 +1521 13959 +1520 13960 +1518 13962 +1517 13963 +1516 13964 +1514 13966 +1513 13967 +1511 13969 +1510 13970 +1509 13972 +1507 13973 +1506 13975 +1504 13976 +1503 13977 +1502 13979 +1500 13980 +1499 13982 +1497 13983 +1496 13985 +1495 13986 +1493 13987 +1492 13989 +1491 13990 +1489 13992 +1488 13993 +1487 13995 +1485 13996 +1484 13997 +1482 13999 +1481 14000 +1480 14002 +1478 14003 +1477 14004 +1476 14006 +1474 14007 +1473 14009 +1472 14010 +1470 14011 +1469 14013 +1468 14014 +1466 14016 +1465 14017 +1464 14018 +1462 14020 +1461 14021 +1460 14022 +1458 14024 +1457 14025 +1456 14027 +1454 14028 +1453 14029 +1452 14031 +1451 14032 +1449 14033 +1448 14035 +1447 14036 +1445 14038 +1444 14039 +1443 14040 +1441 14042 +1440 14043 +1439 14044 +1437 14046 +1436 14047 +1435 14049 +1434 14050 +1432 14051 +1431 14053 +1430 14054 +1428 14055 +1427 14057 +1426 14058 +1425 14059 +1423 14061 +1422 14062 +1421 14064 +1419 14065 +1418 14066 +1417 14068 +1416 14069 +1414 14070 +1413 14072 +1412 14073 +1410 14074 +1409 14076 +1408 14077 +1407 14078 +1405 14080 +1404 14081 +1403 14082 +1402 14084 +1400 14085 +1399 14086 +1398 14088 +1397 14089 +1395 14090 +1394 14092 +1393 14093 +1392 14094 +1390 14096 +1389 14097 +1388 14098 +1387 14100 +1385 14101 +1384 14102 +1383 14104 +1382 14105 +1380 14106 +1379 14108 +1378 14109 +1377 14110 +1375 14112 +1374 14113 +1373 14114 +1372 14115 +1370 14117 +1369 14118 +1368 14119 +1367 14121 +1365 14122 +1364 14123 +1363 14125 +1362 14126 +1361 14127 +1359 14129 +1358 14130 +1357 14131 +1356 14132 +1355 14134 +1353 14135 +1352 14136 +1351 14138 +1350 14139 +1348 14140 +1347 14141 +1346 14143 +1345 14144 +1344 14145 +1342 14147 +1341 14148 +1340 14149 +1339 14150 +1338 14152 +1336 14153 +1335 14154 +1334 14155 +1333 14157 +1332 14158 +1330 14159 +1329 14161 +1328 14162 +1327 14163 +1326 14164 +1325 14166 +1323 14167 +1322 14168 +1321 14169 +1320 14171 +1319 14172 +1317 14173 +1316 14174 +1315 14176 +1314 14177 +1313 14178 +1312 14179 +1310 14181 +1309 14182 +1308 14183 +1307 14184 +1306 14186 +1305 14187 +1303 14188 +1302 14189 +1301 14191 +1300 14192 +1299 14193 +1298 14194 +1296 14195 +1295 14197 +1294 14198 +1293 14199 +1292 14200 +1291 14202 +1290 14203 +1288 14204 +1287 14205 +1286 14206 +1285 14208 +1284 14209 +1283 14210 +1282 14211 +1280 14212 +1279 14214 +1278 14215 +1277 14216 +1276 14217 +1275 14218 +1274 14220 +1272 14221 +1271 14222 +1270 14223 +1269 14224 +1268 14226 +1267 14227 +1266 14228 +1265 14229 +1263 14230 +1262 14231 +1261 14233 +1260 14234 +1259 14235 +1258 14236 +1257 14237 +1256 14238 +1255 14240 +1253 14241 +1252 14242 +1251 14243 +1250 14244 +1249 14245 +1248 14246 +1247 14248 +1246 14249 +1245 14250 +1244 14251 +1242 14252 +1241 14253 +1240 14254 +1239 14256 +1238 14257 +1237 14258 +1236 14259 +1235 14260 +1234 14261 +1233 14262 +1232 14263 +1231 14265 +1229 14266 +1228 14267 +1227 14268 +1226 14269 +1225 14270 +1224 14271 +1223 14272 +1222 14273 +1221 14275 +1220 14276 +1219 14277 +1218 14278 +1217 14279 +1216 14280 +1215 14281 +1213 14282 +1212 14283 +1211 14284 +1210 14286 +1209 14287 +1208 14288 +1207 14289 +1206 14290 +1205 14291 +1204 14292 +1203 14293 +1202 14294 +1201 14295 +1200 14296 +1199 14297 +1198 14298 +1197 14300 +1196 14301 +1195 14302 +1194 14303 +1193 14304 +1192 14305 +1191 14306 +1190 14307 +1188 14308 +1187 14309 +1186 14310 +1185 14311 +1184 14312 +1183 14313 +1182 14314 +1181 14315 +1180 14316 +1179 14317 +1178 14318 +1177 14320 +1176 14321 +1175 14322 +1174 14323 +1173 14324 +1172 14325 +1171 14326 +1170 14327 +1169 14328 +1168 14329 +1167 14330 +1166 14331 +1165 14332 +1164 14333 +1163 14334 +1162 14335 +1161 14336 +1160 14337 +1159 14338 +1158 14339 +1158 14340 +1157 14341 +1156 14342 +1155 14343 +1154 14344 +1153 14345 +1152 14346 +1151 14347 +1150 14348 +1149 14349 +1148 14350 +1147 14351 +1146 14352 +1145 14353 +1144 14354 +1143 14355 +1142 14356 +1141 14357 +1140 14358 +1139 14359 +1138 14360 +1137 14361 +1136 14362 +1135 14363 +1134 14364 +1133 14365 +1133 14366 +1132 14367 +1131 14367 +1130 14368 +1129 14369 +1128 14370 +1127 14371 +1126 14372 +1125 14373 +1124 14374 +1123 14375 +1122 14376 +1121 14377 +1120 14378 +1119 14379 +1118 14380 +1117 14381 +1117 14382 +1116 14383 +1115 14384 +1114 14385 +1113 14386 +1112 14387 +1111 14388 +1110 14389 +1109 14389 +1108 14390 +1107 14391 +1106 14392 +1105 14393 +1105 14394 +1104 14395 +1103 14396 +1102 14397 +1101 14398 +1100 14399 +1099 14400 +1098 14401 +1097 14402 +1096 14403 +1095 14403 +1094 14404 +1094 14405 +1093 14406 +1092 14407 +1091 14408 +1090 14409 +1089 14410 +1088 14411 +1087 14412 +1086 14413 +1085 14414 +1085 14414 +1084 14415 +1083 14416 +1082 14417 +1081 14418 +1080 14419 +1079 14420 +1078 14421 +1077 14422 +1077 14423 +1076 14424 +1075 14424 +1074 14425 +1073 14426 +1072 14427 +1071 14428 +1070 14429 +1069 14430 +1069 14431 +1068 14432 +1067 14433 +1066 14433 +1065 14434 +1064 14435 +1063 14436 +1062 14437 +1061 14438 +1061 14439 +1060 14440 +1059 14440 +1058 14441 +1057 14442 +1056 14443 +1055 14444 +1054 14445 +1054 14446 +1053 14447 +1052 14447 +1051 14448 +1050 14449 +1049 14450 +1048 14451 +1048 14452 +1047 14453 +1046 14454 +1045 14454 +1044 14455 +1043 14456 +1042 14457 +1041 14458 +1041 14459 +1040 14460 +1039 14460 +1038 14461 +1037 14462 +1036 14463 +1035 14464 +1035 14465 +1034 14465 +1033 14466 +1032 14467 +1031 14468 +1030 14469 +1029 14470 +1029 14471 +1028 14471 +1027 14472 +1026 14473 +1025 14474 +1024 14475 +1024 14476 +1023 14476 +1022 14477 +1021 14478 +1020 14479 +1019 14480 +1018 14480 +1018 14481 +1017 14482 +1016 14483 +1015 14484 +1014 14485 +1013 14485 +1013 14486 +1012 14487 +1011 14488 +1010 14489 +1009 14489 +1008 14490 +1007 14491 +1007 14492 +1006 14493 +1005 14493 +1004 14494 +1003 14495 +1002 14496 +1002 14497 +1001 14497 +1000 14498 +999 14499 +998 14500 +997 14501 +997 14501 +996 14502 +995 14503 +994 14504 +993 14505 +992 14505 +991 14506 +991 14507 +990 14508 +989 14508 +988 14509 +987 14510 +986 14511 +986 14512 +985 14512 +984 14513 +983 14514 +982 14515 +981 14515 +981 14516 +980 14517 +979 14518 +978 14519 +977 14519 +976 14520 +976 14521 +975 14522 +974 14522 +973 14523 +972 14524 +971 14525 +971 14525 +970 14526 +969 14527 +968 14528 +967 14528 +966 14529 +966 14530 +965 14531 +964 14531 +963 14532 +962 14533 +961 14534 +961 14535 +960 14535 +959 14536 +958 14537 +957 14538 +956 14538 +956 14539 +955 14540 +954 14541 +953 14541 +952 14542 +952 14543 +951 14544 +950 14544 +949 14545 +948 14546 +947 14547 +947 14547 +946 14548 +945 14549 +944 14550 +943 14550 +943 14551 +942 14552 +941 14553 +940 14554 +939 14554 +938 14555 +938 14556 +937 14557 +936 14557 +935 14558 +934 14559 +934 14560 +933 14560 +932 14561 +931 14562 +930 14563 +930 14563 +929 14564 +928 14565 +927 14566 +926 14566 +926 14567 +925 14568 +924 14569 +923 14569 +922 14570 +922 14571 +921 14572 +920 14572 +919 14573 +918 14574 +918 14575 +917 14575 +916 14576 +915 14577 +914 14578 +914 14578 +913 14579 +912 14580 +911 14581 +910 14581 +910 14582 +909 14583 +908 14584 +907 14584 +907 14585 +906 14586 +905 14587 +904 14587 +903 14588 +903 14589 +902 14590 +901 14590 +900 14591 +899 14592 +899 14593 +898 14593 +897 14594 +896 14595 +896 14595 +895 14596 +894 14597 +893 14598 +892 14598 +892 14599 +891 14600 +890 14601 +889 14601 +889 14602 +888 14603 +887 14604 +886 14604 +885 14605 +885 14606 +884 14607 +883 14607 +882 14608 +882 14609 +881 14609 +880 14610 +879 14611 +879 14612 +878 14612 +877 14613 +876 14614 +875 14615 +875 14615 +874 14616 +873 14617 +872 14618 +872 14618 +871 14619 +870 14620 +869 14620 +869 14621 +868 14622 +867 14623 +866 14623 +866 14624 +865 14625 +864 14626 +863 14626 +863 14627 +862 14628 +861 14628 +860 14629 +860 14630 +859 14631 +858 14631 +857 14632 +857 14633 +856 14634 +855 14634 +854 14635 +854 14636 +853 14636 +852 14637 +851 14638 +851 14639 +850 14639 +849 14640 +848 14641 +848 14641 +847 14642 +846 14643 +845 14644 +845 14644 +844 14645 +843 14646 +842 14647 +842 14647 +841 14648 +840 14649 +839 14649 +839 14650 +838 14651 +837 14652 +836 14652 +836 14653 +835 14654 +834 14654 +833 14655 +833 14656 +832 14657 +831 14657 +831 14658 +830 14659 +829 14659 +828 14660 +828 14661 +827 14662 +826 14662 +825 14663 +825 14664 +824 14664 +823 14665 +823 14666 +822 14667 +821 14667 +820 14668 +820 14669 +819 14669 +818 14670 +817 14671 +817 14671 +816 14672 +815 14673 +815 14674 +814 14674 +813 14675 +812 14676 +812 14676 +811 14677 +810 14678 +810 14678 +809 14679 +808 14680 +807 14681 +807 14681 +806 14682 +805 14683 +805 14683 +804 14684 +803 14685 +802 14685 +802 14686 +801 14687 +800 14688 +800 14688 +799 14689 +798 14690 +797 14690 +797 14691 +796 14692 +795 14692 +795 14693 +794 14694 +793 14694 +792 14695 +792 14696 +791 14697 +790 14697 +790 14698 +789 14699 +788 14699 +788 14700 +787 14701 +786 14701 +785 14702 +785 14703 +784 14703 +783 14704 +783 14705 +782 14705 +781 14706 +780 14707 +780 14707 +779 14708 +778 14709 +778 14709 +777 14710 +776 14711 +776 14711 +775 14712 +774 14713 +773 14713 +773 14714 +772 14715 +771 14715 +771 14716 +770 14717 +769 14717 +769 14718 +768 14719 +767 14719 +766 14720 +766 14721 +765 14721 +764 14722 +764 14723 +763 14723 +762 14724 +762 14725 +761 14725 +760 14726 +760 14727 +759 14727 +758 14728 +757 14728 +757 14729 +756 14730 +755 14730 +755 14731 +754 14732 +753 14732 +753 14733 +752 14734 +751 14734 +751 14735 +750 14735 +749 14736 +749 14737 +748 14737 +747 14738 +746 14739 +746 14739 +745 14740 +744 14741 +744 14741 +743 14742 +742 14742 +742 14743 +741 14744 +740 14744 +740 14745 +739 14746 +738 14746 +738 14747 +737 14747 +736 14748 +736 14749 +735 14749 +734 14750 +734 14751 +733 14751 +732 14752 +732 14752 +731 14753 +730 14754 +730 14754 +729 14755 +728 14756 +728 14756 +727 14757 +726 14757 +726 14758 +725 14759 +724 14759 +724 14760 +723 14760 +722 14761 +722 14762 +721 14762 +720 14763 +720 14764 +719 14764 +718 14765 +718 14765 +717 14766 +716 14767 +716 14767 +715 14768 +715 14768 +714 14769 +713 14770 +713 14770 +712 14771 +711 14771 +711 14772 +710 14773 +709 14773 +709 14774 +708 14775 +707 14775 +707 14776 +706 14776 +706 14777 +705 14778 +704 14778 +704 14779 +703 14779 +702 14780 +702 14780 +701 14781 +700 14782 +700 14782 +699 14783 +699 14783 +698 14784 +697 14785 +697 14785 +696 14786 +695 14786 +695 14787 +694 14788 +694 14788 +693 14789 +692 14789 +692 14790 +691 14791 +690 14791 +690 14792 +689 14792 +689 14793 +688 14793 +687 14794 +687 14795 +686 14795 +685 14796 +685 14796 +684 14797 +684 14797 +683 14798 +682 14799 +682 14799 +681 14800 +681 14800 +680 14801 +679 14801 +679 14802 +678 14803 +677 14803 +677 14804 +676 14804 +676 14805 +675 14805 +674 14806 +674 14806 +673 14807 +673 14808 +672 14808 +671 14809 +671 14809 +670 14810 +670 14810 +669 14811 +669 14811 +668 14812 +667 14813 +667 14813 +666 14814 +666 14814 +665 14815 +664 14815 +664 14816 +663 14816 +663 14817 +662 14817 +661 14818 +661 14819 +660 14819 +660 14820 +659 14820 +659 14821 +658 14821 +657 14822 +657 14822 +656 14823 +656 14823 +655 14824 +654 14824 +654 14825 +653 14826 +653 14826 +652 14827 +652 14827 +651 14828 +650 14828 +650 14829 +649 14829 +649 14830 +648 14830 +648 14831 +647 14831 +647 14832 +646 14832 +645 14833 +645 14833 +644 14834 +644 14834 +643 14835 +643 14835 +642 14836 +641 14836 +641 14837 +640 14838 +640 14838 +639 14839 +639 14839 +638 14840 +638 14840 +637 14841 +637 14841 +636 14842 +635 14842 +635 14843 +634 14843 +634 14844 +633 14844 +633 14845 +632 14845 +632 14846 +631 14846 +631 14847 +630 14847 +629 14848 +629 14848 +628 14849 +628 14849 +627 14850 +627 14850 +626 14851 +626 14851 +625 14852 +625 14852 +624 14853 +624 14853 +623 14854 +623 14854 +622 14855 +621 14855 +621 14855 +620 14856 +620 14856 +619 14857 +619 14857 +618 14858 +618 14858 +617 14859 +617 14859 +616 14860 +616 14860 +615 14861 +615 14861 +614 14862 +614 14862 +613 14863 +613 14863 +612 14864 +612 14864 +611 14865 +611 14865 +610 14866 +610 14866 +609 14866 +609 14867 +608 14867 +608 14868 +607 14868 +607 14869 +606 14869 +606 14870 +605 14870 +605 14871 +604 14871 +604 14872 +603 14872 +603 14873 +602 14873 +602 14873 +601 14874 +601 14874 +600 14875 +600 14875 +599 14876 +599 14876 +598 14877 +598 14877 +597 14878 +597 14878 +596 14878 +596 14879 +595 14879 +595 14880 +594 14880 +594 14881 +593 14881 +593 14882 +592 14882 +592 14883 +591 14883 +591 14883 +590 14884 +590 14884 +589 14885 +589 14885 +589 14886 +588 14886 +588 14887 +587 14887 +587 14887 +586 14888 +586 14888 +585 14889 +585 14889 +584 14890 +584 14890 +583 14891 +583 14891 +582 14892 +582 14892 +582 14892 +581 14893 +581 14893 +580 14894 +580 14894 +579 14895 +579 14895 +578 14896 +578 14896 +577 14897 +577 14897 +576 14897 +576 14898 +576 14898 +575 14899 +575 14899 +574 14900 +574 14900 +573 14901 +573 14901 +572 14901 +572 14902 +572 14902 +571 14903 +571 14903 +570 14904 +570 14904 +569 14905 +569 14905 +568 14906 +568 14906 +568 14906 +567 14907 +567 14907 +566 14908 +566 14908 +565 14909 +565 14909 +564 14909 +564 14910 +564 14910 +563 14911 +563 14911 +562 14912 +562 14912 +561 14913 +561 14913 +561 14913 +560 14914 +560 14914 +559 14915 +559 14915 +559 14916 +558 14916 +558 14916 +557 14917 +557 14917 +556 14918 +556 14918 +556 14919 +555 14919 +555 14919 +554 14920 +554 14920 +553 14921 +553 14921 +553 14922 +552 14922 +552 14922 +551 14923 +551 14923 +551 14924 +550 14924 +550 14925 +549 14925 +549 14925 +549 14926 +548 14926 +548 14927 +547 14927 +547 14928 +546 14928 +546 14928 +546 14929 +545 14929 +545 14930 +544 14930 +544 14931 +544 14931 +543 14931 +543 14932 +542 14932 +542 14933 +542 14933 +541 14934 +541 14934 +540 14934 +540 14935 +539 14935 +539 14936 +539 14936 +538 14937 +538 14937 +537 14937 +537 14938 +537 14938 +536 14939 +536 14939 +535 14940 +535 14940 +535 14940 +534 14941 +534 14941 +533 14942 +533 14942 +533 14943 +532 14943 +532 14943 +531 14944 +531 14944 +531 14945 +530 14945 +530 14946 +529 14946 +529 14946 +529 14947 +528 14947 +528 14948 +527 14948 +527 14949 +527 14949 +526 14949 +526 14950 +525 14950 +525 14951 +525 14951 +524 14952 +524 14952 +524 14952 +523 14953 +523 14953 +522 14954 +522 14954 +522 14954 +521 14955 +521 14955 +520 14956 +520 14956 +520 14957 +519 14957 +519 14957 +518 14958 +518 14958 +518 14959 +517 14959 +517 14960 +517 14960 +516 14960 +516 14961 +515 14961 +515 14962 +515 14962 +514 14963 +514 14963 +514 14963 +513 14964 +513 14964 +512 14965 +512 14965 +512 14966 +511 14966 +511 14966 +511 14967 +510 14967 +510 14968 +509 14968 +509 14969 +509 14969 +508 14969 +508 14970 +508 14970 +507 14971 +507 14971 +506 14972 +506 14972 +506 14972 +505 14973 +505 14973 +505 14974 +504 14974 +504 14975 +503 14975 +503 14976 +503 14976 +502 14976 +502 14977 +502 14977 +501 14978 +501 14978 +500 14979 +500 14979 +500 14979 +499 14980 +499 14980 +499 14981 +498 14981 +498 14982 +497 14982 +497 14982 +497 14983 +496 14983 +496 14984 +496 14984 +495 14985 +495 14985 +495 14985 +494 14986 +494 14986 +493 14987 +493 14987 +493 14988 +492 14988 +492 14989 +492 14989 +491 14989 +491 14990 +491 14990 +490 14991 +490 14991 +490 14992 +489 14992 +489 14992 +488 14993 +488 14993 +488 14994 +487 14994 +487 14995 +487 14995 +486 14995 +486 14996 +486 14996 +485 14997 +485 14997 +485 14998 +484 14998 +484 14999 +483 14999 +483 14999 +483 15000 +482 15000 +482 15001 +482 15001 +481 15002 +481 15002 +481 15003 +480 15003 +480 15003 +480 15004 +479 15004 +479 15005 +479 15005 +478 15006 +478 15006 +478 15006 +477 15007 +477 15007 +476 15008 +476 15008 +476 15009 +475 15009 +475 15009 +475 15010 +474 15010 +474 15011 +474 15011 +473 15012 +473 15012 +473 15012 +472 15013 +472 15013 +472 15014 +471 15014 +471 15015 +471 15015 +470 15016 +470 15016 +470 15016 +469 15017 +469 15017 +469 15018 +468 15018 +468 15019 +468 15019 +467 15019 +467 15020 +467 15020 +466 15021 +466 15021 +466 15022 +465 15022 +465 15022 +464 15023 +464 15023 +464 15024 +463 15024 +463 15025 +463 15025 +462 15025 +462 15026 +462 15026 +461 15027 +461 15027 +461 15028 +460 15028 +460 15028 +460 15029 +459 15029 +459 15030 +459 15030 +458 15031 +458 15031 +458 15031 +457 15032 +457 15032 +457 15033 +456 15033 +456 15034 +456 15034 +455 15034 +455 15035 +455 15035 +454 15036 +454 15036 +454 15036 +453 15037 +453 15037 +453 15038 +452 15038 +452 15039 +452 15039 +451 15039 +451 15040 +451 15040 +450 15041 +450 15041 +450 15042 +449 15042 +449 15042 +449 15043 +448 15043 +448 15044 +448 15044 +447 15044 +447 15045 +447 15045 +446 15046 +446 15046 +446 15047 +445 15047 +445 15047 +445 15048 +444 15048 +444 15049 +444 15049 +443 15049 +443 15050 +443 15050 +443 15051 +442 15051 +442 15051 +442 15052 +441 15052 +441 15053 +441 15053 +440 15054 +440 15054 +440 15054 +439 15055 +439 15055 +439 15056 +438 15056 +438 15056 +438 15057 +437 15057 +437 15058 +437 15058 +436 15058 +436 15059 +436 15059 +435 15060 +435 15060 +435 15060 +434 15061 +434 15061 +434 15062 +433 15062 +433 15062 +433 15063 +432 15063 +432 15064 +432 15064 +432 15064 +431 15065 +431 15065 +431 15066 +430 15066 +430 15066 +430 15067 +429 15067 +429 15068 +429 15068 +428 15068 +428 15069 +428 15069 +427 15070 +427 15070 +427 15070 +427 15071 +426 15071 +426 15071 +426 15072 +425 15072 +425 15073 +425 15073 +424 15073 +424 15074 +424 15074 +423 15075 +423 15075 +423 15075 +423 15076 +422 15076 +422 15077 +422 15077 +421 15077 +421 15078 +421 15078 +420 15078 +420 15079 +420 15079 +419 15080 +419 15080 +419 15080 +419 15081 +418 15081 +418 15082 +418 15082 +417 15082 +417 15083 +417 15083 +416 15083 +416 15084 +416 15084 +416 15085 +415 15085 +415 15085 +415 15086 +414 15086 +414 15087 +414 15087 +413 15087 +413 15088 +413 15088 +413 15088 +412 15089 +412 15089 +412 15090 +411 15090 +411 15090 +411 15091 +410 15091 +410 15091 +410 15092 +410 15092 +409 15093 +409 15093 +409 15093 +408 15094 +408 15094 +408 15094 +408 15095 +407 15095 +407 15096 +407 15096 +406 15096 +406 15097 +406 15097 +405 15097 +405 15098 +405 15098 +405 15098 +404 15099 +404 15099 +404 15100 +403 15100 +403 15100 +403 15101 +403 15101 +402 15101 +402 15102 +402 15102 +401 15103 +401 15103 +401 15103 +400 15104 +400 15104 +400 15104 +400 15105 +399 15105 +399 15105 +399 15106 +398 15106 +398 15107 +398 15107 +398 15107 +397 15108 +397 15108 +397 15108 +396 15109 +396 15109 +396 15110 +396 15110 +395 15110 +395 15111 +395 15111 +394 15111 +394 15112 +394 15112 +394 15112 +393 15113 +393 15113 +393 15113 +392 15114 +392 15114 +392 15115 +392 15115 +391 15115 +391 15116 +391 15116 +390 15116 +390 15117 +390 15117 +390 15117 +389 15118 +389 15118 +389 15118 +388 15119 +388 15119 +388 15119 +388 15120 +387 15120 +387 15120 +387 15121 +386 15121 +386 15121 +386 15122 +386 15122 +385 15123 +385 15123 +385 15123 +384 15124 +384 15124 +384 15124 +384 15125 +383 15125 +383 15125 +383 15126 +383 15126 +382 15126 +382 15127 +382 15127 +381 15127 +381 15128 +381 15128 +381 15128 +380 15129 +380 15129 +380 15129 +380 15130 +379 15130 +379 15130 +379 15131 +378 15131 +378 15131 +378 15131 +378 15132 +377 15132 +377 15132 +377 15133 +377 15133 +376 15133 +376 15134 +376 15134 +375 15134 +375 15135 +375 15135 +375 15135 +374 15136 +374 15136 +374 15136 +374 15137 +373 15137 +373 15137 +373 15138 +373 15138 +372 15138 +372 15138 +372 15139 +372 15139 +371 15139 +371 15140 +371 15140 +371 15140 +370 15141 +370 15141 +370 15141 +369 15142 +369 15142 +369 15142 +369 15142 +368 15143 +368 15143 +368 15143 +368 15144 +367 15144 +367 15144 +367 15145 +367 15145 +366 15145 +366 15145 +366 15146 +366 15146 +365 15146 +365 15147 +365 15147 +365 15147 +364 15147 +364 15148 +364 15148 +364 15148 +363 15149 +363 15149 +363 15149 +363 15149 +362 15150 +362 15150 +362 15150 +362 15151 +361 15151 +361 15151 +361 15151 +361 15152 +361 15152 +360 15152 +360 15152 +360 15153 +360 15153 +359 15153 +359 15154 +359 15154 +359 15154 +358 15154 +358 15155 +358 15155 +358 15155 +357 15155 +357 15156 +357 15156 +357 15156 +357 15156 +356 15157 +356 15157 +356 15157 +356 15157 +355 15158 +355 15158 +355 15158 +355 15158 +354 15159 +354 15159 +354 15159 +354 15159 +354 15160 +353 15160 +353 15160 +353 15160 +353 15161 +352 15161 +352 15161 +352 15161 +352 15162 +352 15162 +351 15162 +351 15162 +351 15163 +351 15163 +350 15163 +350 15163 +350 15163 +350 15164 +350 15164 +349 15164 +349 15164 +349 15165 +349 15165 +349 15165 +348 15165 +348 15166 +348 15166 +348 15166 +348 15166 +347 15166 +347 15167 +347 15167 +347 15167 +346 15167 +346 15168 +346 15168 +346 15168 +346 15168 +345 15168 +345 15169 +345 15169 +345 15169 +345 15169 +344 15170 +344 15170 +344 15170 +344 15170 +344 15170 +343 15171 +343 15171 +343 15171 +343 15171 +343 15171 +342 15172 +342 15172 +342 15172 +342 15172 +342 15173 +341 15173 +341 15173 +341 15173 +341 15173 +341 15174 +341 15174 +340 15174 +340 15174 +340 15174 +340 15175 +340 15175 +339 15175 +339 15175 +339 15175 +339 15175 +339 15176 +338 15176 +338 15176 +338 15176 +338 15176 +338 15177 +338 15177 +337 15177 +337 15177 +337 15177 +337 15178 +337 15178 +336 15178 +336 15178 +336 15178 +336 15179 +336 15179 +336 15179 +335 15179 +335 15179 +335 15179 +335 15180 +335 15180 +334 15180 +334 15180 +334 15180 +334 15180 +334 15181 +334 15181 +333 15181 +333 15181 +333 15181 +333 15182 +333 15182 +333 15182 +332 15182 +332 15182 +332 15182 +332 15183 +332 15183 +332 15183 +331 15183 +331 15183 +331 15183 +331 15184 +331 15184 +331 15184 +330 15184 +330 15184 +330 15184 +330 15185 +330 15185 +330 15185 +329 15185 +329 15185 +329 15185 +329 15185 +329 15186 +329 15186 +328 15186 +328 15186 +328 15186 +328 15186 +328 15187 +328 15187 +328 15187 +327 15187 +327 15187 +327 15187 +327 15187 +327 15188 +327 15188 +326 15188 +326 15188 +326 15188 +326 15188 +326 15188 +326 15189 +326 15189 +325 15189 +325 15189 +325 15189 +325 15189 +325 15189 +325 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15190 +324 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +323 15191 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +322 15192 +321 15192 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +321 15193 +320 15193 +320 15194 +320 15194 +320 15194 +320 15194 +320 15194 +320 15194 +319 15194 +319 15194 +319 15195 +319 15195 +319 15195 +319 15195 +319 15195 +319 15195 +318 15195 +318 15195 +318 15195 +318 15196 +318 15196 +318 15196 +318 15196 +318 15196 +317 15196 +317 15196 +317 15196 +317 15196 +317 15196 +317 15197 +317 15197 +317 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15197 +316 15198 +316 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +315 15198 +314 15198 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +314 15199 +313 15199 +313 15199 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +313 15200 +312 15200 +312 15200 +312 15200 +312 15200 +312 15201 +312 15201 +312 15201 +312 15201 +312 15201 +312 15201 +311 15201 +311 15201 +311 15201 +311 15201 +311 15202 +311 15202 +311 15202 +311 15202 +311 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15202 +310 15203 +310 15203 +310 15203 +310 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15203 +309 15204 +309 15204 +309 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15204 +308 15205 +308 15205 +308 15205 +308 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15205 +307 15206 +307 15206 +307 15206 +307 15206 +307 15206 +306 15206 +306 15206 +306 15206 +306 15206 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +306 15207 +305 15207 +305 15207 +305 15207 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +305 15208 +304 15208 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15209 +304 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15210 +303 15211 +303 15211 +303 15211 +302 15211 +302 15211 +302 15211 +302 15211 +302 15211 +302 15212 +302 15212 +302 15212 +302 15212 +302 15212 +302 15212 +301 15212 +301 15212 +301 15212 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +301 15213 +300 15213 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15214 +300 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15215 +299 15216 +299 15216 +299 15216 +299 15216 +298 15216 +298 15216 +298 15216 +298 15216 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +298 15217 +297 15217 +297 15217 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +297 15218 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15219 +296 15220 +296 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15220 +295 15221 +295 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15221 +294 15222 +294 15222 +294 15222 +294 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15222 +293 15223 +293 15223 +293 15223 +293 15223 +293 15223 +292 15223 +292 15223 +292 15223 +292 15223 +292 15223 +292 15224 +292 15224 +292 15224 +292 15224 +292 15224 +292 15224 +291 15224 +291 15224 +291 15224 +291 15224 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +291 15225 +290 15225 +290 15225 +290 15225 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +290 15226 +289 15226 +289 15226 +289 15226 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +289 15227 +288 15227 +288 15227 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +288 15228 +287 15228 +287 15228 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +287 15229 +286 15229 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +286 15230 +285 15230 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +285 15231 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15232 +284 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15233 +283 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15234 +282 15235 +282 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15235 +281 15236 +281 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15236 +280 15237 +280 15237 +280 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15237 +279 15238 +279 15238 +279 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15238 +278 15239 +278 15239 +278 15239 +278 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15239 +277 15240 +277 15240 +277 15240 +277 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15240 +276 15241 +276 15241 +276 15241 +276 15241 +275 15241 +275 15241 +275 15241 +275 15241 +275 15241 +275 15242 +275 15242 +275 15242 +275 15242 +275 15242 +274 15242 +274 15242 +274 15242 +274 15242 +274 15242 +274 15243 +274 15243 +274 15243 +274 15243 +274 15243 +273 15243 +273 15243 +273 15243 +273 15243 +273 15243 +273 15244 +273 15244 +273 15244 +273 15244 +272 15244 +272 15244 +272 15244 +272 15244 +272 15244 +272 15245 +272 15245 +272 15245 +272 15245 +272 15245 +271 15245 +271 15245 +271 15245 +271 15245 +271 15245 +271 15246 +271 15246 +271 15246 +271 15246 +271 15246 +270 15246 +270 15246 +270 15246 +270 15246 +270 15247 +270 15247 +270 15247 +270 15247 +270 15247 +270 15247 +269 15247 +269 15247 +269 15247 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +269 15248 +268 15248 +268 15248 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +268 15249 +267 15249 +267 15249 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +267 15250 +266 15250 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +266 15251 +265 15251 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +265 15252 +264 15252 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +264 15253 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15254 +263 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15255 +262 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15256 +261 15257 +261 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15257 +260 15258 +260 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15258 +259 15259 +259 15259 +259 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15259 +258 15260 +258 15260 +258 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15260 +257 15261 +257 15261 +257 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15261 +256 15262 +256 15262 +256 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15262 +255 15263 +255 15263 +255 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15263 +254 15264 +254 15264 +254 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15264 +253 15265 +253 15265 +253 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15265 +252 15266 +252 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15266 +251 15267 +251 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15267 +250 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15268 +249 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15269 +248 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +247 15270 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +246 15271 +245 15271 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +245 15272 +244 15272 +244 15272 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +244 15273 +243 15273 +243 15273 +243 15273 +243 15274 +243 15274 +243 15274 +243 15274 +243 15274 +243 15274 +242 15274 +242 15274 +242 15274 +242 15274 +242 15275 +242 15275 +242 15275 +242 15275 +242 15275 +242 15275 +241 15275 +241 15275 +241 15275 +241 15275 +241 15275 +241 15276 +241 15276 +241 15276 +241 15276 +241 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15276 +240 15277 +240 15277 +240 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15277 +239 15278 +239 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +238 15278 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +237 15279 +236 15279 +236 15279 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +236 15280 +235 15280 +235 15280 +235 15280 +235 15281 +235 15281 +235 15281 +235 15281 +235 15281 +235 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15281 +234 15282 +234 15282 +234 15282 +234 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15282 +233 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +232 15283 +231 15283 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +231 15284 +230 15284 +230 15284 +230 15284 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +230 15285 +229 15285 +229 15285 +229 15285 +229 15285 +229 15285 +229 15286 +229 15286 +229 15286 +229 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15286 +228 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +227 15287 +226 15287 +226 15287 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +226 15288 +225 15288 +225 15288 +225 15288 +225 15288 +225 15289 +225 15289 +225 15289 +225 15289 +225 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15289 +224 15290 +224 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +223 15290 +222 15290 +222 15290 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +222 15291 +221 15291 +221 15291 +221 15291 +221 15291 +221 15291 +221 15292 +221 15292 +221 15292 +221 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15292 +220 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +219 15293 +218 15293 +218 15293 +218 15293 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +218 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15294 +217 15295 +217 15295 +217 15295 +217 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +216 15295 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +215 15296 +214 15296 +214 15296 +214 15296 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +214 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15297 +213 15298 +213 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +212 15298 +211 15298 +211 15298 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +211 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15299 +210 15300 +210 15300 +210 15300 +210 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +209 15300 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +208 15301 +207 15301 +207 15301 +207 15301 +207 15301 +207 15302 +207 15302 +207 15302 +207 15302 +207 15302 +207 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15302 +206 15303 +206 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +205 15303 +204 15303 +204 15303 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +204 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15304 +203 15305 +203 15305 +203 15305 +203 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +202 15305 +201 15305 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +201 15306 +200 15306 +200 15306 +200 15306 +200 15306 +200 15306 +200 15307 +200 15307 +200 15307 +200 15307 +200 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +199 15307 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +198 15308 +197 15308 +197 15308 +197 15308 +197 15308 +197 15309 +197 15309 +197 15309 +197 15309 +197 15309 +197 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15309 +196 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +195 15310 +194 15310 +194 15310 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +194 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15311 +193 15312 +193 15312 +193 15312 +193 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +192 15312 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +191 15313 +190 15313 +190 15313 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +190 15314 +189 15314 +189 15314 +189 15314 +189 15314 +189 15315 +189 15315 +189 15315 +189 15315 +189 15315 +189 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15315 +188 15316 +188 15316 +188 15316 +188 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +187 15316 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +186 15317 +185 15317 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +185 15318 +184 15318 +184 15318 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +184 15319 +183 15319 +183 15319 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +183 15320 +182 15320 +182 15320 +182 15320 +182 15320 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +182 15321 +181 15321 +181 15321 +181 15321 +181 15321 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +181 15322 +180 15322 +180 15322 +180 15322 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +180 15323 +179 15323 +179 15323 +179 15323 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +179 15324 +178 15324 +178 15324 +178 15324 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +178 15325 +177 15325 +177 15325 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +177 15326 +176 15326 +176 15326 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +176 15327 +175 15327 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +175 15328 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15329 +174 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15330 +173 15331 +173 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15331 +172 15332 +172 15332 +172 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15332 +171 15333 +171 15333 +171 15333 +171 15333 +171 15333 +171 15333 +170 15333 +170 15333 +170 15333 +170 15333 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +170 15334 +169 15334 +169 15334 +169 15334 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +169 15335 +168 15335 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15336 +168 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15337 +167 15338 +167 15338 +167 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15338 +166 15339 +166 15339 +166 15339 +166 15339 +166 15339 +166 15339 +165 15339 +165 15339 +165 15339 +165 15339 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +165 15340 +164 15340 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15341 +164 15342 +164 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15342 +163 15343 +163 15343 +163 15343 +163 15343 +163 15343 +162 15343 +162 15343 +162 15343 +162 15343 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +162 15344 +161 15344 +161 15344 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15345 +161 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15346 +160 15347 +160 15347 +160 15347 +160 15347 +159 15347 +159 15347 +159 15347 +159 15347 +159 15347 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +159 15348 +158 15348 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15349 +158 15350 +158 15350 +158 15350 +157 15350 +157 15350 +157 15350 +157 15350 +157 15350 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +157 15351 +156 15351 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15352 +156 15353 +156 15353 +156 15353 +155 15353 +155 15353 +155 15353 +155 15353 +155 15353 +155 15354 +155 15354 +155 15354 +155 15354 +155 15354 +155 15354 +154 15354 +154 15354 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15355 +154 15356 +154 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15356 +153 15357 +153 15357 +153 15357 +153 15357 +153 15357 +152 15357 +152 15357 +152 15357 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15358 +152 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15359 +151 15360 +151 15360 +151 15360 +150 15360 +150 15360 +150 15360 +150 15360 +150 15360 +150 15361 +150 15361 +150 15361 +150 15361 +150 15361 +150 15361 +149 15361 +149 15361 +149 15361 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +149 15362 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15363 +148 15364 +148 15364 +148 15364 +147 15364 +147 15364 +147 15364 +147 15364 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +147 15365 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15366 +146 15367 +146 15367 +146 15367 +146 15367 +146 15367 +145 15367 +145 15367 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15368 +145 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15369 +144 15370 +144 15370 +144 15370 +144 15370 +144 15370 +144 15370 +143 15370 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15371 +143 15372 +143 15372 +143 15372 +142 15372 +142 15372 +142 15372 +142 15372 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +142 15373 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15374 +141 15375 +141 15375 +141 15375 +141 15375 +140 15375 +140 15375 +140 15375 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15376 +140 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15377 +139 15378 +139 15378 +139 15378 +139 15378 +138 15378 +138 15378 +138 15378 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +138 15379 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15380 +137 15381 +137 15381 +137 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15381 +136 15382 +136 15382 +136 15382 +136 15382 +136 15382 +135 15382 +135 15382 +135 15382 +135 15382 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +135 15383 +134 15383 +134 15383 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +134 15384 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +133 15385 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15386 +132 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +131 15387 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +130 15388 +129 15388 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +129 15389 +128 15389 +128 15389 +128 15389 +128 15390 +128 15390 +128 15390 +128 15390 +128 15390 +128 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15390 +127 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +126 15391 +125 15391 +125 15391 +125 15391 +125 15391 +125 15392 +125 15392 +125 15392 +125 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +124 15392 +123 15392 +123 15392 +123 15392 +123 15392 +123 15393 +123 15393 +123 15393 +123 15393 +123 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +122 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15393 +121 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +120 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +119 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +118 15394 +117 15394 +117 15394 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +117 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +116 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +115 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +114 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +113 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +112 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +111 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +110 15395 +109 15395 +109 15395 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +109 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +108 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15394 +107 15393 +107 15393 +107 15393 +107 15393 +107 15393 +107 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +106 15393 +105 15393 +105 15393 +105 15393 +105 15393 +105 15393 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +104 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15391 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +103 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15390 +102 15389 +102 15389 +102 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +101 15389 +100 15389 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +100 15388 +99 15388 +99 15388 +99 15388 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +99 15387 +98 15387 +98 15387 +98 15387 +98 15387 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +98 15386 +97 15386 +97 15386 +97 15386 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +97 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +96 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15385 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +95 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +94 15384 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +93 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +91 15382 +91 15382 +91 15382 +91 15382 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15380 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +90 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15375 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +89 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15376 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15377 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15378 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +90 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15379 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15380 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15381 +91 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15382 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15383 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +92 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15384 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15385 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15386 +93 15387 +93 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15387 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +94 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15388 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +95 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +96 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +97 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +98 15389 +99 15389 +99 15389 +99 15389 +99 15389 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +99 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15390 +100 15391 +100 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +101 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +102 15391 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +103 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +104 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +105 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +106 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15392 +107 15393 +107 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +108 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +109 15393 +110 15393 +110 15393 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +110 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +111 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +112 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +113 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +114 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +115 15392 +116 15392 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +116 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +117 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +118 15391 +119 15391 +119 15391 +119 15391 +119 15391 +119 15391 +119 15390 +119 15390 +119 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +120 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15390 +121 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +122 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15389 +123 15388 +123 15388 +123 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +124 15388 +125 15388 +125 15388 +125 15388 +125 15388 +125 15387 +125 15387 +125 15387 +125 15387 +125 15387 +125 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15387 +126 15386 +126 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +127 15386 +128 15386 +128 15386 +128 15386 +128 15385 +128 15385 +128 15385 +128 15385 +128 15385 +128 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15385 +129 15384 +129 15384 +129 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +130 15384 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +131 15383 +132 15383 +132 15383 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +132 15382 +133 15382 +133 15382 +133 15382 +133 15382 +133 15381 +133 15381 +133 15381 +133 15381 +133 15381 +134 15381 +134 15381 +134 15381 +134 15381 +134 15381 +134 15380 +134 15380 +134 15380 +134 15380 +134 15380 +135 15380 +135 15380 +135 15380 +135 15380 +135 15380 +135 15379 +135 15379 +135 15379 +135 15379 +135 15379 +136 15379 +136 15379 +136 15379 +136 15379 +136 15379 +136 15378 +136 15378 +136 15378 +136 15378 +136 15378 +137 15378 +137 15378 +137 15378 +137 15378 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +137 15377 +138 15377 +138 15377 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +138 15376 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15375 +139 15374 +139 15374 +139 15374 +140 15374 +140 15374 +140 15374 +140 15374 +140 15374 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +140 15373 +141 15373 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15372 +141 15371 +141 15371 +141 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15371 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +142 15370 +143 15370 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15369 +143 15368 +143 15368 +143 15368 +143 15368 +144 15368 +144 15368 +144 15368 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15367 +144 15366 +144 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15366 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +145 15365 +146 15365 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15364 +146 15363 +146 15363 +146 15363 +146 15363 +146 15363 +147 15363 +147 15363 +147 15363 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15362 +147 15361 +147 15361 +147 15361 +148 15361 +148 15361 +148 15361 +148 15361 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15360 +148 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15359 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +149 15358 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15357 +150 15356 +150 15356 +150 15356 +150 15356 +150 15356 +150 15356 +151 15356 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15355 +151 15354 +151 15354 +151 15354 +152 15354 +152 15354 +152 15354 +152 15354 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +152 15353 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15352 +153 15351 +153 15351 +153 15351 +153 15351 +153 15351 +154 15351 +154 15351 +154 15351 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15350 +154 15349 +154 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15349 +155 15348 +155 15348 +155 15348 +155 15348 +155 15348 +155 15348 +156 15348 +156 15348 +156 15348 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15347 +156 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15346 +157 15345 +157 15345 +157 15345 +157 15345 +157 15345 +158 15345 +158 15345 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15344 +158 15343 +158 15343 +158 15343 +159 15343 +159 15343 +159 15343 +159 15343 +159 15343 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +159 15342 +160 15342 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15341 +160 15340 +160 15340 +160 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15340 +161 15339 +161 15339 +161 15339 +161 15339 +161 15339 +161 15339 +162 15339 +162 15339 +162 15339 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15338 +162 15337 +162 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15337 +163 15336 +163 15336 +163 15336 +163 15336 +163 15336 +164 15336 +164 15336 +164 15336 +164 15336 +164 15336 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +164 15335 +165 15335 +165 15335 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15334 +165 15333 +165 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15333 +166 15332 +166 15332 +166 15332 +166 15332 +167 15332 +167 15332 +167 15332 +167 15332 +167 15332 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +167 15331 +168 15331 +168 15331 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +168 15330 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15329 +169 15328 +169 15328 +169 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15328 +170 15327 +170 15327 +170 15327 +170 15327 +171 15327 +171 15327 +171 15327 +171 15327 +171 15327 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +171 15326 +172 15326 +172 15326 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +172 15325 +173 15325 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +173 15324 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15323 +174 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15322 +175 15321 +175 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15321 +176 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15320 +177 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15319 +178 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15318 +179 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15317 +180 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15316 +181 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +182 15315 +183 15315 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +183 15314 +184 15314 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +184 15313 +185 15313 +185 15313 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +185 15312 +186 15312 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15311 +186 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15310 +187 15309 +187 15309 +187 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15309 +188 15308 +188 15308 +188 15308 +188 15308 +188 15308 +188 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15308 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +189 15307 +190 15307 +190 15307 +190 15307 +190 15307 +190 15307 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +190 15306 +191 15306 +191 15306 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +191 15305 +192 15305 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15304 +192 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15303 +193 15302 +193 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15302 +194 15301 +194 15301 +194 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15301 +195 15300 +195 15300 +195 15300 +195 15300 +195 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15300 +196 15299 +196 15299 +196 15299 +196 15299 +196 15299 +196 15299 +197 15299 +197 15299 +197 15299 +197 15299 +197 15299 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +197 15298 +198 15298 +198 15298 +198 15298 +198 15298 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +198 15297 +199 15297 +199 15297 +199 15297 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +199 15296 +200 15296 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +200 15295 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +201 15294 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15293 +202 15292 +202 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15292 +203 15291 +203 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15291 +204 15290 +204 15290 +204 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15290 +205 15289 +205 15289 +205 15289 +205 15289 +205 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15289 +206 15288 +206 15288 +206 15288 +206 15288 +206 15288 +206 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15288 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +207 15287 +208 15287 +208 15287 +208 15287 +208 15287 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +208 15286 +209 15286 +209 15286 +209 15286 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +209 15285 +210 15285 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +210 15284 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +211 15283 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15282 +212 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15281 +213 15280 +213 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15280 +214 15279 +214 15279 +214 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15279 +215 15278 +215 15278 +215 15278 +215 15278 +215 15278 +216 15278 +216 15278 +216 15278 +216 15278 +216 15278 +216 15277 +216 15277 +216 15277 +216 15277 +216 15277 +216 15277 +217 15277 +217 15277 +217 15277 +217 15277 +217 15277 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +217 15276 +218 15276 +218 15276 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +218 15275 +219 15275 +219 15275 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +219 15274 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15273 +220 15272 +220 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15272 +221 15271 +221 15271 +221 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15271 +222 15270 +222 15270 +222 15270 +222 15270 +223 15270 +223 15270 +223 15270 +223 15270 +223 15270 +223 15269 +223 15269 +223 15269 +223 15269 +223 15269 +223 15269 +224 15269 +224 15269 +224 15269 +224 15269 +224 15268 +224 15268 +224 15268 +224 15268 +224 15268 +224 15268 +225 15268 +225 15268 +225 15268 +225 15268 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +225 15267 +226 15267 +226 15267 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +226 15266 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15265 +227 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15264 +228 15263 +228 15263 +228 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15263 +229 15262 +229 15262 +229 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15262 +230 15261 +230 15261 +230 15261 +230 15261 +230 15261 +231 15261 +231 15261 +231 15261 +231 15261 +231 15261 +231 15260 +231 15260 +231 15260 +231 15260 +231 15260 +232 15260 +232 15260 +232 15260 +232 15260 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +232 15259 +233 15259 +233 15259 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +233 15258 +234 15258 +234 15258 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +234 15257 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15256 +235 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15255 +236 15254 +236 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15254 +237 15253 +237 15253 +237 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15253 +238 15252 +238 15252 +238 15252 +238 15252 +239 15252 +239 15252 +239 15252 +239 15252 +239 15252 +239 15251 +239 15251 +239 15251 +239 15251 +239 15251 +240 15251 +240 15251 +240 15251 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +240 15250 +241 15250 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +241 15249 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15248 +242 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15247 +243 15246 +243 15246 +243 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15246 +244 15245 +244 15245 +244 15245 +245 15245 +245 15245 +245 15245 +245 15245 +245 15245 +245 15244 +245 15244 +245 15244 +245 15244 +245 15244 +246 15244 +246 15244 +246 15244 +246 15244 +246 15243 +246 15243 +246 15243 +246 15243 +246 15243 +246 15243 +247 15243 +247 15243 +247 15243 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +247 15242 +248 15242 +248 15242 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +248 15241 +249 15241 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15240 +249 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15239 +250 15238 +250 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15238 +251 15237 +251 15237 +251 15237 +251 15237 +252 15237 +252 15237 +252 15237 +252 15237 +252 15237 +252 15236 +252 15236 +252 15236 +252 15236 +252 15236 +253 15236 +253 15236 +253 15236 +253 15236 +253 15235 +253 15235 +253 15235 +253 15235 +253 15235 +253 15235 +254 15235 +254 15235 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +254 15234 +255 15234 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15233 +255 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15232 +256 15231 +256 15231 +256 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15231 +257 15230 +257 15230 +257 15230 +257 15230 +258 15230 +258 15230 +258 15230 +258 15230 +258 15229 +258 15229 +258 15229 +258 15229 +258 15229 +258 15229 +259 15229 +259 15229 +259 15229 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +259 15228 +260 15228 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15227 +260 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15226 +261 15225 +261 15225 +261 15225 +262 15225 +262 15225 +262 15225 +262 15225 +262 15225 +262 15224 +262 15224 +262 15224 +262 15224 +262 15224 +263 15224 +263 15224 +263 15224 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +263 15223 +264 15223 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15222 +264 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15221 +265 15220 +265 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15220 +266 15219 +266 15219 +266 15219 +266 15219 +266 15219 +267 15219 +267 15219 +267 15219 +267 15219 +267 15218 +267 15218 +267 15218 +267 15218 +267 15218 +267 15218 +268 15218 +268 15218 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +268 15217 +269 15217 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15216 +269 15215 +269 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15215 +270 15214 +270 15214 +270 15214 +271 15214 +271 15214 +271 15214 +271 15214 +271 15214 +271 15213 +271 15213 +271 15213 +271 15213 +271 15213 +271 15213 +272 15213 +272 15213 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15212 +272 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15211 +273 15210 +273 15210 +273 15210 +273 15210 +274 15210 +274 15210 +274 15210 +274 15210 +274 15209 +274 15209 +274 15209 +274 15209 +274 15209 +274 15209 +275 15209 +275 15209 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15208 +275 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15207 +276 15206 +276 15206 +276 15206 +276 15206 +277 15206 +277 15206 +277 15206 +277 15206 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +277 15205 +278 15205 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15204 +278 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15203 +279 15202 +279 15202 +279 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15202 +280 15201 +280 15201 +280 15201 +280 15201 +280 15201 +281 15201 +281 15201 +281 15201 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +281 15200 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15199 +282 15198 +282 15198 +282 15198 +283 15198 +283 15198 +283 15198 +283 15198 +283 15198 +283 15197 +283 15197 +283 15197 +283 15197 +283 15197 +283 15197 +284 15197 +284 15197 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15196 +284 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15195 +285 15194 +285 15194 +285 15194 +285 15194 +285 15194 +286 15194 +286 15194 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15193 +286 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15192 +287 15191 +287 15191 +287 15191 +287 15191 +287 15191 +287 15191 +288 15191 +288 15191 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15190 +288 15189 +288 15189 +289 15189 +289 15189 +289 15189 +289 15189 +289 15189 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +289 15188 +290 15187 +290 15187 +290 15187 +290 15187 +290 15187 +290 15187 +290 15186 +290 15186 +290 15186 +290 15186 +290 15186 +290 15186 +291 15186 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15185 +291 15184 +291 15184 +291 15184 +292 15184 +292 15184 +292 15184 +292 15183 +292 15183 +292 15183 +292 15183 +292 15183 +292 15183 +292 15182 +292 15182 +293 15182 +293 15182 +293 15182 +293 15182 +293 15182 +293 15181 +293 15181 +293 15181 +293 15181 +293 15181 +293 15181 +293 15180 +294 15180 +294 15180 +294 15180 +294 15180 +294 15180 +294 15179 +294 15179 +294 15179 +294 15179 +294 15179 +294 15179 +295 15178 +295 15178 +295 15178 +295 15178 +295 15178 +295 15178 +295 15177 +295 15177 +295 15177 +295 15177 +296 15177 +296 15176 +296 15176 +296 15176 +296 15176 +296 15176 +296 15176 +296 15175 +296 15175 +296 15175 +296 15175 +297 15175 +297 15175 +297 15174 +297 15174 +297 15174 +297 15174 +297 15174 +297 15173 +297 15173 +298 15173 +298 15173 +298 15173 +298 15173 +298 15172 +298 15172 +298 15172 +298 15172 +298 15172 +298 15171 +299 15171 +299 15171 +299 15171 +299 15171 +299 15171 +299 15170 +299 15170 +299 15170 +299 15170 +300 15170 +300 15169 +300 15169 +300 15169 +300 15169 +300 15169 +300 15168 +300 15168 +300 15168 +300 15168 +301 15168 +301 15167 +301 15167 +301 15167 +301 15167 +301 15167 +301 15166 +301 15166 +302 15166 +302 15166 +302 15166 +302 15165 +302 15165 +302 15165 +302 15165 +302 15165 +302 15164 +303 15164 +303 15164 +303 15164 +303 15164 +303 15163 +303 15163 +303 15163 +303 15163 +304 15163 +304 15162 +304 15162 +304 15162 +304 15162 +304 15162 +304 15161 +304 15161 +305 15161 +305 15161 +305 15161 +305 15160 +305 15160 +305 15160 +305 15160 +305 15160 +306 15159 +306 15159 +306 15159 +306 15159 +306 15158 +306 15158 +306 15158 +306 15158 +307 15158 +307 15157 +307 15157 +307 15157 +307 15157 +307 15157 +307 15156 +308 15156 +308 15156 +308 15156 +308 15155 +308 15155 +308 15155 +308 15155 +309 15155 +309 15154 +309 15154 +309 15154 +309 15154 +309 15153 +309 15153 +309 15153 +310 15153 +310 15153 +310 15152 +310 15152 +310 15152 +310 15152 +310 15151 +311 15151 +311 15151 +311 15151 +311 15151 +311 15150 +311 15150 +312 15150 +312 15150 +312 15149 +312 15149 +312 15149 +312 15149 +312 15149 +313 15148 +313 15148 +313 15148 +313 15148 +313 15147 +313 15147 +314 15147 +314 15147 +314 15146 +314 15146 +314 15146 +314 15146 +314 15145 +315 15145 +315 15145 +315 15145 +315 15144 +315 15144 +315 15144 +316 15144 +316 15144 +316 15143 +316 15143 +316 15143 +316 15143 +317 15142 +317 15142 +317 15142 +317 15142 +317 15141 +317 15141 +318 15141 +318 15141 +318 15140 +318 15140 +318 15140 +318 15140 +319 15139 +319 15139 +319 15139 +319 15139 +319 15138 +319 15138 +320 15138 +320 15138 +320 15137 +320 15137 +320 15137 +320 15137 +321 15136 +321 15136 +321 15136 +321 15135 +321 15135 +322 15135 +322 15135 +322 15134 +322 15134 +322 15134 +322 15134 +323 15133 +323 15133 +323 15133 +323 15133 +323 15132 +324 15132 +324 15132 +324 15132 +324 15131 +324 15131 +324 15131 +325 15130 +325 15130 +325 15130 +325 15130 +325 15129 +326 15129 +326 15129 +326 15129 +326 15128 +326 15128 +327 15128 +327 15127 +327 15127 +327 15127 +327 15127 +328 15126 +328 15126 +328 15126 +328 15126 +328 15125 +329 15125 +329 15125 +329 15124 +329 15124 +329 15124 +330 15124 +330 15123 +330 15123 +330 15123 +330 15123 +331 15122 +331 15122 +331 15122 +331 15121 +331 15121 +332 15121 +332 15121 +332 15120 +332 15120 +332 15120 +333 15119 +333 15119 +333 15119 +333 15119 +333 15118 +334 15118 +334 15118 +334 15117 +334 15117 +335 15117 +335 15117 +335 15116 +335 15116 +335 15116 +336 15116 +336 15115 +336 15115 +336 15115 +336 15114 +337 15114 +337 15114 +337 15114 +337 15113 +338 15113 +338 15113 +338 15112 +338 15112 +338 15112 +339 15112 +339 15111 +339 15111 +339 15111 +340 15110 +340 15110 +340 15110 +340 15109 +341 15109 +341 15109 +341 15109 +341 15108 +341 15108 +342 15108 +342 15107 +342 15107 +342 15107 +343 15107 +343 15106 +343 15106 +343 15106 +344 15105 +344 15105 +344 15105 +344 15105 +345 15104 +345 15104 +345 15104 +345 15103 +345 15103 +346 15103 +346 15103 +346 15102 +346 15102 +347 15102 +347 15101 +347 15101 +347 15101 +348 15101 +348 15100 +348 15100 +348 15100 +349 15099 +349 15099 +349 15099 +349 15098 +350 15098 +350 15098 +350 15098 +350 15097 +351 15097 +351 15097 +351 15096 +351 15096 +352 15096 +352 15096 +352 15095 +352 15095 +353 15095 +353 15094 +353 15094 +353 15094 +354 15093 +354 15093 +354 15093 +355 15093 +355 15092 +355 15092 +355 15092 +356 15092 +356 15091 +356 15091 +356 15091 +357 15091 +357 15090 +357 15090 +357 15090 +358 15090 +358 15089 +358 15089 +359 15089 +359 15089 +359 15088 +359 15088 +360 15088 +360 15088 +360 15087 +360 15087 +361 15087 +361 15087 +361 15086 +362 15086 +362 15086 +362 15086 +362 15085 +363 15085 +363 15085 +363 15085 +363 15084 +364 15084 +364 15084 +364 15083 +365 15083 +365 15083 +365 15083 +365 15082 +366 15082 +366 15082 +366 15082 +367 15081 +367 15081 +367 15081 +367 15081 +368 15080 +368 15080 +368 15080 +369 15080 +369 15079 +369 15079 +369 15079 +370 15079 +370 15078 +370 15078 +371 15078 +371 15078 +371 15077 +371 15077 +372 15077 +372 15076 +372 15076 +373 15076 +373 15076 +373 15075 +373 15075 +374 15075 +374 15075 +374 15074 +375 15074 +375 15074 +375 15074 +376 15073 +376 15073 +376 15073 +376 15072 +377 15072 +377 15072 +377 15072 +378 15071 +378 15071 +378 15071 +378 15071 +379 15070 +379 15070 +379 15070 +380 15070 +380 15069 +380 15069 +381 15069 +381 15068 +381 15068 +381 15068 +382 15068 +382 15067 +382 15067 +383 15067 +383 15067 +383 15066 +384 15066 +384 15066 +384 15065 +384 15065 +385 15065 +385 15065 +385 15064 +386 15064 +386 15064 +386 15064 +387 15063 +387 15063 +387 15063 +388 15062 +388 15062 +388 15062 +388 15062 +389 15061 +389 15061 +389 15061 +390 15060 +390 15060 +390 15060 +391 15060 +391 15059 +391 15059 +392 15059 +392 15059 +392 15058 +392 15058 +393 15058 +393 15057 +393 15057 +394 15057 +394 15057 +394 15056 +395 15056 +395 15056 +395 15055 +396 15055 +396 15055 +396 15055 +397 15054 +397 15054 +397 15054 +397 15053 +398 15053 +398 15053 +398 15053 +399 15052 +399 15052 +399 15052 +400 15051 +400 15051 +400 15051 +401 15051 +401 15050 +401 15050 +402 15050 +402 15049 +402 15049 +403 15049 +403 15049 +403 15048 +403 15048 +404 15048 +404 15047 +404 15047 +405 15047 +405 15046 +405 15046 +406 15046 +406 15046 +406 15045 +407 15045 +407 15045 +407 15044 +408 15044 +408 15044 +408 15044 +409 15043 +409 15043 +409 15043 +410 15042 +410 15042 +410 15042 +411 15041 +411 15041 +411 15041 +411 15041 +412 15040 +412 15040 +412 15040 +413 15039 +413 15039 +413 15039 +414 15038 +414 15038 +414 15038 +415 15038 +415 15037 +415 15037 +416 15037 +416 15036 +416 15036 +417 15036 +417 15035 +417 15035 +418 15035 +418 15035 +418 15034 +419 15034 +419 15034 +419 15033 +420 15033 +420 15033 +420 15032 +421 15032 +421 15032 +421 15032 +422 15031 +422 15031 +422 15031 +423 15030 +423 15030 +423 15030 +424 15029 +424 15029 +424 15029 +425 15029 +425 15028 +425 15028 +426 15028 +426 15027 +426 15027 +427 15027 +427 15026 +427 15026 +428 15026 +428 15025 +428 15025 +429 15025 +429 15025 +429 15024 +430 15024 +430 15024 +430 15023 +431 15023 +431 15023 +431 15022 +432 15022 +432 15022 +432 15022 +433 15021 +433 15021 +433 15021 +434 15020 +434 15020 +434 15020 +435 15019 +435 15019 +435 15019 +436 15018 +436 15018 +436 15018 +437 15017 +437 15017 +437 15017 +438 15017 +438 15016 +438 15016 +439 15016 +439 15015 +439 15015 +440 15015 +440 15014 +441 15014 +441 15014 +441 15013 +442 15013 +442 15013 +442 15013 +443 15012 +443 15012 +443 15012 +444 15011 +444 15011 +444 15011 +445 15010 +445 15010 +445 15010 +446 15009 +446 15009 +446 15009 +447 15008 +447 15008 +447 15008 +448 15007 +448 15007 +448 15007 +449 15006 +449 15006 +449 15006 +450 15006 +450 15005 +451 15005 +451 15005 +451 15004 +452 15004 +452 15004 +452 15003 +453 15003 +453 15003 +453 15002 +454 15002 +454 15002 +454 15001 +455 15001 +455 15001 +455 15000 +456 15000 +456 15000 +456 14999 +457 14999 +457 14999 +457 14998 +458 14998 +458 14998 +459 14997 +459 14997 +459 14997 +460 14996 +460 14996 +460 14996 +461 14995 +461 14995 +461 14995 +462 14994 +462 14994 +462 14994 +463 14993 +463 14993 +463 14993 +464 14992 +464 14992 +464 14992 +465 14991 +465 14991 +466 14991 +466 14990 +466 14990 +467 14990 +467 14989 +467 14989 +468 14989 +468 14988 +468 14988 +469 14988 +469 14987 +469 14987 +470 14987 +470 14986 +470 14986 +471 14986 +471 14985 +472 14985 +472 14985 +472 14984 +473 14984 +473 14984 +473 14983 +474 14983 +474 14983 +474 14982 +475 14982 +475 14982 +475 14981 +476 14981 +476 14981 +476 14980 +477 14980 +477 14980 +478 14979 +478 14979 +478 14979 +479 14978 +479 14978 +479 14978 +480 14977 +480 14977 +480 14977 +481 14976 +481 14976 +481 14975 +482 14975 +482 14975 +482 14974 +483 14974 +483 14974 +484 14973 +484 14973 +484 14973 +485 14972 +485 14972 +485 14972 +486 14971 +486 14971 +486 14971 +487 14970 +487 14970 +488 14969 +488 14969 +488 14969 +489 14968 +489 14968 +489 14968 +490 14967 +490 14967 +490 14967 +491 14966 +491 14966 +491 14965 +492 14965 +492 14965 +493 14964 +493 14964 +493 14964 +494 14963 +494 14963 +494 14963 +495 14962 +495 14962 +495 14961 +496 14961 +496 14961 +497 14960 +497 14960 +497 14960 +498 14959 +498 14959 +498 14958 +499 14958 +499 14958 +499 14957 +500 14957 +500 14957 +501 14956 +501 14956 +501 14955 +502 14955 +502 14955 +502 14954 +503 14954 +503 14954 +504 14953 +504 14953 +504 14952 +505 14952 +505 14952 +505 14951 +506 14951 +506 14951 +507 14950 +507 14950 +507 14949 +508 14949 +508 14949 +508 14948 +509 14948 +509 14947 +510 14947 +510 14947 +510 14946 +511 14946 +511 14946 +512 14945 +512 14945 +512 14944 +513 14944 +513 14944 +513 14943 +514 14943 +514 14942 +515 14942 +515 14942 +515 14941 +516 14941 +516 14940 +517 14940 +517 14940 +517 14939 +518 14939 +518 14938 +519 14938 +519 14938 +519 14937 +520 14937 +520 14936 +521 14936 +521 14936 +521 14935 +522 14935 +522 14934 +523 14934 +523 14934 +523 14933 +524 14933 +524 14932 +525 14932 +525 14932 +525 14931 +526 14931 +526 14930 +527 14930 +527 14930 +527 14929 +528 14929 +528 14928 +529 14928 +529 14928 +529 14927 +530 14927 +530 14926 +531 14926 +531 14926 +531 14925 +532 14925 +532 14924 +533 14924 +533 14924 +533 14923 +534 14923 +534 14922 +535 14922 +535 14922 +536 14921 +536 14921 +536 14920 +537 14920 +537 14919 +538 14919 +538 14919 +538 14918 +539 14918 +539 14918 +540 14917 +540 14917 +540 14916 +541 14916 +541 14916 +542 14915 +542 14915 +543 14915 +543 14914 +543 14914 +544 14914 +544 14913 +545 14913 +545 14912 +546 14912 +546 14912 +546 14911 +547 14911 +547 14911 +548 14910 +548 14910 +548 14909 +549 14909 +549 14909 +550 14908 +550 14908 +551 14908 +551 14907 +551 14907 +552 14906 +552 14906 +553 14906 +553 14905 +554 14905 +554 14905 +554 14904 +555 14904 +555 14903 +556 14903 +556 14903 +557 14902 +557 14902 +557 14902 +558 14901 +558 14901 +559 14900 +559 14900 +560 14900 +560 14899 +560 14899 +561 14898 +561 14898 +562 14898 +562 14897 +563 14897 +563 14897 +563 14896 +564 14896 +564 14895 +565 14895 +565 14895 +566 14894 +566 14894 +566 14893 +567 14893 +567 14893 +568 14892 +568 14892 +569 14891 +569 14891 +569 14891 +570 14890 +570 14890 +571 14889 +571 14889 +572 14889 +572 14888 +572 14888 +573 14887 +573 14887 +574 14887 +574 14886 +575 14886 +575 14885 +576 14885 +576 14884 +576 14884 +577 14884 +577 14883 +578 14883 +578 14882 +579 14882 +579 14882 +580 14881 +580 14881 +580 14880 +581 14880 +581 14879 +582 14879 +582 14879 +583 14878 +583 14878 +584 14877 +584 14877 +584 14876 +585 14876 +585 14876 +586 14875 +586 14875 +587 14874 +587 14874 +588 14873 +588 14873 +588 14873 +589 14872 +589 14872 +590 14871 +590 14871 +591 14870 +591 14870 +592 14870 +592 14869 +592 14869 +593 14868 +593 14868 +594 14867 +594 14867 +595 14867 +595 14866 +596 14866 +596 14865 +597 14865 +597 14864 +597 14864 +598 14863 +598 14863 +599 14863 +599 14862 +600 14862 +600 14861 +601 14861 +601 14860 +602 14860 +602 14859 +603 14859 +603 14858 +603 14858 +604 14858 +604 14857 +605 14857 +605 14856 +606 14856 +606 14855 +607 14855 +607 14854 +608 14854 +608 14853 +609 14853 +609 14852 +610 14852 +610 14852 +611 14851 +611 14851 +612 14850 +612 14850 +613 14849 +613 14849 +613 14848 +614 14848 +614 14847 +615 14847 +615 14846 +616 14846 +616 14845 +617 14845 +617 14844 +618 14844 +618 14844 +619 14843 +619 14843 +620 14842 +620 14842 +621 14841 +621 14841 +622 14840 +622 14840 +623 14839 +623 14839 +624 14838 +624 14838 +625 14837 +625 14837 +626 14836 +626 14836 +627 14835 +627 14835 +628 14834 +628 14834 +629 14833 +629 14833 +630 14832 +630 14832 +631 14832 +631 14831 +632 14831 +632 14830 +633 14830 +633 14829 +634 14829 +634 14828 +635 14828 +635 14827 +636 14827 +637 14826 +637 14826 +638 14825 +638 14825 +639 14824 +639 14824 +640 14823 +640 14823 +641 14822 +641 14822 +642 14821 +642 14821 +643 14820 +643 14820 +644 14819 +644 14819 +645 14818 +645 14818 +646 14817 +646 14817 +647 14816 +648 14816 +648 14815 +649 14815 +649 14814 +650 14814 +650 14813 +651 14813 +651 14812 +652 14812 +652 14811 +653 14811 +653 14810 +654 14810 +655 14809 +655 14809 +656 14808 +656 14808 +657 14807 +657 14807 +658 14806 +658 14806 +659 14805 +659 14805 +660 14804 +661 14803 +661 14803 +662 14802 +662 14802 +663 14801 +663 14801 +664 14800 +664 14800 +665 14799 +666 14799 +666 14798 +667 14798 +667 14797 +668 14797 +668 14796 +669 14796 +669 14795 +670 14795 +671 14794 +671 14794 +672 14793 +672 14793 +673 14792 +673 14792 +674 14791 +675 14790 +675 14790 +676 14789 +676 14789 +677 14788 +677 14788 +678 14787 +679 14787 +679 14786 +680 14786 +680 14785 +681 14785 +681 14784 +682 14784 +683 14783 +683 14782 +684 14782 +684 14781 +685 14781 +685 14780 +686 14780 +687 14779 +687 14779 +688 14778 +688 14778 +689 14777 +690 14776 +690 14776 +691 14775 +691 14775 +692 14774 +693 14774 +693 14773 +694 14773 +694 14772 +695 14771 +695 14771 +696 14770 +697 14770 +697 14769 +698 14769 +698 14768 +699 14768 +700 14767 +700 14766 +701 14766 +701 14765 +702 14765 +703 14764 +703 14764 +704 14763 +704 14763 +705 14762 +706 14761 +706 14761 +707 14760 +707 14760 +708 14759 +709 14759 +709 14758 +710 14757 +711 14757 +711 14756 +712 14756 +712 14755 +713 14755 +714 14754 +714 14753 +715 14753 +715 14752 +716 14752 +717 14751 +717 14751 +718 14750 +718 14749 +719 14749 +720 14748 +720 14748 +721 14747 +722 14747 +722 14746 +723 14745 +723 14745 +724 14744 +725 14744 +725 14743 +726 14743 +727 14742 +727 14741 +728 14741 +728 14740 +729 14740 +730 14739 +730 14739 +731 14738 +732 14737 +732 14737 +733 14736 +733 14736 +734 14735 +735 14734 +735 14734 +736 14733 +737 14733 +737 14732 +738 14731 +738 14731 +739 14730 +740 14730 +740 14729 +741 14729 +742 14728 +742 14727 +743 14727 +744 14726 +744 14726 +745 14725 +746 14724 +746 14724 +747 14723 +747 14723 +748 14722 +749 14721 +749 14721 +750 14720 +751 14720 +751 14719 +752 14719 +753 14718 +753 14717 +754 14717 +755 14716 +755 14716 +756 14715 +757 14714 +757 14714 +758 14713 +759 14713 +759 14712 +760 14711 +761 14711 +761 14710 +762 14709 +763 14709 +763 14708 +764 14708 +765 14707 +765 14706 +766 14706 +767 14705 +767 14705 +768 14704 +769 14703 +769 14703 +770 14702 +771 14702 +771 14701 +772 14700 +773 14700 +773 14699 +774 14699 +775 14698 +775 14697 +776 14697 +777 14696 +777 14695 +778 14695 +779 14694 +779 14694 +780 14693 +781 14692 +781 14692 +782 14691 +783 14691 +783 14690 +784 14689 +785 14689 +785 14688 +786 14687 +787 14687 +787 14686 +788 14686 +789 14685 +790 14684 +790 14684 +791 14683 +792 14683 +792 14682 +793 14681 +794 14681 +794 14680 +795 14679 +796 14679 +796 14678 +797 14678 +798 14677 +798 14676 +799 14676 +800 14675 +800 14674 +801 14674 +802 14673 +803 14673 +803 14672 +804 14671 +805 14671 +805 14670 +806 14669 +807 14669 +807 14668 +808 14667 +809 14667 +810 14666 +810 14666 +811 14665 +812 14664 +812 14664 +813 14663 +814 14662 +814 14662 +815 14661 +816 14660 +817 14660 +817 14659 +818 14659 +819 14658 +819 14657 +820 14657 +821 14656 +821 14655 +822 14655 +823 14654 +824 14653 +824 14653 +825 14652 +826 14651 +826 14651 +827 14650 +828 14650 +829 14649 +829 14648 +830 14648 +831 14647 +831 14646 +832 14646 +833 14645 +833 14644 +834 14644 +835 14643 +836 14642 +836 14642 +837 14641 +838 14640 +838 14640 +839 14639 +840 14638 +841 14638 +841 14637 +842 14637 +843 14636 +844 14635 +844 14635 +845 14634 +846 14633 +846 14633 +847 14632 +848 14631 +849 14631 +849 14630 +850 14629 +851 14629 +851 14628 +852 14627 +853 14627 +854 14626 +854 14625 +855 14625 +856 14624 +856 14623 +857 14623 +858 14622 +859 14621 +859 14621 +860 14620 +861 14619 +862 14619 +862 14618 +863 14617 +864 14617 +865 14616 +865 14615 +866 14615 +867 14614 +867 14613 +868 14613 +869 14612 +870 14611 +870 14611 +871 14610 +872 14609 +873 14609 +873 14608 +874 14607 +875 14607 +876 14606 +876 14605 +877 14605 +878 14604 +878 14603 +879 14603 +880 14602 +881 14601 +881 14601 +882 14600 +883 14599 +884 14599 +884 14598 +885 14597 +886 14597 +887 14596 +887 14595 +888 14595 +889 14594 +890 14593 +890 14593 +891 14592 +892 14591 +893 14591 +893 14590 +894 14589 +895 14589 +896 14588 +896 14587 +897 14587 +898 14586 +899 14585 +899 14585 +900 14584 +901 14583 +902 14583 +902 14582 +903 14581 +904 14581 +905 14580 +905 14579 +906 14579 +907 14578 +908 14577 +908 14577 +909 14576 +910 14575 +911 14575 +911 14574 +912 14573 +913 14573 +914 14572 +914 14571 +915 14571 +916 14570 +917 14569 +918 14569 +918 14568 +919 14567 +920 14567 +921 14566 +921 14565 +922 14565 +923 14564 +924 14563 +924 14563 +925 14562 +926 14561 +927 14561 +927 14560 +928 14559 +929 14559 +930 14558 +931 14557 +931 14557 +932 14556 +933 14555 +934 14554 +934 14554 +935 14553 +936 14552 +937 14552 +938 14551 +938 14550 +939 14550 +940 14549 +941 14548 +941 14547 +942 14547 +943 14546 +944 14545 +945 14545 +945 14544 +946 14543 +947 14542 +948 14542 +949 14541 +949 14540 +950 14539 +951 14539 +952 14538 +952 14537 +953 14537 +954 14536 +955 14535 +956 14534 +956 14534 +957 14533 +958 14532 +959 14531 +960 14531 +960 14530 +961 14529 +962 14528 +963 14528 +964 14527 +964 14526 +965 14526 +966 14525 +967 14524 +968 14523 +968 14523 +969 14522 +970 14521 +971 14520 +972 14520 +972 14519 +973 14518 +974 14517 +975 14516 +976 14516 +976 14515 +977 14514 +978 14513 +979 14513 +980 14512 +980 14511 +981 14510 +982 14510 +983 14509 +984 14508 +984 14507 +985 14507 +986 14506 +987 14505 +988 14504 +989 14503 +989 14503 +990 14502 +991 14501 +992 14500 +993 14500 +993 14499 +994 14498 +995 14497 +996 14496 +997 14496 +998 14495 +998 14494 +999 14493 +1000 14492 +1001 14492 +1002 14491 +1002 14490 +1003 14489 +1004 14489 +1005 14488 +1006 14487 +1007 14486 +1007 14485 +1008 14485 +1009 14484 +1010 14483 +1011 14482 +1012 14481 +1012 14481 +1013 14480 +1014 14479 +1015 14478 +1016 14477 +1017 14476 +1017 14476 +1018 14475 +1019 14474 +1020 14473 +1021 14472 +1022 14472 +1023 14471 +1023 14470 +1024 14469 +1025 14468 +1026 14467 +1027 14467 +1028 14466 +1028 14465 +1029 14464 +1030 14463 +1031 14462 +1032 14462 +1033 14461 +1034 14460 +1034 14459 +1035 14458 +1036 14457 +1037 14457 +1038 14456 +1039 14455 +1039 14454 +1040 14453 +1041 14452 +1042 14452 +1043 14451 +1044 14450 +1045 14449 +1045 14448 +1046 14447 +1047 14446 +1048 14446 +1049 14445 +1050 14444 +1051 14443 +1051 14442 +1052 14441 +1053 14440 +1054 14440 +1055 14439 +1056 14438 +1057 14437 +1058 14436 +1058 14435 +1059 14434 +1060 14433 +1061 14433 +1062 14432 +1063 14431 +1064 14430 +1064 14429 +1065 14428 +1066 14427 +1067 14426 +1068 14425 +1069 14425 +1070 14424 +1071 14423 +1071 14422 +1072 14421 +1073 14420 +1074 14419 +1075 14418 +1076 14417 +1077 14416 +1078 14415 +1078 14415 +1079 14414 +1080 14413 +1081 14412 +1082 14411 +1083 14410 +1084 14409 +1085 14408 +1085 14407 +1086 14406 +1087 14405 +1088 14404 +1089 14403 +1090 14402 +1091 14402 +1092 14401 +1093 14400 +1093 14399 +1094 14398 +1095 14397 +1096 14396 +1097 14395 +1098 14394 +1099 14393 +1100 14392 +1101 14391 +1101 14390 +1102 14389 +1103 14388 +1104 14387 +1105 14386 +1106 14385 +1107 14384 +1108 14383 +1109 14382 +1109 14381 +1110 14380 +1111 14379 +1112 14378 +1113 14377 +1114 14377 +1115 14376 +1116 14375 +1117 14374 +1118 14373 +1118 14372 +1119 14371 +1120 14370 +1121 14369 +1122 14368 +1123 14366 +1124 14365 +1125 14364 +1126 14363 +1127 14362 +1128 14361 +1128 14360 +1129 14359 +1130 14358 +1131 14357 +1132 14356 +1133 14355 +1134 14354 +1135 14353 +1136 14352 +1137 14351 +1138 14350 +1139 14349 +1139 14348 +1140 14347 +1141 14346 +1142 14345 +1143 14344 +1144 14343 +1145 14342 +1146 14340 +1147 14339 +1148 14338 +1149 14337 +1150 14336 +1151 14335 +1152 14334 +1152 14333 +1153 14332 +1154 14331 +1155 14330 +1156 14329 +1157 14327 +1158 14326 +1159 14325 +1160 14324 +1161 14323 +1162 14322 +1163 14321 +1164 14320 +1165 14319 +1166 14318 +1167 14316 +1167 14315 +1168 14314 +1169 14313 +1170 14312 +1171 14311 +1172 14310 +1173 14309 +1174 14307 +1175 14306 +1176 14305 +1177 14304 +1178 14303 +1179 14302 +1180 14301 +1181 14300 +1182 14298 +1183 14297 +1184 14296 +1185 14295 +1186 14294 +1187 14293 +1187 14292 +1188 14290 +1189 14289 +1190 14288 +1191 14287 +1192 14286 +1193 14285 +1194 14283 +1195 14282 +1196 14281 +1197 14280 +1198 14279 +1199 14278 +1200 14276 +1201 14275 +1202 14274 +1203 14273 +1204 14272 +1205 14271 +1206 14269 +1207 14268 +1208 14267 +1209 14266 +1210 14265 +1211 14264 +1212 14262 +1213 14261 +1214 14260 +1215 14259 +1216 14258 +1217 14256 +1218 14255 +1219 14254 +1220 14253 +1221 14252 +1222 14250 +1223 14249 +1224 14248 +1225 14247 +1226 14246 +1227 14244 +1228 14243 +1229 14242 +1230 14241 +1231 14239 +1232 14238 +1233 14237 +1234 14236 +1235 14234 +1236 14233 +1237 14232 +1238 14231 +1239 14230 +1240 14228 +1241 14227 +1242 14226 +1243 14225 +1244 14223 +1245 14222 +1246 14221 +1247 14220 +1248 14218 +1249 14217 +1250 14216 +1251 14215 +1252 14213 +1253 14212 +1255 14211 +1256 14209 +1257 14208 +1258 14207 +1259 14206 +1260 14204 +1261 14203 +1262 14202 +1263 14201 +1264 14199 +1265 14198 +1266 14197 +1267 14195 +1268 14194 +1269 14193 +1270 14192 +1271 14190 +1272 14189 +1273 14188 +1274 14186 +1276 14185 +1277 14184 +1278 14183 +1279 14181 +1280 14180 +1281 14179 +1282 14177 +1283 14176 +1284 14175 +1285 14173 +1286 14172 +1287 14171 +1288 14169 +1289 14168 +1291 14167 +1292 14165 +1293 14164 +1294 14163 +1295 14161 +1296 14160 +1297 14159 +1298 14157 +1299 14156 +1300 14155 +1301 14153 +1302 14152 +1304 14151 +1305 14149 +1306 14148 +1307 14147 +1308 14145 +1309 14144 +1310 14143 +1311 14141 +1312 14140 +1313 14139 +1314 14137 +1316 14136 +1317 14135 +1318 14133 +1319 14132 +1320 14130 +1321 14129 +1322 14128 +1323 14126 +1325 14125 +1326 14124 +1327 14122 +1328 14121 +1329 14119 +1330 14118 +1331 14117 +1332 14115 +1334 14114 +1335 14113 +1336 14111 +1337 14110 +1338 14108 +1339 14107 +1340 14106 +1342 14104 +1343 14103 +1344 14101 +1345 14100 +1346 14099 +1347 14097 +1349 14096 +1350 14094 +1351 14093 +1352 14092 +1353 14090 +1354 14089 +1356 14087 +1357 14086 +1358 14084 +1359 14083 +1360 14082 +1361 14080 +1363 14079 +1364 14077 +1365 14076 +1366 14075 +1367 14073 +1368 14072 +1370 14070 +1371 14069 +1372 14067 +1373 14066 +1374 14064 +1376 14063 +1377 14062 +1378 14060 +1379 14059 +1380 14057 +1382 14056 +1383 14054 +1384 14053 +1385 14051 +1386 14050 +1388 14049 +1389 14047 +1390 14046 +1391 14044 +1392 14043 +1394 14041 +1395 14040 +1396 14038 +1397 14037 +1399 14035 +1400 14034 +1401 14032 +1402 14031 +1403 14029 +1405 14028 +1406 14027 +1407 14025 +1408 14024 +1410 14022 +1411 14021 +1412 14019 +1413 14018 +1415 14016 +1416 14015 +1417 14013 +1418 14012 +1420 14010 +1421 14009 +1422 14007 +1423 14006 +1425 14004 +1426 14003 +1427 14001 +1428 14000 +1430 13998 +1431 13996 +1432 13995 +1433 13993 +1435 13992 +1436 13990 +1437 13989 +1438 13987 +1440 13986 +1441 13984 +1442 13983 +1443 13981 +1445 13980 +1446 13978 +1447 13977 +1448 13975 +1450 13974 +1451 13972 +1452 13971 +1454 13969 +1455 13967 +1456 13966 +1457 13964 +1459 13963 +1460 13961 +1461 13960 +1463 13958 +1464 13957 +1465 13955 +1466 13954 +1468 13952 +1469 13951 +1470 13949 +1472 13947 +1473 13946 +1474 13944 +1475 13943 +1477 13941 +1478 13940 +1479 13938 +1481 13937 +1482 13935 +1483 13934 +1484 13932 +1486 13930 +1487 13929 +1488 13927 +1490 13926 +1491 13924 +1492 13923 +1493 13921 +1495 13920 +1496 13918 +1497 13917 +1499 13915 +1500 13913 +1501 13912 +1502 13910 +1504 13909 +1505 13907 +1506 13906 +1508 13904 +1509 13902 +1510 13901 +1512 13899 +1513 13898 +1514 13896 +1515 13895 +1517 13893 +1518 13892 +1519 13890 +1521 13888 +1522 13887 +1523 13885 +1525 13884 +1526 13882 +1527 13881 +1528 13879 +1530 13877 +1531 13876 +1532 13874 +1534 13873 +1535 13871 +1536 13869 +1538 13868 +1539 13866 +1540 13865 +1541 13863 +1543 13862 +1544 13860 +1545 13858 +1547 13857 +1548 13855 +1549 13854 +1551 13852 +1552 13850 +1553 13849 +1555 13847 +1556 13846 +1557 13844 +1559 13843 +1560 13841 +1561 13839 +1563 13838 +1564 13836 +1565 13835 +1567 13833 +1568 13831 +1569 13830 +1571 13828 +1572 13827 +1573 13825 +1575 13823 +1576 13822 +1577 13820 +1579 13818 +1580 13817 +1581 13815 +1583 13814 +1584 13812 +1586 13810 +1587 13809 +1588 13807 +1590 13806 +1591 13804 +1592 13802 +1594 13801 +1595 13799 +1597 13797 +1598 13796 +1599 13794 +1601 13793 +1602 13791 +1604 13789 +1605 13788 +1606 13786 +1608 13784 +1609 13783 +1611 13781 +1612 13779 +1613 13778 +1615 13776 +1616 13774 +1618 13773 +1619 13771 +1620 13770 +1622 13768 +1623 13766 +1625 13765 +1626 13763 +1628 13761 +1629 13760 +1630 13758 +1632 13756 +1633 13755 +1635 13753 +1636 13751 +1638 13750 +1639 13748 +1640 13747 +1642 13745 +1643 13743 +1645 13742 +1646 13740 +1648 13738 +1649 13737 +1651 13735 +1652 13733 +1654 13732 +1655 13730 +1657 13728 +1658 13727 +1660 13725 +1661 13723 +1662 13722 +1664 13720 +1665 13718 +1667 13717 +1668 13715 +1670 13714 +1671 13712 +1673 13710 +1674 13709 +1676 13707 +1677 13706 +1679 13704 +1680 13702 +1682 13701 +1684 13699 +1685 13698 +1687 13696 +1688 13694 +1690 13693 +1691 13691 +1693 13689 +1694 13688 +1696 13686 +1697 13685 +1699 13683 +1700 13681 +1702 13680 +1703 13678 +1705 13676 +1707 13675 +1708 13673 +1710 13671 +1711 13670 +1713 13668 +1714 13666 +1716 13665 +1717 13663 +1719 13661 +1720 13660 +1722 13658 +1724 13656 +1725 13655 +1727 13653 +1728 13651 +1730 13650 +1731 13648 +1733 13646 +1735 13645 +1736 13643 +1738 13641 +1739 13640 +1741 13638 +1743 13636 +1744 13634 +1746 13633 +1747 13631 +1749 13629 +1751 13628 +1752 13626 +1754 13624 +1756 13622 +1757 13621 +1759 13619 +1760 13617 +1762 13615 +1764 13614 +1765 13612 +1767 13610 +1769 13608 +1770 13607 +1772 13605 +1774 13603 +1775 13601 +1777 13600 +1779 13598 +1780 13596 +1782 13594 +1784 13593 +1785 13591 +1787 13589 +1789 13587 +1790 13586 +1792 13584 +1794 13582 +1796 13580 +1797 13578 +1799 13577 +1801 13575 +1802 13573 +1804 13571 +1806 13569 +1808 13568 +1809 13566 +1811 13564 +1813 13562 +1815 13560 +1816 13558 +1818 13557 +1820 13555 +1822 13553 +1823 13551 +1825 13549 +1827 13547 +1829 13546 +1830 13544 +1832 13542 +1834 13540 +1836 13538 +1838 13536 +1839 13534 +1841 13533 +1843 13531 +1845 13529 +1847 13527 +1848 13525 +1850 13523 +1852 13521 +1854 13519 +1856 13518 +1858 13516 +1859 13514 +1861 13512 +1863 13510 +1865 13508 +1867 13506 +1869 13504 +1871 13502 +1872 13500 +1874 13499 +1876 13497 +1878 13495 +1880 13493 +1882 13491 +1884 13489 +1886 13487 +1887 13485 +1889 13483 +1891 13481 +1893 13479 +1895 13477 +1897 13475 +1899 13473 +1901 13471 +1903 13469 +1905 13467 +1907 13465 +1909 13463 +1911 13461 +1912 13460 +1914 13458 +1916 13456 +1918 13454 +1920 13452 +1922 13450 +1924 13448 +1926 13446 +1928 13444 +1930 13442 +1932 13440 +1934 13438 +1936 13436 +1938 13434 +1940 13432 +1942 13430 +1944 13428 +1946 13426 +1948 13424 +1950 13422 +1952 13420 +1954 13418 +1956 13416 +1959 13414 +1961 13412 +1963 13410 +1965 13408 +1967 13406 +1969 13404 +1971 13402 +1973 13400 +1975 13398 +1977 13396 +1979 13394 +1981 13392 +1983 13390 +1986 13388 +1988 13386 +1990 13384 +1992 13382 +1994 13380 +1996 13378 +1998 13376 +2000 13374 +2003 13372 +2005 13370 +2007 13367 +2009 13365 +2011 13363 +2013 13361 +2016 13359 +2018 13357 +2020 13355 +2022 13353 +2024 13351 +2026 13349 +2029 13347 +2031 13345 +2033 13342 +2035 13340 +2038 13338 +2040 13336 +2042 13334 +2044 13332 +2046 13330 +2049 13328 +2051 13325 +2053 13323 +2055 13321 +2058 13319 +2060 13317 +2062 13315 +2065 13313 +2067 13310 +2069 13308 +2071 13306 +2074 13304 +2076 13302 +2078 13300 +2081 13297 +2083 13295 +2085 13293 +2088 13291 +2090 13289 +2092 13287 +2095 13284 +2097 13282 +2099 13280 +2102 13278 +2104 13276 +2106 13273 +2109 13271 +2111 13269 +2114 13267 +2116 13265 +2118 13262 +2121 13260 +2123 13258 +2126 13256 +2128 13253 +2130 13251 +2133 13249 +2135 13247 +2138 13244 +2140 13242 +2143 13240 +2145 13238 +2147 13235 +2150 13233 +2152 13231 +2155 13229 +2157 13226 +2160 13224 +2162 13222 +2165 13219 +2167 13217 +2170 13215 +2172 13212 +2175 13210 +2177 13208 +2180 13206 +2182 13203 +2185 13201 +2188 13199 +2190 13196 +2193 13194 +2195 13192 +2198 13189 +2200 13187 +2203 13185 +2205 13182 +2208 13180 +2211 13178 +2213 13175 +2216 13173 +2218 13170 +2221 13168 +2224 13166 +2226 13163 +2229 13161 +2232 13159 +2234 13156 +2237 13154 +2240 13151 +2242 13149 +2245 13147 +2248 13144 +2250 13142 +2253 13139 +2256 13137 +2258 13134 +2261 13132 +2264 13130 +2266 13127 +2269 13125 +2272 13122 +2275 13120 +2277 13117 +2280 13115 +2283 13113 +2286 13110 +2288 13108 +2291 13105 +2294 13103 +2297 13100 +2300 13098 +2302 13095 +2305 13093 +2308 13090 +2311 13088 +2314 13085 +2316 13083 +2319 13080 +2322 13078 +2325 13075 +2328 13073 +2331 13070 +2333 13068 +2336 13065 +2339 13063 +2342 13060 +2345 13057 +2348 13055 +2351 13052 +2354 13050 +2356 13047 +2359 13045 +2362 13042 +2365 13040 +2368 13037 +2371 13034 +2374 13032 +2377 13029 +2380 13027 +2383 13024 +2386 13021 +2389 13019 +2392 13016 +2395 13014 +2398 13011 +2401 13008 +2404 13006 +2407 13003 +2410 13000 +2413 12998 +2416 12995 +2419 12992 +2422 12990 +2425 12987 +2428 12985 +2431 12982 +2434 12979 +2437 12977 +2440 12974 +2444 12971 +2447 12968 +2450 12966 +2453 12963 +2456 12960 +2459 12958 +2462 12955 +2465 12952 +2469 12950 +2472 12947 +2475 12944 +2478 12941 +2481 12939 +2484 12936 +2488 12933 +2491 12930 +2494 12928 +2497 12925 +2500 12922 +2504 12919 +2507 12917 +2510 12914 +2513 12911 +2516 12908 +2520 12905 +2523 12903 +2526 12900 +2530 12897 +2533 12894 +2536 12891 +2539 12889 +2543 12886 +2546 12883 +2549 12880 +2553 12877 +2556 12875 +2559 12872 +2563 12869 +2566 12866 +2569 12863 +2573 12860 +2576 12857 +2579 12855 +2583 12852 +2586 12849 +2590 12846 +2593 12843 +2596 12840 +2600 12837 +2603 12834 +2607 12831 +2610 12829 +2614 12826 +2617 12823 +2620 12820 +2624 12817 +2627 12814 +2631 12811 +2634 12808 +2638 12805 +2641 12802 +2645 12799 +2648 12796 +2652 12793 +2655 12790 +2659 12787 +2663 12784 +2666 12781 +2670 12778 +2673 12775 +2677 12772 +2680 12769 +2684 12766 +2688 12763 +2691 12760 +2695 12757 +2698 12754 +2702 12751 +2706 12748 +2709 12745 +2713 12742 +2717 12739 +2720 12736 +2724 12733 +2728 12730 +2731 12727 +2735 12724 +2739 12721 +2742 12717 +2746 12714 +2750 12711 +2754 12708 +2757 12705 +2761 12702 +2765 12699 +2769 12696 +2772 12693 +2776 12689 +2780 12686 +2784 12683 +2788 12680 +2791 12677 +2795 12674 +2799 12671 +2803 12667 +2807 12664 +2810 12661 +2814 12658 +2818 12655 +2822 12651 +2826 12648 +2830 12645 +2834 12642 +2838 12639 +2841 12635 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 diff --git a/Analysis/config/exclusionMask_p1.1.17.txt.png b/Analysis/config/exclusionMask_p1.1.17.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee73ce02e00f9dbc6ab91c4884e1e3307fcdaec GIT binary patch literal 213354 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�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 RQL#ldq0gYWy|MbN{{xIu*ysQN literal 0 HcmV?d00001 diff --git a/Analysis/config/gopt_318D.param.json b/Analysis/config/gopt_318D.param.json new file mode 100644 index 00000000..f9d3e9a5 --- /dev/null +++ b/Analysis/config/gopt_318D.param.json @@ -0,0 +1,64 @@ +{ + "metadata": { + "compatible_chip_type": "318D", + "created": "2016-05-24 15:50:57.069486", + "optimizable params": "km_const(1-4), krate(6-9), d_coeff(11-14), sigma_mult(16-19), t_mid_nuc_delay(21-24), sens(25), tau_R_m(26), tau_R_o(27), emphasis(28-35), emp_amplitude(36), emp_width(37), clonal_call_scale(38-42), tau_E(43), min_tauB/max_tauB/mid_tauB(44/45/46)", + "source": "gopt", + "version": 1 + }, + "parameters": { + "clonal_call_scale": [ + 0.001, + 0.709681, + 0.246037, + 0.02, + 0.11484 + ], + "d_coeff": [ + 26.8412, + 156.299, + 150.531, + 52.4835 + ], + "emp_amplitude": 7.572, + "emp_width": 4.961651, + "emphasis": [ + 7.238, + 1.58, + 1.695, + 1.4475, + 7.3275, + 1.11375, + 0.495, + 11.56725 + ], + "km_const": [ + 18.7799, + 22.2851, + 17.3087, + 15.5264 + ], + "krate": [ + 17.1972, + 25.0697, + 25.2226, + 31.6017 + ], + "molecules_to_micromolar_conv": 6.2e-05, + "sens": 1.374064, + "sigma_mult": [ + 0.981658, + 1.30326, + 0.8448, + 0.810976 + ], + "t_mid_nuc_delay": [ + 0.707009, + 2.03301, + 0.006868, + 0.209029 + ], + "tau_R_m": -16.08, + "tau_R_o": 28.719999 + } +} \ No newline at end of file diff --git a/Analysis/config/phredTable.318D.Recal.h5 b/Analysis/config/phredTable.318D.Recal.h5 new file mode 100644 index 0000000000000000000000000000000000000000..d8f274c8753d35c185e74f34e872d1c981c06a6d GIT binary patch literal 418024 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 k>;)fJ%ur;4wp4qoWtEsJq9!Lf%Uk? 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 k>;)fJ%ur;4wp4qoWtEsJq9!Lf%Uk? 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

q8T4UvE%~%0Ds!D>h2TboDH+8fFDSR&$Zai^!{D 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≪gfr+i$?1 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 literal 1600008 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

_tO*R_=cr8JhjH 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 JsowBO{tpEYuPgun diff --git a/Analysis/config/phredTable.521_ExT.h5 b/Analysis/config/phredTable.521_ExT.h5 index 3ae78b8e31787b9ea6e6880ecbe9b83920b09a77..0ff682a26ba5d6f3803d33adec63bacacfb7183b 100644 GIT binary patch literal 14978533 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 YXbWKP>y0YmzHfB2IJ2qlOqd1P!t&RyXC<%fr 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 -<#LRW7v5?Uu#*WyUOaFm$_f;R z2M&m(RMD-mhv-WrJAza zf^NZjFb&MkR(buPzlvr+ePW|Z$>rFZx^ZwJ^5TJ0Bo$Bn!%aK=Fg9|U)1sC{AO^Wle`C49%ycBtI* zVpbP-Wy_IQ4c#gj{_9x+X*6#bUYqD?@j~{bUgGRZqYTdNMj`S zVchR`JKl`IiZpOm{8UnGK4@`gkCl}uOU=cNJ{aw-i-Y}el+>r9V~bh29bd4k9UD`l zbbjF-8CEDDa?&}78Bk_pK7ZD1 zY)*&TRg)zW1w#rB73|R%)dLy>r@VTe6=~qCSSw_u<}OwlNC}B&@pC#BZgeJNJ4Vw9 ztl8r#FI{gr#v%=zjnoSH8C0E8jXJbMFXdx4>1-f z(!kkBqmTgB$#>BtszsJ+(v__+a0auqsPZyxNoc`syt&Lki`3CCmwhjXBK^kMNLM@q zo`F^wsE`-^I@QPTDAod7P^>tZjQm6(tA)tOi=hTzmCxZwbnE{U2~QPA)wqFJT~|h? zo7Kpxx*(ruhe2jmeAFO}I$S%+f+96n)P@-i6hT}DS%N(6D-I@53l^&fk&$Ob5k}6Q zBb$rr0b=uS7u5yHJ?(WL*>5gFkkwq=fV&?z^u0Ei1&j3c1Cd5L(lzf+1P^rFbQ71T zc?*#SP5>%?<6l6L1`b3DX}^!nZ%|Amqc|@B;h|?QB9ggCMtN>7K4)N37|#MzRE{Di zQMF=)yok!=BXAf-URfa{iZpOm{4Avud4HmO$|@m9G$4y}_Y8Q*6p9UZY~&Yu8l!sX zs~cdE2F^y-3R$W71B)Z{>?1UABfEP2^LV7m3Htz?5O|@1Gr=v^@-l5n`1!~p$TDPB z9Ysf88b!4rQMxJny)@6Dt2P{YN#ykJ;qW^8Q)yq=p-BJ6*+^GB1D=6a8F1u9e@Uu? zFiuzG^@flaiPe!H7@bM9j0TH{oE%V<*A^i!ELI&7m)@QgMHo3Dvbm@pG&cWsQC*PS z6RNXnA;Ou79YwASGVJ9AiuAPvkw&7u2odrcwg{f`_;Ov5#;D{t!EUi*BLYOv_a~YZ zkzneAI0%ac>B{Ca*syU1H)n)m*Bu)X-Fm)15z;Lv20W;KK)MCM$@8Qk@*={)i?A6W zgGKsNX(Nq70wz2jDKpRzc`=DWZ~BhB6rbzL>kRT@oYGOW1NXNhFP)TLn3cW>`z>x% zldfzz@^a+W>b2j4Jp+%L0Y_d5c!iR(uE^_vkQa&7Q6Mi8Eu(=V;x@+wvP7{}5(SLH z;#s;yAIRn$DHL)XdC836WxF68bL6GK6-IL^D7cQi6i>QEVy3znTuirMWaeCj;mE5d zPk}V`0^I6*1S{SMii&o{NXbm1hmn_^kcAXSUN)nb<8@%Hx*wJ zpOI*h7ZxR63EpwzRbC=}MB#)VoE8OLh%25Tv+5{9^1zimCvY1cI4h!puF3t0 z<%5$Q69Pqqyue~y)9KkEY4E{0&84xA$|lM_c?iY2vJrAoC7t~Xa%;q}(+3p*6)-#8oTif6zx&?*Ctyy!1U zbr6~ATIJ=)OYyl-52j$0Pi)le#*vrG$d&1q>P>KYj10ONLy`WTfJh_JmWv2^4O;|H zxf(YYGv_Ldv8g#BWCGXC#v+YTiEaY1;eoRv4V)Db-5TGYSUxz}F-uTH$P2A)J{=>p zg~!N5I`Wceiam^{Y+Uz9g=}aAbvmGEC;Ugf2D5-w3wK@mD#SET>Tc$rG{LG-M zHXM0rL^YUlGDx?e=*X)T+3;Y`0L;KYG(nE`9L!Vig2AFx$ird{jIu?WGn&TKT#7N8 zL^}_V?%hQsbY+`k5d_lajQOZ9gDy#1F4h6rIwSJFjyXZmh#Nkt0w`+!OG)o1EwFz= z6C0B#DN7Y8#p)rOh%zR6W_2%?FC`w)97ZG}MAAwFkA6HN5pLs%CQBZ-^v{{;!vTu) zJpqwMIvx*bV&kwy@RY|x58@VG6N@xPYJbJujsd0>Y2d8*sigg$n-5yt*<)oTdd%kH zMjuSV*2R%QS&3*WI<}aJJ?Yq~c5KY8D&vKBY_uW`oE1SO9yQl-tJSTE%)(4R&x`tj zLL?H+%4{47w%VM}&EAf4I@GS3ERkpt4b^?o7}WzB1E;)UgcWJvtXL~#rRFYH8Au6< zXYq477H)JVsE`jATXL0`F54Spkp|92YK8pq(<6w47VTp~k+u!6k>^AW+z6I|)$d0m zU%p;5rlBCOzvwqkQ=fnHq&X|nZ=4m4Lc(CE(V#m|V`N{r@|sZq;tI&WJjU{0~I8{$KH*>iGC9a2phBK37C0@i+ao$jmAkzu&Q#KCjOdSv%Px z(aP_USuxB?fgc|v>LZ95iDl!2*7CxlMP5W*5c1k1x`oB6mjd7Hu_$7qvqyC6j0k$a zN6e}h5%m4EZ!AKP)m+?w8yz?F1vQuji}bq$B8_yUYu=p*9_YB~CN5F)79tIt091?v z7Eq*t1Cc`7?_={D6gB1ukJ(rRfvZJh5s;JR@>yVt%2DJbs#dI!7g3pfgqkQuURfa{ ziZpOmL~|Ht_a~MQo)dyZ1F|@Gc*jNyvS@*?Vw$RbYZnTQQ9bn4wIl~iA`P64tQE3S z_i8hvYS7hA9M@_lv%^7R$Gl-dW zRR-Lg5sQeN91!xtn=>K~7UmmL7saK|h$1FBTiNhS5g;$T)I8!~T_Zu?TdZl{aix0L z98PR}p?~8Hka?khA$6*)dB{3?Hc<78Y2VEut)=EBaK1=COjS~GcYO! zESUg#F^SQTAow(J3i{AAKq%6{*+`?1(3Khu`kDrLQI@-I<7fxYm#;TD@hY!NU>|jl z{`k@j-iZDx+81^x(w|Bj>56B-Gtepnj=TysMG)%-kr$pH=g3R2v|;bq=*UYUt}D|m z)tlh*7%AkmS}{4XBK^mS6~Rm|Ih70UAr&+&mOiGtY#2aBLo>9}#^CDJ!+PY8KE^!oEAMVzJPqNY2(W24E)B@53Sc}bwu z3rdo==#g1Swc*H1gQphINjGKxa?RxdfFmzKr&?5b zIr35{?#ygQL8gLp22TM5*f~;lF}SE|4$pRkGZQj-?35AEZ?!QrbGb^ zjS(EXMX~O8uwv(-h>#ar+k84k$ZOP0bmV0LRSRZi8$0qc)ar6&TISXyw7C1Fkk=1- z*ET?gXQWz@Ucjwr6cQd9PxXT=9V-?=3*gLDguj=Wlt4G;DVzzjI@ za^#h`P%2SNegs4*L@c~1CO48fV>3tA%{h+oe}>bAYB}x746t)R(sbw zHikef(noq0;K#=~G0iQkmXMEY$v1BP$3`|+>WPg-8`O8|d=ZgM5`UQrC?uXt6bW$0 z#vmHX!&4xQh(Ok9AmR~lup<@~UTN!^w4F}JBNEXzj&BH~?Y8s_W4dvGBK=;0NFyDO z2SkmAExv){0JOIjqS_si-R;(j+cO7^kqX}J7+_kF2F{9~O4{$a`Jly}Jyuqt$80Wc z^ub7NT^t#)l{BWB1G$)qJ?Yq~c5KY8D&vKBY_uW`oE5>0RBEo{R;ybRnZ%iXo)`54 zg-9ftMc6nJY_&O`o4p<9bf{f5St8NG94ZE*F{%eN22OePJS)<`S+Q2gO3huYGLRAy z&*JBFEZpc!P$Az0*6cCz(zSeJEYiT)NUe}RetHCPj2;t;v~7S*$T?ACG(uot_50Dt zcWhK}>n2OoYSNW0pV;Wg>(NPuKfh<7Q3e9?qW|uoBT%UDgT?>+C#vaZKwim7=g)us z`JL=={)Y|%S-;c8elc&MY=Z$B{^6UY2a*Rt&o)>A6OirXU7ZO7h{x6uzWqC6jMONIK`{H6lD7L zDz7ul3WBWWq9ZRyUNt)!OrccW<=Prd=jq=#`=Sm-`Zvx-y5bq|47AF?V<4|1LS9&; zKP>6U(CSD^c9L;G$csdK1hIx)(IU~pAS?oM@^)al6>e30x<$wfi->NWS&XZ=K{DG! z+NbQiLKn!~nQNvFmuFObg=ag$nTd_XJ&IhxiH#4tV0pibFhRvkH5*-5hdLtdhg>#ReF0@E$X!5n!hW+mO|wZMIea}{StUYeR+j>0+elJMsi zNtEni^m^%MO}et>$jgz}qYw*!PR~H63^?-2V2h0w$m^;*Hf}av<&|+48^cCk54&R{ zA+I^A@|vS6udZUQ;wp@JVP_MpWJ40YYa5=!)2ysWFW^>0AZv1eqIqx%o)cgW51bWg;H-#H(&YZc^1;cDS%PBtNc{kdanT&c4#zp<#gpa? z&v`<~tCw}%&8Z3S(T>2 zXN-{77}2de6w7QRS1E;CazyAeA`k!@dq)|v1%yL+R?mUf= zeSwD}4V;a1#WUa;XqAD0yae?jhsEYQHd4Gee#IRd#DE7CrhE3;STdcCqDBeS9ak3y=Wq%l=V*kUI3 zq+_Sru`xAD=NI0wF*)h{GNXY@PC8R$GVAjoL`snD=XtSyx@d(+B$~|#I1+5NIiH)o z9l3l`?W!3oD#5%+s{5ibss}U%PI9V~s7HQyYq*lluKRtp-Xwg0<6lvQ48+lIDY>fySSp9x9^5yF_W4dxdm+lVx z5PAJYzj22AIPy{{xkZRa17}~@p-6u!ZKNxn0nb3I4D^7!K1$@pyoHWW7Kv%m|C9IY zSA~%>@+z>>ZwfGUD~lx;W4c9u2g-wp6B6H>i{;g#O&AJ_72PU(^hwOh9wZjeSD7bB zx1eZv6$|u4X2-@oi#i&Mh;HrgPt?g^f8SU{AnVvz+;1!q*zGp-&zYfCSfp^+FMs~324lEX~4^7yk{m3FG;lVuR>(?`4=j$*v*R`G%~ay& zXRv%dp%hbp#5lz#HY&)}wH^#)88WMmq9ZTOm>hWxfEuYT2KN@}7VIbe8)skGp-BJ6 z*+^GB1D=6a8Mr;<#Z1f~uPl~83DPYn9_gC&7o$8rKe1TGM4KX!ctFUDM0*5LLoX6- z^%fQb*`n&7e4?F)Gnb7+4-S zE7HJO5z(#Z`xDCtCp%^dismUW`*{j0`lxA`P64Gztlr@OY%mzz7T+ zKweB@G$aT<4V;2LGz}1nG;lW3C?s^HMuWblL0(+#(^0eo_qQW2ofJo2iqi}RC8>Vj zEC-{%iuQ#aiu9+_M!Mn|@C>xdfFm#ZOOlT>bnCjvYa6(SE>UshC2J>j6asT{kkX!+D&mape&RtDWn z=r4&C5g?k}pJ*POgh_L;AYIuk>w@CD_*qbbK45ZE@|*yOc;Ku^17}54&^5U~v3zi{ zW0s&8K2krxVqDWH8jYsfIpoEY<|MQs?}-M^ibml`7i9);LL~4Zk6{254JBZK#>fPF zm6u9M3ycPtRY%bdoFgxj6GvX(OnB7gg8R@Ijs8^Hw{j@bzi~Fw70-ZYpj8GOdC^~z zd^qxw9Em)Pl7=!b(mW17QDgC9RbGy~B=#B%@*2~BoEV-nroSgvM0D#Gw_NPlPxIg; z_>PS=Itr+0jELEl#kz~Ziv5J5qFpgkf|Tfu5#NFfo?DUl0Ic$IH%gpkmJS#@mQk#51}2JcXnm+#o9vN9CpAxylz1n-QXTURH zGvLT8^DZ(Rc^Mq!$V*K_#2ic_o8a*Vbl{+W&eRo;M@L>Ve~!HL7iTbVOn01pM_x5L za^%&FgApWf3udKSI`ZlWovzy)GOLcFf;;3o@-jJb~iet+ihu#Og|1#q=B=MMj_$QL8M;U z<{ia$T>@EpJMrQ>Qnrfo#)>pE zd!?sp(^i=)D>5=G7hhvP95qFHeXZs|E@om+I(DiZ8&jine&HP(latObGa9($r1Q%x zS;ct}A|=T7^BY97G8&VeJ%UKpboMc!NZSV3 z$aA9Rbp*@6>i46OFJG^j*HDnxU-TPiAkV*`NWXD5(kLVhrci3oou@JSmpPzF17`wV z@C-JdkcM9iHHU58;b~J z9kB=y&3=QJz=p=ibO4Jqa5mB?By2i})N$MVq9RD$ZqtLfEGSGe*GmIlh`cOI#**wM z4V)lUj6fDpq=B=MLLnzKzd^AeJK0=ht1$Wy0XbPNp9Q9<97Rr|YQ+k9m3%DJSuygm zA`P4sONH#%{DDOSvN$7n$3_52rsc|P_N`qgG)DE%SJ#ppEQvI5HnLX8N|6sNj?lAr zS-zet@xbA0d1=PfbqXX(vfZ)q49J3X3yO}s9C_9J$dOl_i(vJV5c)UHzOh4*{*AMd zu6PDK1FbRuS<*ghiUbwtRkjYxl4riRfg-bwTC|Ep}`)ZAp0PAkz2BHovHtm{NJh8!46%$?hdG zU=96)2Ce`;(d3#FE7EVA6^%lIDVX7)hZggi^@VZe9UBqddVXY4|6;I@kr&wbZ_*HX zF;1ahE(6W5NWXD5(kLWg!sC%L10yhSH=;k4_Jtja^lzMvbj35^8EBOOM_%-oBp+w!7S9b- z$LHr|kQdc7MH-XpQqr0JDmwBiP!EPl}GWKGgzG z$%fJE+T4+sgi*IhqGS)F*GoVDwnr$>Gd#y9Hma2LQl_Qu5!@Cxf^-Xt^ak4M)!u_W z1D*k!0Y_dX%W8wXwy4T0h`P#a6C-7n*9KL2ZA>{9A+I>^OjHb!@iZ$d(w`J78ij<3i!uX~%()7qtP6_o z;%7k#`he?#YGzc;Oyq&HA`P4sONHzJd?t@9BIE@YX7GyPV zPJb%xTR9XRdD%p{;u&b00Y_eryb@>giH!;!U98H>k(WYTLqT3+`i~O{ZO9uVB{EV)SM7Ef$L^tk;bSbRDsy=z*!L?uW=bhc5MF0B0^qhZS(0E@hv^@IK@8uG)e(u(w_(uzhQ;TEaUp!Ht@6itQ%P(<{=tGraIy0SG#Uh-@9&=}2;mn2!Y2(n}k zqlf!RtkNq~5<>_?nD5mEv&Lzdk_9;ee8IPf$|=~=Fzkw4V)E?LPFf&Kz-6sqz>B1j$*4W?RARShvE)JLJ?*DZL7CO zB!3IUv|64%(-^5M>~_2vffZ@stf&-nsrjHqv?1wAXN~-g6=`PnN>A0Mtuj|uWb{`; z(ToC1=u;JhEoNd*I(DiZ8*{75c;Ouztw;lBMXiwkG8=%%6=|lQ-yoXh+W4AatIhe` z?Cr=ChSjc`aSkz<7fD!?#;6|97&ztC^Q=e%XT@3}D>Zkq%0Nm;Jd2;xv2dd^L4|y{ z*piW#F54Spkp|92YK8pq(<6vu^q5c_J%NOr6BU5tZ3n>W_oI>T*eJ=`ZI-AZfFcc?jT8zwq4^Dp0OqU~ zml6RvSuURirl=f6PNHhX3VD@$EEFj*^0FcgoE1xj?AZK)MFX-pBY63GRH{_2%my$< zDbX0!LtkA>a`AguITL z1F?Tpz$nBVMgJ;?i<;E&-~WQ$6`PC17}N#Hg=^Y>d>m|-SPZ3S^0`8!lsMAjt>eM4 zDSGvfML3E9K;*?0MCJ-xdIT#Xy5(zmDQeVhV&i@-a7sicE3OMtw>cht$HoMfsx#h5 zQ3VCJm&kxM^bZ<1Lwq!U5c2|Fh2c9k)_^U1DTlpdV}VmjzNKu&DfAm> zpcxkF-#8m-6cRAu@kp70Q87lz1jvhdmxctvr-4(@XFT@h>s3lxa4-WFDa$`Qigw`Q zD(FV{wY*eHt~{;MtGsG3#@4Xm82zcVFYHjHf8%VVE1m(*K&uQm@}j>a#c^xMi)xzU zGE=%lh5jl==2qY=M_vjh!4zwBQL|UdI4GHE+pU^Y1$Qh8aOkY zj=WS#TF@=kGPtHA^R4f({;OqemiiLSHdO5 zS&^-?t|Ko=q1d||uPX*DnU1^^vKqHwR=O$sEt+?vTd=v}IOIC=GC8?2-7@_l;in@n zM_#So@jcix;2FpnNZ>@2EP0hz0m05yd2L0?g7fjQMO9uK{!mwWZBUihrr=l6B;*z6 zor#Jea_E&UcA#kD$Vj(W+ zGz01r8&yg$iBdtq?IjW+FCI7vf_TOs6zShM8)+17?xM^3|}7(|6>hQsu}?eNB2{ zLFyjCy~7(h^11+tlDCMSBd=Cy!Gk>mx0nG@-IbhJ<(0hZ%v>m%Y{hwcY!wF(^L)Ol zMfTbFMB;~;0~kV8Z^)(g;%b!xuh~&VH~J3}VVB-f!;JPqo}hH=!_QWHuQhypgE-m=8ynIw2kB2me7wrcD$Q_xh#)n zL(znh5H~nbpEMMygEq3G*s4o=og((3h_=OigCffO+g5LnNd6Xxdeyw-(-^5M>~^c> zqH0SnH6OHyHY8o8Y!&B?6=`PnN>A0Mtuj|uWGq)g(TwS84D|Y1(Xqu$>`BKC@7PFV zlv`EC%dEMm+min>8-U0aX{MjwAe!ac_?lo5w)x!b?Kr1H?W)NVi5BKi-4~5fJ)kjg z`rKTs+wvD798>1Iu>qpCa91P7hCc?Q(d+??4haP+geXxfr+htTP|kBN{M51gSi|7tFpwj}gIc>G2( z13X5S3p5vPTUR^-o`EkiU`Y}wJs__Wf=*aOfarw!dHVdXJx;%@_TMui{bkX|sRBvj ze#PBu$X19c_~YY*J72fT)(Igm0A7g4*n=^4eZx2w1; zC`>ZfO9Nhryevz`lI$gokswq|Jr>PH(UzRh{02n;b5@H>iGZ9eml9J{AVZOps9Lc? zUL_w3MM{jk8j58Y(;Yr$4=fsx#Rj|ZpWuh3TFx}FaWz6KHp=iQL=vpW>=xc&3LuS=cbmXOB z)-CcnM_!5=NjG|f;dS&^(Y~HH7j0WtJOiGAFEWs5jX3v!yp9NYVG&WIqv&7d91-$5 zBII>Oq`xfsI3nZ)?*8#{uw7y?Gp*VrK*5E}c0sY*T3-9Lz$p=(thg@7^y;O#XxftS(m|x}m2G}eF)^j` zj5ks&Ba+=qB)`O)gT}~^AFerRD4H-5Ou-BXJ+zo-GZw~kl%aPY5K$f~ka4|7!jJ(G5awnk;c~~@I9O{34+M`S98&{<>oHR4D^x^8YBC%-dwb8 zUGWTf2ENFEBQN^%QXG!Fz8Pe}IgC(rC<`)&4lcjET7-XeOAyuJVl{*h;37#V1cygukZW9r~) z&fX`QviE^js&65t`G9wGen(GY7q2Jfgj6IR;qvuI8Y4xlXv0S+nlKXZevx+-84!_v zA`;QIm|gdFeA^N^(#MY1TQk^-N3)@5!bpf49H>tkiqt_H*->oOrM*rO`%pyNV!lBU zW&Ulew?`y@3&gZqo<7qUsVnSuyct15QH7C9%?B-_4M|rzYvgaNNHeonda5>UmASGa z>hzJ76fy zznY7tEeX949>0;y0FRL+1RiLNT{Vs1G?Eho$T-RH-b#C47nN4FmKE~(RIn1{E z{%>)Eq(~9KP?l6hvSdR)EMS-vDbSg#d>2T%>QDp^#m_`^3qvQtGRP%NL9ZR!&j8Me z#LNvQ7F974PW^~YP|``idymzRsC6s?ZgbirCa|M1Qjc{zv(*$;F%rUVZ{nMpB6VDs z>{SGzJMKFWH-N_MHVV8DdD-NQCE6PrBLzBE^+i>egq4Xr0)U)sHXKtFAVZLoplY!| zUO6B0NlFfR)fDq$j_$Z?@xY=2SuP-W`+Ag>l<&-@XYEX(F)FcD@hr~4z7@;5WW|yX zEKbn7<1O*CZR?{6wy!6YqEd|7Y%tC@+ygpA+ItP z-s&VE^iQRpp4S(3T~jOq7J+XfpswF@mMyjt^24g}mEINjHgC7Y!WeGvwKcjUpr} z1;~BhK+M)NCeu!AECHMURlbzIw|W-UUj_*nc|}~3-DfqCm%}Oa7pI_E15wo_0TUi8 z$s;hy1}vHYc{$!CA%XX4-~`{98uF46X@k5d%569J(gXLGAukye%kt_2`-nEW<4Xf< zi2kY6)ARbGu4{@#z#{NX1Qg40f*~(Xt}ZCcs~_Y=IZc5D$rKg(SJ9AH#*VrmX6=v{ zKC!U@-J&7RUND$zrI64@B&MpX_bT=Vvg$X4CiQOM1m7{_r30cALtYF}^^B)j+eM06 zTVjU(IvOA^mdI7<81mv2%6a|qy2!b*yCJU@%t}^uz9BCGn1;xgL>Ik#<9?#Oyli2k zpjn+{WQjI@1kKf_Td?FeQ+xZ$B482F5isN>=@$3B2a(qSWqBPy?T8Jiy-P50NmYylz5jfDk?5AL-m8d66h26? zw~}Bho245L#dF*;tD2%JM#9WQ9)Vd(?aGWiFDT5xD?thR!sP|!e~F&hxPhxF=EcYk z)gM_z$O~-7HJ_u=sH+hZfC*i0(VT)oSX9MG=w!3^`)f!c8aLO6B4Przuu+7n0o@Wc zz1udSORYYoK?B^zvb@xf8AJ(&u20dB7e`N{p$=Sx;UsDHZ{Xz&8;v zid7-_{=TpSD;D+Z~q`AH! zFTv9K<8=zWI2Zs$MG2fh|0=4&wwIR(NdpcBnN>?s51gIYsA4h} zk}z(RG?q7_G1AlX`l7CDibcR8@J$2^c^UGG=&CNWx?WqAG~}ho52jo6KYY5RQb^Eg zTJ=RkUYdkK;svw3Fx{eo<0QxEmWoFO?n|62GYol^h^uG1RW_WlHA7w;MfFFbc)P*t zq;)}O=+HpApr|N;6X+j6RoIZ12#Fyt@!J_9UlLvP?v4A2vb+p=3B)xTURR%P!IIxh z?d>azfJHz@pdIr1Os6Mi;0SG0|50Deo-{u59d!8}i(p~n`?aokfhRUz=wHRiuhJch z>FdnE=S5}(l6m8j2>W0y^|&y6DIi{9_4?;UqGKH24uq5Bb%ZB2y3tfUz@jQfLO9?+ zd{a}T4(gIE#YSB^$q+doibqruis-<{x7|r!2Ou7H`fNwSV(#1l@qpuTTVE7)$))O} z7SVyETe;^T`FkzW%IvLNs!c~_ZnfxGSq>!=JIiTI!N*n$b|#&3iW3`Ct-@Hl6B}!a zvKaa6#Rx<$#8Qb9Z1wO|dNI zehB`W^V2^~oWFz^u+RCFxxtyCM826$&!cg3$V(>pO&f@^F8R~vM-T}u>eqxK9UGua zR*8bOxnnlL?)Q_4ZC@`5*Ny3x?2hxF5c0CUybO8i$-OBS0gHf)KyBppN&gvB7B0vR zL|zDkeOv(xY9f|Rmu+6riH$)Nt&u1ca#5E)k@#`_?OvTf;T@H1A|kqlQIcR8K3%<$TO1DLLd-Q_PDwy5p|J1B(h|xq#s9>rqxxzB8MiwKIjrsKi#qvp5I) zRxIn16-z#_I6?1@x5Ur3t&b+yzMfEuT3zRGie-5T$kZUAOVsqP-pi|kSwWChUo_+; z8IvKev7#i=Z|@E<^^5*DPR_~y_@ln4>zZN_un2q;fgB|z8=ctL5_w%XFbZA+G4(BD zPZxx|f+$)eQ7GhAY}=&^Qb~Zk&Y<9XZSitee2EYR63-zC=PWMzS24sSd(MHx%nc?M zRWTA;Emy2x{ZSS+o^}kU1az`><1<9voZmvd56RK;9%6L|zM@>+mv2DAkf=QKt+q4ERk2Ck-<7b81Ve`HbpWsrc8 zSHvaReO42BIh;acq@Y;?QPm{@6CNwcBQVJZESdm$Io>59f%j?P6uPm6jUprsIG6&9 zF59Q2s0YrruU9cK?i$Gsi)`lMP1hvi-1Mon+W76DcOd+IL^8$ z@;ZeCB)MRS{#7*OmGK{#rqxp1WzSD07D1|#Tc@S326T&tIJFuLji>0FHe#=IE51d# zyVfJo%I`N8(WKrDoElC;ULquIAzrqHS2AB}(=9_@C5r0~&7r@J2FQyga#cEpyf}q& zUVpqUa<1%d$V(urX#-{@t2)0y^_FxCR#$lE*^rlti6Jk+X$*P&a6szQEm-oKsl9z= z5wHm82qesv8-~1UAg=>2r1E8oVffHzqRAI~V5+O0U*vOGp#W{?kkawq0%t%)EEdke8j<$T8UejfR8Or(3Y(H&c82$|7J9&=E+OD>n>z8S;vh>gPB3GUSzmByTrJ z{Ze&F&}mxr#jZe>>Stv`IqHhGAuj=hFd!0zzClT2DVaD|W*G7+5m(Q2t86%9Ylgfy zit3L<@pgmPN$U#HIC20*gcK~xON6Q|9BWCpV0ArkhP+fvhREv-d2zfo8oI^19lSpv z$f_^u8NDeM0gJ#l5%@b?={Q19z98}{m{<;jfb{Y4`5_NCbX9&M5tby;U3oiI_80o1 zz6iFo|M>WV^-)7edj}#qnz2H=A{~jBbBGdsMm9xveBfhNAemz>sbI=CLVDrC@I`@m zh0i!46^iH#%C`ei9(f&M<`{ftKO%KdH`G$3u<2)3iZVpbKOyl5#4{A(fsebBz79a7 zV;tqic?Uvq8(sT&+&;314kX>G&>aqYEz-*Dtz4>2M`doc=vZ9@q3)j8T~1>PKDJu0 zGwGb;PHaq7N@Kk!?PT4VNCDDs^aG;WuFX9YoI9{ws`ZE)|4Os62Px*~{G zB|4nL+0oUx#J(Aix_)^HiM;j)mDHDirjS38_;HmdHaaqeNK{)9lPC>d-rgYcI!c)1 z{GPNld2Hx=Q+#;}W;=oc0NjXCWR6M)DIKT|a9-5)PW9oNt7RRp0sLT1%c z)FA6XbW4F3A}^bqu|#{5&`4&iGCP?jrceZ@*=#uFC^!h`A(f&-UO60dc*b1| z6H^2l21J1@S08U*kFt{To!RuPohb}?70=@1ZsNRtKqe@jcuV|j+xlpN?du7ps01R1 zQ*=HUm28{oB)N_AALW@K%8*&L6b*Svlr}`%XUJ>#AdB}fco?|-_>A5jXc4dod=r7b zkk>^*EXSIvM2);IuDC>A7lgcm=!$0-iTb*rEUyaDZ55wA!6C1+`;b>%@p3sUJVi(Y ziRZIt9cOX582KxE&d)DGyk6@*la?!HJt@p85y2b?oo*{aJcUKTZB9FeQvy2KZqU28 zIdYMn*QPpkTvzW^j7%xN5AnoC!EwB+GG3f&q6k%kfMn|#liBvf#>94l;R(-+i75h2 z1LDhz4~)DbF3Ik*n#hZQ2Bt9xqghE2m9_!N1}vHYc{vgzA%XWF8BcZb?h1KPl%HCP zdf@29MlD*Fmx{@aIhY|Y4wNuT;$^;rPPZ-j&D`9+vItlNemnw(yfTtS=J1f$DPSMD zU?>u=d|@MiPW72p%aT=e3vw`oeQry)VC$G}q0ZwI8^z4uyP7YxWL7P!_e!@^=v?bL z>WcP_MKPZbE*i}SFvqI%2;k5q&XpO4yb_%XDQY+7b?C370rFyrT$PR?FF~=$Z40vW z6RVyXIahW!r9?=@zW6NY1k%FBOv;(=F8>5}pisaY$vz>&K~pJw-GSp z^FGy^0ZY<@X_OU(bn6@Mb3R2(%4IM!J%j=LLm1cqJ%7UrfqBK5uzqGcz$oplLu5 z@&cQ2&F5$|>S|TUtCQW@5es_gh9upYy2&>b(X3pTq^kV^BGu2)w+@+AOHmJ;AukmZ zLtY#&jprmp(Qoe#5suw0S+Ezg2n;d;hP(`UMflb#NzAgmh!e4Ew@Z`C%+P$P3y@X+ znbc+7rC!AxbwztD$m>xKQsi7Kc%8g^;#`^0_Qb~Qug{sD*i21K5oj6^guKw+=JP4y zTX4g3EfS`HNlgz;qIyGfiW)f>07XR!V9C)#C-v^VzEMlM1*_|UqZ1poXbT%vOm57< zP?p!Pu@D~XCy&4&p@4PDB5=PEFyv*(%a9lGI-WBYJj?RRqtX=zi?rC>l5RP|l~(fzd!nN>?Mj#YU#?<@;g58F)tDAA(qVX zO0r{zvwQmPe;aREHE+5q>Wl!=t|#=c`XV}R^6kKqzJhav7_ELoKVFi$rJvcW2%_|M zLLxe`@myOx?oRqT0FjPylpE(A2*+)7?c?$FkwtVM=}Lv}aM(-HFVbGgrP_2<=60dP z?RK-sVQ7KrR_w^df{v{g>`Xf6xDy)z*;_B}Uvvp=>NS5rRGYB5XM&?PmrHfF*_P;f*3J}$yozV>aW`>ZKOhqnPq=z?l8sR` z!S?lpQdFvu!zp?Rq-A+Y?9^y;GZ1CStXhhOybO7jM8=3&84GW95|BG)3-)RjfdNLK zFXVMW$g4u+49oMUi~E#xi>iC##U5SpOi8iGuQ;3$mQ={=dOZsSMlNL#)fW-aI0Nw< zl5o!ag2=1YW?Xh+qY6I6Q&|Fn6oIi0PJS!@E0xyl9;kxHMlfouhKb%OYTZ$z7#G@z^h3K_+}#h3_YxwnV2HbG$4X@h3bf$fWfFORUxlC z+$$c1?#8bv>CV(m;h~6T`nn`leNxp;)FHu9GUUa7W}ToU-bC<**pMMFj-EzCw|KXM zH$Ws{U9t$=a|8@|8S;wstW}a29eNm_Q{JmYPOE}fpG5Um(vVj^P+f7bF(a=VpKmD8 zl6S}5rNp^1!%l2WbjnU_yfF(JAfD=?ONzxf0sut>3Uo=MTPk=>7+_z>>lYf$A!46L z?k7TCc4DL8Nu%Ll_Y4>8-7Esbi+~|7LtYV40cKTygRd&O6(3&Ssbo%4qCG&-kXIZW zcQ@pv3Ddawl764<_Qr3Nk*;JK@-pO=xQ-#O6pCPU5)cJw9AkhYf)Bc+Aukm?LtgTy zV{A=C1P8>B7sp$ppMcdthWCFX zwSib3_Ly7fp>?%)P*5*xtW;{B?TC&?yk6f*u2fp15p%o}?Q{7a$-1CT{x5H_N`8kT zs(uY;1mtzet}lY)Cg0u;{KNt89U(>o)%!Zal0cSjy-)E#jj0Qr*!YITb8YdsJL&5H zL^{S%Zk%_ZOtg)zeLTKCvWN~OU8&F=4tpv3McOO5RGW^<+%A;3-EJ0n9$`Xf6(20#`S><{GaNo$ql8Yrvok8ioX)DS-$NV$FCTz>4I@@tc7e8TeDj6vcBILdZVnNSsae7|mxBv{LmMh*J}JR8T;swM9BMfIrT8 zRieaglr#o*zn@I(#74oKo-uTirJUbiNrb#?FE7E9M#I4#fHBxBS_B3Z0R*xx2v1d< zgy@);z&yA*d?pq@|0%EV>`#AUqiCl$@#BIRRVQNNI+-CjjB^Q7WCfDhp9uPN(d2|m zjYWhxW@u|QxZm0ZftS-BF#(9Y8Z?9=-=;dzCSAQ(QSmPNb)$H;6*b5@5ZzLYg~-b$ zXDrd)07$Z3naCs1G9ZAQY&I`!s$Mvphg6CVc_jiA48QjjBW8{Rxy!^dRMi1dAj=iS z+t&j~GS7Er)3bJ_uyHS*#mC)5fxNy8$OOd`Z;2oGtk3RX`+7ntYRR3$DVF6WAQRr9 zOBC`hE(xLxnN>^Cke7s6{edjmay3RX%EGRDdl%Nv5}Lct)18?PH2ROGeQw= zUoTUYJmE1>ATJycA1m>Jkyl1cqB*OHyc|wJ#LPqyl}-W@M=QGnBaTmwL6i%IQfgOb81fQ9ZAsxU-4bnhcgM%fd$djqT$(S@18ykx zO1D(#T?ATO54 zRp}V=5@?IuPUx0yh8oIHS2PWINkr8aWXb0_A3w7t-GbF6-FbdC1Rf^>hP*i9!s%ohWHmuvdtLofu|Cc-0gg|OVci+a7$PSlUCA`$ zC78;Mk(Zc7dUq+h=-nHN^kdVl_bNurmEV`S6Yc&)wzExi5cWa)L~TtJp=uD2jC3V4 z&kG6@@JdjEzJSOJu68uB{1?k3FB}jAgu!N9emgESAhk4_{zJmk$!_fk`#f|*ipDq? z07XR!V983Dqn>Fcbx7JKbQ$vEztSOsEZ)Q5;eOh}MoyW=P2nB~HQ1Y41cnd+Ltciw zA~I`0%n-L}$(IILy=|6PIe{!K&6g;aHxzrNTk2=yT8~64zu#Dll#AU3$MNos&o{ik zE+x*D8Fpf0`Ok;PP1Dl%R?i|rUTAOg`4qt|xZ$}LY2t6lOQ5*^D4fDB5M-fgy4O^7 z5^(;KmE;k?QG(2>rKkt*ucu#H>~mvwYn?}+KJxm-DNyjn&4M1}jfMaLtPd7}$BBR; zFGF62yl6$LqA?^G^2(#q6&%Y^SF{az37YjNY%L>Q$u#6uBCek4R@rdI)(m-Z6xAPz z;_U{nlh!$hAH0y?n~h!jv=(upqA>^hx}yOX{SK%^5J<;Hmj zLU9{i`?!C-p?E;8_bI9<9S-|d9nmk+Udg4}bX4Ycp~UTWvxp$n-3t`BVy7z>bZoU? zXVN*xo!BVop>x-Z`x6D8a;`dq@+U;O!Iys~IBIjbRA)Q7tVw?4;wmIkf_0IUA;v~C z6ch&(n%E#9Qxwl>2qF8NBXNcj`DQvjkH+m8WcN)?v$igMA*k^Fx3D)(4Bg<3s>~tT%+b-e5^D z3Y_#NeD5|8BWugh*C(ppoA~htpwoL7A|gCjg=icjfXJ&q@u$HHJx_xd5@GUHZ(`U* zO}r}>0k=8rkKlsH3qMmd?A;$P>G+ar2#SP|bjcqOH8ncy_bo*LUI55C9CjPUSctrA za>f$v4S*!um5Dq8Edv6`$!7Dyrs{>mc}S(`ke6dJ+2xaz7%`(SP0S+X%<6zBkmZWv z6B|)hQob{rp0zWDjeGGdKJF$8)ZCMce8^=QrQ>j|Z(1R@eZJ{Xn6`Ad?Us>V}Ylr-eU5!txO-s7MKdsBVcUv{6>L|!!O$210EG%G2h(l#L3fJM_zY!o1>M>MCvBB2zGyqbxc z#@LCCDkg@!I9~d{aq}hdGTy!M8w~*hSRX6`j}rj|vJMD&9bid>ybcIDH4qWj-%rG{|alVk4+tmA?eY$q{6%kkpb{wXBY~O>ZQsw}vp?Lhrr<1D6(IF?yk?5k2pQ zB6VE14#gj!TPk$!nK|l;_Kig`pARk@#=?0qkw>6yKr+&mOhaCGg}hiISEXafOHeFw z+k!0p#HwdT&XwH_c^UF5kYFGbAPhF+n$OW_)YYnxS0}r*BNp_~4N1CV+t;gz^tk}jvb;)&<*Yjd zS^NcqhhNi>7pF`O_!6(!7*BOkeFzZ1`d|@woCp~5;#pxuI#~{>Al-tZAuol(dc#v) zG-SvtAI~44TlwR>?Vg#Vu4vy_WScohM{pePKFUFgy}f%zx{_%pHVP6oE&USW^(P|a zh4wa|PZ8gO8=h;?keB9g{ZTjtc-$NuifF2?OHx(ap-aTCK|uOKUcb<2V(?-mc?9~4 z5<*^fVxz!Oqv2qWgBt8jEdoP`fFUnKUNPgV!D30pE=cGCI^Fh}hP?7|Hsqzjk0CD+ zlePg#oGUX7d6kIEmo(y@3sPqT2M8n?2tsC;vGM+Ji)86eML2( zctIsEkv`>jRPqA1ug3>I=Ff_De|C>~JjeTphdhr@nVNA#<+S80uQG-&g?MYPj>yWK2e?w`9Ch;7A=TrBw5YQfH= zbB;T)QBXPOt{3+w3Owc9HH%VFpsgr3&-V?1qc)dIb+)5RnB+$;u0jqaSQkkdVr(=+ zL2*E#i46iWMe&@55VFrX5@#rpZ>H1pXxyGbcHi{LL>2)Q5CVg&jCY_&CpPMmRU!vy z{+La$`~75MCpJp(grSox<^29iBINZa#3?572n-R9JrHZKSG5QXAOZ+^odGraJOjS; zS(N0+3swK95CQT!BjiP`>rSCAeVh?W8jTo{RCd?g#t{mLy!sOXAi5k|iDy*(j95?y z;@cTvpECk4XT(@eSOnAm13a;@KM~Qb4HOYy+H@gmYIN8EzSKwrkq02_K+jBUB^iXg z3OG>S98&?1WV*}A3r_*=L_~CJ z2}SUl=VcdSp2HLE8}H@C=7xo{Iu9rX>@)EqOSwIx>qNulB z?WhOtFGF53D2BWQyusW?6!NZ>6j8wec_7wcuWAt(Km-u-I%vA|@mcKa&cN4aQIaDs zRQ;nu1jy@vkQcSCJB7OR0nSW(e{Ucr0F)V+1+~IH2)q!Y;mGUgFOac9Qr5j>_9wP_ zcgVrIKwbxgypVW08~|6`fmLs(3NZ{-_UYgapUwxb6Oe_512+We78JXnTkx3?bLICN zBQLhhCpu^}4S9*Ev<*nMg;z3PYSS%4UbUE2S;$-8&@EU}(5_HjfEe#aT9V&c8rL_D zAuk=#PUx0yWWcPQkQdq~YHOn4U=0G2I9F!mc|l?R{S7M7 zU%@OdxZ2Ug@?R_uvT#5U@&cQ2&F6Y7>MO(qAmQm`w|0bm9=aigXxtPYifE>Hj1C~SdiDR z`i#X&@(A3Rm<@mdSQjh;4-^4IUWU93d3Cg~v7D)vmLV^}y>3jma?lp-xRjCK4Nvh1 zbb{s*=gJI2UgbX@hP=8UJoZc;0nMxod1-3ZU}d7NJjjKOhP(`UB_MQT7L@vt8F$#F z2VxEOsuqC(L;xW#w{6znY|}31e17S&R}$maTeL0LZ(rmZl;82nMgG*H`>+fp$_NaUHZJ{_(IwormjTI}t(W-_U85J&FQ>c^9343D3%`E1pgV zv@_d5rRN>!oPo94KJWE)z1G=%$NlRK#RF=+Pf+SQ)H~q&1LiG($mgK%t2Z0y0JMoQ4px&p8rjQ;x;FH=CfPs?S37z~||a z#g@oRR;R45X9e6EiYjw#GO_LJ1@U?wOP1H485c_C9Ri~CZkaLe*Bk%?ur62x9w-7D zV|{#_9r7wRmUke_!JHUeK;*`o08^QGd08eaOqk-#01h4tF zh9V+S>n=o1jSf4&ml}y6@&IHV=$VPFB!iGwVg}K0$u-fAcgI;)(TR5xV1)6v%S1!zVTZNHWiNW)l=dx@_EwXYp}2Q6R7H z0y07Igd3*S+`gVj3;-u|h@(U_&R2-WL=Yw%B@ktBVx!|=tx6j5GUQdVq;aF9@;ypx zh;Q^jtifK@A~1jmAmp`2$ZHRf*ANeO&ecd0qBdiD+K- zdE!xFHxUt$Kr{yKRNq7-_#mmmX|I{U>7?I?`UqgoXv8z>d`t0!o{7Zs38fXa786Br zMo3lJLgckTKd??jM7I`Dl)ppgFS-!(9G+;i#GWY5qmkb`5n0Yda29jH1YRA?Q5i2L ziZC?{h$}8HJF&5hsYi42SJ$)Xn-d=xd4YrfW;Kx);Q&ly5Jt0-A}VbI0tU`bY?Oow z<0)Cn`E5%okVR-l)2(Krq&v^ge;M+UL9r~a5*T@Fh}1}Qaa{cz00XctSOgv@0*1U8 z4XfZ_h)A?UUUg4wWYi>gSht;H$V-jV4k0fj9>7lZlmetyy&da|4$FYWQ`@=fB?+C- zE!YsGTTtwRZoy~f04v%MdA-DM^LtBimOm@nGlab4p5cbPN)Qt)hpHrvX&NPoyZkd3 z;Fh5;MrGT8fBn4H9`x;}GQcPDDhvrcgw5Yubev;Z=U0=1#Qx z6VbFBA}_@ce$#r1J@+tDF7}C|B)38H zp#w*4E|=Dj8Rxa4Aw=?X8eS~P*5CDXkvqaOxcNzMd+sC&OYZzoS{U%nNH86 z%)V!l-RD`)(<6(5c7=facadn?bf)ml_fBsO-gFCrFn9-atk%n1;Q^RYxT zrL`^1(i(|)M*Zwdd^>MZkhhvyozGiMIZh`izJlrhVFMa^TMtk~@4oIr)bTv*0AFe( zg2)4qMZ{~Z7>h$*Y~t^y^px2mM2}fkCZ-594G2o;0_23Y#Aj3W!V%;os9GfCC9~#y zl9EJTCZ=%=Ms+|G$Z|#TiH$rPDtP(MY=VLamm{y@S$v316v*qlfWUad)uWSo+t(Ab z0pMf;^1-O2LNt!}nCPOAT{s{h%HYIC$H7{aG~{K-tE5Cj1Vm-;N^9Kx94$Gpj#~sq z83Ba6_6T|H5%LnEM^G0M6^m*uswcj`U-tFIk^q$!%8j|)R@@Q#kW}G>QdUkdS!h+h zTxpF&JfnWL6i?`xNIacZ8oaa@8BPgn=C&(Dw-y5wi!MaIs4#f&#Kt))*@?(< z9)h!&3nt)+jm&s4v3`{5Bd^+rGunxbW%xa_lE2R%5&c8N2S#3ioXlz>FTwzr#uQM8 z-K-=F$9AYkC^#Kw$Fc@yI)S<3lsODd2>XhzemW}>7!&(D7u@{&QZEUywRd28H! zNxaMu-e{E6z&dRa7;OX)^4cNfwL{2Dhyi(#s94mct*D+z$ZJ<$ED2C)L2A^Octi~y z1@fXbS42&AjGP1qZp?mJlrL9WBN5N2pDo25dgg9k#wJ>a-CQ9rN&%XSMQ=y960W95 zcxGNK!eY1i0O=O&QWta!J`;f~h`iVWw^*yCh!1>3$V(;*8Qm)Jinp2u#E@4C;E#}~ z^t51f_e?>%LPLVNco%?^7m>+T=@KYt%WY7X?3p4eI3Qr;40%bI)fQyQ=Q$rg(~uWu zQ9~qAyobT-q@QT}dRy2iXx3=Z+-S*xb=)E_$_OCjwM58kiIA5N1M(tKv8YR1Q9Y56 z*RsAC*+za}=1#Qx5|5~% zLS8jR8Z5TmP@Is?5MLUinEeiuIZIY%v=#H8b+j>wir?n`cXp23v9+UpQF*JtI_ly(wIBkD_%e- z@80+|6^(IIcqpQozAjk_bJR1fqz*~jgf2+T^uSq`mx@Uz%u2OFLW713d2zfo8oI^1 z9lTLO1M9R!V7w79iGfoRB!$NkchZo%q^Ze5UgYvk47AVoyXI;oyPtz=G< zl^Jct9AHIz8jbwkpNQt=5P6wck`hm=8UzHN?SbrQ$VHY<|ha#H4>ylIz=P#*eT1g#}-jLTXG@1=) zj8*3m=p=LqdHsSo#Y7%~A;Pioq66!>MPQ5(K*(!`vb<&}%S(tM%Zo&nesyUpswYyG z*Q~xM_=^yB%4M5Qvp=&qz8FOH)6BT>BF z;C0fv!Y*zq4@Ex5FFaL}1VdgTDh&bx)3jQOdf@(gGUO$q(h0K?ZFsjqLx#MD53+a< zgNK3J7@>jn*&;CB2z(OWlGj^>s0kC5>`BzkyuZKQp9l|;1a#}|h)NDogq`n8M28W) z?Jo-YyuIy!c*dfxeLn3nh7(OB9`Gh?sGs#uQlK}|SB0oM|Ms?rVtkGKzR#U#w-jIF zbJCsGm^KjAiqzBT01kXaB5lpK&eyK`a~|D^jq1S1*Vi>GS=8FU=G8kDRhJHjJvtKf z#^MU~Q;L2+!j;;haJSpMC~>>puAmVzq{!=YUphZTEDfJy>c?w$;ynj zqK@Zb2bfhO5r7u}vgj~^wHzZlE@HP9xke$aC4zwB?#rx9Oc7`r5D{eW0&=q1ys)Xd za~#O3EyBILB=Yh@Y+@QrLcDarfGCjVO5zh6QC3pT6B`MTaO71yi;ugB0(pHG5ExIm zdUT@lDqmi}_VvVU063X|d@w4h5RD@~Cb}qO7Y+!BGB~l(aj;e;4S7j!)d{nbjWmQ~ zhP*hrx-qljoe5rpE{zcySf4EdE?$w?`fx_T)|wp7nDsfD z7j!)0ZN(Gn63Ppr3?h(80uOwATOs5%Jn@XW)P;CL4I%M#T50goUSv3>NKaWIy0sXf zm_e;%E|QfQZN<38Fu%hS8|Qh6Xm=vAoQL2n<|PjBRzNf+rU*0*NPXnB0M`s?3n)V5 zB~z6gwpp8)1~VWp91tHX@qv*SASbh$$cr!lrZEVkSxFI?dw%c40#En(*Wj(n%=eQA}Zr02i9|oz$hb-=vIcj zwn>c4i0&k2UNEmnymTU}wrMY~ZG922pv(4_;t{oe+-4+!Ok!O`O}31j1ZRh;zb(KU z4NyFzes&@5P(!=9j?!T_SICP}faVG|))xuM%nQoJZu0@sE!ZXgQw&~Px&@yZ;Z=Tz z$crs-i=|tNc%wXoyx?A5k_j}$J|$xC)^CS6N8muU2SLml;F5U&;6OOh?NV>o;UsUK|^Z8>5Yq z8d#?-0;7#UqFWjAS|%}LN+f1pFt13wbRw#@33)B+i<)v=E*+f1SpS(7CpK~>5Rlh0 z&hGO&>iiqp5`TbV%T4L)S0Byx_3qdLb=_FxKgHn9x)Af9b+j>wir?nS(w(WBkwXzp)pbd#YPnk?ehmU*$cz8XhM0WCdlVvHMB@KBA3Rxdy)h{_3m?S9BHCS%fCK z2#576+&z%DvUOn*olo2kPQ)zA>{QZr$CnFH5v|f zc7jU z|3pyyEL&G6W&;i=9H4kchck8|9(EugRbq*z3X68CSCoV1^A)0)={tA33`Y&@0!MQ^;jFp)=~en8+K0zR>kXF~;VlI83r z3IfRqAI;)foNAgVkk@wsf$@Z^NAod?!v(glCy)cc2_542!-U5@P7$6*5mfB)U}^@2|#JC*%bVC>)@8Mu#(YAs)6MAXTCQFT|`CGKa^3EG#1A zwJ2~3%NbfAzv7&TkM91+pm0nr<}{^}zjQ$V&#rvb;)) z7|cK*ime{0?%@L&;0bgMMpH_6VB*CwH^Xb{7RVvchz8|D>`iFs6rZs~wH zF~J3Ixb&dyb`!IN$~%eqJ(rte{(S9lFDIj*!Zk5LSGT9mOT1L{9?nDlv$RI*rhaT2H9&vCbiEimA zIg!%*=fZ`c#O5cTe*nl$vsziXiHZy>{DnyRF0>znuiNkD$ zkk=4J{!9#jkcK2A z%1wl3ZW|$(<<+*I;?$?Uj91k_c zz`ldxOSv^+VZFXC`MCphhJjnxt;7{A@1Eud9Qe~4N#tkJ6~{O}=Oqh!h0aGXQA7#` zL=OkmT7r%8Xy62@Tf1fXQ-0K@lW5>Hx1nXKtYlGwS2uCqHXu6k^Z68rGbrk%#DqB& z@^YKpt1K@Q1qkamAQGcJKil^89IUw?tx|3;0_64QpPT|&me*)F*l5Xtb=)E_$_OO7 z6%5CA&gJ&?CF(h&o$4#ZvR1OrV7ql{D1BcV6=KeN$o7a}14Ts-ND$k?ha&xmGzZoV zaWOy@((;l~JnT0)bRpZ#bN4EAuKH30#A)LZ*=zc)Q|FPms3lUAUR6mi7SB#M5;rf& zD~u{}txE2<+w}{Z;ydScvy5Vx(Yxs+P2g2^*WOaLexIT`?9#pFLZOe{>uX(cv)1Hf zN1~X!yU=5CR+dZ)Eax*e*LKeG<%LfzixNv!(iOftae>~bjYyQ^MLl!5oNJLL-%!+a zYd#l{#5M3D$w|>_o6gfC95Ia9gYG9qV z2#huY2zjlNn31Sek=#{E1jn$L7b)!Xs--Akj)zt&C?e#w>P=)ch8#DkU;THH*vWQP z;FJsvY_}?&9_EOE97iR(Q015Jd@eZq2Sf#(=$ZAA*Gg3<g^JPIY?7E*ikmB| zWFf~~6bpJJv4l>gg_3YyImil8T{RHXIFv@kUS44yUw*o2A4+n-lX^JQH&M*38w4cI zy0iNTm$=;3%_0~Z%QPBrp!I7PJKU62nppPMK` z)ixk-;1FX0n1Ccy@~07v&xx{zvIsOoUV7v#%S*+?ke5J5L!?Hci-V=QqgV zDR@Z+xwhM7#<~iV#1z`XaNK4gqT~S4ctjaG-}w$i#jIK)FIoYY=vLxgL{%2E1-KOQ zqLM3xydFs`0aUV9i)5C@3M`)5v=l3l7nt!G@)EP^9&sNSjw#v_ssTB{h(^Am^R1+a zN`ruOFmP>Qj(8dG+A~E|08f6YNa#|OzzG?L2_|WVyz;2rJ|J$K_+`k8$AdgWUV_sY z^7`R`jFK8yr!4}bjesF9j>y__Fo|fK|DTZ;!4$Wb4ZINYnjz#h8c~MMkthX-ZZ57= zm#UE0dhEYaYm2-(>8FT= zcOSZ;n8lUd+YTH|{W4Mf^x6g_r!(GmFaU~*5>%BmxT)C zc${}18l#Pt99YLK0;7z8AumH-1j>9q*p1=X{UNVxrdOKsHHqo0rjIO^P%BxhX);Tr z3$X%um7`TyZlhr_t2Cg|TR;qX@lcQEr>Opp+Ox{GFo#Dnc=k*Y6->8sWRp0Uq6AJr zC2vf4tV>x0XovvJ`RmVLXf#1dSlvVwy-q?0wy*#7%TN-Jz-TbqD5-&U+9EL82q5G& zOJbQybe^HTys%i-O4fOn%+hEgiu@(dsU)HaB1c%koAoA2cHw-L*i$X?g^fPTYYyfO zB$Z(dmQQ~YQ5piss^29Ux|=6-!5lN4kyV}L1xHT4CNUjDUM1M_Ru^;&KGTqwnC!yC z+HNfx^5Uap$cx8gZp`Y31Crs9V7l2n6xB4ps-z*WJbo0V8wW%WoFOk2lTMhGYQ_B+ z>WhZFMoPDMw}UrYa9|y`2#hlVa%XktA5G*D7(qZBNZIXpxBcGvJ&A_{Xh?~!mlfjc zE9-nH`TFYa`9&N|i*!CB6gTU}Y2fBr$q?_?*Lg0|P|&qm%!>mweIZ&fyk65zYqKehNVxpDaEk@a9cO88AIDk( zx13I%!w9fX{i~y|yAiY3j3x_Rw<1UJ&ZC$DakGG8{6k208W4TdCJh|eu8jtc15rP4 z(@7k+bvba;rxG%|yu69u;`&ej1K=MkbUuQKJV>uFAaj0TM5rRi$c-2cTpFXopg*oD zKVfj?|KcX)z`1dY0~d~JEItdU!>gOv&54agT=UyB{i~R%&rg25+=#i$&D;N?^>Kxm zZvO8ud9jL{>6=(apx-E!EH8Zy z5$HGq?7u3_8FvJhsQdRPF3@ZKS21jqHh*EyjAGhFt=Na&{m1@G63gV1*|f9^MnaSC zX)ytJ|4}DxCWpBlt%Db&QHA@#=AmSUjVc#^^r{T-+KxAGpagYsdy}#fJlq z5ftMXv3N#N9{Aw@;<7)kXySwaA~^|vY+(-m^e1ZwcFDv#qZ!AD#WRW-2T5ieto#lK zZXz;X{4`JuT=7&xUNQVX6p%DpnE|)2_fU&{&@kz#xC$SH>T7J)m8K=z;U$!u`qSgMm5`TQ-~ z-ypAqy5cnaj-uP)t7HLBW^V&G5rgL330yqYwoB>VGQ*yk1;d%~uPnbqaxzuZYBy{7 zQ#{VQ7Gx2XP6CpTSYg9~lRU)FFY&;kT%);(Vl>+Z1P)wAY=Y_jx-t|HOhaBGPA*D9 z8Uh0}xdYRIl+aZ!ta4di;F?T+^!@JkVK*H(LtY~MZU1D%^JaB)Zc$z4p(l#Jy;QoT#Mlk_HULsD8oCwK~m-^YbD4ED3 zfCEyJu4EbVGUU}UD6q#{1nwgO;y;zVYtsP{l}-Wz{;RTyB)aI`nTb3CodiUUQnrSX zAumBrivR58Jq%ta{j`R71ZHU9;u^;PIdIP~%WE_niStkdZ#10;1J@RLjpp4)J#g%= zDmWAL7YAp%7c8Fw7B!ylW*zR1)XP3`1Uq zyzYZX*jrcxI*x$&PbG7EivXC%&EV;b^?PLVI1#H(0)hrEZrjD?c;Y{Ozd>G|^i#yb zyARzE8aOlopP_*>`}IqWrfc0q9)TMN#1EAGkjTgRquIj! zkvre<{N5gK5wHlb2#EjIF}Jq}fO(uDFB$zI&P>t38S)Y%Hb^6}3yQ@{mZh8pMK@@?7KC96carzo*B05B($r$0Xdd3l237bV) zh}ZvXMz<7U`U$#K(3f=c&(wHp)X2;8EdBxM78Duoat=Y46QWyKM0D!}MMSqw&BS+p zU43<~y|)*|1IeXl0u)tWJc8;MEP{3}dnh8hwTB|2TYD2Zq^ci~4GP_cKP`Azlyu8M zkrfmr-SQ?_Ot4slE5A*?R4Vh! zRBTNdIEqS2ayaGrQ$iNi|VWw+_uj0i&+I`j3m^fev{c0JCZ!?m+bm77^XrA#sc7)*6Zk(X376kg9D!_|t;7 zh9bxd+P**$0V0(f;~j3KWW ziQJE4eWHurHRPp8ln|WT4X-N~#wF|J3*aqCx1eaqt7LXR z!|OQhDxS&liuW+`Vu@Upjv+4{&?d}E*Pb`2Zpe#KtJchlJuNs*o++V~Lm!5`I1#!r zo`PvwEk*u}IX0^ZNCI7@F;mGxm7oNDF+mAkGbp0mt(l1l znR&W3lRI=db%s)^W_r6JG(b?RLSD}(_3Ih6Zlb_mO#&hjjhk*uZHi{(UW02Gq1ZP4 zN%e_VP~PD)4SBKO+8xp@2*=n<7mgt>LtZ6^_$tlPGO>(>x9SH(R5bVgB4EhNkXHmf zhP+}V40&DJqNK6#I!L#mi0IZ7iimDaP2|X{A|NH{O4bx1FSu_u77^W=LJ`resfiqU zO%d{%BIq|$V>2~ws@)w+$Xx|T|-`yoP0^a`L`8O zk4(F(2>heAK_{WM_=QS-!Rw!YvfbKa2`K*k_wV1aK)V0^cQQ-k@4p4Fa_*PgT3udP zl%<@Xp6;BF;;Ini*V(C^U@ zOuV?(MNqC6AvpIcu|-o~y&~<}1@TNC8?e(#sXb z4~Q@5nJ+0PFB!|_3&&#X^^z`es-}}j)YyjdzSxP4q2T-lisgWl6-$~}MBv+igfH3W ze9GM5%%*yb<};YH_x!1n_GVKo>Vj1AVZ|wWcPP>h^g2paq9k*aGxq!>6UWGJvVA?I zF(&c|fHMN%!2ylLzw|`|2O!HIPy_}457QVc$s^E7ls*wofFdernnl1O&@2LoS=C2g zpQ!pL0$B}2hrArrNY_9Ae11Z@1;mdJK({Kz0%fI^h;cExReVt;MqF5OE5dE16D=(g zj`>j3#xWWwI(ow|VkroDo$8Cln=vSJsuWoz6&ETaO|lzO?U4+@OXUh>~u(ng8kzMM*T=YXYqk5s;J3=7q6~!V%;os9N;M zE2m?`f!wt~3mflH13(Yx2bOed0@I3Iu$0xxjeE-0dBKAFfPfbbM#RxL$CUYuApB2luc^9^}%Xk=_n z#N+?|fOHFr%_jC%+9F^PKoLO9%8-}iUL^8&!~vEB zc|qHk2N4nSQrSMPP+KwO2xZ0#IIwX64`+m8gA*GO2YVT{i0IZ!OVJ@Nc|p1bz{$(3 zCh~GP1?^OiMaXSTN~mLV^}lZJ@VL>Il=mTtjk zW~3{bhP+BfFeBPisfR!N2%aE5q zTjaK-Tl$Ig%$9TuR@dOgke7-{0}`d`HUnAps~hs-l&Jw<;uV8;Y^MFC=*d)Y&fRbMpZ#iNS2Vt>eMiL$)z zP!zPwyUVeND4}ciAR@w3vo6HMxiTZq3kq{_#7HW&2};wNJ&1_t)~uz7kQdmDYd)_W zk{VxBA+KkY`t^(mkBK5G4FaOTi<^-{5lz)C%S(i+EeC_ls->t0&XAXii6JjRiEa#t zL~p!1?k+*P1w}M{H_ako5oi_xLtciwBK)x|uNaBq4%4(+iZILT4(S$zcw?bku%C!- zO&>%=bZgp$n31kzPF2ncArrW7HWm@xnm&k#=+?BQh>#cB+k8GnXbX>#Drv||04s7E zFe_cTQFVcAXa{w=pr|N;6X3R8P*l$(p)DY~zh}0jTd=wwxWAr$eQNdj35uX#lht`v zokySnTSHl1zieS6r(vVvV32M>vDw7lN?Qah0w@B8ybO6o&|}CeM#7NSr4tGVQPNn% z2ccW=HM^i&@R^BoWriWIl2Liw)PUEOHN6$8w-gO|aiG*6iQ?@BuanjlHF6VlDDs(m z;X%3uMfFS)40(yD^tQ0kkeB#5Hy}}>rguB3Zpdq-bc=U8c(#2#q7tTA1UierKeQH( z4jy!yOqI*;oS3|5V37_}RF^wpQCm7dx>r6r8_6k&SySX<`w9Zvzs(WS{dXbAvNOiF`QO^7&IG?aiiG z)CH;J!-|wmr{5ilbZmewStSaB=8o9}yWdYHwtc|D zaNY0s4Cuw$m127HSn`vqmq0M8B+3qbe7t{t#P8$Xi;RGgLd*(mO0K)fOShsxR(ALzg|GTaO|l zzO?U4+@OXUh>~tOh_JdtQBo53nn3GB1mtA1d136La0EFCsun%+%IO&48Fwwv!p1uk z`KuT2-cD2?%f%CKU#~$HZSa*XQ`NI}rZBoyJd1O%D6(IOiF5vkPy}RxqJ;Of&&DX4 zVEcMPDQeA}!zs3~Q9!1)d21lbkXf}94S7i#)qq6FstrL_{pyCiINll!-QwL2o@IHB znVwkRM;L*}KwcMstbSLC8hKGkjl9kPd3~KV^7?xBVtQ(k#dQYQ=lu-0&HEXVlQW`Q z4UTa{bgN=vBWV7BkXMDMU+H=P@7_ezk9@x#pa}YYMafrKY`JY0tPc0`S|bj2ha!Sb zc4DL8Np@mmwy4pWc4A}6s2pBekNd>N$WZ9c02g;0PC+}>W3hlb?1o_uD-v0di97<6 zY>c7_ke4GdXsT|=t83&%QE$82Q4gGLU$0_f$V+gVaq}hdGTy!M8^LY6pg2OhU|qHd z3@!qOybAaeZjOVcD}8fYkQb*=&g&22Mb4Go4S5NU)#ILHD$C1|m!eZm)&}az zjg}VPwz_3`adLHIX2m-byaru@bPI}uV+YnDi@*pYV8|bYFBlaA6K!#Yvz(L2DRi417`#sUX~>IHrUrb8S8R-@x~LB678FNF7p%(` zfx$(uSq?TpaI;r%d)&gq&vZV z0@=`^fpkF;&EE}qiBK8x5gsg5V@;7>LYIVMYFfOMu3n`SYaFxHj8syI*X;Xb`V$)kp$W%0))UeG^VgAxmlTE4 zxL^_Clm?oL<@I{GAQ2to_=ffeJ04I<)%ih06#6q1-@tVS+FJwhq_4vPX$N@8!x>Mf z<8k{SqK*>1`w?7i#l5^77~5r60%bY3DL~n3!Oo;}jz6(6)ky}O*qH2O+?hxLS})Se z6~+&UY7aN}OmNiZa;eUCT++o46I?loM8R>$B{IfNYzzhGFHmIT9!r{7MBv+igfH3W ze9GM5%%*yb<}(N|pFcI$oMKTIoK2q}RwT5j-yMo{Y=ACVB?K8 zG8oRUFCvh2sV$z+CTzz&qFaw5B4)J*;;{>H13=bpBN5AfzuRpx#FEU7q+1RmtnN^h zl*GLz&^i$TIiV998M`PPK~93IMUT94I!1WLT?>>Gb%!E<_2S*zi3()7c)kE0GKXTm zGn<~ZGlkKuq5;mqqR4(BCeHaELJ^P&iW1%uKijrGnqd2SLMdvODTh-m%S%9}AJ6it zU{(-h)fWwU8S*Omk#Vpx7T)S4AdqfBafEcix@-{`Tm&8id0i0l!eSy-Mar6^T=>Ms z5_u({^>rrXMHPM*NoW3H?(2oDyq_bi$?s>tZQjm^Zuw&zWoaxTx^?J8%wHwg z*8_O>CZc}i`}F`t84Ty6^~tx&zCsQ)yokXOVd*?m?M zc{!YdcB;o>0d?37!yHy5vLF+A1SZ*lMH3(|$GapX@cySjKF)oDA}HA7Nr+=T|G8=oSD>ZRr+-V~}n^5%dVtEfnM{Vpf;h;trKGz34L)YT1nB}~*5I@QNhU6f4S4#p6W7fa--bcjY^x&=8HCpN(| zrtMUCwBLybcI?9Z;6n9%Xs$0nB;d18(z<*2nGl_30KYX~>Jyu-hW9PWFmN zD=2T=5Fk-+OCat#5i_Wj%w@7N1MFl&5@J@f2N4nSnsp(fEU!E-D9phtK?(X|f>6@z zK}3YSW-UcTgu!N9emgESAhk4_{zK$t+t&+_W!u*abcCsqDCFJIlr-eUfl?j?%O`$@01}iRulG0lGDAkdUkx29c)2zgDjT$6;n;J(>dM09KVAR?k$ZI5w8$P4XlKDQGa`@mD84ez$)U*aBwi+X7&_gy zB&1tVY&NmC(iQ=W0E&P|UX1#XkLM(Q0k5xiRCC0D+ z*PWjKJM9u54HBbS-dl>a<9zw7XhE^~vgmpAVwL=^PHc2vqJ@oUUE@1G#*toA9nN^I zFABKh-OGr@%WbbCX#VX02+#3=QmW2S1kb#jA4CM9dpoxjPx?9>kOq!+BuO65csd=A zTPW&hQpx)hkKo#Z*zL7Q=VWfxS(&@tR*Md=Wou5UNO9%drpOIjE!dfK&haNU#wz8V zPHZflXss8~%I@P8DL}gOKOm|T3fwcnQJc%9I@{5$80LowuEHW>ur898Ke7`WL&5nA z6xq1Pk|q`r_%d>mpjiYuKwck+ zZk3`x#jpTE|NO&&k&}NDw^4K}iJ^Qr5%I&7%rEyP6yK?2(NcNCB6Y7UQdsX$EZ)Jt zPxv5=MSFS4>z%~>3S8@okQb&~h(tlUbwbDsi|Cmr6L|y>$hy=PPkVp_j(bG6u!!i^ zzNLt$(SC5^5p}7pxB)a~w^86_zuRqc#uDv~q+5=Qte`0AmM=LLF~dE{TqP?rx)1?5 z*=$}IyC@t%PJ*gMkGyg^MtH_u3$(Ct!3!28O-vE+p;I8ssV^v135xm7Y|JS%yPuG5L9yAy-bz~pECMJ3!$V#!XG|flBnAl86nR|`^1>nkD}}r+ zNId5lE8?%S+o_&*!7sAeJpFq2VhO?m@;W2rbw+7kA+bkbi8G>G4UTc-Y$ted3-?DB zcZe@xQ4EiBPdkQF0y=4OVk2sO-BJYod_!j@-d5b9F0~dTyvpzR7)L*~mmeT6X!`=i zj0+`G<;J*gOib|!21Z^BaLs_WfZ`m{trsXJwi68h3wSXxMIZ!3{$V&D(k%c^US>6s zm%}OWa7HK=P>0Z9;(!a%EhzSeybhjn$P^VrUYy!> zf#xvXLeOakP?B4x1um53wT2>U$dDJ0bQ2DiqpoNh@)AGM8&f#6ef>*qx@E{K(W#K4 z)(Nj`iM&`MSEWOQ!;lvTQS<&Fo*^$z*xIIP@uvl^!82RZEm&RBo#$smUMePryf|JO zIbRYlYAr_0mEV`S6K$}Q4M`}WYX(I@k-R%Ikw*Xr z1R<|HFDT5#D?thRVuBL7W>74Fk+(eCnVFa(5CVdb7ubwzK1ZWbSF1u^&nV05#+$th zGUeTIcS#`{2Lqs}D1j4TLKhU(Gf8N`f*zzgWL7OjJ#dD+R7?zc2}*QhKqPuI1ZLH; zOOS3svDw7lN?Qah0w@B8ybO6ocGN|p)Tp1?Tb7q0FHYTB(=Glu@9i+%ikK_E*GFFX z$VJX}g4fCUhWFPHLS9pqb3)t%?wgH8M7Qwiiizz6!?T^Ki75ghAP9M(z0K!S#JBJm zsgeXUV3wC5F9Fy(VQUH{IT!#%MG2e$=@t~#Gf6PyC8E-H4I_kOdf@(g`t_;R=O-wF zf`i=b{r>ui7B>EZJJmz6C8DwFJOXVQAf#JRMALWEECLpRW)U#tWys5r*Yyg~oFOlP zj!^Q|TL+>cFA1|Ag_s%g5_~DS!59LCGvvjoQzy`gHxa!48WPNf4h^IWiU=tf@)DtH z3-KVcYANc0GvuXWGL|eaLtY$2jb{NSq+3vIHnF$T76FR@ihw`xkW-(}&wu?TR4OSH za!GV6?EDp%?m5w2ymZih7lr6MS{L{g)&}we4ZmDTxoFToQ;HwD92IFPszxdW-fdsZ zo!IDJo7eFom&ij48}W&aXtm?@GlHt@et}|EAeonBWyY9AwBD~r4ISh7b`ZeGxyJ+A zw(AT<)c(k&>W>APtb0gFJh2zV03;Z%Rfs~}sP z5cwWSK;*^nLC~z|@B8ma&>TqnAEJE6&rhljL@iNCEE3%k{fx*fKG_4lcaOZvHwXml zS|533RH`%)-8w6xlDt#5w;%C;~D;QNml|tEI#*FW|&RLMbZM$l(;r@)D5g$FsaDm=y$B^+iKo zhP+C4(}3HQl}gL1m$C2`4hW=MP;54_x6&2?ivWs%Coz)f^p)jR5F-wbF0M-gC4G1? zP@|G=k?5IH_$XeYN|f*T`AH>#s3j_i#IsB268-%0qDy`YH3IVTV%bxVye?;eye{XG zKNS|yh>$QNu*4bBtpgO*Gf9B!_=YAD*a@dq3c}Ny*luCtX~%F%KquQRLS7GjVq;`V z`5hmmn3srlCnf#VUVebQpzRA3ODt4cDmTV`Wnvj1Z(-zRCpHS`l-ywWUuq|ZGs1@C zABOWW@&e>!Rug$SoPu_$$6^6>*bT!RRwS|@6L|zCVBi4qa=c4I0`J3ZyPzo8kaybx zqWgQM0*e&owi|rufwS%FRZJ|)s}{Erg}xy?R+2~H4@kG5h^FtRSp+Ns%_3mPi{mUv zx1iV$@}fkiKzL$aO1wk51w}(%9GTT8X6=xdTc-ssLtdQTcEYS85Xu7+ySRz-YW5|nBDChMD@eFx!Zgyh| zXUL1gs3DRl-oxN^(oeK~y)A6yB+2&jYK8%3QrO83d`15#t=D>Zb4B!)3Urc19KkaV7(!)hd!|}$5PR5aJpiwlHc1_2V2=J zmxyl7peX5%^JgaV2;hJqF@W6cOQ>nV2HrVb=^HFR&Tce2zw= zu2zM-?8HWa!aCX0o&z89+!P*)Xr^xq8%3zv5;H?y{Abn)O5#lfZ-@;U^5S?4FBs%y zf;YsHFrf>IkAfuZJuL!VM!=AlA+N~9Y+<87VTQbTLRJ@tGrqOOQ#tC2cH3vxMqX2d zydLFrMUJq7*Y@2J@|vog6XGTl#7?k?=++d9iR}c#vz@7lDFPuN2zjBs&F9nlW28zF zG=OUu4S5;zN6L6m=D1w5|Lj*+k*H47J?8HWaxQ0l#L>Ikl z3mdzn8}@9AfQo=2FGF62ye!L0u&uXl=$2}QgtmApVy^t&_L+vfIL>MdviQ@!gJXug z1Yb&SFor}=rRK7q{E#2#O>?L#duVV;Mm{s`uZyNZLbm?Nh@lXC7sCjd;gnx zg(J6w_c9bw?H;udG1eeBv5~&_2R^cL&H)P>->=~`NB#*#xPAQvEo{6*qLtqtSQMFD z!M#GY{?U-=7{|8*AUwwd+RN(~KsD5A)} z9pssbz9sWu&zxz(cMV>!)C z^eksV1t?oB*qLI?13D8BZ|qr!Gn&kafW#pfRUCVgh?ax3CBX{t`BJ6W=rtK|kO2gAr&yMkfJ{H0&A3TcEcX7E5AOBCZQUUc7q2xGs!!MN!Z$?_NCeD%K-uR7RLKhSV#SN@87J-pQz>rtTqMVh{t&)3XVj^5UAumdF3WP_ds6e^}MTODo zL#Nx8969oG>$JcH=@t}Q(k)p1wsZ@&9ywKhZ_BLmXGOcgGts1e4e1sX!KfSEl0m_^ z5AEgkV#q5oF8|Byk8Y)QbGC;5IvOA^mdI7<81fPr%8@87btD?{;?SyXnihXr@ESa` zCEbG6CEa;`HsqyZV#rHSq8kGu(VHPKtCn4YbPI|jB@EVWi@=~FV94vgBCi9=@;aa_ zue~c-HWRYE_9)A1ukaKhudr;5Wekx6=@t~#Gj9vB>bL$l$ZLs^*Ag+SwjzQ~%iD?( zN#*yp`_Jdkigtr%f~{$wzzLtK=ydu7MNn|sfCW8B zb;zt*ihAG-d8wEf@)AVHkk=0f1kx=i4vHICXDk9EjesGql0`WyOX&I^Szd;`1WT*A zT+xu1Mp=fuB9|)e4e*qx>D{)~Ya_2ILSFryZ^$81@W$ObLS9pqb3)u?g4hWb5#5?X zF|nOsc(yY&F-0H*1R*c9xA}Zpe~eT~f*EiPqaiOtUI}zP3LI-mw_tUJcPPv2*QZvW zpP&c|-g;m62k8>p%j?&tATiGU1B#&F{~5>40#E@l-ytpfx;Q`;?${= zJS*Nr@cL^gL8WLooDl7FM~uO1Frf>I=~&_s0YuGmx{?)vb+p= z-Hcg@hP~T>m_fP)#X)fc>x@NUq!B1nnKJ8Nf8~qu92ra7SY-wjzp?_!MB*nDQHr#Ku&mG}epKPS%}?6d?UZKOm}2*xWP0xdY3kI@{5qWPX_7D$r%HE|T)T*l31= zbAEiMiLCVr` zelmIbQ!yVBhKPM0xt}P@>(7?p#j5iNG+x$?W z77@t0mdLB@<`td8*g)j@UBSDSk1wrEEIW4v$m{FMkyjvCSB|`*2=EpFF9oLISb>gl zM9Ax;Vb1kB8j-(Z@J=@s5y-k=5zv^^9x(xU3S`)>O}uL-BIxJaesJOub*Zhm$tX-T zcN+y>h`ela#uDv~q+1S^tnN=t1Sm6BnVrloL_kh98%{Y24#IgzrRb1X4#ymxao57c z6oG~TQ6S6J$J^JVRHb}pHa%-+3PWDSv-r51IIka&35qA)5??JPet7{WHWEQmsYVW` z=zK6LiSw5vjd8w}6j9NKGgdGw2(s#nhP)(78zSyAtt33$m@cT7ZyvZR9Y8=ye8}wax%} zUCwCx`mQqn+Wj~7RB&5_q1a;C7_e-7QN=W zHxcynZGBrYGNt_9;B>{jTD03%_YgpSB-WD|D&xg{XJQtih5>j<&DYqGfrhnB16y8S>)j$&lAb*@Jc4B5;EUB&?GghP?7ZFHFE2;z=f(C@`|vkxVL3i;&I+> z@JuwRUxTqUx+R0s7WWzQN;J&>2#1K~&|gOb!f?|=|7G&usRz1^@7w1zA z@D#5YytdU1d2#M^W9Y;?6TJSqWXQ{q*9}O4HE9vJiwJPGQ+S5Fcvv9vI-o4C1IqH+ zqb#pI%JSMPltjoYEO28PL*y9p;_L`AtCnJK$g9Er^Zc!Y*PuE;b8pMriU?1&J#w+- zGr?9i%cZ0{&YzjcBhWS=2zljsL18Xl2};lxlLV2nEU%e~DFPm0H3$eoUSKn>`5cW# zU9Adv^|ysP0z{9}y5cr&UJpey{nsU_YFq51T7RHt8uH>lv;O!JZ#Q^1ZrzZVA+Nh& z5B3rkfg41CvmHZT8Qm&FlZoXTMnhhL&$Z1lLolZ$vuau0ke7gAoxrh{c&dx)wUJkU zM;UUm6udiZ9U-r&$~hr!GC}MFi;3-I$ZKk1ihxI04FZCY7uwr=KCM4Ssw4ponAG$r z(CN0oP4Oi+*@q%x0=nb_;VBiRA9(k^kk>CXnl~mq)+HW+CxpCyaS9Z?aj*D$kT<%) zoZp(X2t05EO0JVxmgSWNgG@tSKTNm8Jn}OQdGVjwLsK|IUYv%Fo9sO>d$3ox z2;3k7ob41|L|!SS$r+y?{#&Mz2rjf!{YQNfJn`1j@46j${{ZJO)|alRC=xGl`+9uf zBjQVsTGD5d$$*e}MJZ;N`sYQWV;tWO0vI{>2+>?Pp+NMZ6A^Uj?c7on{q}DC&Yw{0 z|DU}x(Q;Hrx^>@GL)v&w&qEG%PXF)!iZ>)7p@`Vdh9ben4puLt*cM1^InOG69Y|2z zqnc1e&YyO2cSVYKK#Z&9$+NzEzh6JIh&Ci$OV=pgYLVW|UdvatX{*e&79D}h6ZsO6 z%Mncp$`&)WC!Gu2j*aP(s<50@-O2Z5zOE=b~$( zq=iJ#!0Pv-ku6`($=ZD`-H(vhPfmd>%WE(k>;`jwYtkHeVh8#{UKfPCu!!)|CGTgf zaQX8mY`Pc)cMf8%1W_nn)EZrWUF17FUP47F0%(Pp)f*HsiPA`cwsAyjc^xFoaq-=W zv^<%9qPr9k$U0*Y(3ry(F#(u47M5!hu5==Tex9~>C+<;~+KMYcV>T-VUWmM`a>f$N ztAs|f!XhiDqKV0hP^fPRASbI8ryL~*;XLH3=#W*kmYqo$P0^rIlX7E7Jj76 zvhyE+yn-n1L84G7JOk}Zk<+bXxT6#S@;UE8#i=F= zs~Q-BUTmDqz)=8g21SUx?r|r?#GpdK|Nic=pYjjG#Ta=3a#GYpUJj=qVrHUnr4vKI zzyajtNQ{I8KGuAv5#h1RxdXox@*BeFg%&O%*(WHK<$I|GQ@}vRXQrD7zv%G54Ekj-vHuJyX zPKX)$>u7+ySR&Wez>t?fTNHLew{$bqkRdMtop>0c{`R0W77_9aFSxOcAqot6ainvP$g7%v zR;4-bc8K~D5%TI}AIAC(Av`s|tq7K~na>fTDNN)Jv^4}}dF6RQVJ?0Zl%Ow0)gPZP zd16zTm>g(o2tr<9F|O$pjYeJV8uEI;wc-(Uy76Nw!sDj!P((9*U6U^D4-lz-4!(6m zUi@d)2}B5|b;8}Ow&yxfpi`fo*35|u6th3+mlgs88}i~mv!|wThP>_{Wbqz`2m`l1Ci&KeIbaS%2NLGW3mAEY8Vm;gla`Xt zWVBHIdwnsxQ&!Bom*juY0`v{Ubff=OK<&V5z$=VRB)E+u63=kQM!ex;gje}_*fkM! z!dm{!uIy_8F?=EKQGaJiw2k9wC*Y0?_Yk9n9STJAors`Ir(;V|^xKE^JAXi}LvfF4 zLJ@8Fc-kEFu><06qxW{CT(FhCdcR*kvxqh%UCT8GDc)+4-ppRhSG8%Y%(WIBmdi8o z5~j-uPYKEvGqxw43*3&4>5{6joK@vidN7d;(oggaqFS!a-4iUrHlM4#9bNV$-(heO z5-P!ak(BqvMl)2L^6fiKY;8!ekbO?aEDS+`68Uhl<%`PMD~hp++!bWVxVA{!2JqXt z=$a@QAQ3dM`u%8R%hz+VMtcNE!NoTg5%T(3pRrg??f@oHyiSNZsTn8Yb~Xphfj%6_ zQBo=+nZF^6Z|F*7ka(9jSCvfTA}`FfrW6{37)Ha%-+3PWDyz4*ABIIVBU2>A)$ z9<8~2J)snpf8=nAWqAq6)OH;+;4)-ZEk#3Kl5pLaZprR=;AY5+^QcZp6mP`42!|K@_c#C=?3IeHi%{S44ok&_0ZBb;UE> zoDqu0kT7!;2SQ%ozwXxM^8NQbpCMnmLlNRBECQ5t*f5+D(8+p*KvsX-ID&rKj*XFO z#j|Gs>O*iGAF7NOrPSde^S$dDIjM_mvzOt*NuA;N2UiJ89- z`=eW^pG!ktk!ywXl82>;CiP1+nhju%UCtfA)*13j=;;gSl=_^w-eCVfGAur{LAumCRZVZS-Z|;Fv zwd_)wiCe)OFb8^YAYrb&V92Wm^4g(Ax}dn#HP7d*dI^yb1riV8T{f06M1dhMj&$x3 zd5QU^4{iB+LEQ$ue0{+&PKZntt~4+NA+J0yD9pvLf)ez_NcCy&-M>X{9mqTXNQq%)y$V+)*$V(6% zLtfu(NDp&-Yr-5b2a*Fh=}KioUWU9Pk+T;YIa<52jicZ=F5GO=q%tcQ@)A}J8hH(S zdqhE|e0bw0Lda{Za!!bwj1W7)V)obPik{evO-v3nH3T6qw6^(lTz`yIO`;qysp-Zn z=nnBz7hO`&34$y|2%?}ae|Bco(vX&P3tk?_syhGv^`i=^(mi6Is&`fCS^A0A^7hP;#~_lVI9d2y0;k06WpFhsbYJP#SgEB5n*)pAzrUfH`W41TO6^@B&v3^^ zyy0VnSNVC!H4$>UrsdD<%Dx^D!x!={2PwE1if9kY(@wx07w+NuVDO%uh@eZSLrbx? zOZbUux4s88k5GggK5h>B*a4BYag+<^?P!g!mA-nvUq7>mHY8mu>vp@X7U|9GwR}~Z zw#r;<(b2eky|qN=a>7%Bvc-(;N#_E$V`I9cDlBJJIh7tvB!l!5eS@eLVRQEcTW!wg zYH!Cm9ejtug_B6+5XnW9_r*ptRGjkdJ56kDNU)H7PRA?^L4gwaaI)o#%GoQ5v5DLj zWXQO-NZVra+qvkPC|H}@W)rM_KN{Kc^^$oF1$q6f&seM`cL0+pUMEDI)J!vRE0_c3 zKo1T8DDQo7XV;s&EfPZqwU{eZ_LmFo z{enQ&6%j07UmH)o)vt+3ltv1t6GUEn33FWBlXyOZhOUYFiKymHMYt4LSVc|r3yXlp z9JYuFK;(t*DH`@++nS)CCj^LYEACO3+KL)v?TBtE@IvHel{1!DUL`b=6&6`J6-`W5 zghG8o06AH$IOQlg2ej+$O!of-yW^Gd_AEQm1^X0ip~eKEH8OQR4fQa`w^$m@vm zx9W=L^HJd`LK;Xs9zE+gisRmvuRou`s$bVc1alyCx~&ND6czzWI&2tD3Fu_KLeJjc zHjbd5ryFnM7@1Oj#v3W-HDbAwnjQkk55aMKs4`xhYND{Jfg$L{#>osE1<+ zfKMp+!8WmSBNvYTG@F#Qq;zg7zqh{tZI_b z;5|OOoICJKAun>dt*8gimakWySe92srhMtC@s#l5TMS7vaVwYu=0Fb)81mvI?Ea9~ zAz&Z*!jK^^&W^f(W0-F7c0+{M@)9$DANEJLP(N=>w;*tf@G3ud09k#t;RDyg1UiN92`euUWyCuNT~F&^tC39OHz@MBz#ULlE-H^MXR!$yHE- zz8GbHeXi( z6|cR+7;j#ksV1jYA-Y4cZI)M>RAvQ3UXO&lZoI#tKubOxc9#(H8fULpCFC_i-~@}= zU!N;_Vly@|IndM)guKw&=F@TgF;X>&UBKk48?&H0#8X{#NueZ10HBCKfv)+OBuW|5 z)noRByndk3yhrTw%>6{j>j%UsCUOVv5svjS$+tGl0dpWaV93jmSENyO=@#T*hP(vI zIc+v+QkfMDc?q8eo#i!bzI2Dg%#c_5Z$(oQl`ag0?k+dv#VOMRQaA;39AkhYf)Bc8 zS1d?%=`kBJF@_iZO4Z!&WXzxu$zXCqGQKtQI51qxX_$Fc>>Q@*WDYf@@ zJRc&b%FlSi$Gk=?-(yXb`i1%l>tXQ}pEsUeHMK_oX_o`KL46TzH+k9#xZ}b-%p9xV zP}`cIOQ*v?M6`|Lv9`G19Q3gRB5mU+7tY(!(qAim^?v{Q%p%&5bfK)%f%9uEoN*_Iv2Pd8v)r{&K_SR@}(d18$`7To4Y62 zYI8nUdppkQ;5!U1oJ1mrNG_thFE*N?;*@XSX<}5QmR zejSgGH1R(Ol_Y@8Lux(Ay&l93Xw-xuOOKn9BvUWtb6k{RsvdS4t zEUy40SuafF4zx4`kdxJlQ;w2@a2|41bjT}YJ*7{dq{N7sV>a$EF`4v*Aqr$U>+tgR z8e|d5%3f2|vv#I1TYo4yRa_mw-%d*R%&NLuS=d zG~^`-*Ny3x?2ZR+hP*hrx-qljoe5EcF2#wsoy`GrpbrQ7LSAQtysn4$+#hQyvDKOpjI^=2Y`U9rAsFE&b= zbl5PQ641$d1%~+C-!_i2lP!Bc?q5bbGPb?Fe3|#041fDuowX7k8TMse0XEJ z1zb9H!mJ_?%g+rOLX-L>8^$td0CVhe?f|yVkXHgI~O zj`c9dwB!?DFWa$E(5<$HAmlZUi6-ex&;+iV zjm5-vGUPQjF*)E7Rs%y2@k@b17eZb?I0XvPuvyTPJW-m7TfrPK2YPV8ke4AZ zLtez|VD46ZQPLfX4S7jZ40(MsAQHFpVO!{Q+j|=F`fC!EE)0dHAgli6hP*h6>W@V6 zc0<%j>zu<-z5 z13ucrmnFgFwArQTjSt(3l0vz7e^YUXT1PuJ!c8QPs3sKmn}a@fK%{LP<-&P8S`%!g zuio!ppIJm3k}i~WyWLib{w3{&d{vva%3RNsxL&Vj`HE~Mkz26c6-!XIn6W+STtGWE zqSq>yGeGtRE(&JlLbV6wcZhODeSc4|)#iMz_I8}p!FL#3IEh3Jb13hNjb^AQHYhZ) zfgxk$=Qvo%KBq`5P$C~rwtP`Jdqpuek-LHnA+*RVxd@81EvBw{P2^O~Z#w{1zaNck z`FaVn`U6?AWf$Yt5%T)UDUfA(4TgiInYb0q0dt@S2f~(z{PX<5V;^1FT@i4fGoo8f z#DBA+yg%M@5j6hZNlnD9&RvLr@LT}-AWVU3_9ucqb%SX7Y6xa|)n0GyjKIrbinTsS9zB{A?>~khLSar5Fp5msQSKVtEB1$$DWTcc7&qfSjyW6E;;Z z1I|OPiVk@>CX*dLNr@3N`qISAAy>R^hyq!zD&DbCgDhI$E4`jNndn(NQ`oqd_u}Jj zqCj3>88SkC!na3jE?-Y5MI{iC0P?}8CeB}yG{(hNQ@Enn@*vCyuIqE#5MuQe1p28v@Ganr`45tKivR)zU`rF14^z(G%Z5$(0%FhjU zSIjSq<+hjm`M&(%Y$rr`+d-?z9cbVZda-db14jY085H62^)gk-54MRF_xi#R`G?_R zjJz_s6^lhp&z|kMw5+mWm8`CZR%MMXnJQXoler|hDG^t;L!CW_hId(aB z09$9sD^Vx^BfLinhyFSmATO54bu}>LB`6k!Z9$g4hv`Szer6-I!VN&V;BzmwK4vTNCDhIglJM#b za)eU`O}Aj@aY7e*_6;91AKCKt(v-(rz8V_oUvE}QPBYnPrX<1$s#B!wv1X=t6 zLxdmGkQb*+4fqnjurZ$M;_@^Tw}Lrf4)oxFAumH-5q~vE5<}PrQlmkOhP(u{!purk z@?nFTd3shX&toA!_b0N&oMRw3jt_^uJ>nfc5kg+$m}rvDgi=Nbr(m(-IKJeG&Dg}` zKvP2y@U@)EA}2eL%NKE$mX^6G&^@g_oK$SYw3E0_aA zb>NeT&xboQ^7AJTGF=z{)zbSr6#t-_P<%tJzoE<9itM4w2Ht|GUq$?G<$`V);S)cD znrFCt{gGc6qQQs=I-$;=kErIUwg@i|djyDk>_zNDEF!|Q6I%|ra1SvWzGwZ`>(@m6 z+}~8(q1MrkjbLj?GE~=eqO+PEhr@nz(8mslv}2=OIB!QlY^AT>Z(naH?ojJ}ipr(k zZo8->`j@m9@>OlxDsw$k;(EQBDe>W$<1k<$bZy3>C!&g(fyIWQ_bA2MgKf6o~~& zTnN z*U#P%jWxs_Xv@Kd3J$E_=D;u=K*;NapwkIKr%psq-~@&1q@gdKh*PE$4@1<2hzQR` zAsWXBAoA)@tj}25)r84cy@_9}wi4mhR&?t$% zXe(-JwA*f5iU7O-khR-wR*JC@d0FL*C6-qJlB^ddatB%(0?5f~HDOcrGT=Pqs_2kc ziF)G6Cn-r5WMZ_ZJYP3Nfh<=QFJF(clJb?=^sJpJY~0Ix@o_g%Ag`|s86iLMlKANr z;;I0auP2nE5{O6u`CwGjvb+R&YG4R(88WMuq9HE{v-$&BvgPX-&5+mkLMMK=5VZwa z_3t@MY+$`M2ZrhZLSB1>y!HqXbs{3<)k#BNJWj+f z^q+E8P(oLO#kg8tjym5PiR!H(h^Md!P|{(;a7sWY>lL!Dzik{rKTmiYM})kVU5Jq> z<>$6rF6Q@&t(8vA8ZpV?)8Ns z@(;ts7J)8j7Ta!H^5W*WwG4$ax|%5qS0&sbL?{N0JhGMSE5e-M|h7E4*hjBKwd17>uO-g zOQ0;8EU8^U8!WqOH!)_X$N#BR0j-saqv~LwVYhFMqb$f>@YX=N@8S;wwt3l=%;x@gJsNNbfvK<@6&v_`w>j$SmAsTl2(~~^WtpR|wZw@?{ z1BSc|c^UGemroJIGvsB+tJ=y;a2%ssT^M4>>#s>vx-b-)f~@+N8}i~Psy`CN+YM1C ztt&)h$V*w$yXvoWuS3*rj%#sxyBE;vy4feF=z^xrf$SbX&MGJY+|Nh}5 zh_p{vRU98-)vtg4`SeHlwiJ2Q`r;c}N$ic^Eksy^mp2ggTZj#m3#tpnGpc!x^eI21 znrFCtJ>Kv!zgH~xR}-Nm)cU!B2=CcSJRQL0r~Mhc2Z**9I6-kIfDwiJ9mHr5xOF0e z2Rfk@+5QzFW+xpZz%3i>wSvKrQL43 zs3ZEfvlrhOo$H7AOIYnZD z68Uhl<%`PMD~j>jm+88A_JdPeEsV zXv|8ua*u}G8UR@P=D>3~fRNXb0FgpoM})jwO*%O8D#d`M;%8ei-X}jF^B|Uw2qg_h zj7TawpO2EpIBWrtSAU}Hl8g7P#3Q==h_Fuw;^~O6&k=za$XE^tyc{-Q`oDuaHufhX z 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 z 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�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

q8T4UvE%~%0Ds!D>h2TboDH+8fFDSR&$Zai^!{D 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≪gfr+i$?1 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 literal 1600008 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

_tO*R_=cr8JhjH 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 JsowBO{tpEYuPgun diff --git a/Analysis/config/xtalk.550.well.settings.json b/Analysis/config/xtalk.550.well.settings.json new file mode 100644 index 00000000..362454f4 --- /dev/null +++ b/Analysis/config/xtalk.550.well.settings.json @@ -0,0 +1 @@ +{"ChipType":"550","GridType":"HexGridCol","MapSpan":25,"NNX":2,"NNY":2,"XtalkCoef":[0.0021,0.0064,0.0059,0.0057,0.0007,0.002,0.0025,0.012,0.0039,0.0018,0.0089,0.0162,0.0001,0.0154,0.0075,0.0021,0.0167,0.0118,0.0154,0.0012,0.001,0.004,0.0059,0.0018,0.0018],"Xtype":"Simple"} diff --git a/Analysis/config/xtalk.p2.3.1.well.settings.json b/Analysis/config/xtalk.p2.3.1.well.settings.json new file mode 100644 index 00000000..cbdeac46 --- /dev/null +++ b/Analysis/config/xtalk.p2.3.1.well.settings.json @@ -0,0 +1 @@ +{"ChipType":"P2.3.1","GridType":"HexGridCol","MapSpan":25,"NNX":2,"NNY":2,"XtalkCoef":[0.0021,0.0064,0.0059,0.0057,0.0007,0.002,0.0025,0.012,0.0039,0.0018,0.0089,0.0162,0.0001,0.0154,0.0075,0.0021,0.0167,0.0118,0.0154,0.0012,0.001,0.004,0.0059,0.0018,0.0018],"Xtype":"Simple"} diff --git a/Analysis/crop/ChkDat.cpp b/Analysis/crop/ChkDat.cpp index 4d813e6a..d1b5a9e5 100755 --- a/Analysis/crop/ChkDat.cpp +++ b/Analysis/crop/ChkDat.cpp @@ -41,7 +41,7 @@ uint32_t numAcq = 0; int dont_wait = 0; char *DstDir = (char *)"."; char *oneFile = NULL; -uint32_t verbose=1; +uint32_t verbose=0; uint32_t Error = 0; char DirList[200][512]; diff --git a/Analysis/crop/Crop.cpp b/Analysis/crop/Crop.cpp index 7d7d2eae..4ac40fcc 100644 --- a/Analysis/crop/Crop.cpp +++ b/Analysis/crop/Crop.cpp @@ -374,6 +374,9 @@ int main ( int argc, char *argv[] ) else options.applyXtalkCorrection2 = 0.2; break; + case 'N': + options.doGainCorrection = 1; + break; case 'O': options.doGainCorrection_bf = 1; break; diff --git a/Analysis/polyclonal_filter.h b/Analysis/polyclonal_filter.h deleted file mode 100644 index 53f0b694..00000000 --- a/Analysis/polyclonal_filter.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Copyright (C) 2014 Ion Torrent Systems, Inc. All Rights Reserved */ - -#ifndef POLYCLONAL_FILTER_H -#define POLYCLONAL_FILTER_H - -class PolyclonalFilterOpts { - public: - PolyclonalFilterOpts(); - - bool enable; - int mixed_first_flow; - int mixed_last_flow; - int max_iterations; - int mixed_model_option; - double mixed_stringency; - bool verbose; - bool use_last_iter_params; - bool filter_extreme_ppf_only; -}; - -#endif // POLYCLONAL_FILTER_H diff --git a/Analysis/realignment/Realigner.cpp b/Analysis/realignment/Realigner.cpp index b0eb15fd..23d125fd 100644 --- a/Analysis/realignment/Realigner.cpp +++ b/Analysis/realignment/Realigner.cpp @@ -220,11 +220,18 @@ void Realigner::SetSequences(const string& q_seq, const string& t_seq, const str if (debug_ and verbose_) cout << "Hello from SetSequences." << endl; + // If we have an empty aln_path, and alignment_bandwidth_>0 we align along the diagonal + // Empty aln_path and alignment_bandwidth_==0 evaluates the whole DP matrix + if (aln_path.length()==0 and alignment_bandwidth_>0){ + create_dummy_pretty(t_seq.length(), q_seq.length()); + } + else + pretty_aln_ = aln_path; + // We align all sequences in forward direction - pretty_aln_ = aln_path; + // but have to worry about clipping the correct end of the alignment q_seq_ = q_seq; t_seq_ = t_seq; - // but have to worry about clipping the correct end of the alignment if (isForwardStrandRead_ != isForward) { ReverseClipping(); isForwardStrandRead_ = isForward; @@ -246,6 +253,33 @@ void Realigner::SetSequences(const string& q_seq, const string& t_seq, const str cout << "Successfully set sequences." << endl; } +// ------------------------------------------------------------------- +// For tubed alignment to work without a prior alignment input +// Create a pseudo diagonal pretty alignment string for initialization + +void Realigner::create_dummy_pretty(unsigned int target_length, unsigned int query_length) +{ + pretty_aln_.assign(max(target_length, query_length), '|'); + unsigned int plen = pretty_aln_.length(); + char indel = '-'; + int n_indels = target_length - query_length; + + if (n_indels==0) + return; + else if (n_indels<0) { + indel = '+'; + n_indels = -n_indels; + } + int interval = plen / n_indels; + unsigned int pos = (plen - ((n_indels-1)*interval))/2; + int count = 0; + + while (count& CigarData, vector& MD_data, unsigned int& start_pos_update) { - string dummy_string; - // Compute boundaries for tubed alignment around previously found alignment if (!ComputeTubedAlignmentBoundaries()) return false; diff --git a/Analysis/realignment/Realigner.h b/Analysis/realignment/Realigner.h index 49526f25..ced16a23 100644 --- a/Analysis/realignment/Realigner.h +++ b/Analysis/realignment/Realigner.h @@ -221,6 +221,9 @@ class Realigner { //! @ brief Spells out the constants representing alignment types string PrintAlignType(int align_type); + //! @ brief - Create a dummy pretty alignment string if none is available + void create_dummy_pretty(unsigned int target_length, unsigned int query_length); + bool start_anywhere_in_ref_; //!< Allow aligned read to start at any point in the reference bool stop_anywhere_in_ref_; //!< Allow aligned read to end at any point in the reference diff --git a/Analysis/version b/Analysis/version index fa476b39..5b986bf6 100644 --- a/Analysis/version +++ b/Analysis/version @@ -1,4 +1,4 @@ # Version for target of current development cycle MAJOR=5 -MINOR=2 -RELEASE=26 +MINOR=4 +RELEASE=11 diff --git a/Analysis/xtalk_sim/DiffEqModel.h b/Analysis/xtalk_sim/DiffEqModel.h index 9da2315a..1a307a3e 100755 --- a/Analysis/xtalk_sim/DiffEqModel.h +++ b/Analysis/xtalk_sim/DiffEqModel.h @@ -18,7 +18,7 @@ typedef enum { #include #include "xtalk_sim.h" -#include "WorkerInfoQueue.h" +#include "../Util/WorkerInfoQueue.h" #include "SignalAverager.h" #include "WellVoxelTracker.h" #include "DelsqCUDA.h" diff --git a/Analysis/xtalk_sim/WorkerInfoQueue.cpp b/Analysis/xtalk_sim/WorkerInfoQueue.cpp deleted file mode 100644 index b1af6d25..00000000 --- a/Analysis/xtalk_sim/WorkerInfoQueue.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ - -#include "WorkerInfoQueue.h" - -// create a queue w/ that can hold the specified number of items -WorkerInfoQueue::WorkerInfoQueue(int _depth) -{ - depth = _depth; - rdndx = 0; - wrndx = 0; - num = 0; - not_done_cnt = 0; - qlist = new WorkerInfoQueueItem[depth]; - - pthread_mutex_init(&lock, NULL); - pthread_cond_init(&rdcond,NULL); - pthread_cond_init(&wrcond,NULL); - pthread_cond_init(&donecond,NULL); -} - -// put a new item on the queue. this will block if the queue is full -void WorkerInfoQueue::PutItem(WorkerInfoQueueItem &new_item) -{ - // obtain the lock - pthread_mutex_lock(&lock); - - // wait for someone to signal new free space - // fprintf(stdout, "WorkerInfoQueue::PutItem: %d\n", num); - while (num >= depth) - pthread_cond_wait(&wrcond,&lock); - - qlist[wrndx] = new_item; - if (++wrndx >= depth) wrndx = 0; - num++; - not_done_cnt++; - - // signal readers to check for the new item - pthread_cond_signal(&rdcond); - - // give up the lock - pthread_mutex_unlock(&lock); -} - -// remove an item from the queue. this will block if the queue is empty -WorkerInfoQueueItem WorkerInfoQueue::GetItem(void) -{ - WorkerInfoQueueItem item; - - // obtain the lock - pthread_mutex_lock(&lock); - - // wait for someone to signal a new item - // fprintf(stdout, "WorkerInfoQueue::GetItem: %d\n", num); - while (num == 0) - pthread_cond_wait(&rdcond,&lock); - - item = qlist[rdndx]; - if (++rdndx >= depth) rdndx = 0; - num--; - - // signal writers that more free space is available - pthread_cond_signal(&wrcond); - - // give up the lock - pthread_mutex_unlock(&lock); - - return(item); -} - -// try to remove an item from the queue. this will return item with empty data if the queue is empty -WorkerInfoQueueItem WorkerInfoQueue::TryGetItem(void) -{ - WorkerInfoQueueItem item; - - // obtain the lock - pthread_mutex_lock(&lock); - - // wait for someone to signal a new item - if (num == 0) { - pthread_mutex_unlock(&lock); - item.private_data = NULL; - return item; - } - - item = qlist[rdndx]; - if (++rdndx >= depth) rdndx = 0; - num--; - - // signal writers that more free space is available - pthread_cond_signal(&wrcond); - - // give up the lock - pthread_mutex_unlock(&lock); - - return(item); -} - - -// NOTE: just because the q is empty...doesn't mean the workers are done with the -// last item they pulled off. Worker's decrement the 'not done' count whenever they -// finish a work item. This waits till all the work items have been completed -void WorkerInfoQueue::WaitTillDone(void) -{ - // obtain the lock - pthread_mutex_lock(&lock); - - // wait for someone to signal new free space - while (not_done_cnt > 0) - pthread_cond_wait(&donecond,&lock); - - // give up the lock - pthread_mutex_unlock(&lock); -} - -// Allows workers to indicate that they have completed a task -void WorkerInfoQueue::DecrementDone(void) -{ - // obtain the lock - pthread_mutex_lock(&lock); - - // decrement done count - not_done_cnt--; - - // if everything is done, signal the condition change - if (not_done_cnt == 0) - pthread_cond_signal(&donecond); - - // give up the lock - pthread_mutex_unlock(&lock); -} - -WorkerInfoQueue::~WorkerInfoQueue() -{ - pthread_cond_destroy(&donecond); - pthread_cond_destroy(&wrcond); - pthread_cond_destroy(&rdcond); - pthread_mutex_destroy(&lock); - delete [] qlist; -} - - -// create a queue w/ that can hold the specified number of items -DynamicWorkQueueGpuCpu::DynamicWorkQueueGpuCpu(int _depth) -{ - start = false; - depth = _depth; - gpuRdIdx = 0; - cpuRdIdx = depth - 1; - wrIdx = 0; - not_done_cnt = 0; - qlist = new WorkerInfoQueueItem[depth]; - - pthread_mutex_init(&lock, NULL); - pthread_cond_init(&startcond,NULL); - pthread_cond_init(&donecond,NULL); -} - - -WorkerInfoQueueItem DynamicWorkQueueGpuCpu::GetGpuItem() { - - WorkerInfoQueueItem item; - - pthread_mutex_lock(&lock); - //printf("Acquiring lock gpu\n"); - - - while (!start) - pthread_cond_wait(&startcond, &lock); - - if (gpuRdIdx == cpuRdIdx) - start = false; - - //printf("Getting Gpu Item, GpuIdx: %d CpuIdx: %d, start: %d\n", gpuRdIdx, cpuRdIdx, start); - item = qlist[gpuRdIdx++]; - - //printf("Releasing lock gpu\n"); - pthread_mutex_unlock(&lock); - - return item; -} - -WorkerInfoQueueItem DynamicWorkQueueGpuCpu::GetCpuItem() { - - WorkerInfoQueueItem item; - - pthread_mutex_lock(&lock); - - //printf("Acquiring lock cpu\n"); - - - while (!start) - pthread_cond_wait(&startcond, &lock); - - if (cpuRdIdx == gpuRdIdx) - start = false; - - //printf("Getting Cpu Item, GpuIdx: %d CpuIdx: %d, start: %d\n", gpuRdIdx, cpuRdIdx, start); - item = qlist[cpuRdIdx--]; - - //printf("Releasing lock cpu\n"); - pthread_mutex_unlock(&lock); - - return item; -} - -void DynamicWorkQueueGpuCpu::PutItem(WorkerInfoQueueItem &new_item) { - - - qlist[wrIdx++] = new_item; - not_done_cnt++; - - //printf("Putting Item\n"); - if (wrIdx == depth) { - pthread_mutex_lock(&lock); - //printf("Signalling to start\n"); - start = true; - pthread_cond_broadcast(&startcond); - pthread_mutex_unlock(&lock); - } - -} - - -// NOTE: just because the q is empty...doesn't mean the workers are done with the -// last item they pulled off. Worker's decrement the 'not done' count whenever they -// finish a work item. This waits till all the work items have been completed -void DynamicWorkQueueGpuCpu::WaitTillDone(void) -{ - // obtain the lock - pthread_mutex_lock(&lock); - - // wait for someone to signal new free space - while (not_done_cnt > 0) - pthread_cond_wait(&donecond,&lock); - - // give up the lock - pthread_mutex_unlock(&lock); -} - -// Allows workers to indicate that they have completed a task -void DynamicWorkQueueGpuCpu::DecrementDone(void) -{ - // obtain the lock - pthread_mutex_lock(&lock); - - // decrement done count - not_done_cnt--; - - // if everything is done, signal the condition change - if (not_done_cnt == 0) { - start = false; - pthread_cond_signal(&donecond); - } - - // give up the lock - pthread_mutex_unlock(&lock); -} - -void DynamicWorkQueueGpuCpu::ResetIndices() { - pthread_mutex_lock(&lock); - wrIdx = 0; - gpuRdIdx = 0; - cpuRdIdx = depth - 1; - pthread_mutex_unlock(&lock); -} - -int DynamicWorkQueueGpuCpu::getGpuReadIndex() { - return gpuRdIdx; -} - -DynamicWorkQueueGpuCpu::~DynamicWorkQueueGpuCpu() -{ - pthread_cond_destroy(&donecond); - pthread_cond_destroy(&startcond); - pthread_mutex_destroy(&lock); - delete [] qlist; -} - - diff --git a/Analysis/xtalk_sim/WorkerInfoQueue.h b/Analysis/xtalk_sim/WorkerInfoQueue.h deleted file mode 100644 index 10b26ba6..00000000 --- a/Analysis/xtalk_sim/WorkerInfoQueue.h +++ /dev/null @@ -1,339 +0,0 @@ -/* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */ -#ifndef WORKERINFOQUEUE_H -#define WORKERINFOQUEUE_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct WorkerInfoQueueItem -{ - bool finished; - void *private_data; - WorkerInfoQueueItem() - { - finished = false; - private_data = NULL; - } -}; - -/// helper class to distribute work items to a set of threads for processing -/// add class access is thread safe -class WorkerInfoQueue -{ - public: - /** Create a queue w/ that can hold the specified number of items */ - WorkerInfoQueue(int _depth); - - /** Put a new item on the queue. Blocks if the queue is full */ - void PutItem(WorkerInfoQueueItem &new_item); - - /** remove an item from the queue. this will block if the queue is empty */ - WorkerInfoQueueItem GetItem(void); - - /** try to remove an item from the queue. this will return item with empty data if the queue is empty */ - WorkerInfoQueueItem TryGetItem(void); - - // NOTE: just because the q is empty...doesn't mean the workers are done with the - // last item they pulled off. Worker's decrement the 'not done' count whenever they - // finish a work item. - /** Wait till all the work items have been completed */ - void WaitTillDone(void); - - /* Call when a worker has completed a task */ - void DecrementDone(void); - - int GetNumNotDone(void) { return not_done_cnt; } - - ~WorkerInfoQueue(); - -private: - int rdndx; - int wrndx; - int num; - int not_done_cnt; - int depth; - WorkerInfoQueueItem *qlist; - - // synchronization objects - pthread_mutex_t lock; - pthread_cond_t wrcond; - pthread_cond_t rdcond; - pthread_cond_t donecond; -}; - -class DynamicWorkQueueGpuCpu { - -public: - - // create a queue w/ that can hold the specified number of items - DynamicWorkQueueGpuCpu(int _depth); - - WorkerInfoQueueItem GetGpuItem(); - WorkerInfoQueueItem GetCpuItem(); - - void PutItem(WorkerInfoQueueItem &new_item); - - // NOTE: just because the q is empty...doesn't mean the workers are done with the - // last item they pulled off. Worker's decrement the 'not done' count whenever they - // finish a work item. This waits till all the work items have been completed - void WaitTillDone(void); - - // Allows workers to indicate that they have completed a task - void DecrementDone(void); - - void ResetIndices(); - int getGpuReadIndex(); - - ~DynamicWorkQueueGpuCpu(); - -private: - int gpuRdIdx; - int cpuRdIdx; - int wrIdx; - int depth; - int not_done_cnt; - bool start; - WorkerInfoQueueItem *qlist; - - // synchronization objects - pthread_mutex_t lock; - pthread_cond_t donecond; - pthread_cond_t startcond; -}; - - -/* -#ifndef __FRAME_OUTPUT_QUEUE_H -#define __FRAME_OUTPUT_QUEUE_H - -#include - -struct frame_info -{ - int frameNumber; - UINT8 *ptr; - struct timeval timestamp; - UINT32 frame_duration; -}; - -struct FrameOutputQueue -{ - struct frame_info *frames; - int rd_ndx; - int wr_ndx; - int num; - int nFrames; - int acqFinished; - - pthread_mutex_t lock; - pthread_cond_t wrcond; - pthread_cond_t rdcond; -}; - -// Creates a new output queue large enough to hold numFrames -struct FrameOutputQueue *foq_Create(int numFrames); - -// Destroys a FrameOutputQueue and free's the resources used by the control structures -void foq_Destroy(struct FrameOutputQueue *q); - -// Puts a new frame into the queue. Blocks if there isn't space for the new frame -void foq_PutFrame(struct FrameOutputQueue *q,int frameNum,struct timeval timestamp,UINT32 frame_duration,UINT8 *ptr); - -// Gets the next frame from the queue. Blocks if there aren't any new frames available -int foq_GetFrame(struct FrameOutputQueue *q,struct frame_info *pframe); - -// indicate to readers that the acquisition is complete and no more frames should be -// expected -void foq_FinishAcquisition(struct FrameOutputQueue *q); - -// Empty's the queue -void foq_Reset(struct FrameOutputQueue *q); - -// Queries number of frames in the queue -int foq_GetNum(struct FrameOutputQueue *q); - -#endif - - - #include "datacollect_global.h" -#include "FrameOutputQueue.h" - -#include "dbgmem.h" - -// Creates a new output queue large enough to hold numFrames -struct FrameOutputQueue *foq_Create(int numFrames) -{ - struct frame_info *frames; - struct FrameOutputQueue *q; - - q = (struct FrameOutputQueue *)malloc(sizeof(struct FrameOutputQueue)); - - if (q == NULL) - goto err0; - - frames = (struct frame_info *)malloc(sizeof(struct frame_info)*numFrames); - - if (frames == NULL) - goto err1; - - q->frames = frames; - - if (pthread_mutex_init(&q->lock, NULL) != 0) - goto err2; - - if (pthread_cond_init(&q->rdcond,NULL) != 0) - goto err3; - - if (pthread_cond_init(&q->wrcond,NULL) != 0) - goto err4; - - q->nFrames = numFrames; - foq_Reset(q); - - return(q); - -err4: - pthread_cond_destroy(&q->rdcond); -err3: - pthread_mutex_destroy(&q->lock); -err2: - free(frames); -err1: - free(q); -err0: - return NULL; -} - -// Destroys a FrameOutputQueue and free's the resources used by the control structures -void foq_Destroy(struct FrameOutputQueue *q) -{ - // technically...I should check here to see if anyone is blocked on these - // things before destroying them...will fix this at some point - pthread_cond_destroy(&q->wrcond); - pthread_cond_destroy(&q->rdcond); - pthread_mutex_destroy(&q->lock); - free(q->frames); - free(q); -} - -// Puts a new frame into the queue. Blocks if there isn't space for the new frame -void foq_PutFrame(struct FrameOutputQueue *q,int frameNum,struct timeval timestamp,UINT32 frame_duration,UINT8 *ptr) -{ - pthread_mutex_lock(&q->lock); - - while (q->num >= q->nFrames) - { - // wait for someone to signal new free space - pthread_cond_wait(&q->wrcond,&q->lock); - } - - q->frames[q->wr_ndx].frameNumber = frameNum; - q->frames[q->wr_ndx].timestamp = timestamp; - q->frames[q->wr_ndx].frame_duration = frame_duration; - q->frames[q->wr_ndx].ptr = ptr; - - if (++(q->wr_ndx) >= q->nFrames) q->wr_ndx = 0; - q->num++; - - // signal readers to check for the new frame - pthread_cond_signal(&q->rdcond); - - pthread_mutex_unlock(&q->lock); -} - -// Gets the next frame from the queue. Blocks if there aren't any new frames available -int foq_GetFrame(struct FrameOutputQueue *q,struct frame_info *pframe) -{ - int nrcv = 0; - - pthread_mutex_lock(&q->lock); - - while ((q->num <= 0) && (q->acqFinished == 0)) - { - // wait for someone to signal new frame - pthread_cond_wait(&q->rdcond,&q->lock); - } - - // if there was a new frame available, get the info - if (q->num > 0) - { - pframe->frameNumber = q->frames[q->rd_ndx].frameNumber; - pframe->timestamp = q->frames[q->rd_ndx].timestamp; - pframe->ptr = q->frames[q->rd_ndx].ptr; - pframe->frame_duration = q->frames[q->rd_ndx].frame_duration; - - if (++(q->rd_ndx) >= q->nFrames) q->rd_ndx = 0; - q->num--; - - // signal new free space to writers - pthread_cond_signal(&q->wrcond); - - nrcv = 1; - } - - pthread_mutex_unlock(&q->lock); - - // if we return 0, it means there aren't any more frames available - // and the acqusition has finished...so don't expect any more - return nrcv; -} - -// indicate to readers that the acquisition is complete and no more frames should be -// expected -void foq_FinishAcquisition(struct FrameOutputQueue *q) -{ - pthread_mutex_lock(&q->lock); - - q->acqFinished = 1; - pthread_cond_signal(&q->rdcond); - - pthread_mutex_unlock(&q->lock); -} - -// Empty's the queue -void foq_Reset(struct FrameOutputQueue *q) -{ - pthread_mutex_lock(&q->lock); - - q->acqFinished = q->rd_ndx = q->wr_ndx = q->num = 0; - - pthread_mutex_unlock(&q->lock); -} - -// Queries number of frames in the queue -int foq_GetNum(struct FrameOutputQueue *q) -{ - int ret; - - pthread_mutex_lock(&q->lock); - - ret = q->num; - - pthread_mutex_unlock(&q->lock); - - return(ret); -} - - -*/ - -#endif // WORKERINFOQUEUE_H diff --git a/RSM/version b/RSM/version index 183b81a6..a37e111d 100644 --- a/RSM/version +++ b/RSM/version @@ -1,4 +1,4 @@ # Version for target of current development cycle MAJOR=5 -MINOR=2 -RELEASE=5 +MINOR=4 +RELEASE=0 diff --git a/TSVersion b/TSVersion index 7820c525..45b60990 100644 --- a/TSVersion +++ b/TSVersion @@ -1,4 +1,4 @@ # Version for target of current development cycle TS_MAJOR=5 -TS_MINOR=2 -TS_RELEASE=2 +TS_MINOR=4 +TS_RELEASE=0 diff --git a/buildTools/BUILD.txt b/buildTools/BUILD.txt index 4a1de59b..2054e556 100644 --- a/buildTools/BUILD.txt +++ b/buildTools/BUILD.txt @@ -51,7 +51,6 @@ QUICK START on Ubuntu 11.10 (=Oneiric) (EXPERIMENTAL) libarmadillo-dev \ libboost-serialization-dev \ libncurses-dev \ - libbz2-dev libbz2-dev \ libcurl4-openssl-dev \ libncurses-dev \ diff --git a/buildTools/LICENSE.txt.in b/buildTools/LICENSE.txt.in index 2f53b08f..34102a71 100644 --- a/buildTools/LICENSE.txt.in +++ b/buildTools/LICENSE.txt.in @@ -1,489 +1,649 @@ @CMAKE_PROJECT_NAME@ -- @PROJECT_DESCRIPTION@ +Torrent Suite Software Version 5.4 ===================================================================== -1. Torrent Suite Software License -===================================================================== - -Torrent Suite Software: This software provides for the analysis and storage of data received from the Ion Personal Genome Machine(R) PGM(tm) Sequencer or the Ion Proton(R) Sequencer. ---------------------------------------------------------------------- - -Copyright (C) 2011 Life Technologies Corporation +Torrent Suite(tm) Software v5.4: This software provides for the analysis and storage of data received from the Ion Personal Genome Machine(tm) Sequencer, the Ion Proton(tm) Sequencer, the Ion S5(tm) Sequencer or the Ion S5(tm) XL Sequencer. +April 19, 2017 +Copyright (C) 2017 Life Technologies Corporation All Rights Reserved. This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License, or (at your -option) any later version. +under the terms of the GNU General Public License version 2 as published by the +Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. +General Public License version 2 for more details. -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. You can also find the -GPL on the GNU web site: . +You should have received a copy of the GNU General Public License version 2 along +with this program (see Section 2 below); if not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. You can also find the +GPL version 2 on the GNU web site: https://www.gnu.org/licenses/gpl-2.0.html + +1. Third Party Software Notices and Licenses ===================================================================== -2. Third Party Software Notices and Licenses. -===================================================================== + +This software uses third party software components from several sources. Portions of these software components are copyrighted and licensed by their respective owners as indicated below. Various licenses require distribution of source code or if a link is used to point the end-user to a source-code repository, and the source code is not available at such site, the distributor must, for a time determined by license, offer to provide source code. In such cases, please contact your Life Technologies representative. As well, various licenses require that the end-user receive a copy of the license or certain notices. Such licenses and notices may be found below. In order to use this software, the end-user must abide by the terms and conditions of these third party licenses. + +Armadillo +------------------------------------------------------------- + +Mozilla Public License Version 2.0 +------------------------------------------------------------- + +1. Definitions +------------------------------------------------------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or -The following third party software components are included in the Torrent Suite -Software. These software components are copyrighted and licensed by their respective -owners. Various components require distribution of source code or if a URL is used to -point the end-user to a source-code repository, and the source code is not available at -such site, the distributor must, for a time determined by the license, offer to provide the -source code. In such cases, please contact your Ion Torrent representative. As well, -various licenses require that the end-user receive a copy of the license and such licenses -may be found below. In order to use this Torrent Suite Software, the end-user must -abide by the terms and conditions of these third-party licenses. + (b) any new file in Source Code Form that contains any Covered + Software. -lapack -URL: http://www.netlib.org/lapack/ -License: BSD +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. -k-means -URL: http://www.cs.umd.edu/~mount/Projects/KMeans/ -License: GPL +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. -boost -URL: http://www.boost.org/ -License: BSD +1.13. "Source Code Form" + means the form of the work preferred for making modifications. -atlas -URL: http://math-atlas.sourceforge.net -License: BSD +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. -libgfortran -License: GPL +2. License Grants and Conditions +------------------------------------------------------------- -LAPACK ---------------------------------------------------------------------- -Copyright (c) 1992-2008 The University of Tennessee. All rights -reserved. +2.1. Grants -$COPYRIGHT$ +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: -Additional copyrights may follow +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and -$HEADER$ +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------------------------------------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +------------------------------------------------------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + + + +6. Disclaimer of Warranty +------------------------------------------------------------- + +Covered Software is provided under this License on an "as is" basis, without +warranty of any kind, either expressed, implied, or statutory, including, +without limitation, warranties that the Covered Software is free of defects, +merchantable, fit for a particular purpose or non-infringing. The entire risk +as to the quality and performance of the Covered Software is with You. Should +any Covered Software prove defective in any respect, You (not any Contributor) +assume the cost of any necessary servicing, repair, or correction. This +disclaimer of warranty constitutes an essential part of this License. No use of +any Covered Software is authorized under this License except under this +disclaimer. + + +7. Limitation of Liability +------------------------------------------------------------- + +Under no circumstances and under no legal theory, whether tort (including +negligence), contract, or otherwise, shall any Contributor, or anyone who +distributes Covered Software as permitted above, be liable to You for any +direct, indirect, special, incidental, or consequential damages of any +character including, without limitation, damages for lost profits, loss of +goodwill, work stoppage, computer failure or malfunction, or any and all other +commercial damages or losses, even if such party shall have been informed of +the possibility of such damages. This limitation of liability shall not apply +to liability for death or personal injury resulting from such party's +negligence to the extent applicable law prohibits such limitation. Some +jurisdictions do not allow the exclusion or limitation of incidental or +consequential damages, so this exclusion and limitation may not apply to You. + + +8. Litigation +------------------------------------------------------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +------------------------------------------------------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +------------------------------------------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------------------------- + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +------------------------------------------------------------- + +This Source Code Form is "Incompatible With Secondary Licenses", as +defined by the Mozilla Public License, v. 2.0. + + +HTS Lib and Samtools +------------------------------------------------------------- + +The MIT/Expat License +Copyright (C) 2008-2014 Genome Research Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +VCF Lib +------------------------------------------------------------- + +The MIT License (see text above) +Copyright (c) 2012 Erik Garrison + +Blas and Lapack +------------------------------------------------------------- +Copyright (c) 1992-2013 The University of Tennessee and The University of Tennessee Research Foundation. All rights reserved. + +Copyright (c) 2000-2013 The University of California Berkeley. All rights reserved. + +Copyright (c) 2006-2013 The University of Colorado Denver. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -- Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer listed - in this license in the documentation and/or other materials - provided with the distribution. - -- Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +The copyright holders provide no reassurances that the source code +provided does not infringe any patent, copyright, or any other +intellectual property rights of third parties. The copyright holders +disclaim any liability to any recipient for claims brought against +recipient by any third party for infringement of that parties +intellectual property rights. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Django +------------------------------------------------------------- +Copyright (c) Django Software Foundation and individual contributors. All rights reserved. -K-MEANS ---------------------------------------------------------------------- -KMlocal: A testbed for k-means clustering algorithms based on local -search -Version: 1.7 -Date: 08/10/2005 +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Copyright (C) 2004-2005 David M. Mount and University of Maryland -All Rights Reserved. +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Cuda Toolkit +------------------------------------------------------------- +END USER LICENSE AGREEMENT (EULA) + +Important Notice READ CAREFULLY: This Software License Agreement ("Agreement") for NVIDIA CUDA Toolkit, including computer software and associated documentation ("Software"), is the Agreement which governs use of the SOFTWARE of NVIDIA Corporation and its subsidiaries ("NVIDIA") downloadable herefrom. By downloading, installing, copying, or otherwise using the SOFTWARE, You (as defined below) agree to be bound by the terms of this Agreement. If You do not agree to the terms of this Agreement, do not download the SOFTWARE. Recitals Use of NVIDIA's SOFTWARE requires three elements: the SOFTWARE, an NVIDIA GPU or application processor ("NVIDIA Hardware"), and a computer system. The SOFTWARE is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE is not sold, and instead is only licensed for Your use, strictly in accordance with this Agreement. The NVIDIA Hardware is protected by various patents, and is sold, but this Agreement does not cover the sale or use of such hardware, since it may not necessarily be sold as a package with the SOFTWARE. This Agreement sets forth the terms and conditions of the SOFTWARE only. + +1.1. Definitions + + 1.1.1. Licensee "You", or "Your" shall mean the entity or individual that downloads and uses the SOFTWARE. + + 1.1.2. Redistributable Software "Redistributable Software" shall mean the redistributable libraries referenced in Attachment A of this Agreement. + + 1.1.3. Software "SOFTWARE" shall mean the deliverables provided pursuant to this Agreement. SOFTWARE may be provided in either source or binary form, at NVIDIA's discretion. + +1.2. Grant of License + + 1.2.1. Rights and Limitations of Grant Provided that Licensee complies with the terms of this Agreement, NVIDIA hereby grants Licensee the following limited, non-exclusive, non-transferable, non-sublicensable (except as expressly permitted otherwise for Redistributable Software in Section 1.2.1.1 and Section 1.2.1.3 of this Agreement) right to use the SOFTWARE -- and, if the SOFTWARE is provided in source form, to compile the SOFTWARE -- with the following limitations: + + 1.2.1.1. Redistribution Rights Licensee may transfer, redistribute, and sublicense certain files of the Redistributable SOFTWARE, as defined in Attachment A of this Agreement, provided, however, that (a) the Redistributable SOFTWARE shall be distributed solely in binary form to Licensee's licensees ("Customers") only as a component of Licensee's own software products (each, a "Licensee Application"); (b) Licensee shall design the Licensee Application such that the Redistributable SOFTWARE files are installed only in a private (non-shared) directory location that is used only by the Licensee Application; (c) Licensee shall obtain each Customer's written or clickwrap agreement to the license terms under a written, legally enforceable agreement that has the effect of protecting the SOFTWARE and the rights of NVIDIA under terms no less restrictive than this Agreement. + + 1.2.1.2. Usage Rights Licensee may install and use multiple copies of the SOFTWARE on a shared computer or concurrently on different computers, and make multiple back-up copies of the SOFTWARE, solely for Licensee's use within Licensee's Enterprise. "Enterprise" shall mean individual use by Licensee or any legal entity (such as a corporation or university) and the subsidiaries it owns by more than 50 percent. + + 1.2.1.3. Further Redistribution Rights Subject to the terms and conditions of the Agreement, Licensee may authorize Customers to further redistribute the Redistributable SOFTWARE that such Customers receive as part of the Licensee Application, solely in binary form, provided, however, that Licensee shall require in their standard software license agreements with Customers that all such redistributions must be made pursuant to a license agreement that has the effect of protecting the SOFTWARE and the rights of NVIDIA whose terms and conditions are at least as restrictive as those in the applicable Licensee software license agreement covering the Licensee Application. For avoidance of doubt, termination of this Agreement shall not affect rights previously granted by Licensee to its Customers under this Agreement to the extent validly granted to Customers under Section 1.2.1.1. + + 1.2.1.4. Linux/FreeBSD Exception Notwithstanding the foregoing terms of Section 1.2.1.2, Section 1.2.1.1 and Section 1.2.1.3, SOFTWARE designed exclusively for use on the Linux or FreeBSD operating systems, or other operating systems derived from the source code to these operating systems, may be copied and redistributed, provided that the binary files thereof are not modified in any way (except for unzipping of compressed files). + + 1.2.1.5. Additional Licensing Obligations Licensee acknowledges and agrees that its use of certain third party components included with the SOFTWARE may be subject to additional licensing terms and conditions as set forth or referenced in Attachment B of this Agreement. + + 1.2.1.6. Limitations No Reverse Engineering If the SOFTWARE is provided in binary form, Licensee may not reverse engineer, decompile, or disassemble the SOFTWARE, nor attempt in any other manner to obtain the source code. No Separation of Components The SOFTWARE is licensed as a single product. Except as authorized in this Agreement, Software component parts of the Software may not be separated for use on more than one computer, nor otherwise used separately from the other parts. No Rental Licensee may not rent or lease the SOFTWARE to someone else. +No Modifications +If the SOFTWARE is provided in source form, Licensee may not modify or create derivative works of the SOFTWARE. + +1.3. Term and Termination This Agreement will continue in effect for two (2) years ("Initial Term") after Your initial download and use of the SOFTWARE, subject to the exclusive right of NVIDIA to terminate as provided herein. The term of this Agreement will automatically renew for successive one (1) year renewal terms after the Initial Term, unless either party provides to the other party at least three (3) months prior written notice of termination before the end of the applicable renewal term. This Agreement will automatically terminate if Licensee fails to comply with any of the terms and conditions hereof. In such event, Licensee must destroy all copies of the SOFTWARE and all of its component parts. Defensive Suspension If Licensee commences or participates in any legal proceeding against NVIDIA, then NVIDIA may, in its sole discretion, suspend or terminate all license grants and any other rights provided under this Agreement during the pendency of such legal proceedings. -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License, or (at your -option) any later version. +1.4. Copyright All rights, title, interest and copyrights in and to the SOFTWARE (including but not limited to all images, photographs, animations, video, audio, music, text, and other information incorporated into the SOFTWARE), the accompanying printed materials, and any copies of the SOFTWARE, are owned by NVIDIA, or its suppliers. The SOFTWARE is protected by copyright laws and international treaty provisions. Accordingly, Licensee is required to treat the SOFTWARE like any other copyrighted material, except as otherwise allowed pursuant to this Agreement and that it may make one copy of the SOFTWARE solely for backup or archive purposes. RESTRICTED RIGHTS NOTICE. Software has been developed entirely at private expense and is commercial computer software provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the U.S. Government or a U.S. Government subcontractor is subject to the restrictions set forth in the Agreement under which Software was obtained pursuant to DFARS 227.7202-3(a) or as set forth in subparagraphs (c)(1) and (2) of the Commercial Computer Software - Restricted Rights clause at FAR 52.227-19, as applicable. Contractor/manufacturer is NVIDIA, 2701 San Tomas Expressway, Santa Clara, CA 95050. -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -Disclaimer ----------- -The University of Maryland and the authors make no representations -about the suitability or fitness of this software for any purpose. -It is provided "as is" without express or implied warranty. - -Author ------- -David Mount -Dept of Computer Science -University of Maryland, -College Park, MD 20742 USA -mount@cs.umd.edu -http://www.cs.umd.edu/~mount/ - - - -BOOST ---------------------------------------------------------------------- -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or -organization obtaining a copy of the software and accompanying -documentation covered by this license (the "Software") to use, -reproduce, display, distribute, execute, and transmit the Software, and -to prepare derivative works of the Software, and to permit third-parties -to whom the Software is furnished to do so, all subject to the -following: - -The copyright notices in the Software and this entire statement, -including the above license grant, this restriction and the following -disclaimer, must be included in all copies of the Software, in whole or -in part, and all derivative works of the Software, unless such copies or -derivative works are solely in the form of machine-executable object -code generated by a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON- -INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE -DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, -WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -ATLAS ---------------------------------------------------------------------- -Upstream Authors: -R. Clint Whaley atlas@cs.utk.edu -Jack Dongarra atlas@cs.utk.edu - -Copyright: -(C) Copyright 1997-2008 All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby granted -provided that the above copyright notice appear in all copies and -that both the copyright notice and this permission notice appear in -supporting documentation. - -Neither the University of Tennessee nor the Author make any -representations about the suitability of this software for any -purpose. This software is provided ``as is'' without express or -implied warranty. - -On alpha architectures, the standard builds use K. GOTO's kernels -contributed to the ATLAS project under the LGPL. Therefore on these -platforms, the distributed prebuilt libraries, as well as any user -built library using this kernel, is licensed under the LGPL. - -On Debian GNU/Linux systems, the complete text of the GNU Lesser General -Public License can be found in `/usr/share/common-licenses/LGPL'. - - -BAMTOOLS (The MIT License) ---------------------------------------------------------------------- -Copyright (c) 2009-2010 Derek Barnett, Erik Garrison, Gabor Marth, Michael Stromberg +1.5. Applicable Law This Agreement shall be deemed to have been made in, and shall be construed pursuant to, the laws of the State of Delaware. The United Nations Convention on Contracts for the International Sale of Goods is specifically disclaimed. The courts of Santa Clara County, California shall have exclusive jurisdiction and venue over any dispute arising out of or relating to this Agreement. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +1.6. Disclaimer of Warranties and Limitations on Liability -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + 1.6.1. No Warranties TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS IS" AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. + + 1.6.2. No Liability for Consequential Damages TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + 1.6.3. No Support . NVIDIA has no obligation to support or to provide any updates of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +1.7. Miscellaneous + + 1.7.1. Feedback Notwithstanding any Non-Disclosure Agreement executed by and between the parties, the parties agree that in the event Licensee or NVIDIA provides Feedback (as defined below) to the other party on how to design, implement, or improve the SOFTWARE or Licensee's product(s) for use with the SOFTWARE, the following terms and conditions apply the Feedback: + 1.7.1.1. Exchange of Feedback Both parties agree that neither party has an obligation to give the other +party any suggestions, comments or other feedback, whether verbally or in written or source code form, relating to (i) the SOFTWARE; (ii) Licensee's products; (iii) Licensee's use of the SOFTWARE; or (iv) optimization/interoperability of Licensee's product with the SOFTWARE (collectively defined as "Feedback"). In the event either party provides Feedback to the other party, the party receiving the Feedback may use any Feedback that the other party voluntarily provides to improve the (i) SOFTWARE or other related NVIDIA technologies, respectively for the benefit of NVIDIA; or (ii) Licensee's product or other related Licensee technologies, respectively for the benefit of Licensee. Accordingly, if either party provides Feedback to the other party, both parties agree that the other party and its respective licensees may freely use, reproduce, license, distribute, and otherwise commercialize the Feedback in the (i) SOFTWARE or other related technologies; or (ii) Licensee's products or other related technologies, respectively, without the payment of any royalties or fees. -VCFLIB ---------------------------------------------------------------------- -Copyright (c) 2012 Erik Garrison + 1.7.1.2. Residual Rights Licensee agrees that NVIDIA shall be free to use any general knowledge, skills and experience, (including, but not limited to, ideas, concepts, know-how, or techniques) ("Residuals"), contained in the (i) Feedback provided by Licensee to NVIDIA; (ii) Licensee's products shared or disclosed to NVIDIA in connection with the Feedback; or (c) Licensee's confidential information voluntarily provided to NVIDIA in connection with the Feedback, which are retained in the memories of NVIDIA's employees, agents, or contractors who have had access to such Residuals. Subject to the terms and conditions of this Agreement, NVIDIA's employees, agents, or contractors shall not be prevented from using Residuals as part of such employee's, agent's or contractor's general knowledge, skills, experience, talent, and/or expertise. NVIDIA shall not have any obligation to limit or restrict the assignment of such employees, agents or contractors or to pay royalties for any work resulting from the use of Residuals. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + 1.7.1.3. Disclaimer of Warranty FEEDBACK FROM EITHER PARTY IS PROVIDED FOR THE OTHER PARTY'S USE "AS IS" AND BOTH PARTIES DISCLAIM ALL WARRANTIES, EXPRESS, IMPLIED AND STATUTORY INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. BOTH PARTIES DO NOT REPRESENT OR WARRANT THAT THE FEEDBACK WILL MEET THE OTHER PARTY'S REQUIREMENTS OR THAT THE OPERATION OR IMPLEMENTATION OF THE FEEDBACK WILL BE UNINTERRUPTED OR ERROR-FREE. + 1.7.1.4. No Liability for Consequential Damages TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE FEEDBACK, EVEN IF THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + 1.7.2. Freedom of Action Licensee agrees that this Agreement is nonexclusive and NVIDIA may currently or in the future be developing software, other technology or confidential information internally, or receiving confidential information from other parties that maybe similar to the Feedback and Licensee's confidential information (as provided in Section 1.7.1.2 above), which may be provided to NVIDIA in connection with Feedback by Licensee. Accordingly, Licensee agrees that nothing in this Agreement will be construed as a representation or inference that NVIDIA will not develop, design, manufacture, acquire, market products, or have products developed, designed, manufactured, acquired, or marketed for NVIDIA, that compete with the Licensee's products or confidential information. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + 1.7.3. No Implied Licenses Under no circumstances should anything in this Agreement be construed as NVIDIA granting by implication, estoppel or otherwise, (i) a license to any NVIDIA product or technology other than the SOFTWARE; or (ii) any additional license rights for the SOFTWARE other than the licenses expressly granted in this Agreement. If any provision of this Agreement is inconsistent with, or cannot be fully enforced under, the law, such provision will be construed as limited to the extent necessary to be consistent with and fully enforceable under the law. This Agreement is the final, complete and exclusive agreement between the parties relating to the subject matter hereof, and supersedes all prior or contemporaneous understandings and agreements relating to such subject matter, whether oral or written. This Agreement may only be modified in writing signed by an authorized officer of NVIDIA. Licensee agrees that it will not ship, transfer or export the SOFTWARE into any country, or use the SOFTWARE in any manner, prohibited by the United States Bureau of Industry and Security or any export laws, restrictions or regulations. The parties agree that the following sections of the Agreement will survive the termination of the License: Section 1.2.1.4, Section 1.4, Section 1.5, Section 1.6, and Section 1.7. +1.8. Attachment A Redistributable Software In connection with Section 1.2.1.1 of this Agreement, the following files may be redistributed with software applications developed by Licensee, including certain variations of these files that have version number or architecture specific information NVIDIA CUDA Toolkit License Agreement www.nvidia.com End User License Agreements (EULA) DR-06739-001_v01_v8.0 | 9 embedded in the file name - as an example only, for release version 6.0 of the 64-bit Windows software, the file cudart64_60.dll is redistributable. +Component : CUDA Runtime Windows : cudart.dll, cudart_static.lib, cudadevrt.lib Mac OSX : libcudart.dylib, libcudart_static.a, libcudadevrt.a Linux : libcudart.so, libcudart_static.a, libcudadevrt.a Android : libcudart.so, libcudart_static.a, libcudadevrt.a Component : CUDA FFT Library Windows : cufft.dll, cufftw.dll Mac OSX : libcufft.dylib, libcufft_static.a, libcufftw.dylib, libcufftw_static.a Linux : libcufft.so, libcufft_static.a, libcufftw.so, libcufftw_static.a Android : libcufft.so, libcufft_static.a, libcufftw.so, libcufftw_static.a Component : CUDA BLAS Library Windows : cublas.dll, cublas_device.lib Mac OSX : libcublas.dylib, libcublas_static.a, libcublas_device.a Linux : libcublas.so, libcublas_static.a, libcublas_device.a Android : libcublas.so, libcublas_static.a, libcublas_device.a Component : NVIDIA "Drop-in" BLAS Library Windows : nvblas.dll Mac OSX : libnvblas.dylib Linux : libnvblas.so Component : CUDA Sparse Matrix Library Windows : cusparse.dll Mac OSX : libcusparse.dylib, libcusparse_static.a Linux : libcusparse.so, libcusparse_static.a Android : libcusparse.so, libcusparse_static.a Component : CUDA Linear Solver Library Windows : cusolver.dll Mac OSX : libcusolver.dylib, libcusolver_static.a Linux : libcusolver.so, libcusolver_static.a Android : libcusolver.so, libcusolver_static.a Component : CUDA Random Number Generation Library Windows : curand.dll Mac OSX : libcurand.dylib, libcurand_static.a Linux : libcurand.so, libcurand_static.a Android : libcurand.so, libcurand_static.a Component : NVIDIA Performance Primitives Library Windows : nppc.dll, nppi.dll, npps.dll Mac OSX : libnppc.dylib, libnppi.dylib, libnpps.dylib, libnppc_static.a, libnpps_static.a, libnppi_static.a Linux : libnppc.so, libnppi.so, libnpps.so, libnppc_static.a, libnpps_static.a, libnppi_static.a Android : libnppc.so, libnppi.so, libnpps.so, libnppc_static.a, libnpps_static.a, libnppi_static.a Component : Internal common library required for statically linking to cuBLAS, cuSPARSE, cuFFT, cuRAND and NPP Mac OSX : libculibos.a Linux : libculibos.a Component : NVIDIA Runtime Compilation Library Windows : nvrtc.dll, nvrtc-builtins.dll Mac OSX : libnvrtc.dylib, libnvrtc-builtins.dylib Linux : libnvrtc.so, libnvrtc-builtins.so Component : NVIDIA Optimizing Compiler Library Windows : nvvm.dll Mac OSX : libnvvm.dylib Linux : libnvvm.so Component : NVIDIA Common Device Math Functions Library Windows : libdevice.compute_20.bc, libdevice.compute_30.bc, libdevice.compute_35.bc Mac OSX : libdevice.compute_20.bc, libdevice.compute_30.bc, libdevice.compute_35.bc Linux : libdevice.compute_20.bc, libdevice.compute_30.bc, libdevice.compute_35.bc Component : CUDA Occupancy Calculation Header Library All : cuda_occupancy.h Component : Profiling Tools Interface Library Windows : cupti.dll Mac OSX : libcupti.dylib Linux : libcupti.so -FREEBAYES v0.9.8 ---------------------------------------------------------------------- -Copyright (c) 2010 Erik Garrison, Gabor Marth +1.9. Attachment B Additional Licensing Obligations The following third party components included in the SOFTWARE are licensed to Licensee pursuant to the following terms and conditions: + -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +1. Licensee's use of the GDB third party component is subject to the terms and conditions of GNU GPL v3: This product includes copyrighted third-party software licensed under the terms of the GNU General Public License v3 ("GPL v3"). All third-party software packages are copyright by their respective authors. GPL v3 terms and conditions are hereby incorporated into the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt Consistent with these licensing requirements, the software listed below is provided under the terms of the specified open source software licenses. To obtain source code for software provided under licenses that require redistribution of source code, including the GNU General Public License (GPL) and GNU Lesser General Public License (LGPL), contact oss-requests@nvidia.com. This offer is valid for a period of three (3) years from the date of the distribution of this product by NVIDIA CORPORATION. Component License CUDA-GDB GPL v3 -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +2. Licensee represents and warrants that any and all third party licensing and/or royalty payment obligations in connection with Licensee's use of the H.264 video codecs are solely the responsibility of Licensee. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +3. Licensee's use of the Thrust library is subject to the terms and conditions of the Apache License Version 2.0. All third-party software packages are copyright by their respective authors. Apache License Version 2.0 terms and conditions are hereby incorporated into the Agreement by this reference. http://www.apache.org/licenses/ LICENSE-2.0.html. In addition, Licensee acknowledges the following notice: Thrust includes source code from the Boost Iterator, Tuple, System, and Random Number libraries. Boost Software License - Version 1.0 - August 17th, 2003 . . . . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +4. Licensee's use of the LLVM third party component is subject to the following terms and conditions: + + LLVM Release License + + University of Illinois/NCSA Open Source License Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana- Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +5. Licensee's use of the PCRE third party component is subject to the following terms and conditions: + PCRE LICENCE + + PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Release 8 of PCRE is distributed under the terms of the "BSD" licence, as specified below. The documentation for PCRE, supplied in the "doc" directory, is distributed under the same terms as the software itself. The basic library functions are written in C and are freestanding. Also included in the distribution is a set of C++ wrapper functions, and a justin-time compiler that can be used to optimize pattern matching. These are both optional features that can be omitted when the library is built. + + + THE BASIC LIBRARY FUNCTIONS + + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER -===================================================================== -3. GPL v2 -===================================================================== -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu Copyright(c) 2009-2012 Zoltan Herczeg All rights reserved. + + THE C++ WRAPPER FUNCTIONS + + Contributed by: Google Inc. Copyright (c) 2007-2012, Google Inc. All rights reserved. + + THE "BSD" LICENCE + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the name of Google Inc. nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -Preamble -The licenses for most software are designed to take away your freedom to share and -change it. By contrast, the GNU General Public License is intended to guarantee your -freedom to share and change free software--to make sure the software is free for all its -users. This General Public License applies to most of the Free Software Foundation's -software and to any other program whose authors commit to using it. (Some other Free -Software Foundation software is covered by the GNU Lesser General Public License -instead.) You can apply it to your programs, too. -When we speak of free software, we are referring to freedom, not price. Our General -Public Licenses are designed to make sure that you have the freedom to distribute copies -of free software (and charge for this service if you wish), that you receive source code or -can get it if you want it, that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. -To protect your rights, we need to make restrictions that forbid anyone to deny you these -rights or to ask you to surrender the rights. These restrictions translate to certain -responsibilities for you if you distribute copies of the software, or if you modify it. -For example, if you distribute copies of such a program, whether gratis or for a fee, you -must give the recipients all the rights that you have. You must make sure that they, too, -receive or can get the source code. And you must show them these terms so they know -their rights. -We protect your rights with two steps: (1) copyright the software, and (2) offer you this -license which gives you legal permission to copy, distribute and/or modify the software. -Also, for each author's protection and ours, we want to make certain that everyone -understands that there is no warranty for this free software. If the software is modified -by someone else and passed on, we want its recipients to know that what they have is not -the original, so that any problems introduced by others will not reflect on the original -authors' reputations. -Finally, any free program is threatened constantly by software patents. We wish to avoid -the danger that redistributors of a free program will individually obtain patent licenses, in -effect making the program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. -The precise terms and conditions for copying, distribution and modification follow. -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND -MODIFICATION -0. This License applies to any program or other work which contains a notice placed by -the copyright holder saying it may be distributed under the terms of this General Public -License. The "Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, either verbatim or with -modifications and/or translated into another language. (Hereinafter, translation is -included without limitation in the term "modification".) Each licensee is addressed as -"you". -Activities other than copying, distribution and modification are not covered by this -License; they are outside its scope. The act of running the Program is not restricted, and -the output from the Program is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). Whether that is true -depends on what the Program does. -1. You may copy and distribute verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and appropriately publish on -each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; and give any other -recipients of the Program a copy of this License along with the Program. -You may charge a fee for the physical act of transferring a copy, and you may at your -option offer warranty protection in exchange for a fee. -2. You may modify your copy or copies of the Program or any portion of it, thus forming -a work based on the Program, and copy and distribute such modifications or work under -the terms of Section 1 above, provided that you also meet all of these conditions: -a) You must cause the modified files to carry prominent notices stating that you -changed the files and the date of any change. -b) You must cause any work that you distribute or publish, that in whole or in part -contains or is derived from the Program or any part thereof, to be licensed as a -whole at no charge to all third parties under the terms of this License. -c) If the modified program normally reads commands interactively when run, you -must cause it, when started running for such interactive use in the most ordinary -way, to print or display an announcement including an appropriate copyright -notice and a notice that there is no warranty (or else, saying that you provide a -warranty) and that users may redistribute the program under these conditions, and -telling the user how to view a copy of this License. (Exception: if the Program -itself is interactive but does not normally print such an announcement, your work -based on the Program is not required to print an announcement.) -These requirements apply to the modified work as a whole. If identifiable sections of that -work are not derived from the Program, and can be reasonably considered independent -and separate works in themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you distribute the same -sections as part of a whole which is a work based on the Program, the distribution of the -whole must be on the terms of this License, whose permissions for other licensees extend -to the entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest your rights to work -written entirely by you; rather, the intent is to exercise the right to control the distribution -of derivative or collective works based on the Program. -In addition, mere aggregation of another work not based on the Program with the -Program (or with a work based on the Program) on a volume of a storage or distribution -medium does not bring the other work under the scope of this License. -3. You may copy and distribute the Program (or a work based on it, under Section 2) in -object code or executable form under the terms of Sections 1 and 2 above provided that -you also do one of the following: -a) Accompany it with the complete corresponding machine-readable source code, -which must be distributed under the terms of Sections 1 and 2 above on a medium -customarily used for software interchange; or, -b) Accompany it with a written offer, valid for at least three years, to give any -third party, for a charge no more than your cost of physically performing source -distribution, a complete machine-readable copy of the corresponding source code, -to be distributed under the terms of Sections 1 and 2 above on a medium -customarily used for software interchange; or, -c) Accompany it with the information you received as to the offer to distribute -corresponding source code. (This alternative is allowed only for noncommercial -distribution and only if you received the program in object code or executable -form with such an offer, in accord with Subsection b above.) -The source code for a work means the preferred form of the work for making -modifications to it. For an executable work, complete source code means all the source -code for all modules it contains, plus any associated interface definition files, plus the -scripts used to control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major components (compiler, kernel, -and so on) of the operating system on which the executable runs, unless that component -itself accompanies the executable. -If distribution of executable or object code is made by offering access to copy from a -designated place, then offering equivalent access to copy the source code from the same -place counts as distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. -4. You may not copy, modify, sublicense, or distribute the Program except as expressly -provided under this License. Any attempt otherwise to copy, modify, sublicense or -distribute the Program is void, and will automatically terminate your rights under this -License. However, parties who have received copies, or rights, from you under this -License will not have their licenses terminated so long as such parties remain in full -compliance. -5. You are not required to accept this License, since you have not signed it. However, -nothing else grants you permission to modify or distribute the Program or its derivative -works. These actions are prohibited by law if you do not accept this License. Therefore, -by modifying or distributing the Program (or any work based on the Program), you -indicate your acceptance of this License to do so, and all its terms and conditions for -copying, distributing or modifying the Program or works based on it. -6. Each time you redistribute the Program (or any work based on the Program), the -recipient automatically receives a license from the original licensor to copy, distribute or -modify the Program subject to these terms and conditions. You may not impose any -further restrictions on the recipients' exercise of the rights granted herein. You are not -responsible for enforcing compliance by third parties to this License. -7. If, as a consequence of a court judgment or allegation of patent infringement or for any -other reason (not limited to patent issues), conditions are imposed on you (whether by -court order, agreement or otherwise) that contradict the conditions of this License, they -do not excuse you from the conditions of this License. If you cannot distribute so as to -satisfy simultaneously your obligations under this License and any other pertinent -obligations, then as a consequence you may not distribute the Program at all. For example, -if a patent license would not permit royalty-free redistribution of the Program by all those -who receive copies directly or indirectly through you, then the only way you could satisfy -both it and this License would be to refrain entirely from distribution of the Program. -If any portion of this section is held invalid or unenforceable under any particular -circumstance, the balance of the section is intended to apply and the section as a whole is -intended to apply in other circumstances. -It is not the purpose of this section to induce you to infringe any patents or other property -right claims or to contest validity of any such claims; this section has the sole purpose of -protecting the integrity of the free software distribution system, which is implemented by -public license practices. Many people have made generous contributions to the wide range -of software distributed through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing to distribute software -through any other system and a licensee cannot impose that choice. -This section is intended to make thoroughly clear what is believed to be a consequence of -the rest of this License. -8. If the distribution and/or use of the Program is restricted in certain countries either by -patents or by copyrighted interfaces, the original copyright holder who places the -Program under this License may add an explicit geographical distribution limitation -excluding those countries, so that distribution is permitted only in or among countries not -thus excluded. In such case, this License incorporates the limitation as if written in the -body of this License. -9. The Free Software Foundation may publish revised and/or new versions of the General -Public License from time to time. Such new versions will be similar in spirit to the present -version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Program specifies a version -number of this License which applies to it and "any later version", you have the option of -following the terms and conditions either of that version or of any later version published -by the Free Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. -10. If you wish to incorporate parts of the Program into other free programs whose -distribution conditions are different, write to the author to ask for permission. For -software which is copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our decision will be -guided by the two goals of preserving the free status of all derivatives of our free -software and of promoting the sharing and reuse of software generally. -NO WARRANTY -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE -COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM -"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR -IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE -ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO -IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO -MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED -ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, -SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF -THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT -LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR -LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE -PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH -HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. -===================================================================== -4. GPL v3 -===================================================================== +6. Some of the cuBLAS library routines were written by or derived from code written by Vasily Volkov and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2007-2009, Regents of the University of California All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of California, Berkeley nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +7. Some of the cuBLAS library routines were written by or derived from code written by Davide Barbieri and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +8. Some of the cuBLAS library routines were derived from code developed by the University of Tennessee and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2010 The University of Tennessee. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +9. Some of the cuBLAS library routines were written by or derived from code written by Jonathan Hogg and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2012, The Science and Technology Facilities Council (STFC). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the STFC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE STFC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +10. Some of the cuBLAS library routines were written by or derived from code written by Ahmad M. Abdelfattah, David Keyes, and Hatem Ltaief, and are subject to the Apache License, Version 2.0, as follows: -- (C) Copyright 2013 King Abdullah University of Science and Technology Authors: Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) David Keyes (david.keyes@kaust.edu.sa) Hatem Ltaief (hatem.ltaief@kaust.edu.sa) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the King Abdullah University of Science and Technology nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +11. Some of the cuSPARSE library routines were written by or derived from code written by Li-Wen Chang and are subject to the NCSA Open Source License as follows: Copyright (c) 2012, University of Illinois. All rights reserved. Developed by: IMPACT Group, University of Illinois, http:// impact.crhc.illinois.edu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of IMPACT Group, University of Illinois, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + +12. Some of the cuRAND library routines were written by or derived from code written by Mutsuo Saito and Makoto Matsumoto and are subject to the following license: Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima University. All rights reserved. Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima University and University of Tokyo. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Hiroshima University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +13. Some of the cuRAND library routines were derived from code developed by D. E. Shaw Research and are subject to the following license: Copyright 2010-2011, D. E. Shaw Research. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of D. E. Shaw Research nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +14. Licensee's use of the lz4 third party component is subject to the following terms and conditions: Copyright (C) 2011-2013, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +15. The NPP library uses code from the Boost Math Toolkit, and is subject to the following license: Boost Software License - Version 1.0 - August 17th, 2003 . . . . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Ansible +------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + Copyright (c) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + Preamble + The GNU General Public License is a free, copyleft license for software and other kinds of works. + The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to @@ -495,6 +655,7 @@ Public Licenses are designed to make sure that you have the freedom to distribut of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of @@ -503,13 +664,16 @@ For example, if you distribute copies of such a program, whether gratis or for a must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. -Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright -on the software, and (2) offer you this License giving you legal permission to copy, -distribute and/or modify it. + +Developers that use the GNU GPL protect your rights with two steps: + + (1) assert copyright on the software, and + (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The @@ -518,13 +682,16 @@ which is precisely where it is most unacceptable. Therefore, we have designed th of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. + TERMS AND CONDITIONS + 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as @@ -551,6 +718,7 @@ copyright notice, and (2) tells the user that there is no warranty for the work the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. @@ -579,6 +747,7 @@ parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. + 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License @@ -596,6 +765,7 @@ exclusively on your behalf, under your direction and control, on terms that proh from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty @@ -606,6 +776,7 @@ technological measures to the extent such circumvention is effected by exercisin under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an @@ -615,22 +786,23 @@ of the absence of any warranty; and give all recipients a copy of this License a the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: -* a) The work must carry prominent notices stating that you modified it, and giving + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. -* b) The work must carry prominent notices stating that it is released under this + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". -* c) You must license the entire work, as a whole, under this License to anyone who + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. -* d) If the work has interactive user interfaces, each must display Appropriate Legal + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are @@ -640,14 +812,15 @@ called an "aggregate" if the compilation and its resulting copyright are not use the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: -* a) Convey the object code in, or embodied in, a physical product (including a + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. -* b) Convey the object code in, or embodied in, a physical product (including a + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a @@ -656,11 +829,11 @@ covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. -* c) Convey individual copies of the object code with a copy of the written offer to + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. -* d) Convey the object code by offering access from a designated place (gratis or + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy @@ -670,7 +843,7 @@ copying facilities, provided you maintain clear directions next to the object co saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. -* e) Convey the object code using peer-to-peer transmission, provided you inform + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the @@ -710,6 +883,7 @@ Corresponding Source conveyed, and Installation Information provided, in accord this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable @@ -726,19 +900,19 @@ which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: -* a) Disclaiming warranty or limiting liability differently from the terms of sections + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or -* b) Requiring preservation of specified reasonable legal notices or author + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or -* c) Prohibiting misrepresentation of the origin of that material, or requiring that + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or -* d) Limiting the use for publicity purposes of names of licensors or authors of the + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or -* e) Declining to grant rights under trademark law for use of some trade names, + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or -* f) Requiring indemnification of licensors and authors of that material by anyone + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. @@ -754,6 +928,7 @@ relevant source files, a statement of the additional terms that apply to those f notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will @@ -773,6 +948,7 @@ Termination of your rights under this section does not terminate the licenses of who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of @@ -781,6 +957,7 @@ However, nothing other than this License grants you permission to propagate or m any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. @@ -798,6 +975,7 @@ other charge for exercise of rights granted under this License, and you may not litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the @@ -847,6 +1025,7 @@ that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of @@ -856,6 +1035,7 @@ you may not convey it at all. For example, if you agree to terms that obligate y collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero @@ -863,6 +1043,7 @@ General Public License into a single combined work, and to convey the resulting The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the @@ -879,6 +1060,7 @@ version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN @@ -890,6 +1072,7 @@ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO @@ -902,6 +1085,7 @@ SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most @@ -909,3 +1093,220 @@ closely approximates an absolute waiver of all civil liability in connection wit Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. +ion_timeout.sh +------------------------------------------------------------- + +GNU Lesser General Public License (LGPL) + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +------------------------------------------------------------- + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + + Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + + If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +2. GNU General Public License version 2 (GPL v2) +===================================================================== + +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +------------------------------------------------------------- + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/buildTools/build.sh b/buildTools/build.sh index 15cc7590..aee0f01d 100755 --- a/buildTools/build.sh +++ b/buildTools/build.sh @@ -82,6 +82,14 @@ for MODULE in $MODULES; do fi done; +# if the environmental variable is set to create a repository, we will move all of the packages into that repository and +# index them so aptitude can read them in as a repository +if [ ! -z ${MAKE_REPO_DIRECTORY} ]; then + mkdir -p ${BUILD_ROOT}/build/repo + find ${BUILD_ROOT}/build -type f -iname "*.deb" ! -path "$BUILD_ROOT/build/*" -exec mv {} ${BUILD_ROOT}/build/repo \; + cd ${BUILD_ROOT}/build/repo && dpkg-scanpackages -m ./ > ${BUILD_ROOT}/build/repo/Packages +fi + if [ $ERR != 0 ]; then echo -e $ERRMSG echo "FAILURES: $ERR modules failed to build." @@ -89,3 +97,4 @@ if [ $ERR != 0 ]; then else echo "SUCCESS: All modules built." fi + diff --git a/buildTools/cmake/CMakeLists.compiler.txt b/buildTools/cmake/CMakeLists.compiler.txt index 83c84616..028ace3d 100644 --- a/buildTools/cmake/CMakeLists.compiler.txt +++ b/buildTools/cmake/CMakeLists.compiler.txt @@ -16,8 +16,9 @@ endif(LSB_RELEASE_COMMAND) # Common compiler flags # char is per default signed on x86_64 systems but unsigned on ARM, use signed-char -if (${LSB_RELEASE_CODENAME} STREQUAL "trusty") - set(ION_COMMON_FLAGS "${ION_COMMON_FLAGS} -Wall -Wextra -Werror -fPIC -fsigned-char") +if (${LSB_RELEASE_CODENAME} STREQUAL "trusty" OR + ${LSB_RELEASE_CODENAME} STREQUAL "xenial") + set(ION_COMMON_FLAGS "${ION_COMMON_FLAGS} -Wall -Wextra -Werror -fPIC -fsigned-char -Wno-deprecated-declarations") else() set(ION_COMMON_FLAGS "${ION_COMMON_FLAGS} -Wall -Wextra -fPIC -fsigned-char") endif() @@ -78,13 +79,14 @@ set(CUDA_PROPAGATE_HOST_FLAGS OFF) ## NOTE: -O2 enables strict-aliasing, and code is not clean SET( CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} - "-std=c++11" "-O2" "--compiler-options=-fno-strict-aliasing" "-g" "--use_fast_math" + "-D_MWAITXINTRIN_H_INCLUDED" "-D_FORCE_INLINES" "-std=c++11" "-O2" "--compiler-options=-fno-strict-aliasing" "-g" "--use_fast_math" "-gencode=arch=compute_20,code=sm_20" "-gencode=arch=compute_30,code=sm_30" "-gencode=arch=compute_35,code=sm_35" "-gencode=arch=compute_50,code=sm_50" "-gencode=arch=compute_52,code=sm_52" "-gencode=arch=compute_52,code=compute_52" + "-gencode=arch=compute_61,code=sm_61" "-maxrregcount=48" ) ## If user set CXX on command line, make sure NVCC respects it diff --git a/buildTools/cmake/CMakeLists.dependencies.txt b/buildTools/cmake/CMakeLists.dependencies.txt index f821d2cc..0ed70f09 100644 --- a/buildTools/cmake/CMakeLists.dependencies.txt +++ b/buildTools/cmake/CMakeLists.dependencies.txt @@ -92,10 +92,10 @@ mark_as_advanced(ION_USE_SYSTEM_BOOST) option(ION_USE_SYSTEM_PICARD "Use picard system libraries" OFF) mark_as_advanced(ION_USE_SYSTEM_PICARD) -set(cuda_proj_version "7.5.18-19867135") -set(cuda_toolkit_tar_file "cuda-linux64-rel-7.5.18-19867135.tar.gz") -set(cuda_toolkit_patch_file "cuda_toolkit-7.5.18.patch") -set(CUDA_VERSION "7.5") +set(cuda_proj_version "8.0.44-21122537") +set(cuda_toolkit_tar_file "cuda-linux64-16.04-rel-8.0.44-21122537.tar.gz") +set(CUDA_VERSION "8.0") + set(cuda_toolkit "cuda_toolkit") set(cuda_toolkit_version "${cuda_toolkit}-${cuda_proj_version}") @@ -118,7 +118,7 @@ if(ION_USE_CUDA) CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - PATCH_COMMAND patch -p1 -t -N < "${PROJECT_SOURCE_DIR}/../external/${cuda_toolkit_patch_file}" + #PATCH_COMMAND patch -p1 -t -N < "${PROJECT_SOURCE_DIR}/../external/${cuda_toolkit_patch_file}" ) @@ -156,18 +156,19 @@ if(NOT ION_USE_SYSTEM_HDF5) --prefix=${PROJECT_BINARY_DIR}/../${proj_name_version}-install ) include_directories("${PROJECT_BINARY_DIR}/../${proj_name_version}-install/include") - set(ION_HDF5_LIBS "${PROJECT_BINARY_DIR}/../${proj_name_version}-install/lib/libhdf5.a") - set(ION_HDF5_HL_LIBS "${PROJECT_BINARY_DIR}/../${proj_name_version}-install/lib/libhdf5_hl.a") + set(HDF5_LIBRARIES "${PROJECT_BINARY_DIR}/../${proj_name_version}-install/lib/libhdf5.a") + set(HDF5_HL_LIBRARIES "${PROJECT_BINARY_DIR}/../${proj_name_version}-install/lib/libhdf5_hl.a") + set(ION_HDF5_LIBS "${HDF5_HL_LIBRARIES};${HDF5_LIBRARIES}") else() add_custom_target(hdf5_proj) - set(HDF5_USE_STATIC_LIBRARIES True) + set(HDF5_USE_STATIC_LIBRARIES OFF) find_package(HDF5 REQUIRED) if (${LSB_RELEASE_CODENAME} STREQUAL "trusty" OR ${LSB_RELEASE_CODENAME} STREQUAL "vivid" OR ${LSB_RELEASE_CODENAME} STREQUAL "xenial") - set(ION_HDF5_LIBS "hdf5;${HDF5_LIBRARIES}") + set(ION_HDF5_LIBS "hdf5;hdf5_hl;${HDF5_LIBRARIES}") else() - set(ION_HDF5_LIBS "${HDF5_LIBRARIES}") + set(ION_HDF5_LIBS "${HDF5_HL_LIBRARIES};${HDF5_LIBRARIES}") endif() if (${LSB_RELEASE_CODENAME} STREQUAL "vivid" OR ${LSB_RELEASE_CODENAME} STREQUAL "xenial") diff --git a/buildTools/terms-of-use.txt b/buildTools/terms-of-use.txt index c05f8f2d..6fa2cdce 100644 --- a/buildTools/terms-of-use.txt +++ b/buildTools/terms-of-use.txt @@ -1,414 +1,647 @@ +Torrent Suite Software Version 5.4 ===================================================================== -1. Torrent Suite Software License -===================================================================== - -Torrent Suite Software: This software provides for the analysis and storage of data received from the Ion Personal Genome Machine(R) PGM(tm) Sequencer or the Ion Proton(R) Sequencer. ---------------------------------------------------------------------- - -Copyright (C) 2011 Life Technologies Corporation +Torrent Suite(tm) Software v5.4: This software provides for the analysis and storage of data received from the Ion Personal Genome Machine(tm) Sequencer, the Ion Proton(tm) Sequencer, the Ion S5(tm) Sequencer or the Ion S5(tm) XL Sequencer. +April 19, 2017 +Copyright (C) 2017 Life Technologies Corporation All Rights Reserved. This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License, or (at your -option) any later version. +under the terms of the GNU General Public License version 2 as published by the +Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. +General Public License version 2 for more details. -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. You can also find the -GPL on the GNU web site: . +You should have received a copy of the GNU General Public License version 2 along +with this program (see Section 2 below); if not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. You can also find the +GPL version 2 on the GNU web site: https://www.gnu.org/licenses/gpl-2.0.html + +1. Third Party Software Notices and Licenses ===================================================================== -2. Third Party Software Notices and Licenses. -===================================================================== + +This software uses third party software components from several sources. Portions of these software components are copyrighted and licensed by their respective owners as indicated below. Various licenses require distribution of source code or if a link is used to point the end-user to a source-code repository, and the source code is not available at such site, the distributor must, for a time determined by license, offer to provide source code. In such cases, please contact your Life Technologies representative. As well, various licenses require that the end-user receive a copy of the license or certain notices. Such licenses and notices may be found below. In order to use this software, the end-user must abide by the terms and conditions of these third party licenses. + +Armadillo +------------------------------------------------------------- + +Mozilla Public License Version 2.0 +------------------------------------------------------------- + +1. Definitions +------------------------------------------------------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +------------------------------------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------------------------------------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +------------------------------------------------------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + + + +6. Disclaimer of Warranty +------------------------------------------------------------- + +Covered Software is provided under this License on an "as is" basis, without +warranty of any kind, either expressed, implied, or statutory, including, +without limitation, warranties that the Covered Software is free of defects, +merchantable, fit for a particular purpose or non-infringing. The entire risk +as to the quality and performance of the Covered Software is with You. Should +any Covered Software prove defective in any respect, You (not any Contributor) +assume the cost of any necessary servicing, repair, or correction. This +disclaimer of warranty constitutes an essential part of this License. No use of +any Covered Software is authorized under this License except under this +disclaimer. + + +7. Limitation of Liability +------------------------------------------------------------- + +Under no circumstances and under no legal theory, whether tort (including +negligence), contract, or otherwise, shall any Contributor, or anyone who +distributes Covered Software as permitted above, be liable to You for any +direct, indirect, special, incidental, or consequential damages of any +character including, without limitation, damages for lost profits, loss of +goodwill, work stoppage, computer failure or malfunction, or any and all other +commercial damages or losses, even if such party shall have been informed of +the possibility of such damages. This limitation of liability shall not apply +to liability for death or personal injury resulting from such party's +negligence to the extent applicable law prohibits such limitation. Some +jurisdictions do not allow the exclusion or limitation of incidental or +consequential damages, so this exclusion and limitation may not apply to You. + + +8. Litigation +------------------------------------------------------------- -The following third party software components are included in the Torrent Suite -Software. These software components are copyrighted and licensed by their respective -owners. Various components require distribution of source code or if a URL is used to -point the end-user to a source-code repository, and the source code is not available at -such site, the distributor must, for a time determined by the license, offer to provide the -source code. In such cases, please contact your Ion Torrent representative. As well, -various licenses require that the end-user receive a copy of the license and such licenses -may be found below. In order to use this Torrent Suite Software, the end-user must -abide by the terms and conditions of these third-party licenses. +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. -lapack -URL: http://www.netlib.org/lapack/ -License: BSD +9. Miscellaneous +------------------------------------------------------------- -k-means -URL: http://www.cs.umd.edu/~mount/Projects/KMeans/ -License: GPL +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. -boost -URL: http://www.boost.org/ -License: BSD +10. Versions of the License +------------------------------------------------------------- -atlas -URL: http://math-atlas.sourceforge.net -License: BSD +10.1. New Versions -libgfortran -License: GPL +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. -LAPACK ---------------------------------------------------------------------- -Copyright (c) 1992-2008 The University of Tennessee. All rights -reserved. +10.2. Effect of New Versions -$COPYRIGHT$ +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. -Additional copyrights may follow +10.3. Modified Versions -$HEADER$ +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------------------------- + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +------------------------------------------------------------- + +This Source Code Form is "Incompatible With Secondary Licenses", as +defined by the Mozilla Public License, v. 2.0. + + +HTS Lib and Samtools +------------------------------------------------------------- + +The MIT/Expat License +Copyright (C) 2008-2014 Genome Research Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +VCF Lib +------------------------------------------------------------- + +The MIT License (see text above) +Copyright (c) 2012 Erik Garrison + +Blas and Lapack +------------------------------------------------------------- +Copyright (c) 1992-2013 The University of Tennessee and The University of Tennessee Research Foundation. All rights reserved. + +Copyright (c) 2000-2013 The University of California Berkeley. All rights reserved. + +Copyright (c) 2006-2013 The University of Colorado Denver. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -- Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer listed - in this license in the documentation and/or other materials - provided with the distribution. - -- Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +The copyright holders provide no reassurances that the source code +provided does not infringe any patent, copyright, or any other +intellectual property rights of third parties. The copyright holders +disclaim any liability to any recipient for claims brought against +recipient by any third party for infringement of that parties +intellectual property rights. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Django +------------------------------------------------------------- +Copyright (c) Django Software Foundation and individual contributors. All rights reserved. -K-MEANS ---------------------------------------------------------------------- -KMlocal: A testbed for k-means clustering algorithms based on local -search -Version: 1.7 -Date: 08/10/2005 +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Copyright (C) 2004-2005 David M. Mount and University of Maryland -All Rights Reserved. +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License, or (at your -option) any later version. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -Disclaimer ----------- -The University of Maryland and the authors make no representations -about the suitability or fitness of this software for any purpose. -It is provided "as is" without express or implied warranty. - -Author ------- -David Mount -Dept of Computer Science -University of Maryland, -College Park, MD 20742 USA -mount@cs.umd.edu -http://www.cs.umd.edu/~mount/ - - - -BOOST ---------------------------------------------------------------------- -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or -organization obtaining a copy of the software and accompanying -documentation covered by this license (the "Software") to use, -reproduce, display, distribute, execute, and transmit the Software, and -to prepare derivative works of the Software, and to permit third-parties -to whom the Software is furnished to do so, all subject to the -following: - -The copyright notices in the Software and this entire statement, -including the above license grant, this restriction and the following -disclaimer, must be included in all copies of the Software, in whole or -in part, and all derivative works of the Software, unless such copies or -derivative works are solely in the form of machine-executable object -code generated by a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON- -INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE -DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, -WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -ATLAS ---------------------------------------------------------------------- -Upstream Authors: -R. Clint Whaley atlas@cs.utk.edu -Jack Dongarra atlas@cs.utk.edu - -Copyright: -(C) Copyright 1997-2008 All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby granted -provided that the above copyright notice appear in all copies and -that both the copyright notice and this permission notice appear in -supporting documentation. - -Neither the University of Tennessee nor the Author make any -representations about the suitability of this software for any -purpose. This software is provided ``as is'' without express or -implied warranty. - -On alpha architectures, the standard builds use K. GOTO's kernels -contributed to the ATLAS project under the LGPL. Therefore on these -platforms, the distributed prebuilt libraries, as well as any user -built library using this kernel, is licensed under the LGPL. - -On Debian GNU/Linux systems, the complete text of the GNU Lesser General -Public License can be found in `/usr/share/common-licenses/LGPL'. +Cuda Toolkit +------------------------------------------------------------- +END USER LICENSE AGREEMENT (EULA) -===================================================================== -3. GPL v2 -===================================================================== -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +Important Notice READ CAREFULLY: This Software License Agreement ("Agreement") for NVIDIA CUDA Toolkit, including computer software and associated documentation ("Software"), is the Agreement which governs use of the SOFTWARE of NVIDIA Corporation and its subsidiaries ("NVIDIA") downloadable herefrom. By downloading, installing, copying, or otherwise using the SOFTWARE, You (as defined below) agree to be bound by the terms of this Agreement. If You do not agree to the terms of this Agreement, do not download the SOFTWARE. Recitals Use of NVIDIA's SOFTWARE requires three elements: the SOFTWARE, an NVIDIA GPU or application processor ("NVIDIA Hardware"), and a computer system. The SOFTWARE is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE is not sold, and instead is only licensed for Your use, strictly in accordance with this Agreement. The NVIDIA Hardware is protected by various patents, and is sold, but this Agreement does not cover the sale or use of such hardware, since it may not necessarily be sold as a package with the SOFTWARE. This Agreement sets forth the terms and conditions of the SOFTWARE only. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -Preamble -The licenses for most software are designed to take away your freedom to share and -change it. By contrast, the GNU General Public License is intended to guarantee your -freedom to share and change free software--to make sure the software is free for all its -users. This General Public License applies to most of the Free Software Foundation's -software and to any other program whose authors commit to using it. (Some other Free -Software Foundation software is covered by the GNU Lesser General Public License -instead.) You can apply it to your programs, too. -When we speak of free software, we are referring to freedom, not price. Our General -Public Licenses are designed to make sure that you have the freedom to distribute copies -of free software (and charge for this service if you wish), that you receive source code or -can get it if you want it, that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. -To protect your rights, we need to make restrictions that forbid anyone to deny you these -rights or to ask you to surrender the rights. These restrictions translate to certain -responsibilities for you if you distribute copies of the software, or if you modify it. -For example, if you distribute copies of such a program, whether gratis or for a fee, you -must give the recipients all the rights that you have. You must make sure that they, too, -receive or can get the source code. And you must show them these terms so they know -their rights. -We protect your rights with two steps: (1) copyright the software, and (2) offer you this -license which gives you legal permission to copy, distribute and/or modify the software. -Also, for each author's protection and ours, we want to make certain that everyone -understands that there is no warranty for this free software. If the software is modified -by someone else and passed on, we want its recipients to know that what they have is not -the original, so that any problems introduced by others will not reflect on the original -authors' reputations. -Finally, any free program is threatened constantly by software patents. We wish to avoid -the danger that redistributors of a free program will individually obtain patent licenses, in -effect making the program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. -The precise terms and conditions for copying, distribution and modification follow. -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND -MODIFICATION -0. This License applies to any program or other work which contains a notice placed by -the copyright holder saying it may be distributed under the terms of this General Public -License. The "Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, either verbatim or with -modifications and/or translated into another language. (Hereinafter, translation is -included without limitation in the term "modification".) Each licensee is addressed as -"you". -Activities other than copying, distribution and modification are not covered by this -License; they are outside its scope. The act of running the Program is not restricted, and -the output from the Program is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). Whether that is true -depends on what the Program does. -1. You may copy and distribute verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and appropriately publish on -each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; and give any other -recipients of the Program a copy of this License along with the Program. -You may charge a fee for the physical act of transferring a copy, and you may at your -option offer warranty protection in exchange for a fee. -2. You may modify your copy or copies of the Program or any portion of it, thus forming -a work based on the Program, and copy and distribute such modifications or work under -the terms of Section 1 above, provided that you also meet all of these conditions: -a) You must cause the modified files to carry prominent notices stating that you -changed the files and the date of any change. -b) You must cause any work that you distribute or publish, that in whole or in part -contains or is derived from the Program or any part thereof, to be licensed as a -whole at no charge to all third parties under the terms of this License. -c) If the modified program normally reads commands interactively when run, you -must cause it, when started running for such interactive use in the most ordinary -way, to print or display an announcement including an appropriate copyright -notice and a notice that there is no warranty (or else, saying that you provide a -warranty) and that users may redistribute the program under these conditions, and -telling the user how to view a copy of this License. (Exception: if the Program -itself is interactive but does not normally print such an announcement, your work -based on the Program is not required to print an announcement.) -These requirements apply to the modified work as a whole. If identifiable sections of that -work are not derived from the Program, and can be reasonably considered independent -and separate works in themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you distribute the same -sections as part of a whole which is a work based on the Program, the distribution of the -whole must be on the terms of this License, whose permissions for other licensees extend -to the entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest your rights to work -written entirely by you; rather, the intent is to exercise the right to control the distribution -of derivative or collective works based on the Program. -In addition, mere aggregation of another work not based on the Program with the -Program (or with a work based on the Program) on a volume of a storage or distribution -medium does not bring the other work under the scope of this License. -3. You may copy and distribute the Program (or a work based on it, under Section 2) in -object code or executable form under the terms of Sections 1 and 2 above provided that -you also do one of the following: -a) Accompany it with the complete corresponding machine-readable source code, -which must be distributed under the terms of Sections 1 and 2 above on a medium -customarily used for software interchange; or, -b) Accompany it with a written offer, valid for at least three years, to give any -third party, for a charge no more than your cost of physically performing source -distribution, a complete machine-readable copy of the corresponding source code, -to be distributed under the terms of Sections 1 and 2 above on a medium -customarily used for software interchange; or, -c) Accompany it with the information you received as to the offer to distribute -corresponding source code. (This alternative is allowed only for noncommercial -distribution and only if you received the program in object code or executable -form with such an offer, in accord with Subsection b above.) -The source code for a work means the preferred form of the work for making -modifications to it. For an executable work, complete source code means all the source -code for all modules it contains, plus any associated interface definition files, plus the -scripts used to control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major components (compiler, kernel, -and so on) of the operating system on which the executable runs, unless that component -itself accompanies the executable. -If distribution of executable or object code is made by offering access to copy from a -designated place, then offering equivalent access to copy the source code from the same -place counts as distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. -4. You may not copy, modify, sublicense, or distribute the Program except as expressly -provided under this License. Any attempt otherwise to copy, modify, sublicense or -distribute the Program is void, and will automatically terminate your rights under this -License. However, parties who have received copies, or rights, from you under this -License will not have their licenses terminated so long as such parties remain in full -compliance. -5. You are not required to accept this License, since you have not signed it. However, -nothing else grants you permission to modify or distribute the Program or its derivative -works. These actions are prohibited by law if you do not accept this License. Therefore, -by modifying or distributing the Program (or any work based on the Program), you -indicate your acceptance of this License to do so, and all its terms and conditions for -copying, distributing or modifying the Program or works based on it. -6. Each time you redistribute the Program (or any work based on the Program), the -recipient automatically receives a license from the original licensor to copy, distribute or -modify the Program subject to these terms and conditions. You may not impose any -further restrictions on the recipients' exercise of the rights granted herein. You are not -responsible for enforcing compliance by third parties to this License. -7. If, as a consequence of a court judgment or allegation of patent infringement or for any -other reason (not limited to patent issues), conditions are imposed on you (whether by -court order, agreement or otherwise) that contradict the conditions of this License, they -do not excuse you from the conditions of this License. If you cannot distribute so as to -satisfy simultaneously your obligations under this License and any other pertinent -obligations, then as a consequence you may not distribute the Program at all. For example, -if a patent license would not permit royalty-free redistribution of the Program by all those -who receive copies directly or indirectly through you, then the only way you could satisfy -both it and this License would be to refrain entirely from distribution of the Program. -If any portion of this section is held invalid or unenforceable under any particular -circumstance, the balance of the section is intended to apply and the section as a whole is -intended to apply in other circumstances. -It is not the purpose of this section to induce you to infringe any patents or other property -right claims or to contest validity of any such claims; this section has the sole purpose of -protecting the integrity of the free software distribution system, which is implemented by -public license practices. Many people have made generous contributions to the wide range -of software distributed through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing to distribute software -through any other system and a licensee cannot impose that choice. -This section is intended to make thoroughly clear what is believed to be a consequence of -the rest of this License. -8. If the distribution and/or use of the Program is restricted in certain countries either by -patents or by copyrighted interfaces, the original copyright holder who places the -Program under this License may add an explicit geographical distribution limitation -excluding those countries, so that distribution is permitted only in or among countries not -thus excluded. In such case, this License incorporates the limitation as if written in the -body of this License. -9. The Free Software Foundation may publish revised and/or new versions of the General -Public License from time to time. Such new versions will be similar in spirit to the present -version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Program specifies a version -number of this License which applies to it and "any later version", you have the option of -following the terms and conditions either of that version or of any later version published -by the Free Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. -10. If you wish to incorporate parts of the Program into other free programs whose -distribution conditions are different, write to the author to ask for permission. For -software which is copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our decision will be -guided by the two goals of preserving the free status of all derivatives of our free -software and of promoting the sharing and reuse of software generally. -NO WARRANTY -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE -COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM -"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR -IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE -ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO -IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO -MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED -ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, -SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF -THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT -LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR -LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE -PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH -HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. +1.1. Definitions -===================================================================== -4. GPL v3 -===================================================================== + 1.1.1. Licensee "You", or "Your" shall mean the entity or individual that downloads and uses the SOFTWARE. + + 1.1.2. Redistributable Software "Redistributable Software" shall mean the redistributable libraries referenced in Attachment A of this Agreement. + + 1.1.3. Software "SOFTWARE" shall mean the deliverables provided pursuant to this Agreement. SOFTWARE may be provided in either source or binary form, at NVIDIA's discretion. + +1.2. Grant of License + + 1.2.1. Rights and Limitations of Grant Provided that Licensee complies with the terms of this Agreement, NVIDIA hereby grants Licensee the following limited, non-exclusive, non-transferable, non-sublicensable (except as expressly permitted otherwise for Redistributable Software in Section 1.2.1.1 and Section 1.2.1.3 of this Agreement) right to use the SOFTWARE -- and, if the SOFTWARE is provided in source form, to compile the SOFTWARE -- with the following limitations: + + 1.2.1.1. Redistribution Rights Licensee may transfer, redistribute, and sublicense certain files of the Redistributable SOFTWARE, as defined in Attachment A of this Agreement, provided, however, that (a) the Redistributable SOFTWARE shall be distributed solely in binary form to Licensee's licensees ("Customers") only as a component of Licensee's own software products (each, a "Licensee Application"); (b) Licensee shall design the Licensee Application such that the Redistributable SOFTWARE files are installed only in a private (non-shared) directory location that is used only by the Licensee Application; (c) Licensee shall obtain each Customer's written or clickwrap agreement to the license terms under a written, legally enforceable agreement that has the effect of protecting the SOFTWARE and the rights of NVIDIA under terms no less restrictive than this Agreement. + + 1.2.1.2. Usage Rights Licensee may install and use multiple copies of the SOFTWARE on a shared computer or concurrently on different computers, and make multiple back-up copies of the SOFTWARE, solely for Licensee's use within Licensee's Enterprise. "Enterprise" shall mean individual use by Licensee or any legal entity (such as a corporation or university) and the subsidiaries it owns by more than 50 percent. + + 1.2.1.3. Further Redistribution Rights Subject to the terms and conditions of the Agreement, Licensee may authorize Customers to further redistribute the Redistributable SOFTWARE that such Customers receive as part of the Licensee Application, solely in binary form, provided, however, that Licensee shall require in their standard software license agreements with Customers that all such redistributions must be made pursuant to a license agreement that has the effect of protecting the SOFTWARE and the rights of NVIDIA whose terms and conditions are at least as restrictive as those in the applicable Licensee software license agreement covering the Licensee Application. For avoidance of doubt, termination of this Agreement shall not affect rights previously granted by Licensee to its Customers under this Agreement to the extent validly granted to Customers under Section 1.2.1.1. + + 1.2.1.4. Linux/FreeBSD Exception Notwithstanding the foregoing terms of Section 1.2.1.2, Section 1.2.1.1 and Section 1.2.1.3, SOFTWARE designed exclusively for use on the Linux or FreeBSD operating systems, or other operating systems derived from the source code to these operating systems, may be copied and redistributed, provided that the binary files thereof are not modified in any way (except for unzipping of compressed files). + + 1.2.1.5. Additional Licensing Obligations Licensee acknowledges and agrees that its use of certain third party components included with the SOFTWARE may be subject to additional licensing terms and conditions as set forth or referenced in Attachment B of this Agreement. + + 1.2.1.6. Limitations No Reverse Engineering If the SOFTWARE is provided in binary form, Licensee may not reverse engineer, decompile, or disassemble the SOFTWARE, nor attempt in any other manner to obtain the source code. No Separation of Components The SOFTWARE is licensed as a single product. Except as authorized in this Agreement, Software component parts of the Software may not be separated for use on more than one computer, nor otherwise used separately from the other parts. No Rental Licensee may not rent or lease the SOFTWARE to someone else. +No Modifications +If the SOFTWARE is provided in source form, Licensee may not modify or create derivative works of the SOFTWARE. + +1.3. Term and Termination This Agreement will continue in effect for two (2) years ("Initial Term") after Your initial download and use of the SOFTWARE, subject to the exclusive right of NVIDIA to terminate as provided herein. The term of this Agreement will automatically renew for successive one (1) year renewal terms after the Initial Term, unless either party provides to the other party at least three (3) months prior written notice of termination before the end of the applicable renewal term. This Agreement will automatically terminate if Licensee fails to comply with any of the terms and conditions hereof. In such event, Licensee must destroy all copies of the SOFTWARE and all of its component parts. Defensive Suspension If Licensee commences or participates in any legal proceeding against NVIDIA, then NVIDIA may, in its sole discretion, suspend or terminate all license grants and any other rights provided under this Agreement during the pendency of such legal proceedings. + +1.4. Copyright All rights, title, interest and copyrights in and to the SOFTWARE (including but not limited to all images, photographs, animations, video, audio, music, text, and other information incorporated into the SOFTWARE), the accompanying printed materials, and any copies of the SOFTWARE, are owned by NVIDIA, or its suppliers. The SOFTWARE is protected by copyright laws and international treaty provisions. Accordingly, Licensee is required to treat the SOFTWARE like any other copyrighted material, except as otherwise allowed pursuant to this Agreement and that it may make one copy of the SOFTWARE solely for backup or archive purposes. RESTRICTED RIGHTS NOTICE. Software has been developed entirely at private expense and is commercial computer software provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the U.S. Government or a U.S. Government subcontractor is subject to the restrictions set forth in the Agreement under which Software was obtained pursuant to DFARS 227.7202-3(a) or as set forth in subparagraphs (c)(1) and (2) of the Commercial Computer Software - Restricted Rights clause at FAR 52.227-19, as applicable. Contractor/manufacturer is NVIDIA, 2701 San Tomas Expressway, Santa Clara, CA 95050. + +1.5. Applicable Law This Agreement shall be deemed to have been made in, and shall be construed pursuant to, the laws of the State of Delaware. The United Nations Convention on Contracts for the International Sale of Goods is specifically disclaimed. The courts of Santa Clara County, California shall have exclusive jurisdiction and venue over any dispute arising out of or relating to this Agreement. + +1.6. Disclaimer of Warranties and Limitations on Liability + + 1.6.1. No Warranties TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS IS" AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. + + 1.6.2. No Liability for Consequential Damages TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + 1.6.3. No Support . NVIDIA has no obligation to support or to provide any updates of the Software. + +1.7. Miscellaneous + + 1.7.1. Feedback Notwithstanding any Non-Disclosure Agreement executed by and between the parties, the parties agree that in the event Licensee or NVIDIA provides Feedback (as defined below) to the other party on how to design, implement, or improve the SOFTWARE or Licensee's product(s) for use with the SOFTWARE, the following terms and conditions apply the Feedback: + + 1.7.1.1. Exchange of Feedback Both parties agree that neither party has an obligation to give the other +party any suggestions, comments or other feedback, whether verbally or in written or source code form, relating to (i) the SOFTWARE; (ii) Licensee's products; (iii) Licensee's use of the SOFTWARE; or (iv) optimization/interoperability of Licensee's product with the SOFTWARE (collectively defined as "Feedback"). In the event either party provides Feedback to the other party, the party receiving the Feedback may use any Feedback that the other party voluntarily provides to improve the (i) SOFTWARE or other related NVIDIA technologies, respectively for the benefit of NVIDIA; or (ii) Licensee's product or other related Licensee technologies, respectively for the benefit of Licensee. Accordingly, if either party provides Feedback to the other party, both parties agree that the other party and its respective licensees may freely use, reproduce, license, distribute, and otherwise commercialize the Feedback in the (i) SOFTWARE or other related technologies; or (ii) Licensee's products or other related technologies, respectively, without the payment of any royalties or fees. + + 1.7.1.2. Residual Rights Licensee agrees that NVIDIA shall be free to use any general knowledge, skills and experience, (including, but not limited to, ideas, concepts, know-how, or techniques) ("Residuals"), contained in the (i) Feedback provided by Licensee to NVIDIA; (ii) Licensee's products shared or disclosed to NVIDIA in connection with the Feedback; or (c) Licensee's confidential information voluntarily provided to NVIDIA in connection with the Feedback, which are retained in the memories of NVIDIA's employees, agents, or contractors who have had access to such Residuals. Subject to the terms and conditions of this Agreement, NVIDIA's employees, agents, or contractors shall not be prevented from using Residuals as part of such employee's, agent's or contractor's general knowledge, skills, experience, talent, and/or expertise. NVIDIA shall not have any obligation to limit or restrict the assignment of such employees, agents or contractors or to pay royalties for any work resulting from the use of Residuals. + + 1.7.1.3. Disclaimer of Warranty FEEDBACK FROM EITHER PARTY IS PROVIDED FOR THE OTHER PARTY'S USE "AS IS" AND BOTH PARTIES DISCLAIM ALL WARRANTIES, EXPRESS, IMPLIED AND STATUTORY INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. BOTH PARTIES DO NOT REPRESENT OR WARRANT THAT THE FEEDBACK WILL MEET THE OTHER PARTY'S REQUIREMENTS OR THAT THE OPERATION OR IMPLEMENTATION OF THE FEEDBACK WILL BE UNINTERRUPTED OR ERROR-FREE. + 1.7.1.4. No Liability for Consequential Damages TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE FEEDBACK, EVEN IF THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + 1.7.2. Freedom of Action Licensee agrees that this Agreement is nonexclusive and NVIDIA may currently or in the future be developing software, other technology or confidential information internally, or receiving confidential information from other parties that maybe similar to the Feedback and Licensee's confidential information (as provided in Section 1.7.1.2 above), which may be provided to NVIDIA in connection with Feedback by Licensee. Accordingly, Licensee agrees that nothing in this Agreement will be construed as a representation or inference that NVIDIA will not develop, design, manufacture, acquire, market products, or have products developed, designed, manufactured, acquired, or marketed for NVIDIA, that compete with the Licensee's products or confidential information. + + 1.7.3. No Implied Licenses Under no circumstances should anything in this Agreement be construed as NVIDIA granting by implication, estoppel or otherwise, (i) a license to any NVIDIA product or technology other than the SOFTWARE; or (ii) any additional license rights for the SOFTWARE other than the licenses expressly granted in this Agreement. If any provision of this Agreement is inconsistent with, or cannot be fully enforced under, the law, such provision will be construed as limited to the extent necessary to be consistent with and fully enforceable under the law. This Agreement is the final, complete and exclusive agreement between the parties relating to the subject matter hereof, and supersedes all prior or contemporaneous understandings and agreements relating to such subject matter, whether oral or written. This Agreement may only be modified in writing signed by an authorized officer of NVIDIA. Licensee agrees that it will not ship, transfer or export the SOFTWARE into any country, or use the SOFTWARE in any manner, prohibited by the United States Bureau of Industry and Security or any export laws, restrictions or regulations. The parties agree that the following sections of the Agreement will survive the termination of the License: Section 1.2.1.4, Section 1.4, Section 1.5, Section 1.6, and Section 1.7. + +1.8. Attachment A Redistributable Software In connection with Section 1.2.1.1 of this Agreement, the following files may be redistributed with software applications developed by Licensee, including certain variations of these files that have version number or architecture specific information NVIDIA CUDA Toolkit License Agreement www.nvidia.com End User License Agreements (EULA) DR-06739-001_v01_v8.0 | 9 embedded in the file name - as an example only, for release version 6.0 of the 64-bit Windows software, the file cudart64_60.dll is redistributable. +Component : CUDA Runtime Windows : cudart.dll, cudart_static.lib, cudadevrt.lib Mac OSX : libcudart.dylib, libcudart_static.a, libcudadevrt.a Linux : libcudart.so, libcudart_static.a, libcudadevrt.a Android : libcudart.so, libcudart_static.a, libcudadevrt.a Component : CUDA FFT Library Windows : cufft.dll, cufftw.dll Mac OSX : libcufft.dylib, libcufft_static.a, libcufftw.dylib, libcufftw_static.a Linux : libcufft.so, libcufft_static.a, libcufftw.so, libcufftw_static.a Android : libcufft.so, libcufft_static.a, libcufftw.so, libcufftw_static.a Component : CUDA BLAS Library Windows : cublas.dll, cublas_device.lib Mac OSX : libcublas.dylib, libcublas_static.a, libcublas_device.a Linux : libcublas.so, libcublas_static.a, libcublas_device.a Android : libcublas.so, libcublas_static.a, libcublas_device.a Component : NVIDIA "Drop-in" BLAS Library Windows : nvblas.dll Mac OSX : libnvblas.dylib Linux : libnvblas.so Component : CUDA Sparse Matrix Library Windows : cusparse.dll Mac OSX : libcusparse.dylib, libcusparse_static.a Linux : libcusparse.so, libcusparse_static.a Android : libcusparse.so, libcusparse_static.a Component : CUDA Linear Solver Library Windows : cusolver.dll Mac OSX : libcusolver.dylib, libcusolver_static.a Linux : libcusolver.so, libcusolver_static.a Android : libcusolver.so, libcusolver_static.a Component : CUDA Random Number Generation Library Windows : curand.dll Mac OSX : libcurand.dylib, libcurand_static.a Linux : libcurand.so, libcurand_static.a Android : libcurand.so, libcurand_static.a Component : NVIDIA Performance Primitives Library Windows : nppc.dll, nppi.dll, npps.dll Mac OSX : libnppc.dylib, libnppi.dylib, libnpps.dylib, libnppc_static.a, libnpps_static.a, libnppi_static.a Linux : libnppc.so, libnppi.so, libnpps.so, libnppc_static.a, libnpps_static.a, libnppi_static.a Android : libnppc.so, libnppi.so, libnpps.so, libnppc_static.a, libnpps_static.a, libnppi_static.a Component : Internal common library required for statically linking to cuBLAS, cuSPARSE, cuFFT, cuRAND and NPP Mac OSX : libculibos.a Linux : libculibos.a Component : NVIDIA Runtime Compilation Library Windows : nvrtc.dll, nvrtc-builtins.dll Mac OSX : libnvrtc.dylib, libnvrtc-builtins.dylib Linux : libnvrtc.so, libnvrtc-builtins.so Component : NVIDIA Optimizing Compiler Library Windows : nvvm.dll Mac OSX : libnvvm.dylib Linux : libnvvm.so Component : NVIDIA Common Device Math Functions Library Windows : libdevice.compute_20.bc, libdevice.compute_30.bc, libdevice.compute_35.bc Mac OSX : libdevice.compute_20.bc, libdevice.compute_30.bc, libdevice.compute_35.bc Linux : libdevice.compute_20.bc, libdevice.compute_30.bc, libdevice.compute_35.bc Component : CUDA Occupancy Calculation Header Library All : cuda_occupancy.h Component : Profiling Tools Interface Library Windows : cupti.dll Mac OSX : libcupti.dylib Linux : libcupti.so + +1.9. Attachment B Additional Licensing Obligations The following third party components included in the SOFTWARE are licensed to Licensee pursuant to the following terms and conditions: + + +1. Licensee's use of the GDB third party component is subject to the terms and conditions of GNU GPL v3: This product includes copyrighted third-party software licensed under the terms of the GNU General Public License v3 ("GPL v3"). All third-party software packages are copyright by their respective authors. GPL v3 terms and conditions are hereby incorporated into the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt Consistent with these licensing requirements, the software listed below is provided under the terms of the specified open source software licenses. To obtain source code for software provided under licenses that require redistribution of source code, including the GNU General Public License (GPL) and GNU Lesser General Public License (LGPL), contact oss-requests@nvidia.com. This offer is valid for a period of three (3) years from the date of the distribution of this product by NVIDIA CORPORATION. Component License CUDA-GDB GPL v3 + +2. Licensee represents and warrants that any and all third party licensing and/or royalty payment obligations in connection with Licensee's use of the H.264 video codecs are solely the responsibility of Licensee. + +3. Licensee's use of the Thrust library is subject to the terms and conditions of the Apache License Version 2.0. All third-party software packages are copyright by their respective authors. Apache License Version 2.0 terms and conditions are hereby incorporated into the Agreement by this reference. http://www.apache.org/licenses/ LICENSE-2.0.html. In addition, Licensee acknowledges the following notice: Thrust includes source code from the Boost Iterator, Tuple, System, and Random Number libraries. Boost Software License - Version 1.0 - August 17th, 2003 . . . . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +4. Licensee's use of the LLVM third party component is subject to the following terms and conditions: + + LLVM Release License + + University of Illinois/NCSA Open Source License Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana- Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + +5. Licensee's use of the PCRE third party component is subject to the following terms and conditions: + + PCRE LICENCE + + PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Release 8 of PCRE is distributed under the terms of the "BSD" licence, as specified below. The documentation for PCRE, supplied in the "doc" directory, is distributed under the same terms as the software itself. The basic library functions are written in C and are freestanding. Also included in the distribution is a set of C++ wrapper functions, and a justin-time compiler that can be used to optimize pattern matching. These are both optional features that can be omitted when the library is built. + + + THE BASIC LIBRARY FUNCTIONS + + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + + Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu Copyright(c) 2009-2012 Zoltan Herczeg All rights reserved. + + THE C++ WRAPPER FUNCTIONS + + Contributed by: Google Inc. Copyright (c) 2007-2012, Google Inc. All rights reserved. + + THE "BSD" LICENCE + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the name of Google Inc. nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +6. Some of the cuBLAS library routines were written by or derived from code written by Vasily Volkov and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2007-2009, Regents of the University of California All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of California, Berkeley nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +7. Some of the cuBLAS library routines were written by or derived from code written by Davide Barbieri and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +8. Some of the cuBLAS library routines were derived from code developed by the University of Tennessee and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2010 The University of Tennessee. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer listed in this license in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +9. Some of the cuBLAS library routines were written by or derived from code written by Jonathan Hogg and are subject to the Modified Berkeley Software Distribution License as follows: Copyright (c) 2012, The Science and Technology Facilities Council (STFC). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the STFC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE STFC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +10. Some of the cuBLAS library routines were written by or derived from code written by Ahmad M. Abdelfattah, David Keyes, and Hatem Ltaief, and are subject to the Apache License, Version 2.0, as follows: -- (C) Copyright 2013 King Abdullah University of Science and Technology Authors: Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) David Keyes (david.keyes@kaust.edu.sa) Hatem Ltaief (hatem.ltaief@kaust.edu.sa) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the King Abdullah University of Science and Technology nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +11. Some of the cuSPARSE library routines were written by or derived from code written by Li-Wen Chang and are subject to the NCSA Open Source License as follows: Copyright (c) 2012, University of Illinois. All rights reserved. Developed by: IMPACT Group, University of Illinois, http:// impact.crhc.illinois.edu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of IMPACT Group, University of Illinois, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + +12. Some of the cuRAND library routines were written by or derived from code written by Mutsuo Saito and Makoto Matsumoto and are subject to the following license: Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima University. All rights reserved. Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima University and University of Tokyo. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Hiroshima University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +13. Some of the cuRAND library routines were derived from code developed by D. E. Shaw Research and are subject to the following license: Copyright 2010-2011, D. E. Shaw Research. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of D. E. Shaw Research nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +14. Licensee's use of the lz4 third party component is subject to the following terms and conditions: Copyright (C) 2011-2013, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +15. The NPP library uses code from the Boost Math Toolkit, and is subject to the following license: Boost Software License - Version 1.0 - August 17th, 2003 . . . . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Ansible +------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + Copyright (c) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + Preamble + The GNU General Public License is a free, copyleft license for software and other kinds of works. + The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to @@ -420,6 +653,7 @@ Public Licenses are designed to make sure that you have the freedom to distribut of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of @@ -428,13 +662,16 @@ For example, if you distribute copies of such a program, whether gratis or for a must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. -Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright -on the software, and (2) offer you this License giving you legal permission to copy, -distribute and/or modify it. + +Developers that use the GNU GPL protect your rights with two steps: + + (1) assert copyright on the software, and + (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The @@ -443,13 +680,16 @@ which is precisely where it is most unacceptable. Therefore, we have designed th of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. + TERMS AND CONDITIONS + 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as @@ -476,6 +716,7 @@ copyright notice, and (2) tells the user that there is no warranty for the work the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. @@ -504,6 +745,7 @@ parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. + 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License @@ -521,6 +763,7 @@ exclusively on your behalf, under your direction and control, on terms that proh from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty @@ -531,6 +774,7 @@ technological measures to the extent such circumvention is effected by exercisin under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an @@ -540,22 +784,23 @@ of the absence of any warranty; and give all recipients a copy of this License a the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: -* a) The work must carry prominent notices stating that you modified it, and giving + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. -* b) The work must carry prominent notices stating that it is released under this + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". -* c) You must license the entire work, as a whole, under this License to anyone who + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. -* d) If the work has interactive user interfaces, each must display Appropriate Legal + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are @@ -565,14 +810,15 @@ called an "aggregate" if the compilation and its resulting copyright are not use the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: -* a) Convey the object code in, or embodied in, a physical product (including a + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. -* b) Convey the object code in, or embodied in, a physical product (including a + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a @@ -581,11 +827,11 @@ covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. -* c) Convey individual copies of the object code with a copy of the written offer to + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. -* d) Convey the object code by offering access from a designated place (gratis or + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy @@ -595,7 +841,7 @@ copying facilities, provided you maintain clear directions next to the object co saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. -* e) Convey the object code using peer-to-peer transmission, provided you inform + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the @@ -635,6 +881,7 @@ Corresponding Source conveyed, and Installation Information provided, in accord this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable @@ -651,19 +898,19 @@ which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: -* a) Disclaiming warranty or limiting liability differently from the terms of sections + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or -* b) Requiring preservation of specified reasonable legal notices or author + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or -* c) Prohibiting misrepresentation of the origin of that material, or requiring that + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or -* d) Limiting the use for publicity purposes of names of licensors or authors of the + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or -* e) Declining to grant rights under trademark law for use of some trade names, + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or -* f) Requiring indemnification of licensors and authors of that material by anyone + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. @@ -679,6 +926,7 @@ relevant source files, a statement of the additional terms that apply to those f notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will @@ -698,6 +946,7 @@ Termination of your rights under this section does not terminate the licenses of who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of @@ -706,6 +955,7 @@ However, nothing other than this License grants you permission to propagate or m any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. @@ -723,6 +973,7 @@ other charge for exercise of rights granted under this License, and you may not litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the @@ -772,6 +1023,7 @@ that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of @@ -781,6 +1033,7 @@ you may not convey it at all. For example, if you agree to terms that obligate y collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero @@ -788,6 +1041,7 @@ General Public License into a single combined work, and to convey the resulting The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the @@ -804,6 +1058,7 @@ version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN @@ -815,6 +1070,7 @@ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO @@ -827,6 +1083,7 @@ SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most @@ -834,3 +1091,220 @@ closely approximates an absolute waiver of all civil liability in connection wit Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. +ion_timeout.sh +------------------------------------------------------------- + +GNU Lesser General Public License (LGPL) + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +------------------------------------------------------------- + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + + Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + + If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +2. GNU General Public License version 2 (GPL v2) +===================================================================== + +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +------------------------------------------------------------- + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/dbReports/.pylintrc b/dbReports/.pylintrc index 7a7ee07b..c7cd3b89 100644 --- a/dbReports/.pylintrc +++ b/dbReports/.pylintrc @@ -29,7 +29,7 @@ cache-size=500 [MESSAGES CONTROL] -# C0111 Missing docstring +# C0111 Missing docstring # I0011 Warning locally suppressed using disable-msg # I0012 Warning locally suppressed using disable-msg # W0704 Except doesn't do anything Used when an except clause does nothing but "pass" and there is no "else" clause @@ -47,7 +47,7 @@ disable=C0111,I0011,I0012,W0704,W0142,W0212,W0232,W0613,W0702,R0201,C0103,C0301, # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html -output-format=parseable +output-format=colorized # Include message's id in output include-ids=yes diff --git a/dbReports/CMakeLists.txt b/dbReports/CMakeLists.txt index 5db8c404..b9418a7e 100644 --- a/dbReports/CMakeLists.txt +++ b/dbReports/CMakeLists.txt @@ -91,24 +91,10 @@ install(DIRECTORY DESTINATION /var/log/ion DIRECTORY_PERMISSIONS OWNER_READ OWNE install(PROGRAMS iondb/bin/ionCrawler DESTINATION /etc/init.d) install(PROGRAMS iondb/bin/ionJobServer DESTINATION /etc/init.d) install(PROGRAMS iondb/bin/ionPlugin DESTINATION /etc/init.d) - -# celeryd and celerybeat init and config files: subject to relocation in future - -install(PROGRAMS celery/init.d/celeryd DESTINATION /etc/init.d) -install(PROGRAMS celery/init.d/celerybeat DESTINATION /etc/init.d) -configure_file ( - "${PROJECT_SOURCE_DIR}/celery/config/celeryd.in" - "${PROJECT_BINARY_DIR}/celery/config/celeryd" @ONLY -) -install(FILES "${PROJECT_BINARY_DIR}/celery/config/celeryd" DESTINATION /etc/default) -configure_file ( - "${PROJECT_SOURCE_DIR}/celery/config/celerybeat.in" - "${PROJECT_BINARY_DIR}/celery/config/celerybeat" @ONLY -) -install(FILES "${PROJECT_BINARY_DIR}/celery/config/celerybeat" DESTINATION /etc/default) - +install(PROGRAMS celery/init/celeryd.conf DESTINATION /etc/init) +install(PROGRAMS celery/init/celerybeat.conf DESTINATION /etc/init) +install(PROGRAMS celery/init/DjangoFTP.conf DESTINATION /etc/init) install(PROGRAMS manage.py DESTINATION ${ION_INSTALL_PREFIX}) -#install(FILES celeryconfig.py DESTINATION ${ION_INSTALL_PREFIX}) configure_file ( "${PROJECT_SOURCE_DIR}/apacheconfig/torrent-server-config.conf.in" @@ -159,6 +145,8 @@ set(CPACK_GENERATOR "DEB") include(../buildTools/cmake/CMakeLists.cpack.txt) +set(CPACK_DEBIAN_PACKAGE_PREDEPENDS "debconf") + #% nice to automate this set(CPACK_DEBIAN_PACKAGE_DEPENDS "python, avahi-daemon, @@ -169,10 +157,12 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "python, pdftk, postgresql, php5, + apache2, libapache2-mod-wsgi (>= 3.3), libapache2-mod-gnutls, apache2-utils, ssl-cert, + dmidecode, python-avahi, python-celery (>= 3.1.6), python-django (>= 1.6.1), @@ -196,13 +186,18 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "python, python-feedparser, python-boto (>=2.20.1), python-oauthlib, + python-gobject, + python-gnupg, + python-pyftpdlib, memcached, python-memcache, python-dev, rabbitmq-server (>= 3.2.0), ion-analysis (>=3.5.15), megacli, - python-apt") + python-apt, + ion-tsconfig, + coreutils") set(CPACK_PACKAGE_DESCRIPTION "This package contains the Torrent Browser and related programs including the job server and run crawler.") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA @@ -211,5 +206,6 @@ set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA ${CMAKE_CURRENT_BINARY_DIR}/debian/prerm ${CMAKE_CURRENT_BINARY_DIR}/debian/postrm ${CMAKE_CURRENT_BINARY_DIR}/debian/triggers + ${PROJECT_SOURCE_DIR}/debian/templates ) include(CPack) diff --git a/dbReports/apacheconfig/torrent-server-config.conf.in b/dbReports/apacheconfig/torrent-server-config.conf.in index 71d2a511..15b8bf94 100644 --- a/dbReports/apacheconfig/torrent-server-config.conf.in +++ b/dbReports/apacheconfig/torrent-server-config.conf.in @@ -20,6 +20,7 @@ WSGIDaemonProcess iontorrent threads=6 processes=16 maximum-requests=500 shutdow WSGIScriptAlias /data @ION_INSTALL_PREFIX@/iondb/wsgi.py WSGIScriptAlias /configure @ION_INSTALL_PREFIX@/iondb/wsgi.py WSGIScriptAlias /rundb @ION_INSTALL_PREFIX@/iondb/wsgi.py +WSGIScriptAlias /security @ION_INSTALL_PREFIX@/iondb/wsgi.py WSGIScriptAlias /plan @ION_INSTALL_PREFIX@/iondb/wsgi.py WSGIScriptAlias /sample @ION_INSTALL_PREFIX@/iondb/wsgi.py WSGIScriptAlias /monitor @ION_INSTALL_PREFIX@/iondb/wsgi.py @@ -61,16 +62,6 @@ WSGIScriptAlias /admin @ION_INSTALL_PREFIX@/iondb/wsgi.py Allow from 127.0.0.0/8 Allow from ::1 Satisfy Any - - # CORS for cross domain json requests - # - makes API accessible from other sites - - - SetEnvIf Origin ":" IS_CORS - Header set Access-Control-Allow-Origin "*" env=IS_CORS - Header set Access-Control-Allow-Credentials "true" env=IS_CORS - - diff --git a/dbReports/celery/config/celerybeat.in b/dbReports/celery/config/celerybeat.in deleted file mode 100644 index 90d691ce..00000000 --- a/dbReports/celery/config/celerybeat.in +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2014 Ion Torrent Systems, Inc. All Rights Reserved - -CELERY_BIN="celery" -CELERY_APP="iondb" - -CELERYBEAT_CHDIR="@ION_INSTALL_PREFIX@" - -# Extra arguments to celerybeat -CELERYBEAT_OPTS="--schedule=/var/run/celery/celerybeat-schedule" - -export DJANGO_SETTINGS_MODULE="iondb.settings" - -CELERYBEAT_LOG_LEVEL=INFO -CELERYBEAT_LOG_FILE="/var/log/ion/celerybeat.log" -CELERYBEAT_PID_FILE="/var/run/celery/celerybeat.pid" - -CELERY_CREATE_DIRS=1 - diff --git a/dbReports/celery/config/celeryd.in b/dbReports/celery/config/celeryd.in deleted file mode 100644 index 87f86698..00000000 --- a/dbReports/celery/config/celeryd.in +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (C) 2014 Ion Torrent Systems, Inc. All Rights Reserved - -# Name of the projects settings module. (set in celery.py) -export DJANGO_SETTINGS_MODULE="iondb.settings" -export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" - -# Name of nodes to start, here we have a single node -# Edits to NODES need to be in iondb/rundb/configure/views.py - services tab reporting -CELERYD_NODES="w1 plugins periodic slowlane transfer diskutil dmprocess dmmanage" - -# Where to chdir at start. -CELERYD_CHDIR="@ION_INSTALL_PREFIX@" - -CELERY_APP="iondb" - -# Default log level -CELERYD_LOG_LEVEL=INFO - -# The following abbreviations will be expanded in file names below -# * %n -> node name -# * %h -> host name -# Extra arguments to celeryd -CELERYD_OPTS="-Ofair --event --time-limit=21600" -CELERYD_OPTS="$CELERYD_OPTS --queue:w1=w1 --concurrency:w1=4" -CELERYD_OPTS="$CELERYD_OPTS --queue:plugins=plugins --concurrency:plugins=2" -CELERYD_OPTS="$CELERYD_OPTS --queue:periodic=periodic --concurrency:periodic=6" -CELERYD_OPTS="$CELERYD_OPTS --queue:slowlane=slowlane --concurrency:slowlane=1" -CELERYD_OPTS="$CELERYD_OPTS --queue:transfer=transfer --concurrency:transfer=1" -CELERYD_OPTS="$CELERYD_OPTS --queue:diskutil=diskutil --concurrency:diskutil=2" -CELERYD_OPTS="$CELERYD_OPTS --queue:dmprocess=dmprocess --concurrency:dmprocess=8" -CELERYD_OPTS="$CELERYD_OPTS --queue:dmmanage=dmmanage --concurrency:dmmanage=8" - -# %N will be replaced with the first part of the nodename. -CELERYD_LOG_FILE="/var/log/ion/celery_%n.log" -CELERYD_PID_FILE="/var/run/celery/celery_%n.pid" - -# Workers should run as an unprivileged user. -# You need to create this user manually (or you can choose -# a user/group combination that already exists, e.g. nobody). -CELERYD_USER="root" -CELERYD_GROUP="root" -export C_FORCE_ROOT="true" - -# If enabled pid and log directories will be created if missing, -# and owned by the userid/group configured. -CELERY_CREATE_DIRS=1 - - diff --git a/dbReports/celery/init.d/celerybeat b/dbReports/celery/init.d/celerybeat deleted file mode 100644 index fa4cd68b..00000000 --- a/dbReports/celery/init.d/celerybeat +++ /dev/null @@ -1,318 +0,0 @@ -#!/bin/bash -# ========================================================= -# celerybeat - Starts the Celery periodic task scheduler. -# ========================================================= -# -# :Usage: /etc/init.d/celerybeat {start|stop|force-reload|restart|try-restart|status} -# :Configuration file: /etc/default/celerybeat or /etc/default/celeryd -# -# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts - -### BEGIN INIT INFO -# Provides: celerybeat -# Required-Start: $network $local_fs $remote_fs -# Required-Stop: $network $local_fs $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: celery periodic task scheduler -### END INIT INFO - -# Cannot use set -e/bash -e since the kill -0 command will abort -# abnormally in the absence of a valid process ID. -#set -e -VERSION=10.1 -echo "celery init v${VERSION}." - -if [ $(id -u) -ne 0 ]; then - echo "Error: This program can only be used by the root user." - echo " Unpriviliged users must use 'celery beat --detach'" - exit 1 -fi - - -# May be a runlevel symlink (e.g. S02celeryd) -if [ -L "$0" ]; then - SCRIPT_FILE=$(readlink "$0") -else - SCRIPT_FILE="$0" -fi -SCRIPT_NAME="$(basename "$SCRIPT_FILE")" - -# /etc/init.d/celerybeat: start and stop the celery periodic task scheduler daemon. - -# Make sure executable configuration script is owned by root -_config_sanity() { - local path="$1" - local owner=$(ls -ld "$path" | awk '{print $3}') - local iwgrp=$(ls -ld "$path" | cut -b 6) - local iwoth=$(ls -ld "$path" | cut -b 9) - - if [ "$(id -u $owner)" != "0" ]; then - echo "Error: Config script '$path' must be owned by root!" - echo - echo "Resolution:" - echo "Review the file carefully and make sure it has not been " - echo "modified with mailicious intent. When sure the " - echo "script is safe to execute with superuser privileges " - echo "you can change ownership of the script:" - echo " $ sudo chown root '$path'" - exit 1 - fi - - if [ "$iwoth" != "-" ]; then # S_IWOTH - echo "Error: Config script '$path' cannot be writable by others!" - echo - echo "Resolution:" - echo "Review the file carefully and make sure it has not been " - echo "modified with malicious intent. When sure the " - echo "script is safe to execute with superuser privileges " - echo "you can change the scripts permissions:" - echo " $ sudo chmod 640 '$path'" - exit 1 - fi - if [ "$iwgrp" != "-" ]; then # S_IWGRP - echo "Error: Config script '$path' cannot be writable by group!" - echo - echo "Resolution:" - echo "Review the file carefully and make sure it has not been " - echo "modified with malicious intent. When sure the " - echo "script is safe to execute with superuser privileges " - echo "you can change the scripts permissions:" - echo " $ sudo chmod 640 '$path'" - exit 1 - fi -} - -scripts="" - -if test -f /etc/default/celeryd; then - scripts="/etc/default/celeryd" - _config_sanity /etc/default/celeryd - . /etc/default/celeryd -fi - -EXTRA_CONFIG="/etc/default/${SCRIPT_NAME}" -if test -f "$EXTRA_CONFIG"; then - scripts="$scripts, $EXTRA_CONFIG" - _config_sanity "$EXTRA_CONFIG" - . "$EXTRA_CONFIG" -fi - -echo "Using configuration: $scripts" - -CELERY_BIN=${CELERY_BIN:-"celery"} -DEFAULT_USER="celery" -DEFAULT_PID_FILE="/var/run/celery/beat.pid" -DEFAULT_LOG_FILE="/var/log/celery/beat.log" -DEFAULT_LOG_LEVEL="INFO" -DEFAULT_CELERYBEAT="$CELERY_BIN beat" - -CELERYBEAT=${CELERYBEAT:-$DEFAULT_CELERYBEAT} -CELERYBEAT_LOG_LEVEL=${CELERYBEAT_LOG_LEVEL:-${CELERYBEAT_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} - -# Sets --app argument for CELERY_BIN -CELERY_APP_ARG="" -if [ ! -z "$CELERY_APP" ]; then - CELERY_APP_ARG="--app=$CELERY_APP" -fi - -CELERYBEAT_USER=${CELERYBEAT_USER:-${CELERYD_USER:-$DEFAULT_USER}} - -# Set CELERY_CREATE_DIRS to always create log/pid dirs. -CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} -CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS -CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS -if [ -z "$CELERYBEAT_PID_FILE" ]; then - CELERYBEAT_PID_FILE="$DEFAULT_PID_FILE" - CELERY_CREATE_RUNDIR=1 -fi -if [ -z "$CELERYBEAT_LOG_FILE" ]; then - CELERYBEAT_LOG_FILE="$DEFAULT_LOG_FILE" - CELERY_CREATE_LOGDIR=1 -fi - -export CELERY_LOADER=default - -CELERYBEAT_OPTS="$CELERYBEAT_OPTS -f $CELERYBEAT_LOG_FILE -l $CELERYBEAT_LOG_LEVEL" - -if [ -n "$2" ]; then - CELERYBEAT_OPTS="$CELERYBEAT_OPTS $2" -fi - -CELERYBEAT_LOG_DIR=`dirname $CELERYBEAT_LOG_FILE` -CELERYBEAT_PID_DIR=`dirname $CELERYBEAT_PID_FILE` - -# Extra start-stop-daemon options, like user/group. - -CELERYBEAT_CHDIR=${CELERYBEAT_CHDIR:-$CELERYD_CHDIR} -if [ -n "$CELERYBEAT_CHDIR" ]; then - DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYBEAT_CHDIR" -fi - - -export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" - -check_dev_null() { - if [ ! -c /dev/null ]; then - echo "/dev/null is not a character device!" - exit 75 # EX_TEMPFAIL - fi -} - -maybe_die() { - if [ $? -ne 0 ]; then - echo "Exiting: $*" - exit 77 # EX_NOPERM - fi -} - -create_default_dir() { - if [ ! -d "$1" ]; then - echo "- Creating default directory: '$1'" - mkdir -p "$1" - maybe_die "Couldn't create directory $1" - echo "- Changing permissions of '$1' to 02755" - chmod 02755 "$1" - maybe_die "Couldn't change permissions for $1" - if [ -n "$CELERYBEAT_USER" ]; then - echo "- Changing owner of '$1' to '$CELERYBEAT_USER'" - chown "$CELERYBEAT_USER" "$1" - maybe_die "Couldn't change owner of $1" - fi - if [ -n "$CELERYBEAT_GROUP" ]; then - echo "- Changing group of '$1' to '$CELERYBEAT_GROUP'" - chgrp "$CELERYBEAT_GROUP" "$1" - maybe_die "Couldn't change group of $1" - fi - fi -} - -check_paths() { - if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then - create_default_dir "$CELERYBEAT_LOG_DIR" - fi - if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then - create_default_dir "$CELERYBEAT_PID_DIR" - fi -} - - -create_paths () { - create_default_dir "$CELERYBEAT_LOG_DIR" - create_default_dir "$CELERYBEAT_PID_DIR" -} - - -wait_pid () { - pid=$1 - forever=1 - i=0 - while [ $forever -gt 0 ]; do - kill -0 $pid 1>/dev/null 2>&1 - if [ $? -eq 1 ]; then - echo "OK" - forever=0 - else - kill -TERM "$pid" - i=$((i + 1)) - if [ $i -gt 60 ]; then - echo "ERROR" - echo "Timed out while stopping (30s)" - forever=0 - else - sleep 0.5 - fi - fi - done -} - - -stop_beat () { - echo -n "Stopping ${SCRIPT_NAME}... " - if [ -f "$CELERYBEAT_PID_FILE" ]; then - wait_pid $(cat "$CELERYBEAT_PID_FILE") - else - echo "NOT RUNNING" - fi -} - -_chuid () { - su "$CELERYBEAT_USER" -c "$CELERYBEAT $*" -} - -start_beat () { - echo "Starting ${SCRIPT_NAME}..." - _chuid $CELERY_APP_ARG $CELERYBEAT_OPTS $DAEMON_OPTS --detach \ - --pidfile="$CELERYBEAT_PID_FILE" -} - - -check_status () { - local failed= - local pid_file=$CELERYBEAT_PID_FILE - if [ ! -e $pid_file ]; then - echo "${SCRIPT_NAME} is up: no pid file found" - failed=true - elif [ ! -r $pid_file ]; then - echo "${SCRIPT_NAME} is in unknown state, user cannot read pid file." - failed=true - else - local pid=`cat "$pid_file"` - local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` - if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then - echo "${SCRIPT_NAME}: bad pid file ($pid_file)" - failed=true - else - local failed= - kill -0 $pid 2> /dev/null || failed=true - if [ "$failed" ]; then - echo "${SCRIPT_NAME} (pid $pid) is down, but pid file exists!" - failed=true - else - echo "${SCRIPT_NAME} (pid $pid) is up..." - fi - fi - fi - - [ "$failed" ] && exit 1 || exit 0 -} - - -case "$1" in - start) - check_dev_null - check_paths - start_beat - ;; - stop) - check_paths - stop_beat - ;; - reload|force-reload) - echo "Use start+stop" - ;; - status) - check_status - ;; - restart) - echo "Restarting celery periodic task scheduler" - check_paths - stop_beat - check_dev_null - start_beat - ;; - create-paths) - check_dev_null - create_paths - ;; - check-paths) - check_dev_null - check_paths - ;; - *) - echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|create-paths|status}" - exit 64 # EX_USAGE - ;; -esac - -exit 0 diff --git a/dbReports/celery/init.d/celeryd b/dbReports/celery/init.d/celeryd deleted file mode 100644 index 2a6bcf50..00000000 --- a/dbReports/celery/init.d/celeryd +++ /dev/null @@ -1,397 +0,0 @@ -#!/bin/sh -e -# ============================================ -# celeryd - Starts the Celery worker daemon. -# ============================================ -# -# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} -# :Configuration file: /etc/default/celeryd -# -# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts - - -### BEGIN INIT INFO -# Provides: celeryd -# Required-Start: $network $local_fs $remote_fs -# Required-Stop: $network $local_fs $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: celery task worker daemon -### END INIT INFO -# -# -# To implement separate init scripts, copy this script and give it a different -# name: -# I.e., if my new application, "little-worker" needs an init, I -# should just use: -# -# cp /etc/init.d/celeryd /etc/init.d/little-worker -# -# You can then configure this by manipulating /etc/default/little-worker. -# -VERSION=10.1 -echo "celery init v${VERSION}." -if [ $(id -u) -ne 0 ]; then - echo "Error: This program can only be used by the root user." - echo " Unprivileged users must use the 'celery multi' utility, " - echo " or 'celery worker --detach'." - exit 1 -fi - - -# Can be a runlevel symlink (e.g. S02celeryd) -if [ -L "$0" ]; then - SCRIPT_FILE=$(readlink "$0") -else - SCRIPT_FILE="$0" -fi -SCRIPT_NAME="$(basename "$SCRIPT_FILE")" - -DEFAULT_USER="celery" -DEFAULT_PID_FILE="/var/run/celery/%n.pid" -DEFAULT_LOG_FILE="/var/log/celery/%n.log" -DEFAULT_LOG_LEVEL="INFO" -DEFAULT_NODES="celery" -DEFAULT_CELERYD="-m celery worker --detach" - -CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/${SCRIPT_NAME}"} - -# Make sure executable configuration script is owned by root -_config_sanity() { - local path="$1" - local owner=$(ls -ld "$path" | awk '{print $3}') - local iwgrp=$(ls -ld "$path" | cut -b 6) - local iwoth=$(ls -ld "$path" | cut -b 9) - - if [ "$(id -u $owner)" != "0" ]; then - echo "Error: Config script '$path' must be owned by root!" - echo - echo "Resolution:" - echo "Review the file carefully and make sure it has not been " - echo "modified with mailicious intent. When sure the " - echo "script is safe to execute with superuser privileges " - echo "you can change ownership of the script:" - echo " $ sudo chown root '$path'" - exit 1 - fi - - if [ "$iwoth" != "-" ]; then # S_IWOTH - echo "Error: Config script '$path' cannot be writable by others!" - echo - echo "Resolution:" - echo "Review the file carefully and make sure it has not been " - echo "modified with malicious intent. When sure the " - echo "script is safe to execute with superuser privileges " - echo "you can change the scripts permissions:" - echo " $ sudo chmod 640 '$path'" - exit 1 - fi - if [ "$iwgrp" != "-" ]; then # S_IWGRP - echo "Error: Config script '$path' cannot be writable by group!" - echo - echo "Resolution:" - echo "Review the file carefully and make sure it has not been " - echo "modified with malicious intent. When sure the " - echo "script is safe to execute with superuser privileges " - echo "you can change the scripts permissions:" - echo " $ sudo chmod 640 '$path'" - exit 1 - fi -} - -if [ -f "$CELERY_DEFAULTS" ]; then - _config_sanity "$CELERY_DEFAULTS" - echo "Using config script: $CELERY_DEFAULTS" - . "$CELERY_DEFAULTS" -fi - -# Sets --app argument for CELERY_BIN -CELERY_APP_ARG="" -if [ ! -z "$CELERY_APP" ]; then - CELERY_APP_ARG="--app=$CELERY_APP" -fi - -CELERYD_USER=${CELERYD_USER:-$DEFAULT_USER} - -# Set CELERY_CREATE_DIRS to always create log/pid dirs. -CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} -CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS -CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS -if [ -z "$CELERYD_PID_FILE" ]; then - CELERYD_PID_FILE="$DEFAULT_PID_FILE" - CELERY_CREATE_RUNDIR=1 -fi -if [ -z "$CELERYD_LOG_FILE" ]; then - CELERYD_LOG_FILE="$DEFAULT_LOG_FILE" - CELERY_CREATE_LOGDIR=1 -fi - -CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} -CELERY_BIN=${CELERY_BIN:-"celery"} -CELERYD_MULTI=${CELERYD_MULTI:-"$CELERY_BIN multi"} -CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES} - -export CELERY_LOADER=default - -if [ -n "$2" ]; then - CELERYD_OPTS="$CELERYD_OPTS $2" -fi - -CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE` -CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE` - -# Extra start-stop-daemon options, like user/group. -if [ -n "$CELERYD_CHDIR" ]; then - DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR" -fi - - -check_dev_null() { - if [ ! -c /dev/null ]; then - echo "/dev/null is not a character device!" - exit 75 # EX_TEMPFAIL - fi -} - - -maybe_die() { - if [ $? -ne 0 ]; then - echo "Exiting: $* (errno $?)" - exit 77 # EX_NOPERM - fi -} - -create_default_dir() { - if [ ! -d "$1" ]; then - echo "- Creating default directory: '$1'" - mkdir -p "$1" - maybe_die "Couldn't create directory $1" - echo "- Changing permissions of '$1' to 02755" - chmod 02755 "$1" - maybe_die "Couldn't change permissions for $1" - if [ -n "$CELERYD_USER" ]; then - echo "- Changing owner of '$1' to '$CELERYD_USER'" - chown "$CELERYD_USER" "$1" - maybe_die "Couldn't change owner of $1" - fi - if [ -n "$CELERYD_GROUP" ]; then - echo "- Changing group of '$1' to '$CELERYD_GROUP'" - chgrp "$CELERYD_GROUP" "$1" - maybe_die "Couldn't change group of $1" - fi - fi -} - - -check_paths() { - if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then - create_default_dir "$CELERYD_LOG_DIR" - fi - if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then - create_default_dir "$CELERYD_PID_DIR" - fi - # Touch a tracking file in the /results directory. - touch /results/.headnode_$(cat /etc/hostname) || true -} - -create_paths() { - create_default_dir "$CELERYD_LOG_DIR" - create_default_dir "$CELERYD_PID_DIR" -} - -export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" - - -_get_pidfiles () { - # note: multi < 3.1.14 output to stderr, not stdout, hence the redirect. - ${CELERYD_MULTI} expand "${CELERYD_PID_FILE}" ${CELERYD_NODES} 2>&1 -} - - -_get_pids() { - found_pids=0 - my_exitcode=0 - - for pidfile in $(_get_pidfiles); do - local pid=`cat "$pidfile"` - local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` - if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then - echo "bad pid file ($pidfile)" - one_failed=true - my_exitcode=1 - else - found_pids=1 - echo "$pid" - fi - - if [ $found_pids -eq 0 ]; then - echo "${SCRIPT_NAME}: All nodes down" - exit $my_exitcode - fi - done -} - - -_chuid () { - su "$CELERYD_USER" -c "$CELERYD_MULTI $*" -} - - -start_workers () { - if [ ! -z "$CELERYD_ULIMIT" ]; then - ulimit $CELERYD_ULIMIT - fi - _chuid $* start $CELERYD_NODES $DAEMON_OPTS \ - --pidfile="$CELERYD_PID_FILE" \ - --logfile="$CELERYD_LOG_FILE" \ - --loglevel="$CELERYD_LOG_LEVEL" \ - $CELERY_APP_ARG \ - $CELERYD_OPTS -} - - -dryrun () { - (C_FAKEFORK=1 start_workers --verbose) -} - - -stop_workers () { - _chuid stopwait $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" -} - - -restart_workers () { - _chuid restart $CELERYD_NODES $DAEMON_OPTS \ - --pidfile="$CELERYD_PID_FILE" \ - --logfile="$CELERYD_LOG_FILE" \ - --loglevel="$CELERYD_LOG_LEVEL" \ - $CELERY_APP_ARG \ - $CELERYD_OPTS -} - - -kill_workers() { - _chuid kill $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" -} - - -restart_workers_graceful () { - echo "WARNING: Use with caution in production" - echo "The workers will attempt to restart, but they may not be able to." - local worker_pids= - worker_pids=`_get_pids` - [ "$one_failed" ] && exit 1 - - for worker_pid in $worker_pids; do - local failed= - kill -HUP $worker_pid 2> /dev/null || failed=true - if [ "$failed" ]; then - echo "${SCRIPT_NAME} worker (pid $worker_pid) could not be restarted" - one_failed=true - else - echo "${SCRIPT_NAME} worker (pid $worker_pid) received SIGHUP" - fi - done - - [ "$one_failed" ] && exit 1 || exit 0 -} - - -check_status () { - my_exitcode=0 - found_pids=0 - - local one_failed= - for pidfile in $(_get_pidfiles); do - if [ ! -r $pidfile ]; then - echo "${SCRIPT_NAME} down: no pidfiles found" - one_failed=true - break - fi - - local node=`basename "$pidfile" .pid` - local pid=`cat "$pidfile"` - local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` - if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then - echo "bad pid file ($pidfile)" - one_failed=true - else - local failed= - kill -0 $pid 2> /dev/null || failed=true - if [ "$failed" ]; then - echo "${SCRIPT_NAME} (node $node) (pid $pid) is down, but pidfile exists!" - one_failed=true - else - echo "${SCRIPT_NAME} (node $node) (pid $pid) is up..." - fi - fi - done - - [ "$one_failed" ] && exit 1 || exit 0 -} - - -case "$1" in - start) - check_dev_null - check_paths - start_workers - ;; - - stop) - check_dev_null - check_paths - stop_workers - ;; - - reload|force-reload) - echo "Use restart" - ;; - - status) - check_status - ;; - - restart) - check_dev_null - check_paths - restart_workers - ;; - - graceful) - check_dev_null - restart_workers_graceful - ;; - - kill) - check_dev_null - kill_workers - ;; - - dryrun) - check_dev_null - dryrun - ;; - - try-restart) - check_dev_null - check_paths - restart_workers - ;; - - create-paths) - check_dev_null - create_paths - ;; - - check-paths) - check_dev_null - check_paths - ;; - - *) - echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|graceful|kill|dryrun|create-paths}" - exit 64 # EX_USAGE - ;; -esac - -exit 0 diff --git a/dbReports/celery/init.d/celeryd.new b/dbReports/celery/init.d/celeryd.new deleted file mode 100644 index b8c87b26..00000000 --- a/dbReports/celery/init.d/celeryd.new +++ /dev/null @@ -1,188 +0,0 @@ -#!/bin/sh -# Copyright (C) 2011 Ion Torrent Systems, Inc. All Rights Reserved -### BEGIN INIT INFO -# Provides: celeryd -# Required-Start: $network $local_fs $remote_fs postgresql -# Required-Stop: $network $local_fs $remote_fs postgresql -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: celery task worker daemon -### END INIT INFO - -# Name of the projects settings module. -export DJANGO_SETTINGS_MODULE="iondb.settings" -export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" - -# Name of nodes to start, here we have a single node -# Edits to NODES need to be in iondb/rundb/configure/views.py - services tab reporting -CELERYD_NODES="w1 plugins periodic slowlane transfer diskutil" - -# Where to chdir at start. -CELERYD_CHDIR="/opt/ion" - -# How to call "manage.py celeryd_multi" -CELERY_MULTI="$CELERYD_CHDIR/manage.py celery multi" - -# The following abbreviations will be expanded in file names below -# * %n -> node name -# * %h -> host name -# Extra arguments to celeryd -CELERYD_OPTS="--events " -CELERYD_OPTS="$CELERYD_OPTS --loglevel=INFO" - -CELERYD_REQ="--pidfile=/var/run/celery/celery_%n.pid" -CELERYD_REQ="$CELERYD_REQ --logfile=/var/log/ion/celery_%n.log" - -CELERYBEAT="$CELERYD_CHDIR/manage.py celery beat" -CELERYBEAT_PID_FILE="/var/run/celery/celerybeat.pid" -CELERYBEAT_SCHEDULE_FILE="/var/run/celery/celerybeat-schedule" - -if [ -n "$2" ]; then - CELERYD_OPTS="$CELERYD_OPTS $2" -fi - -check_dev_null() { - if [ ! -c /dev/null ]; then - echo "/dev/null is not a character device!" - exit 1 - fi -} - -check_privsep_dir() { - # Create the PrivSep empty dir if necessary - if [ ! -d /var/run/celery ]; then - mkdir /var/run/celery - fi - chown -R ionian:ionadmin /var/run/celery - chmod 0775 /var/run/celery -} - -wait_pid () { - pid=$1 - forever=1 - i=0 - while [ $forever -gt 0 ]; do - kill -0 $pid 1>/dev/null 2>&1 - if [ $? -eq 1 ]; then - echo "OK" - forever=0 - else - i=$((i + 1)) - if [ $i -gt 60 ]; then - echo "Timed out while stopping (30s)" - kill -9 "$pid" - forever=0 - else - kill -TERM "$pid" - sleep 0.5 - fi - fi - done -} - -stop_beat () { - echo -n "Stopping celerybeat... " - if [ -f "$CELERYBEAT_PID_FILE" ]; then - wait_pid $(cat "$CELERYBEAT_PID_FILE") - else - echo "celerybeat not running" - fi -} - -start_beat () { - echo "Starting celerybeat..." - $CELERYBEAT $CELERYBEAT_OPTS \ - --pidfile="$CELERYBEAT_PID_FILE" \ - --logfile="/var/log/ion/celerybeat.log" \ - --loglevel=INFO \ - --schedule="$CELERYBEAT_SCHEDULE_FILE" \ - --detach -} - -multi_command () { - $CELERY_MULTI $1 $CELERYD_NODES \ - $CELERYD_OPTS \ - $CELERYD_REQ \ - --concurrency:w1=4 \ - --concurrency:plugins=2 \ - --queue:plugins=plugins \ - --concurrency:periodic=6 \ - --queue:periodic=periodic \ - --concurrency:slowlane=1 \ - --queue:slowlane=slowlane \ - --concurrency:transfer=1 \ - --queue:transfer=transfer \ - --queue:diskutil=diskutil \ - --concurrency:diskutil=2 -} - -start_workers () { - multi_command start; - start_beat; -} - -restart_workers () { - stop_beat; - multi_command restart; - start_beat; -} - -stop_workers () { - stop_beat; - multi_command stopwait; - if ps aux|grep "/opt/ion/manage.py celery"|grep -q -v grep; then - echo "There are still processes around!" - fi -} - -check_status () { - pids=`cat /var/run/celery/celery_*.pid 2> /dev/null` || pids="" - if [ "$pids" != "" ]; - then - echo "Celery is running with pids:" - echo $pids - else - echo "No Celery node processes." - exit 1 - fi -} - -case "$1" in - start) - check_dev_null - check_privsep_dir - start_workers - ;; - - stop) - check_dev_null - stop_workers - ;; - - reload|force-reload) - echo "Use restart" - ;; - - status) - check_status - ;; - - restart) - check_dev_null - check_privsep_dir - restart_workers - ;; - - try-restart) - check_dev_null - check_privsep_dir - restart_workers - ;; - - *) - echo "Usage: /etc/init.d/celeryd {start|stop|restart|try-restart|kill}" - exit 1 - ;; -esac - -exit 0 diff --git a/dbReports/celery/init/DjangoFTP.conf b/dbReports/celery/init/DjangoFTP.conf new file mode 100644 index 00000000..a7a31b7d --- /dev/null +++ b/dbReports/celery/init/DjangoFTP.conf @@ -0,0 +1,15 @@ +# Should be /etc/init/DjangoFTP.conf +description "This service manages the FTP server which authenticates against Django." +author "Brian Bourke-Martin " + +start on runlevel [2345] + +env DJANGO_SETTINGS_MODULE="iondb.settings" +setuid www-data +setgid www-data + +script + chdir /opt/ion/ + sleep 5 + python manage.py ftpserver +end script \ No newline at end of file diff --git a/dbReports/celery/init/celerybeat.conf b/dbReports/celery/init/celerybeat.conf new file mode 100644 index 00000000..8ba9f664 --- /dev/null +++ b/dbReports/celery/init/celerybeat.conf @@ -0,0 +1,39 @@ +# Should be /etc/init/celerybeat.conf +description "This service manages the periodic celery tasks for the Torrent Suite" +author "Brian Bourke-Martin " + +start on (celeryd-running or started celeryd) +stop on (celeryd-stopped or stopping celeryd) + +env DJANGO_SETTINGS_MODULE="iondb.settings" +env CELERY_APP="iondb" +env CELERY_CHDIR="/opt/ion" +env CELERY_LOG_DIR="/var/log/ion" +env CELERY_RUN_DIR="/var/run/celery" +env CELERY_LOG_FILE=celerybeat.log +env CELERY_PID_FILE=celerybeat.pid +env CELERY_LOG_LEVEL="INFO" +env USER=www-data +env GROUP=www-data + +pre-start script + if [ ! -d "CELERY_LOG_DIR" ]; then + mkdir -p "$CELERY_LOG_DIR" + chown "$USER":"$GROUP" "$CELERY_LOG_DIR" + fi + + if [ ! -d "CELERY_RUN_DIR" ]; then + mkdir -p "$CELERY_RUN_DIR" + chown "$USER":"$GROUP" "$CELERY_RUN_DIR" + fi +end script + +exec /usr/bin/celery beat --pidfile="$CELERY_RUN_DIR/$CELERY_PID_FILE" \ + --logfile="$CELERY_LOG_DIR/$CELERY_LOG_FILE" \ + --loglevel="$CELERY_LOG_LEVEL" \ + --app="$CELERY_APP" \ + --schedule=/var/run/celery/celerybeat-schedule \ + --workdir="$CELERY_CHDIR" \ + --uid="$USER" \ + --gid="$GROUP" \ + --no-color \ No newline at end of file diff --git a/dbReports/celery/init/celeryd.conf b/dbReports/celery/init/celeryd.conf new file mode 100644 index 00000000..5b6f5aac --- /dev/null +++ b/dbReports/celery/init/celeryd.conf @@ -0,0 +1,68 @@ +# Should be /etc/init/celeryd.conf +description "This service manages all of the celery queues for the Torrent Suite" +author "Brian Bourke-Martin " + +start on (rabbitmq-server-running or started rabbitmq-server) +stop on (rabbitmq-server-stopped or stopping rabbitmq-server) + +env DJANGO_SETTINGS_MODULE="iondb.settings" +env CELERY_APP="iondb" +env CELERY_CHDIR="/opt/ion" +env CELERY_NODES="w1 plugins periodic slowlane transfer diskutil dmprocess dmmanage" +env CELERY_OPTS="-Ofair --event --time-limit=21600 --queue:w1=w1 --concurrency:w1=4 --queue:plugins=plugins --concurrency:plugins=2 --queue:periodic=periodic --concurrency:periodic=6 --queue:slowlane=slowlane --concurrency:slowlane=1 --queue:transfer=transfer --concurrency:transfer=1 --queue:diskutil=diskutil --concurrency:diskutil=2 --queue:dmprocess=dmprocess --concurrency:dmprocess=8 --queue:dmmanage=dmmanage --concurrency:dmmanage=8" +env CELERY_LOG_DIR="/var/log/ion" +env CELERY_RUN_DIR="/var/run/celery" +env CELERY_LOG_FILE=celery_%n.log +env CELERY_PID_FILE=celery_%n.pid +env CELERY_LOG_LEVEL="INFO" +env USER=www-data +env GROUP=www-data + +script + # we need this section so that pre-stop gets run! + # https://bugs.launchpad.net/upstart/+bug/252996 + while true + do sleep 1d + done +end script + +pre-start script + if [ ! -d "$CELERY_LOG_DIR" ]; then + mkdir -p "$CELERY_LOG_DIR" + chown "$USER":"$GROUP" "$CELERY_LOG_DIR" + fi + + if [ ! -d "$CELERY_RUN_DIR" ]; then + mkdir -p "$CELERY_RUN_DIR" + chown "$USER":"$GROUP" "$CELERY_RUN_DIR" + fi + + /usr/bin/celery multi start $CELERY_NODES \ + --pidfile="$CELERY_RUN_DIR/$CELERY_PID_FILE" \ + --logfile="$CELERY_LOG_DIR/$CELERY_LOG_FILE" \ + --loglevel="$CELERY_LOG_LEVEL" \ + --app="$CELERY_APP" \ + --workdir="$CELERY_CHDIR" \ + --uid=$USER \ + --gid=$GROUP \ + --no-color \ + $CELERY_OPTS +end script + +pre-stop script + /usr/bin/celery multi --verbose stop $CELERY_NODES \ + --pidfile="$CELERY_RUN_DIR/$CELERY_PID_FILE" \ + --logfile="$CELERY_LOG_DIR/$CELERY_LOG_FILE" \ + --loglevel="$CELERY_LOG_LEVEL" \ + --app="$CELERY_APP" \ + --workdir="$CELERY_CHDIR" \ + --uid=$USER \ + --gid=$GROUP \ + --no-color + + # wait for all of the PID's to be removed indicating that the queue's are done + while [ $(ls -1 $CELERY_RUN_DIR/celery_*.pid 2>/dev/null | wc -l) -gt 0 ] + do sleep 1s + done + +end script diff --git a/dbReports/debian/postinst.in b/dbReports/debian/postinst.in index efb1ae52..9d4e8727 100755 --- a/dbReports/debian/postinst.in +++ b/dbReports/debian/postinst.in @@ -13,6 +13,14 @@ unset CELERY_LOADER case "$1" in configure) + KEY_FILE=/var/spool/ion/key + if [ ! -f $KEY_FILE ]; then + cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 256 | head -n 1 > $KEY_FILE + chown www-data:www-data $KEY_FILE + fi + # updated all instances of the key file to this permission level as per TS-14740 + chmod 0440 $KEY_FILE + # create or update the off-cycle release source url if [ -f /etc/apt/sources.list.d/iontorrent-offcycle.list ]; then sed -i "s/deb http:\/\/ionupdates.com\/updates\/software\/offcycle\/[^0-9.]*\([0-9.]*\).*/deb http:\/\/ionupdates.com\/updates\/software\/offcycle\/@TS_MAJOR@.@TS_MINOR@.@TS_RELEASE@\/ @LSB_RELEASE_CODENAME@\//" /etc/apt/sources.list.d/iontorrent-offcycle.list @@ -63,10 +71,17 @@ case "$1" in chmod -R 777 /results/referenceLibrary/tmap-f3 chmod -R 777 /results/referenceLibrary/disabled chmod -R 777 /results/referenceLibrary/TestFragment + + chown -R www-data:www-data /results/referenceLibrary/TestFragment + #make pluginMedia dir mkdir -p /results/pluginMedia chmod -R 777 /results/pluginMedia + # make the celeryd pid run directory with permissions + mkdir -p /var/run/celery + chmod ugo+rw /var/run/celery + # ------------------------------------------ # Replace existing httpd.conf # ------------------------------------------ @@ -119,16 +134,9 @@ ENDOFAPACHEENV /usr/sbin/a2ensite torrent-server.conf /usr/sbin/a2ensite torrent-server-ssl.conf - # Enable config files - if [ -x /usr/sbin/a2enconf ]; then - # Apache 2.4 on Ubuntu 14.04 - /usr/sbin/a2enconf h5bp-server.conf - /usr/sbin/a2enconf torrent-server-config.conf - else - # Compat with Apache 2.2 and conf.d on Ubuntu 10.04 - /bin/ln -snf /etc/apache2/conf-available/h5bp-server.conf /etc/apache2/conf.d/ - /bin/ln -snf /etc/apache2/conf-available/torrent-server-config.conf /etc/apache2/conf.d/ - fi + # Apache 2.4 on Ubuntu 14.04 + /usr/sbin/a2enconf h5bp-server.conf + /usr/sbin/a2enconf torrent-server-config.conf # apache2 restarted by invoke-rc.d command below @@ -210,6 +218,16 @@ ENDOFAPACHEENV rm -f /results/analysis/output/disabled/tmap-* ln -s -f /results/referenceLibrary/disabled/tmap-* /results/analysis/output/disabled + # ------------------------------------------ + # Create ionian user if it doesn't exist: user is needed to initialize + # ownership of log files below + # ------------------------------------------ + if ! id -u ionian; then + # NOTE: non-unique was added as result of TS-11113 + groupadd ionian --gid=1100 --system + useradd --gid ionian --uid=1100 --non-unique --system ionian + fi + #--- ---# #--- Configure runlevels to start the Ion daemons at boot ---# #--- ---# @@ -217,8 +235,6 @@ ENDOFAPACHEENV ionCrawler ionJobServer ionPlugin - celeryd - celerybeat ) for DAEMON in ${DAEMONS[@]}; do # Need to fix the start/kill priority for the daemons @@ -229,7 +245,6 @@ ENDOFAPACHEENV update-rc.d ${DAEMON} defaults 97 03 done - # ------------------------------------------ # Create directory for logging # ------------------------------------------ @@ -266,8 +281,13 @@ ENDOFAPACHEENV fi invoke-rc.d apache2 stop - invoke-rc.d celerybeat stop - invoke-rc.d celeryd stop + + # we are going to have to do an explicit startup of the postgresql system to make sure it's up and going + # before we attempt to setup the database environment + invoke-rc.d postgresql start + + # doing an explicit wait to make sure that PG is initialized from the previous command + sleep 5 # Setup python and database environment ( @@ -307,6 +327,12 @@ ENDOFAPACHEENV # THIS HAS TO HAPPEN AFTER THE PHP_BASE.HTML FILE IS PUT INTO PLACE DO NOT MOVE /usr/bin/python ./bin/install_script.py + # Load barcode kits to database if needed + echo "Going to add barcode kits if needed..." + /usr/bin/python ./bin/add_barcodes.py "Ion SingleSeq Barcode set 1-24" rundb/fixtures/barcodes/SingleSeq_1-24.csv + /usr/bin/python ./bin/add_barcodes.py "Ion SingleSeq Barcode set 1-96" rundb/fixtures/barcodes/SingleSeq_1-96.csv OVERRIDE + /usr/bin/python ./bin/add_barcodes.py "TagSequencing" rundb/fixtures/barcodes/TagSequencing.csv + # Loads system templates to database if needed echo "Going to add or update system plan templates if needed..." /usr/bin/python ./bin/add_or_update_systemPlanTemplates.py @@ -349,7 +375,7 @@ EOF invoke-rc.d apache2 start invoke-rc.d celeryd start invoke-rc.d celerybeat start - + service DjangoFTP start #load the references for a first time (wget -q -O /dev/null http://localhost/configure/references/ > /dev/null) || \ @@ -366,7 +392,6 @@ EOF # Check whether or not we're online for CSA support upload python -c 'import iondb.bin.djangoinit, iondb.rundb.data.data_export as tasks; tasks.poll_support_site.apply_async(countdown=61)' - #TS-6081: Create DMFileStat objs for existing Results objs without them. echo -n "Migrating to new Data Management..." python -c 'import iondb.bin.djangoinit, iondb.rundb.data.data_management as dm; dm.backfill_create_dmfilestat()' @@ -377,6 +402,9 @@ EOF invoke-rc.d ionPlugin restart invoke-rc.d ionCrawler restart invoke-rc.d avahi-daemon restart + + # fire off the grooming script + /opt/ion/iondb/bin/grooming.py ;; esac diff --git a/dbReports/debian/postrm.in b/dbReports/debian/postrm.in index 348269f9..5ae4a8d8 100755 --- a/dbReports/debian/postrm.in +++ b/dbReports/debian/postrm.in @@ -23,7 +23,8 @@ case "$1" in ionPlugin celeryd celerybeat - ) + DjangoFTP + ) for DAEMON in ${DAEMONS[@]}; do update-rc.d -f ${DAEMON} remove || true done @@ -32,6 +33,7 @@ case "$1" in if [ -h /usr/lib64/libsysfs.so.2.0.2 ]; then rm -f /usr/lib64/libsysfs.so.2.0.2 fi + ;; esac diff --git a/dbReports/debian/preinst.in b/dbReports/debian/preinst.in old mode 100644 new mode 100755 index 0b5fd4b7..5858487e --- a/dbReports/debian/preinst.in +++ b/dbReports/debian/preinst.in @@ -1,28 +1,35 @@ -#!/bin/bash +#!/bin/sh # Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved set -e -#set -x -echo "preinst $@" + case "$1" in install|upgrade) - if [ -e "/etc/init.d/ionCrawler" ]; then - invoke-rc.d ionCrawler stop - fi - if [ -e "/etc/init.d/ionJobServer" ]; then - invoke-rc.d ionJobServer stop - fi - if [ -e "/etc/init.d/ionPlugin" ]; then - invoke-rc.d ionPlugin stop - fi - - invoke-rc.d apache2 stop - if [ -d @ION_INSTALL_PREFIX@/iondb/ ]; then - find @ION_INSTALL_PREFIX@/iondb/ -name '*.pyc' -delete - fi - - # ionArchive is removed as of this version - make sure sysv links are gone - update-rc.d -f ionArchive remove + + if [ -e "/etc/init.d/ionCrawler" ]; then + invoke-rc.d ionCrawler stop + fi + if [ -e "/etc/init.d/ionJobServer" ]; then + invoke-rc.d ionJobServer stop + fi + if [ -e "/etc/init.d/ionPlugin" ]; then + invoke-rc.d ionPlugin stop + fi + + invoke-rc.d apache2 stop + if [ -d @ION_INSTALL_PREFIX@/iondb/ ]; then + find @ION_INSTALL_PREFIX@/iondb/ -name '*.pyc' -delete + fi + + # ionArchive is removed as of this version - make sure sysv links are gone + update-rc.d -f ionArchive remove + + invoke-rc.d celerybeat stop || true + update-rc.d -f celerybeat remove + + invoke-rc.d celeryd stop || true + update-rc.d -f celeryd remove + + invoke-rc.d DjangoFTP stop || true + update-rc.d -f DjangoFTP remove ;; esac - -exit 0 diff --git a/dbReports/debian/prerm.in b/dbReports/debian/prerm.in index bca586b3..bec8924b 100755 --- a/dbReports/debian/prerm.in +++ b/dbReports/debian/prerm.in @@ -22,6 +22,9 @@ case "$1" in if [ -e "/etc/init.d/celeryd" ]; then invoke-rc.d celeryd stop fi + if [ -e "/etc/init/DjangoFTP" ]; then + invoke-rc.d DjangoFTP stop + fi if [ ! -e @ION_INSTALL_PREFIX@/.computenode ]; then #--- Remove symbolic links ---# diff --git a/dbReports/iondb/anaserve/serve.py b/dbReports/iondb/anaserve/serve.py index 9546edd5..6a78bdc6 100755 --- a/dbReports/iondb/anaserve/serve.py +++ b/dbReports/iondb/anaserve/serve.py @@ -41,36 +41,22 @@ """ import datetime import json -import simplejson import os -import uuid from os import path import re import signal import subprocess import sys import threading -import tempfile import traceback import logging -from logging import handlers as loghandlers +from logging import handlers from twisted.web import xmlrpc, server #for tmap queue -import urllib -import urllib2 -from twisted.internet import utils, reactor -from twisted.web import xmlrpc, server -from twisted.application import service -from twisted.internet import defer, utils -from twisted.python import log -import shutil -from random import choice -import string +from twisted.internet import reactor -#import libs to zip with -import zipfile LOG_FILENAME = '/var/log/ion/jobserver.log' logger = logging.getLogger(__name__) @@ -86,9 +72,14 @@ REFERENCE_LIBRARY_TEMP_DIR = "/results/referenceLibrary/temp/" import iondb.anaserve.djangoinit -import iondb.rundb.models - -__version__ = filter(str.isdigit, "$Revision$") +#from iondb.bin import djangoinit +#from iondb.rundb import models +try: + import iondb.version as version # @UnresolvedImport + GITHASH = version.IonVersionGetGitHash() +except: + GITHASH = "" +__version__ = GITHASH # local settings @@ -450,7 +441,7 @@ def terminate(self): try: logger.debug("terminate job %s, status %s" % (blockjobid, _session.jobStatus(blockjobid))) _session.control(blockjobid, drmaa.JobControlAction.TERMINATE) - except Exception as err: + except Exception: logger.error("Failed to terminate %s" % blockjobid) except: logger.error("DRMAA terminate error reading from %s" % joblistfile) @@ -521,7 +512,7 @@ def suspend(self): return False try: self.proc.send_signal(signal.SIGSTOP) - except Exception as err: + except Exception: logger.warning("SIGSTOP failed") return False return self._running() @@ -532,7 +523,7 @@ def resume(self): return False try: self.proc.send_signal(signal.SIGCONT) - except Exception as err: + except Exception: logger.warning("SIGCONT failed") return False return True @@ -752,7 +743,7 @@ def xmlrpc_updatestatus(self, from ion.reports import uploadMetrics try: uploadMetrics.updateStatus(primarykeyPath, status, reportLink) - except Exception as err: + except Exception: logger.error("Update status failed") return traceback.format_exc() return 0 @@ -893,14 +884,14 @@ def xmlrpc_resultdiskspace(self, pk): update_dmfilestat_diskusage.delay(pk) except: logger.warn("update_diskusage celery task failed to launch") - + # Generate serialized json file for future Data Management Import try: from iondb.rundb.data.tasks import save_serialized_json save_serialized_json.delay(pk) except: logger.warn("save_serialized_json celery task failed") - + return 0 diff --git a/dbReports/iondb/bin/IonMeshDiscoveryManager.py b/dbReports/iondb/bin/IonMeshDiscoveryManager.py index d3ca50fd..621e5beb 100755 --- a/dbReports/iondb/bin/IonMeshDiscoveryManager.py +++ b/dbReports/iondb/bin/IonMeshDiscoveryManager.py @@ -1,18 +1,16 @@ #!/usr/bin/env python -# Copyright (C) 2015 Thermo Fisher Scientific. All Rights Reserved. +# Copyright (C) 2017 Thermo Fisher Scientific. All Rights Reserved. import dbus, gobject, avahi from dbus.mainloop.glib import DBusGMainLoop import threading import time import copy +import socket class IonMeshDiscoveryManager(threading.Thread): - - """ - This class will monitor avahi via dbus in order to detect the current mesh network setup - """ + """This class will monitor avahi via dbus in order to detect the current mesh network setup""" # singleton instance __singleton = None @@ -26,6 +24,9 @@ class IonMeshDiscoveryManager(threading.Thread): # thread lock __threadLock = threading.Lock() + # local host name + __localhost = '' + def __new__(cls, *args, **kwargs): if not cls.__singleton: cls.__singleton = super(IonMeshDiscoveryManager, cls).__new__(cls, *args, **kwargs) @@ -33,8 +34,7 @@ def __new__(cls, *args, **kwargs): # setup dbus stuff cls.__singleton.__bus = dbus.SystemBus(mainloop=DBusGMainLoop()) - cls.__singleton.__server = dbus.Interface(cls.__singleton.__bus.get_object( - avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) + cls.__singleton.__server = dbus.Interface(cls.__singleton.__bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) # Look for self.regtype services and hook into callbacks cls.__singleton.__browser = dbus.Interface(cls.__singleton.__bus.get_object(avahi.DBUS_NAME, cls.__singleton.__server.ServiceBrowserNew( @@ -49,18 +49,21 @@ def __new__(cls, *args, **kwargs): return cls.__singleton + def stop(self): """ Call this to stop the thread as one would expect """ self.__loop.quit() + def run(self): """ Method called with the thread object's "start" method is called """ self.__loop.run() + def getMeshComputers(self): """ This will create a copy of the list of the comupters in the mesh by hostname @@ -72,20 +75,20 @@ def getMeshComputers(self): finally: self.__threadLock.release() + + def getLocalComputer(self): + """Gets the localhost name""" + return self.__localhost or socket.getfqdn() + + def __serviceFound(self, interface, protocol, name, stype, domain, flags): - """ - Callback for when a service needs to be added to the mesh list - :param interface: - :param protocol: - :param name: - :param stype: - :param domain: - :param flags: - """ + """Callback for when a service needs to be added to the mesh list""" # skip local services # http://sources.debian.net/src/avahi/0.6.32-1/avahi-python/avahi/__init__.py/?hl=50#L50 if flags & avahi.LOOKUP_RESULT_LOCAL: + self.__localhost = str(name) return + # add the computer name to the list of mesh computers self.__threadLock.acquire() try: @@ -94,6 +97,7 @@ def __serviceFound(self, interface, protocol, name, stype, domain, flags): finally: self.__threadLock.release() + def __serviceRemoved(self, interface, protocol, name, stype, domain, flags): """ Callback for when a service needs to be removed from the mesh list diff --git a/dbReports/iondb/bin/add_barcodes.py b/dbReports/iondb/bin/add_barcodes.py index 0666e4a7..e0ffa24a 100644 --- a/dbReports/iondb/bin/add_barcodes.py +++ b/dbReports/iondb/bin/add_barcodes.py @@ -13,7 +13,15 @@ import sys -def command_line_add_barcode(name, dest_path): +def _get_new_or_db_barcode(name, index): + if (name and index): + barcodes = dnaBarcode.objects.filter(name = name, index = index) + if barcodes: + print ("db barcode FOUND for name=%s; index=%s" %(name, index)) + return barcodes[0] + return dnaBarcode() + +def command_line_add_barcode(name, dest_path, is_override): """add the barcodes, with CSV validation""" # name = request.POST.get('name', '') @@ -34,14 +42,21 @@ def command_line_add_barcode(name, dest_path): return expectedHeader = ["id_str", "type", "sequence", "floworder", "index", "annotation", "adapter", "score_mode", "score_cutoff"] - if sorted(firstCSV[0]) != sorted(expectedHeader): - print("Barcode csv header is not as expected. Please try again starting with the provided example") - return + ##if sorted(firstCSV[0]) != sorted(expectedHeader): + ## print("Barcode csv header is not as expected. Please try again starting with the provided example") + ## return + # test if the barcode set name has been used before barCodeSet = dnaBarcode.objects.filter(name=name) - if barCodeSet: - print("Error: Barcode set with the same name already exists") - return + if barCodeSet and not is_override: + print("Nothing to do: Barcode set with the same name %s already exists" %name) + return True + + is_override_option = is_override + if is_override and not barCodeSet: + is_override_option = False + + print(">>> Start processing barcode set %s" %name) index = 0 barCodes = [] failed = {} @@ -52,7 +67,9 @@ def command_line_add_barcode(name, dest_path): if invalid: # don't make dna object or add it to the list failed[index] = invalid continue - newBarcode = dnaBarcode() + + newBarcode = _get_new_or_db_barcode(name, index) + newBarcode.name = name # set the name newBarcode.index = index # set index this can be overwritten later nucs = ["sequence", "floworder", "adapter"] # fields that have to be uppercase @@ -102,11 +119,24 @@ def command_line_add_barcode(name, dest_path): if __name__ == "__main__": - if len(sys.argv) != 3: - print("Usage: add_barcode.py NAME BARCODE_CSV_PATH") + if len(sys.argv) < 2 or len(sys.argv) > 4: + print("Usage: add_barcode.py BARCODE_KIT_NAME BARCODE_CSV_PATH or add_barcode.py BARCODE_KIT_NAME BARCODE_CSV_PATH OVERRIDE") sys.exit(1) - name, path = sys.argv[1:3] - if command_line_add_barcode(name, path): + + is_override = False + isDone = False + if len(sys.argv) == 3: + name, path = sys.argv[1:3] + if command_line_add_barcode(name, path, is_override): + isDone = True + elif len(sys.argv) == 4: + name, path, option = sys.argv[1:4] + is_override = (option.lower() == "override") + + if command_line_add_barcode(name, path, is_override): + isDone = True + + if isDone: print("Completed successfully") sys.exit(0) else: diff --git a/dbReports/iondb/bin/add_or_update_systemPlanTemplates.py b/dbReports/iondb/bin/add_or_update_systemPlanTemplates.py old mode 100755 new mode 100644 index 67079117..6b9d7072 --- a/dbReports/iondb/bin/add_or_update_systemPlanTemplates.py +++ b/dbReports/iondb/bin/add_or_update_systemPlanTemplates.py @@ -313,7 +313,7 @@ def finish_sys_template(sysTemplate, isCreated, templateParams, plugins={}): eas.reference = templateParams.reference hasChanges = True - if (eas.selectedPlugins != plugins): + if not simple_compare_dict(eas.selectedPlugins, plugins): print ">>> DIFF: orig eas.selectedPlugins=%s for system template.id=%d; name=%s" % \ (eas.selectedPlugins, sysTemplate.id, sysTemplate.planName) print ">>> DIFF: NEW selectedPlugins=%s for system template.id=%d; name=%s" % \ @@ -368,18 +368,45 @@ def finish_sys_template(sysTemplate, isCreated, templateParams, plugins={}): return sysTemplate -def _get_third_party_plugin_dict(pluginId=9999, pluginName=""): - pluginUserInput = {} +def _get_plugin_dict(pluginName, userInput={}): + try: + selectedPlugin = models.Plugin.objects.get(name=pluginName, selected=True, active=True) + pluginDict = { + "id": selectedPlugin.id, + "name": selectedPlugin.name, + "version": selectedPlugin.version, + "userInput": userInput, + "features": [] + } + except models.Plugin.DoesNotExist: + pluginDict = { + "id": 9999, + "name": pluginName, + "version": "1.0", + "userInput": userInput, + "features": [] + } - thirdPartyPluginDict = { - "id": pluginId, - "name": pluginName, - "version": "1.0", - "userInput": pluginUserInput, - "features": [] - } - - return thirdPartyPluginDict + return pluginDict + + +def simple_compare_dict(dict1, dict2): + ''' accepts multi-level dictionaries + compares values as strings, will not report type mismatch + ''' + if sorted(dict1.keys()) != sorted(dict2.keys()): + return False + + for key, value in dict1.iteritems(): + if isinstance(value, dict): + if not simple_compare_dict(value, dict2[key]): + return False + elif isinstance(value, list): + if sorted(value) != sorted(dict2[key]): + return False + elif str(value) != str(dict2[key]): + return False + return True def add_or_update_sys_template(templateParams, isSystemDefault=False): @@ -446,14 +473,13 @@ def add_or_update_sys_template(templateParams, isSystemDefault=False): sysTemplate.templatingSize = templateParams.templatingSize hasChanges = True - if templateParams.libraryReadLength: - if (sysTemplate.libraryReadLength != templateParams.libraryReadLength): - print ">>> DIFF: orig sysTemplate.libraryReadLength=%s for system template.id=%d; name=%s" % \ - (sysTemplate.libraryReadLength, - sysTemplate.id, sysTemplate.planName) + if (sysTemplate.libraryReadLength != templateParams.libraryReadLength): + print ">>> DIFF: orig sysTemplate.libraryReadLength=%s for system template.id=%d; name=%s" % \ + (sysTemplate.libraryReadLength, + sysTemplate.id, sysTemplate.planName) - sysTemplate.libraryReadLength = templateParams.libraryReadLength - hasChanges = True + sysTemplate.libraryReadLength = templateParams.libraryReadLength + hasChanges = True if (sysTemplate.planStatus not in ["planned", "inactive"]): print ">>> DIFF: orig sysTemplate.planStatus=%s not supported for system template.id=%d; name=%s" % \ @@ -576,28 +602,21 @@ def add_or_update_default_system_templates(): def add_or_update_ampliseq_system_templates(): # ampliseq + CATEGORIES = "onco_solidTumor" # 3 templateParams = TemplateParams("Ion AmpliSeq Cancer Hotspot Panel v2", PGM, "AMPS") - templateParams.update({ - "flows": 500, - "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", - "reference": "hg19" - }) - sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) - finish_sys_template(sysTemplate, isCreated, templateParams) - - # 4 - templateParams = TemplateParams("Ion AmpliSeq Cancer Panel", PGM, "AMPS") templateParams.update({ "flows": 500, "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "reference": "hg19", - "targetRegionBedFile": "/hg19/unmerged/detail/HSMv12.1_reqions_NO_JAK2_NODUP.bed", - "hotSpotRegionBedFile":"/hg19/unmerged/detail/HSMv12.1_hotspots_NO_JAK2.bed" + "categories": CATEGORIES }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams) + # 4 - retired + # templateParams = TemplateParams("Ion AmpliSeq Cancer Panel", PGM, "AMPS") + # 5 templateParams = TemplateParams("Ion AmpliSeq Cancer Panel 1_0 Lib Chem", PGM, "AMPS") templateParams.update({ @@ -616,6 +635,7 @@ def add_or_update_ampliseq_system_templates(): "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "reference": "hg19", "targetRegionBedFile": "/hg19/unmerged/detail/4477685_CCP_bedfile_20120517.bed", + "categories": CATEGORIES }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams) @@ -642,10 +662,11 @@ def add_or_update_ampliseq_system_templates(): # 9 templateParams = TemplateParams("Ion AmpliSeq Inherited Disease Panel", PGM, "AMPS") templateParams.update({ - "chipType": "316", + "chipType": "318", "flows": 500, "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "reference": "hg19", + "categories": "inheritedDisease", "targetRegionBedFile": "/hg19/unmerged/detail/4477686_IDP_bedfile_20120613.bed", }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) @@ -668,25 +689,10 @@ def add_or_update_ampliseq_rna_system_templates(): # pre-select plugins plugins = {} - pluginUserInput = {} - - selectedPlugins = models.Plugin.objects.filter( - name__in=["coverageAnalysis"], selected=True, active=True) - - for selectedPlugin in selectedPlugins: - - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict + plugins["coverageAnalysis"] = _get_plugin_dict("coverageAnalysis") thirdPartyPluginName = "PartekFlowUploader" - plugins[thirdPartyPluginName] = _get_third_party_plugin_dict(pluginId=9999, pluginName=thirdPartyPluginName) + plugins[thirdPartyPluginName] = _get_plugin_dict(thirdPartyPluginName) finish_sys_template(sysTemplate, isCreated, templateParams, plugins) @@ -790,7 +796,7 @@ def add_or_update_rna_system_templates(): plugins = {} thirdPartyPluginName = "PartekFlowUploader" - plugins[thirdPartyPluginName] = _get_third_party_plugin_dict(pluginId=9999, pluginName=thirdPartyPluginName) + plugins[thirdPartyPluginName] = _get_plugin_dict(thirdPartyPluginName) finish_sys_template(sysTemplate, isCreated, templateParams, plugins) @@ -807,25 +813,10 @@ def add_or_update_rna_system_templates(): # pre-select plugins plugins = {} - pluginUserInput = {} - - selectedPlugins = models.Plugin.objects.filter( - name__in=["RNASeqAnalysis"], selected=True, active=True).order_by("-id") - if len(selectedPlugins) > 0: - selectedPlugin = selectedPlugins[0] - - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict + plugins["RNASeqAnalysis"] = _get_plugin_dict("RNASeqAnalysis") thirdPartyPluginName = "PartekFlowUploader" - plugins[thirdPartyPluginName] = _get_third_party_plugin_dict(pluginId=9999, pluginName=thirdPartyPluginName) + plugins[thirdPartyPluginName] = _get_plugin_dict(thirdPartyPluginName) finish_sys_template(sysTemplate, isCreated, templateParams, plugins) @@ -864,7 +855,8 @@ def add_or_update_metagenomics_system_templates(): "templatingKitName": "Ion PGM Template OT2 400 Kit", "sampleGrouping": "Self", "sequencekitname": "IonPGM400Kit", - "libraryKitName": "IonPlusFragmentLibKit" + "libraryKitName": "IonPlusFragmentLibKit", + "categories": "16s" }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams) @@ -872,6 +864,7 @@ def add_or_update_metagenomics_system_templates(): def add_or_update_oncomine_system_templates(): # Oncomine + CATEGORIES = "Oncomine;onco_solidTumor" # 22 templateParams = TemplateParams("Oncomine Comprehensive DNA", PGM, "AMPS") templateParams.update({ @@ -879,7 +872,7 @@ def add_or_update_oncomine_system_templates(): "flows": 400, "controlSequencekitname": "Ion AmpliSeq Sample ID Panel", "sampleGrouping": "Self", - "categories": "Oncomine", + "categories": CATEGORIES, "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "barcodeKitName": "IonXpress", "reference": "hg19", @@ -895,7 +888,7 @@ def add_or_update_oncomine_system_templates(): "flows": 400, "controlSequencekitname": "Ion AmpliSeq Sample ID Panel", "sampleGrouping": "Self", - "categories": "Oncomine", + "categories": CATEGORIES, "libraryKitName": "Ion AmpliSeq RNA Library Kit", "barcodeKitName": "IonXpress", }) @@ -910,38 +903,22 @@ def add_or_update_oncomine_system_templates(): "flows": 400, "controlSequencekitname": "Ion AmpliSeq Sample ID Panel", "sampleGrouping": "DNA and Fusions", - "categories": "Oncomine", + "categories": CATEGORIES, "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "barcodeKitName": "IonXpress", "reference": "hg19", - "targetRegionBedFile": "/hg19/unmerged/detail/OCP3.20140718.designed.bed", - "hotSpotRegionBedFile":"/hg19/unmerged/detail/OCP3.20140611.hotspots.blist.bed" }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) # pre-select plugins plugins = {} - pluginUserInput = {} - - selectedPlugins = models.Plugin.objects.filter( - name__in=["coverageAnalysis"], selected=True, active=True).order_by("-id") - if len(selectedPlugins) > 0: - selectedPlugin = selectedPlugins[0] - - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict + plugins["coverageAnalysis"] = _get_plugin_dict("coverageAnalysis") finish_sys_template(sysTemplate, isCreated, templateParams, plugins) def add_or_update_onconet_system_templates(): # OncoNetwork + CATEGORIES = "Onconet;onco_solidTumor" # 25 templateParams = TemplateParams("Ion AmpliSeq Colon and Lung Cancer Panel v2", PGM, "AMPS") templateParams.update({ @@ -949,7 +926,7 @@ def add_or_update_onconet_system_templates(): "flows": 400, "controlSequencekitname": "Ion AmpliSeq Sample ID Panel", "sampleGrouping": "Self", - "categories": "Onconet", + "categories": CATEGORIES, "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "barcodeKitName": "IonXpress", "reference": "hg19", @@ -965,7 +942,7 @@ def add_or_update_onconet_system_templates(): "flows": 400, "controlSequencekitname": "Ion AmpliSeq Sample ID Panel", "sampleGrouping": "Self", - "categories": "Onconet", + "categories": CATEGORIES, "libraryKitName": "Ion AmpliSeq RNA Library Kit", "barcodeKitName": "IonXpress", }) @@ -980,7 +957,7 @@ def add_or_update_onconet_system_templates(): "flows": 400, "controlSequencekitname": "Ion AmpliSeq Sample ID Panel", "sampleGrouping": "DNA and Fusions", - "categories": "Onconet", + "categories": CATEGORIES, "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", "barcodeKitName": "IonXpress", "reference": "hg19" @@ -1004,37 +981,13 @@ def add_or_update_onconet_system_templates(): # pre-select plugins plugins = {} - pluginUserInput = {} - - selectedPlugins = models.Plugin.objects.filter( - name__in=["ampliSeqRNA"], selected=True, active=True) - - for selectedPlugin in selectedPlugins: - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict - - if len(selectedPlugins) < 1: - ionCommunityPluginDict = { - "id": 9999, - "name": "ampliSeqRNA", - "version": "1.0", - "userInput": pluginUserInput, - "features": [] - } - - plugins["ampliSeqRNA"] = ionCommunityPluginDict + plugins["ampliSeqRNA"] = _get_plugin_dict("ampliSeqRNA") finish_sys_template(sysTemplate, isCreated, templateParams, plugins) def add_or_update_ocp_focus_system_templates(): + CATEGORIES = "Oncomine;onco_solidTumor" # OCP Focus OCP_FOCUS_SEQ_KIT_NAME = "IonPGMSelectKit" OCP_FOCUS_LIB_KIT_NAME = "Ion PGM Select Library Kit" @@ -1047,7 +1000,7 @@ def add_or_update_ocp_focus_system_templates(): "flows": 400, "templatingKitName": OCP_FOCUS_TEMPLATE_KIT_NAME, "sampleGrouping": "Self", - "categories": "Oncomine", + "categories": CATEGORIES, "sequencekitname": OCP_FOCUS_SEQ_KIT_NAME, "libraryKitName": OCP_FOCUS_LIB_KIT_NAME, "barcodeKitName": OCP_FOCUS_BARCODE_KIT_NAME, @@ -1064,7 +1017,7 @@ def add_or_update_ocp_focus_system_templates(): "flows": 400, "templatingKitName": OCP_FOCUS_TEMPLATE_KIT_NAME, "sampleGrouping": "Self", - "categories": "Oncomine", + "categories": CATEGORIES, "sequencekitname": OCP_FOCUS_SEQ_KIT_NAME, "libraryKitName": OCP_FOCUS_LIB_KIT_NAME, "barcodeKitName": OCP_FOCUS_BARCODE_KIT_NAME @@ -1080,7 +1033,7 @@ def add_or_update_ocp_focus_system_templates(): "flows": 400, "templatingKitName": OCP_FOCUS_TEMPLATE_KIT_NAME, "sampleGrouping": "DNA and Fusions", - "categories": "Oncomine", + "categories": CATEGORIES, "sequencekitname": OCP_FOCUS_SEQ_KIT_NAME, "libraryKitName": OCP_FOCUS_LIB_KIT_NAME, "barcodeKitName": OCP_FOCUS_BARCODE_KIT_NAME, @@ -1093,38 +1046,78 @@ def add_or_update_ocp_focus_system_templates(): def add_or_update_reproseq_system_templates(): # ReproSeq DEFAULT_P1_3_PRIME_ADAPTER_SEQUENCE = "ATCACCGACTGCCCATAGAGAGGAAAGCGG" + BARCODE_KIT_PGM = "Ion SingleSeq Barcode set 1-24" + BARCODE_KIT_S5 = "Ion SingleSeq Barcode set 1-96" + CATEGORIES = "repro" + LIBRARY_KIT = "IonPicoPlex" + LIBRARY_READ_LENGTH = 0 + REFERENCE = "hg19" + RUN_TYPE = "WGNM" + + # pre-select plugins + plugins = {} + plugins["FilterDuplicates"] = _get_plugin_dict("FilterDuplicates") + # 32 - templateParams = TemplateParams("Ion ReproSeq Aneuploidy", PGM, "WGNM") + templateParams = TemplateParams("Ion ReproSeq Aneuploidy - Ion PGM System", PGM, RUN_TYPE) templateParams.update({ "chipType": "318", "flows": 250, "templatingKitName": "Ion PGM Template IA Tech Access Kit", "sampleGrouping": "Self", "sequencekitname": "IonPGMHiQ", - "libraryKitName": "IonPicoPlex", - "barcodeKitName": "Ion SingleSeq Barcode set 1", - "threePrimeAdapter": DEFAULT_P1_3_PRIME_ADAPTER_SEQUENCE + "libraryKitName": LIBRARY_KIT, + "barcodeKitName": BARCODE_KIT_PGM, + "reference": REFERENCE, + "threePrimeAdapter": DEFAULT_P1_3_PRIME_ADAPTER_SEQUENCE, + "categories": CATEGORIES }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) - finish_sys_template(sysTemplate, isCreated, templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + #TODO- need finalized barcodeKit # 33 is S5 System Default Template + flowOrderList = models.FlowOrder.objects.filter(name="Ion samba.contradanza") + PGS_S5_FLOWORDER = flowOrderList[0].flowOrder if flowOrderList else "" + + threePrimeAdapterList = models.ThreePrimeadapter.objects.filter(name = "Ion P1B") + PGS_S5_3PrimeAdapter = threePrimeAdapterList[0].sequence if threePrimeAdapterList else "" + + templateParams = TemplateParams("Ion ReproSeq Aneuploidy - Ion S5 System", S5, RUN_TYPE) + templateParams.update({ + "chipType": "530", + "flows": 250, + "flowOrder" : PGS_S5_FLOWORDER, + "templatingKitName": "Ion Chef PGS V1", + "sampleGrouping": "Self", + "sequencekitname": "Ion S5 ExT Sequencing Kit", + "libraryKitName": LIBRARY_KIT, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "barcodeKitName": BARCODE_KIT_S5, + "reference": REFERENCE, + "threePrimeAdapter": PGS_S5_3PrimeAdapter, + "categories": CATEGORIES + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) def add_or_update_tagseq_system_templates(): - LIQUID_BIOPSY_PLUGIN_CONFIG_FILENAME = "rundb/fixtures/systemtemplateparams/tagseq_cfdna_lowstringency_parameters.json" - TUMOR_PLUGIN_CONFIG_FILENAME = "rundb/fixtures/systemtemplateparams/tagseq_ffpe_lowstringency_parameters.json" + LIQUID_BIOPSY_PLUGIN_CONFIG_FILENAME = "rundb/fixtures/systemtemplateparams/tagseq_liquidbiopsy_parameters.json" + TUMOR_PLUGIN_CONFIG_FILENAME = "rundb/fixtures/systemtemplateparams/tagseq_tumor_parameters.json" # 34 Lung Liquid Biopsy DNA sysTemplate, isCreated, isUpdated, templateParams = add_or_update_tagseq_system_template("Oncomine Lung Liquid Biopsy DNA") liquid_biopsy_plugins = create_tagseq_plugins(LIQUID_BIOPSY_PLUGIN_CONFIG_FILENAME) + templateParams.selectedPlugins = liquid_biopsy_plugins finish_sys_template(sysTemplate, isCreated, templateParams, liquid_biopsy_plugins) # 35 Lung Tumor DNA sysTemplate, isCreated, isUpdated, templateParams = add_or_update_tagseq_system_template("Oncomine Lung Tumor DNA") tumor_plugins = create_tagseq_plugins(TUMOR_PLUGIN_CONFIG_FILENAME) + templateParams.selectedPlugins = tumor_plugins finish_sys_template(sysTemplate, isCreated, templateParams, tumor_plugins) @@ -1152,7 +1145,7 @@ def add_or_update_tagseq_system_templates(): def add_or_update_tagseq_system_template(templateName): # Tag Sequencing TAG_SEQ_APPLICATION_GROUP = "onco_liquidBiopsy" - TAG_SEQ_BARCODE_KIT_NAME = "IonCode - TagSequencing" + TAG_SEQ_BARCODE_KIT_NAME = "TagSequencing" TAG_SEQ_CATEGORIES = "barcodes_8" TAG_SEQ_CHIP_NAME = "530" TAG_SEQ_FLOWS = 500 @@ -1193,19 +1186,7 @@ def create_tagseq_plugins(configFileName = None): if data: pluginUserInput = data - selectedPlugins = models.Plugin.objects.filter( - name__in=["variantCaller"], selected=True, active=True) - - for selectedPlugin in selectedPlugins: - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict + plugins["variantCaller"] = _get_plugin_dict("variantCaller", pluginUserInput) return plugins @@ -1213,7 +1194,7 @@ def create_tagseq_plugins(configFileName = None): def add_or_update_oncomine_ocav1_system_templates(): # OCAv1 OCAV1_BARCODE_KIT_NAME = "IonXpress" - OCAV1_CATEGORIES = "Oncomine" + OCAV1_CATEGORIES = "Oncomine;onco_solidTumor" OCAV1_CHIP_NAME = "540" OCAV1_FLOWS = 400 OCAV1_LIB_KIT_NAME = "Ion AmpliSeq 2.0 Library Kit" @@ -1262,7 +1243,7 @@ def add_or_update_oncomine_ocav1_system_templates(): def add_or_update_oncomine_ocav2_system_templates(): # OCAv2 OCAV2_BARCODE_KIT_NAME = "IonXpress" - OCAV2_CATEGORIES = "Oncomine;ocav2" + OCAV2_CATEGORIES = "Oncomine;ocav2;onco_solidTumor" OCAV2_CHIP_NAME = "540" OCAV2_FLOWS = 400 OCAV2_LIB_KIT_NAME = "Ion AmpliSeq 2.0 Library Kit" @@ -1273,23 +1254,8 @@ def add_or_update_oncomine_ocav2_system_templates(): # pre-select plugins plugins = {} - pluginUserInput = {} - - selectedPlugins = models.Plugin.objects.filter( - name__in=["coverageAnalysis"], selected=True, active=True) - - for selectedPlugin in selectedPlugins: - - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict - + plugins["coverageAnalysis"] = _get_plugin_dict("coverageAnalysis") + # 42 Oncomine Comprehensive v2 DNA and Fusions for S5 templateParams = TemplateParams("Oncomine Comprehensive v2 DNA and Fusions for S5", S5, "AMPS_DNA_RNA") templateParams.update({ @@ -1312,7 +1278,8 @@ def add_or_update_oncomine_ocav2_system_templates(): def add_or_update_oncomine_ocav3_system_templates(): # OCAv3 OCAV3_BARCODE_KIT_NAME = "IonXpress" - OCAV3_CATEGORIES = "Oncomine" + OCAV3_CATEGORIES = "Oncomine;onco_solidTumor" + OCAV3_CATEGORIES_2 = "Oncomine;barcodes_16;onco_solidTumor" OCAV3_CHIP_NAME = "540" OCAV3_FLOWS = 400 OCAV3_FUSION_LIB_KIT_NAME = "Ion AmpliSeq Library Kit Plus" @@ -1321,26 +1288,11 @@ def add_or_update_oncomine_ocav3_system_templates(): OCAV3_REFERENCE = "hg19" OCAV3_SEQ_KIT_NAME = "Ion S5 Sequencing Kit" OCAV3_TEMPLATE_KIT_NAME = "Ion Chef S540 V1" - OCAV3_STATUS = "inactive" + OCAV3_STATUS = "planned" # pre-select plugins plugins = {} - pluginUserInput = {} - - selectedPlugins = models.Plugin.objects.filter( - name__in=["coverageAnalysis"], selected=True, active=True) - - for selectedPlugin in selectedPlugins: - - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict + plugins["coverageAnalysis"] = _get_plugin_dict("coverageAnalysis") # 43 Oncomine Comprehensive v3 DNA templateParams = TemplateParams("Oncomine Comprehensive v3 DNA", S5, "AMPS") @@ -1361,12 +1313,12 @@ def add_or_update_oncomine_ocav3_system_templates(): sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams, plugins) - # 44 Oncomine Comprehensive v3 DNA and Fusion ST - templateParams = TemplateParams("Oncomine Comprehensive v3 DNA and Fusion ST", S5, "AMPS_DNA_RNA") + # 44 Oncomine Comprehensive v3 DNA and Fusions + templateParams = TemplateParams("Oncomine Comprehensive v3 DNA and Fusions", S5, "AMPS_DNA_RNA") templateParams.update({ "applicationGroup": "DNA + RNA", "barcodeKitName": OCAV3_BARCODE_KIT_NAME, - "categories": OCAV3_CATEGORIES, + "categories": OCAV3_CATEGORIES_2, "chipType": OCAV3_CHIP_NAME, "flows": OCAV3_FLOWS, "libraryKitName": OCAV3_LIB_KIT_NAME, @@ -1380,8 +1332,8 @@ def add_or_update_oncomine_ocav3_system_templates(): sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams, plugins) - # 45 Oncomine Fusion ST - templateParams = TemplateParams("Oncomine Fusion ST", S5, "AMPS_RNA") + # 45 Oncomine Comprehensive v3 Fusions + templateParams = TemplateParams("Oncomine Comprehensive v3 Fusions", S5, "AMPS_RNA") templateParams.update({ "applicationGroup": "DNA + RNA", "barcodeKitName": OCAV3_BARCODE_KIT_NAME, @@ -1403,7 +1355,7 @@ def add_or_update_oncomine_ocav3_system_templates(): def add_or_update_oncomine_pediatric_system_templates(): # pediatric BARCODE_KIT_NAME = "IonXpress" - CATEGORIES = "Oncomine" + CATEGORIES = "onco_solidTumor;onco_heme" CHIP_NAME = "540" FLOWS = 400 FUSION_LIB_KIT_NAME = "Ion AmpliSeq RNA Library Kit" @@ -1474,19 +1426,19 @@ def add_or_update_oncomine_pediatric_system_templates(): def add_or_update_oncomine_BRCA_system_templates(): # BRCA - BARCODE_KIT_NAME = "IonCode Barcodes 1-32" - CATEGORIES = "Oncomine" + BARCODE_KIT_NAME = "IonXpress" + CATEGORIES = "Oncomine;onco_solidTumor;inheritedDisease" CHIP_NAME_PGM = "318" CHIP_NAME_S5 = "530" FLOWS = 500 - LIB_KIT_NAME = "Ampliseq DNA V1" + LIB_KIT_NAME = "Ion AmpliSeq Library Kit Plus" LIBRARY_READ_LENGTH = 200 REFERENCE = "hg19" SEQ_KIT_NAME_PGM = "IonPGMHiQ" SEQ_KIT_NAME_S5 = "Ion S5 Sequencing Kit" TEMPLATE_KIT_NAME_PGM = "Ion PGM Hi-Q View Chef Kit" TEMPLATE_KIT_NAME_S5 = "Ion Chef S530 V1" - + # 49 Oncomine BRCA for PGM templateParams = TemplateParams("Oncomine BRCA Research for PGM", PGM, "AMPS") templateParams.update({ @@ -1525,18 +1477,24 @@ def add_or_update_oncomine_BRCA_system_templates(): def add_or_update_immune_response_system_templates(): - PLAN_STATUS = "inactive" - - # 51 Immune Response Panel - templateParams = TemplateParams("Immune Response Panel", S5, "AMPS_RNA") + PLAN_STATUS = "planned" + CATEGORIES = "onco_immune" + PLUGIN = "immuneResponseRNA" + REFERENCE = "ImmuneResponse_v3.1" + BEDFILE = "/%s/unmerged/detail/ImmuneResponse_v3.1_target_designed_20160908.bed" % REFERENCE + + # 51 Immune Response Panel S5 + templateParams = TemplateParams("Oncomine Immune Response Research Assay for S5 with Chef", S5, "AMPS_RNA") templateParams.update({ "applicationGroup": "RNA", - "barcodeKitName": "IonXpress", + "barcodeKitName": "IonCode Barcodes 1-32", + "categories": CATEGORIES, "chipType": "530", "flows": 500, - "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", + "libraryKitName": "Ampliseq DNA V1", "libraryReadLength" : 200, - "reference": "hg19_rna_ImmuneResponsePanelv2", + "reference": REFERENCE, + "targetRegionBedFile": BEDFILE, "sampleGrouping": "Self", "sequencekitname": "Ion S5 Sequencing Kit", "templatingKitName": "Ion Chef S530 V1", @@ -1547,30 +1505,32 @@ def add_or_update_immune_response_system_templates(): # pre-select plugins plugins = {} pluginUserInput = {} + plugins[PLUGIN] = _get_plugin_dict(PLUGIN, pluginUserInput) - pluginName = "immuneResponseRNA" - selectedPlugins = models.Plugin.objects.filter( - name__in=[pluginName], selected=True, active=True).order_by("-id") - if len(selectedPlugins) > 0: - selectedPlugin = selectedPlugins[0] - - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - - plugins[selectedPlugin.name] = pluginDict - else: - plugins[pluginName] = _get_third_party_plugin_dict(pluginId=9999, pluginName=pluginName) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + #67 Immune Response Panel PGM + templateParams = TemplateParams("Oncomine Immune Response Research Assay for PGM with OT2", PGM, "AMPS_RNA") + templateParams.update({ + "applicationGroup": "RNA", + "barcodeKitName": "IonXpress", + "categories": CATEGORIES, + "chipType": "318", + "flows": 500, + "libraryKitName": "Ion AmpliSeq 2.0 Library Kit", + "reference": REFERENCE, + "targetRegionBedFile": BEDFILE, + "sequencekitname": "IonPGMHiQView", + "templatingKitName": "Ion PGM Hi-Q View OT2 Kit - 200", + "planStatus" : PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams, plugins) def add_or_update_S5_ocp_focus_system_templates(): # OCP Focus + OCP_S5_FOCUS_CATEGORIES = "Oncomine;onco_solidTumor" OCP_S5_FOCUS_CHIP = "520" OCP_S5_FOCUS_FLOWS = 400 OCP_S5_FOCUS_LIBRARY_KIT_NAME = "Ion AmpliSeq 2.0 Library Kit" @@ -1581,11 +1541,11 @@ def add_or_update_S5_ocp_focus_system_templates(): templateParams = TemplateParams("Oncomine Focus DNA for S5", S5, "AMPS") templateParams.update({ "chipType": OCP_S5_FOCUS_CHIP, + "categories": OCP_S5_FOCUS_CATEGORIES, "flows": OCP_S5_FOCUS_FLOWS, "libraryKitName": OCP_S5_FOCUS_LIBRARY_KIT_NAME, "templatingKitName": OCP_S5_FOCUS_TEMPLATE_KIT_NAME, "sampleGrouping": "Self", - "categories": "Oncomine", "barcodeKitName": OCP_S5_FOCUS_BARCODE_KIT_NAME, "reference": "hg19" }) @@ -1596,12 +1556,13 @@ def add_or_update_S5_ocp_focus_system_templates(): templateParams = TemplateParams("Oncomine Focus Fusions for S5", S5, "AMPS_RNA") templateParams.update({ "applicationGroup": "DNA + RNA", + "categories": OCP_S5_FOCUS_CATEGORIES, "chipType": OCP_S5_FOCUS_CHIP, "flows": OCP_S5_FOCUS_FLOWS, "libraryKitName": OCP_S5_FOCUS_LIBRARY_KIT_NAME, "templatingKitName": OCP_S5_FOCUS_TEMPLATE_KIT_NAME, "sampleGrouping": "Self", - "categories": "Oncomine", + "categories": OCP_S5_FOCUS_CATEGORIES, "barcodeKitName": OCP_S5_FOCUS_BARCODE_KIT_NAME }) sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) @@ -1611,12 +1572,12 @@ def add_or_update_S5_ocp_focus_system_templates(): templateParams = TemplateParams("Oncomine Focus DNA and Fusions for S5", S5, "AMPS_DNA_RNA") templateParams.update({ "applicationGroup": "DNA + RNA", + "categories": OCP_S5_FOCUS_CATEGORIES, "chipType": OCP_S5_FOCUS_CHIP, "flows": OCP_S5_FOCUS_FLOWS, "libraryKitName": OCP_S5_FOCUS_LIBRARY_KIT_NAME, "templatingKitName": OCP_S5_FOCUS_TEMPLATE_KIT_NAME, "sampleGrouping": "DNA and Fusions", - "categories": "Oncomine", "barcodeKitName": OCP_S5_FOCUS_BARCODE_KIT_NAME, "reference": "hg19" }) @@ -1846,9 +1807,9 @@ def add_or_update_hid_system_templates(): def add_or_update_hid_dexter_system_templates(): # HID - default_flowOrderList = models.FlowOrder.objects.filter(name="Ion samba.gafieira") + default_flowOrderList = models.FlowOrder.objects.filter(name="Ion samba HID2") - BARCODE_KIT_NAME = "IonCode Barcodes 1-32" + BARCODE_KIT_NAME = "IonCode" CATEGORIES = "" CHIP_NAME = "530" FLOWS = 650 @@ -1857,7 +1818,7 @@ def add_or_update_hid_dexter_system_templates(): LIBRARY_READ_LENGTH = 200 REFERENCE = "hg19" SAMPLE_PREP_PROTOCOL = "" - SEQ_KIT_NAME = "HID S5 Sequencing Kit" + SEQ_KIT_NAME = "precisionIDS5Kit" TEMPLATE_KIT_NAME = "Ion Chef HID S530 V2" TEMPLATING_SIZE = None @@ -1903,6 +1864,456 @@ def add_or_update_hid_dexter_system_templates(): sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) finish_sys_template(sysTemplate, isCreated, templateParams) + +def add_or_update_ocp_myeloid_pgm_system_templates(): + BARCODE_KIT_NAME = "IonXpress" + CATEGORIES_DNA = "barcodes_6;onco_heme" + CATEGORIES_RNA_FUSIONS = "barcodes_24;onco_heme" + CATEGORIES_DNA_n_FUSIONS = "barcodes_12;onco_heme" + CHIP_NAME = "318" + FLOWS = 850 + LIB_KIT_NAME = "Ion AmpliSeq Library Kit Plus" + LIBRARY_READ_LENGTH = 400 + REFERENCE = "hg19" + SAMPLE_GROUPING = "Self" + SAMPLE_PREP_PROTOCOL = "" + SEQ_KIT_NAME = "IonPGMHiQView" + TEMPLATE_KIT_NAME = "Ion PGM Hi-Q View Chef Kit" + ##TEMPLATING_SIZE = "400" + PLAN_STATUS = "inactive" + + # pre-select plugins + plugins = {} + plugins["coverageAnalysis"] = _get_plugin_dict("coverageAnalysis") + + # 67 + templateParams = TemplateParams("AmpliSeq Myeloid Research DNA for PGM", PGM, "AMPS") + templateParams.update({ + "applicationGroup" : "DNA", + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES_DNA, + "chipType": CHIP_NAME, + "flows": FLOWS, + "libraryKitName": LIB_KIT_NAME, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + # 68 + templateParams = TemplateParams("AmpliSeq Myeloid Research Fusions for PGM", PGM, "AMPS_RNA") + templateParams.update({ + "applicationGroup": "DNA + RNA", + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES_RNA_FUSIONS, + "chipType": CHIP_NAME, + "flows": FLOWS, + "libraryKitName": LIB_KIT_NAME, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + # 69 + templateParams = TemplateParams("AmpliSeq Myeloid Research DNA and Fusions for PGM", PGM, "AMPS_DNA_RNA") + templateParams.update({ + "applicationGroup": "DNA + RNA", + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES_DNA_n_FUSIONS, + "chipType": CHIP_NAME, + "flows": FLOWS, + "libraryKitName": LIB_KIT_NAME, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": "DNA and Fusions", + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + +def add_or_update_ocp_myeloid_s5_system_templates(): + BARCODE_KIT_NAME = "IonXpress" + CATEGORIES_DNA = "barcodes_12;onco_heme;chef_myeloid_protocol" + CATEGORIES_RNA_FUSIONS = "barcodes_48;onco_heme;chef_myeloid_protocol" + CATEGORIES_DNA_n_FUSIONS = "barcodes_24;onco_heme;chef_myeloid_protocol" + CHIP_NAME = "530" + FLOWS = 850 + LIB_KIT_NAME = "Ion AmpliSeq Library Kit Plus" + LIBRARY_READ_LENGTH = 400 + REFERENCE = "hg19" + SAMPLE_GROUPING = "Self" + SAMPLE_PREP_PROTOCOL = "denature30_cycles45_20" + SEQ_KIT_NAME = "Ion S5 Sequencing Kit" + TEMPLATE_KIT_NAME = "Ion Chef S530 V2" + ##TEMPLATING_SIZE = "400" + PLAN_STATUS = "inactive" + + # pre-select plugins + plugins = {} + plugins["coverageAnalysis"] = _get_plugin_dict("coverageAnalysis") + + # 70 + templateParams = TemplateParams("AmpliSeq Myeloid Research DNA for S5", S5, "AMPS") + templateParams.update({ + "applicationGroup" : "DNA", + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES_DNA, + "chipType": CHIP_NAME, + "flows": FLOWS, + "libraryKitName": LIB_KIT_NAME, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "samplePrepProtocol": SAMPLE_PREP_PROTOCOL, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + # 71 + templateParams = TemplateParams("AmpliSeq Myeloid Research Fusions for S5", S5, "AMPS_RNA") + templateParams.update({ + "applicationGroup": "DNA + RNA", + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES_RNA_FUSIONS, + "chipType": CHIP_NAME, + "flows": FLOWS, + "libraryKitName": LIB_KIT_NAME, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "samplePrepProtocol": SAMPLE_PREP_PROTOCOL, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + # 72 + templateParams = TemplateParams("AmpliSeq Myeloid Research DNA and Fusions for S5", S5, "AMPS_DNA_RNA") + templateParams.update({ + "applicationGroup": "DNA + RNA", + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES_DNA_n_FUSIONS, + "chipType": CHIP_NAME, + "flows": FLOWS, + "libraryKitName": LIB_KIT_NAME, + "libraryReadLength" : LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": "DNA and Fusions", + "samplePrepProtocol": SAMPLE_PREP_PROTOCOL, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + +def add_or_update_proton_PQ_system_template(): + CATEGORIES = "" + CHIP = "P2.2.2" + FLOWS = 150 + LIBRARY_KIT_NAME = "Ion Xpress Plus Fragment Library Kit" + TEMPLATE_KIT_NAME = "Ion PQ Template OT2 Kit" + SEQ_KIT_NAME = "IonProtonPQKit" + BARCODE_KIT_NAME = "IonXpress" + PLAN_STATUS = "inactive" + + # 73 + templateParams = TemplateParams("Ion NIPT template - PQ", PROTON, "WGNM") + templateParams.update({ + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME, + "reference": "hg19", + "sampleGrouping": "Self", + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams) + + +def add_or_update_mouse_transcriptome_s5_system_templates(): + APPLICATION_GROUP = "RNA" + BARCODE_KIT_NAME = "IonCode Barcodes 1-32" + BARCODE_KIT_NAME_MANUAL = "IonXpress" + CATEGORIES = "" + CHIP = "540" + FLOWS = 500 + LIBRARY_KIT_NAME = "Ampliseq DNA V1" + LIBRARY_KIT_NAME_MANUAL = "Ion AmpliSeq Library Kit Plus" + LIBRARY_READ_LENGTH = 200 + REFERENCE = "mm10" + SAMPLE_GROUPING = "Self" + SEQ_KIT_NAME = "Ion S5 Sequencing Kit" + TEMPLATE_KIT_NAME = "Ion Chef S540 V1" + PLAN_STATUS = "inactive" + + # pre-select plugins + plugins = {} + plugins["ampliSeqRNA"] = _get_plugin_dict("ampliSeqRNA") + + # 74 + templateParams = TemplateParams("Ion AmpliSeq Transcriptome Mouse Gene Expression Chef-S5", S5, "AMPS_RNA") + templateParams.update({ + "applicationGroup": APPLICATION_GROUP, + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME, + "libraryReadLength": LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + # 75 + templateParams = TemplateParams("Ion AmpliSeq Transcriptome Mouse Gene Expression Manual Chef-S5", S5, "AMPS_RNA") + templateParams.update({ + "applicationGroup": APPLICATION_GROUP, + "barcodeKitName": BARCODE_KIT_NAME_MANUAL, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME_MANUAL, + "libraryReadLength": LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + +def add_or_update_mouse_transcriptome_proton_system_templates(): + APPLICATION_GROUP = "RNA" + BARCODE_KIT_NAME = "IonXpress" + CATEGORIES = "" + CHIP = "P1.1.17" + FLOWS = 500 + LIBRARY_KIT_NAME = "Ion AmpliSeq Library Kit Plus" + REFERENCE = "mm10" + SAMPLE_GROUPING = "Self" + SEQ_KIT_NAME = " ProtonI200Kit-v3" + TEMPLATE_KIT_NAME = "Ion PI Template OT2 200 Kit v3" + PLAN_STATUS = "inactive" + + # pre-select plugins + plugins = {} + plugins["ampliSeqRNA"] = _get_plugin_dict("ampliSeqRNA") + + # 76 + templateParams = TemplateParams("Ion AmpliSeq Transcriptome Mouse Gene Expression Panel OT2-Proton", PROTON, "AMPS_RNA") + templateParams.update({ + "applicationGroup": APPLICATION_GROUP, + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + +def add_or_update_mutation_load_s5_system_templates(): + APPLICATION_GROUP = "DNA" + BARCODE_KIT_NAME = "IonCode Barcodes 1-32" + CATEGORIES = "onco_immune" + CHIP = "540" + FLOWS = 400 + LIBRARY_KIT_NAME = "Ion AmpliSeq Library Kit Plus" + LIBRARY_READ_LENGTH = 200 + REFERENCE = "hg19" + SAMPLE_GROUPING = "Self" + SEQ_KIT_NAME = "Ion S5 Sequencing Kit" + TEMPLATE_KIT_NAME = "Ion Chef S540 V1" + PLAN_STATUS = "inactive" + + # 77 + templateParams = TemplateParams("Oncomine Mutation Load", S5, "AMPS") + templateParams.update({ + "applicationGroup": APPLICATION_GROUP, + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME, + "libraryReadLength": LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams) + + +def add_or_update_tagseq_cfdna_system_templates(): + LIQUID_BIOPSY_PLUGIN_CONFIG_FILENAME = "rundb/fixtures/systemtemplateparams/tagseq_liquidbiopsy_parameters.json" + TUMOR_PLUGIN_CONFIG_FILENAME = "rundb/fixtures/systemtemplateparams/tagseq_tumor_parameters.json" + + # 78 + sysTemplate, isCreated, isUpdated, templateParams = add_or_update_tagseq_cfdna_s5_chef_system_template("Oncomine TagSeq Tumor") + tumor_plugins = create_tagseq_plugins(TUMOR_PLUGIN_CONFIG_FILENAME) + templateParams.selectedPlugins = tumor_plugins + finish_sys_template(sysTemplate, isCreated, templateParams, tumor_plugins) + + # 79 + sysTemplate, isCreated, isUpdated, templateParams = add_or_update_tagseq_cfdna_s5_chef_system_template("Oncomine TagSeq Liquid Biopsy") + liquid_biopsy_plugins = create_tagseq_plugins(LIQUID_BIOPSY_PLUGIN_CONFIG_FILENAME) + templateParams.selectedPlugins = liquid_biopsy_plugins + finish_sys_template(sysTemplate, isCreated, templateParams, liquid_biopsy_plugins) + + + +def add_or_update_tagseq_cfdna_s5_chef_system_template(templateName): + # Tag Sequencing + TAG_SEQ_APPLICATION_GROUP = "onco_liquidBiopsy" + TAG_SEQ_BARCODE_KIT_NAME = "TagSequencing" + TAG_SEQ_CATEGORIES = "barcodes_8" + TAG_SEQ_CHIP_NAME = "530" + TAG_SEQ_FLOWS = 500 + TAG_SEQ_LIB_KIT_NAME = "Oncomine cfDNA Assay" + TAG_SEQ_LIBRARY_READ_LENGTH = 200 + TAG_SEQ_REFERENCE = "hg19" + TAG_SEQ_RUN_TYPE = "TAG_SEQUENCING" + TAG_SEQ_SAMPLE_GROUPING = "Self" + TAG_SEQ_SEQ_KIT_NAME = "Ion S5 Sequencing Kit" + TAG_SEQ_TEMPLATE_KIT_NAME = "Ion Chef S530 V1" + PLAN_STATUS = "planned" + + templateParams = TemplateParams(templateName, S5, TAG_SEQ_RUN_TYPE) + templateParams.update({ + "applicationGroup" : TAG_SEQ_APPLICATION_GROUP, + "barcodeKitName": TAG_SEQ_BARCODE_KIT_NAME, + "categories": TAG_SEQ_CATEGORIES, + "chipType": TAG_SEQ_CHIP_NAME, + "flows": TAG_SEQ_FLOWS, + "libraryKitName": TAG_SEQ_LIB_KIT_NAME, + "libraryReadLength" : TAG_SEQ_LIBRARY_READ_LENGTH, + "reference": TAG_SEQ_REFERENCE, + "sampleGrouping": TAG_SEQ_SAMPLE_GROUPING, + "sequencekitname": TAG_SEQ_SEQ_KIT_NAME, + "templatingKitName": TAG_SEQ_TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + return sysTemplate, isCreated, isUpdated, templateParams + + +def add_or_update_immune_repertoire_s5_system_templates(): + APPLICATION_GROUP = "immune_repertoire" + BARCODE_KIT_NAME = "IonXpress" + CATEGORIES = "onco_immune" + CHIP = "530" + FLOWS = 800 + LIBRARY_KIT_NAME = "Ion AmpliSeq Library Kit Plus" + LIBRARY_READ_LENGTH = 400 + REFERENCE = "" + SAMPLE_GROUPING = "Self" + SEQ_KIT_NAME = "Ion S5 Sequencing Kit" + TEMPLATE_KIT_NAME = "Ion Chef S530 V2" + PLAN_STATUS = "inactive" + + # pre-select plugins + plugins = {} + plugins["TCR_Repertoire"] = _get_plugin_dict("TCR_Repertoire") + + # 80 + templateParams = TemplateParams("Oncomine Immune Repertoire TCRB Assay - S5", S5, "AMPS_RNA") + templateParams.update({ + "applicationGroup": APPLICATION_GROUP, + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME, + "libraryReadLength": LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + +def add_or_update_immune_repertoire_pgm_system_templates(): + APPLICATION_GROUP = "immune_repertoire" + BARCODE_KIT_NAME = "IonXpress" + CATEGORIES = "onco_immune" + CHIP = "318" + FLOWS = 800 + LIBRARY_KIT_NAME = "Ion AmpliSeq Library Kit Plus" + LIBRARY_READ_LENGTH = 400 + REFERENCE = "" + SAMPLE_GROUPING = "Self" + SEQ_KIT_NAME = "IonPGMHiQView" + TEMPLATE_KIT_NAME = "Ion PGM Hi-Q View Chef Kit" + PLAN_STATUS = "inactive" + + # pre-select plugins + plugins = {} + plugins["TCR_Repertoire"] = _get_plugin_dict("TCR_Repertoire") + + # 81 + templateParams = TemplateParams("Oncomine Immune Repertoire TCRB Assay - PGM", PGM, "AMPS_RNA") + templateParams.update({ + "applicationGroup": APPLICATION_GROUP, + "barcodeKitName": BARCODE_KIT_NAME, + "categories": CATEGORIES, + "chipType": CHIP, + "flows": FLOWS, + "libraryKitName": LIBRARY_KIT_NAME, + "libraryReadLength": LIBRARY_READ_LENGTH, + "reference": REFERENCE, + "sampleGrouping": SAMPLE_GROUPING, + "sequencekitname": SEQ_KIT_NAME, + "templatingKitName": TEMPLATE_KIT_NAME, + "planStatus": PLAN_STATUS + }) + + sysTemplate, isCreated, isUpdated = add_or_update_sys_template(templateParams) + finish_sys_template(sysTemplate, isCreated, templateParams, plugins) + + @transaction.commit_manually() def add_or_update_all_system_templates(): @@ -1929,6 +2340,15 @@ def add_or_update_all_system_templates(): add_or_update_S5_ocp_focus_system_templates() add_or_update_hid_system_templates() add_or_update_hid_dexter_system_templates() + add_or_update_ocp_myeloid_pgm_system_templates() + add_or_update_ocp_myeloid_s5_system_templates() + add_or_update_proton_PQ_system_template() + add_or_update_mouse_transcriptome_s5_system_templates() + add_or_update_mouse_transcriptome_proton_system_templates() + add_or_update_mutation_load_s5_system_templates() + add_or_update_tagseq_cfdna_system_templates() + add_or_update_immune_repertoire_s5_system_templates() + add_or_update_immune_repertoire_pgm_system_templates() except: print format_exc() transaction.rollback() @@ -1962,7 +2382,25 @@ def clean_up_obsolete_templates(): " Oncomine Comprehensive v2 for S5 DNA and Fusions", "Oncomine BRCA for PGM", "Oncomine BRCA for S5", - "Applied Biosystems Precision ID GlobalFiler NGS STR Panel" + "Applied Biosystems Precision ID GlobalFiler NGS STR Panel", + "Oncomine Comprehensive v3 DNA and Fusion ST", + "Oncomine Fusion ST", + "Ion ReproSeq Aneuploidy", + "Ion AmpliSeq Cancer Panel", + "AmpliSeq Myeloid DNA for PGM", + "AmpliSeq Myeloid Fusions for PGM", + "AmpliSeq Myeloid DNA and Fusions for PGM", + "AmpliSeq Myeloid DNA for S5", + "AmpliSeq Myeloid Fusions for S5", + "AmpliSeq Myeloid DNA and Fusions for S5", + "Ion ReproSeq - PGM", + "Ion ReproSeq - S5", + " Oncomine Lung Liquid Biopsy Total Nucleic Acid", + "Oncomine Mutational Load", + "Oncomine Lung Liquid Biopsy Total Nucleic Acid", + "Oncomine Lung Tumor Total Nucleic Acid", + "Oncomine Breast Liquid Biopsy DNA v2", + "Oncomine Breast Tumor DNA v2" ] templates = models.PlannedExperiment.objects.filter( @@ -2021,38 +2459,21 @@ def add_or_updateSystemTemplate_OffCycleRelease(**sysTemp): def pre_select_plugins(application, pluginsList): plugins = {} - pluginUserInput = {} - - thirdPartyPluginName = "PartekFlowUploader" - thirdPartyPluginDict = _get_third_party_plugin_dict(pluginId=9999, pluginName=thirdPartyPluginName) # pre-select plugins for plugin in pluginsList: - logger.debug("PreSelect plugin(%s) in the System Template " % plugin) - selectedPlugins = models.Plugin.objects.filter( - name__in=[plugin], selected=True, active=True).order_by("-id") - - for selectedPlugin in selectedPlugins: - pluginDict = { - "id": selectedPlugin.id, - "name": selectedPlugin.name, - "version": selectedPlugin.version, - "userInput": pluginUserInput, - "features": [] - } - plugins[selectedPlugin.name] = pluginDict - - if ((plugin == 'ampliSeqRNA') and (len(selectedPlugins) < 1)): - ionCommunityPluginDict = { - "id": 9999, - "name": "ampliSeqRNA", - "version": "1.0", - "userInput": pluginUserInput, - "features": [] - } - plugins["ampliSeqRNA"] = ionCommunityPluginDict + if isinstance(plugin, dict): + pluginName = plugin.get('name') + userInput = plugin.get('userInput') + else: + pluginName = plugin + userInput = {} + + plugins[pluginName] = _get_plugin_dict(pluginName, userInput) + if (application == "AMPS_RNA" or application == "RNA"): - plugins[thirdPartyPluginName] = thirdPartyPluginDict + thirdPartyPluginName = "PartekFlowUploader" + plugins[thirdPartyPluginName] = _get_plugin_dict(thirdPartyPluginName) return plugins diff --git a/dbReports/iondb/bin/check_system_services.py b/dbReports/iondb/bin/check_system_services.py new file mode 100644 index 00000000..9e92e7ec --- /dev/null +++ b/dbReports/iondb/bin/check_system_services.py @@ -0,0 +1,149 @@ +#!/usr/bin/python +# Copyright (C) 2017 Ion Torrent Systems, Inc. All Rights Reserved + +''' + This script can test essential TS system services are running and restart them + Usage: + no option: print status + -n/--notify: update webpage banner and send message to IT contact if any service is down + -s/--start: attempt to (re-)start all services, must be run as root +''' + +import os +import sys +sys.path.append('/opt/ion/') +os.environ['DJANGO_SETTINGS_MODULE'] = 'iondb.settings' + +import subprocess +import traceback +import argparse +from iondb.rundb.models import Message +from iondb.rundb.tasks import notify_services_error + +import logging +logger = logging.getLogger(__name__) + + +def process_status(): + # modified from /rundb/configure/views.py process_set() + def simple_status(name): + proc = subprocess.Popen("service %s status" % name, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + #logger.debug("%s out = '%s' err = %s''" % (name, stdout, stderr)) + return proc.returncode == 0 + + def complicated_status(filename, parse): + try: + if os.path.exists(filename): + data = open(filename).read() + pid = parse(data) + proc = subprocess.Popen("ps %d" % pid, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc.communicate() + return proc.returncode == 0 + except Exception as err: + return False + + proc_set = {} + processes = [ + "ionJobServer", + "ionCrawler", + "ionPlugin", + "apache2", + "postgresql" + ] + + for name in processes: + proc_set[name] = simple_status(name) + + proc_set["RabbitMQ"] = complicated_status("/var/run/rabbitmq/pid", int) + proc_set["gridengine"] = complicated_status("/var/run/gridengine/execd.pid", int) + + for node in ['celerybeat', 'celery_w1', 'celery_plugins', 'celery_periodic', 'celery_slowlane', 'celery_transfer', 'celery_diskutil']: + proc_set[node] = complicated_status("/var/run/celery/%s.pid" % node, int) + + alerts = [] + for process, active in sorted(proc_set.items(), key=lambda s: s[0].lower()): + print 'ok' if active else 'DOWN', process + if not active: + alerts.append(process) + + return alerts + + +def start_service(name): + cmd = "service %s restart" % name + print cmd + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + if proc.returncode: + print 'Error: %s returned %d, %s, %s' % (cmd, proc.returncode, stdout, stderr) + return proc.returncode == 0 + + +def update_banner(alerts): + new = False + # update message banner + message = Message.objects.filter(tags="service_status_alert") + if len(alerts) > 0: + msg = 'ALERT system services are down: %s. ' % ', '.join(alerts) + msg += ' Please contact your system administrator for assistance.' + if not message or message[0].body != msg: + message.delete() + Message.warn(msg, tags="service_status_alert") + new = True + else: + message.delete() + print '...updated message banner' + return new + + +def send_alert(alerts): + # send an email to IT contact + msg = "The following system services are down:\n" + for service in alerts: + msg += service + '\n' + notify_services_error('Torrent Server Alert', msg, msg.replace('\n','
')) + print '...notified IT list' + + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="This script can check and restart essential TS services") + parser.add_argument('-n', '--notify', dest='notify', action='store_true', default=False, help='notify with banner and email alert') + parser.add_argument('-s', '--start', dest='start', action='store_true', default=False, help='attempt to (re-)start all services') + args = parser.parse_args() + + if args.start: + if os.geteuid() != 0: + sys.exit('Run this script with root permissions to start services') + + start_order = ['postgresql', + 'apache2', + 'gridengine-master', 'gridengine-exec', + 'rabbitmq-server', 'celeryd', 'celerybeat', + 'ionJobServer', 'ionCrawler', 'ionPlugin' + ] + for name in start_order: + start_service(name) + + # check status + alerts = process_status() + + if args.notify: + logger.info("check_system_services: %s" % alerts) + + try: + new = update_banner(alerts) + except: + logger.error('check_system_services: unable to update Message banner') + print traceback.format_exc() + else: + # send an email to IT contact + if alerts and new: + try: + send_alert(alerts) + except: + logger.error('check_system_services: unable to send email alert') + print traceback.format_exc() + + diff --git a/dbReports/iondb/bin/crawler.py b/dbReports/iondb/bin/crawler.py index 0bd9c00a..3e7269fb 100755 --- a/dbReports/iondb/bin/crawler.py +++ b/dbReports/iondb/bin/crawler.py @@ -37,9 +37,11 @@ import urllib import argparse -from iondb.bin.djangoinit import * +#from iondb.bin.djangoinit import * +from iondb.bin import djangoinit from django import db from django.db import connection +from django.conf import settings from twisted.internet import reactor from twisted.internet import task diff --git a/dbReports/iondb/bin/dj_config.py b/dbReports/iondb/bin/dj_config.py index c4fc2252..01f5963f 100644 --- a/dbReports/iondb/bin/dj_config.py +++ b/dbReports/iondb/bin/dj_config.py @@ -14,6 +14,7 @@ import os import subprocess +import requests def call(*cmd): @@ -32,3 +33,23 @@ def call(*cmd): def get_tmap_version(): return call("tmap", "index", "--version") + + +def is_s5_tsvm(): + """This method will return true if we are part of the S5 tsvm, false otherwise""" + # I am not super excited to use the existance of such a file as there could be a point + # in time in the future where the 1:1 correlation may change between the existance of + # this file and this TS being a S5 tsvm instance + return os.path.exists('/etc/init.d/mountExternal') + + +def get_s5_ip_addr(): + """get the S5 IP address""" + try: + resp = requests.get('http://192.168.122.1/instrument/software/www/config/DataCollect.config', timeout=2.0) + for line in resp.text.split('\n'): + if 'IP Address Str' in line: + return line[15:] + except requests.ConnectionError: + # we don't really need to do anything here asides from assume this is not an S5 and move on + return None diff --git a/dbReports/iondb/bin/from_wells_analysis.py b/dbReports/iondb/bin/from_wells_analysis.py index 7f269fdb..af8939e4 100755 --- a/dbReports/iondb/bin/from_wells_analysis.py +++ b/dbReports/iondb/bin/from_wells_analysis.py @@ -27,6 +27,7 @@ import logging from iondb.bin.djangoinit import * from iondb.rundb import models +from ion.utils.explogparser import load_log from ion.utils.explogparser import load_log_path from ion.utils.explogparser import parse_log from iondb.bin.crawler import generate_updateruninfo_post @@ -38,6 +39,12 @@ def __init__(self): self.errors = logging.getLogger(__name__) self.errors.propagate = True + def warn(self, msg): + print(msg) + + def error(self, msg): + print(msg) + logger = fakelog() TIMESTAMP_RE = models.Experiment.PRETTY_PRINT_RE @@ -72,7 +79,7 @@ def generate_http_post(exp, data_path, thumbnail_analysis=False): # instead of relying on globalConfig, user can now set isDuplicateReads for the experiment eas = exp.get_EAS() - if (eas): + if eas: # logger.errors.info("from_well_analysis.generate_http_post() exp.name=%s; # id=%s; isDuplicateReads=%s" %(exp.expName, str(exp.pk), # str(eas.isDuplicateReads))) @@ -136,9 +143,9 @@ def newExperiment(_explog_path, _plan_json=''): # Test if Experiment object already exists try: _newExp = models.Experiment.objects.get(unique=folder) - print "DEBUG: Experiment exists in database: %s" % (folder) + print("DEBUG: Experiment exists in database: %s" % folder) except: - print "DEBUG: Experiment does not exist in database" + print("DEBUG: Experiment does not exist in database") _newExp = None if _newExp is None: @@ -156,24 +163,50 @@ def newExperiment(_explog_path, _plan_json=''): # Experiment object exists in database _newExp = exp_set[0] else: - print "ERROR: Could not update/generate new Experiment record in database" - print ret_val + print("ERROR: Could not update/generate new Experiment record in database") + print(ret_val) return None # Append to expName to indicate imported dataset. _newExp.expName += "_foreign" + chefLog_parsed = isChefInfoAvailable(folder) + if chefLog_parsed: + update_chefSummary(_newExp, chefLog_parsed) _newExp.save() if _plan_json: planObj = _newExp.plan easObj = _newExp.get_EAS() update_plan_info(_plan_json, planObj, easObj) except: - print "DEBUG: There was an error adding the experiment" + print("DEBUG: There was an error adding the experiment") _newExp = None - print traceback.format_exc() + print(traceback.format_exc()) return _newExp +def isChefInfoAvailable(folder): + # parse chef_param.json + chefLog_parsed = {} + JSON_BASENAME = "chef_params.json" + chefLog = load_log(folder, JSON_BASENAME) + if chefLog is None: + payload = "Chef summary info not available read %s" % (os.path.join(folder, JSON_BASENAME)) + logger.warn(payload) + else: + try: + chefLog_parsed = json.loads(chefLog) + except: + logger.warn("Error parsing %s, skipping %s" % (JSON_BASENAME, folder)) + logger.error(traceback.format_exc()) + print(traceback.format_exc()) + + return chefLog_parsed + +def update_chefSummary(_newExp, chefSummary): + if chefSummary: + for k, v in chefSummary.items(): + if not v: continue + setattr(_newExp, k, v) def update_plan_info(_plan_json, planObj, easObj): # update Plan and EAS fields @@ -193,7 +226,7 @@ def update_plan_info(_plan_json, planObj, easObj): easObj.save() planObj.save() except: - print traceback.format_exc() + print(traceback.format_exc()) def getReportURL(_report_name): @@ -204,7 +237,7 @@ def getReportURL(_report_name): except models.Results.DoesNotExist: URLString = "Not found" except: - print traceback.format_exc() + print(traceback.format_exc()) finally: return URLString @@ -225,16 +258,16 @@ def getReportURL(_report_name): # Test inputs if not os.path.isdir(args.directory): - print "Does not exist: %s" % args.directory + print("Does not exist: %s" % args.directory) sys.exit(1) src_dir = args.directory # Validate existence of prerequisite files explog_path = os.path.join(src_dir, 'explog.txt') if not os.path.isfile(explog_path): - print "Does not exist: %s" % explog_path - print "Cannot create environment for re-analysis to take place" - print "STATUS: Error" + print("Does not exist: %s" % explog_path) + print("Cannot create environment for re-analysis to take place") + print("STATUS: Error") sys.exit(1) if os.path.exists(os.path.join(src_dir, 'onboard_results', 'sigproc_results')): @@ -252,39 +285,39 @@ def getReportURL(_report_name): else: wells_path = os.path.join(test_dir, '1.wells') if not os.path.isfile(wells_path): - print "Does not exist: %s" % wells_path - print "Cannot basecall without output from signal processing" - print "STATUS: Error" + print("Does not exist: %s" % wells_path) + print("Cannot basecall without output from signal processing") + print("STATUS: Error") sys.exit(1) testpath = os.path.join(test_dir, 'analysis.bfmask.bin') if not os.path.isfile(testpath): testpath = os.path.join(test_dir, 'bfmask.bin') if not os.path.isfile(testpath): - print "Does not exist: %s" % testpath - print "Cannot basecall without bfmask.bin from signal processing" - print "STATUS: Error" + print("Does not exist: %s" % testpath) + print("Cannot basecall without bfmask.bin from signal processing") + print("STATUS: Error") sys.exit(1) testpath = os.path.join(test_dir, 'analysis.bfmask.stats') if not os.path.isfile(testpath): testpath = os.path.join(test_dir, 'bfmask.stats') if not os.path.isfile(testpath): - print "Does not exist: %s" % testpath - print "Cannot basecall without bfmask.stats from signal processing" - print "STATUS: Error" + print("Does not exist: %s" % testpath) + print("Cannot basecall without bfmask.stats from signal processing") + print("STATUS: Error") sys.exit(1) # Missing these files just means key signal graph will not be generated testpath = os.path.join(test_dir, 'avgNukeTrace_ATCG.txt') if not os.path.isfile(testpath): - print "Does not exist: %s" % testpath - print "Cannot create TF key signal graph without %s file" % 'avgNukeTrace_ATCG.txt' + print("Does not exist: %s" % testpath) + print("Cannot create TF key signal graph without %s file" % 'avgNukeTrace_ATCG.txt') testpath = os.path.join(test_dir, 'avgNukeTrace_TCAG.txt') if not os.path.isfile(testpath): - print "Does not exist: %s" % testpath - print "Cannot create Library key signal graph without %s file" % 'avgNukeTrace_TACG.txt' + print("Does not exist: %s" % testpath) + print("Cannot create Library key signal graph without %s file" % 'avgNukeTrace_TACG.txt') # Plan parameters, if any plan_json = '' @@ -294,23 +327,23 @@ def getReportURL(_report_name): with open(plan_params_file) as f: plan_json = json.loads(f.read()) except: - print "Unable to read Plan info from ", plan_params_file + print("Unable to read Plan info from ", plan_params_file) # Create Experiment record newExp = newExperiment(explog_path, plan_json) if newExp is None: - print ("Could not create an experiment object") - print "STATUS: Error" + print("Could not create an experiment object") + print("STATUS: Error") sys.exit(1) # Submit analysis job URL report_name = generate_http_post(newExp, src_dir, thumbnail_analysis=args.thumbnail_only) if report_name == "Failure to generate POST": - print ("Could not start a new analysis") - print "STATUS: Error" + print("Could not start a new analysis") + print("STATUS: Error") sys.exit(1) else: - print ("DEBUG: Report Name is %s" % report_name) + print("DEBUG: Report Name is %s" % report_name) # Test for Report Object count = 0 @@ -320,17 +353,17 @@ def getReportURL(_report_name): count += 1 reportURL = getReportURL(report_name) if reportURL is None: - print "STATUS: Error" + print("STATUS: Error") sys.exit(1) elif reportURL == "Not found": - print "Retry %d of %d in %d second" % (count, retries, delay) + print("Retry %d of %d in %d second" % (count, retries, delay)) time.sleep(delay) else: count = retries if reportURL == "Not found": - print "STATUS: Error" + print("STATUS: Error") sys.exit(1) else: - print "STATUS: Success" - print "REPORT-URL: %s" % reportURL + print("STATUS: Success") + print("REPORT-URL: %s" % reportURL) diff --git a/dbReports/iondb/bin/grooming.py b/dbReports/iondb/bin/grooming.py new file mode 100755 index 00000000..1cf590c8 --- /dev/null +++ b/dbReports/iondb/bin/grooming.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +"""This will go through old log files and groom them.""" + +import os +os.chdir('/opt/ion') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iondb.settings") + +import simplejson as json +from iondb.rundb.models import PluginResult + +for result in PluginResult.objects.filter(plugin__name='RunTransfer'): + # groom the database entry + for plugin_result_job in result.plugin_result_jobs.all(): + if 'user_password' in plugin_result_job.config: + del plugin_result_job.config['user_password'] + plugin_result_job.save() + + # groom the log file + log_path = os.path.join(result.path(), "drmaa_stdout.txt") + if os.path.exists(log_path): + lines = [line for line in open(log_path) if 'user_password' not in line] + open(log_path, 'w').writelines(lines) + + # groom the startplugin.json on the file system + start_plugin_path = os.path.join(result.path(), "startplugin.json") + if os.path.exists(start_plugin_path): + start_plugin = dict() + with open(start_plugin_path) as start_plugin_fp: + start_plugin = json.load(start_plugin_fp) + + if 'pluginconfig' in start_plugin and 'user_password' in start_plugin['pluginconfig']: + del start_plugin['pluginconfig']['user_password'] + with open(start_plugin_path, 'w') as start_plugin_fp: + json.dump(start_plugin, start_plugin_fp, indent=4) + diff --git a/dbReports/iondb/bin/install_dmfilesets.py b/dbReports/iondb/bin/install_dmfilesets.py index 4133fa59..65cc3a69 100644 --- a/dbReports/iondb/bin/install_dmfilesets.py +++ b/dbReports/iondb/bin/install_dmfilesets.py @@ -18,10 +18,11 @@ # import sys import traceback -from djangoinit import * +from iondb.bin import djangoinit +from iondb import settings from iondb.rundb import models from iondb.rundb.data import dmactions_types - +#pylint: disable=W1401 DM_FILE_SETS = [ { 'type': dmactions_types.SIG, @@ -31,11 +32,14 @@ 'X\d+_Y\d+.*\.dat', 'thumbnail.*\.dat', 'thumbnail/explog_final.txt', + 'thumbnail/explog_final.json', #'.[^/]*?\.txt', need to be specific '.[^/]*?alW1Step_explog\.txt', '.[^/]*?hecksum_status\.txt', '.[^/]*?xplog\.txt', + '.[^/]*?xplog\.json', '.[^/]*?xplog_final\.txt', + '.[^/]*?xplog_final\.json', '.[^/]*?nitLog.*?txt', '.[^/]*?nitValsW.*?txt', '.[^/]*?awInit\.txt', @@ -59,6 +63,7 @@ 'keepwith':{ dmactions_types.BASE: [ '.[^/]*?xplog\.txt', + '.[^/]*?xplog\.json', ], }, 'version': settings.RELVERSION, @@ -97,16 +102,19 @@ 'onboard_results/sigproc_results/avgNukeTrace_TCAG\.txt', 'onboard_results/sigproc_results/Bead_density_.*?\.png', '.[^/]*?xplog\.txt', + '.[^/]*?xplog\.json', ], 'exclude':[ ], 'keepwith':{ dmactions_types.OUT: [ 'sigproc_results/analysis\.bfmask\.stats', + 'onboard_results/sigproc_results/analysis\.bfmask\.stats', 'sigproc_results/Bead_density_.*?\.png', ], dmactions_types.SIG:[ '.[^/]*?xplog\.txt', + '.[^/]*?xplog\.json', ], }, 'version': settings.RELVERSION, @@ -151,6 +159,7 @@ 'basecaller_results/.[^/]*\.bam', 'sigproc_results/.[^/]*?\.json', 'sigproc_results/analysis.bfmask.stats', + 'onboard_results/sigproc_results/analysis\.bfmask\.stats', 'sigproc_results/Bead_density_.*?\.png', 'bc_files/.*?', 'bc_filtered/.*?', @@ -170,6 +179,7 @@ 'keepwith':{ dmactions_types.BASE: [ 'sigproc_results/analysis.bfmask.stats', + 'onboard_results/sigproc_results/analysis\.bfmask\.stats', 'sigproc_results/Bead_density_.*?\.png', ], }, @@ -211,10 +221,13 @@ 'X\d+_Y\d+.*\.dat', 'thumbnail.*\.dat', 'thumbnail/explog_final.txt', + 'thumbnail/explog_final.json', '.*?CalW1Step_explog\.txt', '.*?checksum_status\.txt', '.*?explog\.txt', + '.*?explog\.json', '.*?explog_final\.txt', + '.*?explog_final\.json', '.*?InitLog.*?txt', '.*?InitValsW.*?txt', '.*?RawInit\.txt', diff --git a/dbReports/iondb/bin/install_new_tsvm.py b/dbReports/iondb/bin/install_new_tsvm.py new file mode 100755 index 00000000..381d8640 --- /dev/null +++ b/dbReports/iondb/bin/install_new_tsvm.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved + +""" +This will install or upgrade the tsvm package +""" + +import json +import os +import sys + +TSVM_PACKAGE = 'ion-tsvm' + +# turn off complex traceback stuff +sys.tracebacklimit = 0 + +# check for root level permissions +if os.geteuid() != 0: + sys.exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") + +# NOTE: Must be run with root privileges +response_obj = {'action': 'update'} +try: + import pkg_resources + from distutils.version import LooseVersion + import apt + apt_cache = apt.Cache() + apt_cache.update() + apt_cache.open() + + if apt_cache.has_key(TSVM_PACKAGE): + pkg = apt_cache[TSVM_PACKAGE] + + if bool(pkg.is_installed): + pkg.mark_upgrade() + else: + pkg.mark_install() + + apt_cache.commit() + response_obj.update({'state': 'Success', 'msg': "ion-tsvm updated"}) +except Exception as e: + response_obj.update({'state': 'Error', 'msg': str(e)}) + +print(json.dumps(response_obj)) diff --git a/dbReports/iondb/bin/install_script.py b/dbReports/iondb/bin/install_script.py index 0352cde1..78c92710 100755 --- a/dbReports/iondb/bin/install_script.py +++ b/dbReports/iondb/bin/install_script.py @@ -41,6 +41,15 @@ def create_user_profiles(): print "Added missing userprofile for: %s" % user.username +def default_location(): + loc = models.Location.objects.filter(defaultlocation=True) or models.Location.objects.filter(name='Home') + if loc: + loc = loc[0] + else: + loc = models.Location.objects.all()[0] + return loc + + def add_fileserver(_name, _path): fs = models.FileServer.objects.all() if len(fs) == 0: @@ -58,7 +67,7 @@ def add_fileserver(_name, _path): if not exists: fs = models.FileServer(name=_name, filesPrefix=_path, - location=models.Location.objects.all()[0]) + location=default_location() ) fs.save() else: @@ -180,15 +189,11 @@ def add_global_config(configs): defaultStore = 'A' kwargs = {'name': 'Config', 'selected': False, - 'plugin_folder': 'plugins', - 'fasta_path': '', - 'reference_path': '', 'records_to_display': 20, 'default_test_fragment_key': 'ATCG', 'default_library_key': 'TCAG', 'default_flow_order': 'TACG', 'plugin_output_folder': 'plugin_out', - 'default_plugin_script': 'launch.sh', 'web_root': '', 'site_name': 'Torrent Server', 'default_storage_options': defaultStore, @@ -215,38 +220,6 @@ def runtype_add_obsolete(type, description): # print type, " RunType added" -def VariantFrequencies_add(name): - """Helper function to add VariantFrequencies""" - - vf = models.VariantFrequencies.objects.filter(name=name) - - if vf: - # print "VariantFrequency" , vf[0], "exists" - pass - else: - vf = models.VariantFrequencies(name=name) - vf.save() - # print name, " VariantFrequency added" - - -def create_default_pgm(pgmname, comment=''): - pgms = models.Rig.objects.all() - for pgm in pgms: - if pgm.name == pgmname: - # print "PGM named %s already exists" % pgmname - return True - locs = models.Location.objects.all() - if locs: - loc = locs[locs.count() - 1] - pgm = models.Rig(name=pgmname, - location=loc, - comments=comment) - pgm.save() - else: - print "Error: No Location object exists in database!" - return 1 - - def add_or_update_barcode_set(blist, btype, name, adapter): # Attempt to read dnabarcode set named 'IonXpress' from dbase dnabcs = models.dnaBarcode.objects.filter(name=name) @@ -1542,13 +1515,13 @@ def add_or_update_ioncode_tagseq_dnabarcode_set(): # mark barcodes 1-96 within a plate. index = 0 index = add_or_update_barcode_set2( - blist[0: 96], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 101, index, True) + blist[0: 96], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 101, index, False) index = add_or_update_barcode_set2( - blist[96: 96 * 2], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 201, index, True) + blist[96: 96 * 2], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 201, index, False) index = add_or_update_barcode_set2( - blist[96 * 2: 96 * 3], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 301, index, True) + blist[96 * 2: 96 * 3], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 301, index, False) index = add_or_update_barcode_set2( - blist[96 * 3: 96 * 4], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 401, index, True) + blist[96 * 3: 96 * 4], btype, name, adapter, 1, 2.00, barcode_prefix, False, 4, 401, index, False) return @@ -1778,25 +1751,6 @@ def add_libraryKey(direction, name, sequence, description, isDefault): libKey.save() -def add_barcode_args(): - # print "Adding barcode_args" - try: - gc = models.GlobalConfig.get() - try: - barcode_args = gc.barcode_args - except AttributeError: - print "barcode_args field does not exist" - return 1 - # Only add the argument if it does not exist - if "doesnotexist" in str(gc.barcode_args.get('filter', "doesnotexist")): - gc.barcode_args['filter'] = 0.01 - gc.save() - print "added barcodesplit:filter" - except: - print "There is no GlobalConfig object in database" - print traceback.format_exc() - - def add_sequencing_kit_info(name, description, flowCount): # print "Adding sequencing kit info" try: @@ -1986,21 +1940,11 @@ def check_bool(value): # print 'Adding runType failed' # print traceback.format_exc() # sys.exit(1) - # try to add variant frequencies - try: - VariantFrequencies_add("Germ Line") - VariantFrequencies_add("Somatic") - except: - print 'Adding VariantFrequencies failed' - print traceback.format_exc() - sys.exit(1) # try to add PGMs try: - create_default_pgm('default', comment="This is a model PGM. Do not delete.") - # causes errors if system is not completely configured and auto-analysis - # kicks off. - # create_default_pgm('PGM_test',comment = "This is a test pgm for staging sample datasets.") + models.Rig.objects.get_or_create(name = 'default', defaults={'location': default_location(), + 'comments': "This is a model PGM. Do not delete." }) except: print 'Adding default PGM failed' print traceback.format_exc() @@ -2104,13 +2048,6 @@ def check_bool(value): # print traceback.format_exc() # sys.exit(1) - try: - add_barcode_args() - except: - print "Modifying barcode-args list failed" - print traceback.format_exc() - sys.exit(1) - try: add_libraryKey('Forward', 'Ion TCAG', 'TCAG', 'Default forward library key', True) add_libraryKey('Reverse', 'Ion Paired End', 'TCAGC', 'Default reverse library key', True) @@ -2130,5 +2067,22 @@ def check_bool(value): for chip in models.Chip.objects.all(): chip.delete() + load_dbData("rundb/fixtures/ts_dbData_chips_kits.json") load_dbData("rundb/fixtures/ts_dbData.json") + load_dbData("rundb/fixtures/ts_dbData_analysisargs.json") load_dbData("rundb/fixtures/ionusers_group.json") + + # Setup an ion mesh user for mesh authed api calls to use + load_dbData("rundb/fixtures/ionmesh_group.json") + try: + user, is_newly_added = add_user("ionmesh", "ionmesh") + if is_newly_added: + user.set_unusable_password() + group = Group.objects.get(name='ionmesh') + if group and user.groups.count() == 0: + user.groups.add(group) + user.save() + except: + print 'Adding ionmesh user failed' + print traceback.format_exc() + sys.exit(1) diff --git a/dbReports/iondb/bin/ionPlugin.py b/dbReports/iondb/bin/ionPlugin.py index 5c10919c..b6f86735 100755 --- a/dbReports/iondb/bin/ionPlugin.py +++ b/dbReports/iondb/bin/ionPlugin.py @@ -26,7 +26,7 @@ from iondb.plugins.launch_utils import get_plugins_to_run, add_hold_jid from iondb.plugins.plugin_json import make_plugin_json -from iondb.rundb.models import Results, PluginResult, User, Plugin +from iondb.rundb.models import Results, PluginResult, PluginResultJob, User, Plugin from django.db import IntegrityError from ion.plugin.constants import Feature, RunLevel @@ -172,16 +172,15 @@ def SGEPluginJob(start_json, hold=False): args is a dict of all the args that are needed by for the plugin to run """ try: - os.umask(0000) + os.umask(0o0002) plugin_output = start_json['runinfo']['results_dir'] # Make sure the dirs exist if not os.path.exists(plugin_output): - os.makedirs(plugin_output, 0775) + os.makedirs(plugin_output, 0o0775) plugin = start_json['runinfo']['plugin'] - logger.info("Preparing for SGE submission - plugin %s --v%s on result %s (%s)", plugin[ - 'name'], plugin['version'], start_json['expmeta']['results_name'], start_json['runinfo']['pk']) + logger.info("Preparing for SGE submission - plugin %s --v%s on result %s (%s)", plugin['name'], plugin['version'], start_json['expmeta']['results_name'], start_json['runinfo']['pk']) # Branch for launch.sh vs 3.0 plugin class # logger.debug("Finding plugin definition: '%s':'%s'", plugin['name'], plugin['path']) @@ -318,35 +317,20 @@ def GetSupportedPluginInfo(self, pluginPackageName): return plugin - def pluginStart(self, start_json): - """ - Launch the plugin defined by the start_json block - """ - logger.debug("SGE job start request") - try: - jobid = SGEPluginJob(start_json, hold=True) - _session.control(jid, drmaa.JobControlAction.RELEASE) # no return value - return jobid - except: - logger.error(traceback.format_exc()) - return -1 - def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=RunLevel.DEFAULT, params={}): """ Launch multiple plugins with dependencies For multi-runlevel plugins the input 'plugins' is common for all runlevels """ msg = '' - logger.debug("[launchPlugins] result %s requested plugins: %s" % - (result_pk, ','.join(plugins.keys()))) + logger.debug("[launchPlugins] result %s requested plugins: %s" % (result_pk, ','.join(plugins.keys()))) try: # get plugins to run for this runlevel plugins, plugins_to_run, satisfied_dependencies = get_plugins_to_run(plugins, result_pk, runlevel) if len(plugins_to_run) > 0: - logger.debug("[launchPlugins] runlevel: %s, depsolved launch order: %s" % - (runlevel, ','.join(plugins_to_run))) + logger.debug("[launchPlugins] runlevel: %s, depsolved launch order: %s" % (runlevel, ','.join(plugins_to_run))) else: logger.debug("[launchPlugins] no plugins to run at runlevel: %s" % runlevel) return plugins, msg @@ -360,8 +344,7 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run user = User.objects.get(username=username) except User.DoesNotExist: user = User.objects.get(pk=1) - logger.error("Invalid user specified for plugin launch: %s, will use %s" % - (username, user.username)) + logger.error("Invalid user specified for plugin launch: %s, will use %s" % (username, user.username)) for name in plugins_to_run: try: @@ -369,8 +352,7 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run # get params for this plugin, make empty json value if doesn't exist plugin_params = params.setdefault('plugins', {}).setdefault(name, {}) - # Get pluginresult for multi-runlevel plugins or if specified to be reused - # by manual launch + # Get pluginresult for multi-runlevel plugins or if specified to be reused by manual launch pr = None pluginresult_pk = p.get('pluginresult') or plugin_params.get('pluginresult') @@ -379,21 +361,13 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run try: pr = result.pluginresult_set.get(pk=pluginresult_pk) except: - logger.error("Failed to find pluginresult for plugin %s, result %s: %s" % - (name, result.resultsName, pluginresult_pk)) + logger.error("Failed to find pluginresult for plugin %s, result %s: %s" % (name, result.resultsName, pluginresult_pk)) pr = None - elif Feature.EXPORT in p.get('features', []): - # Export plugins rerun in place to enable resuming upload - pr = result.pluginresult_set.filter(plugin=p['id']) - if pr.count() > 0: - pr = pr[0] - pr.owner = user # Create new pluginresult - this is the most common path if not pr: pr = PluginResult.objects.create(result_id=result_pk, plugin_id=p['id'], owner=user) - logger.debug("New pluginresult id=%s created for plugin %s and result %s." % - (pr.pk, name, result.resultsName)) + logger.debug("New pluginresult id=%s created for plugin %s and result %s." % (pr.pk, name, result.resultsName)) # Always create new, unique output folder. # Never fallback to old *_out format. plugin_output = pr.path(create=True, fallback=False) @@ -405,8 +379,8 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run p['pluginresult'] = pr.pk p, holding_for = add_hold_jid(p, plugins, runlevel, satisfied_dependencies) - start_json = make_plugin_json(result_pk, report_dir, p, plugin_output, net_location, url_root, username, runlevel, params.get( - 'blockId', ''), params.get('block_dirs', ["."]), plugin_params.get('instance_config', {})) + start_json = make_plugin_json(result_pk, report_dir, p, plugin_output, net_location, url_root, username, runlevel, + params.get('blockId', ''), params.get('block_dirs', ["."]), plugin_params.get('instance_config', {})) # Pass on run_mode (launch source - manual/instance, pipeline) start_json['runplugin']['run_mode'] = params.get('run_mode', '') @@ -425,7 +399,7 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run } # prepare for launch: updates config, sets pluginresults status, generates api key - pr.prepare(config=start_json['pluginconfig']) + pr.prepare() pr.save() # update startplugin json with pluginresult info start_json['runinfo']['pluginresult'] = pr.pk @@ -438,12 +412,19 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run if jid: # Update pluginresult status - PluginResult.objects.filter(pk=pr.pk).update(state='Queued', jobid=jid) + prj, created = PluginResultJob.objects.get_or_create( + plugin_result=pr, + run_level=runlevel, + grid_engine_jobid=jid, + state = 'Queued', + config = start_json['pluginconfig'], + ) + prj.save() + # Release now that jobid and queued state are set. _session.control(jid, drmaa.JobControlAction.RELEASE) # no return value - msg += 'Plugin: %s result: %s, jid %s, depends %s, holding for %s \n' % ( - p.get('name', ''), result.resultsName, jid, p.get('depends', []), holding_for) + msg += 'Plugin: %s result: %s, jid %s, depends %s, holding for %s \n' % (p.get('name', ''), result.resultsName, jid, p.get('depends', []), holding_for) if runlevel != RunLevel.BLOCK: p['jid'] = jid @@ -454,8 +435,6 @@ def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=Run logger.error(traceback.format_exc()) msg += 'ERROR: Plugin %s failed to launch.\n' % p['name'] pr = PluginResult.objects.get(pk=pr.pk) - pr.complete('Error') - pr.save() except: logger.error(traceback.format_exc()) msg += 'ERROR: Failed to launch requested plugins.' @@ -477,10 +456,8 @@ def sgeStop(self, jobid): try: jobinfo = _session.wait(jobid, timeout=20) # returns JobInfo object - logger.info("Job %s wasAborted=%s with exit_status=%s", jobinfo.jobId, - jobinfo.wasAborted, jobinfo.resourceUsage.get('exit_status')) - status = "Job %s wasAborted=%s with exit_status=%s" % ( - jobinfo.jobId, jobinfo.wasAborted, jobinfo.resourceUsage.get('exit_status')) + logger.info("Job %s wasAborted=%s with exit_status=%s", jobinfo.jobId, jobinfo.wasAborted, jobinfo.resourceUsage.get('exit_status')) + status = "Job %s wasAborted=%s with exit_status=%s" % (jobinfo.jobId, jobinfo.wasAborted, jobinfo.resourceUsage.get('exit_status')) except drmaa.errors.InvalidJobException: status = "Job already terminated, or started previously." logger.warn("SGE job %s already terminated", jobid) @@ -519,24 +496,7 @@ def pluginStatus(self, jids): return ret if return_list else ret[0] - def createPR(self, resultpk, pluginpk, username=None, config={}): - if username is None: - username = "ionadmin" - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - user = User.objects.get(pk=1) - try: - pr = PluginResult.objects.create( - result_id=resultpk, plugin_id=pluginpk, owner=user, pluginconfig=config) - except IntegrityError: - return False - # from tastypie.serializers import Serializer - # s = Serializer() - # return s.to_json(pr) - return True - - def updatePR(self, pk, state, store, jobid=None): + def updatePR(self, pk, grid_engine_job_id, state, store, jobid=None): """ Because of the Apache security it is hard for crucher nodes to contact the API directly. This method is a simple proxy to update the status of plugins @@ -548,8 +508,8 @@ def updatePR(self, pk, state, store, jobid=None): # TODO: should do an explicit check for the database object in case it's been deleted pr = PluginResult.objects.get(id=pk) - if (state is not None) and (state != pr.state): - pr.SetState(state, jobid) + if state is not None: + pr.SetState(state, grid_engine_job_id, jobid) if store is not None: # Validate JSON? @@ -561,6 +521,13 @@ def updatePR(self, pk, state, store, jobid=None): return True + def delete_pr_directory(self, directory): + """We need to use the same user to delete the folder as created it""" + def delete_error(func, path, info): + logger.error("Failed to delete %s: %s", path, info) + + shutil.rmtree(directory, onerror=delete_error) + if __name__ == '__main__': # Instantiate the xmlrpc server server = AsyncXMLRPCServer(('', settings.IPLUGIN_PORT), SimpleXMLRPCRequestHandler, allow_none=True) diff --git a/dbReports/iondb/bin/ion_add_nfs_mount.py b/dbReports/iondb/bin/ion_add_nfs_mount.py new file mode 100644 index 00000000..25616976 --- /dev/null +++ b/dbReports/iondb/bin/ion_add_nfs_mount.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved + +""" +This module will install a new nfs mount + +This module will require three arguments +1) Server name: The host or ip address of the nas server +2) Share name: The name of the share to be mounted +3) Mount point: The place on the local file system to mount it. +""" + +import os +import shutil +import sys +import subprocess + + +# turn off complex traceback stuff +sys.tracebacklimit = 0 + +# check for root level permissions +if os.geteuid() != 0: + sys.exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") + +if len(sys.argv) < 4: + sys.exit("Not enough arguments..") + +# get the arguments +servername = sys.argv[1] +sharename = sys.argv[2] +mountpoint = sys.argv[3] + +# create all_local from all +filename = "/usr/share/ion-tsconfig/ansible/group_vars/all_local" +tempfile_path = "/usr/share/ion-tsconfig/ansible/group_vars/all_local.temp" +if not os.path.isfile(filename): + shutil.copy2("/usr/share/ion-tsconfig/ansible/group_vars/all", filename) +# Line to add - spaces are important! +new_nas = " - { name: %s, directory: %s, mountpoint: %s, options: %s }" % (servername, sharename, mountpoint, "defaults") +# Check if line exists. This is a small file so we can do this cheat +with open(filename, 'r') as fh: + if new_nas in fh.read(): + print("This line: '%s'. Already exists in all_local." % new_nas) + else: + fh.seek(0) + # Insert line after pattern ^nas_mounts: + with open(tempfile_path, "w") as out: + for line in fh: + out.write(line) + if 'nas_mounts:' in line: + out.write(new_nas + '\n') + shutil.move(tempfile_path, filename) + +# Which hosts file to use +hosts_file = '/usr/share/ion-tsconfig/ansible/torrentsuite_hosts' +if os.path.isfile(hosts_file + '_local'): + hosts_file += '_local' + +# Run ansible-playbook in a subshell since running it from python celery task is broken +cmd = ["ansible-playbook", "-i", hosts_file, 'nfs_client.yml', "--sudo"] +p = subprocess.Popen(cmd, cwd="/usr/share/ion-tsconfig/ansible", stdout=subprocess.PIPE, stderr=subprocess.PIPE) +stdout, stderr = p.communicate() +sys.stdout.write(stdout) +sys.stderr.write(stderr) \ No newline at end of file diff --git a/dbReports/iondb/bin/ion_check_for_new_tsvm.py b/dbReports/iondb/bin/ion_check_for_new_tsvm.py new file mode 100644 index 00000000..0eb0d594 --- /dev/null +++ b/dbReports/iondb/bin/ion_check_for_new_tsvm.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved + +""" +This module will check to see if there is a new version of tsvm available +""" + +import apt +import json +import os +import sys + +# turn off complex traceback stuff +sys.tracebacklimit = 0 + +# check for root level permissions +if os.geteuid() != 0: + sys.exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") + +# update the aptitude cache +apt_cache = apt.Cache() +apt_cache.update() +apt_cache.open() + +# get the available version and current state +state = 'Not Available' +msg = 'No ion-tsvm packages are available for installation.' +if apt_cache.has_key('ion-tsvm'): + pkg = apt_cache['ion-tsvm'] + msg = 'New ion-tsvm package version %s is available' % pkg.candidate if pkg.is_upgradable else 'ion-tsvm is up to date.' + state = 'Upgradable' if pkg.is_upgradable else 'Not Upgradable' + +# write to stdout +print(json.dumps({'state': state, 'msg': msg})) \ No newline at end of file diff --git a/dbReports/iondb/bin/ion_remove_nfs_mount.py b/dbReports/iondb/bin/ion_remove_nfs_mount.py new file mode 100644 index 00000000..bb4f7b99 --- /dev/null +++ b/dbReports/iondb/bin/ion_remove_nfs_mount.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved + +""" +This module will remove an nfs mount + +This will require one argument which is the mount point +""" + +import os +import shutil +import sys +import subprocess + + +# turn off complex traceback stuff +sys.tracebacklimit = 0 + +# check for root level permissions +if os.geteuid() != 0: + sys.exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") + +if len(sys.argv) < 2: + sys.exit("Not enough arguments..") + +# get the arguments +mountpoint = sys.argv[1] + +filename = "/usr/share/ion-tsconfig/ansible/group_vars/all_local" +tempfile = "/tmp/all_local.temp" +# Delete the line matching the pattern +with open(tempfile, "w") as out, open(filename, "r") as fh: + for line in fh: + if mountpoint not in line: + out.write(line) +shutil.move(tempfile, filename) + +# Which hosts file to use +hosts_file = '/usr/share/ion-tsconfig/ansible/torrentsuite_hosts' +if os.path.isfile(hosts_file + '_local'): + hosts_file += '_local' + +# Run ansible-playbook in a subshell since running it from python celery task is broken +cmd = ["ansible-playbook", "-i", hosts_file, 'nfs_client.yml', "--sudo"] +p = subprocess.Popen(cmd, cwd="/usr/share/ion-tsconfig/ansible", stdout=subprocess.PIPE, stderr=subprocess.PIPE) +stdout, stderr = p.communicate() +sys.stdout.write(stdout) +sys.stderr.write(stderr) \ No newline at end of file diff --git a/dbReports/iondb/bin/lock_ion_apt_sources.py b/dbReports/iondb/bin/lock_ion_apt_sources.py new file mode 100644 index 00000000..a53aae55 --- /dev/null +++ b/dbReports/iondb/bin/lock_ion_apt_sources.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# Copyright (C) 2016 Ion Torrent Systems, Inc. All Rights Reserved + +""" +This script will lock or unlock the version of the ion software by manipulating the apt source path. +""" +from ion import version +import os +import subprocess +import sys + +# turn off complex traceback stuff +sys.tracebacklimit = 0 + +# check for root level permissions +if os.geteuid() != 0: + sys.exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") + +# look for the file name +if len(sys.argv) < 2: + sys.exit("Enable flag is required") + +# parse the first argument to a bool +enable = sys.argv[1].lower() == 'true' + +# get distribution string +os_codename = 'trusty' +with open('/etc/lsb-release', 'r') as fp: + for line in fp.readlines(): + if line.startswith('DISTRIB_CODENAME'): + os_codename = line.split('=')[1].strip() + +if enable: + find_string = "updates\/software.*%s\/" % os_codename + replace_string = "updates\/software\/archive\/%s %s\/" % (version, os_codename) +else: + find_string = "updates\/software\/archive\/%s.*" % version + replace_string = "updates\/software %s\/" % os_codename +sed_string = "s/%s/%s/g" % (find_string, replace_string) + +# Possible locations of Ion Apt repository strings: +# /etc/apt/sources.list +# /etc/apt/sources.list.d/*.list +# +filepaths = [os.path.join("/etc/apt/sources.list.d", x) for x in os.listdir("/etc/apt/sources.list.d") if os.path.splitext(x)[1] == '.list'] +filepaths.append("/etc/apt/sources.list") +for filepath in filepaths: + print("Looking in %s" % filepath) + cmd = ["sed", "-i", sed_string, filepath] + + stdout = '' + stderr = '' + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + if proc.returncode: + sys.stderr.write(stderr) + except: + sys.stderr.write(stderr) diff --git a/dbReports/iondb/bin/migrate_schema22.py b/dbReports/iondb/bin/migrate_schema22.py index 8d08aa22..44b85f49 100755 --- a/dbReports/iondb/bin/migrate_schema22.py +++ b/dbReports/iondb/bin/migrate_schema22.py @@ -998,56 +998,6 @@ def forwards(self, orm): log.info("UserProfile table already exists") db.clear_deferred_sql() - try: - db.start_transaction() - # Adding model 'SequencingKit' - db.create_table('rundb_sequencingkit', ( - ('id', models.fields.AutoField(primary_key=True)), - ('name', models.fields.CharField(max_length=512, blank=True)), - ('description', models.fields.CharField(max_length=3024, blank=True)), - ('sap', models.fields.CharField(max_length=7, blank=True)), - )) - db.send_create_signal('rundb', ['SequencingKit']) - db.execute_deferred_sql() - db.commit_transaction() - except DatabaseError: - db.rollback_transaction() - log.info("SequencingKit table already exists") - db.clear_deferred_sql() - - try: - db.start_transaction() - # Adding model 'LibraryKit' - db.create_table('rundb_librarykit', ( - ('id', models.fields.AutoField(primary_key=True)), - ('name', models.fields.CharField(max_length=512, blank=True)), - ('description', models.fields.CharField(max_length=3024, blank=True)), - ('sap', models.fields.CharField(max_length=7, blank=True)), - )) - db.send_create_signal('rundb', ['LibraryKit']) - db.execute_deferred_sql() - db.commit_transaction() - except DatabaseError: - db.rollback_transaction() - log.info("LibraryKit table already exists") - db.clear_deferred_sql() - - try: - db.start_transaction() - # Adding model 'VariantFrequencies' - db.create_table('rundb_variantfrequencies', ( - ('id', models.fields.AutoField(primary_key=True)), - ('name', models.fields.CharField(max_length=512, blank=True)), - ('description', models.fields.CharField(max_length=3024, blank=True)), - )) - db.send_create_signal('rundb', ['VariantFrequencies']) - db.execute_deferred_sql() - db.commit_transaction() - except DatabaseError: - db.rollback_transaction() - log.info("VariantFrequencies table already exists") - db.clear_deferred_sql() - try: db.start_transaction() # Adding model 'KitInfo' @@ -1619,13 +1569,6 @@ def backwards(self): 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), 'sequence': ('django.db.models.fields.CharField', [], {'max_length': '64'}) }, - 'rundb.librarykit': { - 'Meta': {'object_name': 'LibraryKit'}, - 'description': ('django.db.models.fields.CharField', [], {'max_length': '3024', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), - 'sap': ('django.db.models.fields.CharField', [], {'max_length': '7', 'blank': 'True'}) - }, 'rundb.location': { 'Meta': {'object_name': 'Location'}, 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), @@ -1832,13 +1775,6 @@ def backwards(self): 'meta': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}), 'runType': ('django.db.models.fields.CharField', [], {'max_length': '512'}) }, - 'rundb.sequencingkit': { - 'Meta': {'object_name': 'SequencingKit'}, - 'description': ('django.db.models.fields.CharField', [], {'max_length': '3024', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), - 'sap': ('django.db.models.fields.CharField', [], {'max_length': '7', 'blank': 'True'}) - }, 'rundb.template': { 'Meta': {'object_name': 'Template'}, 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), @@ -1914,12 +1850,6 @@ def backwards(self): 'phone_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'default': "'user'", 'max_length': '256'}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) - }, - 'rundb.variantfrequencies': { - 'Meta': {'object_name': 'VariantFrequencies'}, - 'description': ('django.db.models.fields.CharField', [], {'max_length': '3024', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}) } } diff --git a/dbReports/iondb/bin/startup_housekeeping.py b/dbReports/iondb/bin/startup_housekeeping.py index 82e49bc0..b987a260 100644 --- a/dbReports/iondb/bin/startup_housekeeping.py +++ b/dbReports/iondb/bin/startup_housekeeping.py @@ -31,7 +31,7 @@ def bother_the_user(): info.extend([profile.phone_number, profile.user.email]) if not any(info): models.Message.info("""Please supply some customer support contact info. -
Add contact information.""") +
Add contact information.""") def events_from_settings(): diff --git a/dbReports/iondb/celery.py b/dbReports/iondb/celery.py index b0d03229..6ed17b73 100644 --- a/dbReports/iondb/celery.py +++ b/dbReports/iondb/celery.py @@ -24,7 +24,7 @@ CELERY_RESULT_BACKEND='cache+memcached://127.0.0.1:11211/', # Avoid indefinite hangs by forcing results to expire after 30 minutes - CELERY_TASK_RESULT_EXPIRES=180, + CELERY_TASK_RESULT_EXPIRES=1800, # Tracking rate limits is expensive, not used CELERY_DISABLE_RATE_LIMITS=True, diff --git a/dbReports/iondb/ftpserver/__init__.py b/dbReports/iondb/ftpserver/__init__.py new file mode 100644 index 00000000..bda83ac8 --- /dev/null +++ b/dbReports/iondb/ftpserver/__init__.py @@ -0,0 +1,7 @@ +#from iondb.ftpserver.management.commands.ftpserver import Command +#def start_ftp_server(): +# ftp_server_cmd = Command() + # call this to start the ftp server with all of the settings... from the settings +# ftp_server_cmd.handle() + +#start_ftp_server() diff --git a/dbReports/iondb/ftpserver/_unix.py b/dbReports/iondb/ftpserver/_unix.py new file mode 100644 index 00000000..5a6e3917 --- /dev/null +++ b/dbReports/iondb/ftpserver/_unix.py @@ -0,0 +1,25 @@ +try: + import os + import pwd +except ImportError: + pass +else: + class UnixPersonateUser(object): + def __init__(self, file_access_user): + self.file_access_user = file_access_user + self.gid = os.getgid() + self.uid = os.getuid() + + def impersonate_user(self, username, password): + """impersonate user when operating file system + """ + uid = pwd.getpwnam(self.file_access_user).pw_uid + gid = pwd.getpwnam(self.file_access_user).pw_gid + os.setegid(gid) + os.seteuid(uid) + + def terminate_impersonation(self, username): + """undo user from impersonation + """ + os.setegid(self.gid) + os.seteuid(self.uid) diff --git a/dbReports/iondb/ftpserver/authorizers.py b/dbReports/iondb/ftpserver/authorizers.py new file mode 100644 index 00000000..71acf452 --- /dev/null +++ b/dbReports/iondb/ftpserver/authorizers.py @@ -0,0 +1,63 @@ +import os +from django.contrib.auth import authenticate +from django.contrib.auth.models import User +from pyftpdlib.authorizers import AuthenticationFailed +from . import _unix + +PERMMISSIONS = 'elradfmw' + +class FTPAccountAuthorizer(object): + """Authorizer class by django authentication.""" + personate_user_class = None + + def __init__(self, file_access_user=None): + if file_access_user: + personate_user_class = (self.personate_user_class or _unix.UnixPersonateUser) + self.personate_user = personate_user_class(file_access_user) + else: + self.personate_user = None + + def has_user(self, username): + """return True if exists user.""" + return User.objects.filter(username=username).exists() + + def validate_authentication(self, username, password, handler): + """authenticate user with password""" + + if not authenticate(username=username, password=password): + raise AuthenticationFailed("Authentication failed.") + + try: + return User.objects.get(username=username) + except User.DoesNotExist: + raise AuthenticationFailed("Authentication failed.") + + def get_home_dir(self, username): + """Get the home directory""" + return '/' + + def get_msg_login(self, username): + """message for welcome.""" + return 'welcome.' + + def get_msg_quit(self, username): + """The quit message""" + return 'good bye.' + + def has_perm(self, username, perm, path=None): + """check user permission""" + return perm in PERMMISSIONS + + def get_perms(self, username): + """return user permissions""" + return PERMMISSIONS + + def impersonate_user(self, username, password): + """delegate to personate_user method""" + if self.personate_user: + self.personate_user.impersonate_user(username, password) + + def terminate_impersonation(self, username): + """delegate to terminate_impersonation method""" + if self.personate_user: + self.personate_user.terminate_impersonation(username) diff --git a/pipeline/tests/__init__.py b/dbReports/iondb/ftpserver/management/__init__.py similarity index 100% rename from pipeline/tests/__init__.py rename to dbReports/iondb/ftpserver/management/__init__.py diff --git a/dbReports/iondb/ftpserver/management/commands/__init__.py b/dbReports/iondb/ftpserver/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dbReports/iondb/ftpserver/management/commands/ftpserver.py b/dbReports/iondb/ftpserver/management/commands/ftpserver.py new file mode 100644 index 00000000..759f4f5c --- /dev/null +++ b/dbReports/iondb/ftpserver/management/commands/ftpserver.py @@ -0,0 +1,122 @@ +import sys +import os + +from optparse import make_option + +import pyftpdlib +from pyftpdlib import handlers +from pyftpdlib.servers import FTPServer + +from django import get_version +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError +from django.utils.daemonize import become_daemon +from iondb.ftpserver.authorizers import FTPAccountAuthorizer +from iondb.ftpserver import utils + + +class Command(BaseCommand): + help = "Start FTP server" + + option_list = BaseCommand.option_list + ( + make_option('--daemonize', action='store_true', dest='daemonize', help="become background service."), + make_option('--pidfile', action='store', dest='pidfile', help="filename to write process id (PID)."), + make_option('--timeout', action='store', dest='timeout', type=int, help="timeout for remote client."), + make_option('--passive-ports', action='store', dest='passive-ports', help="Passive ports. eg. 12345,30000-50000"), + make_option('--masquerade-address', action='store', dest='masquerade-address', help="masquerade address."), + make_option('--file-access-user', action='store', dest='file-access-user', help="user for access to file."), + make_option('--certfile', action='store', dest='certfile', help="TLS certificate file."), + make_option('--keyfile', action='store', dest='keyfile', help="TLS private key file."), + make_option('--sendfile', action='store_true', dest='sendfile', help="Use sendfile."), + ) + + def make_server(self, server_class, handler_class, authorizer_class, host_port, file_access_user=None, **handler_options): + """Helper method for making a server""" + return utils.make_server(server_class, handler_class, authorizer_class, host_port, file_access_user=file_access_user, **handler_options) + + def handle(self, *args, **options): + # bind host and port + host_port = options.get('host_port', None) + if host_port: + host, _port = host_port.split(':', 1) + port = int(_port) + else: + host = utils.get_settings_value('FTPSERVER_HOST') or '127.0.0.1' + port = utils.get_settings_value('FTPSERVER_PORT') or 21 + + timeout = options.get('timeout') or utils.get_settings_value('FTPSERVER_TIMEOUT') + + # passive ports + _passive_ports = options.get('passive-ports') or utils.get_settings_value('FTPSERVER_PASSIVE_PORTS') + if _passive_ports: + try: + passive_ports = utils.parse_ports(_passive_ports) + except (TypeError, ValueError): + raise CommandError("Invalid passive ports: {}".format(_passive_ports)) + else: + passive_ports = None + + # masquerade address + masquerade_address = options.get('masquerade-address') or utils.get_settings_value('FTPSERVER_MASQUERADE_ADDRESS') + + # file access user + file_access_user = options.get('file-access-user') or utils.get_settings_value('FTPSERVER_FILE_ACCESS_USER') + + # certfile + certfile = options.get('certfile') or utils.get_settings_value('FTPSERVER_CERTFILE') + + # keyfile + keyfile = options.get('keyfile') or utils.get_settings_value('FTPSERVER_KEYFILE') + + # sendfile + sendfile = options.get('sendfile') or utils.get_settings_value('FTPSERVER_SENDFILE') + + # daemonize + daemonize = options.get('daemonize') or utils.get_settings_value('FTPSERVER_DAEMONIZE') + if daemonize: + daemonize_options = utils.get_settings_value('FTPSERVER_DAEMONIZE_OPTIONS') or {} + become_daemon(**daemonize_options) + + # write pid to file + pidfile = options.get('pidfile') or utils.get_settings_value('FTPSERVER_PIDFILE') + if pidfile: + with open(pidfile, 'w') as f: + f.write(str(os.getpid())) + + # select handler class + if certfile or keyfile: + if hasattr(handlers, 'TLS_FTPHandler'): + handler_class = (utils.get_settings_value('FTPSERVER_TLSHANDLER')) or handlers.TLS_FTPHandler + else: + # unsupported + raise CommandError("Can't import OpenSSL. Please install pyOpenSSL.") + else: + handler_class = (utils.get_settings_value('FTPSERVER_HANDLER')) or handlers.FTPHandler + + # setup server + server = self.make_server( + server_class=FTPServer, + handler_class=handler_class, + authorizer_class=FTPAccountAuthorizer, + host_port=(host, port), + file_access_user=file_access_user, + timeout=timeout, + passive_ports=passive_ports, + masquerade_address=masquerade_address, + certfile=certfile, + keyfile=keyfile, + sendfile=sendfile + ) + + # start server + sys.stdout.write(( + "Django version {version_dj}, using settings '{settings}'\n" + "pyftpdlib version {version_ftp}\n" + "Quit the server with {quit_command}.\n").format( + version_dj=get_version(), + version_ftp=pyftpdlib.__ver__, + settings=settings.SETTINGS_MODULE, + quit_command='CONTROL-C' + ) + ) + server.serve_forever() diff --git a/dbReports/iondb/ftpserver/utils.py b/dbReports/iondb/ftpserver/utils.py new file mode 100644 index 00000000..29622323 --- /dev/null +++ b/dbReports/iondb/ftpserver/utils.py @@ -0,0 +1,59 @@ +from django.conf import settings + + +def get_settings_value(name): + """Return the django settings value for name attribute + """ + return getattr(settings, name, None) + + +def parse_ports(ports_text): + """Parse ports text + + e.g. ports_text = "12345,13000-15000,20000-30000" + """ + ports_set = set() + for bit in ports_text.split(','): + if '-' in bit: + low, high = bit.split('-', 1) + ports_set = ports_set.union(range(int(low), int(high) + 1)) + else: + ports_set.add(int(bit)) + return sorted(list(ports_set)) + + +def import_class(class_path): + from importlib import import_module + pieces = class_path.split('.') + module = '.'.join(pieces[:-1]) + cls = pieces[-1] + module = import_module(module) + return getattr(module, cls) + + +def make_server(server_class, handler_class, authorizer_class, host_port, file_access_user=None, **handler_options): + """make server instance + + :host_port: (host, port) + :file_access_user: 'spam' + + handler_options: + + * timeout + * passive_ports + * masquerade_address + * certfile + * keyfile + """ + if isinstance(handler_class, basestring): + handler_class = import_class(handler_class) + + if isinstance(authorizer_class, basestring): + authorizer_class = import_class(authorizer_class) + + authorizer = authorizer_class(file_access_user) + handler = handler_class + for key, value in handler_options.items(): + setattr(handler, key, value) + handler.authorizer = authorizer + return server_class(host_port, handler) diff --git a/dbReports/iondb/media/Makefile b/dbReports/iondb/media/Makefile index afe3cf87..24859fab 100644 --- a/dbReports/iondb/media/Makefile +++ b/dbReports/iondb/media/Makefile @@ -32,7 +32,7 @@ hr=-------------------------------------------------- # # BUILD DOCS # -all: |compile-css minify-js +all: |compile-css #minify-js compile-css: @echo "\n${HR}" @@ -112,7 +112,6 @@ minify-js: | bulk-minify-js @echo "Minify'ing random Torrent Browser JS" @uglifyjs -nc runliveness.js > runliveness.min.js @uglifyjs -nc tbHelper.js > tbHelper.min.js - @uglifyjs -nc resources/jquery/jquery.iframe-auto-height.plugin.1.7.1.js > resources/jquery/jquery.iframe-auto-height.plugin.1.7.1.min.js @echo "Minify'ing random Torrent Browser JS... ${CHECK} Done" @echo "${HR}" diff --git a/dbReports/iondb/media/jquery/js/plupload/jquery-ui.min.js b/dbReports/iondb/media/jquery/js/plupload/jquery-ui.min.js deleted file mode 100644 index c11e844f..00000000 --- a/dbReports/iondb/media/jquery/js/plupload/jquery-ui.min.js +++ /dev/null @@ -1,1012 +0,0 @@ -/*! - * jQuery UI 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI - */ -(function(c){c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.2",plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=0)&&c(a).is(":focusable")}})}})(jQuery); -;/*! - * jQuery UI Widget 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Widget - */ -(function(b){var j=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add(this).each(function(){b(this).triggerHandler("remove")});return j.call(b(this),a,c)})};b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend({},c.options);b[e][a].prototype= -b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.substring(0,1)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==undefined){h=i;return false}}):this.each(function(){var g= -b.data(this,a);if(g){d&&g.option(d);g._init()}else b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){this.element=b(c).data(this.widgetName,this);this.options=b.extend(true,{},this.options,b.metadata&&b.metadata.get(c)[this.widgetName],a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create(); -this._init()},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a,e=this;if(arguments.length===0)return b.extend({},e.options);if(typeof a==="string"){if(c===undefined)return this.options[a];d={};d[a]=c}b.each(d,function(f, -h){e._setOption(f,h)});return e},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a= -b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery); -;/*! - * jQuery UI Mouse 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Mouse - * - * Depends: - * jquery.ui.widget.js - */ -(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(a._preventClickEvent){a._preventClickEvent=false;b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&& -this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault(); -return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.browser.safari||a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&& -this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=a.target==this._mouseDownEvent.target;this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX- -a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); -;/* - * jQuery UI Position 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Position - */ -(function(c){c.ui=c.ui||{};var m=/left|center|right/,n=/top|center|bottom/,p=c.fn.position,q=c.fn.offset;c.fn.position=function(a){if(!a||!a.of)return p.apply(this,arguments);a=c.extend({},a);var b=c(a.of),d=(a.collision||"flip").split(" "),e=a.offset?a.offset.split(" "):[0,0],g,h,i;if(a.of.nodeType===9){g=b.width();h=b.height();i={top:0,left:0}}else if(a.of.scrollTo&&a.of.document){g=b.width();h=b.height();i={top:b.scrollTop(),left:b.scrollLeft()}}else if(a.of.preventDefault){a.at="left top";g=h= -0;i={top:a.of.pageY,left:a.of.pageX}}else{g=b.outerWidth();h=b.outerHeight();i=b.offset()}c.each(["my","at"],function(){var f=(a[this]||"").split(" ");if(f.length===1)f=m.test(f[0])?f.concat(["center"]):n.test(f[0])?["center"].concat(f):["center","center"];f[0]=m.test(f[0])?f[0]:"center";f[1]=n.test(f[1])?f[1]:"center";a[this]=f});if(d.length===1)d[1]=d[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(a.at[0]==="right")i.left+=g;else if(a.at[0]==="center")i.left+= -g/2;if(a.at[1]==="bottom")i.top+=h;else if(a.at[1]==="center")i.top+=h/2;i.left+=e[0];i.top+=e[1];return this.each(function(){var f=c(this),k=f.outerWidth(),l=f.outerHeight(),j=c.extend({},i);if(a.my[0]==="right")j.left-=k;else if(a.my[0]==="center")j.left-=k/2;if(a.my[1]==="bottom")j.top-=l;else if(a.my[1]==="center")j.top-=l/2;j.left=parseInt(j.left);j.top=parseInt(j.top);c.each(["left","top"],function(o,r){c.ui.position[d[o]]&&c.ui.position[d[o]][r](j,{targetWidth:g,targetHeight:h,elemWidth:k, -elemHeight:l,offset:e,my:a.my,at:a.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(j,{using:a.using}))})};c.ui.position={fit:{left:function(a,b){var d=c(window);b=a.left+b.elemWidth-d.width()-d.scrollLeft();a.left=b>0?a.left-b:Math.max(0,a.left)},top:function(a,b){var d=c(window);b=a.top+b.elemHeight-d.height()-d.scrollTop();a.top=b>0?a.top-b:Math.max(0,a.top)}},flip:{left:function(a,b){if(b.at[0]!=="center"){var d=c(window);d=a.left+b.elemWidth-d.width()-d.scrollLeft();var e=b.my[0]==="left"? --b.elemWidth:b.my[0]==="right"?b.elemWidth:0,g=-2*b.offset[0];a.left+=a.left<0?e+b.targetWidth+g:d>0?e-b.targetWidth+g:0}},top:function(a,b){if(b.at[1]!=="center"){var d=c(window);d=a.top+b.elemHeight-d.height()-d.scrollTop();var e=b.my[1]==="top"?-b.elemHeight:b.my[1]==="bottom"?b.elemHeight:0,g=b.at[1]==="top"?b.targetHeight:-b.targetHeight,h=-2*b.offset[1];a.top+=a.top<0?e+b.targetHeight+h:d>0?e+g+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(a,b){if(/static/.test(c.curCSS(a,"position")))a.style.position= -"relative";var d=c(a),e=d.offset(),g=parseInt(c.curCSS(a,"top",true),10)||0,h=parseInt(c.curCSS(a,"left",true),10)||0;e={top:b.top-e.top+g,left:b.left-e.left+h};"using"in b?b.using.call(a,e):d.css(e)};c.fn.offset=function(a){var b=this[0];if(!b||!b.ownerDocument)return null;if(a)return this.each(function(){c.offset.setOffset(this,a)});return q.call(this)}}})(jQuery); -;/* - * jQuery UI Draggable 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Draggables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== -"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= -this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;return true},_mouseStart:function(a){var b=this.options;this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top- -this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions(); -d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis|| -this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if(!this.element[0]||!this.element[0].parentNode)return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element, -b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this== -a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]|| -0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], -this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top- -(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment== -"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&& -a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"), -10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], -this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft(): -f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.leftthis.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?e:!(e-this.offset.click.left').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options; -if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!= -"HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>=i&& -e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), -top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= -this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!d(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", -nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var e=0;e');/sw|se|ne|nw/.test(g)&&f.css({zIndex:++a.zIndex});"se"==g&&f.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[g]=".ui-resizable-"+g;this.element.append(f)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== -String)this.handles[i]=d(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=d(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}d(this.handles[i])}};this._renderAxis(this.element);this._handles=d(".ui-resizable-handle",this.element).disableSelection(); -this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();d(this.element).addClass("ui-resizable-autohide").hover(function(){d(this).removeClass("ui-resizable-autohide");b._handles.show()},function(){if(!b.resizing){d(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(c){d(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()}; -if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=false;for(var c in this.handles)if(d(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(), -e=this.element;this.resizing=true;this.documentScroll={top:d(document).scrollTop(),left:d(document).scrollLeft()};if(e.is(".ui-draggable")||/absolute/.test(e.css("position")))e.css({position:"absolute",top:c.top,left:c.left});d.browser.opera&&/relative/.test(e.css("position"))&&e.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();c=m(this.helper.css("left"));var g=m(this.helper.css("top"));if(a.containment){c+=d(a.containment).scrollLeft()||0;g+=d(a.containment).scrollTop()||0}this.offset= -this.helper.offset();this.position={left:c,top:g};this.size=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalSize=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalPosition={left:c,top:g};this.sizeDiff={width:e.outerWidth()-e.width(),height:e.outerHeight()-e.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio: -this.originalSize.width/this.originalSize.height||1;a=d(".ui-resizable-"+this.axis).css("cursor");d("body").css("cursor",a=="auto"?this.axis+"-resize":a);e.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,e=this._change[this.axis];if(!e)return false;c=e.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize", -b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var e=this._proportionallyResizeElements,g=e.length&&/textarea/i.test(e[0].nodeName);e=g&&d.ui.hasScroll(e[0],"left")?0:c.sizeDiff.height; -g={width:c.size.width-(g?0:c.sizeDiff.width),height:c.size.height-e};e=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var f=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(d.extend(g,{top:f,left:e}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}d("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop", -b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size,e=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(e=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(e=="nw"){b.top= -a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,e=k(b.width)&&a.maxWidth&&a.maxWidthb.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(f)b.width=a.minWidth;if(h)b.height=a.minHeight;if(e)b.width=a.maxWidth;if(g)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height, -l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(f&&l)b.left=i-a.minWidth;if(e&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(g&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=d.browser.msie&&d.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+ -a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return d.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return d.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return d.extend(this._change.n.apply(this, -arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return d.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){d.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});d.extend(d.ui.resizable, -{version:"1.8.2"});d.ui.plugin.add("resizable","alsoResize",{start:function(){var b=d(this).data("resizable").options,a=function(c){d(c).each(function(){d(this).data("resizable-alsoresize",{width:parseInt(d(this).width(),10),height:parseInt(d(this).height(),10),left:parseInt(d(this).css("left"),10),top:parseInt(d(this).css("top"),10)})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else d.each(b.alsoResize,function(c){a(c)}); -else a(b.alsoResize)},resize:function(){var b=d(this).data("resizable"),a=b.options,c=b.originalSize,e=b.originalPosition,g={height:b.size.height-c.height||0,width:b.size.width-c.width||0,top:b.position.top-e.top||0,left:b.position.left-e.left||0},f=function(h,i){d(h).each(function(){var j=d(this),l=d(this).data("resizable-alsoresize"),p={};d.each((i&&i.length?i:["width","height","top","left"])||["width","height","top","left"],function(n,o){if((n=(l[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(/relative/.test(j.css("position"))&& -d.browser.opera){b._revertToRelativePosition=true;j.css({position:"absolute",top:"auto",left:"auto"})}j.css(p)})};typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?d.each(a.alsoResize,function(h,i){f(h,i)}):f(a.alsoResize)},stop:function(){var b=d(this).data("resizable");if(b._revertToRelativePosition&&d.browser.opera){b._revertToRelativePosition=false;el.css({position:"relative"})}d(this).removeData("resizable-alsoresize-start")}});d.ui.plugin.add("resizable","animate",{stop:function(b){var a= -d(this).data("resizable"),c=a.options,e=a._proportionallyResizeElements,g=e.length&&/textarea/i.test(e[0].nodeName),f=g&&d.ui.hasScroll(e[0],"left")?0:a.sizeDiff.height;g={width:a.size.width-(g?0:a.sizeDiff.width),height:a.size.height-f};f=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(d.extend(g,h&&f?{top:h,left:f}:{}),{duration:c.animateDuration,easing:c.animateEasing, -step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};e&&e.length&&d(e[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});d.ui.plugin.add("resizable","containment",{start:function(){var b=d(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof d?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement= -d(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:d(document),left:0,top:0,width:d(document).width(),height:d(document).height()||document.body.parentNode.scrollHeight}}else{var e=d(a),g=[];d(["Top","Right","Left","Bottom"]).each(function(i,j){g[i]=m(e.css("padding"+j))});b.containerOffset=e.offset();b.containerPosition=e.position();b.containerSize={height:e.innerHeight()-g[3],width:e.innerWidth()-g[1]};c=b.containerOffset; -var f=b.containerSize.height,h=b.containerSize.width;h=d.ui.hasScroll(a,"left")?a.scrollWidth:h;f=d.ui.hasScroll(a)?a.scrollHeight:f;b.parentData={element:a,left:c.left,top:c.top,width:h,height:f}}}},resize:function(b){var a=d(this).data("resizable"),c=a.options,e=a.containerOffset,g=a.position;b=a._aspectRatio||b.shiftKey;var f={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))f=e;if(g.left<(a._helper?e.left:0)){a.size.width+=a._helper?a.position.left-e.left: -a.position.left-f.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?e.left:0}if(g.top<(a._helper?e.top:0)){a.size.height+=a._helper?a.position.top-e.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?e.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-f.left:a.offset.left-f.left)+a.sizeDiff.width);e=Math.abs((a._helper?a.offset.top-f.top:a.offset.top- -e.top)+a.sizeDiff.height);g=a.containerElement.get(0)==a.element.parent().get(0);f=/relative|absolute/.test(a.containerElement.css("position"));if(g&&f)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(e+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-e;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=d(this).data("resizable"),a=b.options,c=b.containerOffset,e=b.containerPosition, -g=b.containerElement,f=d(b.helper),h=f.offset(),i=f.outerWidth()-b.sizeDiff.width;f=f.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(g.css("position"))&&d(this).css({left:h.left-e.left-c.left,width:i,height:f});b._helper&&!a.animate&&/static/.test(g.css("position"))&&d(this).css({left:h.left-e.left-c.left,width:i,height:f})}});d.ui.plugin.add("resizable","ghost",{start:function(){var b=d(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25, -display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=d(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=d(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});d.ui.plugin.add("resizable","grid",{resize:function(){var b= -d(this).data("resizable"),a=b.options,c=b.size,e=b.originalSize,g=b.originalPosition,f=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-e.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-e.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(f)){b.size.width=e.width+h;b.size.height=e.height+a}else if(/^(ne)$/.test(f)){b.size.width=e.width+h;b.size.height=e.height+a;b.position.top=g.top-a}else{if(/^(sw)$/.test(f)){b.size.width=e.width+h;b.size.height= -e.height+a}else{b.size.width=e.width+h;b.size.height=e.height+a;b.position.top=g.top-a}b.position.left=g.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery); -; -/* - * jQuery UI Selectable 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Selectables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function($) { - -$.widget("ui.selectable", $.ui.mouse, { - options: { - appendTo: 'body', - autoRefresh: true, - distance: 0, - filter: '*', - tolerance: 'touch' - }, - _create: function() { - var self = this; - - this.element.addClass("ui-selectable"); - - this.dragged = false; - - // cache selectee children based on filter - var selectees; - this.refresh = function() { - selectees = $(self.options.filter, self.element[0]); - selectees.each(function() { - var $this = $(this); - var pos = $this.offset(); - $.data(this, "selectable-item", { - element: this, - $element: $this, - left: pos.left, - top: pos.top, - right: pos.left + $this.outerWidth(), - bottom: pos.top + $this.outerHeight(), - startselected: false, - selected: $this.hasClass('ui-selected'), - selecting: $this.hasClass('ui-selecting'), - unselecting: $this.hasClass('ui-unselecting') - }); - }); - }; - this.refresh(); - - this.selectees = selectees.addClass("ui-selectee"); - - this._mouseInit(); - - this.helper = $("

"); - }, - - destroy: function() { - this.selectees - .removeClass("ui-selectee") - .removeData("selectable-item"); - this.element - .removeClass("ui-selectable ui-selectable-disabled") - .removeData("selectable") - .unbind(".selectable"); - this._mouseDestroy(); - - return this; - }, - - _mouseStart: function(event) { - var self = this; - - this.opos = [event.pageX, event.pageY]; - - if (this.options.disabled) - return; - - var options = this.options; - - this.selectees = $(options.filter, this.element[0]); - - this._trigger("start", event); - - $(options.appendTo).append(this.helper); - // position helper (lasso) - this.helper.css({ - "z-index": 100, - "position": "absolute", - "left": event.clientX, - "top": event.clientY, - "width": 0, - "height": 0 - }); - - if (options.autoRefresh) { - this.refresh(); - } - - this.selectees.filter('.ui-selected').each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.startselected = true; - if (!event.metaKey) { - selectee.$element.removeClass('ui-selected'); - selectee.selected = false; - selectee.$element.addClass('ui-unselecting'); - selectee.unselecting = true; - // selectable UNSELECTING callback - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - }); - - $(event.target).parents().andSelf().each(function() { - var selectee = $.data(this, "selectable-item"); - if (selectee) { - var doSelect = !event.metaKey || !selectee.$element.hasClass('ui-selected'); - selectee.$element - .removeClass(doSelect ? "ui-unselecting" : "ui-selected") - .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); - selectee.unselecting = !doSelect; - selectee.selecting = doSelect; - selectee.selected = doSelect; - // selectable (UN)SELECTING callback - if (doSelect) { - self._trigger("selecting", event, { - selecting: selectee.element - }); - } else { - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - return false; - } - }); - - }, - - _mouseDrag: function(event) { - var self = this; - this.dragged = true; - - if (this.options.disabled) - return; - - var options = this.options; - - var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY; - if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; } - if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; } - this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); - - this.selectees.each(function() { - var selectee = $.data(this, "selectable-item"); - //prevent helper from being selected if appendTo: selectable - if (!selectee || selectee.element == self.element[0]) - return; - var hit = false; - if (options.tolerance == 'touch') { - hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); - } else if (options.tolerance == 'fit') { - hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); - } - - if (hit) { - // SELECT - if (selectee.selected) { - selectee.$element.removeClass('ui-selected'); - selectee.selected = false; - } - if (selectee.unselecting) { - selectee.$element.removeClass('ui-unselecting'); - selectee.unselecting = false; - } - if (!selectee.selecting) { - selectee.$element.addClass('ui-selecting'); - selectee.selecting = true; - // selectable SELECTING callback - self._trigger("selecting", event, { - selecting: selectee.element - }); - } - } else { - // UNSELECT - if (selectee.selecting) { - if (event.metaKey && selectee.startselected) { - selectee.$element.removeClass('ui-selecting'); - selectee.selecting = false; - selectee.$element.addClass('ui-selected'); - selectee.selected = true; - } else { - selectee.$element.removeClass('ui-selecting'); - selectee.selecting = false; - if (selectee.startselected) { - selectee.$element.addClass('ui-unselecting'); - selectee.unselecting = true; - } - // selectable UNSELECTING callback - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - if (selectee.selected) { - if (!event.metaKey && !selectee.startselected) { - selectee.$element.removeClass('ui-selected'); - selectee.selected = false; - - selectee.$element.addClass('ui-unselecting'); - selectee.unselecting = true; - // selectable UNSELECTING callback - self._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - } - }); - - return false; - }, - - _mouseStop: function(event) { - var self = this; - - this.dragged = false; - - var options = this.options; - - $('.ui-unselecting', this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass('ui-unselecting'); - selectee.unselecting = false; - selectee.startselected = false; - self._trigger("unselected", event, { - unselected: selectee.element - }); - }); - $('.ui-selecting', this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass('ui-selecting').addClass('ui-selected'); - selectee.selecting = false; - selectee.selected = true; - selectee.startselected = true; - self._trigger("selected", event, { - selected: selectee.element - }); - }); - this._trigger("stop", event); - - this.helper.remove(); - - return false; - } - -}); - -$.extend($.ui.selectable, { - version: "1.8.2" -}); - -})(jQuery); -(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), -selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("
")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, -c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({"z-index":100,position:"absolute",left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting"); -b.unselecting=true;f._trigger("unselecting",c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f= -this;this.dragged=true;if(!this.options.disabled){var d=this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); -this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this, -arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem= -c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset, -{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment(); -if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start", -a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute"); -if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a, -c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]== -document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp();this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate", -null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem): -d(this.domPosition.parent).prepend(this.currentItem);return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c}, -_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a= -this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)? -h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"), -b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)? -i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement, -c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height= -this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()- -parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0], -this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b= -1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update", -g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity", -this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()==location.href.toLowerCase()}},_create:function(){var a=this.options,b=this;this.running=0;this.element.addClass("ui-accordion ui-widget ui-helper-reset"); -this.element.children("li").addClass("ui-accordion-li-fix");this.headers=this.element.find(a.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){c(this).removeClass("ui-state-focus")});this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); -if(a.navigation){var d=this.element.find("a").filter(a.navigationFilter);if(d.length){var f=d.closest(".ui-accordion-header");this.active=f.length?f:d.closest(".ui-accordion-content").prev()}}this.active=this._findActive(this.active||a.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");this.active.next().addClass("ui-accordion-content-active");this._createIcons();this.resize();this.element.attr("role","tablist");this.headers.attr("role", -"tab").bind("keydown",function(g){return b._keydown(g)}).next().attr("role","tabpanel");this.headers.not(this.active||"").attr("aria-expanded","false").attr("tabIndex","-1").next().hide();this.active.length?this.active.attr("aria-expanded","true").attr("tabIndex","0"):this.headers.eq(0).attr("tabIndex","0");c.browser.safari||this.headers.find("a").attr("tabIndex","-1");a.event&&this.headers.bind(a.event+".accordion",function(g){b._clickHandler.call(b,g,this);g.preventDefault()})},_createIcons:function(){var a= -this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.find(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role").unbind(".accordion").removeData("accordion"); -this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex");this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");if(a.autoHeight||a.fillHeight)b.css("height", -"");return this},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();b&&this._createIcons()}},_keydown:function(a){var b=c.ui.keyCode;if(!(this.options.disabled||a.altKey||a.ctrlKey)){var d=this.headers.length,f=this.headers.index(a.target),g=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:g=this.headers[(f+1)%d];break;case b.LEFT:case b.UP:g=this.headers[(f-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target}, -a.target);a.preventDefault()}if(g){c(a.target).attr("tabIndex","-1");c(g).attr("tabIndex","0");g.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0, -b-c(this).innerHeight()+c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a=="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d= -this.options;if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]==this.active[0];d.active=d.collapsible&&b?false:c(".ui-accordion-header",this.element).index(a);if(!(this.running||!d.collapsible&&b)){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").find(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected); -a.next().addClass("ui-accordion-content-active")}e=a.next();f=this.active.next();g={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):e,oldContent:f};d=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(e,f,g,b,d)}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); -this.active.next().addClass("ui-accordion-content-active");var f=this.active.next(),g={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:f},e=this.active=c([]);this._toggle(e,f,g)}},_toggle:function(a,b,d,f,g){var e=this.options,k=this;this.toShow=a;this.toHide=b;this.data=d;var i=function(){if(k)return k._completed.apply(k,arguments)};this._trigger("changestart",null,this.data);this.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&f?{toShow:c([]), -toHide:b,complete:i,down:g,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:i,down:g,autoHeight:e.autoHeight||e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;f=c.ui.accordion.animations;var h=e.duration,j=e.animated;if(j&&!f[j]&&!c.easing[j])j="slide";f[j]||(f[j]=function(l){this.slide(l,{easing:j, -duration:h||700})});f[j](d)}else{if(e.collapsible&&f)a.toggle();else{b.hide();a.show()}i(true)}b.prev().attr("aria-expanded","false").attr("tabIndex","-1").blur();a.prev().attr("aria-expanded","true").attr("tabIndex","0").focus()},_completed:function(a){var b=this.options;this.running=a?0:--this.running;if(!this.running){b.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion, -{version:"1.8.2",animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),f=0,g={},e={},k;b=a.toShow;k=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(i,h){e[h]="hide";i=(""+c.css(a.toShow[0], -h)).match(/^([\d+-.]+)(.*)$/);g[h]={value:i[1],unit:i[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(e,{step:function(i,h){if(h.prop=="height")f=h.end-h.start===0?0:(h.now-h.start)/(h.end-h.start);a.toShow[0].style[h.prop]=f*g[h.prop].value+g[h.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css("width",k);a.toShow.css({overflow:d});a.complete()}})}else a.toHide.animate({height:"hide"}, -a);else a.toShow.animate({height:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); -;/* - * jQuery UI Autocomplete 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Autocomplete - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.position.js - */ -(function(e){e.widget("ui.autocomplete",{options:{minLength:1,delay:300},_create:function(){var a=this,c=this.element[0].ownerDocument;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(d){var b=e.ui.keyCode;switch(d.keyCode){case b.PAGE_UP:a._move("previousPage",d);break;case b.PAGE_DOWN:a._move("nextPage",d);break;case b.UP:a._move("previous",d);d.preventDefault(); -break;case b.DOWN:a._move("next",d);d.preventDefault();break;case b.ENTER:case b.NUMPAD_ENTER:a.menu.active&&d.preventDefault();case b.TAB:if(!a.menu.active)return;a.menu.select(d);break;case b.ESCAPE:a.element.val(a.term);a.close(d);break;case b.LEFT:case b.RIGHT:case b.SHIFT:case b.CONTROL:case b.ALT:case b.COMMAND:case b.COMMAND_RIGHT:case b.INSERT:case b.CAPS_LOCK:case b.END:case b.HOME:break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){a.search(null,d)},a.options.delay); -break}}).bind("focus.autocomplete",function(){a.selectedItem=null;a.previous=a.element.val()}).bind("blur.autocomplete",function(d){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(d);a._change(d)},150)});this._initSource();this.response=function(){return a._response.apply(a,arguments)};this.menu=e("
    ").addClass("ui-autocomplete").appendTo("body",c).mousedown(function(){setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(d,b){b=b.item.data("item.autocomplete"); -false!==a._trigger("focus",null,{item:b})&&/^key/.test(d.originalEvent.type)&&a.element.val(b.value)},selected:function(d,b){b=b.item.data("item.autocomplete");false!==a._trigger("select",d,{item:b})&&a.element.val(b.value);a.close(d);d=a.previous;if(a.element[0]!==c.activeElement){a.element.focus();a.previous=d}a.selectedItem=b},blur:function(){a.menu.element.is(":visible")&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");e.fn.bgiframe&&this.menu.element.bgiframe()}, -destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();e.Widget.prototype.destroy.call(this)},_setOption:function(a){e.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource()},_initSource:function(){var a,c;if(e.isArray(this.options.source)){a=this.options.source;this.source=function(d,b){b(e.ui.autocomplete.filter(a,d.term))}}else if(typeof this.options.source=== -"string"){c=this.options.source;this.source=function(d,b){e.getJSON(c,d,b)}}else this.source=this.options.source},search:function(a,c){a=a!=null?a:this.element.val();if(a.length").data("item.autocomplete", -c).append(""+c.label+"").appendTo(a)},_move:function(a,c){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](c);else this.search(null,c)},widget:function(){return this.menu.element}});e.extend(e.ui.autocomplete,{escapeRegex:function(a){return a.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")},filter:function(a,c){var d=new RegExp(e.ui.autocomplete.escapeRegex(c), -"i");return e.grep(a,function(b){return d.test(b.label||b.value||b)})}})})(jQuery); -(function(e){e.widget("ui.menu",{_create:function(){var a=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(c){if(e(c.target).closest(".ui-menu-item a").length){c.preventDefault();a.select(c)}});this.refresh()},refresh:function(){var a=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", --1).mouseenter(function(c){a.activate(c,e(this).parent())}).mouseleave(function(){a.deactivate()})},activate:function(a,c){this.deactivate();if(this.hasScroll()){var d=c.offset().top-this.element.offset().top,b=this.element.attr("scrollTop"),f=this.element.height();if(d<0)this.element.attr("scrollTop",b+d);else d>f&&this.element.attr("scrollTop",b+d-f+c.height())}this.active=c.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",a,{item:c})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id"); -this._trigger("blur");this.active=null}},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prev().length},last:function(){return this.active&&!this.active.next().length},move:function(a,c,d){if(this.active){a=this.active[a+"All"](".ui-menu-item").eq(0);a.length?this.activate(d,a):this.activate(d,this.element.children(c))}else this.activate(d,this.element.children(c))},nextPage:function(a){if(this.hasScroll())if(!this.active|| -this.last())this.activate(a,this.element.children(":first"));else{var c=this.active.offset().top,d=this.element.height(),b=this.element.children("li").filter(function(){var f=e(this).offset().top-c-d+e(this).height();return f<10&&f>-10});b.length||(b=this.element.children(":last"));this.activate(a,b)}else this.activate(a,this.element.children(!this.active||this.last()?":first":":last"))},previousPage:function(a){if(this.hasScroll())if(!this.active||this.first())this.activate(a,this.element.children(":last")); -else{var c=this.active.offset().top,d=this.element.height();result=this.element.children("li").filter(function(){var b=e(this).offset().top-c+d-e(this).height();return b<10&&b>-10});result.length||(result=this.element.children(":first"));this.activate(a,result)}else this.activate(a,this.element.children(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()
    ").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary;if(d.primary||d.secondary){b.addClass("ui-button-text-icon"+(e?"s":""));d.primary&&b.prepend("");d.secondary&&b.append("");if(!this.options.text){b.addClass(e?"ui-button-icons-only":"ui-button-icon-only").removeClass("ui-button-text-icons ui-button-text-icon"); -this.hasTitle||b.attr("title",c)}}else b.addClass("ui-button-text-only")}}});a.widget("ui.buttonset",{_create:function(){this.element.addClass("ui-buttonset");this._init()},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(":button, :submit, :reset, :checkbox, :radio, a, :data(button)").filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()}, -destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); -;/* - * jQuery UI Dialog 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Dialog - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.button.js - * jquery.ui.draggable.js - * jquery.ui.mouse.js - * jquery.ui.position.js - * jquery.ui.resizable.js - */ -(function(c){c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:"center",resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");var a=this,b=a.options,d=b.title||a.originalTitle||" ",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ -b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g), -h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id", -e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); -a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!== -b.uiDialog[0])d=Math.max(d,c(this).css("z-index"))});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=1;d.uiDialog.css("z-index", -c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;d.next().length&&d.appendTo("body");a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target=== -f[0]&&e.shiftKey){g.focus(1);return false}}});c([]).add(d.find(".ui-dialog-content :tabbable:first")).add(d.find(".ui-dialog-buttonpane :tabbable:first")).add(d).filter(":first").focus();a._trigger("open");a._isOpen=true;return a}},_createButtons:function(a){var b=this,d=false,e=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,function(){return!(d=true)});if(d){c.each(a, -function(g,f){g=c('').text(g).click(function(){f.apply(b.element[0],arguments)}).appendTo(e);c.fn.button&&g.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging"); -b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,originalSize:f.originalSize,position:f.position,size:f.size}}a=a===undefined?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position"); -a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop", -f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0];a=a||c.ui.dialog.prototype.options.position;if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(e,g){if(+b[e]===b[e]){d[e]=b[e];b[e]= -g}})}else if(typeof a==="object"){if("left"in a){b[0]="left";d[0]=a.left}else if("right"in a){b[0]="right";d[0]=-a.right}if("top"in a){b[1]="top";d[1]=a.top}else if("bottom"in a){b[1]="bottom";d[1]=-a.bottom}}(a=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position({my:b.join(" "),at:b.join(" "),offset:d.join(" "),of:window,collision:"fit",using:function(e){var g=c(this).css(e).offset().top;g<0&&c(this).css("top",e.top-g)}});a||this.uiDialog.hide()},_setOption:function(a, -b){var d=this,e=d.uiDialog,g=e.is(":data(resizable)"),f=false;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):e.removeClass("ui-dialog-disabled");break;case "draggable":b?d._makeDraggable():e.draggable("destroy");break; -case "height":f=true;break;case "maxHeight":g&&e.resizable("option","maxHeight",b);f=true;break;case "maxWidth":g&&e.resizable("option","maxWidth",b);f=true;break;case "minHeight":g&&e.resizable("option","minHeight",b);f=true;break;case "minWidth":g&&e.resizable("option","minWidth",b);f=true;break;case "position":d._position(b);break;case "resizable":g&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title", -d.uiDialogTitlebar).html(""+(b||" "));break;case "width":f=true;break}c.Widget.prototype._setOption.apply(d,arguments);f&&d._size()},_size:function(){var a=this.options,b;this.element.css({width:"auto",minHeight:0,height:0});b=this.uiDialog.css({height:"auto",width:a.width}).height();this.element.css(a.height==="auto"?{minHeight:Math.max(a.minHeight-b,0),height:"auto"}:{minHeight:0,height:Math.max(a.height-b,0)}).show();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight", -this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.2",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&& -c(document).bind(c.ui.dialog.overlay.events,function(d){return c(d.target).zIndex()>=c.ui.dialog.overlay.maxZ})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||c("
    ").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&& -b.bgiframe();this.instances.push(b);return b},destroy:function(a){this.oldInstances.push(this.instances.splice(c.inArray(a,this.instances),1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var b=0;c.each(this.instances,function(){b=Math.max(b,this.css("z-index"))});this.maxZ=b},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight, -document.body.offsetHeight);return a");if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}else this.range=d("
    ");this.range.appendTo(this.element).addClass("ui-slider-range");if(b.range==="min"||b.range==="max")this.range.addClass("ui-slider-range-"+b.range);this.range.addClass("ui-widget-header")}d(".ui-slider-handle",this.element).length===0&&d("").appendTo(this.element).addClass("ui-slider-handle"); -if(b.values&&b.values.length)for(;d(".ui-slider-handle",this.element).length").appendTo(this.element).addClass("ui-slider-handle");this.handles=d(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(c){c.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur(); -else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(c){d(this).data("index.ui-slider-handle",c)});this.handles.keydown(function(c){var e=true,f=d(this).data("index.ui-slider-handle"),g,h,i;if(!a.options.disabled){switch(c.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:e= -false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");g=a._start(c,f);if(g===false)return}break}i=a.options.step;g=a.options.values&&a.options.values.length?(h=a.values(f)):(h=a.value());switch(c.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(g+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(g-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(g=== -a._valueMax())return;h=a._trimAlignValue(g+i);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(g===a._valueMin())return;h=a._trimAlignValue(g-i);break}a._slide(c,f,h);return e}}).keyup(function(c){var e=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(c,e);a._change(c,e);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"); -this._mouseDestroy();return this},_mouseCapture:function(a){var b=this.options,c,e,f,g,h,i;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c={x:a.pageX,y:a.pageY};e=this._normValueFromMouse(c);f=this._valueMax()-this._valueMin()+1;h=this;this.handles.each(function(j){var k=Math.abs(e-h.values(j));if(f>k){f=k;g=d(this);i=j}});if(b.range===true&&this.values(1)===b.min){i+=1;g=d(this.handles[i])}if(this._start(a, -i)===false)return false;this._mouseSliding=true;h._handleIndex=i;g.addClass("ui-state-active").focus();b=g.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-g.width()/2,top:a.pageY-b.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)};e=this._normValueFromMouse(c);this._slide(a,i,e);return this._animateOff=true},_mouseStart:function(){return true}, -_mouseDrag:function(a){var b=this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b; -if(this.orientation==="horizontal"){b=this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value= -this.values(b);c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var e;if(this.options.values&&this.options.values.length){e=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>e||b===1&&c1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;e=arguments[0];for(f=0;fthis._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=a%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a= -this.options.range,b=this.options,c=this,e=!this._animateOff?b.animate:false,f,g={},h,i,j,k;if(this.options.values&&this.options.values.length)this.handles.each(function(l){f=(c.values(l)-c._valueMin())/(c._valueMax()-c._valueMin())*100;g[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](g,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(l===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},b.animate);if(l===1)c.range[e?"animate":"css"]({width:f- -h+"%"},{queue:false,duration:b.animate})}else{if(l===0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},b.animate);if(l===1)c.range[e?"animate":"css"]({height:f-h+"%"},{queue:false,duration:b.animate})}h=f});else{i=this.value();j=this._valueMin();k=this._valueMax();f=k!==j?(i-j)/(k-j)*100:0;g[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](g,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"}, -b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.2"})})(jQuery); -;/* - * jQuery UI Tabs 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Tabs - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(d){function s(){return++u}function v(){return++w}var u=0,w=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:'
  • #{label}
  • '},_create:function(){this._tabify(true)},_setOption:function(c,e){if(c=="selected")this.options.collapsible&& -e==this.options.selected||this.select(e);else{this.options[c]=e;this._tabify()}},_tabId:function(c){return c.title&&c.title.replace(/\s/g,"_").replace(/[^A-Za-z0-9\-_:\.]/g,"")||this.options.idPrefix+s()},_sanitizeSelector:function(c){return c.replace(/:/g,"\\:")},_cookie:function(){var c=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+v());return d.cookie.apply(null,[c].concat(d.makeArray(arguments)))},_ui:function(c,e){return{tab:c,panel:e,index:this.anchors.index(c)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var c= -d(this);c.html(c.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function e(g,f){g.css({display:""});!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}this.list=this.element.find("ol,ul").eq(0);this.lis=d("li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);var a=this,b=this.options,h=/^#.+/;this.anchors.each(function(g,f){var j=d(f).attr("href"),l=j.split("#")[0],p;if(l&&(l===location.toString().split("#")[0]|| -(p=d("base")[0])&&l===p.href)){j=f.hash;f.href=j}if(h.test(j))a.panels=a.panels.add(a._sanitizeSelector(j));else if(j!="#"){d.data(f,"href.tabs",j);d.data(f,"load.tabs",j.replace(/#.*$/,""));j=a._tabId(f);f.href="#"+j;f=d("#"+j);if(!f.length){f=d(b.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else b.disabled.push(g)});if(c){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); -this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(b.selected===undefined){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){b.selected=g;return false}});if(typeof b.selected!="number"&&b.cookie)b.selected=parseInt(a._cookie(),10);if(typeof b.selected!="number"&&this.lis.filter(".ui-tabs-selected").length)b.selected= -this.lis.index(this.lis.filter(".ui-tabs-selected"));b.selected=b.selected||(this.lis.length?0:-1)}else if(b.selected===null)b.selected=-1;b.selected=b.selected>=0&&this.anchors[b.selected]||b.selected<0?b.selected:0;b.disabled=d.unique(b.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(b.selected,b.disabled)!=-1&&b.disabled.splice(d.inArray(b.selected,b.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); -if(b.selected>=0&&this.anchors.length){this.panels.eq(b.selected).removeClass("ui-tabs-hide");this.lis.eq(b.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[b.selected],a.panels[b.selected]))});this.load(b.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else b.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));this.element[b.collapsible?"addClass": -"removeClass"]("ui-tabs-collapsible");b.cookie&&this._cookie(b.selected,b.cookie);c=0;for(var i;i=this.lis[c];c++)d(i)[d.inArray(c,b.disabled)!=-1&&!d(i).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");b.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(b.event!="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+g)};this.lis.bind("mouseover.tabs", -function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(b.fx)if(d.isArray(b.fx)){m=b.fx[0];o=b.fx[1]}else m=o=b.fx;var q=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",function(){e(f,o);a._trigger("show", -null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},r=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};this.anchors.bind(b.event+".tabs", -function(){var g=this,f=d(this).closest("li"),j=a.panels.filter(":not(.ui-tabs-hide)"),l=d(a._sanitizeSelector(this.hash));if(f.hasClass("ui-tabs-selected")&&!b.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}b.selected=a.anchors.index(this);a.abort();if(b.collapsible)if(f.hasClass("ui-tabs-selected")){b.selected=-1;b.cookie&&a._cookie(b.selected,b.cookie);a.element.queue("tabs",function(){r(g, -j)}).dequeue("tabs");this.blur();return false}else if(!j.length){b.cookie&&a._cookie(b.selected,b.cookie);a.element.queue("tabs",function(){q(g,l)});a.load(a.anchors.index(this));this.blur();return false}b.cookie&&a._cookie(b.selected,b.cookie);if(l.length){j.length&&a.element.queue("tabs",function(){r(g,j)});a.element.queue("tabs",function(){q(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";d.browser.msie&&this.blur()});this.anchors.bind("click.tabs", -function(){return false})},destroy:function(){var c=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(b,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this, -"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});c.cookie&&this._cookie(null,c.cookie);return this},add:function(c,e,a){if(a===undefined)a=this.anchors.length;var b=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,e));c=!c.indexOf("#")?c.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs", -true);var i=d("#"+c);i.length||(i=d(h.panelTemplate).attr("id",c).data("destroy.tabs",true));i.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);i.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);i.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");i.removeClass("ui-tabs-hide"); -this.element.queue("tabs",function(){b._trigger("show",null,b._ui(b.anchors[0],b.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(c){var e=this.options,a=this.lis.eq(c).remove(),b=this.panels.eq(c).remove();if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(c+(c+1=c?--h:h});this._tabify();this._trigger("remove", -null,this._ui(a.find("a")[0],b[0]));return this},enable:function(c){var e=this.options;if(d.inArray(c,e.disabled)!=-1){this.lis.eq(c).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=c});this._trigger("enable",null,this._ui(this.anchors[c],this.panels[c]));return this}},disable:function(c){var e=this.options;if(c!=e.selected){this.lis.eq(c).addClass("ui-state-disabled");e.disabled.push(c);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[c],this.panels[c]))}return this}, -select:function(c){if(typeof c=="string")c=this.anchors.index(this.anchors.filter("[href$="+c+"]"));else if(c===null)c=-1;if(c==-1&&this.options.collapsible)c=this.options.selected;this.anchors.eq(c).trigger(this.options.event+".tabs");return this},load:function(c){var e=this,a=this.options,b=this.anchors.eq(c)[0],h=d.data(b,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(b,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(c).addClass("ui-state-processing"); -if(a.spinner){var i=d("span",b);i.data("label.tabs",i.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){d(e._sanitizeSelector(b.hash)).html(k);e._cleanup();a.cache&&d.data(b,"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[c],e.panels[c]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[c],e.panels[c]));try{a.ajaxOptions.error(k,n,c,b)}catch(m){}}}));e.element.dequeue("tabs");return this}}, -abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},url:function(c,e){this.anchors.eq(c).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.2"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(c,e){var a=this,b=this.options,h=a._rotate||(a._rotate= -function(i){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=b.selected;a.select(++k')}function E(a,b){d.extend(a, -b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.2"}});var y=(new Date).getTime();d.extend(J.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]= -f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('
    ')}}, -_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& -b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f== -""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, -c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), -true);this._updateDatepicker(b);this._updateAlternate(b)}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});b=b&&b.constructor== -Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]); -d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}}, -_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b= -d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false; -for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target|| -a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a); -d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&& -d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=d.datepicker._getBorders(b.dpDiv);b.dpDiv.find("iframe.ui-datepicker-cover").css({left:-i[0],top:-i[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f, -h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover"); -this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover"); -this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);var e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"); -a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus()},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(), -k=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>k&&k>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"]; -a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val(): -"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&& -!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth; -b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){a=this._getInst(d(a)[0]); -a.input&&a._selectingMonthYear&&!d.browser.msie&&a.input.focus();a._selectingMonthYear=!a._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a, -"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")|| -this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null; -for(var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff,f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,k=c=-1,l=-1,u=-1,j=false,o=function(p){(p=z+1-1){k=1;l=u;do{e=this._getDaysInMonth(c,k-1);if(l<=e)break;k++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c, -k-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=k||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c? -c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=j+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear|| -a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),k=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay? -new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),j=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=j&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-k,1)),this._getFormatConfig(a)); -n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m, -g+k,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";k=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&& -a.currentDay?u:b;k=!h?k:this.formatDate(k,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;k=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),w=this._get(a,"showOtherMonths"),G=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var K=this._getDefaultDate(a),H="",C=0;C1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='
    '+(/all|left/.test(t)&&C==0?c? -f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,j,o,C>0||D>0,z,v)+'
    ';var A=k?'":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}x+=A+"";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, -A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var N=0;N";var O=!k?"":'";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,I=B&&!G||!F[0]||j&&qo;O+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=O+""}g++;if(g>11){g=0;m++}x+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(q)+""+(B&&!w?" ":I?''+q.getDate()+ -"":''+q.getDate()+"")+"
    "+(l?""+(i[0]>0&&D==i[1]-1?'
    ':""):"");L+=x}H+=L}H+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': -"");a._keyEvent=false;return H},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var k=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),j='
    ',o="";if(h||!k)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(j+=o+(h||!(k&&l)?" ":""));if(h||!l)j+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b, -i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(j+='"}j+=this._get(a,"yearSuffix");if(u)j+=(h||!(k&&l)?" ":"")+o;j+="
    ";return j},_adjustInstDate:function(a,b,c){var e= -a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, -"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); -c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, -"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= -function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); -return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new J;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.2";window["DP_jQuery_"+y]=d})(jQuery); -;/* - * jQuery UI Progressbar 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(b){b.widget("ui.progressbar",{options:{value:0},_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this._valueMin(),"aria-valuemax":this._valueMax(),"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); -this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===undefined)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){switch(a){case "value":this.options.value=c;this._refreshValue();this._trigger("change");break}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;if(athis._valueMax())a=this._valueMax();return a}, -_valueMin:function(){return 0},_valueMax:function(){return 100},_refreshValue:function(){var a=this.value();this.valueDiv[a===this._valueMax()?"addClass":"removeClass"]("ui-corner-right").width(a+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.2"})})(jQuery); -;/* - * jQuery UI Effects 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Effects/ - */ -jQuery.effects||function(f){function k(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], -16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return l.transparent;return l[f.trim(c).toLowerCase()]}function q(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return k(b)}function m(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, -a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function n(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in r||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function s(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function j(c,a,b,d){if(typeof c=="object"){d= -a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(f.isFunction(b)){d=b;b=null}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:f.fx.speeds[b]||f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=q(b.elem,a);b.end=k(b.end);b.colorInit= -true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var l={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189, -183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255, -165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},o=["add","remove","toggle"],r={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,d){if(f.isFunction(b)){d=b;b=null}return this.each(function(){var e=f(this),g=e.attr("style")||" ",h=n(m.call(this)),p,t=e.attr("className");f.each(o,function(u, -i){c[i]&&e[i+"Class"](c[i])});p=n(m.call(this));e.attr("className",t);e.animate(s(h,p),a,b,function(){f.each(o,function(u,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)})})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a? -f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===undefined?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.2",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"}); -c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=j.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c|| -typeof c=="number"||f.fx.speeds[c])return this._show.apply(this,arguments);else{var a=j.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c])return this._hide.apply(this,arguments);else{var a=j.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||typeof c=="boolean"||f.isFunction(c))return this.__toggle.apply(this, -arguments);else{var a=j.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c, -a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+ -b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2, -10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)* -a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ -e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); -;/* - * jQuery UI Effects Fold 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Effects/Fold - * - * Depends: - * jquery.effects.core.js - */ -(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","left"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],10)/100* -f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); -;/* - * jQuery UI Effects Highlight 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Effects/Highlight - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& -this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Pulsate 1.8.2 - * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Effects/Pulsate - * - * Depends: - * jquery.effects.core.js - */ -(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); -b.dequeue()})})}})(jQuery); -; \ No newline at end of file diff --git a/dbReports/iondb/media/jquery/js/plupload/jquery.form.js b/dbReports/iondb/media/jquery/js/plupload/jquery.form.js deleted file mode 100644 index dde39427..00000000 --- a/dbReports/iondb/media/jquery/js/plupload/jquery.form.js +++ /dev/null @@ -1,660 +0,0 @@ -/* - * jQuery Form Plugin - * version: 2.36 (07-NOV-2009) - * @requires jQuery v1.2.6 or later - * - * Examples and documentation at: http://malsup.com/jquery/form/ - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - */ -;(function($) { - -/* - Usage Note: - ----------- - Do not use both ajaxSubmit and ajaxForm on the same form. These - functions are intended to be exclusive. Use ajaxSubmit if you want - to bind your own submit handler to the form. For example, - - $(document).ready(function() { - $('#myForm').bind('submit', function() { - $(this).ajaxSubmit({ - target: '#output' - }); - return false; // <-- important! - }); - }); - - Use ajaxForm when you want the plugin to manage all the event binding - for you. For example, - - $(document).ready(function() { - $('#myForm').ajaxForm({ - target: '#output' - }); - }); - - When using ajaxForm, the ajaxSubmit function will be invoked for you - at the appropriate time. -*/ - -/** - * ajaxSubmit() provides a mechanism for immediately submitting - * an HTML form using AJAX. - */ -$.fn.ajaxSubmit = function(options) { - // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) - if (!this.length) { - log('ajaxSubmit: skipping submit process - no element selected'); - return this; - } - - if (typeof options == 'function') - options = { success: options }; - - var url = $.trim(this.attr('action')); - if (url) { - // clean url (don't include hash vaue) - url = (url.match(/^([^#]+)/)||[])[1]; - } - url = url || window.location.href || ''; - - options = $.extend({ - url: url, - type: this.attr('method') || 'GET', - iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' - }, options || {}); - - // hook for manipulating the form data before it is extracted; - // convenient for use with rich editors like tinyMCE or FCKEditor - var veto = {}; - this.trigger('form-pre-serialize', [this, options, veto]); - if (veto.veto) { - log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); - return this; - } - - // provide opportunity to alter form data before it is serialized - if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { - log('ajaxSubmit: submit aborted via beforeSerialize callback'); - return this; - } - - var a = this.formToArray(options.semantic); - if (options.data) { - options.extraData = options.data; - for (var n in options.data) { - if(options.data[n] instanceof Array) { - for (var k in options.data[n]) - a.push( { name: n, value: options.data[n][k] } ); - } - else - a.push( { name: n, value: options.data[n] } ); - } - } - - // give pre-submit callback an opportunity to abort the submit - if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { - log('ajaxSubmit: submit aborted via beforeSubmit callback'); - return this; - } - - // fire vetoable 'validate' event - this.trigger('form-submit-validate', [a, this, options, veto]); - if (veto.veto) { - log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); - return this; - } - - var q = $.param(a); - - if (options.type.toUpperCase() == 'GET') { - options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; - options.data = null; // data is null for 'get' - } - else - options.data = q; // data is the query string for 'post' - - var $form = this, callbacks = []; - if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); - if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); - - // perform a load on the target only if dataType is not provided - if (!options.dataType && options.target) { - var oldSuccess = options.success || function(){}; - callbacks.push(function(data) { - $(options.target).html(data).each(oldSuccess, arguments); - }); - } - else if (options.success) - callbacks.push(options.success); - - options.success = function(data, status) { - for (var i=0, max=callbacks.length; i < max; i++) - callbacks[i].apply(options, [data, status, $form]); - }; - - // are there files to upload? - var files = $('input:file', this).fieldValue(); - var found = false; - for (var j=0; j < files.length; j++) - if (files[j]) - found = true; - - var multipart = false; -// var mp = 'multipart/form-data'; -// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); - - // options.iframe allows user to force iframe mode - // 06-NOV-09: now defaulting to iframe mode if file input is detected - if ((files.length && options.iframe !== false) || options.iframe || found || multipart) { - // hack to fix Safari hang (thanks to Tim Molendijk for this) - // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d - if (options.closeKeepAlive) - $.get(options.closeKeepAlive, fileUpload); - else - fileUpload(); - } - else - $.ajax(options); - - // fire 'notify' event - this.trigger('form-submit-notify', [this, options]); - return this; - - - // private function for handling file uploads (hat tip to YAHOO!) - function fileUpload() { - var form = $form[0]; - - if ($(':input[name=submit]', form).length) { - alert('Error: Form elements must not be named "submit".'); - return; - } - - var opts = $.extend({}, $.ajaxSettings, options); - var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts); - - var id = 'jqFormIO' + (new Date().getTime()); - var $io = $('';n=y.firstChild;j.appendChild(n);b.addEvent(n,"load",function(D){var E=D.target,C,A;if(!k){return}try{C=E.contentWindow.document||E.contentDocument||d.frames[E.id].document}catch(B){p.trigger("Error",{code:b.SECURITY_ERROR,message:b.translate("Security error."),file:k});return}A=C.body.innerHTML;if(A){k.status=b.DONE;k.loaded=1025;k.percent=100;p.trigger("UploadProgress",k);p.trigger("FileUploaded",k,{response:A})}},p.id)}if(p.settings.container){j=e(p.settings.container);if(b.getStyle(j,"position")==="static"){j.style.position="relative"}}p.bind("UploadFile",function(y,B){var C,A;if(B.status==b.DONE||B.status==b.FAILED||y.state==b.STOPPED){return}C=e("form_"+B.id);A=e("input_"+B.id);A.setAttribute("name",y.settings.file_data_name);C.setAttribute("action",y.settings.url);b.each(b.extend({name:B.target_name||B.name},y.settings.multipart_params),function(F,D){var E=a.createElement("input");b.extend(E,{type:"hidden",name:D,value:F});C.insertBefore(E,C.firstChild)});k=B;e("form_"+q).style.top=-1048575+"px";C.submit();C.parentNode.removeChild(C)});p.bind("FileUploaded",function(y){y.refresh()});p.bind("StateChanged",function(y){if(y.state==b.STARTED){u()}if(y.state==b.STOPPED){d.setTimeout(function(){b.removeEvent(n,"load",y.id);if(n.parentNode){n.parentNode.removeChild(n)}},0)}});p.bind("Refresh",function(A){var G,B,C,D,y,H,I,F,E;G=e(A.settings.browse_button);if(G){y=b.getPos(G,e(A.settings.container));H=b.getSize(G);I=e("form_"+q);F=e("input_"+q);b.extend(I.style,{top:y.y+"px",left:y.x+"px",width:H.w+"px",height:H.h+"px"});if(A.features.triggerDialog){if(b.getStyle(G,"position")==="static"){b.extend(G.style,{position:"relative"})}E=parseInt(G.style.zIndex,10);if(isNaN(E)){E=0}b.extend(G.style,{zIndex:E});b.extend(I.style,{zIndex:E-1})}C=A.settings.browse_button_hover;D=A.settings.browse_button_active;B=A.features.triggerDialog?G:I;if(C){b.addEvent(B,"mouseover",function(){b.addClass(G,C)},A.id);b.addEvent(B,"mouseout",function(){b.removeClass(G,C)},A.id)}if(D){b.addEvent(B,"mousedown",function(){b.addClass(G,D)},A.id);b.addEvent(a.body,"mouseup",function(){b.removeClass(G,D)},A.id)}}});f.bind("FilesRemoved",function(y,B){var A,C;for(A=0;A":"gt","&":"amp",'"':"quot","'":"#39"},l=/[<>&\"\']/g,b,c=window.setTimeout,d={},e;function h(){this.returnValue=false}function j(){this.cancelBubble=true}(function(n){var o=n.split(/,/),p,r,q;for(p=0;p0){g.each(o,function(r,q){n[q]=r})}});return n},cleanName:function(n){var o,p;p=[/[\300-\306]/g,"A",/[\340-\346]/g,"a",/\307/g,"C",/\347/g,"c",/[\310-\313]/g,"E",/[\350-\353]/g,"e",/[\314-\317]/g,"I",/[\354-\357]/g,"i",/\321/g,"N",/\361/g,"n",/[\322-\330]/g,"O",/[\362-\370]/g,"o",/[\331-\334]/g,"U",/[\371-\374]/g,"u"];for(o=0;o0?"&":"?")+p}return o},each:function(q,r){var p,o,n;if(q){p=q.length;if(p===b){for(o in q){if(q.hasOwnProperty(o)){if(r(q[o],o)===false){return}}}}else{for(n=0;n1073741824){return Math.round(n/1073741824,1)+" GB"}if(n>1048576){return Math.round(n/1048576,1)+" MB"}if(n>1024){return Math.round(n/1024,1)+" KB"}return n+" b"},getPos:function(o,s){var t=0,r=0,v,u=document,p,q;o=o;s=s||u.body;function n(B){var z,A,w=0,C=0;if(B){A=B.getBoundingClientRect();z=u.compatMode==="CSS1Compat"?u.documentElement:u.body;w=A.left+z.scrollLeft;C=A.top+z.scrollTop}return{x:w,y:C}}if(o&&o.getBoundingClientRect&&(navigator.userAgent.indexOf("MSIE")>0&&u.documentMode!==8)){p=n(o);q=n(s);return{x:p.x-q.x,y:p.y-q.y}}v=o;while(v&&v!=s&&v.nodeType){t+=v.offsetLeft||0;r+=v.offsetTop||0;v=v.offsetParent}v=o.parentNode;while(v&&v!=s&&v.nodeType){t-=v.scrollLeft||0;r-=v.scrollTop||0;v=v.parentNode}return{x:t,y:r}},getSize:function(n){return{w:n.offsetWidth||n.clientWidth,h:n.offsetHeight||n.clientHeight}},parseSize:function(n){var o;if(typeof(n)=="string"){n=/^([0-9]+)([mgk]?)$/.exec(n.toLowerCase().replace(/[^0-9mkg]/g,""));o=n[2];n=+n[1];if(o=="g"){n*=1073741824}if(o=="m"){n*=1048576}if(o=="k"){n*=1024}}return n},xmlEncode:function(n){return n?(""+n).replace(l,function(o){return a[o]?"&"+a[o]+";":o}):n},toArray:function(p){var o,n=[];for(o=0;o=0;o--){if(q[o].key===p||q[o].orig===t){if(s.detachEvent){s.detachEvent("on"+n,q[o].func)}else{if(s.removeEventListener){s.removeEventListener(n,q[o].func,false)}}q[o].orig=null;q[o].func=null;q.splice(o,1);if(t!==b){break}}}if(!q.length){delete d[s[e]][n]}if(g.isEmptyObj(d[s[e]])){delete d[s[e]];try{delete s[e]}catch(r){s[e]=b}}},removeAllEvents:function(o){var n=arguments[1];if(o[e]===b||!o[e]){return}g.each(d[o[e]],function(q,p){g.removeEvent(o,p,n)})}};g.Uploader=function(q){var o={},t,s=[],p;t=new g.QueueProgress();q=g.extend({chunk_size:0,multipart:true,multi_selection:true,file_data_name:"file",filters:[]},q);function r(){var v,w=0,u;if(this.state==g.STARTED){for(u=0;u0?Math.ceil(t.uploaded/s.length*100):0}else{t.bytesPerSec=Math.ceil(t.loaded/((+new Date()-p||1)/1000));t.percent=t.size>0?Math.ceil(t.loaded/t.size*100):0}}g.extend(this,{state:g.STOPPED,runtime:"",features:{},files:s,settings:q,total:t,id:g.guid(),init:function(){var z=this,A,w,v,y=0,x;if(typeof(q.preinit)=="function"){q.preinit(z)}else{g.each(q.preinit,function(C,B){z.bind(B,C)})}q.page_url=q.page_url||document.location.pathname.replace(/\/[^\/]+$/g,"/");if(!/^(\w+:\/\/|\/)/.test(q.url)){q.url=q.page_url+q.url}q.chunk_size=g.parseSize(q.chunk_size);q.max_file_size=g.parseSize(q.max_file_size);z.bind("FilesAdded",function(B,E){var D,C,G=0,H,F=q.filters;if(F&&F.length){H=[];g.each(F,function(I){g.each(I.extensions.split(/,/),function(J){if(/^\s*\*\s*$/.test(J)){H.push("\\.*")}else{H.push("\\."+J.replace(new RegExp("["+("/^$.*+?|()[]{}\\".replace(/./g,"\\$&"))+"]","g"),"\\$&"))}})});H=new RegExp(H.join("|")+"$","i")}for(D=0;Dq.max_file_size){B.trigger("Error",{code:g.FILE_SIZE_ERROR,message:g.translate("File size error."),file:C});continue}s.push(C);G++}if(G){c(function(){z.trigger("QueueChanged");z.refresh()},1)}else{return false}});if(q.unique_names){z.bind("UploadFile",function(B,C){var E=C.name.match(/\.([^.]+)$/),D="tmp";if(E){D=E[1]}C.target_name=C.id+"."+D})}z.bind("UploadProgress",function(B,C){C.percent=C.size>0?Math.ceil(C.loaded/C.size*100):100;n()});z.bind("StateChanged",function(B){if(B.state==g.STARTED){p=(+new Date())}else{if(B.state==g.STOPPED){for(A=B.files.length-1;A>=0;A--){if(B.files[A].status==g.UPLOADING){B.files[A].status=g.QUEUED;n()}}}}});z.bind("QueueChanged",n);z.bind("Error",function(B,C){if(C.file){C.file.status=g.FAILED;n();if(B.state==g.STARTED){c(function(){r.call(z)},1)}}});z.bind("FileUploaded",function(B,C){C.status=g.DONE;C.loaded=C.size;B.trigger("UploadProgress",C);c(function(){r.call(z)},1)});if(q.runtimes){w=[];x=q.runtimes.split(/\s?,\s?/);for(A=0;A=0;u--){if(s[u].id===v){return s[u]}}},removeFile:function(v){var u;for(u=s.length-1;u>=0;u--){if(s[u].id===v.id){return this.splice(u,1)[0]}}},splice:function(w,u){var v;v=s.splice(w===b?0:w,u===b?s.length:u);this.trigger("FilesRemoved",v);this.trigger("QueueChanged");return v},trigger:function(v){var x=o[v.toLowerCase()],w,u;if(x){u=Array.prototype.slice.call(arguments);u[0]=this;for(w=0;w=0;v--){if(x[v].func===w){x.splice(v,1);break}}}else{x=[]}if(!x.length){delete o[u]}}},unbindAll:function(){var u=this;g.each(o,function(w,v){u.unbind(v)})},destroy:function(){this.trigger("Destroy");this.unbindAll()}})};g.File=function(q,o,p){var n=this;n.id=q;n.name=o;n.size=p;n.loaded=0;n.percent=0;n.status=0};g.Runtime=function(){this.getFeatures=function(){};this.init=function(n,o){}};g.QueueProgress=function(){var n=this;n.size=0;n.loaded=0;n.uploaded=0;n.failed=0;n.queued=0;n.percent=0;n.bytesPerSec=0;n.reset=function(){n.size=n.loaded=n.uploaded=n.failed=n.queued=n.percent=n.bytesPerSec=0}};g.runtimes={};window.plupload=g})(); \ No newline at end of file diff --git a/dbReports/iondb/media/jquery/js/plupload/plupload.silverlight.js b/dbReports/iondb/media/jquery/js/plupload/plupload.silverlight.js deleted file mode 100755 index beecef10..00000000 --- a/dbReports/iondb/media/jquery/js/plupload/plupload.silverlight.js +++ /dev/null @@ -1 +0,0 @@ -(function(g,b,d,e){var a={},h={};function c(o){var n,m=typeof o,j,l,k;if(o===e||o===null){return"null"}if(m==="string"){n="\bb\tt\nn\ff\rr\"\"''\\\\";return'"'+o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g,function(q,p){var i=n.indexOf(p);if(i+1){return"\\"+n.charAt(i+1)}q=p.charCodeAt().toString(16);return"\\u"+"0000".substring(q.length)+q})+'"'}if(m=="object"){j=o.length!==e;n="";if(j){for(l=0;l3){k.pop()}while(k.length<4){k.push(0)}l=r.split(".");while(l.length>4){l.pop()}do{t=parseInt(l[p],10);m=parseInt(k[p],10);p++}while(p8?"":0.01});o.className="plupload silverlight";if(p.settings.container){k=b.getElementById(p.settings.container);if(d.getStyle(k,"position")==="static"){k.style.position="relative"}}k.appendChild(o);for(l=0;l';function j(){return b.getElementById(p.id+"_silverlight").content.Upload}p.bind("Silverlight:Init",function(){var i,r={};if(h[p.id]){return}h[p.id]=true;p.bind("Silverlight:StartSelectFiles",function(s){i=[]});p.bind("Silverlight:SelectFile",function(s,v,t,u){var w;w=d.guid();r[w]=v;r[v]=w;i.push(new d.File(w,t,u))});p.bind("Silverlight:SelectSuccessful",function(){if(i.length){p.trigger("FilesAdded",i)}});p.bind("Silverlight:UploadChunkError",function(s,v,t,w,u){p.trigger("Error",{code:d.IO_ERROR,message:"IO Error.",details:u,file:s.getFile(r[v])})});p.bind("Silverlight:UploadFileProgress",function(s,w,t,v){var u=s.getFile(r[w]);if(u.status!=d.FAILED){u.size=v;u.loaded=t;s.trigger("UploadProgress",u)}});p.bind("Refresh",function(s){var t,u,v;t=b.getElementById(s.settings.browse_button);if(t){u=d.getPos(t,b.getElementById(s.settings.container));v=d.getSize(t);d.extend(b.getElementById(s.id+"_silverlight_container").style,{top:u.y+"px",left:u.x+"px",width:v.w+"px",height:v.h+"px"})}});p.bind("Silverlight:UploadChunkSuccessful",function(s,v,t,y,x){var w,u=s.getFile(r[v]);w={chunk:t,chunks:y,response:x};s.trigger("ChunkUploaded",u,w);if(u.status!=d.FAILED){j().UploadNextChunk()}if(t==y-1){u.status=d.DONE;s.trigger("FileUploaded",u,{response:x})}});p.bind("Silverlight:UploadSuccessful",function(s,v,t){var u=s.getFile(r[v]);u.status=d.DONE;s.trigger("FileUploaded",u,{response:t})});p.bind("FilesRemoved",function(s,u){var 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 0 ? this.model.reports.at(0).toJSON() : null, "progress_flows": (status == "Complete" ? this.model.get('flows') : status), @@ -299,6 +303,10 @@ $(function () { }, toggle_star: function () { + if(this.model.get("_host")){ + alert("Runs can only be starred on thier originating Torrent Server."); + return; + } if (this.model.get("star")) { this.model.patch({star: false}); } else { @@ -307,6 +315,10 @@ $(function () { }, change_representative: function(report) { + if(this.model.get("_host")){ + alert("Reports can only be marked as representative on thier originating Torrent Server."); + return; + } console.log("Toggling experiment"); var url = report.url() + 'representative/'; var data; @@ -403,7 +415,8 @@ $(function () { 'click .edit-run': 'edit' }, - initialize: function () { + initialize: function (options) { + this.options = options; _.bindAll(this, 'render', 'destroy_view', 'toggle_star', 'edit'); this.model.bind('change', this.render); this.model.bind('remove', this.destroy_view); @@ -422,20 +435,28 @@ $(function () { url_prefix = "http://" + this.model.get('_host'); exp_name_prefix = "" + this.model.get('_host') + " " } + var other_reports = this.model.reports.map(function (report) { + return report.toJSON() + }).filter(function (report) { + if (king_report && king_report.id == report.id) { + return false; + } + return true; + }); display_host = this.$el.html(this.template.render({ "exp": this.model.toJSON(), "is_local": is_local, //Mesh "url_prefix": url_prefix, //Mesh "exp_name_prefix": exp_name_prefix, //Mesh - "run_date_string": this.model.get('date').toString("MM/dd/yy"), - "result_date_string": this.model.get('resultDate').toString("MM/dd/yy"), + "run_date_string": this.model.get('date').toString("MMM d yyyy"), + "result_date_string": this.model.get('resultDate').toString("MMM d yyyy"), "king_report": king_report, "progress_flows": (status == "Complete" ? this.model.get('flows') : status), "progress_percent": status == "Complete" ? 100 : Math.round((status / this.model.get('flows')) * 100), "in_progress": !isNaN(parseInt(status)), "total_q20bp": function () { - return king_report && king_report.quality_metrics && precisionUnits(king_report.quality_metrics.q20_bases); + return (king_report && king_report.isShowAllMetrics) ? king_report.quality_metrics && precisionUnits(king_report.quality_metrics.q20_bases) : "---"; }, "total_q0bp": function () { return king_report && king_report.quality_metrics && precisionUnits(king_report.quality_metrics.q0_bases); @@ -445,11 +466,14 @@ $(function () { }, "read_length": function () { return king_report && king_report.quality_metrics && precisionUnits(king_report.quality_metrics.q0_mean_read_length); - } + }, + "other_reports": other_reports, + "has_other_reports": other_reports.length > 0, + more_columns: this.options.more_columns })); var samples = this.$el.children('.samples') samples.html(this.sample_template(this.model.toJSON())); - samples.find('[rel=popover]').popover({content: samples.find('#sample' + this.model.id).html()}); + samples.find('[rel=popover]').popover({content: samples.find(jqid('sample' + this.model.id)).html()}); }, edit: function (e) { @@ -492,11 +516,14 @@ $(function () { events: { 'change .search-field': 'search', 'click #search_text_go': 'search', + 'keypress #search_text' : 'check_search_key', 'click #clear_filters': 'clear_filters', 'click #live_button': 'toggle_live_update', 'click #download_csv': 'csv_download', 'click .sort_link': 'sort', - 'change #id_data_source': 'data_source_update' + 'change #id_data_source': 'data_source_update', + 'click #toggle_more_filters': 'toggle_more_filters', + 'click #toggle_more_columns': 'toggle_more_columns' }, initialize: function () { @@ -508,7 +535,18 @@ $(function () { $(".chzn-select").chosen({no_results_text:"No results matched", "allow_single_deselect":true}); $('.chzn-drop').css('width', $(".chzn-select").outerWidth()-2); //Patched per https://github.com/harvesthq/chosen/issues/453#issuecomment-8884310 $('.chzn-search input').css('width', $(".chzn-select").outerWidth()*.815); //Patched per https://github.com/harvesthq/chosen/issues/453#issuecomment-8884310 - $('#rangeA').daterangepicker({dateFormat: 'mm/dd/yy'}); + today = Date.parse('today'); + now = new Date(); + $('#rangeA').daterangepicker({ + dateFormat: 'M d yy', + presetRanges: [ + {text: 'Today', dateStart: today, dateEnd: today}, + {text: 'Last 7 Days', dateStart: 'today-7days', dateEnd: today}, + {text: 'Last 30 Days', dateStart: 'today-30days', dateEnd: today}, + {text: 'Last 60 Days', dateStart: 'today-60days', dateEnd: today}, + {text: 'Last 90 Days', dateStart: 'today-90days', dateEnd: today} + ], + }); this.current_view = null; this.collection.bind('add', this.addRun); this.collection.bind('reset', this.render); @@ -519,7 +557,96 @@ $(function () { this.router.on("route:full_view", this.view_full); this.router.on("route:table_view", this.view_table); this.live_update = null; + + // Load the more_* attributes in html storage + this.more_filters = false; + if (localStorage.getItem('data-more-filters') == "true") { + this.toggle_more_filters(); + } + this.more_columns = false; + if (localStorage.getItem('data-more-columns') == "true") { + this.toggle_more_columns(); + } + + this.isLocalDataSource = true; + this.updateSortIndicators(); //this.countdown_update(); + this.meshValues = null; + this.prefetchMeshValues(); + this.meshDropdowns = [ + {element: $("#id_project"), key: "projects"}, + {element: $("#id_sample"), key: "samples"}, + {element: $("#id_pgm"), key: "rigs"}, + {element: $("#id_reference"), key: "references"}, + ]; + + // If there are form fields with values loaded from the browser cache, we need to re fetch the table data + if (!$.isEmptyObject(this._get_query())) { + console.log("Search fields are not empty on page load. Running search."); + this.search(); + } + + }, + + prefetchMeshValues: function () { + $.ajax({ + url: "/rundb/api/mesh/v1/meshprefetch/", + dataType: "json" + }).done(function (response) { + //Save values + this.meshValues = response.values; + //Side effect to show error message + if (response.warnings && response.warnings.length > 0) { + response.warnings.map(function (warningText) { + console.log(warningText) + }); + } + //Enable mesh dropdown if we have any mesh nodes + var haveCompatibleNodes = false; + Object.keys(response.nodes).map(function (host) { + var optionElement = $("

    kM_$&W$K!Q%#G14tFNLh8J(!g)%65<9j=Us{I!6*E zdl;=z`dPN{$|*mwQPZf4L37>d7R-5IXzy>i2iybJ1CG3Y7kO>aEH4&ig=F!{FLOPc znO8K+3yHh998Am@BEylF4HSnWuQL8Dsn(t94f5ig26-(I^7;Z%I+H*a*J(xi z7PiKdez%+65EoU2OI^Mc~|`v5AjZ}GgK z^8IV3X>p7?igh+h2f{PEW)UGTuo)MvVH|J~6Mz%Cc+s4JL0B}|NI1v6;?J<32GQ^k z9*StB?+Y81t9sBaRWg`ECv-t#W&`K5yo^sqVOFLU5_&Y0L03sQ@^a*5$M&wc2iyb3 z1CG2TjOsqi%aNCYc0JZb8gfD`Q-r**i0IZRhcm*4 z5Z&?<8_O3oU>puyks7)+trbegoYyQO2~1K)RraM*5Dtl&c(hsgKDhEJ(Ex%x~CG23;lL z$jgzJ9oxI&9&isB56~a^zr5m}Q7T&$kirQ~%Z##?Vz{l4oeF6m8o2Q+*Hc$3!N1BPo=MJd6+wHo$`0=4i z@2L3E#CP2|w?0$^Cz7u9o`YmRTai{~uk})GIx2H*MP_yNJiyHEY8un5f?VEZF=_mhdA*r=)5ITEu%x^l`FHahY$Lwgt81MUIq0s7A~MmGD+0>hi)4MpaRjG{}qJL&!@uZmZ9VWmx>yo86y_MX+rb z7IXC_@dJUZAN!hrHR(6N4qjg&^4ijKk_RLwVNoh%Z4o&+lDOY*$q(kKuxPT8fcVl- z5$^gGcTrPaz#^bAyJvs|F!E}4W|LuJQ7U9TqriQEC?ArZhU>JV2^{ByqAW`m=Y%3~ z-4-#cp`ry@8=_kVybyU=)r=*j8F}f!{(9M6L=(D}M-xrYNm!nr2*}B5B{4+w6F3G_Tilr>2 zQqC(DC+OMnmiXPauP2mZwuxe#;J0_fHri7zh!6zC}`^eE9I6&2Y; zu~I0>qEuK!?n z((^>2{KQ5c$=4SJ&M+dLbJ9^nbnEVDqUj_FX1We%I-GhS}ut;cT z=$`hP?%dw`_VvamKFh06q>#E8l%)E7wwpGBue%$TBGzcFJ~+XU)ZP#*TtYY z`j^WN&%2AZtSjyT_rL)U(3k6%R|HooTNLA~Kvwk|C7tM2u7%%9q9}hU$ZLaUc|nm^ ze(7ufEWuW=pty_6!NiOqGRTq9yA>62L)9ji>0fHL?Y{oU8qiA&1+K?A7nlm&9 zizXWhqipv6{TeddhKKM_L?eA)*r;6P$V+`pMqxp!jbMJmh6?G*DMwz8ybcWV{Vn%^ zdmwp${($`D714^y7RC4~cuxIBNhi9MYvH$&D9StXk}%AXmj)#R=WcZ`K6I9s8W#uC zkyjl~=3oVMmDGvF&d6&@zh?#Cr^fNfM@KC}z%8C8lQ132_s+Z#EVwM)fx_ zKpKuwN3qUE=~(=lMTET2-sbZu;#)XICZ{7W3(}6q>lCu_6B~^~NEiyTy4QKESzdq9 zuc84*H{C^3mV|Ezolg6h8FZC|pV;WgD~;&=?jCRt9Pj}BbN0(Cf-9A+&@8q0R{cgv zC%Tnu;kT0L$SYDVpXF8K5Kec;Q)A?GU!6~J~5z|E1Dq*2EsQL^1=jncZIF+9qLA{zetEHCA%p>WKRm;9JrHAX9> zE2kWJIr2I%$oIG01MY$3fsCN)L_?~Q6wI_OeG(Z{eKg{~^%}pApJX*xPJX0fP1W&+ zMUkc1%KoRIVvX?=sU+I%??0gUsbC#y)y` zuVA3Z$A*qA@5PyPZ19PVfb2coe}9o@q?yP30?};O#&v?DHWv$XwjfYP2|IqEpN@7z1eK)qVx)K2+gLGnb?9N9gAsm zwuuVB@iCiV_xs7@=5I-sqIK1L=?oo`L03uK{H>7>+ZVH+)irlM4aEKK9&isF!vl#r ziF<=nk`_#ZyohR9zF#(9Yo>AbQQQ$@q)f0w&x9nrmBaR{h*KHxQ8Yo(jwIRA?zzdO=Rn1sZ zdIccKdg-F{z(9upaV?4vxy^GlcJsn~|7MI5-HlkTc_0DW| z)Gic(yqdaX++Cb^cgO_!6CYt|mJ;9HffE}ErI`6gj8p7Y}rjzD2Y+p;Az{`+X z4HO-DX~K1mxUZ0|oO0x)soBNgzGDpXy$$z(dmworVJ&fQI8M@nIS_fJHH=Nw@rFf_ zrD0IC!B@UAQra`?;!b9T5+qvW6>`?OIK8)Us*m`B=oS_M$lAYj6<=NgkyPnk6f(lO6eYT1_+>EB-@FWneoE1i^^3!9FkWvR*9h}HfFXH6CtiAKJ@USyQIGu zY-8jF$jPiD@*<#tX$-<>o>O^cs6#;D0Ps1+f2e|(lRaQgE>k7E zc;yUE;#pqz;FN%``HR3J7p7qbWYIs_pvuo>5Uj-t_3 zYeQb6?ADH0&{a1ife%>>1E6Rq0nAB7PfU$W=P#*CkXa2BZQvYv8J{@vQk=$-*B3h^ z4aEKK9&isF!vg|!CBu=IBd>&3z{SFdlUxy&%h4wa|PZ8gmbQ6h4z)4M4 zO`?v5<_sW$Aj=SfB$(ot#Fx&n^8@J?ES_Rj?*H8URg984N9WX3+|ssP?INGc$XAo| zUrN$4za&O{R#79)8AUEsu6MEVd@4XWOBQ^DVv`jYe+Ol###DW|Q-6!mKSlkth5ohD z`#V_Jh$VQ8BNF$!=%NIs;=S4AUy0Iea$bTch{*A3uc zTXDWkDS1Dujs_am(Jq$NuK~g_U374E;`0hZ=AOwTDCG7ciZjb zHH+v#(xq|TX7jAYaIC{p9PYpdZGLxe#Pxc0U$e@F#Vy5=iwzxH-itHo*x(ZzCB6&R z!~ORcc|x0c%r6kl26S8}*o1AdFlRe5N|q-KE+btA>mq3~n22V~IG4wFx;WS&(S_o5 zJ{4v(W@sYc49?lR1rzzEE=sQ;htL92nTahZ(y;+HXPc;48y~X?cE6uYeEWLMyeAbQQQ$@q&12Hj*vA}81g_g6 zkTq1aAZtT(%P>4AX`0pw)0l9Z$2Ad-i)ij2I<9I22BlavH8 zV`jsbi^Wy#4ly8$v*PXRE)jPACLMdjd z5#tm)AIxWYY3$T%+8gjPWL5)3M_!J+3XySQR(ugsqc|jw#6962a1R{80}0mD_c4&y zp3#(GS=8cQz^aB8an7nYQElcgB?8unT%2odN`@QNX}J|{2!SlPQ#}-4>Q+-4i*`!a zeh=pwA@L>7jZsrnAoB7P8x_2PAgjBG*D$I!g82lv&F)Fyl!#8&tN7WLI13-+DDybsxVC`r=VOs(PfzoD2{GggVa&WAJliH(X`^*D#IlnkkWym0aG zmU_X+3y_mpN94sg1rakBl~+b_2ngJCVyF$17!3)6ZE}*(V;!Ftmmat=$cwx@RJ4Kf z?dy$Ce3sWSu#akEJic^>4IN>O?@hP|+ymJI6By_#r~@Rb zWcn4e8kkdmG1%^byu?JVR)Yrpqre;mjDqP6DX(;#<>kmrqA9jUA$yLzB#b&o5+!>W ztx@`!ckn8S8(-L{Xx1n*vQ!&ig68r_+!O8r_rMW6kU3YCaOBkmd2LjtU_$X3&GNDf z#|S(iaT`T3V~7j{NgR18cmqLJcX254QbWsN`iYHojA%O9iFc~6=a!{3*vW<@#DZon zN)HTmNakEsqMjENr=PTf67igRL8eRJvmr-b@?$m%N|H@P>l_;@q${T!d1)Hu$mf z(E<$(I{}lXY1ulDDQ>7SbDH>aBQA-=AvU9^~QM{LUAjTakt$*UbBb} zBwZTUZ8pzZ42!gvdZ{)YmASq*;(EQhPau@<4GLU}(-j*&w!9Z-(y>7&Hlnqb5BCZw zvU%ZR;bJ*u&Ymq3dq=;tBs17J}v4=yZ z$e+{bLh(9B;%ur|8=JEUTBv#p#HowYE65?!&LSNfAgg1mP1InJ2pZV^elqbB8zotz zGXk{0_KC#|x=O+qHahaUJoex{b`PAu0}+zQ*MZ1O9H5v8P~cd}NcW!zd5QBH6Y|O! zEERpHPJ)KwX^$mSNI^FB{;^lt*o5Lc%K4rTk*u88k`n>0Jroh10_1}*1)^J6M0IWl zil9$>OHL@)rHF6^CE8jI?zgr_;AQuWm;gjx&CYDnt36tm1*6rmIMF7X^QfZXUG(Wj z{v0Y=khLMYWf%*QmsQPJQhEg-$$IIc^uR!e0CKWg-HD-kV@Mv-Dl+oQ1SlrLE>wb; zF$dzy#p0@VhZvB>S@HJu0Fun>o!RWDT__^%OsgL#p+RIXUpeM!0>wo~jgr?V>_n*dh9h!8S%- zfSk-aA}_`%h?u#kyfTVIK;Whm#c?9}t6K&xBapa|);>b&} zq>I@WUiCYe=h*qnLk8Ys_rN84AakxN;mAwTEXcv&PW4zs$m;+LlE`ZtbLs^{FgK6;;$wS1W(k=5u+2s z$=agTqt1bJ3l`UO=l0f-m+^@sFNKcIks7Hko-N&i{k&v&;C*)wT%HFi(v_2ryuKEB z^*ymsA+NaZOw1S}!;zQdCj;pgEIt%@sZnGwo1N-q=dmdBM9UtJNcs?wK5Vx5QNVXzq&?esL@ zYC~S5?ADI3&s8^M5DmisC>lxtb5hYBgvT^=?ZqLp8YtSpIr1_-apa{qjU%rwcF5%+ z1MjhW;F3L%Iaif%B!5Gmm@EtJ&wE_d6lEgj)r}%xuMLts)Qr2g1C-Mw@SkmTXWJS54W#>i525yK+wrCzE{M`f<>jksQ~?(6w~4G&z3(-j*&w!9Z- z(y_rOHUfnBaR2>8g{RmwXHb5Hs5kfub%IUU77KH>BlBZ<mNAGR-MrqeZd{sd!wZ_+(*#U9ACrb-m%1jNF0=*{LJMTxxNX73?q zDH~HbXK6m=&kfbo44>E-x|GVLJ3QO5yI78Kto%_9krZNTvcB&T$igB(G;b1wu*F0o zwkq>uQRa!(cA#kcG!CLU?PAHei<){?ECOz`d)|TzBClp=HrWzxNal&wket+UTO5ka zmrUzlAX;j)d43)!0`LMr)@JjxGK_`D%c^E9DZK)aWW97zdSIYK06AH$?!-{NF(eOZ z6&ZOklc}ySNl6ehdegnF!BNi|IIogFUBbizY#YVg*!$%#t{T=Ix*CSNsNXB!8SQb=&_E^ zi%SpO801A>9xB?v`S$h3CqB#T7}!U(F&7DfUlJ*21F96D_(5mMnpkBJ%jdh|eEVj@?oL4$s$TgRYV zs*PZFoaN=nOQI>ZMj?BSyd;b|M-nA_7_CwInRoCii5p+osA$$GGqO}0UxMbYm_7KT zy9Z9-fjHKduZ1~PdJ+rEp*J0Q6?cSGCT6w%jF6XIBS!2ZiVAs&xkA~9p^=cvk(Y!< z`+;n2DDqOH$Y3sF;ABU!DDy;X@zo-I*le9pqzPR%=NE_;IXyjH%889=pQyEqijehi zNJYAGvYrC4{`dW?b_*ibhwh4S9XTz2XscI`Lx~ zl*2=KD58X3c`-Kwh#X|D5o8rt{>DvlljNz=Vy2ECtvY7 z?UKZzmmYvipEQu&GWL07*=O5^BV1xpv~{@Q##+* z>(zZd53r$ROL634L&ui);!HX=_{2s7*4FEX`|l?zJjLdN@KTdusHivi3JrmyHWv$X zw&Q{>dF0}fB@!h>VvA;oiD<@*>VQHQdpKl@{5g#-6t8n6&d@}@nVp_Tak~Yx`=&Q8 z3J)NM&{kj~PcWc3xdl1fL=6Uspn=`*Clf!hQIa(}BR~smpIFSGt0a73qa&~LXARy% z_rMu=KoG3PNE5omdp{BK+9%{iNGU)iF0$7wD2Rze)s^m{h??Km#Zo#nRPa=6mUC9j zsu6>dK~`^3KM~vJG26ugx=Lzak|niAh(>}CwulEn9n zn3&rfD<_`i1sKZ?;gsDI82)eIiH%r9kGUEsB9yd(B6!S~)fl3sMw=(VmwJi7^8jRR zsLsq*a)OXo0|&~DnF@d;>!pj*0|Olb$jNGTCx+^cA$drv$jGaNF{HvICC!3d?Cd22 zvekSlXDas#S=a~+Hm@MLMf*Hh;fQnc`3-$bxwN_ zWyq{LijKTA;kq#0(!&YBttMUBa^&U6YXC68hu{$yegqOeD))#v5k>-4UR#8`wi*!e zvA2kBVG*6af;r_V>6m;Xjl8&}L0(&gyjqC*ir6=od0Ir6;uGv6xx#L1nZRxb>6TAl zuOi)%m~};7nWC@}OI4PTZk=!XdPKKoP?Vp-=*{{N%Nm|!UwDEqac@{TGs$+M_u!(8 zK-Ylerx^>wS8S|tR6ujO&g#r!IGuXI$g7}R$s9LCUW`)^F>_HxrI&!DY!&;_z2Z2L zccHs-M_wvaUyi&e>OTz!YbV;o{c_}`gW||bv81b+7G8}zxI-NL@DYKJ&?E4jA|N4B z^bm8x$ZLa;mzmFskGes~3ybLV70i*N4JXpbi(4AxwL!?Mg@}-sxlC?^MFnDli)hR) zikT~H!v-o=NQ$A5#adXDZKAj7C^o&KexA-d*V!@h0t9lkim57I8Qw@55Isz{Vm9S< z+2s-G{jMUUTTslO!os)}SNe;?pSHk1Rlo7yHUQ@7*fi{IAT(R-O)Tm}yb>Z37 z3`x*&Bq*X*C_9e4WK|%xDBb zmLUX5Fx^RLL3q5oj6m0C7$F=>xvKZSe*UPP+8QGEsYh2^w=&K=gI8_%ij4|H{qJhf z+;@Trek~q>;YYxcmm{zAkYkId9Tn{gZb!Z}!0wK`6yoYix76VxF-w@M+&l96Vw9u~ zH&|X*kgjYx@~RQnndw%2;)1O?@{*M48&WulT*zax0L2;3JF;cl%SgbLv z*vKup)ko-3yfhbI=rpGL3mvvNS~OqipjfQeKs;@|o_?#@JLc3R&^YaOM8|6(BEAHt zuYcw!zr7`d75C55?N8jJ6Iqe2riF;EeFsHw_QS5Zh>nSu7yvV0?Y3GZnzP$j!n571 zVvgkLs!7~Dr8a@1CONT)>`Wa(*MhmBbq3~U`n;#7`P|Oo zwp~A6P~4!ipHnnZ+HBU3O~mkY_G5lVI~lZ8<}}%Pzg#Y+B@C_D-J@7?v7%#-Q?u>> zuh=9?beHkkk&bK|H`BFKSkavIFKevQ6{`*b>cn zcIYVL+AS0i+!ElF9l|NQ6}bK1z!e+k@q;bLFqdxhx{4Ylu|J0*xXk^$57APi%?j|P zRw9Tz09hM4W@andAmmlSfqKVRs~%yn zLXnbYK`u6qk^xy9JG^3}tcHqSIWwE0AlemquZ|`2?xI3op9SO|#S=a~+Hm@MVm1Jr z(2Vwf4s*DtfjKuXf+&L(8<~T3YU#+!kynkMoS4Zb3cIys0=r!mx&o2W zNniyL<~C?Z-cm&c2SlbPcqfoorYLO0Qk5k{Ueoy1kf)zYH+o$~M7O3;1edv=_92!v zJjoV?JxQD-qulo*ikgS$Oecy7JTc)h6)!HTFtrQ_H`m)kwINvbkF2TY|K|QBnZA}RXGl_ zR8(37#DFY{vZY(?M4LFDzTU*dtGv#EebhPn<4XfP(Eu2L55XgFLlK~RZNq&Wd8v?e zA@X&)p#XZjH)zaX&WVtyTUOYdF4=l3A4&ai$>dHD%ush zBgT?(Nnz+pu2Vkoffk4co!z^usI&-3k+5Vs@>0m^jMZq4yd;boB8ieCj9xF}%rkh^ z#!r8+QPHekDzel$z68zP5Nq&D^#}|g0*<^S-s(!Xw|{bn7JYQn!}D9V5Dh#d6Te9w(#Rw-8Z5*BFYZpljTRSi+`cV-l6#E%&{&1juU) zk(YjGE)X6Uiw{0Cs}=!4$g8XiiZ|e=I0@A6i_x$d5RD#^i_N2ikQbPYYchf9XegdS zUf*!8ctxgyd*RE}bjPQ!HxW7K1DKAyBv9%FCCO9t$gDb^s3u+6a^$6Hlq0VJzy%+I zM__mnaO5Q+l_Rf`rI6hf5UIlj_qvbS1M(Un4mLue>nb9;HG(3dTcbY25@02Jl#FtJ zJ`vrPL*(URK}r}`bqxqYUcO>u9pr$Ubdb+1BIJeUHlK{p_XtjSZbe64mV}*;gBifX zx9Lzs_wTkPb#3t+pE=R3cRv^M`hl`}(|l=w0m-1NHhjfKM_$8Y3qB%`zyKrQ$jgye zLMzuzqS95A`(Ccdk(UHtT{&1uwUT{4x|PmY?%}Pv<_w{r%f*_;d`UnYdF6nf8D!=2 zM&pySJMxk=>U<YoUXHv5 z2p4=v9)aOS03okfa#Fp%)68B>g^us9uOAW*vH14(k(*cV9!bEW>CP>U$j3AyO<%9I zS3VII^=gPmEs+0uG<`{cB3#B1wS2)MIQt74(h0GIIF$`nY@}(ub}<#j1YaylH+mNo z=?d)GPm18$4?8G=%LFGn9vjSCQDRpDrkF(Am!^ znka2H>>yn3M3aoS87#tJUJch|A?dOxIS;kM}5^OsqiJqg>997pMa(HZszg>lGVa zjHFAv;wYKv+BlwIsmjykVK6pS3lzH8A|NBNVq+D$QQV8y z2@=PskZ)|u^Qf}#PF42>;PdmA#g52JcP9jPXR78}iUy>~>2eK7rf+O|@d9&~bF(8YZ;uRZFRZ=-Ko1!4fmB_0)mQ1mW z3VD4N5ExH9r+u2UzSx85>xskwa6*eXYDB}nK{PIcFySbHD1#LnnS*s|>BvhHt_#yG zJ)8jCYSNW0M_!J+1_&2?NFIUVMS!W1-m03DcpILO*BT+OHNsP(dpyY7TMH2$6Cp1? zrbb=|ECRZ9Soa}HkP{`3sYb!YEe*(`s9U<#PPB>p<;Y71#gUg{Nmny1yc&0KhdB7*Apswe zN8me0fT@w*Dx4B;!xQpaA>_3}cuI7S2YGvIA;M!K}*@$)pS@wvN2|i{{S8X`*(ufMtf{z-Efw;brMvrbU&T`%c^spyMCnFv?4pc7i+~uqMaXMxL`s&J zZYbSmRymT$3yZQ%^u}is^QW*d#)X~Du44JIPBtb{>D_YQOG|*f#t?bw6bFpsV*0Q@ zg(>Y9I3NgljiFfIcURU)P=emLuSXNCfN1oPTx=dCguK9HT$2gPMpKPI0i-d#%+`)r z&`qDna2vjbha$Suw=KT_`O}et>$V=lSM_vPj4L&4~ zz~v*r)JSgvPdV~>IkbRd5~M`Wk(Y`}3%;a!2KU0<4c+RFyhf2#N}M&4ZuHJ4W~5aZ zT^DC84wcOdFV0xvvt&0~u`$3I$iZB!@yRDqLUd~cMTETIyxCZ+M^hs&ouhzpT&zR& zNk9;P(jU$dE7s8jidaiaQ^LGkl2&02C1@uq|&0Pnjrv!L^?YdHq1y93u9) z<#A@vRU5uyqa&}&vjiW3N8kz~fRI;Ja}saE6Y?6PDz7oB$P(S-LEhe4i13)G%8QR_ zs=Tm>3cAL9h@BA4Ho}}t0|>6rbBwr-B?eZWt_rder5n8qQ=_XMk|VGD0S!i14wgVu zxxX+6GdJKQXddzj%&Eaau)HM<2&7w3bmWx-_z8*1ZKBa%OGjQ3MV*gC$@509m$Msg z!;Arn2tL@BUx0ng*>C8Wj=bc@?4~JPO}et>$jgz}6~G1`qDSEJ5nyVhw`UUH>n*l; zCgk-&Ed?ifCOq{)n-r;gLlJg|qB?OnA&pa<_!H5#dwF@j3h{W{13>g#t=M=J8x}>A z%Gq8{JRF|w*-wh_+8c{#;ehA$0hCXzfVjt^?Y-ZvBn1i{5;u4fwrX()d0lAmaMc64 zBD$5gU2GAM zQMK4<+*P3)#l3i)AaRTe`Nptj?@s-fL7-JXZ(00eUPlP*&dd+iQZyLt{@$mrmt>9B z2+#ui7Zx+9Nlm_D>gC*>(tVbmnK{nrdxVA0l3wqD_f4d9C=*ML`dB29xY?(E()Adm8VoWlH|%) zY%HH@Bpa^SI8lv)+lwe_9-=dy)Honm5z)AqBhWS=&5_p>Y-6BJp$L(eE>&V|Gj}m3 zGa@e>kPs{Nf{_;>C*y|5i--oMF$kl1OBI!_0Rf2v$cy<3x+{0&#XPRqy7D>$vMB17 zZnYC_;(j^u(m`?LrC8F{Obf5Z9o!)f{_+UGN8k~-r3fJ8wM58kiK@Jo2v2c$O_L5- zgx#TtkQW_OpXY^71Vked@>=#G0={%u0&)9^n2{H;E}|w&K~AEx;&{>~j{IDa6D0~a zI?vNfihFeSKExF|(Q0C^eY2Vv zZP1YXm?|nbARu#&yb6XD%&s6yACG-}Oh;alMGcWe$q`1cmvQFWuG;V_FHN|HNVoJD z*}o;$;MeLA7(fK*2HaG6@rsS6%4>#@*9_q)?yhOl0gJFZ6cO^GW9swJ>WWY_?Ff0b zSckEBcZ*XF2ee}2_7gF|fPF+>vvhY~?m3>ciPJr!>Fb}-O!b!)_h?9ch%0m=ByLt? zguKQGc{LO%S!fcWrz%Ghd0|nuiQf2ZV*V5s#<;Mv*;On**2%^sD!p6odua)f7oNUe zr#N677coB+N0#^p-XxT?@J(F|&#DDlZe0UYM0R3kfYwRFkf3 zIr7pt$&uFp;DQgqBQU%O5H@k-RTHhw9855%%K3zD$>tRY%P_%_7h+b;;TWP@2P9q^ zd5r?Ue3Lk9B;DwpPt2df!sxmm6wY(Bnh;tML-bU8bJ{u zFF0>D7V9AfD-at!P8XX;2_Y{uxA_Eqk8qC6H;!)n9eF7fcRmVdunXU&LlND-+m_T7 z*Dsl4dP^IUvmvh^D4Q*4jCYq2=p}R+bk&Bh*yzY>cx=H(QSbY;tt zmm{wM!UZ3aM__mnK*(z^kyoe!JMJswRn~-&`1Zk@6yF+)usamXGgS6*Z<*EcgoqYW z+z(IOp&{)Q$YJ-Hm&K$_wqHY0wu#=_h;+eaq`G(=|=BzB43s{`xl5Po|{K~ z?R$!+6)B*P;NjEF%!upN5{S#?Ot0;@UO&xeV)nIV;0-g|oo3p5GAHUw)Z-+7{d6fY zOem<&9>hmAq))_DQXx1Whkt3is z93?&H7g>3lvZr4SqXs$TCp+sff8xfRnmkm~Id`2ral4t{RS#ggRXY51VtiZLO5^FS7Su|6< zp&WESe?nAu`s^-eajAiIx~C7f@YN)2WsY;=lqlWk8SKMyFJk({mHWriNp@G!M1KAB zWEarH;$rbw1-aNQ<`0EenJ2_CYN=V;Y9leC0mHR;NhBQHl@1B44c zB#*%GB7l(B6N$xxjS+c0RaIUBc|9TIMcplB!6KqtbWG44A+IOf?r^vYalhZ%ab_{G zgt)hrBz%|7IijtMz%yF0k;Jl1v@a{}(c+AKh?^z2_EVw(FT|{-x`u~A78VimnpQYP z)C^UtYD%1iMcF2L)3b^4Qy9HzAEGKP3GNh$vb?0K>_Orji{PQTIhs6bZi*bJR%kks zh#-3S_TO8|2(*Y2M_O+WT44Xav3Ms3(F|McplB!6KqtbWG44A+LpPcQ{;yNOa4N zGmDukaCy*jxkyz)^`1lqUNX>*ywvC61l^jbX5?;`6NJ1b2zg;q77InlH4&_KWV?-9hFYc@+#Rm|a1ZJ|6q{n2x+8j2a?|k|T^> zFXPO$UA5s=UYc+Xk#6ZRvj3ggf?u0QV1N;z8*qcX_}MFiyk-b_%@FdU?iMOw5z#F= zCg_fk7g`DNa1|oaEj!LEX5>Ybgl_%CG#lB8KYJ3WB3*xis(+?wmb=Q%rU*C4N2F6E=bI5;=Ibs#H1HyWzIrE zixbtPD_f4dG){8lH9*+lL-Gh*J_2<2?Z~TuLY73)n_n(wx>cO{ki`rW9C-m|)f|qU zqFZIqqfIUQKwia-UTp^ArI6PMiimEF&L-whVPW*Lj3Yu`sQ7BpScH!&KBDd?kXN0p z%HrD)ryMc@y#z$vx(C;hmkj*8m&mJg1h>VBVB*a8hy+JoDpXzZlsa5+yB^bE7rsr0 zBD#OKEvYN6Uoyw^mNq13LtegOqxv|vAW^DkaC_;VL04_~ij9uEF3%Et1RjAahyX%f zTytWoyvC@?YmBPAsJn#}UJ4AfU;?UVTcmI!-O>HKBQKTwA%5y$;?SQiM_wvaUGbDU zTyVP{(_k0AgNGuz)3+@hWSQW#VSunqtD|TW_v@!4FBO$un3Xz5a9fSG2`0Ty%z) zCm*w$xSpG1ZZ{{y0_+5HyWK43Y?b9-C&cxWX}Q@{Vm+@tPv-UXG>>1ddSD@RP^{ZT z>kEQ>Er~O;^zSL4P*hW^(F3Nd)qGM%ovkQ7NRej7ds@tk6&tIm^5{w8F{i5DXko_L zbZVY*rKeu)(h|Lia`3@hKb=jK&+SI<35jXR2Js#gAL^+IE9>cLCf6OHH4ONG`)+ygmq!3T0*}BgMIh6yNWB=L?5nBKNvVm-K22X= zOWYDvd2#o$QLsCUVJtNR2vj1@01w6Re*F?#)_&2wZ;|eS?b)Rmp$chb%Oq~rixRq! z&G)%$GkraG=SkRT%MoF1JIih~1}loo45OsMBXQbDq$oX^mO-qJop&TI9CakVs6RF zibQqq&Y{QRxNeyru$+v=y*4|uhX;9US+$s%mfZN;iBoh%T|}ZB59XM&*~E%;^9@By zwN%#a0nw(T;?J{^e#7%Y&a7yPWi2#yJ9*_uEY{cZG%2nJ|$C7z>andy)zspgY z*APr!Psrv@lasK7BNBUgI?qp3M1UDf;YPEu=*SE3sa}{BI8PfmR+Fx5Ir7rf>}qh| zEx`uAR*%5tBM@)EBk!sh*Zwh!HM-hAR#jfLM9AwAAusA)Hj2ortB8=-V>on8bOODK zq5@fG77HZ9H}&!Ce(O*4$XjaF#+{lpqN)fOtk zd6FP2M)k3UnDbB@C+6~s_wn^B-}a#*2Ygcx@AO?%_tq@}lJ2^T{iq}-l|CNb6)h_B z#KM0q&aTECT<@-;f&*gH@gNeH9(){GWtcaVd1 zpfRER37y@KSw^KX-3sxvne!?y6B9>XiX~kQN>bwv?hprmc?94i@Ce*e1Tx(!s=TV2 z6ItbzR&1;x3n4GGV&fhy&e&N*$Sck&UlX0cxuU3$*O^6yCF0qeixo8CN3>R}<#JZA zuF51aCa%r~bDN5s~Ey2p*p5DxN@I;EvCcm%6JyBklv~7@;YlT96ZrX!Iqr?=4kS zS_Gtr#C3%^>SBWH$5c@PJo({7L6@NfER-N7*rYk0Dx-4wfN-AV=EzINgFHuG3LOoR z8mS=;mTtju-V$u^YxM|RJ_3%sBpv$_IHp*|A0sbs1KfgH@7D736QM(g@Tx&pmDj2#R%^nr@B0pNQzz=uF}X{kpRa7wDLWssdf*_!l=Yu(Z_96n54;*=$xnF<#ij68NSAx8L z{7@h!xQ?e(RE8LkTY?RKtsa5PM*tzOs^&yK*f>UWd5zIrUevvQ-mbFtT}AX@<2W3= zE@BGsDvFY0?awSKED_J%T+9zPhAOWKxNjgSCaw;2U_nvL5g7u=%7clz+~#Q`apNi-P+Kv6M* z=%SVwym-qZ0u;RSMad@4k(Y^yBd;rFUR9*Ja{V675smpCPDp=A>5Y1yf}xlEI|IA|5mzQhyh0B1kZ z@((ag(BDjQ7*iq!cL?v@NESY5~pAh#nZ8&uON$ z@rW`Kx3DhUbV~P*<2<(yvzCfmj_zUx#H;<((cArq#bqW_gsxvPQ}}F9OtYO03Ujuz zL1OwMPHTS#17slppNYBVm~W3R&gzN zqx`E_sCPeOx<(P)Ff#Z>&&O3_-i7(nvzNM1=4{o5S9xjdHN=80Jx2C#i8c7OdISa#0r9U&cdk1EGc^3~Cr;62{;3!z zN}D{0ViW61N_sbEyqCd@16(!j{ z;gtiMvx*)v z%7mPOk(X)8EnO@kaQT2Z@(RB)`Q>VeSxvgK<;Y9pBu8EYfD1kZkHGLEpl>nU_XvEW z2o(Q}&t{7i$8w*-DDU5r{d?qg(ymI2;!&6;{l&`HY{Fp^B z*i7HDau3N#S>+XPFZs^$Z*{ofdXQCAdI?CGvC4*t(>x^HhvLMcTBC`JDw|yc0uxsd zoBG?8kwG#Xd8s(@Z;+BAi@&Vs$SZwfl*VlIM+{iz+Hz1A-Fw3nCiH z4ajrkrQ$>}apa{wCg_J6{s_3JB6pWdy9&dryfpT5J5$Gi#CQAglif0&WI#JT?3-1%_mh!`i|U@ zmyVqyFY_6J-**>voG=(HP**Y?c{%d>V+7zGJp#8B0rgKMch439a342@t1~tqk>AIu zSoIPRlsJsM^gn)suZ9@hUdHJsk`XB9TZ=iQ`*3svK1O+SL20_azk4z^2&3o6R8eVBm85Ue?|%OH@x%Op4ix*`@;LQ_jg+<@ zKTI|`aW2XTTsR;hQTjzAANH@N(&?55e@n2zuhk=P`3R_g>$rQi2!Q)IM_xMmz1%_T zld#|7L@06S<_?mRdpOHVi?eqe{4IA!iF4$oJ`qFwD1yW}@=|$g^9!cCvfoSIb8S~` zIP%ibA0lvT&VnQjU_wH7F$dZGJ9v+gWUBSO)YIN1t zet87oBk%~^QUv}FZU6oELEF3BqR4N=ExdbDlzqwLmX{UZ^!DF>S|Ptb@l|f%MF0Hr ziTID)QX2A}-hSgISqKJQ5sS2=;1>f(S6)c3ufq`2r{u_68j=v-lN$D+-SOB!JcLcP z{8X%tEq3q1Yv~*|oDRLc$)vGImzJ(M?Zee3C++(XIVAgZg0%k*CnQ$h5e2;x^|hkZP+@CYqc$DdtcQX z`(b-3wnf~)vV*hfbfR-a-RDy%rre}`XqlX}zZQ|=GoNyfLb$4zPsFkr*|8-MAKsx@ z<)GfpW)JUkx$*LjsJRfCc5K9AW?f(Af-#T%OUVcI``I6GeAA zM*%)SF(oYRL(Al({dI|yqcGs|g%el$@rsN>!g~)QI;3mgDguaj{huQ*`r4HnAW>fg z#}xdCCu$-ZNJ5>y7xH>}c{%P<7Sd085cyZJ-bmt>4aA72RLf7qEudQmECTX%SVK`I zH@It)&5z@8ovu0U`EX|u83UOUeTi6SjjTwU)-rKlB*w#t*9Age3(=%rw8kuD4;k~?nuWUt5qH0Biycok653HI4fp_jM$J`0`VS;RhM#i|6U}iGDm7^%7{-)Q4-@6L$|oCAtymh2}}EL zAX1L_D#|U8lTu*&=et%;i#E&zUK)$Gp(C&KfeU*^ODE_SWmAJEM_xtn81DR4_`y80 zNIBAs$&pu)lRzvAKbS)hDRG9T@ZUHq8ij=S9z=9V*S=K*9C^_HD4Hs-fM|3!BezKztM=bOq{J22iZ6)p6cmeQ33)xd zLopmhzCgP50LaO>A@ZWz|9hPyrZQMmc?5rJ$YHbP#E zQZx?;zD^vo9Y`CXV}6O4(i!#yI3=2%*Q5*lsGoZ&5>6fx$sorUxgpc zGZ%EJ(>wA~0SRzIY43v-=^JNgiX$%*5*NNQ0*<`sYgcZLyc~JGy~UC&T+jtezhVdR z+M>#9hN`?~`Y#tB{VXE0D08AO5zB8{^SYS`bS>kZt`_$vwTwOU2Ktyozrve{@cesG=2{{k5cTT>b=Ch9j>c`a_^w z#c#xsJV z{wGIhdEH|96IsoNK0?T=vsja^Y>g1|!b@!;cw)#2BN~&qAnF`_1CbI}uq?iydB~9! z3nd}15kg)gguF%wyo?a?YM40U1152S@aS(`#6Bu>-$10q@l}+YBd;RO0izXuFoz&g z;uPGXYBVV7X5^pXV7g8#0HVgCZg}#+#vl6275EYqb>u_K>lP_T73s>mYe8Nnae)Qt z#0{2Nsn1Q@aZ=*AOS$=6UPVj;Fev6#AVi18IHV)#4%F(tMG$41d$R~;;uL(DC%b9_p*#5IFGN`SblT@ z!@zWl5@!gNB&y??K$4V`7o=@wOA& zcDsZkCr&inaZB31pSV@qQu)DcA+Ght#BEh65s3%4Xzo_uq@eopz?B7As733@s}s?K+EzG?n{Z=_DJ95M(tM*XiKJ{;=kB=xr^N z#@_L`<~SE!gSqB-_91HY6HatFQJL@z$(lF~M|M(kG3+y}cQt0R}4xQ5e zcVSTvhkZw08Xa)rv=$O~InkWi1&_cVM!=C5f5CsZ(Lc=029eiiAd5~?0ZJD!xj zdCMhA+#DeJA$hT~o-GrJGBXZ)5$ceiFQC}ka zrY`8TSdgx4K1{(j2HF&g6HZ*gZUW2+i8CT6&WN128xV_2WHUwyis3Ew0_he2ClBL> z$ct|Mc`h&Wa32;qahl!SbK-75)R)NqeMb>w4j?ZkG3fT+k(UBjft)Dq1l`iD*tZo~ z%vcNuYbV;o{c_}`gW||bQKAboEB;AWBl=&9A?dE{JMz-#fP<_xM_wi(E?j2>9C`5< z{C6Aui`s0CymUx7ibB(LE4-=2ucBLg_Uc$^he4JjFNMr5Ot(038fIOXZt3B$KX>FM zan=JTPD-ulmhKujSms=X;mE5dPZh7D$k$qhr#KxQ#G)&k{UI;=Ko(NyP9M`P$ie6@ z%Pr6?PO*krUFnuS9{UZeyc~ITsz!r!3yK}-7VNI+&fT3OFI&*X-#8tq^A&XIGqeBG zKG#?!P6Hi(uu(w;qW31TECwLne^pc7zDNSqNlaYp3C-GEqJ zBAYQvPz-OW7cd!Dn2zgYR(|)!DdZI@h%~0*`R5G_y5q!|mfug*m&pEIN6~;6zS)K% zzhO51sno6g!A2Dk{0yUN6x^-_U5>ouCluFi@h^jHM|=s2x)uB9<6w@wBs=mdFMaRN zA6l(lxJ;|#kj&M%;1T%42srZMFXQhv`WLm?9C_)Ga1@26=~j4Ci;leh`Nxr$qFEQF zTWU1Hogw5kf+Bs+4c!{?w?m_!Zz$?ZWPj9L%$%z*M&>aq;w5n2Y%Fq)3U(7N>Eg^E4D<8WrVmE&XH+J_xiBP%aNCgbXU5i4i{WZw+xLzxBfxn zZ=7!J$V)|JfXM3yCr)QIB+iKZQ)xv0sk{MEUn2XLMP4Csk_7$le_rLKBH~qEniB9A zR%=6qV`hvlcm)110*<`+TmQR_{=S>dk(Uk$M^R{+ZiP3s=*Y{FmwB+!k(Z)GR|Ch? zxP$A+t42o^aHF5fTC4aH|9)OhEY8fPBQH*e=0QWO9MfZDzbgk*bcR+Eqz#JvhS?l> z$yi;>Twad6)JOSRs=OR|X}ZO~-b%alv|V~Q?E8a_8eDMVwC27?;L9TLFU_W1K2;T{ zL<_O(OLpiUUxS%m-rhd`O?I(2%Rqk6KbB~5Mm4EBxh=#G)E$fVJg@JP+VEfrwA6hr}Zk!*uoWo%;1Nu?Ty(rqHX2DBDEO28oZUThdj9!3$lB`6)df zPHAN_C{Fb#MzdVxEm1FC6Y^Mv7t!I2H2LTM9JU6GMixb`}=#JzFrY8f3Q(w znot6CwW1z4`xh?#;=gg`ZXSyKQ)ye$Pvs4W<`P}-2>f9L8q=*e+){BI`dYrf1LQ?w z8{`!ZQ@nh<5%MbhV4f1~=|5gR8jBX(ynnnPG zH=rc}kk9B=dHT1oj3X&Bd{vVsqHGgAb+i*AUB`}3B#xlP>SB+frM&W&&SD}(<$hN> z$%Y~XSWMPrQ23k@)i{k}z%|Y%cYV;G1c{x#^nf-M)ae=zG5IJ!r zvtk_Z2t`huZAm%07EzOqNB)2{6Ll;09~+B+oGca(;{LsI6gi2i6%+CzER(iSzZ8&H zQfMDVPMi_>+HXKCE|JX`5F{Fq#ZB>wjR2BN%9+{hUAs{D%awCq9ZPbsBy!?xOG?}g zi1`xPNC83OJrq%n8cttND8)<@MZ#U6Tbi@@gN-U8T^?+tAlnJPI*X3H9C4X>h@b)0+DpfiH^y23er=>#C90;R7HqEF$D}K*%eU!ARuw z_Ew4fwXL^Gl*p?RCFA)_6iDN+zY^rN;V{q*Nxjh(3o#+DAj)vObe_{*1g&yV@fBX% zaRs^`I*Oq#NFO(>253ZznE_d0bw$&XgWOTnJm|v9IO^fBf8k{u`379Gn^1FNL{6L$ zIY&1j>Puwb)CHXuiz&Ra=_@wYgQqh|GrW(sOzJ&EL2ZrLY}viV^vz(un+zlN%8AC9;2E4#tTyck@u>#MzdV zxEm17CA#1d_`?V|@}ggodix-{ReP1iqTMPyRaSZRgS-L@Qb^;lapWcN*a6RW&0B-#65Dj!3d8vrBpj)bEaB-E_6l`OlO`$m9#1-tOqDDxh zAAHb~KA*Pxw-bv?WV1iyB?`GtI*z=W@;XOek|p7Zv-q!!Y&RNRxTPKr`^YWP}IK^S@$V-Iox{+5{4NxSQoEiU(Ga`Si`VckcnemA( zC+ah^f8jap5%LO;S!s4N=END1<9P$3zC`xN&Ba{WRTyPmP%J+=2};l#p)RNZIsxW{ z#2JwjXGBii4T!}hvKgZU#qgGT0h4h}CMX+CHM;$W1zo;kqe5I=XR23+3of3i-XI!F zO)7IAdmpLD^tXh!^S%x0p7L+lT*%FB_LLR=T7TWU1H4LQ>6X2kzEF(Q9!jL4tl z8xZv+vhOQ4*61jpqOksEtwp|cVX+tvn{k{_RJ1F$MC4?I_||!d8we{^&(ZH z$gDbw2E6cHJrwy3v+=K@ZtciRMPz`;%U5hvALVO7UcO?Z;z_tfO*IOxKiH_z0sl)? zYayPO6U~`j@Cf{21RQw@9^=T1e(pp>9C=A>*2`R8j=U6Pa^$7ZQQ#p;TMkjwV`Sfv zSB;Jwc{%cuJPLy>zVDFjCKKFJw_?9*>6fB2w2~lgP*jW{x{kbLtnd?*vQ6|b2ZLo= z9YveCUq2mrsi-*eQj`dzB>u5d4gWX9mU@irJMz-#fU~JJM_wi(E?j2>77Os;_HWg29W1{KPB4pe|#ur!%aw(v&V;He*9xa(C+mW zi|H!L@s@TzcoggAmG!FYmtJVW$JcjNzw~l^GZK|LaaiNU7f7bLXdaH^qqGq9Ajv=4 z%Q-0)W6ox$vmXxN?24nbck|e4<8Y|$Y#q=tj?bu^Y`dXPXYQAbJrpUusX^ZFQItB1 zAToRNR3kMypZE;6JSccDvm* ziCeoZtwdMUTwLpok=d%)*biGxk)EEaryw7vVof@Bsudekr?j8*ijB!h`}vd-mz=bx zmdUL5S0Qq#mKpqMvA()!g_cM()3tF+FbUgiX4ZC`(WX|_WQjz}ZHSCfj_QQQh7+DW z&x({dE7l5Gsk@6+1X4iaUc635!i~;&WOFnb!w>Te{*A@CdYuK&bMfFO%5n(l3@Hvnp0>tlPf* zo9R{*1I)o7E4TbNZEvr2%cTAL@kVqjdzJCQ$SZL)GI{$rC4T%98|2rIj~AkExqC9O zSf3`fqwe$*MEoirFc^%dXp61VTa}pHxUa-az=Cn)_cS;nQA_V8tHgkBM#O^ z1juWRkk^-s1hLj{mSk<$R(tL$E&$3}Ei__TuUD%@fmpJ+pu`D4#W>&*ij+9eQb@a9 z>^?wIV~%i{jYZIK`Pf(lks&We#&R>+7VCC-ZFL|(b& z_Y=zt&zFfr1F|@Fc*RBlNhamYZ1%2QD3qf*_0_Q?2TLL)&bF)-vQo<%7VpuuKVk3>qGZD|w|aQV}Xv;tL~Mp zbBQ!VS3YMpI*TT7B%tCeEUrNJ<_|XRQ1>O*1)0Onp-5>2EsbMK>|izfSimQA}@j&JeQY& zW>}=e*_K8j0TUi?DI;(X5;wXxe2Gbnh6KT<#F@s9yi`b9KskK4Ho+5gVyjr0JZ|o5mVgwv{)x}(F9fpUz=utR>IW`z%HRoV}FFEqk zVMffVIr3Ve?vA_!UUTGS5z%#XFnc|x=oW05Iagsg@~X*G#p`N;tuGPfA$vq#qLAyP zl}GWKGgzG$&S(M+TD?tq)r!xPI55OJKvBTc}X7C3yG4aaO5>acHv|4 z2>d|=9C`gI@}hMEZIxFg$||pQY?%wZfTd*ZKk0UP~ zX2h&|L0&w4y*~MIhN`?~sGzH}C@@rXW~j>R%SA$7GgRd@Lsec~#azWz7_-98CYZ^F zB+8MYH2j#A6;YMf7!`DVL(xQogmH7RAYIuk>w@CF_(@QL-r%~RlIH|K#EG*as`476 zg062UmeC+PMhS}HE%gE>pJP-0k(VQ{1lQ1`g=gbnou6R@ z5D!1Dc&-ND@{)*a)s=3^!v$}J=~m`kh0z>&jS%u0A>=he$m<)5h;Che#l{ld z1X#m~vm!!XBZR!Zp;$(P>=-2|D%urWB0w}kd<#x^Zbe64HlpVPZVK7_!HSLM2>xKB z3P}qNcCIQfUa`?IB7d+^h2+A-3?VO0oCHDS`CMKqBtwK_lsJnXph*8Xu`ONk2zUfq zMZl4lBQHl@CnosvDlb+3^lDL_nw}2iwH?!N z>SqUOruwQ6?b5@9Bqp=oLos)$+tHayC)xGUlS2DX#ClZOO0X&K(Swb&Vq<G0X>MPh)sX)799F{18R-7 zG&7rhhLKh%03v<1^Cc%^53c>#Sp=isqj;WA1fhH0cNF(_+icX5iR6NX9fL$UQjND; zUW~wslsGHCDQUOk?pG~t?Y0EbTU(LV$y}PXGFPjm6`7e81$fk?+Db%IJp=hT6>HM5 zQ?1yTI;H)bS8Pm9+Rvwyxa6cgwM=HczY392ni>3QvA()!g_cM(>ke>Bu+-*kX4ZD( z@=3L-W~`_L&qY#&80Dx=Xlyv)bqB0SiL+v@kd?Z-SVbTOB<{uQWF*|^j7I>?jcs`z z<&7U~)CGPcEK=fZORbPUy1NCD(4xIeDAKY4w&f{N12-aQVD|g_dtb3p!>sdxEPZD7 z@!1jb`kTIS2D5mTmkJ4nPO4FG&)1R?XK&1*NI#Xfr7Io*k3g#kG(cX=QU3Yo4I!_$ z#>ne6BQH7@eR<#C8AxU16%-afJ{V+4x)o-W2jZ(hUV$The9#h#1@fxI7ZPPP7kOAj z)7QVyPY~g(G)UePo2uwmIBtSim6+VPPb~{F_WF9{_c8>k3=*~ULe$8Tk7I$n>K;2z zR2=2+L~x#$-TA~saEkp7!JMvQar|uVvQ-#RL?G+XSlq2G5!h~NW;UBnEK(+m5k_;3 zqSRT`$cxz0+90pqL`$-^YpXqX6&HZUtQH2mtXHc=$yk!Tpu`D4#W>&*ij+9eQb@a9 z>^?wIV~%i{jYZIK`Pf(lksq>7POR>+7VCC-ZI!N%$L z6Uz(F0YRbxSsXjOVk3YglX7M@d)F=$%2A#A>R6J4C6N+mThW>(w_*`BJGIgy715t*|s-x)0OM@p2oph_yc&d+N`MGSC|;@-huz&u;FO3? zu4BbU8vPt0uYN=uyVcxk&z;2tugd+rbdnwThk2nN0fTP1=EREhj}t3`Vxi@AFU|H zVUfOZwxv->z=X$J$_U(p#EtG1$BDcP-PAksQla{CkiHajHiL)>mnh^q={WL|6pFp`@j6Fd5?Xa-R`O`kYjMnubPIOZbm#8Qk(Y^y zBQM3MZW>RiSbPbugLDguj=b8!4R7xeun}dgQ~nLQC4}atCph5i$q(}MPjP$ zs`vEN8zBi%F+`3dFUfwc8hH(E#YRG2cBcB?L|f5yH7hpqV^)UejOiaIRzx*X%#6F6@+7U>_5CAta3 zh7)H+guF(GZhb?sj0V{;N>D_|3(ajl86m!fb7Wc)AArv=I`UHBsuzf-=!~z}XaYh) z3ue`^J1o=cC|a2F>*o*pRWv%YN-NS&r4@}r!i53R_Zt#IUYs}yg2?mf>s3gG2*)UK z_MRPzj=XH3T=59BjesLBM_!J+XeKcP$MCdW`YAy6R}$F-&ykmi6vgC3<(Q3~BQMQ^ zMyMWah@u`i`z<(F=3Is0$g3t#6|buWw!TD^hjirC3rbSBD!4;DkwG*}4xosTf>(K| zP<4f49qAVAZWHIo%fw`eyv~uABw0fQS#pHY!{dZ>3yO}s+QJQQ?-8&OsDMv?5|6%D z@9&iu?ELbXn7aoCW- z@a^^OzjCXdMSK&_BqoukwAb4kt>vh-R~Ew(mEwH7>Z96daYm((i^ZIma=xeLBC4J` zwh-+wX`IbP1enST$?rE64+nnw^I{uX#_@S$SUbQD+gv zZEvQpr$*-!LFk_M9mT!fHXF5MBDs3jyyR1kRO9V-2}MdAXsM*#j=NvA2v#Irrf3!W zwH4{f?4^EGo0iI4T9J`iT2_Gf?1!yHG!-9voQgH+*r`@*Or6qx&MP)1C++7`N?dZ% zo?0fe-d}~tEX)l4v{+wVv_eZHnn~EWC0J^6HZyBG&S+DsYO+M4WkFQ;MLDVy8XHb{ z_B<<6;;dLJWToydRuM=6iF@%n83{K!V^qk8i!B*>>9V~M7AbMIrB=uv-Q9vn)pYhU zp-9VO+Lot81>ks@O)&fY{k>0LubJ1iAg{ma8)qy?;c?R2`bn&Soiw5oz(|qv}|; z>?%s+ML#o4rsyX~Z9#k`Y8JOR^jB`lbW1`nC=$qOj=U1^DE9SuYS6T6E+Ra&JD*rw zNHTYZ?+|0zA&}KV1T5#UYb+v=b-*H^F}pQD0$ZAy&0Odai$8at#C>cw#7nC>ws2B%4LXi?@TMC7o(0zcSrW|3M=Mn)q zSu7p|rl=f6PNHhX3V9Kg$y=z2V&s(-GNMR{vtp^_mNzUKkj1gXD>hn?MH74#kEzeN@qk{m3FlsMb6R>(>%Z&iDP$|WF#$9Z* zr(5Z0abKP250Te_yYqocxp(w2YI}R5${d5dUP-jb>$F(Ys$fwSjji%} zCXw)iVgUPkJoP~~%|%4FEKYXGhT{&R1+SG4U#|8bSr@R!Ee(?EbEu zeiiKvI~3`s(zbNPBj6Ee6@iLGK|JNi>px~lZ*LbxUORdejzNvucw}*N?o9xafUDC9cn5Td|zi@t`E&oIcMzl2sa3b%AoMgRvSajs(T$V*eR^HI2t zbPIMjc<0WMmx+lZFU5Wwd3|v}Al-r@{f%pNYwwLc0v-Vy0Y_e`P%KivL*pthB2l)= zi$q)HMUSnW5Nlc$tGuYCl3reRBod?%6+`4yyo+{q53ll4G^;t?f-TR6yjrZ-XmA+` zvyP&QS|}-;olQj0X?9r=Ol33U94WrDT=HX9R-`Yb6^%l|*hLwEaY4GWS=I%`d+?K> z1iiuJq-I9dGmM-#D^lXDSSn-(;4{8u5g{)y85cdn*kL<`y!fFx32n%`qr_R!DBRLT z837y+O?O5N1E6Rq0SlBP)7Y!LR7hG7GsvtuiZ*eMyi80Sd3`bAQHKleO=C3rskC?H zP^7ntCHzH=vRTvjuu@TX&(JhMzd7-(@CnJQs?wg6k2Vj+#BQJ@p`hj?g&d?0% zv_a8O0u~&3$yl|ZTe4&Hx<11Q;h0UFuh?i}axKW~2YurVP4emMRY-=&)*#)2q9dG zNEtjANyT84qdK9n;e=<;vmzzVinT&k>h59{ffSIq7q63%aHBIug?wXJv&YCw*Yb_9 zNQtv8wL<>r?iNI+JYLLrH~j`V#OqhY6o zNWToWdn=I|mm8hEbfX=_8hOPtzP?gRi@Z*Y1uzQc0U<9EHGO#IWgM%^ERL}wv2dfg zqXAnH;i=vEM1yjU$DQFjbk#d^>Fq=vIQF}Ntiu)^6VaI68X$o!&CF&l^oT`DoQc!g zC?s?h%@q+_TAxh>AZxqsL0r@jCR?k80WU;e7A0dz_JR^802SkaM<`O_Y)he#6S@yj z)RZHP^IRezCyT{{z!a6E$VpVKSRpT>GI0v>@@5lG-fP@*1? z*8w4~143TBJfav4J1s=|Ww6~_iPX5<=@sHKu#UYBL|$A$Wcu@*_Eto6%jfb^n5Wx=jk}e=DG{A4xh}|HD_^lOfu(BiD>f>~WJ+uJ z{ckAdrx^>wS8S{STR?Nyy<%gr3ZMX7(qWuJKa~cWVUhmE*_K8j0TUi?DI;)S0v>@@5deIMo<5A*S5|pB@={Ms1`eiB6hvM-VpfD=a$!nk(63@7d&Myvc`0TE z*I{ffc0*qLoEF$9agMxFo(SY#m~JW77TmxU%nK#Xq>~@BvLgNC#EM2C;Ttlmj+a@G zu53E;`c}wGqODUN-Tq^`Mc+8dXBcET@{%#>O7;@xD)x@NG@`-u13tYwKfm$l*;~W{D!;e{6krHP`qmVFmQAS{# zIagtnkHW=!@Kc-w>P${*W>h`H$ceKeCC-YaLUur*<69OD3?>0g#x4gQUa|HJrp2!lfUyi&K z-1^@T>6RWN`-~;+Jv$WXZ=7xEibudB&?*86oH+7|5rEGyLeY_zqH}GYiv)~jWBATV zkOk=$6b)nHV>T8Yd6l8+i-TR3Zo#uRMqa*RqeNCqU$L=|aO9;zbuGy22il0Ja6=RIlDqOj=W4jNazZ(njiCA4>mgTQZ#Fb zyv~uABw0fQS#pHY!{dZ>3yO}sAi;3aBhV@W68KbJ^iN1tM`CLA zQN78#ynV!J?BlC_Fpzcn5h%{UW+lc>#VZ|^-;-M#F`P$tmp*&{l@B)Nw|SV^NybT0 z;~-kwQA;l`P^78As(_TBgv8Q~HWU$bI<^q)Fkg;nO8gU|9p&)|hTcLvpza5Ko=~;a zNIV|9uI>1|QA-Z6yQShlJ5tW39@PAtGbEdk$Z`4B6eaRo)-GWGH(OxDLX<1C$@|39A8n3EpXTQI{|M@p1PC*|k(`qi7 zz9bBh8eR1`5%S{18O-umbJ6rA0V5i3DI?G|N|Yme!`@u9eO>VgcmzI)fZ|BeZH&AK zL?!Z490-ZW>V1K{2eRRDyT z#zdl>%PX`@Ez&%BId6_BFCj>N)^CKeH%+MQ1{eCK%FnX9C$ z?keiQvA=69Zc$4t0z|W010=9rQI5>j({yc3Mbnprbw|-$CSA??Y$6EVcHM)xC@4%a zR|^APh`cOH#**v>(0zcSrW|3M=Mn)qSu7-`s6d7yCsDOxg}jK$ z&8k(Xv+L*#Xi zyuKDX$)iQDE68eoO!`%{x9818+t(G3fJfky2pEpUf+9Yl4XpBNj=T=li3IXW4-5+A zbwJ3A#p($4E{K|L@i7hZIxSLbu~ADpfeHW+UtbA2S>#2ct@1i8Dn12QY-GAc_(3rM z5-sxL3LAXk7n2wb34%{KGT_LOmkLP>h-Z)&McI~VwG(aPXclxzoYB3?ONHdZ9L$lI1WM<_ zQ}VoD1IOqaXK&A&i?**T9s!TQClROtQei3L6W(#;C8BaZ&CYo*Vl=?bS-5q&J>U3e~BnK0{^9{+7 zmqbsFygs?V_qRL(9sv=70(8};fIF4!$V)`!^_BRNt@0w#R(a85YbQj7gAUYENiQ!u z5((0XiXn0wc}a4GiJ5E@y#XRGJX1Y_V>5%xNa!k>C{dNy>};ZYABj!B#d2@5g2FARTyPmP`npE2};l#Tmn=xqpD1c6W35In~@!W&-j)_ zguK9HT$2gPMpNw+^5Tc)3=G1eX+}aXlf8ex6B%y9_w`UjxBvcNqY9NHFZD4wA0SdE z4(@fI-I13gFL4j=jz_>F&^Q7$=PE1#cN}@~GL94l5fw*XhRwC990TA6GpptWU9hF$ zHY7OmQqeyj-7@K5wg#coWyQwGYlM&&7A4z>9$vNZmRlC2E1O+cY(#X6b5vrhfN`)` zHcQBBbju<_UTALf$q4Z+ysD;Y>B!51wDa*gg>2fc*hpV4a|C~|QH7)h2Rm1l*AGgZ zVMP95qYBA|i5Ws(oHz-B$os3gX!>$@7i9!`2?*uL-mEtlZC_VB0v>@+B2a*?+7xid zk(VPcjkns+E!~&=4O(H)k(VY>^oLJL-3z+R0ZDM=r2rcwUa-mw(=AGz!B$SUR7h|@ zGUqA`M_x5xi)gO)gLP)nk(Z=V=Oa<_ywU6B?267f@-hJ-p#`(**xiwrq)r!xPI55O zJKvBTd7Xho$x}qnk=G|6!Qb)-Tt@_I&Q(~Ak=N^6T9vTgzr7Ym%H}KG2Ih^_*uGNg z&V5;6oUdr`B*sk+1ZgL_e~7vG<6+R*U(jUl$EG4rbYIQoRYD9BOE=n3M9}HjLIfbI z@!((DoXB3!5q3YI!5?(*P-X;0Q-DN5x3wLgH)_cNcDFp=njXNBv)NEYm$~mMf{Wk3 zok`rG6YW6gp0|xf6#3`P-fo+XUNdpGQUZ@0V{K|}FPwcG8e`*n+8Mbc#s zS8c4VNKa-j^`qLfROZr(49ZF(nZa3!eJVcoI2CKsu~V(sn0r;moL6jYD8A`-%jJA} z1tJB=j`OsruQIemqM25XTY{xFXEU?5BeP_+s%FH?;JHYu`=T7x35^XWyzW3lv2Mm6 zgs@HI>Ypx79%2l{>trO{=!{Vz-x${H@q>-JY;V*;)P2bx-Q9vnXwhCK6lvK2+wzpC zfg2GtF#G-ey-#1SVb=LTmOeB4`0NOI@wx+s()`t2G<`|vh4A=^WCS=zmJ~D>ZC_VB z0v>@+B2W1x(z6G7;<>mI~K4PmmiS{U#` z$_M~*vRFt=QGpCaPNHhX3V9Kg$y+E=V&s(-GCmQ@ zX1V1Jiw0zI?C^?>s4A(Pna$p{3x#r2W2@>|l7oFJ)_uuJEpJ%7N7s(0#BVlzJ)sm+ zf5bS&=kijJscStLh%#hW9Ysf88a!d>q+6ZFQ+>2_AY>H#$pTQb`nm>L9$zA+a85YCvKG{tdRtk_ue%H}3ph0%*>a0&^{r`S4M z%Wf++?p6Y)M0B!ba$*RTuh^KtQnmLL8zl-_n$jA6{~Lw|-JLtgxxmfc#=ElRP4V@4ri-cc;V7)`3N znx9Qnv@5udywoS>8`3Rxg~7#DUQ@7*fi{KWguX{5BZ`2ixmflk`^`UI-C@dD0RLMLbaTgyk6BR?` z(66E+uL8>q5qYWG)!_0h=!Q_W5KUhaW*x;Mj2U^IZ%%uJye_If=cXLTo+V+69b~*95ocnW@N|iw=5#$1t#N~Ol&Ti zE2oebR}dK(ghkVggkC0l|9&UZbZ5kO{ZLdmAi9(z)7V#RR3T|m`qiJY*_QNEX>ZS)i?**T9s!TQClM$>S8Y1-l8_2kc{%b* zq^{bxcm&cg783ekRvnMowaQD~7YDZm-J(ByhGli7TjqdF%uG6{%IkdMH6t%yu~8zc zC0>V7H3}|nS&*)5`ihP94@tmHquLMFnMH)W(A?&e5h7f0!gDJUAArv=V!A~?m4-Vx z-BKZG5fDXZe8ol+5E5E2tB&0vv+5{Xobv1E5BeS%-K)G*NG{C55c1;08FcekbJ6rA z;kr@MwOkS9$ljhe7j0ixJOUnpPa;rruEKKU<;W}XtQH_Ebt(6_yJ0Mjya3&zKYWHz zIr373te1MQ##LU9ycEsCAWJoJHg{^MIbI`|W=p`U}uUgWMzNPQ+O}2_nilRR{Bs@=7QJnQN=fBr~s`>Us ztAD^VjIU4>-72T#LfJlMt1ysws8?((sR%{5bfXQ$SKID*yc!W9vsWbQs|;i0K z+pO%`j>Oq`-2v5hyInRHUsdPbj=EpB2v#Ir>Ny9=Ut5u$%wFn8wP~r$r4<>Em6S8n zvy$6X1!0d4oUQTHW(bax9PRnytagd#1AXH;VHo@%o_xC=1y=F|;g1mSVHU)kD_rJfI zi>5CLS3`}idhnDZdsp6Ew0&Li2zUfOi9kuPvRyH&?6c7vdA%Xzb);``u|@8aM1h2k z!mAt->(QhJB*sG~f%j1?6&BHojp#&gNPIoo`MKzrbeZZjuP<2C&B%X2AnWB&kgjAN z0grfohRAD6;V|R`i>4U~+w+Kr0|G=B7U3h1aTYZde=GtTvs(isu!YErXJ#|O$D(OQ z!n&iVuJCZ6tFf2amk2_)Ma-(JXhGJ7=#~L5L|zspV@Wn6F9SLrn~SC|36Czy2mo@j zSV&A!feb}XqH4tod6j%D6e%(CYABY?nC|d4yu@MzDl{2&1yLO>aj%sXG z9ZPbsPsO?~S*hg>i}&c-@s#+@rmrWIVhV^Dr!d{pm}TTuUMeJAs=Q7xD+schi;lb; zdDZ;LkyjmyVD%CZ`l+kgg{9YNa!HE$|12H zUur;NJY*7hAH`B(5h1TULs!|jvG}q_$P0_*2`c-rPe`lSLvg4U~oAZc+oVE)huWoaB?N$P(M0B!ba$@4#Vi`x%mjqw2F>$GC-*v%9zKJ&* zB~J1g^0?-tp=g?McNb*@F!E}=IHRxFSfjY``F*JUV10>bz8NH7%?uU(`u`hg+(RdnQ4lqW%E)luw*y!bgSyS1QOlwt$8 zy3#FkKqh8I7^6vrSLYKIt_p5fX4UbS=%$_%XVU4&ONFE>#LIKm7si7luS~AKgjwaI zMWgL8(YK=o@)CtyCmly#l0va}K3?a@OY*H26w@CF_(@QL-r%~R@|zfl4JWRlST-X&cE4p2Auli)*JOgS(NsHyynMw* zg~EE7{oZgJh5=AClz;{La+${dV51623l7$iZo%$0agMx9OdNSB9OcOCivvPGmG=I< zxoG>k;t}u&d=i10a}|~&FA1q&l^3L2P;}%aYKv~=!N%1}-4_Sfk=J)ZUcO?ZL{>{* zv9bQr2Q)OQ{a}3q5g{)$xA|m*_!gY-+=|2p;4_Slyc~IDcGQ9zL?n# zLsee9@So9{RW=k&GZKbKjjno}zw8zgr&!GY{`VIpuAyj}k>GSoMWvU3P>$^Fd2`YB zb;Tp#5%?qm1?Z|xM_!J+5~ce33BDY8rP$_u3l5h16vnKuv)Mu{Aylxr`08w8$BL{z zx;L)!a^&U6i%Hfu09nn^oFgwuoqDOWlBbB?`A$@Dp=dHBfFhy?Ugf1i)s?Mvq+77N zO`Iby6O$qGI!nA_;uN4EW<}(rp=g?s&=lN|al&7>>&ywzNd+P_6Di}4UulXSdK_V2H6_TA>< z2dzWdWc%;+AJp<6yJF+(D-_X5(!-2m50`O7;$fcynHk6J7>3526m(2NLnxv{>>7(` z4a(;YT8nU{&(8ktvSQ=QY#xxVEkWp>w?N!O5ia<+(knoazgg|fM$0(riSstZ)fT4V zZo6IHvWONWU7FBsHft-=li5rCs5UK?xwIl9wUTOP#8x7j>KVw#saTVa9bU09cd3o} zw6?SL%|s?~<}$xPG}E>5F~Pb6vzb}jkx{Z*RWs6M@LVKSh>2{*h7-Abr;D8f5<@6n zCnMoTXN(H@#PIBhWG+24r!6JbgVXRVrs@vv=)65y-1Lmdv}0ljZ@r zNAbi{;y0VVo=}P@AYz%pjH*GebNZP>q+V}d9{X4O%2M|en;;I^aywaK8Zk$r)sMY{r25mNf#Vva!DfE3?}Y+~dE z$jP`N@*<#tX$-<>-cm)SYd}EaM)w9fF^SQTAo!-`4dJOiuH7InigGV2Hll0}5&PWo zI30OOpybHwllyyr%Ol_s5D}T`WM-4-7f-lAs2bX>}C4uGpyV z{DXU8x&_&qj3jwfc;(0|-_BPC%4Q3g-lUXv5DT;MHzvv0m+=JFv_~1crShul%O~6>n~rh0cwt5v?KER0^5RnAsnv^bF? zuQXv-+-ukseaUvBH*!%%pld)1(v{7TsW~BT0_V-fV)5-`Gr*dWi#Y;q1EOeG?2Z7@ z2=OhPBh!))1uQ_i73hTGdA@5mfQXR;6eWwOJkyc{M_wu_Edp{bn1kJsmxvri z2v?3t+*CoQ%ZiYBNw(w2t1I1t@ywj7FdTVhIyDGh=g3R)sPmC1dEV&ta&|>$yvoZ2 z)^d;Q0@e0}`~ zwS^*hun|j**LFPYGLtHdLt$t0dPPA@{>XXGQK$?)${vY3bo}NbT7&X=131_guGk2( zc|Nh*wKtDaiUI@|`Yr4oD24c%6)d8=dh805@E0 zd1q?&#^aHTGAbw_gcfBZ_Mk}12H2LTM9u4npn=)%@9%xZMoHFajQ}mMe_;_JFMqI6 zVlaQO@eA01AHpN>4I|J4@_Iwa>xhuo5g{*bX^_{E72ZnBMBU?2l~}sb#wW7vUf+&D zd_8^uH4iBcEWExnW>&Z*##5F^JU>Goveht${Y#3612{X0_KGeo!nwTSENW`7SOkb> zw?<3=BCj<{-1$Uxg<<-7dzo~Jy@()mTLiMYiWX#Th;A9iLgZypGL~d703=yHx+o*i zF(81PEEbY-R2)R|kdq=KuewGmSfNNs5Hn^ryt!D1>U2O1$l|7W`g#CKCgsd*_O4wh z0(n)(l6iM=(mWvdD4y`?(R=eK@xudHv5`=UsXtL@z$(uC{6bW0B>05?Zo5^s5x*Ei%6en}pIo+5BAwu6Kx72X!^^mwy$)2dY zMP4V4S@x@JvkoKcpNn;E*73Nnon%o3Q8MfODba8S681pc$J!WGq5_dur-u-m&(PJp z`JFJ2_uiJqx=VcA6$sulq$o@dOauh^(a z)OD}eSl|=^xTHhi0n-?S(Y&RKO4ooCIZ7r#UQA-pUAZGKh0VGGH-o$=>OVV*HgUfk zdFh}y@{)Mzda9??xPv=HIM&mBy&u*i;1PfkaO5Rn*zl0oHWnc12Zj(iapa}o4XoX2 zF5+ps)H#B?0;NjyoEF$FOt;X-=6rO^gpS!-Dx)g*7ZwZ9RhuB0PPcSWTF@=sm3>_0 zH3i!kXj3S{Dz6Juqncp_D}2bVW=Qo{qPB3ASHdO5UV^RDo+B?svB>QTvg{F09kWPS zG97s-j&;V$v5s^LcGq<0?#_{yiHRdGg^q?ujnohaOSj-S;a$0l9s!R)PZ4nB^{2>d zQx|e2i;9|(S?>>rb?qdJM05L30ur}T6ct0{Aa>!%O9iz#-GVK;H+05q%(M(J%JL}{K8S+v-G2|sUjUlfuc1Ta-db_N7z&rpwV93jmS3oOYPt5LmVx#TYSO79g=)xE+ zr&^&r9Nh|c%+HW`LFCoTu7xE;^47iFC?j35XgfAWI%PXH-ZhCD49zLN1VNS}1VK>E z?+8!z@$4Px7Hl43mCpbC{E>`Qx<>4idY4M~(oeLO*AD?=oN0JUc;y-$0!QU0ng`4S zJ$b;8mmx1hUWDcfJX0B#<&}G-mRaSMT)AD%95#irAuqwb40(kKR(z&hp%Ka#!eRnO zuH;ga#wJD&v~@_z?MfxIo!@laJVvS}kpno7&ybhkUcER(aT_KJP)x`nE2)|!SeBRY zN(+ZTX4O&5!c`Rwc`2VYDdQQX3tzd+w#f1#>S4AU8Zdv3hp zBiae^e0~n_DnH+@NcaS;=a0o_1zt#iBD|*GBS2iExd`^4e16`EEeF`#LyQKR54D9Q z;VFIhA;lftOdV*)#%ClR8;ko52*+-7PzP<}C>PG#5sF)>iiiFF>7GTjA?ZT7ZnxV? z(M{4`$f?@2Rpxr8#PxbL3m}xw6$)GwyDL@+VT)O@CmkEKVMF`L$J{l+B5d=y+S`%SC(9iM7e1pB%!?!lRz%ZR6dM$p*uo)WHUJhHt z1R(NiaUE?rqig(ec8~L-Vs6p0_=$2wEL|#@oV+rLIfF$dMiKPcRIs}lD z)#|Yrs#iwILuy4vUI_uYl1oi0h#7MrK1@ums&|M2SzHw_UoR4poSCo8rbq3CQpCNw zRvC8_1@ijLAtU5Zm`XRwQ!1-6SiYW6ib^$NoTBr=EXzw`r&eiiz{`+XbrcPG8S+Z3 zrx(JLE}u$weWRYn^>$hFfO!CV;JYEO8axHy*q_u)KnVqi-7 z*>-F!Nz&bH4H*~k$_kH-H)n)9Hp1oWWvY@RJSGa{g&pF2C0;P{0_0@U5P309srXHC zGO=(+-Ij}@k~3Sl4g?M$FD5Y(5;$A)omPa$nwK8!Dz zOF*a6^j3?-xH>jB=0ahq?1zF)b`59kRu?rGznr^|)*=na_V z1!p^&m|&RWTG$~7d4a{arc)G+rW!#4NO*c#tsMcPyS|Y^G(3cdA{y!In$&bSK&1M4 z^LIDoResERK}qE;ymgH?GUQd_t(&1+Ww*Tr2jwQ32h0O~dBBjDA+JEVFx?98=4S|q zzMp8wE9Y~4aj+b9h4QT+uY1`@Q7|lL_VVl*>59d%$~hr!f;VSGKm{T%+p$s5triYJ z$P2A)ZaX$gly);1O;IBz08m7rK-au^=%k)~&?g!SdHq1qyhiME&;3Nm>j%UsCYBz! zMmW~jz}~KF9xx9e4;b8vOKvee`YC>wCFTz_LxXbD%me0uzC3`CSEYSqTM@p!y?*$^i-8lp|8hq#{kdwV#ZsxW(2k9D zbqCVviQJvi%tR^`?(s(P{vQ};)CP$sxP1LFz^nXxyCU(HW6oSc8H&&0-TVwi*xc_C zkhi6Bi|r4Mc8;LGDvrBX8wQfG7xKn;30qxibL~^LE$s)U29Q%zB z*xYRn>YyDP<-&P8%064EiiiF7>4M@8-F-+=d1<%XE}DpLlJ-JQ)uyd7*E1!q*Q;5` z{o`}R0~f{aiWMJQ%!)nf*x(%-0YY5PzMm-Y6q{-f%C8XR`e5#wV5`mfTi4&B_hm$QIK(gi7#L_FsA>+m(Z5vQt zj;%UTGC(3|VD}z0e*EJ8A2apFE zBd?N!RjBENkk<(zFDFVsg%{4!$m{JbT+@>$cO4St^~ml+OnWMBS%WfT(fv%TAW`tEh?2Vi9ng!*&l2h`jJM#f_c$4A<80pw)0dMt+Ol~MAL zT9J_#GnwpgNlFDVqc=^=JjBI%hbWN6Rq^um8f4J|U)eNOJ!&tMBJS0-%D9^-kk@Ap z86khd1=B`Zj6xCY*hnZvLPVwkuy+W?L;5LrbReg6T65Y}@zd#I3DL-F$8%H3~Ly*_b>{9>?;kypSa*?H0sc`;5w#LPtDm0lc@1uPT+@?sK$ zM&*XQ`bJ*l^`DA^wG(yV{xRewonpvK5Sem!CnEIWxlM^r_8?=0 zq`;~2^KfE)=fyjHkf_0~iRqSjLTBO~8_VImw=^}P3tUiybPI}KK)00Z_?kKD3grun zXi&dI(KO^GywcSn*%Dr{_}G|k8S-kxtWqUca-a4VxDS0hS|G0?k*n4)>AORb@RY9k1!BNl`T16MY((ostxXghtc61&=gJCsUXUMy*MbuC2F&t; zvmH%LzgUiIVTT~(1s3C?IgA~SI^>0yTSwf+Y6`EkaEPQkV}=1xRFnYLq^4ab_o=(z z(KR8n>L}{K8S+v-G2|tPj-_dRu|s+q*V|>y1Lgtf0YhE^XZf=sufWf$Gsb9$+jQhh zSJ>Q;S03jtpj-LnT$!(#qpncCuvjeS91Ov6oOv%BDGv7R8R?2e+p$rQsIFTsrpvjK zLs%`YiI5js+k84k$ZOP0B%lG4np$uhL42HP&4pL6Llj?vAWIQ~AgJbdgs1v=_KtK5 zHV?5%=YM|wNJc7MBlbzXOQn11CtAzvhX66oG(07|a*YmwqjD3?1LlFAJYdMnkXL|Q zm~Mr4^YazrsXn@7$SY@5hP*WRG2|tD($yi6b7ciXUWrk)#Z#%}AJHw?+>lp^jW9JT z>v-#CYASBSWC4l^Ib6I{Ojm}99>zBZ94itbXam@}79K88Ym?kFDeYlZUu zRL(JV)*fijP}~tD632cAF>es_H$S{vQ zNJ<7PqUkG&4GK+c;gB)%=h(Zjc%33~f)e>AFlX-pB=U`anpk)MIfTGqQO6lj96f-V zb)sNxyv-(9{eCpE?jmRxdT-sN6*JfO()N4p7X z74rH;NXd~V=4K~Px85S;D#S$AxF$GG==Qj#LR%!9Sd@^c8((@Eu6f{8**Wl+>`aIP zBCp}ZtTVCbx0{TnFLV?!w-NVnX5*SiQ1khSu+LF37IHjfEC+;B4jVB1-@zRlhZ7Oq zT0s$^q*WiHrbfFB;7hGU;CTSDc680iR$_sWR|N;siJ1z3BaiHA zS4PQ0YDGp~DU7bL)GF-_co{OQj-nwiLtcsX^g?*jJMiee7A_PG(tdvtqT6CtlXLSB2xz}UuO2Ag6Lqa*}gIuah= zyyOapy=DT31ME_ZnThe)vO39GcS;rcXRffk|H8mqe;et<9 zSwp&|mxU*{^&%p=HHRX2%;UTdG0))%E8Fld6ekUl7vmH}%uEzs>BS)-TG`olY?Rch#mq$6l5lE#s6FyvSzd`uxzb(ZDdCGRaR?lhn`j;|5A@^##FvnUk=G6(uN^{OQ4C~@ z&%4|RF0i}e%L^7W1Qd&y1$BZs2)vvsF>-~yJ3z(?NwITTVK}kV zvtO2j?EvyR>~?@FZosU!LyhP|mAx{&DTnjk>IGz>aNrw3x&_7fDa;D@g1m}pKEXiI zG~^|`($yi^5?-+Q1U0rLR#0OCtXGvt+_C07LGq$a6p3y8$w z6EQLdPf&_np=ikNux-docpAlMK;#t%s#yV(DA8|c_EHnlEhxrMVOB8YB?u2%Cu(it zpL;2CuB?#f1^Kj-T2O-C7-io+F1q|AINQ<01+Boe%QO#w}uiK5X|qme!&Q8%*! zJaWj*bY6iMh5=AilmOObZlju;s9l1+1esMwQ3uYDm-2}ruP-J%;&RTsYm5d*o+zw`;+Dp5zp!6IWY)j8yKm1n_m zoY{4QLc3<^fpJE~qik z{6`-@4SAKs>wkv4;BenW^T54(;2o@o^)3%rhs3blmOqn9Ane_3));nd0@<%fd>Kq^u?u9G3g#`ieEoBRQmHhUcue<9B`s3_ zTZrh{51qtk)EtSlE#^tmA+q;CJ1CJd_0W>|``re0emDjQI{%DztGuAdNsel-%zBaE zkq6p9bBD!j`88ME(LMu9wR|2=htsRQ?YB=C6nE(ELyF2vyWMutM0B&W7jceuD9~1! zvrvxb^?Ef+gfgy=w|I#APLVi4iF^~Co=0(e5ZQeW z;{3d4QDdx543N^c9uz`sDXP@3(a3gelql^Qai1n$G$U0IA7|bS_POWI-#dV?=Qj@w z>wyl)i^$fgKwcFQJ5}f6)It^r<;qXt>iX}&U>pdivL;$B^=jJt^fd41-P z5%MRTJ=$>jdIC8BoY0E)Urc!Ta*FWWlL)*Fc5Gx0)~Ti;FGF4lKN&GgzUU7j4m;ep zyO{^>-2>f_*A^kKZNa)KqlJi&7j6#d)@chx1&Ge))3&*Yhy!Ch)|BM`gU2DBRT2AzWP^wqs-RsY7$}Q#Y{aZYN$a z^75Mv6t9zp$cu;urZEVkSxw=Ut_~?e=8f@)AyaWMUy+c&*+s3&xNBzB(IEcagtvvlqU#z$>qTfc_k3zON^={jcMy8iM!l2XW*CsZ3acSW25Znz|=^% z?zZkOXqWH2U@p!C;N($ea@E-c3A%C{)Fr#7@CtSah@2rW$*qQic!s=67y<}_oNH;w zs{~5DprrB^Ju<6~H!|d9$m`y*g+0G{;7>hZ$jgvdfG|VRElA8Dv+5`!x^JWLEm>`M6eO(=bkeBV)n4IizlXUXHB0^qh zZS(0EeUIRR=UO!6r8(Si6ixvi9;QPPjo)=mYC2p_lt?AufSOhdF8Vko6li{HJQ_-k!mWzgsxubRtRf;#&pY& zSD#bhyuBeeA zFXfT}A)XOtzP7J={PQ_GmqTFHeECsfkix=J7@g+wVp$v9xBy@Ku@LgqGn@EqK zyHqQN2hPuj+z92y;xnrGybIS!=jY?@q+!r;r?uyU5-A<(u+uqF`yJ$p-PVPBM$KzP z(D~}B?(Di2qIU9ehql_R5#@4iJKASpsWyCkdRl@_B=_5=9FqF-VB!wl zeMnJxX}8-hnuu;q!bQF^p(}4Tt4AfS*Q;W=wrYL6MKKAn0%ePGJ3ro_4(!;-NM|W` zY&6l6uHqGYNiKutTnDz=oX^$Xjyz#l#K_Mf2J<2(lX8baUr}sOXkrV8jEfx`E7y(v zxpGx9Wvi$(yj< z;zkI0{asQZZ{0Qvx|1jR%E7!{%RFEn_?-t3@;V~qbu4OD`!I6NM4N(Gor}}4us*Gs z-#xU#&H2^>W}S~vL@eldeIleWT6}&!?h~3xrFAN%(v65m)X$;B=i^#}7qX9QfK!fZ zO*sw+C_aJV|850t^t`-65k32|4^jJbw*h>ql?Xf!Ko+IvEfr&72$Q-OwO+#Li zMztVOvS~|@)x5bOuabt{Hre~#0Nvg<510qO(gOv^s_4}=hfbCDx-Du}D2i)_3a_t0 z44f)IZ*wD*v6$ce^O}HeowiU+1SpkIZp`VjqE7)TxxztjCP2!z{Y=*hRBPE2Bt9xqghSim97o} zfwLVO8M5Waa1>5~ECM8&ZnYCNWBJFBmvo9DuM#ia&Kg;w-_E>7IQEr;dApW*z&!9f z4-_D)qF0+7It8M&DQZ?Iife`n5qWI_r^?SQ#Qg5z61X{{TN@}Q0+dRS8Vx1x(TyrG zas`@nMbu>uuj$Bs?>% zCSet)=@rr~*roU>%nFx9Ud05rfWJD5c*948ykxSF(XE76T&b-?40%NdehG<+SM!Db zt|@4jzmZ@r&II7(QDkz}n#2~mavRhoyQc68b_j@^A+H>FaoLcUAVk*)h(s5idE5Q8 zEUyx0T_eaUd+06P&)*Hu?S1ordEhHOP=Kt8Ud?mp6o}Tms9B*Xt{EysMko&@?$M1ZF){{^yyjtapPy0l6I>eoM#LlPXGd{^u8G9m zW`dB{1R<}6A_c2hZYT^$Cx|ayqZmJhSz(fuW3j84f2>0plc?}+e(t3vKwcAwykv+S z#xXH`*q_`Q|10beguEtDR0Hr@P=ekVrQW!bLo~8WCN}pHLSA4ouIUs-qp4Phyn0!! zU0@(*-t~{SrvfRe*UV5Mfh5OwA-QG73md7uDU6}@^U@gtEeUkYNeL*g^xDXLj$FVDoOYQ#5D z(=|upn-t52-g>?V@q9i4y7jzA+fCktcsha3*NX7j8;fW&g6Hi?L7(U64G@o5)UA() zO(Zy}fW#eMgbnqx`A!P-MEa-^b?2X-w~a)8Fjeyl#3y-oy5^yU$XyV}4(%*iBj$5g z-9C@JW24&e@#$%~%xhNV?bBeQ>e6nvMO%ViSX`ifO3|%HxKK+JZZ@k&C9c=2V!5_z zVZ3=UGqD0?^Kv^s-o=r2Y%FS4TxcnGY-HWU$~)nN9U_;+cdi3lZO-RvZ%3XmEMny6 z5QBMlBF-l*l)MIeQP{e;GIi_4A&^FQ#yW zz#h~}Vl735(MBU%zFuOlTR~ob_d#RqnxzMNfn&cLpxgWA0rS9DdH^A>{byp1na+D+ zR=L3C^PZ4bLlHKIqP(EHQFHND0Fg!)1c;PKBmA8bi5jhLLp-8>UQ~QOt|WLN`?wM) z#hEL$>K6%El;d|EuA>`0FTvd}Euv>fDCyW$)c)LUmK{X^UI56V%?OqnS(q3-(AFWs$<74iWVL!MhU#oE$Z9OYwY((qav?S`29ppk9nc{PWN}Tr zV`G^O<*kN0HWDBykymxCGVUe{m~v zWG`d~b_nn?*s+m0Sf`qXybO6I%F_$sNtaKhyS~v^4(9Dz<^l7-?>vC$R=GK2mF^Y9 z95bDH$HrXX@`-m>Y$(F!P?Q&RH)<{d7IZ>;CJrVZ(A_Jsgbjxc$RvRqK0YtNV_u}{kG!x$oUg8|mV@Wq!n*gY0L8tX4 z#XY)vB}Ujltc$40x*#Xs*r4XmGw?)LC>~Ki`w%ziMw_Yj(rzjFe_XZc@-1f0`}@C;)U`M@`7u5NhZ)1`y|BRN_W-VkXMYp<45wd zbhVB3@0x?*t>OIe|-7~oZY#w04Q z5z4*P1jq|7UoY2*au~689E7%7PmSZMEUJ|7>#%PATgzK7vtmZXu26^E< z6A=d+u4V?cV$pVNOiaMR8JPNIVk0jhx;2I(LSAs)Y%HeUxRN6_GDapg_Yy*0Xl?W9 z81XF}BUO|508Bu-FblduJk>{+1f3Zh@=_id657X@E&Cd!FA4JGc; zjVdt`20yV8=dpk%y26Am*rn#os$)$wnisw?r46`J3Uny=@*a4}7f$5ZyXPF*2p|IfzlBy%~d;X)M0E^e`#r zwkvb<#-ggoH)<>%P|c>|%L}fFc7Z(fAtJi9M>pDb5NX3l-Mk<1h;CFPf@eQ&kq8zy zdp?z|yTqJ3?b6XjvHNy6L+DC8=H)q_+YqEW1B0rYu z(ie!xpF6e4|B?KuMRMo@thctCD{;G71982c%e@`9+o$EcSbc5nc*9&brbp^obazrpEJny)0H{tD-Q+Vx&M+s6g4g+|G~pbfiV@<#a`E zsP-Z>kv$*|>?OJ87&lsitv2U#wYMWr7#90)`Z>g4UZmWy(N`236q?w=A>(3`)5>*Y ze=c69NSvTVz6s3Pdk{H$4&eN}XR$riD5mc<}F3V!A2unzFsn>TR~p7mY3j3 zhP=L(6WGSR+nQO}V0!V<&3-&dDwdl<{Js@Je%s2p-~@6%v$|GEHBA)A>obQyf5O?Ld5l81!1DEkZU8u; zO&q_N@bKjn;khRfcp2>2$Q-OwO+#LWyb?OP8ETZeoJu#-`ESAz>@my(H{t<=yq=<% zA+I17$mZ}qZcqev}r<_w8tdP$Yp zgTy5k!KAq*T0Cm0k{l>kXf`epfw%DR-)fc~XyGOHv^*ZA!1k!I_)vDn0@orO&0SZq zmf{p>Nq(N>IOP-MMMMKm7{rLCxNT)vP2rUm4#^`{l>zc%z9J!kvl&Op4yY&1e2#$D6 zHbywbkeA>ey^s^+c|%@9cwXGYnJq#r@QO15IC;cV;dGW4F#$tf!l^Bom1ybA7R^EA z40%az)q+{crfj!pZpf=-QP)VK${u>Fmwuw<>uqAAKwLM2=DrC>u*WbD+=vGv-O7+x z*qpJL*cikDdCd{>nj_?eMMSseP}G>>bZWdk;?>n8x}`fG#fZF!lF-n=95W0_3%t0OVC8mdMKkn+X^=OeQ(%3gv4Q8ZglU>4SAIxvtCeAc?)k{ zZ zce*cA%|4Ji;WcXW})0m^Y|&u3cc*uh>#abzFIXF;dI3XYF>l9QnacP z55kOclpg5CA!6w6OhaC!WZht<(%2|nhH8G0e~U` z1-d3RZIRTZZglU>heBSqW25*u-wN`w9UBEtx)~1kT|k08hk4*OJb;kbB#PO@#vm41 zUK5n%H9=WkSVRe36DVp-aXJAWaw8(qE#3JjM#kWg7aBKAbaPtE>o&yZ%&KEeLtcT2 z#d8Zd7C(hq!H|~#11*?w!qO{KQ5ATBqYaiji+6 zF-lp@OylNn?pjjJZC9q6O5^6&B@%1XU2-CzaL0X*V&oQ7IH()(yPLd9D1Wthq&*|m zHNQZ73LOq-zGgRZyA0O~=i`1?BW7U775Dqydda0c{i{aYKB=0!T_vVFFuwct&I1yT4p;oR!im4OyUWL zDjl>j<9s$#vs`J`tKM3oH?i!zv$juziRHXqZ#^L~Y}rt}2gS#+<#<%roX@9Pq`@~7HQky{1tj4c zJW6s>-L*^S=@NI@+`%asqaXrEGJQlM+_4dplPXsJR9&l#yNT1T4*6Y-!n%fF`FcV& z50ad?EgX^9b%#N_X66BgB$?@p-9$rPfI77xQJ{4jkk!1oA+Hi|-OL=uvfJLe4N73W zG!J|m4z&kkn<}G}$0BfH;^SB&FUBe2 z9?k^$VAH9I!c|=z0t1H_3mU0EKo0iRh{okap)T2ta<5Q;utRj@EXzyz#E_TZG`G!{ z#KSoA!cX*VNP<0xdEh2I5b0KyN#B2NMOS@po@7R?vi}Xl*v@^*m!;RgpyLI_5TV<`)B^+YyC>eCK-*6|?Gy zyl4hoq+5}95mlK@X5diBi)tyO;mGYI{KRTBqpd8ON&V? zra~WLbL2IKSzacJ!E!GSQQxDEqRkXP^JV0>*Fz27Ye}n+$XHXM1H!=5` zEBE3$u$I>k+p+PBNtC#pGkd{(H$e%kljebM;{k-cCQ(eOM8_Z|iPrL(ptZavXe}=+ z<`=8V$4R}Ikc{J0z9tk)j#XWAFi`*u-_Pb^oY?5Hyry8>K$7&5OMgnfaHVSiSIQ;57|-iNP-1tnJdGA+It@hP+CDOpRH6u|qOE;)^_j zhoTzh*VQ!SmHUspbYX|+z!~yVKIw&7sk`upp}A--aaEgO~?y z!UNf&>anv`IZ7V&1!moKRr!zkwQVo zYBsGl&~%k(!tioQE3Hk&6cK;8ec@&^8aqzn+CI!$DsDM`@DV6p^{0;B?nlfXGZYKg z^(#gS9}DEuSdIm9b1cUKiQ$V7&lC{7)g}cFEZ0VXD}ksB+;|k?woHK=|12Rht$7*yg6)ek3BC1~Sl)MSsU2k*~l)yS^9{4sMDE?Jx#%+6G zj=KN-#2I?bKNbB#Y17Bznn8@Ks8#FG^FQ_ol^7VO=QY2h@sLsesRpsxYiJ1p3?+`;??pG$h=ua}oD88K)E-WU}6-$P^ zB#r7yqU80yf^NZUejAct4`Lp;2@l92hS}zUZ{&gOpYgHSV#l%ACM)FQw^05)@`}W3 z^o_jRH#YnS4)Wd*xt<_s=|Z&`kZ zhXlG($&i;Juc66--OoI5lO7QNRB|T%=d0B$J7Pwww1f7fmmnC$49=HdHyl!S8apaJj={yKrSLAgw&wkT^OJAv2 z8li6-9N1#y^%FV7MB$aL4iVJG(=8~<5&455FX1OcUg|RfzwahWKVdMKNLMTw@-pOg zllZ|pZ5|lf1LB`bW)9{7FpitR-5Hzr$i{KPSG_m{1r8%G@o!&miY_{{mwwufN)P1g zt@#+zI2;YYCn#=)yh?AzFy12f#SakeBp+FC%Dq6SiC22n7xe?jSiC!BtjT+`VJx@3}b& zoFOmqiMYm(A_$xzFA=vkzhJT{+r7j+=60omAus9uYXol1Sx`v>n2^wDym@o0nR$S{ zmzloUmuSeV{ODd7WR;!q7VeUyUCzE|YIM`je&6`Pp58n#vq&gohxcdEgI~WumstFz4~|fAP?cA%R)`;TUl!>$HO~?Bt9c;VfDLYpF0NW1EXR|5CD7321%NZ0oaH=K;?iNMz&vGU^|A{D`(bojx z<2w|q7^KsD{`kHu58mE!5_TN)XTReknB7d2jAImQhpi!@$bnNe`PuJ5jL%4g;HCHG zqHmJFPRH-L>B|!YMRAtw1IH*)PFEUZkprh{a^UVk6wgTOBa9#3n~Ag+t*%)o#`P$x z0;Kx(tq+N^ z2Bll{Lr@+ybnCLni+-5o0YO%CQ9%;IDBlBly`wbkO(T}_41x0ylvbP9%H5XT?`3l_U zghl>$NY1Qy;J<((2Ts-Gz}<={n8td+oo^`0YV0KmNw^LaAKw!)iKWM4{9cXa1v&Am z76bCi_?V+G4tXh&1E)oL_InVsXQZO{(mN0p$l|Jx?=pFbY$#5$oIN$XF9rT`MRZeL zzvN&+NlA=T9Nm(f#gP*w za^SQ`fx8D$J|o#b-=V18OpK+ms4E)s>V)P1iZbLSFwbB7^DDIfU0Rg=A={9b1R5MT zsky*iP*k@j;k^eDUDC8~KA|?hP(=xb>4;0WQ6OyMDZ! zoR(1J0l4Hjk5J^mshS+Pdl2O_lCAQBxK522L02j~zJpCZCqSD)(PepM=*Gbu7dRzy z;Iv4Ay9Y6QMk;zQbwys9oJ<-bFBr($cwzLkyov#kQdj~f&0g@yrffH;It?evJH6&ob(re z=7sjZON+8UhPP`iA9)A+NW$%g%PpV3r}TKIj&_ zrldTEyo5)VkZwWIkXOmQYUoy3$yqg7UI1@Fx&=i;UI}JZAdCLsCGAtXl~qd|%w45? zfeBrYAX%u)bupp$=tN%xQIVk2V4}P=*@nCXPO5=U6eCfQ zpwnQYytN^(>!VxrVUU2UmxL}jFH+u_ZM>TR|2b1c0gC)LPSxbk{5^>38A*8WK}44{ z?OS=kkQe=CiZk8(mP8 zw1JQo$2&} z1E)m_+&zf$8OeS?;bcDQnxrM&Xefx+Q9{*&?A!&32@K;3e^O z@GQ%#DMr)2vn(%3dH&*msa~5X`@`@(Sn1?);t>(PHgi6Qt&VyG=qDY%XaGkG7>|$cthmonpvKaGC+<8&WvL z&3l^dDDOgcFB}Zq2k+Qu$V)(`njAAxi_Km*7`TsRc`2G<$V<7zgx)>yubWa;E~)+Q z#eA~WzyDU^zr}$?N^;Q0zyD@832#I3*RPL{P$8aw{ffm@`1Ona$jJk2&G`1#T$Gn{ z_B;LGFL>TnRMf->MPeRoTJC8?Ud0=?xO|yM}V+%~(8hDGwVv(us7b1%%!;X!ynJ+Bej*Uv>z$uZx zaknTkx0=b$*Nfs2*RCcKAEB6%@3H za7yH7zXLHoA{T;}nv1?g@j4wBrZ*;I9i!<4=Ipu2ry}8Uj71Kdu1SHr15rF8nd9+8 zLy;1-bj>k-#E!xT4Z_gH%>SIcA|Vlwn5fhPvSrN@PU(@rA3w( z2TnKtcA|VlwsG>+U^V$0r$+fu z0eNMB%yAfpyp+g+QzAe69f;W@a?yJUi3((K)knZXrci9SVud_*n;FClRRMHIM^-mviz?AS;sMa`LGoZ{$~~nSV3pQq94v1Y%Na0WPCw=g-8f zTB`Ybk$8-|QBjkSS0M)Env<2f1rrPLy|MdwE%r2jG(3xSSIua^RH6fx81yJ|f#HFQ{J3QCBEGf(;vI zaC1f|cHOa&1DBy6$ATWepO`%&7yV;G(Y+;JAl(AsDjmGvXNC zfhZr5?NLV&L=GS?CNXIEZ^%o4D@RU*RztUBEw*(=7Bd#b!P<#BaNU=$=Wm?6E~Z8| zT~j_H+lIUZPr5MO;t!bU(WU8@><-&56HkrZ`Jb9fFFhd(DKyf@bc?@+66lQRFN_xXPmikk{Y3eQY#Z_t z=mn5?t_avee8r z4}6&i40-Wa?01JDubf&TkY&hAre2+KJ{ZjMnxibQIgjJyRpCYi0Yz2I z1oOZjdccquf5m=x81kYqMLZ07N#=y<7M$&9$V-4L%+^FDXI>%l8Y2!irf-U(TVwu> zk?7|eit-WJ9yb>Q=E~1wHO-2U30yZDiyWhj-8fj|0;fa{oDw;3cOYhu$VKlZC<@x; zYa+%nMraGiNYx|&V8}~2^?^T%LhDSoW=q!+J`YN(jE)V zMY>jO6$>?Dd_NW5Lr3!E<=??2>sG2ymx?97w`1@{b@rlIubpoCE~J<9n^ed?^D0o( z)$F3gVVaB8Q0`LB$oYgYk8gdn#C0u7v&<`?qD&L;~9x37KKmFNJQME zl@_~^3a3*l$I=OIS!`Gpj!-1GfttWeN93iJqV$VvemPOzneAf_;tpLC ziyWhT%f-WCzh6U<1E*_JkiMU|7sp!I&TJuW<-yEslN}MM<%p)r30usHJ?Yp8J2u8< zzOZyVHY$+=r$qj8-J;0cY9>2hFN#N8yP8N;n-OqLu+`>#uJ(4E(;;@%WQjzLXejTC zzg#h*zT(tv7@5a))$7qU^EgkX_d3$3la^Q4L z3fvut;t|Olj~^O}v~7T{Stkkr$J=azrNBlb+p$p)FPqpXG0injSLEOMX8n}4*uF3a z<&f#snj*;+3!G9k7$B<;14~}f^I=L))09y-J-9oJnE1a|A_JNZ*_dUIlkk? z3Xc)Rf?|fxeEqkNkC#l0J;q{&yi!fd@~WzLLn#7z92?F%mFWAnYTk z4TCHwf_j*49T4)uV%5VeAm{S|i&-6CFfB4kNc86`t@{-_!11B}J%>!TNf%`ySr-Y>lfK>+;Gh7uaew+~UqBNLLfxo5xAd&v{ zon3?FN-U8ViGuHtiA6~`$UYqr-P$!5tNwCltD~B(NN)s%&Mao8FV>1>TidY_16#R{I}p_)nqVIILk}48;$Qm+ub6JdBM_|% zBQHEZt~uR;HLHiDpV4p_@{%O!4A-CUC>rt-Gz+seQOTKxyaZ2jltXCi3yZQl^>ho? z445lF8}drz=^G%gB9W`sFyz&g*BSCEsTEGC;(zdz^~M9zEqHg?9k%hB_9~Z-8B0e| zbDMuG%S$@tR@U+|9SIsb1R zdHXp9MvELcRr3o($(VXsl%D(9>_)N{+gG@Jy_;qw+0BFlr$m1CI}qg~vOQ@o=BO)_ z^SmIRH&hEs&>Jo{uCpw!n@AEfW?{A_Dmn8Ck=K|9cM|A~=}VwR{%)z7-%pf}$hPg+ zn9z{}L~g6iq#ar+k84kd~4K9q_MssF9ELbevzV*GYxqObaaiW zU!seiZb1<;tB#`PHvjyz9UFyLZY9ghc5DL?oW5;V(@m!L!reF$xRGotJ`+lIUnIx^&C$g6}@7-aFkO3Hea zy9GsAi)}+*0v#Fh$_W{P9kBM4A+MXw@-pOQ$SV;5{xgxRs&vz$dKweV1ApiNMqWi` z4jU~*)s~z88&<*MiFVbIME>si;}weZUO5MltvWH>DQ@FPLUr|cqUI#(YkqwE(qeNZ zfwHU(>N|D*mE4lm;v$P`Y9kWQU)Lh)kgOS-LJJ@zd zqDW6q)fC9Ztk{!|jcUin&?uajyklc9!g)EPzy%|msb(mq^G%4Hs--$#FQ%vSC07%P zYBK_^3AWmt&(+?Jb2`MXnkM zc}@h!D4JTNz-dt_BmlN%qHRJLYmow{MWv8%;YF_Qq)QYdJ#>d6 z1y0vA#XMjhXypNyK#PEJPztF>pYtl+wsYmHO64?<+;8G=)$m@*O^1>p3 z9A}8U4hVT+5kZneFQUSFFXsbbR{e;`b%(|x0$Gr5LGiE!H`;IM1EO>+(gz9DRMPpp zMS*K00_3$t$m`2Rf>_%(O|te|tvz=YR{&*gRuZvnx0}r>LoBhlqQDh^igCaKiWE3d zvycu4-~0+ii8tczjGGDv-s#!#g%=kTsp=E3@fQd!bN_Qs}E|l^iUH6gXWoDO3tI?^qn6 zXUBUxf~_{kBS}u!&U1%C`GM({s?6j>ixfC5Dusj=D_?!T=E1Ys88WMmq9HGdqFRtB z*_7>Gnj7*eY1nO}Ir>%919m9VPo=JDih001(8>e%fxJ!#dDV#tb%oN2er-yMMb;4^ zFA}M)s9cF9@*+{gAS?pN@q9#dYu8*vkYv}3sGst@1CQR12wZnY$yZqHHiz+mny)!8 zNcCz7MfxCtno2sKmnd*;M1Z`O2zhMesmtl^4X7uo#xC&(B}5V`GkPTwpnHTBN{f5z(#3?UD5`G;2^e_+ax!U%yog)yT3!m8VUYr-Ybu2VOn9tj>46al96(-7V9-e4ke8rt zU3p!Gx_pswN=H!#&M#lDbj$J*F6jkiiF-H`-^h@cA+J_re5-68Fb_lz81f2S%b$sZ z>Ep7<3n7|a3%Uh47=k2*yaY}{%<9xw+@P9@=`v* z&`DJzVYr%xybO88@w*kw1LlEN9x&up60YXRYlpJDNTmPaEV8_|UM#b`NYwdHSQH?J z&FvO~tmdL2uhOfCE1nUv>IHet=~Gkohpah$khF*bH}5JI7|I)Sl;!p1A|bCi%JQ0{ zEU&I&%;L%l^Q;_;U?m%pC`O9X@H8teQsA_R61pbePgD<1!lbzvFjs!g^Md?XycU$8 zH#jdS=Q$1#ap1H_fzu*N=$d>#F@JC>doMxJy(M12Vq7$bvBOb^yf}kM!Jr2UoEDWr zLf3WO@8p^a(eNlAifH(6Szf|bE$EhL>CCPPU532MPw8-Ys=Tkay58N8mm#lKV|=S@ z9xx9?4;bkk=R?uQ8%q-%u0|a^~fCY|PP(gEbsD zEh6MKMs({Nin%wa?7ajeK-$4RVU z1@nM;;Lki@$jgvd06m7hf+q}loxYrI5k1q_y!9+ELtX-LwV+#~rH5mAc2_8m)if(Y zDH@X0iu4c29Nif5f)Fovks+@;&j%C8hBghP1Bz(;Zpcfx%8-}%n6w}~qNOwMy15~* zl4M;Y$SQm2E!(3B}wK5On9;iiL^aZO9o-Oz5m)o&1Na0L7Hol2je6+B; z7e7KJeCsS9rbL-Gtge(j*TruG!^c2!{X_LCgO=CrT)x4xb*W5%+A~I=+BuTYv=P3iu9QQ zHI>B8JBpz9N951pMBuvTV@L6*kKImenMfws*wIN8BT;$3=gkPTNP*Mhn~)9%Zhq6^ zULR}Vy{#5$pUkz|D|541YmrGvUY0-k6LxCfEY zqJB&$(zXG*W}PSi9B;D;R=*#OY{y2)*RGMRA>{Qped82nu`Dm)5)7S0C1(y-lLD8I z$9)-!^i!#8nqnR>547??56J5^M_!(n{d!}O4nkgx&jO%|VqE^pGciH8C=rX>(s!PZ zs`1CiE0w&IiyC=-e7v9?8|%a}?Tf9KaF=u@HdWEBsyqHfBG)N2uE}DFyv~?z0lWop z21)E#zG{v|4$VabNeRKfS3nB$h*GvkPLd`oCN9ft{lK3jG^6?Su*hnZv zEfd8!#j?BvWcu(aCM>*hsx!BIMPNsC~Ct zYVEnR7~oZYUgkz9JN}_w=$oz34dtxliRI0&ZQ!bPreZH-e)2W{`l97a%8-hRBO?3Vpd0G{Yi&<8)1>kbnt~)hsZ>vs4J8Wc_s4XNz_dvuOgAF)*wWI=@xwrOFqLOi+(D#s1&YgV(9_w z5JO%iBD*kfGvrmmsB0uqWe>g8OFwO5W67g#o5InbGd*C3BK?iiHBB)OmwFU zBCj3F@*oW7$U=vSIK^E8hOp>Q&T<;S#$azX%W$_c~=o2 zubQRi`>3gL;byM2=gwkC;>yqS+z4f`k_|}|BSmR=nw1tQa9Tt;QIqc{ss|@w(p(If zD?jIXL4GV=3rf%%OimIrs^&0q;Iv4A(<0(alkX?y4^Cyr)+SH{ICu#b|!Zl4SJ%Alj0w1z?2oFUx(zl6?!c|>~nIW(8W7Z2wDsSPfYrK&m zFGF6f#`sp*JYXJ(9x&u($SZIaOpOpuxtW^b(eg7)=z`*4Szd;`O2pNfZk3mF)&|q9 zfVuK>bL3?^HVT?$J2oatrwcOHred7-t@-g(l;(vu!4EOJn&~8Fyv*(EAXJ30a?u{TzItn4CxjW4SD5BT?CH$ zN&V7%%8-{JN$WAn2fId5cAV`N94yc73gxkyW<@BaAvxii^bg1!-BctBiS(~ZP&1dR z^rvfNRvp<|`IJ7(YYat0UL|$HiBe@9Z{18yMPnEeKoQXc%kmPg>I%mUd6gftyT)jS zyh@UFjUcP+p|@~9A>D$aA+J9}9M;M_kbA%ZpK=}^@2mJ99Xxd|9VC8;346Sxe4WT| z@KIgB)pYk_A>NO6U8KYB(wK=t;CHxftWgzYLnn6?#m0)|n)8MAR#=r$BeE+*buh2- z<=kB4EBYhhoNSr>--(DCoh~ctS$ya7si}B6z2|sl=rY>I@p-3N&hvgpf6h{Hs?rgP z6kbqMN$2ws1+I-K-9;TgqUGxwigY<$vt9&lIs)yvt9aDMZYQ=(BvY=M7i@}=sJ!2= zp-6!PHHCCIaPylM!H%Ts;H~Vu)gsNzUdyT4v{mL>i;T?j3_P~ok<*w8jxA=zo^)(f zJ2r+!;k@J>8-o$f%NYeO7~xDcLouCiLL>+2e%6cW>3qr6M50=*jcbCfHs^D-wh_EmGjLm=r37<|bAi2oCY*;&nPMOm9q3A|EEtjJ#xm z-x!M&I9*dJWRD;2L8NRt{g_asZ3A@8I#B>P-ewc5em@%7j*TT*yUr9fguMQyZ=Av` zmgOZ}f}xYBoBGavM*{)X>xkldg*H*r7+ASbKUV}U6u zLy!}%YOz9IL}lXPl9U*EMTH7pq`+x0S7OaO78S^1-{Bn_QKnPAGMgT?7YfBFg}%C0 z$-#n1fzvgULZwjij>Qpr_AblU6G~C}M~qV}%S%9}FVFI-VO9`iH5Uze8S+X1XgKVX z?wLyWa(DV0r$_8iq`z^xrYYtD^FS*PK$diU$jfb{D9Id&^fOQ#Z*N`2;&RTQYa;Oo zkk>1TT(?ACHKN8_r=!+ho{tVb2nKLWT}r%T*IYz&>%tQo72l!f-XY}GLR8#_ui04a zHnH)rDR7EMCu`0NN)f9{Ep}{FZAn;m6qQ5h!?qku3`{9MFLNW5hZ7wDkuMZDMSfcB z*r?i)pb}=d(p`&TGGl(e@Q#g$Zav zC7p7OGbk;~D}gaC-8LMfUqwA&ha&w{>YApQ2h0PlJYdL+eo5j{L$@x9ybd1GguIk+ zj(yGmiXkt-X`0h5ShE}Q;(1!&pg(7dP#N+HSgSe@N4La{or%WuOOBBWry(!llCHci zM_r+8$SVOh_tCsCa6?~@7Rake(8E?Eynxi5>AQ zMM$@xxICS{;Ix=4RCd5;a?c_{USKh<=@dnysaA))c+y-6ZOD3{z-dt_ zT+_tT1K1%Y@F9y~02CD^V1Z(!DqEJ9a7hb{2ANeyQ3uYDm-2}rFF}be42VQ;u7O!~ z>=OM{>QOlq>2I8_X^MHkJkZJmhP>#PBp!ym5;Cbu!_9V7$xj4bTFhaDQy>+$;cGS) z2g~v@tst*4{o|yBHe`+I_e6_I;hH9v9vJ7SE0iz1V)v)lt-eGvuXwa*e#skXK2vt`TIFJ@gjtC!||YH00HaY*=OU0Q5k) zW9nePa=M{&{!Q$8IP0OgtRIYI*WZF%p&cd)|ydixfC5z6t4Y;N~|if*ncM z(raXIwMa9w*K(>hZI!v!BBQ??ibfQeFwoOe1;`e&Voy3YsvR3+tE#Z%9UHYsfz#rf zl-6r%e*GdjNcXc|ln>-;B2g{Z#x=oKoAbHa+mR;>i(NJS9AYppk}||7Mk%1a;*?j< z(;@{<1afdEbldej(sIkHtWCBun?x9BSxul|0nh}TDsyv`mCeSGAI=*%cd9%p$I zw$Jp-APy3RLS|1QTFa~IQrZ0)Q4_D16VP5HdLWyLMDT4!MGhoprY|1I16o9o^j}Kw~y51zw1}ta8Q@$}0+70jL-UETBk%(=`i)4A=Y$MM*haKZgM=5Vw_@GUIH?Gd6riVvw|S2xoF7CkXHgg!(pFv z&s4gXyVKt|Jz|F<{f*N#O)(Fc2U>X`fRh3#^??rEM+4A+uCzj=vAe1ZJ3yBhs-QTm*Po*BPLy`W*>6)gP2h0PlJYdL+eo5ld zhL{b7ybgZ3b(x|-SFTXmKpuaZKsH5{)CoGUvU@)F4E8!lhZ^W!x3fg=5zS=Uqw3D-!CZn~dn zEiaqcC>YhvpgBmlph(}iRzrKMY#uNVXb%K%Qe;tCmRE#eRH)%#JCxrbWKBE8PUS|U|EG&yh7wn2gkiTr_CEXb_B!D8K2R5-$xT-}$muTtCt_fW*A4~_% zvb>Z}u94Rn@+$Gx&Cso~+uq{!*7T?xiu5;5*EGdEU><1YfdEbndC@OQ9o>Sn9SwP9 z`DI+h(-rS`Vk57{4G#KqCLDJ@8}hnQ49;;T>05{Y)pX7 zktW-*Q2-%BUeY1Lc4iCQr@#xt04OR-zykd$s>(L8QMjZ92ZPM2qo@OCJ2onx+zRsg zLEktwYz&q^yA2q9@!xp7GAkLv1*5sl^hGQp=ybZQsJnhXpE`=t8;c~M7?Z+|XQOWnLqJiGxn9f0MKqkn~SL}e-PX;EqDJkar)?eP`pmZh3SpSSjT8O8KHTqoOCDkaSKtl zC42mE4p|9%d1E4;zy zDCUUji~!LY_0x&}Cyp~CFQNTky_k2$bx9Mvx3}!>si&@=Ww%~m&j@+dJP|-%C!i5} zQ3nwT>a4$chDr|XQ+Z$!L6XB|MU=aB=qM`Bk#J}%?omxF0z`Az0wl2CpcrkNi!!`y zZ##6Dx)x=xLGOiLgZzYGnP;hJBe zC@F{Q=TIUbC#zM7DJqa5$cb0ASRpT>GVyS80~vWmg$kdDd9_&cjztBs*msYBhfJZE zugs=L?S(=yO4zEpLCL{B6;oR(g_?IPj?lB?CGoRm>q8MNUr#7S&8}md!gNbwmNAo) zhN7xQLf1s8o4zLSGGtaAMMGW^P<6#q@_KA{y`~|r5`5h@nxkJuJv?tN>b9ns2h0PX zc%Wo9mDL0CIw9m0#RQUGUrz{molrlWDCyP-A+LIoeq4&f)s!~_cYl3#J2sXzSp->L zu4ev-Tt0$?uG0}9ujixV4uSzdBF{+Mkw?p2Bwssm$?zj!-)=n$QO!{B0rpS(oj^@ zNKgqgT(#mc`-TZDC)q`^7ZtMQ@Uk&372$b zR^oEbY;jFPUWU9r8PVHY<^l7-Cmtx7O=TJKD&2BX6SFK^ltE5nD4ae&Yd6m$rE3+!E=B*aj>`1p@ za}8b$c`2W?AW^C<3B5EoE5dCd_A!=j>>B+NUC%1e~cWy{yg^=uIG z;%Qa_`mEPjRMkkBm{@vX5-?YO&hvu&SiBaLpf@-#C}%{D*l>&*ig`6E`{ws7BIE@Y z6QrD{g~E`A(x4smeC7QMkmAm-zhj0*J&t zoO#yr_^y=5LS4}9W*lGzyYGUO%j$#D5#&2zV!*Hkcv1VdgT#KX}o6%J-= zD9`G`qNH8Urg2=!XUG~i7gbwsZer$oi|fsM>OK6H5>D;t-0F9-cQBbz4)+1LlEGJYdL+eqOS}keA>ghP(u)*$CU2 z8)7%|Xfd%7Cv?%rQUs%WLYKNA35L7`b%Vr(Eb)s~x^SbD&aS7K>b%hn8e zl{9KN5>?*UTfN*}&>2Hs${{4QU{)QQ!(UiS9 zV-dyZ*j1DdPaQqJ{kcdjb@Mv$@CMv;0NV4uv4}kXygTY+x08D&9u8r(Jb9)ViOTyu zZ${8id=vE!2Wo!XBG{31ExkteR*N(lEiw|ziDX1%i48qHRrz3xS+OS_ z8`X}Du~k)A@{WxS#Wz`RyvUY0-k6|7zKKrHqqy-rQ<>m5 zZXwFHWRD;2K_s-O9}|kSZGf&>CraQ(uneqzKN{Kc^^!5&3i9H82Nb3GtGTG!lF$p` zu^W{h;23FA&|K7QO)(Fc2R`w@P{`|ykQWvOS#st%?`NoRe*d7~20DVkXD{Z87lp#2 zmR{eG_lrND^>PB@S)vEBiAd06`@3hT63ecF%N`V(5FRPrfgz}1FBniNJ(Ogt*Nm!UzdH|4<)vCl4703|e#H(7YkQY&z zc(^1bMqW{&!Y5*0E!MnaQGqP>9bUd3WhLb+v*}TLp-_wxwyLgGaL?oWGUSzrj3KY&3s>sJA@ozJ zhv&^j-PRQIfO+5(4+v=E&DtFs>8qF@HQ7@If~o+}skx|-5edbfiJ`&?Aulh6D%F`p zp^$m1h*j~ZW8xP#14p*Sey1v7}Sh#<*s7_rT2 zTFuwp>Z2x}!oF$%@;NFW=}yCfuVHU&;q zHsmE-(gk^umwze_)=t!cvmG0iPb|x82<#*7(H~#B!W+>~r5>I)7j;`x%me0uPdt!O zoK!UARZc|=94vW=-X98i9X!mDDJt}Vfa^zgj7sN0%i9xxAl;(?6fq@p3O2FPoN zvb2WymW;tU9;gV1~R(>;@KZCW-zXQt}zHY{y37lCFDDW~3_? z$62mPL|$;+Y%EgXazf=`4F|5Fm{+5+Z+_3Bpk3Y}2>Xl?-oi0bHEFC5=P+WrMbT6p zFuEmN(!wDMyf6%aqM`&WP&ia&o7gB^(t?9QX4O&DfwLVOl}~O3dGS`8im3e6TvTmI zxNVelD^El*(!=xSqHb%7dB8mIi3c*!m5PSE40#2BV#rGXA-upBSZ;l@5#wTKT9#Mt zPjcIZ6S_*+iAAg-I?bsm)oW9nePa=A`1rh$J3x@V+Z@!f z+vvR=35D^#1ETJJziuwRipGZnHNR~U>`1znYYvjV)gsNzUdyT4v{mL>i;Tx|EE(xp z!az??6(3v7iaqJrsCI0Ot*XM3cWi7Z%4%dUXEz{{gLFT?KvbJ1@HN3!oAbHa+mTta z*j3ZxWiT(2^1diWDWJaM^s%{^+VTg%9n*66PZOt)-Ur3&bX=I;n2Z6ho4}ksMqV;) zZ`?wZZOI-#+=EDHQ9mXWXMClGwIjNv zzzdO=RnAyKnUR+Q9gF6oYD>bx#L@$RoUB$Qrl>%MASYhcVuieNKIW2?7_z0G- zCzPUQ*D+49EH43>S|oIdmd?~`dDSp02(p@shP)(WGURouC`t6&nb(;5MStVunEbE5 znv1%vDdqw5z$YHaQBo}TfV@rs`@FUgW8bI{E_Fi4%Zs5(btX|LWQKhhi+37}FGqyD zuvpx|8>b_nTTnb69g&bJD)g)9eUjmh9f_Igi&#|ENcj3WjPRQGvX<9jQ{WVjPS#9L zl=E9`)eoZurQ9cy&=&yqI@M zNZ@P=oI*D?u~E3B1qV}Lk+Xd|iaKz%e7*9CAuoZBu8|svE?zC&g8ihQNVizn;$wsYJJi`A+M58wZKzlLvM9$-jQy><_hmT81hm+ zG2|sUjUlfuc1Ux&1#5mXw70j+1Lgtkfrz>Cf+4R4$ZLnPyr8)KfA-EryKx*x!!xJ4 zI=16W9N%Lnsj8lN|5sdq6e$Auos~wBZQ0PCjey^vM1fAK^$kd(Dz6Qy@20pO3(*f7gYcLRb^T{Mh(R>jO+k>CXXy47nlW>y_VRFQRM z4hB|lo-?bIe1^PnbJ4Wr<}S(%j0@70P2aJx25bR0jcY$x7ZwrnLTj5($B1vi1<$QW zd;rd2#B_^(4GlPQx}{RmA|Z;-_>PSxAtbb5Rvnvnq+77L!8=st#T)+_ompi=(S(t3 zi_~b){Y1;x`;Lu@r40t7(Jz;MJ#Q}Bwyt;vJOf{3AY-o5aOCC4%aPad=*X+2S>Hgn z%FAV9=EzH7AL&N#QQ%nST!rDtt43TGrd#!m3%2ISOR}UEC`mSq-Yqsah=w5n6cIh} zDle6)u5b)8tB#@_I7eP4C%4G!9C=B+H5j@jyB)oMHK$uJ@{3n{|Hw1o8L%1n6OW8| zn0$Twf5|46KiJ3z#gm;{73UaH2yEtNWfQSgV`|V` z1WVd~eEdn*DbHB6J2rm2|3PATsbqtBjqi9H#}f!W9&$nwo1*ivu(Nr0qTwcA2ssOt z5kjGeF0yYdqCF_zwgidvv0LlAv%kNtXdaV#ZJ)ilNY}H?&x$%l?C+7d1L6USaKp#7 z-T{LA?Rswx+Qv~YoVO(`w={wK-EQ^BBHEC2Wm31@ZmdW%vsZemHf@!;vLX||5{hOX zSkjn^k1b|mPdavZ$Hv^GHeP16ovk|)DM9v$zCkq0wedBJJxGNMTawI(qUp!!@z)&g5)^Zd!_*GHslS>ebamP!C%d%uF$zEnO zQW%TE&Zdhw713y(5I|0rOG!B@4kCHTS&@-f3C9@E@G)~SXP{+549Md4c=>u%)Ksp_ zX5ZR{B9K>gEjjKkPMarWgz|}(#Ba8IJ)sm+YQ#9j)`L-G*GebNZPvF;8MuzSdyV&6L2EYVy7xneo!{gpBxeL zieiq528s4S`!Ieq6PZjM2zePog+&wg{(d|(6%U6%By@>N&SybOzJJ}V)2Y)nY1yu# zdzJYpYkBS00;fcDvRc}N(4L9c6%q3C9UB7)D1Io86I@gA!m5iZRV@-y%xBDI*Bu)( z+lfXP(q|WQ2HGa1_?^fmMqYrNOd29D0vedcAdKdbDl1(R0tRk8GSG=hjD`fkH<1s7 zr}}tygS;rqKRb$c;Ar`JD|(fe$;p*Dm?JLqgpFa#uV9o`L={;K)l- zx7$Nr`&fXaCk#3AlAs2bX>}C4uH~iP{DXUCx&_&q%p`eKc;(0|zs^?%8qF3k$D7Lx z;M8T#RTz%EGM$PgYFFlU`WJ)!7RXB!a-DUE9CYP23Ihe#uc@+v6XM8AlB+A1lXaw9 zu(=^Qw0ylIFO`)m(=FAT;Qn;vB_Wj~um13WU)eKoZy9jp^}iyoEvoW@;szCTH5T`A zikYYwBFB-J1T`J$7Hr-XdDU;KwKYf3>B^$aWJF$F_hD>)%`|hcJm2Dujgr_zZ{niN zK-Yv6q$`_cT~K@%KMP9G2c!CzFIa)tOkB(vXq%7_uYQ2VxTaGy8cnrx$mOp;_f(R-AQ6fgGdnR69J*Bu*+-##`2tQotQGtf36igv~32>Xl?-+~LCTakza zEI=AIN#V0a=h+zN}4JCk)L*h&3F|XhGj&ut)w*yBzHd@gqHkzDVnS-G!uOBxE zbG%D31DLJdHXiWac?Rw+1CG2Lc_sYe$V(v^Ot(}}aMzB!inK7uD%@zE6%%MG&sXMP zka=AU`#kcE3euHLM_!pu-2|_5r@=lDWEmKc1k?Qb)M(IMg3PL; zn8vC;JMyY?QCU}}Tc$rGz=@5Hyd+*4PBq%S0R?{^&%kYGph8}Gad)0C_wk=_d{&>u z(?^&|m*XmDBOJ}kiRRg< z;E}E@!nM5ODr%~;SOkb> zzd=j@BCib^xXX#^3B&UB_A%)Zdl5nEb_is36)nix65TS4g~-dYWGu;E0!Xr2xF|Ew zF(H7QESHjUR2)R|kh3BquewGmSfNNs5Hn^re7IPr>U=^B$l_4Ed_909({g1t`_?WL zfxN0~$#Hja+B_j6lutN&bkuD5dO|6t)QEA4odW4qUK%^KTHFj|88WMmq9ZRyUNsN8 zG7G9ZQd@V}rF#Pk{yd(6+s?qHkk=6*uOmWUN5!xLm^vn|RAMS>9*CE?%9(4H?JB$3 z=8PPiB8%dz)9Fxnl}Qyu%|*i*NH_rT5bJGJi3&ts-#;_a*YYwWf@eDdZnIwtoD$K= zYKiF9mA7#;&qz<>J2r-w{^EyZJJB;0FD$#LQq>|Mc{XEV_>PT=L=Ah##sa4Zz$G2V zDTtW4sIt;aLW(g;=6GbN4U-rR34(7TN$}IpCWn754wymy{~d&__$FHty_>6SobS4CdCSb(G_3?Xvj$VzfmDZ$QDH z$1`x-8F1wFzapqzdmH|gzj=T~*`+j0}Jc4VISW=H<-=OHoD-8~x z8Bdjdu)je}ps74xnS(**CD~5 YXbWKP>y0YmzHfB2IJ2qlOqd1P!t&RyXC<%fr zLkN;!n%^ghy4YiOq+76g8ms#3J2uw2sH~wNFW<3Iv9!Tpw0i>z{yd(6+s=R^FGpUE zyv|TKM_wi3`UbjHUM>@}5_Kipk(VS%tF9hO{OWUn&9!d7CD__WkjSUz5F#aOmVfgdbpCmB|uPkuo)H7U<#BSrr3e-i5@^Z%gd z{8QdF*+_f`qMg=wR%yK9Biae^bb3qhsyq)@Bq(wypQN(kBLBxXgHuR=BHAP16!!>F z*Jv(+Jt*Jawra}(c6Sh?f##Rm*OEY%z56A_E#1rXYG%aMYB@_Fl+P8rTZr8iD?YZE zi9PAqp&c91T+5dkV1NS`Yc7^8vj-)A(pA)p=Z9;8tv2U#v$rExPs$wz7qOrc%!{N6 zkYY4rpxU6&#TE$}qkN8I3dPSU5+`HD+Sr|p&^*;AAdX#>SwRUIHx_Bz0C_p~&WReg z5iA3%-;YMVW20tXLqT4D4TbP{KV=5)jVJi?cm{4f13e(Gw7X(LL*X1mp^$S#a4YQX z$bjr0K4O#4bj`BC=TTpe{654Fd!x_K_uM7!YjwOI5zT5L0>X1r9EM%O6e#j?BIwi6 zk`sy=iU_AwqNUZ~dTU1nUiKTr1R(NiaU+!ZF3pKH+2$7&4ez3FH_B&M(Sock(JjMR zh`cOI#**wMfF!Ghi!uWp69UM|a``M?)hkExkh3BquS|fV5f-2l#Edx*A1>CZI-d{& zvN-e^@Q^7K%az&eTf0!ixK}rjL@9fB~kQg z3lUs0->AG#77LzrI_(RuGLOd=B4QV@tVl9|Lljks3Zz?L;jGiC7ZG(HQlmkO5Km!I zj#=>bYk^ZDI$14I>g=A06m=Pqx@vE9Q86*4^6Wb{N|Lm~`x~mD;0`;on9rEajypEO zXKW!N^brA(#3#Eb>Ip-r%BvT;1#fh|^XiiaMqYUmmH-o$FI|jo!5dwaZmBOv zSQ7urbgKky$yTrPl`Dp^a)!;9Zt#gR=PC?GUX4gp9Tcqat;Ni$VQ zk(WYS}}{qHR;NhBQMFJE(!4*c}W;`izG_+FnYc8^KX0n^oflMaSaB|-8dic z?s*38ECY_b`bSeVv1cGE+;-W5gas=fO~#RQtl^H7$rN9#nbTvRNrMM4VFmCdp)D87N81tsVMSmgz0 zJGxl^#R^;tCj=ocuoxGu^wi-vhrD`OtsP;XhrW@fJ7a!@ha!5@w~^HJr)ln@O$pAD zBQNjfprTSV^`Z)AxVmFFqqbF)QF-cOlaENMjgp4a`GRz1(~(z=xGqe$ z>Khkq&5@TxPj^V+40d4{07XLyV5EaAle{(z5HhQdq8&I#UM44wycCXdjP}h5xi6gH zPvaT5^$ZB)l@_q7*0YMIU*Y)p2&eQ(imb%s#&Z>9MX8+gen|**F08x$>le5N6ssqa z6BJV}in9!8@10N+Bp%`N^@jwn%JXnV;w`B2$3qEXlKqy9@(e}TJnj)twk5m8c9UT$ zqx!K`TMn?hgBY#FjZ_zd+od`6**3qVxK&vTF50p2&5D#zEW*qKal77|gLZ7x3+HXA z`fO}BA zi9PAq;T;SZ=wvh)5aCi=+xMF`6+@ zZBXc9i-e3(KF2YI;^!2J6I94IvE_L*Zcm`PZ|tJX3QEYhu}Iqn$jh;JPSm)KU>R8b zel+qO8#VJ93i9%ajo(b7)a8QP3+}shK;ZrJ4BS@+8Y8brz7pQ4SkVz7uk$OwYr=$g zSIk|i!#~qCOB(ixn)e~*HSN>W^Z7k@sZigKh*`A|OIDR^134H9K;(5f(e#M~OHR6r z*PR#n#d>Qh0T#Txz3n$Ua6shM;zsJm!R^wVzDu?_6qzrXyMKddsnK?`=_ms50zlSw zyIvZ`LgZyxGL~d70VG*1T$CB;m=Hitmdj`Hs$Myghny7|c`=hIj!>i|h#7t8Vv!*> z&nLuyEDpuX*UM_C=#?w8*|&C~h;gs3CCA-Gg}lB>$Oz>V&K_;Jd_AEQQ)N>6-*Gb*XHDo0k#E#*t_HNgovG>U2s^B*nt3 zOu|0RMMDorh}4LYR(-hd3vH%(5*-}a#3dB$_er1>uXxZ*071$tpft@pJ(8{GLZQa^PfbBDi#z^Um>q8 zLSEaddAeFS2T>^GY_-7tHg~D`G+i@yF+>&t*~Hh(9~VZtW+BnttIikb>U6?l5t5PD zwz-JVN8~mcngnA9Dpp7;Syi$xC!X)T`lJsMb+Kz=x}}~lxOm4#g#&_XsS#b^iXx<2 zQ2YkEWm3o2EKygouPmaM`WG~sT?vn0Q)U2HdCkBv0on|TaL2|gQ=^(;1uJ~t2Gb?< z+tC7fi9)Wkjw3IHw#dC4?#rO7HXM05@~Y9#m9bCVk=lBcE;;gYcuv=bJfM* zUQwhko4q>}2~XL`Zx9paD$hgNu@S8kwQ^B%tTqWLio3E|)&<2^@Ux%t%@TDb`^utN%sDxV;{lAfD=@ONJWp>oye8Te*#-rjEQ+{#qpD zQpoEE8qJ61OE;JhguH%83KYFz&-iRwz^4wT_+n3EM`)zlWb4-J`1KJxDw*-mQvD-q7 zb}@*zMV-Gv5v`5(wpPguZewwWBDZV1Hw5k2XqL}=eO)eXRNK# zT~x^HtAvbDKH==qQN3z?w!rfBgi=iX5#toE@=}ng#T-V}GPrhg#xu+cf~@AEBQHl@ zH9H#&HLAN@TZ8HRtpft@pJ(8{G9c(y!bU>;3VH1i^4cNfbw&ipYlo264k52y>Th}8 zftslI79xO7CybI5#|bXtHpjN2>{5%FiRdv~G)K>l@Kp2Jjy-^^;~sSh ziHAL?$U2+>S#o%ScP27Z6gFb1$_mmgyDU5(eoV}RaUwtB+q6njOWIiGy0B=HMj~8Pgi7pix@7VeqiL4c}ZhytW8=oe=@@+9KqYR+SgC7PqOt z|Nh=WY|er@VIKru5~9gmVHXZiu|iVwe)bXB>DeI%>jQah5%NOfe!B%+aSdj@?azpD zsEVHqZxjv)ZZ9AU4F|pvq+3wT-@?Lp6yznQ`6LI8W>>=F*OVE+RbGy~GRgWDW|glN zjkecRv@14OASSpOmlTF5ZaLAMe*5~3ro6Xr{ZGa$xt+5L$Pz+cU@@-g)Q&}SmAcA$bt~2aO9=30uzEnM_wv(KCv;) za!lvT&2~(MUE-`k4*R}iqsmIxgk;WD7`|hpB2nYo4_07MmlF~4LTj5($B1yj1<$SM z$jg$j%W*IRco;lD5fKR+NljbK<1;sU^yZhU^7?^B^P&0D4JHI3uOE^ERg>milo`0i zda!}<0`IP8VBi^Wl@ zb-ZRly0YoWt43TGrd#!m3%2ISOVX&zktliJ==E}U1D*KQABu`<{V;W#An0`jaZY@|h+$W;+?5B+_zR!R1p3IVGiv zf@1hgM@EsZShcKA99bL@Q=L9OB(GwRM0=cU-1`R--!CS%*cwu^5cc8n^=}GS1@~g& z0o~n-)OjnOPA$YeN=Rez4Mif6w#7VxDStbl?TtvY0nw89yX_ivz8fuU&J~YnW6HNH zi^asrY@!`7;a-`|#Vxw#c3mvL7R)W}Gw@=T&wG7+d9ka#?KZDh6u0Q^mlREww%g63 zi5O;QFY+AiWYAWbvt;M|*L%I7$yQ2d-Cae@l@ zCbm3}#_dT}_XXhd^O41l$V)dT1omXA=30sdqm4$se7z)Vw>g6nA+NuhGZv4O8Neh; z#ze1|$bs*M1{;vWsNj)-nyk=Rs8odWVYDlr6L5g{**ERIO9jtF_R zKuLguy4*Rk5-lQO=36)3XWP^}b1qbRA z7q--1w3xFjT&y$j4G96{WVw76uj-W}dB|CjkyqWrV1*(j&4OHPoFxOYICpr*MpTtl zuFR$=h<3%eSJ#r`?xI3oUnOLO@`;zkPixi}XRv%dkr)6@XcI?`XxKN1#zl}OoF$ND zuwx^0uuhSVyc~Jetfv>kQ(wNe9{NTD=Lg<>&%nSkfRNV)A+HTWURWeHWsuiKi6H=s z2zha2aRgX(+yLZt+yIbu+yK7RMnpsc(HOW=eQPnpgkbD9mI>_l_KB#E0OkxvJfO~Z z6!++wNIaZjR&pfbaG+wLGtDzaVI!8RtRV85p)Xi3BBEO}D1v7{%=!?^8lGf}!k#2f zlTn^~5k<{IbY@e<1YVi&n2Hw{Rhn8Rgv0gm>^nB*qy{vnzjYTD!|l`$jJyhBlFUg% zlqk$1{`?_8g_<*AtKQYc{Sa!QBaf85#46Tk(U{zH9}rU z+^*M#D^dxNnf117E;5z@lc!cw(@PS1pB!5G*TAWR zci%H`wG24&670y4mj)$>8ud|5*0G=qHb->ph{T?dmwI6g?nEb;{mY4nZcU(w@YJLa zF@dJ?j7d}(k?dX~0rHwaE3Atw@x^k(WYom!oh7I`Jz!6w#Z$jeK-wWm4BJA(uj4KhS9Q!fpIUG6OA=jF8t4 zNr9p_>=}O#@6Rm}MAhOMzN%}^5DL0ntiRdck`PB;IiVK@S^2)v_~P!4yd;Xc9Epb+$+ySwj(^=TxOu_vR#mv*@64}rz0t-zY?gkWl2M*ph+s|o_fuo> zQrkG5*)OtW7L^TlY$SBI7x=DnZcU^ouw6PUf@eSOJBe?o`PC@N zk*C9zJT%(7&FdA#ExP+9MU$oNcC%d+UHZszA(K|M}7)h7-iL+#uYvX)^tv2U#v$rEp7*=Bx=MaN=k&}tsVK4@& z4GLXsk&v<2v9U_sIG)ANDH12BkZ)qk^Qf}#Nmcg+fb;W_MTxMY*J3R%-JBTKlWE>k zG#qR+^5yF_x9Wvi>6@_M;zkI0{Us@oS9uMFgAJS?c=tU61IqwHUI&T1D1_h;i^UPd z(?KFHVpZ?&r$foGg56I1Rqgj!5UJB4-CBr+lg!g0S;hHuKrHA6MMz`zfJhv67SSA! zs{xA#)cH$^Z-*61@(M7P!^%>Q{T_<1;PrpI1UGtnxj_*<`%539&F6Lv_);qoWFCMl zD$jc{jD?Yxc=2b43tN;JFw4TloPo9pK?PlaoY0c^;#IwJ6gi2i6$yFiO1w~{q>-14 zc^sp0J|PBVaVXxg5mhCXE3+vIqFjl*s%yz1c2Oa(uMz_D31^Rv>Q(Er1(vTT5(B^q zZQ`gA4f_VsxCqjOvjnmXc5Gx0)+y4Fm!?rINR)2c5@a=R?#N5>y}{5e+3gBT4vZIg zcRd3G&j3PR8-%=AERKl0HUN1YHvoCH5drc#Zqls_kagS?PBPDkhy|hAnB#TDSOO%u z!rpEsNRR*)3oX!N4n#bles&c1=$c5}?-v%lbQc9qiF4*w3q-eOHz>+)Vf1Ewh;mY4 z^x%$-Qxw^YC~6*}Gn* zFTw$s#vqL5kt!=)6H<>?wgB>CzJgxW9eF93g+Z1I3NDT`Ad9kY=~g?@4&2|4ymV3= zc_~gaY`&x(Cb(CAqJi@R@4jbXU>QKjYmJZ>i^UO<*BT+OH9}r3M1;K7>DCEt(fPP8 zoMfI65etIU=u+Yi-KY{XSD-mpL`~L$oJ0pM%zm7q#~g@wK>h3}uF*BuQ=6sjdTNjt zl>klEq_^F!xkz|sT1~!qJwqp-MtbsV~EIWp5E@uGirW>OQR1& zJfMDd6xZmQNZhU`2zgBq@@gp3V2S01(re}f@ugc7^S7`tCWW2Nu44JMPBtb{>C^Ju zOC&&E6NtR@5C@FoV*0Ybh9&KnI3WmmO`w?Gy7T!gC_x{L>fQt^AR65z7n^4ZAuq5P z*K~?Tqp3!q0MeLVR%=Hr=%H_9xDCI;LlM2{+sJRgKIZNZbWKNI@@v)$N|LvT-Ywq9 zk(Z=QE%=gb*cwmu(R|?iz`O4m7+3}zc}egEQBn`0BQKfvV@J9Ln=ukD6o+agr`iF zzTw#~g}i*nM)h?b3iA5V2aWM-$_(_v)&|B4yt|%(foA|AuL-L1VzD@4mDdDSc}-B2 zR|^qUc}>!-Q#BV?c})r@nP)`BSAvU5fG$=jwL^EW#0(Q+l^3pKxw$9mm^#R;I*N#H zIr1{l3=^~bEi8<#bgQ^jHZKRVN_>`Ve0TE#PJ-qkZ3$0J6N2TD#vB3=3H$V+^i zzQ9u=E^fo{qQA*)n>+H7DC%+~O5Qhmz1-b!8%JIyC7q`$+AE%$0_n(0qNrOWQL=~8 z>!qKl%FB_Lq*MRy1y8Aa1h?yr2F?$>`<{V;Wk7gHWSdVaFmMC1Wm(NCAt(IcBwrXL# zxtN()fwH;W&X0F-q#YYYWQBlU^p1_JyQl#f%#vO_Kco(9wK<=gy&ZYNuo|N{hZxL@ zq(Z)GbIy2*fog+77h5D`jPf~-DHK1aNSvTTK3r`1r2Z#^z;=EyU)fVYz{?z>u{sy;!psQMHd8!43Qu-utkx}VTcFR&#Q`WhowpK z8;P006voo5`b7d3_4_-&uA>{hy@0!4TPz+qn+PQxx{5ZR+x1IF5r7u}vS>4c7d=L_ zUBvoD)f&aJ7R3WX%)-T-fwl=zNe(U`C(GrtcvWYIK~`fCuH~g+P8ebr^I#I;_6y*++9@2>#Kyoe8SnIqg+o}8({f*!ZQGz&?b(JxD5vx zZsQ`zo!y*~krzmPN70d&rco_Oly2G*WHoQ@$V;-LUPzR@MTMvO5Qh#By#JnoVPzoG ztu%9x&h_Su1yf?NaD#cG9UHYq8z}*`n&7cU8mo8OeZ1dt`9?Xcma6&?^)DMijYG9JBNkimC zZ~mCZAdKdbDl1(Rl837{0PmA?tq}6UB5V#teQWZMtL7qLLC4iK z#T~kPC1%(_tc$40N|2N2tWoo~8F-=_6c4DMeTZvxqxIBgX}g{p}2OF1SQ zw-BjlSL~8vF2SvNNo}d=ZD(CaUN)m$L6*JZbc(N8k5{%Fc`1%{!4fq`UXn%KB8iec zj9xGO^eQh&uCC0iWM`t+qDw;u2;P6sz_2oq=~hwYHP2$flvpg>V4mg(Pt6hX!Xj)A zMSW}XkMriDr5wj&5Bo4Szh)mhHol=H@o!MO>y-rlXI4;(l=8J?Ni4x%&fM(~+0_n)QN` zs}}u#dU>16|XRm;9PNG=+2I zCGpl^=$7nu^oB(Xytkf#!Dk@Tt-8wVbjV_{(IJb48_mNnBb`o%&&DEb4n=Uu)1k4r zN0C?r80{TLHos=4$m1T-tsT1Nrh`au#5V6oJfIt$5sw<$&_;?In~=8ySlsMw-&}kJ zH`)Vnzkk&|I?ys;uLT!klceo7Y-L!l5w_dWK7uQ_IpaoOll?1;>WPB8=|R+~5AOY>_zBena|3` zS<-8ch17wqHs^D*wm^yEJp#1A{*^_9y#9hX#YLHcTZChS zCkXxko`FGSAk(cHdA*u=oo_LJ^=$e_g)*6F~`Y*|s?`8?Zs)4T=Y}Ib$E9f0< z$E^l7>772j!q3ylmS?z)OUqRIT`_0?{miN)n|Q6;%JEnl7suQJbw z6BMaelnU&`yuWj?%K}xrxTvzyJRypQL}mrWk$8U(BQ%>~QS~OcGZ$qBnkNMQBH$ex zWi?dvMyXVf&Y6PX8L483t7}QB>7qhjUnNBTc2tpY_GmdqX}G}h^#pPNIH64(znSpx z)tdcEw}2+B)dXGoOgB~@h) z5?`=kCYj}`7B4QvH|j9zJlJ$9eFX2D~_(b&VVe+ zx}{s~L_2VQJMz*=apa}o4dyl~D7YS}vVs#bc!J;$;29WH1~T0$s=St2ERYwA26-(J z@>(L~g+)ZSmQX}=YdKhv=$7q#7Bg(%@}SjfnW}{9Gl}-uZ;)=mF7-jT;5Ex+-UyFjskgXygS*)N+PIFnMaZMB)n)7f75T3oZ`q! z@sD1}3Cg@9uS;ZJ-6OayhFF4>=mBu@jHklcDlY;Cj=WS-F`iOE!R;CeM$VB}!LWka z6=dn_v5&9m$V;-QTO?7khtcb$pI+r9SrSG`GA4Sr7&$0f;GOjh3_b&yZWYLDp2Y%r zv1pLj93ihcLS9%zbZZVpiz$xBOYN@s{Wl`IWjmk6jJ$}F(5t^#W+OW`A#Z4{_!|@t zXi@q;#O)I75pYff$ZI(P$m@(KkyivZ6Y%0NnUtt2*|#X>Z((6f3Ok!!MMSss)WQ(c zSbRZ|3nW71H64S28$;yfq9P?-6N28~?Ql?akcA{9>P3Vm6MBn;RbE~DsUADH4}DEI zaJh=BFj@>8x+dgcE~->@O^Bf}{0a|6^rmkk&%zvYO^>uG=~~bQiJ2WZuktcE>4jOD zyO7Z0MvlBB&bmdAC42Z4a2q^9@CWb=3@QVeZaMM_5Xq7#lD}QdbgKkcSY7U+7#Ib@+w~RYBOQ3^4wx0#j;tldug7(g@w_}j*SR;q2jA$V-ZeQT%hJ> zkXJogmBp_iPB~--dP#_Sbq}s1FPZq4UNW!l5!@Cxf&*uMk4SLjrBc-uPpQiVx9c?x zcH!4_D5Cdw8%a%Z{gSz+N7|HJ40-)PquGMScypP7UQ&mU*N^6m#Uo_~u1w4ZMGL&M zo`Jz<03okQ7Kk7DK)WbDn2_|tV6lI(954+q*aQeX2s#Mc6?M3ZjRP29YgYworU z#ocbZ3eC&%uQTH2)kJQ$l~}K9&x?7z#z@gE&ZiLA8Iag*jNlM&>^k|BP$;UU)o6n0 zdi^r3v(8o&pQT7Eo($orgx9a4GzNAek960>8foUpUTL} zUZDLETvVBY39-XLt>$2%JQ_HK>RxzS{#fk8$U{j7SbK`5sfx&o?8xRWPP--q5wkH6 zCs4Fgi3xKWIL3?I8^YtFVqlj|h$d!FPd>5Hz^E1^%CzP0d>RZDz~D2G=~hHuOl%JI^7S>q2_nO&S_+YoPe~C}c^%O5^#`y9JKep! zSY=mYqBw7F0TIMVC@O>yBd;RTJFvaG6eCn2t&B|KcC##@3)%dhyD?ef=JPri!D4MV zqIk^Eb?Q74XN^S4(yNIKVs-6&B60bgy~;QzzL?0(YW4CgUh&y^UCxsj-{|>ul3w6V zbH3kP-~B#Cv)Ls-=FFgv_3LX>art7&$(lsKr z6Uo7MC(h6lbrFejJezCI=Tj@v%QqA)-I`7nB;gx8YjRTEwM*ye5)autz$raONd%B& z`iw-lV629UVIxxylMoh#ih+F>q<(b&3o?-JP^)SJ`@)He;7I(y#ru&&A^QKyc%&8*HvMqa}aRf3pqEe#9$2GKC9j>wB**;k9hwfsoe(A+N!R zDr*6G89>B%>Z;7z4TM%BDfEIqg2IJoVyMjEd6p(ef7E~q@**6SwWCxAVCmNb4OlfRv4$0 zeoFG?L_2bhyi86Uc`2gf$m^REGALT$o%IY1J_C-t9C;Bai+t(2#O(f%SMjD-oBA<{ z=`6;NEY?sfTZ?fvYoibG4DzZ+tFm~ZVf9w&K;vQwapWaaJ(#be`912+D!alQnaSw+ zHC0wH-9m`QK!PBk$jiYTzk87Z8V{U#J92;j$LIzI| z`~f@zgUSFxUXv`=r9=l7&01a)w3gQdt>uNq`uf>AOtM)Ul~^O0z^P&+6eY*1nqN#* z03-IZxtJ$5hAOWqcyA!7PENE!`BR;XVBG@9YThN4pgXAYf+MFNlbDVpuNrIxs}H&b zuj$B3z3fhJ5vMrvlB0BsV!RBoxgh9%Bt-E+R zak~X~HzN1)jQIL0Iv*onU-?`fME0ym`y)be`BFAzn|D%tp=+cgu9wF9`6Y$UNqkY_ zYW4d1^70aiWLjXY5^I+@B^@C$iS@GV)Z9F#8MxSKmCwG!Gs6MyekStFz!mvS=Id*+ zLXtxsDK$V`&i-F99y6IDb^VH&!e@hWn(b^*nzNk^64Nh2KGT5M ztu|@kz;bOga1w}yfg6v~xV_W^H~v{O7Ou-1@jA_a{}sLf^2Y-0kKm$A(ko2JRBjj% zo9Hp(5u<_2V^kUR%Qcoe3@*ZN+^8Nn9=9}bajVAaXGMx+a~J#Bv9Zc)c^c=xiiP_0 zGmICH7$1xb{-*hHm6%Uqz4Yv*CKQ~S=g&Mcry?4?C8XZ??`J;#;sMmhEn*+tNA@43 zpLw)u!>hbB_PWJ_F5O4=2M-YZ0Xzf4%7FNr_%D%@_{$dG!GHcG zZUnpJVw2HKVK=A4+-y~c;Ha2(bPpXnq3nD2d*GC z(d6H*j0}?D$V=skv!tY3V1SOi(l<7q?KhbRPR86sWCmTe;mFI8*M)$A-^MdA>PZ3`%Ch-K9L-p=yiR$1Fr+QpsSu(MHT=jGy&vWpYLujcGZD%6#EUaDPK?W0-&= zFO?@ICrWbUWxh6?B^PA|a6)R*l`ThJj=YA=A-wmVfeU3o{ijlJ{dGWPrI&<&|Eg>w zsV)Y0;-btzF9|WD#B@uruF6s5r25ZZ*~94d(oesU%)kT_CXJv_^pc02>r&vw_S|9exii9sIt;EAha}|aoFGpS%0tS8?&%m%Vp#D?I-HT-ayvI%8?u^ZQc4%XtGXE6Ui#@bk{Kx1TZ>~zFX89~e1gW!k(bOSEkv#b_InY*Z(MWa_2b9I z`sp{489;v(n|yl$nkp+T5RG~vC%p6H#}D%dl%iT()0-C==qq)4Vj~UP zj~`|<`R*>t3|u)OVW9L6jeOW2%z9Rj-1%YS1Mj_OU}zao|E=Th#WDci;~aVE?Dz5p zt#879iyNVVLoauboQ&WqD=qHcvGb4I91WZ!FZGSM#jhe5I7eP;+}ivF(@oj$W!yVe zc{%dZ<8(PRS0I;YGWvj`qyZd8Z{sl1#UcY|^I611v#&@;Uh=DZWsoI16FuA|O}m2s z$kb@i&ptF@;QjRs3_An=MaO^s^P7%Od3+)M62~V;{*sM9zeW6yIez~(65n0lJlq|K zM&f>6Of*UU^9S6W#Xt7o#^U&(gTuJ{Z07@t=^=9z8@C~z$|H5D)K2{65&}EFjKbp4 z9&JQ;O(Ztej@!>)wo?Zv(r=aN{lQAdql+p}%@eX!ErW|i8l$~DcDof6Y2akwo+7oh zAHKM=$Lg2W=-Hc#8+EMc__eMN_QUa79LsosZ3pMG*_6g8HA?4~85C*YQbanx%#5^J z4ML>+*nWP4_^cDc*L?m}jX`~y&!2z2$b+|coTMEG<2mm52xfK@E~7E3gVWZKP^5vg zk%hEaq~FDgnb4FJs1=;vQFaE877gdz=`jkFRWP#0yQS|mg^jNZ?hRc0#;oFPB|qJgs_ z4IB`S^zVQF%f`8A@`STw!breHcQi(pAVBdKiUf`S^)DAyN}DG{HFWp>GJuHJe{pS?sMEgk^VU|m}LV+`USI*RyrOxF3Lo~ge*~( zma<{=ut;MhKo&!h1r%xEY^0R-`_TLr#RPkb^FmXO*!dzNnH?hmIaw~B1*WLp*cCa6 zsue5bRp7DLKw%hpS&;?~h^0hyhmVR*H!pi5dTH!Eq6jPMMIECq!#w=qdCsw3^1EP_} zS7Sjbdm%|UWzAS=yY9Pr@9ziM_wAA{L%vZ*A-EZ zT)V3bIPywRr8=L%Q!0wx&XJD1GHhcbrMN&>r!5o_^4cQgwFS)T)LdMH=0_|dy0u0k z*995O@&ZMA9k7vBIv!tKl!&)vkv|{vOkk?sJx8^|ulji;o#X@F4 zy~<0aCId{xaamD?yd&+!#=+0nvT32 zc~y*sUhWKLd2!?=bJ7B2*=(KzgYp*Da6pm9NC6sRR#0^0B@@+#?9~(Fvi1B6;4MhE zpy?-Fc2(C@)P!A(yi_UI8P}1QP3Q&DT%#&4M_vkTk=vPW*-O$jHMAo5 z(L&U%9eDwkbUBdKyhsdm)2qA`@%pzTFO4OBX@PwgRaRg^RE)d#mjOp!3Q?WGQ!45U zkkvfWk(VQ{2v_C^dCd{>nj@IwqGD7n5~5lLcMe5Fw|JVB#5?3o5b~PXIF2-pA6?9# zt2QR4Sm+EU!7|qc)zGJ^$l`&sA`Ki6ODQ!MagQbfc{}qjawqGF}>UwYBfg4Yi#3ID;HJr# -<#LRW7v5?Uu#*WyUOaFm$_f;R z2M&m(RMD-mhv-WrJAza zf^NZjFb&MkR(buPzlvr+ePW|Z$>rFZx^ZwJ^5TJ0Bo$Bn!%aK=Fg9|U)1sC{AO^Wle`C49%ycBtI* zVpbP-Wy_IQ4c#gj{_9x+X*6#bUYqD?@j~{bUgGRZqYTdNMj`S zVchR`JKl`IiZpOm{8UnGK4@`gkCl}uOU=cNJ{aw-i-Y}el+>r9V~bh29bd4k9UD`l zbbjF-8CEDDa?&}78Bk_pK7ZD1 zY)*&TRg)zW1w#rB73|R%)dLy>r@VTe6=~qCSSw_u<}OwlNC}B&@pC#BZgeJNJ4Vw9 ztl8r#FI{gr#v%=zjnoSH8C0E8jXJbMFXdx4>1-f z(!kkBqmTgB$#>BtszsJ+(v__+a0auqsPZyxNoc`syt&Lki`3CCmwhjXBK^kMNLM@q zo`F^wsE`-^I@QPTDAod7P^>tZjQm6(tA)tOi=hTzmCxZwbnE{U2~QPA)wqFJT~|h? zo7Kpxx*(ruhe2jmeAFO}I$S%+f+96n)P@-i6hT}DS%N(6D-I@53l^&fk&$Ob5k}6Q zBb$rr0b=uS7u5yHJ?(WL*>5gFkkwq=fV&?z^u0Ei1&j3c1Cd5L(lzf+1P^rFbQ71T zc?*#SP5>%?<6l6L1`b3DX}^!nZ%|Amqc|@B;h|?QB9ggCMtN>7K4)N37|#MzRE{Di zQMF=)yok!=BXAf-URfa{iZpOm{4Avud4HmO$|@m9G$4y}_Y8Q*6p9UZY~&Yu8l!sX zs~cdE2F^y-3R$W71B)Z{>?1UABfEP2^LV7m3Htz?5O|@1Gr=v^@-l5n`1!~p$TDPB z9Ysf88b!4rQMxJny)@6Dt2P{YN#ykJ;qW^8Q)yq=p-BJ6*+^GB1D=6a8F1u9e@Uu? zFiuzG^@flaiPe!H7@bM9j0TH{oE%V<*A^i!ELI&7m)@QgMHo3Dvbm@pG&cWsQC*PS z6RNXnA;Ou79YwASGVJ9AiuAPvkw&7u2odrcwg{f`_;Ov5#;D{t!EUi*BLYOv_a~YZ zkzneAI0%ac>B{Ca*syU1H)n)m*Bu)X-Fm)15z;Lv20W;KK)MCM$@8Qk@*={)i?A6W zgGKsNX(Nq70wz2jDKpRzc`=DWZ~BhB6rbzL>kRT@oYGOW1NXNhFP)TLn3cW>`z>x% zldfzz@^a+W>b2j4Jp+%L0Y_d5c!iR(uE^_vkQa&7Q6Mi8Eu(=V;x@+wvP7{}5(SLH z;#s;yAIRn$DHL)XdC836WxF68bL6GK6-IL^D7cQi6i>QEVy3znTuirMWaeCj;mE5d zPk}V`0^I6*1S{SMii&o{NXbm1hmn_^kcAXSUN)nb<8@%Hx*wJ zpOI*h7ZxR63EpwzRbC=}MB#)VoE8OLh%25Tv+5{9^1zimCvY1cI4h!puF3t0 z<%5$Q69Pqqyue~y)9KkEY4E{0&84xA$|lM_c?iY2vJrAoC7t~Xa%;q}(+3p*6)-#8oTif6zx&?*Ctyy!1U zbr6~ATIJ=)OYyl-52j$0Pi)le#*vrG$d&1q>P>KYj10ONLy`WTfJh_JmWv2^4O;|H zxf(YYGv_Ldv8g#BWCGXC#v+YTiEaY1;eoRv4V)Db-5TGYSUxz}F-uTH$P2A)J{=>p zg~!N5I`Wceiam^{Y+Uz9g=}aAbvmGEC;Ugf2D5-w3wK@mD#SET>Tc$rG{LG-M zHXM0rL^YUlGDx?e=*X)T+3;Y`0L;KYG(nE`9L!Vig2AFx$ird{jIu?WGn&TKT#7N8 zL^}_V?%hQsbY+`k5d_lajQOZ9gDy#1F4h6rIwSJFjyXZmh#Nkt0w`+!OG)o1EwFz= z6C0B#DN7Y8#p)rOh%zR6W_2%?FC`w)97ZG}MAAwFkA6HN5pLs%CQBZ-^v{{;!vTu) zJpqwMIvx*bV&kwy@RY|x58@VG6N@xPYJbJujsd0>Y2d8*sigg$n-5yt*<)oTdd%kH zMjuSV*2R%QS&3*WI<}aJJ?Yq~c5KY8D&vKBY_uW`oE1SO9yQl-tJSTE%)(4R&x`tj zLL?H+%4{47w%VM}&EAf4I@GS3ERkpt4b^?o7}WzB1E;)UgcWJvtXL~#rRFYH8Au6< zXYq477H)JVsE`jATXL0`F54Spkp|92YK8pq(<6w47VTp~k+u!6k>^AW+z6I|)$d0m zU%p;5rlBCOzvwqkQ=fnHq&X|nZ=4m4Lc(CE(V#m|V`N{r@|sZq;tI&WJjU{0~I8{$KH*>iGC9a2phBK37C0@i+ao$jmAkzu&Q#KCjOdSv%Px z(aP_USuxB?fgc|v>LZ95iDl!2*7CxlMP5W*5c1k1x`oB6mjd7Hu_$7qvqyC6j0k$a zN6e}h5%m4EZ!AKP)m+?w8yz?F1vQuji}bq$B8_yUYu=p*9_YB~CN5F)79tIt091?v z7Eq*t1Cc`7?_={D6gB1ukJ(rRfvZJh5s;JR@>yVt%2DJbs#dI!7g3pfgqkQuURfa{ ziZpOmL~|Ht_a~MQo)dyZ1F|@Gc*jNyvS@*?Vw$RbYZnTQQ9bn4wIl~iA`P64tQE3S z_i8hvYS7hA9M@_lv%^7R$Gl-dW zRR-Lg5sQeN91!xtn=>K~7UmmL7saK|h$1FBTiNhS5g;$T)I8!~T_Zu?TdZl{aix0L z98PR}p?~8Hka?khA$6*)dB{3?Hc<78Y2VEut)=EBaK1=COjS~GcYO! zESUg#F^SQTAow(J3i{AAKq%6{*+`?1(3Khu`kDrLQI@-I<7fxYm#;TD@hY!NU>|jl z{`k@j-iZDx+81^x(w|Bj>56B-Gtepnj=TysMG)%-kr$pH=g3R2v|;bq=*UYUt}D|m z)tlh*7%AkmS}{4XBK^mS6~Rm|Ih70UAr&+&mOiGtY#2aBLo>9}#^CDJ!+PY8KE^!oEAMVzJPqNY2(W24E)B@53Sc}bwu z3rdo==#g1Swc*H1gQphINjGKxa?RxdfFmzKr&?5b zIr35{?#ygQL8gLp22TM5*f~;lF}SE|4$pRkGZQj-?35AEZ?!QrbGb^ zjS(EXMX~O8uwv(-h>#ar+k84k$ZOP0bmV0LRSRZi8$0qc)ar6&TISXyw7C1Fkk=1- z*ET?gXQWz@Ucjwr6cQd9PxXT=9V-?=3*gLDguj=Wlt4G;DVzzjI@ za^#h`P%2SNegs4*L@c~1CO48fV>3tA%{h+oe}>bAYB}x746t)R(sbw zHikef(noq0;K#=~G0iQkmXMEY$v1BP$3`|+>WPg-8`O8|d=ZgM5`UQrC?uXt6bW$0 z#vmHX!&4xQh(Ok9AmR~lup<@~UTN!^w4F}JBNEXzj&BH~?Y8s_W4dvGBK=;0NFyDO z2SkmAExv){0JOIjqS_si-R;(j+cO7^kqX}J7+_kF2F{9~O4{$a`Jly}Jyuqt$80Wc z^ub7NT^t#)l{BWB1G$)qJ?Yq~c5KY8D&vKBY_uW`oE5>0RBEo{R;ybRnZ%iXo)`54 zg-9ftMc6nJY_&O`o4p<9bf{f5St8NG94ZE*F{%eN22OePJS)<`S+Q2gO3huYGLRAy z&*JBFEZpc!P$Az0*6cCz(zSeJEYiT)NUe}RetHCPj2;t;v~7S*$T?ACG(uot_50Dt zcWhK}>n2OoYSNW0pV;Wg>(NPuKfh<7Q3e9?qW|uoBT%UDgT?>+C#vaZKwim7=g)us z`JL=={)Y|%S-;c8elc&MY=Z$B{^6UY2a*Rt&o)>A6OirXU7ZO7h{x6uzWqC6jMONIK`{H6lD7L zDz7ul3WBWWq9ZRyUNt)!OrccW<=Prd=jq=#`=Sm-`Zvx-y5bq|47AF?V<4|1LS9&; zKP>6U(CSD^c9L;G$csdK1hIx)(IU~pAS?oM@^)al6>e30x<$wfi->NWS&XZ=K{DG! z+NbQiLKn!~nQNvFmuFObg=ag$nTd_XJ&IhxiH#4tV0pibFhRvkH5*-5hdLtdhg>#ReF0@E$X!5n!hW+mO|wZMIea}{StUYeR+j>0+elJMsi zNtEni^m^%MO}et>$jgz}qYw*!PR~H63^?-2V2h0w$m^;*Hf}av<&|+48^cCk54&R{ zA+I^A@|vS6udZUQ;wp@JVP_MpWJ40YYa5=!)2ysWFW^>0AZv1eqIqx%o)cgW51bWg;H-#H(&YZc^1;cDS%PBtNc{kdanT&c4#zp<#gpa? z&v`<~tCw}%&8Z3S(T>2 zXN-{77}2de6w7QRS1E;CazyAeA`k!@dq)|v1%yL+R?mUf= zeSwD}4V;a1#WUa;XqAD0yae?jhsEYQHd4Gee#IRd#DE7CrhE3;STdcCqDBeS9ak3y=Wq%l=V*kUI3 zq+_Sru`xAD=NI0wF*)h{GNXY@PC8R$GVAjoL`snD=XtSyx@d(+B$~|#I1+5NIiH)o z9l3l`?W!3oD#5%+s{5ibss}U%PI9V~s7HQyYq*lluKRtp-Xwg0<6lvQ48+lIDY>fySSp9x9^5yF_W4dxdm+lVx z5PAJYzj22AIPy{{xkZRa17}~@p-6u!ZKNxn0nb3I4D^7!K1$@pyoHWW7Kv%m|C9IY zSA~%>@+z>>ZwfGUD~lx;W4c9u2g-wp6B6H>i{;g#O&AJ_72PU(^hwOh9wZjeSD7bB zx1eZv6$|u4X2-@oi#i&Mh;HrgPt?g^f8SU{AnVvz+;1!q*zGp-&zYfCSfp^+FMs~324lEX~4^7yk{m3FG;lVuR>(?`4=j$*v*R`G%~ay& zXRv%dp%hbp#5lz#HY&)}wH^#)88WMmq9ZTOm>hWxfEuYT2KN@}7VIbe8)skGp-BJ6 z*+^GB1D=6a8Mr;<#Z1f~uPl~83DPYn9_gC&7o$8rKe1TGM4KX!ctFUDM0*5LLoX6- z^%fQb*`n&7e4?F)Gnb7+4-S zE7HJO5z(#Z`xDCtCp%^dismUW`*{j0`lxA`P64Gztlr@OY%mzz7T+ zKweB@G$aT<4V;2LGz}1nG;lW3C?s^HMuWblL0(+#(^0eo_qQW2ofJo2iqi}RC8>Vj zEC-{%iuQ#aiu9+_M!Mn|@C>xdfFm#ZOOlT>bnCjvYa6(SE>UshC2J>j6asT{kkX!+D&mape&RtDWn z=r4&C5g?k}pJ*POgh_L;AYIuk>w@CD_*qbbK45ZE@|*yOc;Ku^17}54&^5U~v3zi{ zW0s&8K2krxVqDWH8jYsfIpoEY<|MQs?}-M^ibml`7i9);LL~4Zk6{254JBZK#>fPF zm6u9M3ycPtRY%bdoFgxj6GvX(OnB7gg8R@Ijs8^Hw{j@bzi~Fw70-ZYpj8GOdC^~z zd^qxw9Em)Pl7=!b(mW17QDgC9RbGy~B=#B%@*2~BoEV-nroSgvM0D#Gw_NPlPxIg; z_>PS=Itr+0jELEl#kz~Ziv5J5qFpgkf|Tfu5#NFfo?DUl0Ic$IH%gpkmJS#@mQk#51}2JcXnm+#o9vN9CpAxylz1n-QXTURH zGvLT8^DZ(Rc^Mq!$V*K_#2ic_o8a*Vbl{+W&eRo;M@L>Ve~!HL7iTbVOn01pM_x5L za^%&FgApWf3udKSI`ZlWovzy)GOLcFf;;3o@-jJb~iet+ihu#Og|1#q=B=MMj_$QL8M;U z<{ia$T>@EpJMrQ>Qnrfo#)>pE zd!?sp(^i=)D>5=G7hhvP95qFHeXZs|E@om+I(DiZ8&jine&HP(latObGa9($r1Q%x zS;ct}A|=T7^BY97G8&VeJ%UKpboMc!NZSV3 z$aA9Rbp*@6>i46OFJG^j*HDnxU-TPiAkV*`NWXD5(kLVhrci3oou@JSmpPzF17`wV z@C-JdkcM9iHHU58;b~J z9kB=y&3=QJz=p=ibO4Jqa5mB?By2i})N$MVq9RD$ZqtLfEGSGe*GmIlh`cOI#**wM z4V)lUj6fDpq=B=MLLnzKzd^AeJK0=ht1$Wy0XbPNp9Q9<97Rr|YQ+k9m3%DJSuygm zA`P4sONH#%{DDOSvN$7n$3_52rsc|P_N`qgG)DE%SJ#ppEQvI5HnLX8N|6sNj?lAr zS-zet@xbA0d1=PfbqXX(vfZ)q49J3X3yO}s9C_9J$dOl_i(vJV5c)UHzOh4*{*AMd zu6PDK1FbRuS<*ghiUbwtRkjYxl4riRfg-bwTC|Ep}`)ZAp0PAkz2BHovHtm{NJh8!46%$?hdG zU=96)2Ce`;(d3#FE7EVA6^%lIDVX7)hZggi^@VZe9UBqddVXY4|6;I@kr&wbZ_*HX zF;1ahE(6W5NWXD5(kLWg!sC%L10yhSH=;k4_Jtja^lzMvbj35^8EBOOM_%-oBp+w!7S9b- z$LHr|kQdc7MH-XpQqr0JDmwBiP!EPl}GWKGgzG z$%fJE+T4+sgi*IhqGS)F*GoVDwnr$>Gd#y9Hma2LQl_Qu5!@Cxf^-Xt^ak4M)!u_W z1D*k!0Y_dX%W8wXwy4T0h`P#a6C-7n*9KL2ZA>{9A+I>^OjHb!@iZ$d(w`J78ij<3i!uX~%()7qtP6_o z;%7k#`he?#YGzc;Oyq&HA`P4sONHzJd?t@9BIE@YX7GyPV zPJb%xTR9XRdD%p{;u&b00Y_eryb@>giH!;!U98H>k(WYTLqT3+`i~O{ZO9uVB{EV)SM7Ef$L^tk;bSbRDsy=z*!L?uW=bhc5MF0B0^qhZS(0E@hv^@IK@8uG)e(u(w_(uzhQ;TEaUp!Ht@6itQ%P(<{=tGraIy0SG#Uh-@9&=}2;mn2!Y2(n}k zqlf!RtkNq~5<>_?nD5mEv&Lzdk_9;ee8IPf$|=~=Fzkw4V)E?LPFf&Kz-6sqz>B1j$*4W?RARShvE)JLJ?*DZL7CO zB!3IUv|64%(-^5M>~_2vffZ@stf&-nsrjHqv?1wAXN~-g6=`PnN>A0Mtuj|uWb{`; z(ToC1=u;JhEoNd*I(DiZ8*{75c;Ouztw;lBMXiwkG8=%%6=|lQ-yoXh+W4AatIhe` z?Cr=ChSjc`aSkz<7fD!?#;6|97&ztC^Q=e%XT@3}D>Zkq%0Nm;Jd2;xv2dd^L4|y{ z*piW#F54Spkp|92YK8pq(<6vu^q5c_J%NOr6BU5tZ3n>W_oI>T*eJ=`ZI-AZfFcc?jT8zwq4^Dp0OqU~ zml6RvSuURirl=f6PNHhX3VD@$EEFj*^0FcgoE1xj?AZK)MFX-pBY63GRH{_2%my$< zDbX0!LtkA>a`AguITL z1F?Tpz$nBVMgJ;?i<;E&-~WQ$6`PC17}N#Hg=^Y>d>m|-SPZ3S^0`8!lsMAjt>eM4 zDSGvfML3E9K;*?0MCJ-xdIT#Xy5(zmDQeVhV&i@-a7sicE3OMtw>cht$HoMfsx#h5 zQ3VCJm&kxM^bZ<1Lwq!U5c2|Fh2c9k)_^U1DTlpdV}VmjzNKu&DfAm> zpcxkF-#8m-6cRAu@kp70Q87lz1jvhdmxctvr-4(@XFT@h>s3lxa4-WFDa$`Qigw`Q zD(FV{wY*eHt~{;MtGsG3#@4Xm82zcVFYHjHf8%VVE1m(*K&uQm@}j>a#c^xMi)xzU zGE=%lh5jl==2qY=M_vjh!4zwBQL|UdI4GHE+pU^Y1$Qh8aOkY zj=WS#TF@=kGPtHA^R4f({;OqemiiLSHdO5 zS&^-?t|Ko=q1d||uPX*DnU1^^vKqHwR=O$sEt+?vTd=v}IOIC=GC8?2-7@_l;in@n zM_#So@jcix;2FpnNZ>@2EP0hz0m05yd2L0?g7fjQMO9uK{!mwWZBUihrr=l6B;*z6 zor#Jea_E&UcA#kD$Vj(W+ zGz01r8&yg$iBdtq?IjW+FCI7vf_TOs6zShM8)+17?xM^3|}7(|6>hQsu}?eNB2{ zLFyjCy~7(h^11+tlDCMSBd=Cy!Gk>mx0nG@-IbhJ<(0hZ%v>m%Y{hwcY!wF(^L)Ol zMfTbFMB;~;0~kV8Z^)(g;%b!xuh~&VH~J3}VVB-f!;JPqo}hH=!_QWHuQhypgE-m=8ynIw2kB2me7wrcD$Q_xh#)n zL(znh5H~nbpEMMygEq3G*s4o=og((3h_=OigCffO+g5LnNd6Xxdeyw-(-^5M>~^c> zqH0SnH6OHyHY8o8Y!&B?6=`PnN>A0Mtuj|uWGq)g(TwS84D|Y1(Xqu$>`BKC@7PFV zlv`EC%dEMm+min>8-U0aX{MjwAe!ac_?lo5w)x!b?Kr1H?W)NVi5BKi-4~5fJ)kjg z`rKTs+wvD798>1Iu>qpCa91P7hCc?Q(d+??4haP+geXxfr+htTP|kBN{M51gSi|7tFpwj}gIc>G2( z13X5S3p5vPTUR^-o`EkiU`Y}wJs__Wf=*aOfarw!dHVdXJx;%@_TMui{bkX|sRBvj ze#PBu$X19c_~YY*J72fT)(Igm0A7g4*n=^4eZx2w1; zC`>ZfO9Nhryevz`lI$gokswq|Jr>PH(UzRh{02n;b5@H>iGZ9eml9J{AVZOps9Lc? zUL_w3MM{jk8j58Y(;Yr$4=fsx#Rj|ZpWuh3TFx}FaWz6KHp=iQL=vpW>=xc&3LuS=cbmXOB z)-CcnM_!5=NjG|f;dS&^(Y~HH7j0WtJOiGAFEWs5jX3v!yp9NYVG&WIqv&7d91-$5 zBII>Oq`xfsI3nZ)?*8#{uw7y?Gp*VrK*5E}c0sY*T3-9Lz$p=(thg@7^y;O#XxftS(m|x}m2G}eF)^j` zj5ks&Ba+=qB)`O)gT}~^AFerRD4H-5Ou-BXJ+zo-GZw~kl%aPY5K$f~ka4|7!jJ(G5awnk;c~~@I9O{34+M`S98&{<>oHR4D^x^8YBC%-dwb8 zUGWTf2ENFEBQN^%QXG!Fz8Pe}IgC(rC<`)&4lcjET7-XeOAyuJVl{*h;37#V1cygukZW9r~) z&fX`QviE^js&65t`G9wGen(GY7q2Jfgj6IR;qvuI8Y4xlXv0S+nlKXZevx+-84!_v zA`;QIm|gdFeA^N^(#MY1TQk^-N3)@5!bpf49H>tkiqt_H*->oOrM*rO`%pyNV!lBU zW&Ulew?`y@3&gZqo<7qUsVnSuyct15QH7C9%?B-_4M|rzYvgaNNHeonda5>UmASGa z>hzJ76fy zznY7tEeX949>0;y0FRL+1RiLNT{Vs1G?Eho$T-RH-b#C47nN4FmKE~(RIn1{E z{%>)Eq(~9KP?l6hvSdR)EMS-vDbSg#d>2T%>QDp^#m_`^3qvQtGRP%NL9ZR!&j8Me z#LNvQ7F974PW^~YP|``idymzRsC6s?ZgbirCa|M1Qjc{zv(*$;F%rUVZ{nMpB6VDs z>{SGzJMKFWH-N_MHVV8DdD-NQCE6PrBLzBE^+i>egq4Xr0)U)sHXKtFAVZLoplY!| zUO6B0NlFfR)fDq$j_$Z?@xY=2SuP-W`+Ag>l<&-@XYEX(F)FcD@hr~4z7@;5WW|yX zEKbn7<1O*CZR?{6wy!6YqEd|7Y%tC@+ygpA+ItP z-s&VE^iQRpp4S(3T~jOq7J+XfpswF@mMyjt^24g}mEINjHgC7Y!WeGvwKcjUpr} z1;~BhK+M)NCeu!AECHMURlbzIw|W-UUj_*nc|}~3-DfqCm%}Oa7pI_E15wo_0TUi8 z$s;hy1}vHYc{$!CA%XX4-~`{98uF46X@k5d%569J(gXLGAukye%kt_2`-nEW<4Xf< zi2kY6)ARbGu4{@#z#{NX1Qg40f*~(Xt}ZCcs~_Y=IZc5D$rKg(SJ9AH#*VrmX6=v{ zKC!U@-J&7RUND$zrI64@B&MpX_bT=Vvg$X4CiQOM1m7{_r30cALtYF}^^B)j+eM06 zTVjU(IvOA^mdI7<81mv2%6a|qy2!b*yCJU@%t}^uz9BCGn1;xgL>Ik#<9?#Oyli2k zpjn+{WQjI@1kKf_Td?FeQ+xZ$B482F5isN>=@$3B2a(qSWqBPy?T8Jiy-P50NmYylz5jfDk?5AL-m8d66h26? zw~}Bho245L#dF*;tD2%JM#9WQ9)Vd(?aGWiFDT5xD?thR!sP|!e~F&hxPhxF=EcYk z)gM_z$O~-7HJ_u=sH+hZfC*i0(VT)oSX9MG=w!3^`)f!c8aLO6B4Przuu+7n0o@Wc zz1udSORYYoK?B^zvb@xf8AJ(&u20dB7e`N{p$=Sx;UsDHZ{Xz&8;v zid7-_{=TpSD;D+Z~q`AH! zFTv9K<8=zWI2Zs$MG2fh|0=4&wwIR(NdpcBnN>?s51gIYsA4h} zk}z(RG?q7_G1AlX`l7CDibcR8@J$2^c^UGG=&CNWx?WqAG~}ho52jo6KYY5RQb^Eg zTJ=RkUYdkK;svw3Fx{eo<0QxEmWoFO?n|62GYol^h^uG1RW_WlHA7w;MfFFbc)P*t zq;)}O=+HpApr|N;6X+j6RoIZ12#Fyt@!J_9UlLvP?v4A2vb+p=3B)xTURR%P!IIxh z?d>azfJHz@pdIr1Os6Mi;0SG0|50Deo-{u59d!8}i(p~n`?aokfhRUz=wHRiuhJch z>FdnE=S5}(l6m8j2>W0y^|&y6DIi{9_4?;UqGKH24uq5Bb%ZB2y3tfUz@jQfLO9?+ zd{a}T4(gIE#YSB^$q+doibqruis-<{x7|r!2Ou7H`fNwSV(#1l@qpuTTVE7)$))O} z7SVyETe;^T`FkzW%IvLNs!c~_ZnfxGSq>!=JIiTI!N*n$b|#&3iW3`Ct-@Hl6B}!a zvKaa6#Rx<$#8Qb9Z1wO|dNI zehB`W^V2^~oWFz^u+RCFxxtyCM826$&!cg3$V(>pO&f@^F8R~vM-T}u>eqxK9UGua zR*8bOxnnlL?)Q_4ZC@`5*Ny3x?2hxF5c0CUybO8i$-OBS0gHf)KyBppN&gvB7B0vR zL|zDkeOv(xY9f|Rmu+6riH$)Nt&u1ca#5E)k@#`_?OvTf;T@H1A|kqlQIcR8K3%<$TO1DLLd-Q_PDwy5p|J1B(h|xq#s9>rqxxzB8MiwKIjrsKi#qvp5I) zRxIn16-z#_I6?1@x5Ur3t&b+yzMfEuT3zRGie-5T$kZUAOVsqP-pi|kSwWChUo_+; z8IvKev7#i=Z|@E<^^5*DPR_~y_@ln4>zZN_un2q;fgB|z8=ctL5_w%XFbZA+G4(BD zPZxx|f+$)eQ7GhAY}=&^Qb~Zk&Y<9XZSitee2EYR63-zC=PWMzS24sSd(MHx%nc?M zRWTA;Emy2x{ZSS+o^}kU1az`><1<9voZmvd56RK;9%6L|zM@>+mv2DAkf=QKt+q4ERk2Ck-<7b81Ve`HbpWsrc8 zSHvaReO42BIh;acq@Y;?QPm{@6CNwcBQVJZESdm$Io>59f%j?P6uPm6jUprsIG6&9 zF59Q2s0YrruU9cK?i$Gsi)`lMP1hvi-1Mon+W76DcOd+IL^8$ z@;ZeCB)MRS{#7*OmGK{#rqxp1WzSD07D1|#Tc@S326T&tIJFuLji>0FHe#=IE51d# zyVfJo%I`N8(WKrDoElC;ULquIAzrqHS2AB}(=9_@C5r0~&7r@J2FQyga#cEpyf}q& zUVpqUa<1%d$V(urX#-{@t2)0y^_FxCR#$lE*^rlti6Jk+X$*P&a6szQEm-oKsl9z= z5wHm82qesv8-~1UAg=>2r1E8oVffHzqRAI~V5+O0U*vOGp#W{?kkawq0%t%)EEdke8j<$T8UejfR8Or(3Y(H&c82$|7J9&=E+OD>n>z8S;vh>gPB3GUSzmByTrJ z{Ze&F&}mxr#jZe>>Stv`IqHhGAuj=hFd!0zzClT2DVaD|W*G7+5m(Q2t86%9Ylgfy zit3L<@pgmPN$U#HIC20*gcK~xON6Q|9BWCpV0ArkhP+fvhREv-d2zfo8oI^19lSpv z$f_^u8NDeM0gJ#l5%@b?={Q19z98}{m{<;jfb{Y4`5_NCbX9&M5tby;U3oiI_80o1 zz6iFo|M>WV^-)7edj}#qnz2H=A{~jBbBGdsMm9xveBfhNAemz>sbI=CLVDrC@I`@m zh0i!46^iH#%C`ei9(f&M<`{ftKO%KdH`G$3u<2)3iZVpbKOyl5#4{A(fsebBz79a7 zV;tqic?Uvq8(sT&+&;314kX>G&>aqYEz-*Dtz4>2M`doc=vZ9@q3)j8T~1>PKDJu0 zGwGb;PHaq7N@Kk!?PT4VNCDDs^aG;WuFX9YoI9{ws`ZE)|4Os62Px*~{G zB|4nL+0oUx#J(Aix_)^HiM;j)mDHDirjS38_;HmdHaaqeNK{)9lPC>d-rgYcI!c)1 z{GPNld2Hx=Q+#;}W;=oc0NjXCWR6M)DIKT|a9-5)PW9oNt7RRp0sLT1%c z)FA6XbW4F3A}^bqu|#{5&`4&iGCP?jrceZ@*=#uFC^!h`A(f&-UO60dc*b1| z6H^2l21J1@S08U*kFt{To!RuPohb}?70=@1ZsNRtKqe@jcuV|j+xlpN?du7ps01R1 zQ*=HUm28{oB)N_AALW@K%8*&L6b*Svlr}`%XUJ>#AdB}fco?|-_>A5jXc4dod=r7b zkk>^*EXSIvM2);IuDC>A7lgcm=!$0-iTb*rEUyaDZ55wA!6C1+`;b>%@p3sUJVi(Y ziRZIt9cOX582KxE&d)DGyk6@*la?!HJt@p85y2b?oo*{aJcUKTZB9FeQvy2KZqU28 zIdYMn*QPpkTvzW^j7%xN5AnoC!EwB+GG3f&q6k%kfMn|#liBvf#>94l;R(-+i75h2 z1LDhz4~)DbF3Ik*n#hZQ2Bt9xqghE2m9_!N1}vHYc{vgzA%XWF8BcZb?h1KPl%HCP zdf@29MlD*Fmx{@aIhY|Y4wNuT;$^;rPPZ-j&D`9+vItlNemnw(yfTtS=J1f$DPSMD zU?>u=d|@MiPW72p%aT=e3vw`oeQry)VC$G}q0ZwI8^z4uyP7YxWL7P!_e!@^=v?bL z>WcP_MKPZbE*i}SFvqI%2;k5q&XpO4yb_%XDQY+7b?C370rFyrT$PR?FF~=$Z40vW z6RVyXIahW!r9?=@zW6NY1k%FBOv;(=F8>5}pisaY$vz>&K~pJw-GSp z^FGy^0ZY<@X_OU(bn6@Mb3R2(%4IM!J%j=LLm1cqJ%7UrfqBK5uzqGcz$oplLu5 z@&cQ2&F5$|>S|TUtCQW@5es_gh9upYy2&>b(X3pTq^kV^BGu2)w+@+AOHmJ;AukmZ zLtY#&jprmp(Qoe#5suw0S+Ezg2n;d;hP(`UMflb#NzAgmh!e4Ew@Z`C%+P$P3y@X+ znbc+7rC!AxbwztD$m>xKQsi7Kc%8g^;#`^0_Qb~Qug{sD*i21K5oj6^guKw+=JP4y zTX4g3EfS`HNlgz;qIyGfiW)f>07XR!V9C)#C-v^VzEMlM1*_|UqZ1poXbT%vOm57< zP?p!Pu@D~XCy&4&p@4PDB5=PEFyv*(%a9lGI-WBYJj?RRqtX=zi?rC>l5RP|l~(fzd!nN>?Mj#YU#?<@;g58F)tDAA(qVX zO0r{zvwQmPe;aREHE+5q>Wl!=t|#=c`XV}R^6kKqzJhav7_ELoKVFi$rJvcW2%_|M zLLxe`@myOx?oRqT0FjPylpE(A2*+)7?c?$FkwtVM=}Lv}aM(-HFVbGgrP_2<=60dP z?RK-sVQ7KrR_w^df{v{g>`Xf6xDy)z*;_B}Uvvp=>NS5rRGYB5XM&?PmrHfF*_P;f*3J}$yozV>aW`>ZKOhqnPq=z?l8sR` z!S?lpQdFvu!zp?Rq-A+Y?9^y;GZ1CStXhhOybO7jM8=3&84GW95|BG)3-)RjfdNLK zFXVMW$g4u+49oMUi~E#xi>iC##U5SpOi8iGuQ;3$mQ={=dOZsSMlNL#)fW-aI0Nw< zl5o!ag2=1YW?Xh+qY6I6Q&|Fn6oIi0PJS!@E0xyl9;kxHMlfouhKb%OYTZ$z7#G@z^h3K_+}#h3_YxwnV2HbG$4X@h3bf$fWfFORUxlC z+$$c1?#8bv>CV(m;h~6T`nn`leNxp;)FHu9GUUa7W}ToU-bC<**pMMFj-EzCw|KXM zH$Ws{U9t$=a|8@|8S;wstW}a29eNm_Q{JmYPOE}fpG5Um(vVj^P+f7bF(a=VpKmD8 zl6S}5rNp^1!%l2WbjnU_yfF(JAfD=?ONzxf0sut>3Uo=MTPk=>7+_z>>lYf$A!46L z?k7TCc4DL8Nu%Ll_Y4>8-7Esbi+~|7LtYV40cKTygRd&O6(3&Ssbo%4qCG&-kXIZW zcQ@pv3Ddawl764<_Qr3Nk*;JK@-pO=xQ-#O6pCPU5)cJw9AkhYf)Bc+Aukm?LtgTy zV{A=C1P8>B7sp$ppMcdthWCFX zwSib3_Ly7fp>?%)P*5*xtW;{B?TC&?yk6f*u2fp15p%o}?Q{7a$-1CT{x5H_N`8kT zs(uY;1mtzet}lY)Cg0u;{KNt89U(>o)%!Zal0cSjy-)E#jj0Qr*!YITb8YdsJL&5H zL^{S%Zk%_ZOtg)zeLTKCvWN~OU8&F=4tpv3McOO5RGW^<+%A;3-EJ0n9$`Xf6(20#`S><{GaNo$ql8Yrvok8ioX)DS-$NV$FCTz>4I@@tc7e8TeDj6vcBILdZVnNSsae7|mxBv{LmMh*J}JR8T;swM9BMfIrT8 zRieaglr#o*zn@I(#74oKo-uTirJUbiNrb#?FE7E9M#I4#fHBxBS_B3Z0R*xx2v1d< zgy@);z&yA*d?pq@|0%EV>`#AUqiCl$@#BIRRVQNNI+-CjjB^Q7WCfDhp9uPN(d2|m zjYWhxW@u|QxZm0ZftS-BF#(9Y8Z?9=-=;dzCSAQ(QSmPNb)$H;6*b5@5ZzLYg~-b$ zXDrd)07$Z3naCs1G9ZAQY&I`!s$Mvphg6CVc_jiA48QjjBW8{Rxy!^dRMi1dAj=iS z+t&j~GS7Er)3bJ_uyHS*#mC)5fxNy8$OOd`Z;2oGtk3RX`+7ntYRR3$DVF6WAQRr9 zOBC`hE(xLxnN>^Cke7s6{edjmay3RX%EGRDdl%Nv5}Lct)18?PH2ROGeQw= zUoTUYJmE1>ATJycA1m>Jkyl1cqB*OHyc|wJ#LPqyl}-W@M=QGnBaTmwL6i%IQfgOb81fQ9ZAsxU-4bnhcgM%fd$djqT$(S@18ykx zO1D(#T?ATO54 zRp}V=5@?IuPUx0yh8oIHS2PWINkr8aWXb0_A3w7t-GbF6-FbdC1Rf^>hP*i9!s%ohWHmuvdtLofu|Cc-0gg|OVci+a7$PSlUCA`$ zC78;Mk(Zc7dUq+h=-nHN^kdVl_bNurmEV`S6Yc&)wzExi5cWa)L~TtJp=uD2jC3V4 z&kG6@@JdjEzJSOJu68uB{1?k3FB}jAgu!N9emgESAhk4_{zJmk$!_fk`#f|*ipDq? z07XR!V983Dqn>Fcbx7JKbQ$vEztSOsEZ)Q5;eOh}MoyW=P2nB~HQ1Y41cnd+Ltciw zA~I`0%n-L}$(IILy=|6PIe{!K&6g;aHxzrNTk2=yT8~64zu#Dll#AU3$MNos&o{ik zE+x*D8Fpf0`Ok;PP1Dl%R?i|rUTAOg`4qt|xZ$}LY2t6lOQ5*^D4fDB5M-fgy4O^7 z5^(;KmE;k?QG(2>rKkt*ucu#H>~mvwYn?}+KJxm-DNyjn&4M1}jfMaLtPd7}$BBR; zFGF62yl6$LqA?^G^2(#q6&%Y^SF{az37YjNY%L>Q$u#6uBCek4R@rdI)(m-Z6xAPz z;_U{nlh!$hAH0y?n~h!jv=(upqA>^hx}yOX{SK%^5J<;Hmj zLU9{i`?!C-p?E;8_bI9<9S-|d9nmk+Udg4}bX4Ycp~UTWvxp$n-3t`BVy7z>bZoU? zXVN*xo!BVop>x-Z`x6D8a;`dq@+U;O!Iys~IBIjbRA)Q7tVw?4;wmIkf_0IUA;v~C z6ch&(n%E#9Qxwl>2qF8NBXNcj`DQvjkH+m8WcN)?v$igMA*k^Fx3D)(4Bg<3s>~tT%+b-e5^D z3Y_#NeD5|8BWugh*C(ppoA~htpwoL7A|gCjg=icjfXJ&q@u$HHJx_xd5@GUHZ(`U* zO}r}>0k=8rkKlsH3qMmd?A;$P>G+ar2#SP|bjcqOH8ncy_bo*LUI55C9CjPUSctrA za>f$v4S*!um5Dq8Edv6`$!7Dyrs{>mc}S(`ke6dJ+2xaz7%`(SP0S+X%<6zBkmZWv z6B|)hQob{rp0zWDjeGGdKJF$8)ZCMce8^=QrQ>j|Z(1R@eZJ{Xn6`Ad?Us>V}Ylr-eU5!txO-s7MKdsBVcUv{6>L|!!O$210EG%G2h(l#L3fJM_zY!o1>M>MCvBB2zGyqbxc z#@LCCDkg@!I9~d{aq}hdGTy!M8w~*hSRX6`j}rj|vJMD&9bid>ybcIDH4qWj-%rG{|alVk4+tmA?eY$q{6%kkpb{wXBY~O>ZQsw}vp?Lhrr<1D6(IF?yk?5k2pQ zB6VE14#gj!TPk$!nK|l;_Kig`pARk@#=?0qkw>6yKr+&mOhaCGg}hiISEXafOHeFw z+k!0p#HwdT&XwH_c^UF5kYFGbAPhF+n$OW_)YYnxS0}r*BNp_~4N1CV+t;gz^tk}jvb;)&<*Yjd zS^NcqhhNi>7pF`O_!6(!7*BOkeFzZ1`d|@woCp~5;#pxuI#~{>Al-tZAuol(dc#v) zG-SvtAI~44TlwR>?Vg#Vu4vy_WScohM{pePKFUFgy}f%zx{_%pHVP6oE&USW^(P|a zh4wa|PZ8gO8=h;?keB9g{ZTjtc-$NuifF2?OHx(ap-aTCK|uOKUcb<2V(?-mc?9~4 z5<*^fVxz!Oqv2qWgBt8jEdoP`fFUnKUNPgV!D30pE=cGCI^Fh}hP?7|Hsqzjk0CD+ zlePg#oGUX7d6kIEmo(y@3sPqT2M8n?2tsC;vGM+Ji)86eML2( zctIsEkv`>jRPqA1ug3>I=Ff_De|C>~JjeTphdhr@nVNA#<+S80uQG-&g?MYPj>yWK2e?w`9Ch;7A=TrBw5YQfH= zbB;T)QBXPOt{3+w3Owc9HH%VFpsgr3&-V?1qc)dIb+)5RnB+$;u0jqaSQkkdVr(=+ zL2*E#i46iWMe&@55VFrX5@#rpZ>H1pXxyGbcHi{LL>2)Q5CVg&jCY_&CpPMmRU!vy z{+La$`~75MCpJp(grSox<^29iBINZa#3?572n-R9JrHZKSG5QXAOZ+^odGraJOjS; zS(N0+3swK95CQT!BjiP`>rSCAeVh?W8jTo{RCd?g#t{mLy!sOXAi5k|iDy*(j95?y z;@cTvpECk4XT(@eSOnAm13a;@KM~Qb4HOYy+H@gmYIN8EzSKwrkq02_K+jBUB^iXg z3OG>S98&?1WV*}A3r_*=L_~CJ z2}SUl=VcdSp2HLE8}H@C=7xo{Iu9rX>@)EqOSwIx>qNulB z?WhOtFGF53D2BWQyusW?6!NZ>6j8wec_7wcuWAt(Km-u-I%vA|@mcKa&cN4aQIaDs zRQ;nu1jy@vkQcSCJB7OR0nSW(e{Ucr0F)V+1+~IH2)q!Y;mGUgFOac9Qr5j>_9wP_ zcgVrIKwbxgypVW08~|6`fmLs(3NZ{-_UYgapUwxb6Oe_512+We78JXnTkx3?bLICN zBQLhhCpu^}4S9*Ev<*nMg;z3PYSS%4UbUE2S;$-8&@EU}(5_HjfEe#aT9V&c8rL_D zAuk=#PUx0yWWcPQkQdq~YHOn4U=0G2I9F!mc|l?R{S7M7 zU%@OdxZ2Ug@?R_uvT#5U@&cQ2&F6Y7>MO(qAmQm`w|0bm9=aigXxtPYifE>Hj1C~SdiDR z`i#X&@(A3Rm<@mdSQjh;4-^4IUWU93d3Cg~v7D)vmLV^}y>3jma?lp-xRjCK4Nvh1 zbb{s*=gJI2UgbX@hP=8UJoZc;0nMxod1-3ZU}d7NJjjKOhP(`UB_MQT7L@vt8F$#F z2VxEOsuqC(L;xW#w{6znY|}31e17S&R}$maTeL0LZ(rmZl;82nMgG*H`>+fp$_NaUHZJ{_(IwormjTI}t(W-_U85J&FQ>c^9343D3%`E1pgV zv@_d5rRN>!oPo94KJWE)z1G=%$NlRK#RF=+Pf+SQ)H~q&1LiG($mgK%t2Z0y0JMoQ4px&p8rjQ;x;FH=CfPs?S37z~||a z#g@oRR;R45X9e6EiYjw#GO_LJ1@U?wOP1H485c_C9Ri~CZkaLe*Bk%?ur62x9w-7D zV|{#_9r7wRmUke_!JHUeK;*`o08^QGd08eaOqk-#01h4tF zh9V+S>n=o1jSf4&ml}y6@&IHV=$VPFB!iGwVg}K0$u-fAcgI;)(TR5xV1)6v%S1!zVTZNHWiNW)l=dx@_EwXYp}2Q6R7H z0y07Igd3*S+`gVj3;-u|h@(U_&R2-WL=Yw%B@ktBVx!|=tx6j5GUQdVq;aF9@;ypx zh;Q^jtifK@A~1jmAmp`2$ZHRf*ANeO&ecd0qBdiD+K- zdE!xFHxUt$Kr{yKRNq7-_#mmmX|I{U>7?I?`UqgoXv8z>d`t0!o{7Zs38fXa786Br zMo3lJLgckTKd??jM7I`Dl)ppgFS-!(9G+;i#GWY5qmkb`5n0Yda29jH1YRA?Q5i2L ziZC?{h$}8HJF&5hsYi42SJ$)Xn-d=xd4YrfW;Kx);Q&ly5Jt0-A}VbI0tU`bY?Oow z<0)Cn`E5%okVR-l)2(Krq&v^ge;M+UL9r~a5*T@Fh}1}Qaa{cz00XctSOgv@0*1U8 z4XfZ_h)A?UUUg4wWYi>gSht;H$V-jV4k0fj9>7lZlmetyy&da|4$FYWQ`@=fB?+C- zE!YsGTTtwRZoy~f04v%MdA-DM^LtBimOm@nGlab4p5cbPN)Qt)hpHrvX&NPoyZkd3 z;Fh5;MrGT8fBn4H9`x;}GQcPDDhvrcgw5Yubev;Z=U0=1#Qx z6VbFBA}_@ce$#r1J@+tDF7}C|B)38H zp#w*4E|=Dj8Rxa4Aw=?X8eS~P*5CDXkvqaOxcNzMd+sC&OYZzoS{U%nNH86 z%)V!l-RD`)(<6(5c7=facadn?bf)ml_fBsO-gFCrFn9-atk%n1;Q^RYxT zrL`^1(i(|)M*Zwdd^>MZkhhvyozGiMIZh`izJlrhVFMa^TMtk~@4oIr)bTv*0AFe( zg2)4qMZ{~Z7>h$*Y~t^y^px2mM2}fkCZ-594G2o;0_23Y#Aj3W!V%;os9GfCC9~#y zl9EJTCZ=%=Ms+|G$Z|#TiH$rPDtP(MY=VLamm{y@S$v316v*qlfWUad)uWSo+t(Ab z0pMf;^1-O2LNt!}nCPOAT{s{h%HYIC$H7{aG~{K-tE5Cj1Vm-;N^9Kx94$Gpj#~sq z83Ba6_6T|H5%LnEM^G0M6^m*uswcj`U-tFIk^q$!%8j|)R@@Q#kW}G>QdUkdS!h+h zTxpF&JfnWL6i?`xNIacZ8oaa@8BPgn=C&(Dw-y5wi!MaIs4#f&#Kt))*@?(< z9)h!&3nt)+jm&s4v3`{5Bd^+rGunxbW%xa_lE2R%5&c8N2S#3ioXlz>FTwzr#uQM8 z-K-=F$9AYkC^#Kw$Fc@yI)S<3lsODd2>XhzemW}>7!&(D7u@{&QZEUywRd28H! zNxaMu-e{E6z&dRa7;OX)^4cNfwL{2Dhyi(#s94mct*D+z$ZJ<$ED2C)L2A^Octi~y z1@fXbS42&AjGP1qZp?mJlrL9WBN5N2pDo25dgg9k#wJ>a-CQ9rN&%XSMQ=y960W95 zcxGNK!eY1i0O=O&QWta!J`;f~h`iVWw^*yCh!1>3$V(;*8Qm)Jinp2u#E@4C;E#}~ z^t51f_e?>%LPLVNco%?^7m>+T=@KYt%WY7X?3p4eI3Qr;40%bI)fQyQ=Q$rg(~uWu zQ9~qAyobT-q@QT}dRy2iXx3=Z+-S*xb=)E_$_OCjwM58kiIA5N1M(tKv8YR1Q9Y56 z*RsAC*+za}=1#Qx5|5~% zLS8jR8Z5TmP@Is?5MLUinEeiuIZIY%v=#H8b+j>wir?n`cXp23v9+UpQF*JtI_ly(wIBkD_%e- z@80+|6^(IIcqpQozAjk_bJR1fqz*~jgf2+T^uSq`mx@Uz%u2OFLW713d2zfo8oI^1 z9lTLO1M9R!V7w79iGfoRB!$NkchZo%q^Ze5UgYvk47AVoyXI;oyPtz=G< zl^Jct9AHIz8jbwkpNQt=5P6wck`hm=8UzHN?SbrQ$VHY<|ha#H4>ylIz=P#*eT1g#}-jLTXG@1=) zj8*3m=p=LqdHsSo#Y7%~A;Pioq66!>MPQ5(K*(!`vb<&}%S(tM%Zo&nesyUpswYyG z*Q~xM_=^yB%4M5Qvp=&qz8FOH)6BT>BF z;C0fv!Y*zq4@Ex5FFaL}1VdgTDh&bx)3jQOdf@(gGUO$q(h0K?ZFsjqLx#MD53+a< zgNK3J7@>jn*&;CB2z(OWlGj^>s0kC5>`BzkyuZKQp9l|;1a#}|h)NDogq`n8M28W) z?Jo-YyuIy!c*dfxeLn3nh7(OB9`Gh?sGs#uQlK}|SB0oM|Ms?rVtkGKzR#U#w-jIF zbJCsGm^KjAiqzBT01kXaB5lpK&eyK`a~|D^jq1S1*Vi>GS=8FU=G8kDRhJHjJvtKf z#^MU~Q;L2+!j;;haJSpMC~>>puAmVzq{!=YUphZTEDfJy>c?w$;ynj zqK@Zb2bfhO5r7u}vgj~^wHzZlE@HP9xke$aC4zwB?#rx9Oc7`r5D{eW0&=q1ys)Xd za~#O3EyBILB=Yh@Y+@QrLcDarfGCjVO5zh6QC3pT6B`MTaO71yi;ugB0(pHG5ExIm zdUT@lDqmi}_VvVU063X|d@w4h5RD@~Cb}qO7Y+!BGB~l(aj;e;4S7j!)d{nbjWmQ~ zhP*hrx-qljoe5rpE{zcySf4EdE?$w?`fx_T)|wp7nDsfD z7j!)0ZN(Gn63Ppr3?h(80uOwATOs5%Jn@XW)P;CL4I%M#T50goUSv3>NKaWIy0sXf zm_e;%E|QfQZN<38Fu%hS8|Qh6Xm=vAoQL2n<|PjBRzNf+rU*0*NPXnB0M`s?3n)V5 zB~z6gwpp8)1~VWp91tHX@qv*SASbh$$cr!lrZEVkSxFI?dw%c40#En(*Wj(n%=eQA}Zr02i9|oz$hb-=vIcj zwn>c4i0&k2UNEmnymTU}wrMY~ZG922pv(4_;t{oe+-4+!Ok!O`O}31j1ZRh;zb(KU z4NyFzes&@5P(!=9j?!T_SICP}faVG|))xuM%nQoJZu0@sE!ZXgQw&~Px&@yZ;Z=Tz z$crs-i=|tNc%wXoyx?A5k_j}$J|$xC)^CS6N8muU2SLml;F5U&;6OOh?NV>o;UsUK|^Z8>5Yq z8d#?-0;7#UqFWjAS|%}LN+f1pFt13wbRw#@33)B+i<)v=E*+f1SpS(7CpK~>5Rlh0 z&hGO&>iiqp5`TbV%T4L)S0Byx_3qdLb=_FxKgHn9x)Af9b+j>wir?nS(w(WBkwXzp)pbd#YPnk?ehmU*$cz8XhM0WCdlVvHMB@KBA3Rxdy)h{_3m?S9BHCS%fCK z2#576+&z%DvUOn*olo2kPQ)zA>{QZr$CnFH5v|f zc7jU z|3pyyEL&G6W&;i=9H4kchck8|9(EugRbq*z3X68CSCoV1^A)0)={tA33`Y&@0!MQ^;jFp)=~en8+K0zR>kXF~;VlI83r z3IfRqAI;)foNAgVkk@wsf$@Z^NAod?!v(glCy)cc2_542!-U5@P7$6*5mfB)U}^@2|#JC*%bVC>)@8Mu#(YAs)6MAXTCQFT|`CGKa^3EG#1A zwJ2~3%NbfAzv7&TkM91+pm0nr<}{^}zjQ$V&#rvb;)) z7|cK*ime{0?%@L&;0bgMMpH_6VB*CwH^Xb{7RVvchz8|D>`iFs6rZs~wH zF~J3Ixb&dyb`!IN$~%eqJ(rte{(S9lFDIj*!Zk5LSGT9mOT1L{9?nDlv$RI*rhaT2H9&vCbiEimA zIg!%*=fZ`c#O5cTe*nl$vsziXiHZy>{DnyRF0>znuiNkD$ zkk=4J{!9#jkcK2A z%1wl3ZW|$(<<+*I;?$?Uj91k_c zz`ldxOSv^+VZFXC`MCphhJjnxt;7{A@1Eud9Qe~4N#tkJ6~{O}=Oqh!h0aGXQA7#` zL=OkmT7r%8Xy62@Tf1fXQ-0K@lW5>Hx1nXKtYlGwS2uCqHXu6k^Z68rGbrk%#DqB& z@^YKpt1K@Q1qkamAQGcJKil^89IUw?tx|3;0_64QpPT|&me*)F*l5Xtb=)E_$_OO7 z6%5CA&gJ&?CF(h&o$4#ZvR1OrV7ql{D1BcV6=KeN$o7a}14Ts-ND$k?ha&xmGzZoV zaWOy@((;l~JnT0)bRpZ#bN4EAuKH30#A)LZ*=zc)Q|FPms3lUAUR6mi7SB#M5;rf& zD~u{}txE2<+w}{Z;ydScvy5Vx(Yxs+P2g2^*WOaLexIT`?9#pFLZOe{>uX(cv)1Hf zN1~X!yU=5CR+dZ)Eax*e*LKeG<%LfzixNv!(iOftae>~bjYyQ^MLl!5oNJLL-%!+a zYd#l{#5M3D$w|>_o6gfC95Ia9gYG9qV z2#huY2zjlNn31Sek=#{E1jn$L7b)!Xs--Akj)zt&C?e#w>P=)ch8#DkU;THH*vWQP z;FJsvY_}?&9_EOE97iR(Q015Jd@eZq2Sf#(=$ZAA*Gg3<g^JPIY?7E*ikmB| zWFf~~6bpJJv4l>gg_3YyImil8T{RHXIFv@kUS44yUw*o2A4+n-lX^JQH&M*38w4cI zy0iNTm$=;3%_0~Z%QPBrp!I7PJKU62nppPMK` z)ixk-;1FX0n1Ccy@~07v&xx{zvIsOoUV7v#%S*+?ke5J5L!?Hci-V=QqgV zDR@Z+xwhM7#<~iV#1z`XaNK4gqT~S4ctjaG-}w$i#jIK)FIoYY=vLxgL{%2E1-KOQ zqLM3xydFs`0aUV9i)5C@3M`)5v=l3l7nt!G@)EP^9&sNSjw#v_ssTB{h(^Am^R1+a zN`ruOFmP>Qj(8dG+A~E|08f6YNa#|OzzG?L2_|WVyz;2rJ|J$K_+`k8$AdgWUV_sY z^7`R`jFK8yr!4}bjesF9j>y__Fo|fK|DTZ;!4$Wb4ZINYnjz#h8c~MMkthX-ZZ57= zm#UE0dhEYaYm2-(>8FT= zcOSZ;n8lUd+YTH|{W4Mf^x6g_r!(GmFaU~*5>%BmxT)C zc${}18l#Pt99YLK0;7z8AumH-1j>9q*p1=X{UNVxrdOKsHHqo0rjIO^P%BxhX);Tr z3$X%um7`TyZlhr_t2Cg|TR;qX@lcQEr>Opp+Ox{GFo#Dnc=k*Y6->8sWRp0Uq6AJr zC2vf4tV>x0XovvJ`RmVLXf#1dSlvVwy-q?0wy*#7%TN-Jz-TbqD5-&U+9EL82q5G& zOJbQybe^HTys%i-O4fOn%+hEgiu@(dsU)HaB1c%koAoA2cHw-L*i$X?g^fPTYYyfO zB$Z(dmQQ~YQ5piss^29Ux|=6-!5lN4kyV}L1xHT4CNUjDUM1M_Ru^;&KGTqwnC!yC z+HNfx^5Uap$cx8gZp`Y31Crs9V7l2n6xB4ps-z*WJbo0V8wW%WoFOk2lTMhGYQ_B+ z>WhZFMoPDMw}UrYa9|y`2#hlVa%XktA5G*D7(qZBNZIXpxBcGvJ&A_{Xh?~!mlfjc zE9-nH`TFYa`9&N|i*!CB6gTU}Y2fBr$q?_?*Lg0|P|&qm%!>mweIZ&fyk65zYqKehNVxpDaEk@a9cO88AIDk( zx13I%!w9fX{i~y|yAiY3j3x_Rw<1UJ&ZC$DakGG8{6k208W4TdCJh|eu8jtc15rP4 z(@7k+bvba;rxG%|yu69u;`&ej1K=MkbUuQKJV>uFAaj0TM5rRi$c-2cTpFXopg*oD zKVfj?|KcX)z`1dY0~d~JEItdU!>gOv&54agT=UyB{i~R%&rg25+=#i$&D;N?^>Kxm zZvO8ud9jL{>6=(apx-E!EH8Zy z5$HGq?7u3_8FvJhsQdRPF3@ZKS21jqHh*EyjAGhFt=Na&{m1@G63gV1*|f9^MnaSC zX)ytJ|4}DxCWpBlt%Db&QHA@#=AmSUjVc#^^r{T-+KxAGpagYsdy}#fJlq z5ftMXv3N#N9{Aw@;<7)kXySwaA~^|vY+(-m^e1ZwcFDv#qZ!AD#WRW-2T5ieto#lK zZXz;X{4`JuT=7&xUNQVX6p%DpnE|)2_fU&{&@kz#xC$SH>T7J)m8K=z;U$!u`qSgMm5`TQ-~ z-ypAqy5cnaj-uP)t7HLBW^V&G5rgL330yqYwoB>VGQ*yk1;d%~uPnbqaxzuZYBy{7 zQ#{VQ7Gx2XP6CpTSYg9~lRU)FFY&;kT%);(Vl>+Z1P)wAY=Y_jx-t|HOhaBGPA*D9 z8Uh0}xdYRIl+aZ!ta4di;F?T+^!@JkVK*H(LtY~MZU1D%^JaB)Zc$z4p(l#Jy;QoT#Mlk_HULsD8oCwK~m-^YbD4ED3 zfCEyJu4EbVGUU}UD6q#{1nwgO;y;zVYtsP{l}-Wz{;RTyB)aI`nTb3CodiUUQnrSX zAumBrivR58Jq%ta{j`R71ZHU9;u^;PIdIP~%WE_niStkdZ#10;1J@RLjpp4)J#g%= zDmWAL7YAp%7c8Fw7B!ylW*zR1)XP3`1Uq zyzYZX*jrcxI*x$&PbG7EivXC%&EV;b^?PLVI1#H(0)hrEZrjD?c;Y{Ozd>G|^i#yb zyARzE8aOlopP_*>`}IqWrfc0q9)TMN#1EAGkjTgRquIj! zkvre<{N5gK5wHlb2#EjIF}Jq}fO(uDFB$zI&P>t38S)Y%Hb^6}3yQ@{mZh8pMK@@?7KC96carzo*B05B($r$0Xdd3l237bV) zh}ZvXMz<7U`U$#K(3f=c&(wHp)X2;8EdBxM78Duoat=Y46QWyKM0D!}MMSqw&BS+p zU43<~y|)*|1IeXl0u)tWJc8;MEP{3}dnh8hwTB|2TYD2Zq^ci~4GP_cKP`Azlyu8M zkrfmr-SQ?_Ot4slE5A*?R4Vh! zRBTNdIEqS2ayaGrQ$iNi|VWw+_uj0i&+I`j3m^fev{c0JCZ!?m+bm77^XrA#sc7)*6Zk(X376kg9D!_|t;7 zh9bxd+P**$0V0(f;~j3KWW ziQJE4eWHurHRPp8ln|WT4X-N~#wF|J3*aqCx1eaqt7LXR z!|OQhDxS&liuW+`Vu@Upjv+4{&?d}E*Pb`2Zpe#KtJchlJuNs*o++V~Lm!5`I1#!r zo`PvwEk*u}IX0^ZNCI7@F;mGxm7oNDF+mAkGbp0mt(l1l znR&W3lRI=db%s)^W_r6JG(b?RLSD}(_3Ih6Zlb_mO#&hjjhk*uZHi{(UW02Gq1ZP4 zN%e_VP~PD)4SBKO+8xp@2*=n<7mgt>LtZ6^_$tlPGO>(>x9SH(R5bVgB4EhNkXHmf zhP+}V40&DJqNK6#I!L#mi0IZ7iimDaP2|X{A|NH{O4bx1FSu_u77^W=LJ`resfiqU zO%d{%BIq|$V>2~ws@)w+$Xx|T|-`yoP0^a`L`8O zk4(F(2>heAK_{WM_=QS-!Rw!YvfbKa2`K*k_wV1aK)V0^cQQ-k@4p4Fa_*PgT3udP zl%<@Xp6;BF;;Ini*V(C^U@ zOuV?(MNqC6AvpIcu|-o~y&~<}1@TNC8?e(#sXb z4~Q@5nJ+0PFB!|_3&&#X^^z`es-}}j)YyjdzSxP4q2T-lisgWl6-$~}MBv+igfH3W ze9GM5%%*yb<};YH_x!1n_GVKo>Vj1AVZ|wWcPP>h^g2paq9k*aGxq!>6UWGJvVA?I zF(&c|fHMN%!2ylLzw|`|2O!HIPy_}457QVc$s^E7ls*wofFdernnl1O&@2LoS=C2g zpQ!pL0$B}2hrArrNY_9Ae11Z@1;mdJK({Kz0%fI^h;cExReVt;MqF5OE5dE16D=(g zj`>j3#xWWwI(ow|VkroDo$8Cln=vSJsuWoz6&ETaO|lzO?U4+@OXUh>~u(ng8kzMM*T=YXYqk5s;J3=7q6~!V%;os9N;M zE2m?`f!wt~3mflH13(Yx2bOed0@I3Iu$0xxjeE-0dBKAFfPfbbM#RxL$CUYuApB2luc^9^}%Xk=_n z#N+?|fOHFr%_jC%+9F^PKoLO9%8-}iUL^8&!~vEB zc|qHk2N4nSQrSMPP+KwO2xZ0#IIwX64`+m8gA*GO2YVT{i0IZ!OVJ@Nc|p1bz{$(3 zCh~GP1?^OiMaXSTN~mLV^}lZJ@VL>Il=mTtjk zW~3{bhP+BfFeBPisfR!N2%aE5q zTjaK-Tl$Ig%$9TuR@dOgke7-{0}`d`HUnAps~hs-l&Jw<;uV8;Y^MFC=*d)Y&fRbMpZ#iNS2Vt>eMiL$)z zP!zPwyUVeND4}ciAR@w3vo6HMxiTZq3kq{_#7HW&2};wNJ&1_t)~uz7kQdmDYd)_W zk{VxBA+KkY`t^(mkBK5G4FaOTi<^-{5lz)C%S(i+EeC_ls->t0&XAXii6JjRiEa#t zL~p!1?k+*P1w}M{H_ako5oi_xLtciwBK)x|uNaBq4%4(+iZILT4(S$zcw?bku%C!- zO&>%=bZgp$n31kzPF2ncArrW7HWm@xnm&k#=+?BQh>#cB+k8GnXbX>#Drv||04s7E zFe_cTQFVcAXa{w=pr|N;6X3R8P*l$(p)DY~zh}0jTd=wwxWAr$eQNdj35uX#lht`v zokySnTSHl1zieS6r(vVvV32M>vDw7lN?Qah0w@B8ybO6o&|}CeM#7NSr4tGVQPNn% z2ccW=HM^i&@R^BoWriWIl2Liw)PUEOHN6$8w-gO|aiG*6iQ?@BuanjlHF6VlDDs(m z;X%3uMfFS)40(yD^tQ0kkeB#5Hy}}>rguB3Zpdq-bc=U8c(#2#q7tTA1UierKeQH( z4jy!yOqI*;oS3|5V37_}RF^wpQCm7dx>r6r8_6k&SySX<`w9Zvzs(WS{dXbAvNOiF`QO^7&IG?aiiG z)CH;J!-|wmr{5ilbZmewStSaB=8o9}yWdYHwtc|D zaNY0s4Cuw$m127HSn`vqmq0M8B+3qbe7t{t#P8$Xi;RGgLd*(mO0K)fOShsxR(ALzg|GTaO|l zzO?U4+@OXUh>~tOh_JdtQBo53nn3GB1mtA1d136La0EFCsun%+%IO&48Fwwv!p1uk z`KuT2-cD2?%f%CKU#~$HZSa*XQ`NI}rZBoyJd1O%D6(IOiF5vkPy}RxqJ;Of&&DX4 zVEcMPDQeA}!zs3~Q9!1)d21lbkXf}94S7i#)qq6FstrL_{pyCiINll!-QwL2o@IHB znVwkRM;L*}KwcMstbSLC8hKGkjl9kPd3~KV^7?xBVtQ(k#dQYQ=lu-0&HEXVlQW`Q z4UTa{bgN=vBWV7BkXMDMU+H=P@7_ezk9@x#pa}YYMafrKY`JY0tPc0`S|bj2ha!Sb zc4DL8Np@mmwy4pWc4A}6s2pBekNd>N$WZ9c02g;0PC+}>W3hlb?1o_uD-v0di97<6 zY>c7_ke4GdXsT|=t83&%QE$82Q4gGLU$0_f$V+gVaq}hdGTy!M8^LY6pg2OhU|qHd z3@!qOybAaeZjOVcD}8fYkQb*=&g&22Mb4Go4S5NU)#ILHD$C1|m!eZm)&}az zjg}VPwz_3`adLHIX2m-byaru@bPI}uV+YnDi@*pYV8|bYFBlaA6K!#Yvz(L2DRi417`#sUX~>IHrUrb8S8R-@x~LB678FNF7p%(` zfx$(uSq?TpaI;r%d)&gq&vZV z0@=`^fpkF;&EE}qiBK8x5gsg5V@;7>LYIVMYFfOMu3n`SYaFxHj8syI*X;Xb`V$)kp$W%0))UeG^VgAxmlTE4 zxL^_Clm?oL<@I{GAQ2to_=ffeJ04I<)%ih06#6q1-@tVS+FJwhq_4vPX$N@8!x>Mf z<8k{SqK*>1`w?7i#l5^77~5r60%bY3DL~n3!Oo;}jz6(6)ky}O*qH2O+?hxLS})Se z6~+&UY7aN}OmNiZa;eUCT++o46I?loM8R>$B{IfNYzzhGFHmIT9!r{7MBv+igfH3W ze9GM5%%*yb<}(N|pFcI$oMKTIoK2q}RwT5j-yMo{Y=ACVB?K8 zG8oRUFCvh2sV$z+CTzz&qFaw5B4)J*;;{>H13=bpBN5AfzuRpx#FEU7q+1RmtnN^h zl*GLz&^i$TIiV998M`PPK~93IMUT94I!1WLT?>>Gb%!E<_2S*zi3()7c)kE0GKXTm zGn<~ZGlkKuq5;mqqR4(BCeHaELJ^P&iW1%uKijrGnqd2SLMdvODTh-m%S%9}AJ6it zU{(-h)fWwU8S*Omk#Vpx7T)S4AdqfBafEcix@-{`Tm&8id0i0l!eSy-Mar6^T=>Ms z5_u({^>rrXMHPM*NoW3H?(2oDyq_bi$?s>tZQjm^Zuw&zWoaxTx^?J8%wHwg z*8_O>CZc}i`}F`t84Ty6^~tx&zCsQ)yokXOVd*?m?M zc{!YdcB;o>0d?37!yHy5vLF+A1SZ*lMH3(|$GapX@cySjKF)oDA}HA7Nr+=T|G8=oSD>ZRr+-V~}n^5%dVtEfnM{Vpf;h;trKGz34L)YT1nB}~*5I@QNhU6f4S4#p6W7fa--bcjY^x&=8HCpN(| zrtMUCwBLybcI?9Z;6n9%Xs$0nB;d18(z<*2nGl_30KYX~>Jyu-hW9PWFmN zD=2T=5Fk-+OCat#5i_Wj%w@7N1MFl&5@J@f2N4nSnsp(fEU!E-D9phtK?(X|f>6@z zK}3YSW-UcTgu!N9emgESAhk4_{zK$t+t&+_W!u*abcCsqDCFJIlr-eUfl?j?%O`$@01}iRulG0lGDAkdUkx29c)2zgDjT$6;n;J(>dM09KVAR?k$ZI5w8$P4XlKDQGa`@mD84ez$)U*aBwi+X7&_gy zB&1tVY&NmC(iQ=W0E&P|UX1#XkLM(Q0k5xiRCC0D+ z*PWjKJM9u54HBbS-dl>a<9zw7XhE^~vgmpAVwL=^PHc2vqJ@oUUE@1G#*toA9nN^I zFABKh-OGr@%WbbCX#VX02+#3=QmW2S1kb#jA4CM9dpoxjPx?9>kOq!+BuO65csd=A zTPW&hQpx)hkKo#Z*zL7Q=VWfxS(&@tR*Md=Wou5UNO9%drpOIjE!dfK&haNU#wz8V zPHZflXss8~%I@P8DL}gOKOm|T3fwcnQJc%9I@{5$80LowuEHW>ur898Ke7`WL&5nA z6xq1Pk|q`r_%d>mpjiYuKwck+ zZk3`x#jpTE|NO&&k&}NDw^4K}iJ^Qr5%I&7%rEyP6yK?2(NcNCB6Y7UQdsX$EZ)Jt zPxv5=MSFS4>z%~>3S8@okQb&~h(tlUbwbDsi|Cmr6L|y>$hy=PPkVp_j(bG6u!!i^ zzNLt$(SC5^5p}7pxB)a~w^86_zuRqc#uDv~q+5=Qte`0AmM=LLF~dE{TqP?rx)1?5 z*=$}IyC@t%PJ*gMkGyg^MtH_u3$(Ct!3!28O-vE+p;I8ssV^v135xm7Y|JS%yPuG5L9yAy-bz~pECMJ3!$V#!XG|flBnAl86nR|`^1>nkD}}r+ zNId5lE8?%S+o_&*!7sAeJpFq2VhO?m@;W2rbw+7kA+bkbi8G>G4UTc-Y$ted3-?DB zcZe@xQ4EiBPdkQF0y=4OVk2sO-BJYod_!j@-d5b9F0~dTyvpzR7)L*~mmeT6X!`=i zj0+`G<;J*gOib|!21Z^BaLs_WfZ`m{trsXJwi68h3wSXxMIZ!3{$V&D(k%c^US>6s zm%}OWa7HK=P>0Z9;(!a%EhzSeybhjn$P^VrUYy!> zf#xvXLeOakP?B4x1um53wT2>U$dDJ0bQ2DiqpoNh@)AGM8&f#6ef>*qx@E{K(W#K4 z)(Nj`iM&`MSEWOQ!;lvTQS<&Fo*^$z*xIIP@uvl^!82RZEm&RBo#$smUMePryf|JO zIbRYlYAr_0mEV`S6K$}Q4M`}WYX(I@k-R%Ikw*Xr z1R<|HFDT5#D?thRVuBL7W>74Fk+(eCnVFa(5CVdb7ubwzK1ZWbSF1u^&nV05#+$th zGUeTIcS#`{2Lqs}D1j4TLKhU(Gf8N`f*zzgWL7OjJ#dD+R7?zc2}*QhKqPuI1ZLH; zOOS3svDw7lN?Qah0w@B8ybO6ocGN|p)Tp1?Tb7q0FHYTB(=Glu@9i+%ikK_E*GFFX z$VJX}g4fCUhWFPHLS9pqb3)t%?wgH8M7Qwiiizz6!?T^Ki75ghAP9M(z0K!S#JBJm zsgeXUV3wC5F9Fy(VQUH{IT!#%MG2e$=@t~#Gf6PyC8E-H4I_kOdf@(g`t_;R=O-wF zf`i=b{r>ui7B>EZJJmz6C8DwFJOXVQAf#JRMALWEECLpRW)U#tWys5r*Yyg~oFOlP zj!^Q|TL+>cFA1|Ag_s%g5_~DS!59LCGvvjoQzy`gHxa!48WPNf4h^IWiU=tf@)DtH z3-KVcYANc0GvuXWGL|eaLtY$2jb{NSq+3vIHnF$T76FR@ihw`xkW-(}&wu?TR4OSH za!GV6?EDp%?m5w2ymZih7lr6MS{L{g)&}we4ZmDTxoFToQ;HwD92IFPszxdW-fdsZ zo!IDJo7eFom&ij48}W&aXtm?@GlHt@et}|EAeonBWyY9AwBD~r4ISh7b`ZeGxyJ+A zw(AT<)c(k&>W>APtb0gFJh2zV03;Z%Rfs~}sP z5cwWSK;*^nLC~z|@B8ma&>TqnAEJE6&rhljL@iNCEE3%k{fx*fKG_4lcaOZvHwXml zS|533RH`%)-8w6xlDt#5w;%C;~D;QNml|tEI#*FW|&RLMbZM$l(;r@)D5g$FsaDm=y$B^+iKo zhP+C4(}3HQl}gL1m$C2`4hW=MP;54_x6&2?ivWs%Coz)f^p)jR5F-wbF0M-gC4G1? zP@|G=k?5IH_$XeYN|f*T`AH>#s3j_i#IsB268-%0qDy`YH3IVTV%bxVye?;eye{XG zKNS|yh>$QNu*4bBtpgO*Gf9B!_=YAD*a@dq3c}Ny*luCtX~%F%KquQRLS7GjVq;`V z`5hmmn3srlCnf#VUVebQpzRA3ODt4cDmTV`Wnvj1Z(-zRCpHS`l-ywWUuq|ZGs1@C zABOWW@&e>!Rug$SoPu_$$6^6>*bT!RRwS|@6L|zCVBi4qa=c4I0`J3ZyPzo8kaybx zqWgQM0*e&owi|rufwS%FRZJ|)s}{Erg}xy?R+2~H4@kG5h^FtRSp+Ns%_3mPi{mUv zx1iV$@}fkiKzL$aO1wk51w}(%9GTT8X6=xdTc-ssLtdQTcEYS85Xu7+ySRz-YW5|nBDChMD@eFx!Zgyh| zXUL1gs3DRl-oxN^(oeK~y)A6yB+2&jYK8%3QrO83d`15#t=D>Zb4B!)3Urc19KkaV7(!)hd!|}$5PR5aJpiwlHc1_2V2=J zmxyl7peX5%^JgaV2;hJqF@W6cOQ>nV2HrVb=^HFR&Tce2zw= zu2zM-?8HWa!aCX0o&z89+!P*)Xr^xq8%3zv5;H?y{Abn)O5#lfZ-@;U^5S?4FBs%y zf;YsHFrf>IkAfuZJuL!VM!=AlA+N~9Y+<87VTQbTLRJ@tGrqOOQ#tC2cH3vxMqX2d zydLFrMUJq7*Y@2J@|vog6XGTl#7?k?=++d9iR}c#vz@7lDFPuN2zjBs&F9nlW28zF zG=OUu4S5;zN6L6m=D1w5|Lj*+k*H47J?8HWaxQ0l#L>Ikl z3mdzn8}@9AfQo=2FGF62ye!L0u&uXl=$2}QgtmApVy^t&_L+vfIL>MdviQ@!gJXug z1Yb&SFor}=rRK7q{E#2#O>?L#duVV;Mm{s`uZyNZLbm?Nh@lXC7sCjd;gnx zg(J6w_c9bw?H;udG1eeBv5~&_2R^cL&H)P>->=~`NB#*#xPAQvEo{6*qLtqtSQMFD z!M#GY{?U-=7{|8*AUwwd+RN(~KsD5A)} z9pssbz9sWu&zxz(cMV>!)C z^eksV1t?oB*qLI?13D8BZ|qr!Gn&kafW#pfRUCVgh?ax3CBX{t`BJ6W=rtK|kO2gAr&yMkfJ{H0&A3TcEcX7E5AOBCZQUUc7q2xGs!!MN!Z$?_NCeD%K-uR7RLKhSV#SN@87J-pQz>rtTqMVh{t&)3XVj^5UAumdF3WP_ds6e^}MTODo zL#Nx8969oG>$JcH=@t}Q(k)p1wsZ@&9ywKhZ_BLmXGOcgGts1e4e1sX!KfSEl0m_^ z5AEgkV#q5oF8|Byk8Y)QbGC;5IvOA^mdI7<81fPr%8@87btD?{;?SyXnihXr@ESa` zCEbG6CEa;`HsqyZV#rHSq8kGu(VHPKtCn4YbPI|jB@EVWi@=~FV94vgBCi9=@;aa_ zue~c-HWRYE_9)A1ukaKhudr;5Wekx6=@t~#Gj9vB>bL$l$ZLs^*Ag+SwjzQ~%iD?( zN#*yp`_Jdkigtr%f~{$wzzLtK=ydu7MNn|sfCW8B zb;zt*ihAG-d8wEf@)AVHkk=0f1kx=i4vHICXDk9EjesGql0`WyOX&I^Szd;`1WT*A zT+xu1Mp=fuB9|)e4e*qx>D{)~Ya_2ILSFryZ^$81@W$ObLS9pqb3)u?g4hWb5#5?X zF|nOsc(yY&F-0H*1R*c9xA}Zpe~eT~f*EiPqaiOtUI}zP3LI-mw_tUJcPPv2*QZvW zpP&c|-g;m62k8>p%j?&tATiGU1B#&F{~5>40#E@l-ytpfx;Q`;?${= zJS*Nr@cL^gL8WLooDl7FM~uO1Frf>I=~&_s0YuGmx{?)vb+p= z-Hcg@hP~T>m_fP)#X)fc>x@NUq!B1nnKJ8Nf8~qu92ra7SY-wjzp?_!MB*nDQHr#Ku&mG}epKPS%}?6d?UZKOm}2*xWP0xdY3kI@{5qWPX_7D$r%HE|T)T*l31= zbAEiMiLCVr` zelmIbQ!yVBhKPM0xt}P@>(7?p#j5iNG+x$?W z77@t0mdLB@<`td8*g)j@UBSDSk1wrEEIW4v$m{FMkyjvCSB|`*2=EpFF9oLISb>gl zM9Ax;Vb1kB8j-(Z@J=@s5y-k=5zv^^9x(xU3S`)>O}uL-BIxJaesJOub*Zhm$tX-T zcN+y>h`ela#uDv~q+1S^tnN=t1Sm6BnVrloL_kh98%{Y24#IgzrRb1X4#ymxao57c z6oG~TQ6S6J$J^JVRHb}pHa%-+3PWDSv-r51IIka&35qA)5??JPet7{WHWEQmsYVW` z=zK6LiSw5vjd8w}6j9NKGgdGw2(s#nhP)(78zSyAtt33$m@cT7ZyvZR9Y8=ye8}wax%} zUCwCx`mQqn+Wj~7RB&5_q1a;C7_e-7QN=W zHxcynZGBrYGNt_9;B>{jTD03%_YgpSB-WD|D&xg{XJQtih5>j<&DYqGfrhnB16y8S>)j$&lAb*@Jc4B5;EUB&?GghP?7ZFHFE2;z=f(C@`|vkxVL3i;&I+> z@JuwRUxTqUx+R0s7WWzQN;J&>2#1K~&|gOb!f?|=|7G&usRz1^@7w1zA z@D#5YytdU1d2#M^W9Y;?6TJSqWXQ{q*9}O4HE9vJiwJPGQ+S5Fcvv9vI-o4C1IqH+ zqb#pI%JSMPltjoYEO28PL*y9p;_L`AtCnJK$g9Er^Zc!Y*PuE;b8pMriU?1&J#w+- zGr?9i%cZ0{&YzjcBhWS=2zljsL18Xl2};lxlLV2nEU%e~DFPm0H3$eoUSKn>`5cW# zU9Adv^|ysP0z{9}y5cr&UJpey{nsU_YFq51T7RHt8uH>lv;O!JZ#Q^1ZrzZVA+Nh& z5B3rkfg41CvmHZT8Qm&FlZoXTMnhhL&$Z1lLolZ$vuau0ke7gAoxrh{c&dx)wUJkU zM;UUm6udiZ9U-r&$~hr!GC}MFi;3-I$ZKk1ihxI04FZCY7uwr=KCM4Ssw4ponAG$r z(CN0oP4Oi+*@q%x0=nb_;VBiRA9(k^kk>CXnl~mq)+HW+CxpCyaS9Z?aj*D$kT<%) zoZp(X2t05EO0JVxmgSWNgG@tSKTNm8Jn}OQdGVjwLsK|IUYv%Fo9sO>d$3ox z2;3k7ob41|L|!SS$r+y?{#&Mz2rjf!{YQNfJn`1j@46j${{ZJO)|alRC=xGl`+9uf zBjQVsTGD5d$$*e}MJZ;N`sYQWV;tWO0vI{>2+>?Pp+NMZ6A^Uj?c7on{q}DC&Yw{0 z|DU}x(Q;Hrx^>@GL)v&w&qEG%PXF)!iZ>)7p@`Vdh9ben4puLt*cM1^InOG69Y|2z zqnc1e&YyO2cSVYKK#Z&9$+NzEzh6JIh&Ci$OV=pgYLVW|UdvatX{*e&79D}h6ZsO6 z%Mncp$`&)WC!Gu2j*aP(s<50@-O2Z5zOE=b~$( zq=iJ#!0Pv-ku6`($=ZD`-H(vhPfmd>%WE(k>;`jwYtkHeVh8#{UKfPCu!!)|CGTgf zaQX8mY`Pc)cMf8%1W_nn)EZrWUF17FUP47F0%(Pp)f*HsiPA`cwsAyjc^xFoaq-=W zv^<%9qPr9k$U0*Y(3ry(F#(u47M5!hu5==Tex9~>C+<;~+KMYcV>T-VUWmM`a>f$N ztAs|f!XhiDqKV0hP^fPRASbI8ryL~*;XLH3=#W*kmYqo$P0^rIlX7E7Jj76 zvhyE+yn-n1L84G7JOk}Zk<+bXxT6#S@;UE8#i=F= zs~Q-BUTmDqz)=8g21SUx?r|r?#GpdK|Nic=pYjjG#Ta=3a#GYpUJj=qVrHUnr4vKI zzyajtNQ{I8KGuAv5#h1RxdXox@*BeFg%&O%*(WHK<$I|GQ@}vRXQrD7zv%G54Ekj-vHuJyX zPKX)$>u7+ySR&Wez>t?fTNHLew{$bqkRdMtop>0c{`R0W77_9aFSxOcAqot6ainvP$g7%v zR;4-bc8K~D5%TI}AIAC(Av`s|tq7K~na>fTDNN)Jv^4}}dF6RQVJ?0Zl%Ow0)gPZP zd16zTm>g(o2tr<9F|O$pjYeJV8uEI;wc-(Uy76Nw!sDj!P((9*U6U^D4-lz-4!(6m zUi@d)2}B5|b;8}Ow&yxfpi`fo*35|u6th3+mlgs88}i~mv!|wThP>_{Wbqz`2m`l1Ci&KeIbaS%2NLGW3mAEY8Vm;gla`Xt zWVBHIdwnsxQ&!Bom*juY0`v{Ubff=OK<&V5z$=VRB)E+u63=kQM!ex;gje}_*fkM! z!dm{!uIy_8F?=EKQGaJiw2k9wC*Y0?_Yk9n9STJAors`Ir(;V|^xKE^JAXi}LvfF4 zLJ@8Fc-kEFu><06qxW{CT(FhCdcR*kvxqh%UCT8GDc)+4-ppRhSG8%Y%(WIBmdi8o z5~j-uPYKEvGqxw43*3&4>5{6joK@vidN7d;(oggaqFS!a-4iUrHlM4#9bNV$-(heO z5-P!ak(BqvMl)2L^6fiKY;8!ekbO?aEDS+`68Uhl<%`PMD~hp++!bWVxVA{!2JqXt z=$a@QAQ3dM`u%8R%hz+VMtcNE!NoTg5%T(3pRrg??f@oHyiSNZsTn8Yb~Xphfj%6_ zQBo=+nZF^6Z|F*7ka(9jSCvfTA}`FfrW6{37)Ha%-+3PWDyz4*ABIIVBU2>A)$ z9<8~2J)snpf8=nAWqAq6)OH;+;4)-ZEk#3Kl5pLaZprR=;AY5+^QcZp6mP`42!|K@_c#C=?3IeHi%{S44ok&_0ZBb;UE> zoDqu0kT7!;2SQ%ozwXxM^8NQbpCMnmLlNRBECQ5t*f5+D(8+p*KvsX-ID&rKj*XFO z#j|Gs>O*iGAF7NOrPSde^S$dDIjM_mvzOt*NuA;N2UiJ89- z`=eW^pG!ktk!ywXl82>;CiP1+nhju%UCtfA)*13j=;;gSl=_^w-eCVfGAur{LAumCRZVZS-Z|;Fv zwd_)wiCe)OFb8^YAYrb&V92Wm^4g(Ax}dn#HP7d*dI^yb1riV8T{f06M1dhMj&$x3 zd5QU^4{iB+LEQ$ue0{+&PKZntt~4+NA+J0yD9pvLf)ez_NcCy&-M>X{9mqTXNQq%)y$V+)*$V(6% zLtfu(NDp&-Yr-5b2a*Fh=}KioUWU9Pk+T;YIa<52jicZ=F5GO=q%tcQ@)A}J8hH(S zdqhE|e0bw0Lda{Za!!bwj1W7)V)obPik{evO-v3nH3T6qw6^(lTz`yIO`;qysp-Zn z=nnBz7hO`&34$y|2%?}ae|Bco(vX&P3tk?_syhGv^`i=^(mi6Is&`fCS^A0A^7hP;#~_lVI9d2y0;k06WpFhsbYJP#SgEB5n*)pAzrUfH`W41TO6^@B&v3^^ zyy0VnSNVC!H4$>UrsdD<%Dx^D!x!={2PwE1if9kY(@wx07w+NuVDO%uh@eZSLrbx? zOZbUux4s88k5GggK5h>B*a4BYag+<^?P!g!mA-nvUq7>mHY8mu>vp@X7U|9GwR}~Z zw#r;<(b2eky|qN=a>7%Bvc-(;N#_E$V`I9cDlBJJIh7tvB!l!5eS@eLVRQEcTW!wg zYH!Cm9ejtug_B6+5XnW9_r*ptRGjkdJ56kDNU)H7PRA?^L4gwaaI)o#%GoQ5v5DLj zWXQO-NZVra+qvkPC|H}@W)rM_KN{Kc^^$oF1$q6f&seM`cL0+pUMEDI)J!vRE0_c3 zKo1T8DDQo7XV;s&EfPZqwU{eZ_LmFo z{enQ&6%j07UmH)o)vt+3ltv1t6GUEn33FWBlXyOZhOUYFiKymHMYt4LSVc|r3yXlp z9JYuFK;(t*DH`@++nS)CCj^LYEACO3+KL)v?TBtE@IvHel{1!DUL`b=6&6`J6-`W5 zghG8o06AH$IOQlg2ej+$O!of-yW^Gd_AEQm1^X0ip~eKEH8OQR4fQa`w^$m@vm zx9W=L^HJd`LK;Xs9zE+gisRmvuRou`s$bVc1alyCx~&ND6czzWI&2tD3Fu_KLeJjc zHjbd5ryFnM7@1Oj#v3W-HDbAwnjQkk55aMKs4`xhYND{Jfg$L{#>osE1<+ zfKMp+!8WmSBNvYTG@F#Qq;zg7zqh{tZI_b z;5|OOoICJKAun>dt*8gimakWySe92srhMtC@s#l5TMS7vaVwYu=0Fb)81mvI?Ea9~ zAz&Z*!jK^^&W^f(W0-F7c0+{M@)9$DANEJLP(N=>w;*tf@G3ud09k#t;RDyg1UiN92`euUWyCuNT~F&^tC39OHz@MBz#ULlE-H^MXR!$yHE- zz8GbHeXi( z6|cR+7;j#ksV1jYA-Y4cZI)M>RAvQ3UXO&lZoI#tKubOxc9#(H8fULpCFC_i-~@}= zU!N;_Vly@|IndM)guKw&=F@TgF;X>&UBKk48?&H0#8X{#NueZ10HBCKfv)+OBuW|5 z)noRByndk3yhrTw%>6{j>j%UsCUOVv5svjS$+tGl0dpWaV93jmSENyO=@#T*hP(vI zIc+v+QkfMDc?q8eo#i!bzI2Dg%#c_5Z$(oQl`ag0?k+dv#VOMRQaA;39AkhYf)Bc8 zS1d?%=`kBJF@_iZO4Z!&WXzxu$zXCqGQKtQI51qxX_$Fc>>Q@*WDYf@@ zJRc&b%FlSi$Gk=?-(yXb`i1%l>tXQ}pEsUeHMK_oX_o`KL46TzH+k9#xZ}b-%p9xV zP}`cIOQ*v?M6`|Lv9`G19Q3gRB5mU+7tY(!(qAim^?v{Q%p%&5bfK)%f%9uEoN*_Iv2Pd8v)r{&K_SR@}(d18$`7To4Y62 zYI8nUdppkQ;5!U1oJ1mrNG_thFE*N?;*@XSX<}5QmR zejSgGH1R(Ol_Y@8Lux(Ay&l93Xw-xuOOKn9BvUWtb6k{RsvdS4t zEUy40SuafF4zx4`kdxJlQ;w2@a2|41bjT}YJ*7{dq{N7sV>a$EF`4v*Aqr$U>+tgR z8e|d5%3f2|vv#I1TYo4yRa_mw-%d*R%&NLuS=d zG~^`-*Ny3x?2ZR+hP*hrx-qljoe5EcF2#wsoy`GrpbrQ7LSAQtysn4$+#hQyvDKOpjI^=2Y`U9rAsFE&b= zbl5PQ641$d1%~+C-!_i2lP!Bc?q5bbGPb?Fe3|#041fDuowX7k8TMse0XEJ z1zb9H!mJ_?%g+rOLX-L>8^$td0CVhe?f|yVkXHgI~O zj`c9dwB!?DFWa$E(5<$HAmlZUi6-ex&;+iV zjm5-vGUPQjF*)E7Rs%y2@k@b17eZb?I0XvPuvyTPJW-m7TfrPK2YPV8ke4AZ zLtez|VD46ZQPLfX4S7jZ40(MsAQHFpVO!{Q+j|=F`fC!EE)0dHAgli6hP*h6>W@V6 zc0<%j>zu<-z5 z13ucrmnFgFwArQTjSt(3l0vz7e^YUXT1PuJ!c8QPs3sKmn}a@fK%{LP<-&P8S`%!g zuio!ppIJm3k}i~WyWLib{w3{&d{vva%3RNsxL&Vj`HE~Mkz26c6-!XIn6W+STtGWE zqSq>yGeGtRE(&JlLbV6wcZhODeSc4|)#iMz_I8}p!FL#3IEh3Jb13hNjb^AQHYhZ) zfgxk$=Qvo%KBq`5P$C~rwtP`Jdqpuek-LHnA+*RVxd@81EvBw{P2^O~Z#w{1zaNck z`FaVn`U6?AWf$Yt5%T)UDUfA(4TgiInYb0q0dt@S2f~(z{PX<5V;^1FT@i4fGoo8f z#DBA+yg%M@5j6hZNlnD9&RvLr@LT}-AWVU3_9ucqb%SX7Y6xa|)n0GyjKIrbinTsS9zB{A?>~khLSar5Fp5msQSKVtEB1$$DWTcc7&qfSjyW6E;;Z z1I|OPiVk@>CX*dLNr@3N`qISAAy>R^hyq!zD&DbCgDhI$E4`jNndn(NQ`oqd_u}Jj zqCj3>88SkC!na3jE?-Y5MI{iC0P?}8CeB}yG{(hNQ@Enn@*vCyuIqE#5MuQe1p28v@Ganr`45tKivR)zU`rF14^z(G%Z5$(0%FhjU zSIjSq<+hjm`M&(%Y$rr`+d-?z9cbVZda-db14jY085H62^)gk-54MRF_xi#R`G?_R zjJz_s6^lhp&z|kMw5+mWm8`CZR%MMXnJQXoler|hDG^t;L!CW_hId(aB z09$9sD^Vx^BfLinhyFSmATO54bu}>LB`6k!Z9$g4hv`Szer6-I!VN&V;BzmwK4vTNCDhIglJM#b za)eU`O}Aj@aY7e*_6;91AKCKt(v-(rz8V_oUvE}QPBYnPrX<1$s#B!wv1X=t6 zLxdmGkQb*+4fqnjurZ$M;_@^Tw}Lrf4)oxFAumH-5q~vE5<}PrQlmkOhP(u{!purk z@?nFTd3shX&toA!_b0N&oMRw3jt_^uJ>nfc5kg+$m}rvDgi=Nbr(m(-IKJeG&Dg}` zKvP2y@U@)EA}2eL%NKE$mX^6G&^@g_oK$SYw3E0_aA zb>NeT&xboQ^7AJTGF=z{)zbSr6#t-_P<%tJzoE<9itM4w2Ht|GUq$?G<$`V);S)cD znrFCt{gGc6qQQs=I-$;=kErIUwg@i|djyDk>_zNDEF!|Q6I%|ra1SvWzGwZ`>(@m6 z+}~8(q1MrkjbLj?GE~=eqO+PEhr@nz(8mslv}2=OIB!QlY^AT>Z(naH?ojJ}ipr(k zZo8->`j@m9@>OlxDsw$k;(EQBDe>W$<1k<$bZy3>C!&g(fyIWQ_bA2MgKf6o~~& zTnN z*U#P%jWxs_Xv@Kd3J$E_=D;u=K*;NapwkIKr%psq-~@&1q@gdKh*PE$4@1<2hzQR` zAsWXBAoA)@tj}25)r84cy@_9}wi4mhR&?t$% zXe(-JwA*f5iU7O-khR-wR*JC@d0FL*C6-qJlB^ddatB%(0?5f~HDOcrGT=Pqs_2kc ziF)G6Cn-r5WMZ_ZJYP3Nfh<=QFJF(clJb?=^sJpJY~0Ix@o_g%Ag`|s86iLMlKANr z;;I0auP2nE5{O6u`CwGjvb+R&YG4R(88WMuq9HE{v-$&BvgPX-&5+mkLMMK=5VZwa z_3t@MY+$`M2ZrhZLSB1>y!HqXbs{3<)k#BNJWj+f z^q+E8P(oLO#kg8tjym5PiR!H(h^Md!P|{(;a7sWY>lL!Dzik{rKTmiYM})kVU5Jq> z<>$6rF6Q@&t(8vA8ZpV?)8Ns z@(;ts7J)8j7Ta!H^5W*WwG4$ax|%5qS0&sbL?{N0JhGMSE5e-M|h7E4*hjBKwd17>uO-g zOQ0;8EU8^U8!WqOH!)_X$N#BR0j-saqv~LwVYhFMqb$f>@YX=N@8S;wwt3l=%;x@gJsNNbfvK<@6&v_`w>j$SmAsTl2(~~^WtpR|wZw@?{ z1BSc|c^UGemroJIGvsB+tJ=y;a2%ssT^M4>>#s>vx-b-)f~@+N8}i~Psy`CN+YM1C ztt&)h$V*w$yXvoWuS3*rj%#sxyBE;vy4feF=z^xrf$SbX&MGJY+|Nh}5 zh_p{vRU98-)vtg4`SeHlwiJ2Q`r;c}N$ic^Eksy^mp2ggTZj#m3#tpnGpc!x^eI21 znrFCtJ>Kv!zgH~xR}-Nm)cU!B2=CcSJRQL0r~Mhc2Z**9I6-kIfDwiJ9mHr5xOF0e z2Rfk@+5QzFW+xpZz%3i>wSvKrQL43 zs3ZEfvlrhOo$H7AOIYnZD z68Uhl<%`PMD~j>jm+88A_JdPeEsV zXv|8ua*u}G8UR@P=D>3~fRNXb0FgpoM})jwO*%O8D#d`M;%8ei-X}jF^B|Uw2qg_h zj7TawpO2EpIBWrtSAU}Hl8g7P#3Q==h_Fuw;^~O6&k=za$XE^tyc{-Q`oDuaHufhX z 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 z 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�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