From 7d29808dd276e4dff65eac4f839d7c29db1182aa Mon Sep 17 00:00:00 2001 From: "Stromberg, Michael" Date: Tue, 28 Nov 2017 11:41:16 -0800 Subject: [PATCH 01/51] Feature/cacheutils import (#74) * CacheUtils is now compilable, but still some issues regarding hashsets remain * Added equality operators to IGene, ICompactId, IChromosome, and IInterval. * Exttran can now extract by transcript IDs. * Some minor ReSharper improvements to CacheUtils. * Removed some unused using statements. * Updated the transcript cache reader and writer to be more symmetrical. * Added the ability to extract transcripts via VCF line. * Update mini-cache files almost finished. * ReSharper updates * Added GFF creation to CacheUtils * Added ParseVepDirectory * CacheUtils has been imported and ReSharper updates have been made. * Fixed the ClinVar unit tests. * Major refactoring of cache utils * VEP 90 cache files are successfully being parsed. * All of VEP90 is being parsed in a performant way. * Refactored the abstract data type. * Minor refactoring of the AbstractData classes. * Updated the import node data structures. * DataDumperReader and RegexDecisionTree now have 100% code coverage. * Added gene output for intermediate cache files. Successfully parsed all 10 gene-related files in UGA tool. * ReSharper changes * Added gene symbol updates * Using the RefSeq gene replacement * Universal Gene Archive creation now works. * Updated the UGA routines to add the missing symbols for some older RefSeq genes. * Fixed an issue with the file type in the IntermediateIo header. UGA support looks good. Starting work on CreateCache. * Transcript cache creation is now working. Up to 12% unit test code coverage. * Added the CDS start and end not found flags to the transcript. Added a version to the CompactId class. * CompactId now has total native coverage. * Fixed some issues in the cache creator and streamlined the code for extract transcripts. * Removed using statement. * Added unit tests for ImportTranscript and ImportRegulatoryFeature. Kept startexonphase as an integer for additional transparency. Fixed an issue with BioType conversion. * Added support for RNA edits and updated the cache file format. * Updated the unit tests to handle the updated cache file format. * Added a new specific transcript case in the TranscriptMerger: NM_001202404 * Fixed the downloading logic. * Fixed a null reference error in GetUniqueData * CombineCacheDirectories now works without errors. * Fixed a bug that caused RefSeq transcripts to disappear. Fixed a bug in how start exon phase is calculated. Fixed a bug related to Translation not being null when it was supposed to be. * All unit tests are now working properly. * Fixed some ReSharper issues in CacheUtils. * Moved the Logger code to VariantAnnotation. Refactored the performance metrics class. * ReSharper improvements to VariantAnnotation.Interface and to the VCF libraries. * Fixed LE in PredictionScore * Fixed a bug that occurred when no transcripts occur on a chromosome. * Removed the IPluginAnnotator interface. * Fixed a bug that occurred when no transcripts were found in ExtractTranscripts. * Latest and greatest mini-cache files for end-to-end unit tests * Cleaned up some of the C# project files. --- CacheUtils/CacheUtils.cs | 20 +- CacheUtils/CacheUtils.csproj | 12 + CacheUtils/CacheUtils.dll.gene.json | 12 + .../CombineCacheDirectoriesMain.cs | 257 +++++++ .../CreateCache/CreateNirvanaDatabaseMain.cs | 177 +++++ .../ExtractTranscriptsMain.cs | 219 ++++++ .../Commands/ParseGenbank/ParseGenbankMain.cs | 112 +++ .../ParseVepCacheDirectoryMain.cs | 221 ++++++ .../RegulatoryRegionMerger.cs | 44 ++ .../TranscriptFilter.cs | 345 +++++++++ .../TranscriptIdFilter.cs | 30 + .../TranscriptMerger.cs | 57 ++ .../ParseVepCacheDirectory/VepCacheParser.cs | 127 ++++ .../VepRootDirectory.cs | 55 ++ .../RegulatoryGFF/CreateRegulatoryGffMain.cs | 87 +++ .../UniversalGeneArchive/FilePaths.cs | 24 + .../UniversalGeneArchive/GenbankFile.cs | 40 + .../UniversalGeneArchiveMain.cs | 229 ++++++ .../DataStructures/GenomeSymbolSource.cs | 18 + .../DataStructures/Import/IImportNode.cs | 9 + .../Import/ImportNodeExtensions.cs | 44 ++ .../Import/ListObjectKeyValueNode.cs | 13 + .../Import/ObjectKeyValueNode.cs | 14 + .../DataStructures/Import/ObjectValueNode.cs | 18 + .../Import/StringKeyValueNode.cs | 14 + .../DataStructures/Import/StringValueNode.cs | 8 + .../DataStructures/Mutable/MutableExon.cs | 41 ++ .../DataStructures/Mutable/MutableGene.cs | 81 +++ .../Mutable/MutableTranscript.cs | 114 +++ .../FauxRegex/RegexDecisionTree.cs | 103 +++ .../DataDumperImport/IO/DataDumperReader.cs | 124 ++++ CacheUtils/DataDumperImport/IO/EntryType.cs | 20 + .../DataDumperImport/Import/Attribute.cs | 127 ++++ .../DataDumperImport/Import/ImportExon.cs | 89 +++ .../DataDumperImport/Import/ImportGene.cs | 64 ++ .../DataDumperImport/Import/ImportIntron.cs | 91 +++ .../DataDumperImport/Import/ImportKeys.cs | 113 +++ .../DataDumperImport/Import/ImportMapper.cs | 71 ++ .../Import/ImportMapperPair.cs | 100 +++ .../Import/ImportMapperUnit.cs | 65 ++ .../Import/ImportPairGenomic.cs | 55 ++ .../Import/ImportPrediction.cs | 58 ++ .../ImportProteinFunctionPredictions.cs | 82 +++ .../Import/ImportRegulatoryFeature.cs | 98 +++ .../DataDumperImport/Import/ImportSeqEdits.cs | 71 ++ .../Import/ImportTranscript.cs | 277 +++++++ .../Import/ImportTranscriptMapper.cs | 62 ++ .../Import/ImportTranslation.cs | 89 +++ .../Import/ImportVariantEffectFeatureCache.cs | 116 +++ .../Utilities/MutableTranscriptComparer.cs | 104 +++ .../Utilities/TranscriptUtilities.cs | 62 ++ .../ExtractTranscriptMain.cs | 14 - CacheUtils/GFF/CreateGffMain.cs | 56 ++ CacheUtils/GFF/GffCreator.cs | 226 ++++++ CacheUtils/Genbank/GenbankEntry.cs | 29 + CacheUtils/Genbank/GenbankReader.cs | 148 ++++ CacheUtils/Genbank/GenbankState.cs | 17 + CacheUtils/Genes/Combiners/CombinerUtils.cs | 36 + CacheUtils/Genes/Combiners/HgncIdCombiner.cs | 57 ++ CacheUtils/Genes/Combiners/ICombiner.cs | 10 + .../Genes/Combiners/PartitionCombiner.cs | 85 +++ .../Genes/DataStores/AssemblyDataStore.cs | 65 ++ CacheUtils/Genes/DataStores/EnsemblGtf.cs | 38 + CacheUtils/Genes/DataStores/GeneInfoData.cs | 33 + CacheUtils/Genes/DataStores/GlobalCache.cs | 74 ++ CacheUtils/Genes/DataStores/Hgnc.cs | 138 ++++ .../Genes/DataStores/IUpdateHgncData.cs | 13 + CacheUtils/Genes/DataStores/RefSeqGff.cs | 56 ++ CacheUtils/Genes/DataStores/UpdateHgncData.cs | 21 + .../Genes/DataStructures/EnsemblGene.cs | 25 + CacheUtils/Genes/DataStructures/GeneInfo.cs | 14 + CacheUtils/Genes/DataStructures/HgncGene.cs | 30 + CacheUtils/Genes/DataStructures/IFlatGene.cs | 12 + CacheUtils/Genes/DataStructures/RefSeqGene.cs | 29 + CacheUtils/Genes/DataStructures/UgaGene.cs | 50 ++ CacheUtils/Genes/GeneFlattener.cs | 47 ++ CacheUtils/Genes/GeneMerger.cs | 117 +++ CacheUtils/Genes/GeneSymbolUpdater.cs | 90 +++ CacheUtils/Genes/GeneUtilities.cs | 49 ++ CacheUtils/Genes/HgncIdConsolidator.cs | 42 ++ CacheUtils/Genes/HgncIdUpdater.cs | 32 + CacheUtils/Genes/IO/AssemblyReader.cs | 35 + CacheUtils/Genes/IO/EnsemblGtfReader.cs | 108 +++ CacheUtils/Genes/IO/GeneInfoReader.cs | 104 +++ CacheUtils/Genes/IO/HgncReader.cs | 93 +++ CacheUtils/Genes/IO/RefSeqGffReader.cs | 132 ++++ CacheUtils/Genes/IO/UgaGeneReader.cs | 65 ++ CacheUtils/Genes/IO/UgaGeneWriter.cs | 25 + CacheUtils/Genes/UgaAssemblyCombiner.cs | 75 ++ .../Genes/Utilities/DictionaryUtilities.cs | 67 ++ CacheUtils/Helpers/BioTypeHelper.cs | 84 +++ CacheUtils/Helpers/GeneSymbolSourceHelper.cs | 32 + .../Helpers/RegulatoryRegionTypeHelper.cs | 34 + CacheUtils/Helpers/SequenceHelper.cs | 30 + CacheUtils/Helpers/TranscriptCacheHelper.cs | 22 + CacheUtils/IO/Caches/TranscriptCacheWriter.cs | 90 --- CacheUtils/IntermediateIO/CcdsReader.cs | 41 ++ CacheUtils/IntermediateIO/GenbankReader.cs | 91 +++ CacheUtils/IntermediateIO/GenbankWriter.cs | 38 + .../IntermediateIO/IntermediateIoCommon.cs | 26 + .../IntermediateIO/IntermediateIoHeader.cs | 49 ++ CacheUtils/IntermediateIO/LrgReader.cs | 51 ++ .../IntermediateIO/MutableTranscriptReader.cs | 232 ++++++ .../IntermediateIO/MutableTranscriptWriter.cs | 106 +++ CacheUtils/IntermediateIO/PredictionReader.cs | 78 ++ CacheUtils/IntermediateIO/PredictionWriter.cs | 34 + .../IntermediateIO/RegulatoryRegionReader.cs | 57 ++ .../IntermediateIO/RegulatoryRegionWriter.cs | 23 + CacheUtils/Logger/TranscriptMergerLogger.cs | 22 + CacheUtils/MiniCache/DataBundle.cs | 71 ++ CacheUtils/MiniCache/IStaging.cs | 9 + .../PredictionCache/PredictionCacheBuilder.cs | 169 +++++ .../PredictionCache/PredictionCacheStaging.cs | 33 + .../PredictionCache/PredictionCacheWriter.cs | 74 ++ .../PredictionCache/PredictionExtensions.cs | 39 + .../PredictionCache/PredictionUtilities.cs | 47 ++ CacheUtils/PredictionCache/RoundedEntry.cs | 25 + .../PredictionCache/RoundedEntryPrediction.cs | 21 + CacheUtils/Properties/launchSettings.json | 44 ++ .../Sequence/CompressedSequenceWriter.cs | 167 +++++ CacheUtils/Sequence/SequenceCompressor.cs | 61 ++ CacheUtils/Sequence/TwoBitSequence.cs | 30 + .../CanonicalTranscriptMarker.cs | 167 +++++ .../TranscriptCache/ChromosomeInterval.cs | 19 + .../TranscriptCache/Comparers/GeneComparer.cs | 39 + .../Comparers/IntervalComparer.cs | 18 + .../Comparers/RegulatoryRegionComparer.cs | 30 + .../Comparers/UgaGeneComparer.cs | 53 ++ CacheUtils/TranscriptCache/SortExtensions.cs | 15 + .../TranscriptCache/TranscriptCacheBuilder.cs | 75 ++ .../TranscriptCache/TranscriptCacheStaging.cs | 74 ++ .../TranscriptCacheUtilities.cs | 47 ++ .../TranscriptCache/TranscriptCacheWriter.cs | 112 +++ .../TranscriptConversionExtensions.cs | 33 + CacheUtils/Utilities/AccessionUtilities.cs | 36 + CacheUtils/Utilities/RemoteFile.cs | 66 ++ CacheUtils/Utilities/TaskExtensions.cs | 31 + CommandLine/Builders/ConsoleAppBuilder.cs | 4 +- CommandLine/Builders/IConsoleAppBuilder.cs | 1 + CommandLine/Builders/TopLevelAppBuilder.cs | 21 +- CommandLine/Builders/ValidationExtensions.cs | 95 ++- .../DefaultVersionProvider.cs | 2 +- CommonUtilities/ReferenceNameUtilities.cs | 30 + Compression/Algorithms/Zstandard.cs | 3 - .../InconsistantGenomeAssemblyException.cs | 14 - ErrorHandling/ExitCodes.cs | 26 +- Jasix/DataStructures/Utilities.cs | 2 +- Nirvana.sln.DotSettings | 1 + Nirvana/ConfigurationSettings.cs | 22 - Nirvana/Nirvana.cs | 160 ++-- Nirvana/PluginUtilities.cs | 13 +- Nirvana/ProviderUtilities.cs | 87 ++- Nirvana/ReadWriteUtilities.cs | 11 +- SAUtils/CreateGnomadTsv/GnomadTsvCreator.cs | 15 +- .../CreateIntermediateTsvs.cs | 8 +- .../CreateIntermediateTsvsMain.cs | 251 ++++--- SAUtils/CreateOmimTsv/HgncReader.cs | 178 ++--- SAUtils/DataStructures/ClinVarItem.cs | 2 +- SAUtils/DataStructures/CosmicItem.cs | 2 +- SAUtils/DataStructures/DbSnpItem.cs | 2 +- SAUtils/DataStructures/DgvItem.cs | 9 +- SAUtils/DataStructures/EvsItem.cs | 2 +- SAUtils/DataStructures/ExacItem.cs | 2 +- SAUtils/DataStructures/GnomadItem.cs | 3 +- SAUtils/DataStructures/OneKGenItem.cs | 2 +- SAUtils/DataStructures/SaHeader.cs | 2 +- SAUtils/DegenerateBaseUtilities.cs | 6 +- SAUtils/ExtractMiniSa/MiniSaExtractor.cs | 2 +- SAUtils/ExtractMiniXml/ExtractMiniXmlMain.cs | 100 ++- SAUtils/GeneScoresTsv/GeneScoreTsvCreator.cs | 2 +- .../ClinVar/ClinVarXmlReader.cs | 2 +- .../ClinVar/VariantAligner.cs | 22 +- .../CustomInterval/CustomIntervalParser.cs | 6 +- .../MitoMAP/MitoMapSvReader.cs | 2 +- .../MitoMAP/MitoMapVariantReader.cs | 8 +- .../InputFileParsers/OneKGen/OneKGenReader.cs | 2 +- SAUtils/MergeInterimTsvs/InterimTsvsMerger.cs | 13 +- SAUtils/MergeInterimTsvs/MergeUtilities.cs | 2 +- SAUtils/SAUtils.cs | 22 +- SAUtils/SAUtils.csproj | 4 - SAUtils/TsvWriterUtilities.cs | 4 +- SAUtils/TsvWriters/GeneAnnotationTsvWriter.cs | 2 +- SAUtils/TsvWriters/SaMiscTsvWriter.cs | 4 +- SAUtils/TsvWriters/SaTsvWriter.cs | 4 +- .../Import/ImportNodeExtensionsTests.cs | 101 +++ .../FauxRegex/RegexDecisionTreeTests.cs | 150 ++++ .../FileHandling/DataDumperReaderTests.cs | 172 +++++ .../Import/ImportRegulatoryFeatureTests.cs | 184 +++++ .../Import/ImportTranscriptTests.cs | 685 ++++++++++++++++++ .../Genes/Combiners/CombinerUtilsTests.cs | 41 ++ .../Genes/Combiners/HgncIdCombinerTests.cs | 69 ++ .../Genes/Combiners/PartitionCombinerTests.cs | 69 ++ .../CacheUtils/Genes/GeneFlattenerTests.cs | 72 ++ .../Utilities/DictionaryUtilitiesTests.cs | 89 +++ .../IO/Caches/TranscriptCacheWriterTests.cs | 7 +- .../Utilities/AccessionUtilitiesTests.cs | 73 ++ .../CacheUtils/Utilities/RemoteFileTests.cs | 34 + .../NDesk.Options/OptionSetTests.cs | 10 +- UnitTests/EndToEndTests.cs | 68 +- .../Exceptions/ExceptionsTests.cs | 2 +- UnitTests/Jasix/JasixQueryProcessingTests.cs | 24 +- UnitTests/Jasix/JasixTests.cs | 4 +- .../SA => EndToEnd/GRCh37}/chr12.nsa | Bin .../SA => EndToEnd/GRCh37}/chr12.nsa.idx | 0 .../GRCh37/chr12_7018490_7086889_Both90.bases | Bin 0 -> 29436 bytes .../chr12_7018490_7086889_Both90.polyphen.ndb | Bin 0 -> 143648 bytes .../chr12_7018490_7086889_Both90.sift.ndb | Bin 0 -> 96367 bytes ...r12_7018490_7086889_Both90.transcripts.ndb | Bin 0 -> 12623 bytes ...0_7086889_BothRefSeqAndEnsembl84_pos.bases | Bin 40572 -> 0 bytes ...89_BothRefSeqAndEnsembl84_pos.polyphen.ndb | Bin 119816 -> 0 bytes ...086889_BothRefSeqAndEnsembl84_pos.sift.ndb | Bin 79546 -> 0 bytes ...BothRefSeqAndEnsembl84_pos.transcripts.ndb | Bin 11614 -> 0 bytes .../InputFileParsers/ClinVarXmlReaderTests.cs | 19 +- .../SAUtils/InputFileParsers/CosmicTests.cs | 2 +- .../InputFileParsers/CustomAnnotationTests.cs | 2 +- .../InputFileParsers/CustomIntervalDbRead.cs | 2 +- .../DataSourceVersionTests.cs | 2 +- .../InputFileParsers/DgvReaderTests.cs | 4 +- .../SAUtils/InputFileParsers/DgvTests.cs | 2 +- .../SAUtils/InputFileParsers/EvsTests.cs | 2 +- .../SAUtils/InputFileParsers/ExacTests.cs | 2 +- .../MergedCosmicReaderTests.cs | 2 +- .../TsvWriters/DbsnpGaTsvWriterTests.cs | 2 +- .../TestUtilities/AnnotationUtilities.cs | 15 +- UnitTests/TestUtilities/Resources.cs | 23 +- .../Algorithms/IntervalArrayFactoryTests.cs | 14 +- .../AnnotatedVariantTests.cs | 2 +- .../HgvsCodingNomenclatureTests.cs | 8 +- .../HgvsProteinNomenclatureTests.cs | 9 +- .../RegulatoryRegionAnnotatorTests.cs | 2 +- .../Transcript/CompactIdTest.cs | 92 --- .../Transcript/CompactIdTests.cs | 168 +++++ UnitTests/VariantAnnotation/AnnotatorTests.cs | 331 +++++---- .../EncodedTranscriptDataTests.cs | 35 +- .../Caches/DataStructures/GeneTests.cs | 4 +- .../DataStructures/RegulatoryRegionTests.cs | 4 +- .../Caches/DataStructures/TranscriptTests.cs | 32 +- .../Caches/DataStructures/TranslationTests.cs | 8 +- .../Caches/TranscriptCacheTests.cs | 35 +- .../IO/Caches/TranscriptCacheReaderTests.cs | 161 ++-- .../IO/VcfWriter/VcfConversionTests.cs | 12 +- .../Sequence/ReferenceMetadataTests.cs | 3 +- .../SupplementaryAnnotationCommonTests.cs | 4 +- .../Utilities/FormatUtilitiesTests.cs | 11 +- .../AnnotatedPositions/ConsequenceTag.cs | 115 ++- .../IAnnotatedSaDataSource.cs | 3 +- .../AnnotatedPositions/ICompactId.cs | 30 +- .../AnnotatedPositions/IGene.cs | 1 - .../IOverlappingTranscript.cs | 3 +- .../AnnotatedPositions/IRegulatoryRegion.cs | 2 +- .../AnnotatedPositions/IRnaEdit.cs | 10 + .../AnnotatedPositions/ITranscript.cs | 17 +- .../AnnotatedPositions/ITranslation.cs | 1 - .../AnnotatedPositions/IntervalMatch.cs | 9 - .../AnnotatedPositions/PredictionScore.cs | 27 +- .../Caches/ITranscriptCache.cs | 4 +- ...ElementType.cs => RegulatoryRegionType.cs} | 2 +- VariantAnnotation.Interface/ILogger.cs | 11 + .../IO/IExtendedBinaryWriter.cs | 3 +- VariantAnnotation.Interface/IO/IVcfReader.cs | 1 - VariantAnnotation.Interface/IO/VcfCommon.cs | 29 +- .../IPluginAnnotator.cs | 13 - .../Intervals/IIntervalSearch.cs | 3 +- .../Intervals/OverlapBehavior.cs | 7 + .../Plugins/IPlugin.cs | 8 +- .../Providers/ISequenceProvider.cs | 5 +- .../SA/ISupplementaryAnnotationHeader.cs | 2 - .../Sequence/GenomeAssemblyHelper.cs | 10 +- .../Sequence/ISequenceReader.cs | 11 - .../Algorithms/IntervalArrayFactory.cs | 1 - .../AnnotatedRegulatoryRegion.cs | 2 +- .../AnnotatedSaDataSource.cs | 2 +- .../AnnotatedPositions/AnnotatedVariant.cs | 2 +- .../HgvsCodingNomenclature.cs | 8 +- .../HgvsProteinNomenclature.cs | 4 +- .../AnnotatedPositions/HgvsUtilities.cs | 4 +- .../Transcript/AnnotatedTranscript.cs | 12 +- .../Transcript/CompactId.cs | 272 ++++--- .../Transcript/MappedPositionsUtils.cs | 28 +- .../Transcript/OverlappingTranscript.cs | 22 +- .../Transcript/TranscriptUtilities.cs | 2 - VariantAnnotation/Annotator.cs | 103 ++- .../DataStructures/CdnaCoordinateMap.cs | 4 +- .../DataStructures/EncodedTranscriptData.cs | 136 ++-- .../Caches/DataStructures/Gene.cs | 20 +- .../Caches/DataStructures/IntervalArray.cs | 39 +- .../Caches/DataStructures/IntervalForest.cs | 13 +- .../Caches/DataStructures/Prediction.cs | 27 +- .../Caches/DataStructures/RegulatoryRegion.cs | 10 +- .../Caches/DataStructures/RnaEdit.cs | 35 + .../Caches/DataStructures/Transcript.cs | 115 +-- .../Caches/DataStructures/Translation.cs | 26 +- VariantAnnotation/Caches/PredictionCache.cs | 7 +- VariantAnnotation/Caches/TranscriptCache.cs | 25 +- .../Caches/TranscriptCacheData.cs | 54 ++ VariantAnnotation/GeneAnnotation/OmimEntry.cs | 2 +- VariantAnnotation/IO/Caches/CacheConstants.cs | 9 +- VariantAnnotation/IO/Caches/CacheHeader.cs | 12 +- .../IO/Caches/PredictionCacheReader.cs | 59 +- .../IO/Caches/TranscriptCacheCustomHeader.cs | 6 +- .../IO/Caches/TranscriptCacheReader.cs | 74 +- .../IO/VcfWriter/VcfConversion.cs | 60 +- VariantAnnotation/Logger/ConsoleLogger.cs | 14 + VariantAnnotation/Logger/NullLogger.cs | 13 + VariantAnnotation/PerformanceMetrics.cs | 111 +-- VariantAnnotation/PhyloP/PhylopInterval.cs | 3 - .../Providers/DataSourceVersion.cs | 81 +-- .../Providers/ReferenceSequenceProvider.cs | 74 +- .../SupplementaryAnnotationProvider.cs | 15 +- .../Providers/TranscriptAnnotationProvider.cs | 27 +- VariantAnnotation/SA/SupplementaryInterval.cs | 4 +- .../Sequence/CompressedSequenceReader.cs | 87 +-- .../Sequence/ReferenceMetadata.cs | 10 +- .../FullTranscriptAnnotator.cs | 136 ++-- .../ReducedTranscriptAnnotator.cs | 20 +- .../TranscriptAnnotationFactory.cs | 31 +- .../Utilities/FormatUtilities.cs | 20 +- VariantAnnotation/VariantAnnotation.csproj | 6 - Vcf/Info/InfoData.cs | 10 +- Vcf/Info/VcfInfoParser.cs | 1 + Vcf/Sample/AlleleDepths.cs | 4 +- Vcf/Sample/FormatIndices.cs | 5 +- Vcf/Sample/IntermediateSampleFields.cs | 9 +- Vcf/Sample/ReadCounts.cs | 6 +- Vcf/Sample/VariantFrequency.cs | 15 +- Vcf/VariantCreator/ReferenceVariantCreator.cs | 34 +- Vcf/VariantCreator/SmallVariantCreator.cs | 10 +- .../StructuralVariantCreator.cs | 12 +- Vcf/VariantCreator/VariantFactory.cs | 306 ++++---- Vcf/VcfReader.cs | 20 +- Vcf/VcfReaderUtils.cs | 18 +- 331 files changed, 13694 insertions(+), 2560 deletions(-) create mode 100644 CacheUtils/CacheUtils.dll.gene.json create mode 100644 CacheUtils/Commands/CombineCacheDirectories/CombineCacheDirectoriesMain.cs create mode 100644 CacheUtils/Commands/CreateCache/CreateNirvanaDatabaseMain.cs create mode 100644 CacheUtils/Commands/ExtractTranscripts/ExtractTranscriptsMain.cs create mode 100644 CacheUtils/Commands/ParseGenbank/ParseGenbankMain.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/ParseVepCacheDirectoryMain.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/RegulatoryRegionMerger.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/TranscriptFilter.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/TranscriptIdFilter.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/TranscriptMerger.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/VepCacheParser.cs create mode 100644 CacheUtils/Commands/ParseVepCacheDirectory/VepRootDirectory.cs create mode 100644 CacheUtils/Commands/RegulatoryGFF/CreateRegulatoryGffMain.cs create mode 100644 CacheUtils/Commands/UniversalGeneArchive/FilePaths.cs create mode 100644 CacheUtils/Commands/UniversalGeneArchive/GenbankFile.cs create mode 100644 CacheUtils/Commands/UniversalGeneArchive/UniversalGeneArchiveMain.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/GenomeSymbolSource.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/IImportNode.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensions.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/ListObjectKeyValueNode.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/ObjectKeyValueNode.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/ObjectValueNode.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/StringKeyValueNode.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Import/StringValueNode.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Mutable/MutableExon.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Mutable/MutableGene.cs create mode 100644 CacheUtils/DataDumperImport/DataStructures/Mutable/MutableTranscript.cs create mode 100644 CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTree.cs create mode 100644 CacheUtils/DataDumperImport/IO/DataDumperReader.cs create mode 100644 CacheUtils/DataDumperImport/IO/EntryType.cs create mode 100644 CacheUtils/DataDumperImport/Import/Attribute.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportExon.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportGene.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportIntron.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportKeys.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportMapper.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportMapperPair.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportMapperUnit.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportPairGenomic.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportPrediction.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportProteinFunctionPredictions.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportRegulatoryFeature.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportSeqEdits.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportTranscript.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportTranscriptMapper.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportTranslation.cs create mode 100644 CacheUtils/DataDumperImport/Import/ImportVariantEffectFeatureCache.cs create mode 100644 CacheUtils/DataDumperImport/Utilities/MutableTranscriptComparer.cs create mode 100644 CacheUtils/DataDumperImport/Utilities/TranscriptUtilities.cs delete mode 100644 CacheUtils/ExtractTranscripts/ExtractTranscriptMain.cs create mode 100644 CacheUtils/GFF/CreateGffMain.cs create mode 100644 CacheUtils/GFF/GffCreator.cs create mode 100644 CacheUtils/Genbank/GenbankEntry.cs create mode 100644 CacheUtils/Genbank/GenbankReader.cs create mode 100644 CacheUtils/Genbank/GenbankState.cs create mode 100644 CacheUtils/Genes/Combiners/CombinerUtils.cs create mode 100644 CacheUtils/Genes/Combiners/HgncIdCombiner.cs create mode 100644 CacheUtils/Genes/Combiners/ICombiner.cs create mode 100644 CacheUtils/Genes/Combiners/PartitionCombiner.cs create mode 100644 CacheUtils/Genes/DataStores/AssemblyDataStore.cs create mode 100644 CacheUtils/Genes/DataStores/EnsemblGtf.cs create mode 100644 CacheUtils/Genes/DataStores/GeneInfoData.cs create mode 100644 CacheUtils/Genes/DataStores/GlobalCache.cs create mode 100644 CacheUtils/Genes/DataStores/Hgnc.cs create mode 100644 CacheUtils/Genes/DataStores/IUpdateHgncData.cs create mode 100644 CacheUtils/Genes/DataStores/RefSeqGff.cs create mode 100644 CacheUtils/Genes/DataStores/UpdateHgncData.cs create mode 100644 CacheUtils/Genes/DataStructures/EnsemblGene.cs create mode 100644 CacheUtils/Genes/DataStructures/GeneInfo.cs create mode 100644 CacheUtils/Genes/DataStructures/HgncGene.cs create mode 100644 CacheUtils/Genes/DataStructures/IFlatGene.cs create mode 100644 CacheUtils/Genes/DataStructures/RefSeqGene.cs create mode 100644 CacheUtils/Genes/DataStructures/UgaGene.cs create mode 100644 CacheUtils/Genes/GeneFlattener.cs create mode 100644 CacheUtils/Genes/GeneMerger.cs create mode 100644 CacheUtils/Genes/GeneSymbolUpdater.cs create mode 100644 CacheUtils/Genes/GeneUtilities.cs create mode 100644 CacheUtils/Genes/HgncIdConsolidator.cs create mode 100644 CacheUtils/Genes/HgncIdUpdater.cs create mode 100644 CacheUtils/Genes/IO/AssemblyReader.cs create mode 100644 CacheUtils/Genes/IO/EnsemblGtfReader.cs create mode 100644 CacheUtils/Genes/IO/GeneInfoReader.cs create mode 100644 CacheUtils/Genes/IO/HgncReader.cs create mode 100644 CacheUtils/Genes/IO/RefSeqGffReader.cs create mode 100644 CacheUtils/Genes/IO/UgaGeneReader.cs create mode 100644 CacheUtils/Genes/IO/UgaGeneWriter.cs create mode 100644 CacheUtils/Genes/UgaAssemblyCombiner.cs create mode 100644 CacheUtils/Genes/Utilities/DictionaryUtilities.cs create mode 100644 CacheUtils/Helpers/BioTypeHelper.cs create mode 100644 CacheUtils/Helpers/GeneSymbolSourceHelper.cs create mode 100644 CacheUtils/Helpers/RegulatoryRegionTypeHelper.cs create mode 100644 CacheUtils/Helpers/SequenceHelper.cs create mode 100644 CacheUtils/Helpers/TranscriptCacheHelper.cs delete mode 100644 CacheUtils/IO/Caches/TranscriptCacheWriter.cs create mode 100644 CacheUtils/IntermediateIO/CcdsReader.cs create mode 100644 CacheUtils/IntermediateIO/GenbankReader.cs create mode 100644 CacheUtils/IntermediateIO/GenbankWriter.cs create mode 100644 CacheUtils/IntermediateIO/IntermediateIoCommon.cs create mode 100644 CacheUtils/IntermediateIO/IntermediateIoHeader.cs create mode 100644 CacheUtils/IntermediateIO/LrgReader.cs create mode 100644 CacheUtils/IntermediateIO/MutableTranscriptReader.cs create mode 100644 CacheUtils/IntermediateIO/MutableTranscriptWriter.cs create mode 100644 CacheUtils/IntermediateIO/PredictionReader.cs create mode 100644 CacheUtils/IntermediateIO/PredictionWriter.cs create mode 100644 CacheUtils/IntermediateIO/RegulatoryRegionReader.cs create mode 100644 CacheUtils/IntermediateIO/RegulatoryRegionWriter.cs create mode 100644 CacheUtils/Logger/TranscriptMergerLogger.cs create mode 100644 CacheUtils/MiniCache/DataBundle.cs create mode 100644 CacheUtils/MiniCache/IStaging.cs create mode 100644 CacheUtils/PredictionCache/PredictionCacheBuilder.cs create mode 100644 CacheUtils/PredictionCache/PredictionCacheStaging.cs create mode 100644 CacheUtils/PredictionCache/PredictionCacheWriter.cs create mode 100644 CacheUtils/PredictionCache/PredictionExtensions.cs create mode 100644 CacheUtils/PredictionCache/PredictionUtilities.cs create mode 100644 CacheUtils/PredictionCache/RoundedEntry.cs create mode 100644 CacheUtils/PredictionCache/RoundedEntryPrediction.cs create mode 100644 CacheUtils/Properties/launchSettings.json create mode 100644 CacheUtils/Sequence/CompressedSequenceWriter.cs create mode 100644 CacheUtils/Sequence/SequenceCompressor.cs create mode 100644 CacheUtils/Sequence/TwoBitSequence.cs create mode 100644 CacheUtils/TranscriptCache/CanonicalTranscriptMarker.cs create mode 100644 CacheUtils/TranscriptCache/ChromosomeInterval.cs create mode 100644 CacheUtils/TranscriptCache/Comparers/GeneComparer.cs create mode 100644 CacheUtils/TranscriptCache/Comparers/IntervalComparer.cs create mode 100644 CacheUtils/TranscriptCache/Comparers/RegulatoryRegionComparer.cs create mode 100644 CacheUtils/TranscriptCache/Comparers/UgaGeneComparer.cs create mode 100644 CacheUtils/TranscriptCache/SortExtensions.cs create mode 100644 CacheUtils/TranscriptCache/TranscriptCacheBuilder.cs create mode 100644 CacheUtils/TranscriptCache/TranscriptCacheStaging.cs create mode 100644 CacheUtils/TranscriptCache/TranscriptCacheUtilities.cs create mode 100644 CacheUtils/TranscriptCache/TranscriptCacheWriter.cs create mode 100644 CacheUtils/TranscriptCache/TranscriptConversionExtensions.cs create mode 100644 CacheUtils/Utilities/AccessionUtilities.cs create mode 100644 CacheUtils/Utilities/RemoteFile.cs create mode 100644 CacheUtils/Utilities/TaskExtensions.cs create mode 100644 CommonUtilities/ReferenceNameUtilities.cs delete mode 100644 ErrorHandling/Exceptions/InconsistantGenomeAssemblyException.cs delete mode 100644 Nirvana/ConfigurationSettings.cs create mode 100644 UnitTests/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensionsTests.cs create mode 100644 UnitTests/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTreeTests.cs create mode 100644 UnitTests/CacheUtils/DataDumperImport/FileHandling/DataDumperReaderTests.cs create mode 100644 UnitTests/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeatureTests.cs create mode 100644 UnitTests/CacheUtils/DataDumperImport/Import/ImportTranscriptTests.cs create mode 100644 UnitTests/CacheUtils/Genes/Combiners/CombinerUtilsTests.cs create mode 100644 UnitTests/CacheUtils/Genes/Combiners/HgncIdCombinerTests.cs create mode 100644 UnitTests/CacheUtils/Genes/Combiners/PartitionCombinerTests.cs create mode 100644 UnitTests/CacheUtils/Genes/GeneFlattenerTests.cs create mode 100644 UnitTests/CacheUtils/Genes/Utilities/DictionaryUtilitiesTests.cs create mode 100644 UnitTests/CacheUtils/Utilities/AccessionUtilitiesTests.cs create mode 100644 UnitTests/CacheUtils/Utilities/RemoteFileTests.cs rename UnitTests/Resources/{GRCh37_chr12_7018490_7086889/SA => EndToEnd/GRCh37}/chr12.nsa (100%) rename UnitTests/Resources/{GRCh37_chr12_7018490_7086889/SA => EndToEnd/GRCh37}/chr12.nsa.idx (100%) create mode 100644 UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.bases create mode 100644 UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.polyphen.ndb create mode 100644 UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.sift.ndb create mode 100644 UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.transcripts.ndb delete mode 100644 UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.bases delete mode 100644 UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.polyphen.ndb delete mode 100644 UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.sift.ndb delete mode 100644 UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.transcripts.ndb delete mode 100644 UnitTests/VariantAnnotation/AnnotatedPositions/Transcript/CompactIdTest.cs create mode 100644 UnitTests/VariantAnnotation/AnnotatedPositions/Transcript/CompactIdTests.cs create mode 100644 VariantAnnotation.Interface/AnnotatedPositions/IRnaEdit.cs delete mode 100644 VariantAnnotation.Interface/AnnotatedPositions/IntervalMatch.cs rename VariantAnnotation.Interface/Caches/{RegulatoryElementType.cs => RegulatoryRegionType.cs} (82%) create mode 100644 VariantAnnotation.Interface/ILogger.cs delete mode 100644 VariantAnnotation.Interface/IPluginAnnotator.cs create mode 100644 VariantAnnotation.Interface/Intervals/OverlapBehavior.cs rename SAUtils/GenomeAssemblyUtilities.cs => VariantAnnotation.Interface/Sequence/GenomeAssemblyHelper.cs (69%) delete mode 100644 VariantAnnotation.Interface/Sequence/ISequenceReader.cs create mode 100644 VariantAnnotation/Caches/DataStructures/RnaEdit.cs create mode 100644 VariantAnnotation/Caches/TranscriptCacheData.cs create mode 100644 VariantAnnotation/Logger/ConsoleLogger.cs create mode 100644 VariantAnnotation/Logger/NullLogger.cs diff --git a/CacheUtils/CacheUtils.cs b/CacheUtils/CacheUtils.cs index 2eba7c5f..ade62b46 100644 --- a/CacheUtils/CacheUtils.cs +++ b/CacheUtils/CacheUtils.cs @@ -1,17 +1,31 @@ using System.Collections.Generic; +using CacheUtils.Commands.CombineCacheDirectories; +using CacheUtils.Commands.CreateCache; +using CacheUtils.Commands.ExtractTranscripts; +using CacheUtils.Commands.ParseGenbank; +using CacheUtils.Commands.ParseVepCacheDirectory; +using CacheUtils.Commands.RegulatoryGFF; +using CacheUtils.Commands.UniversalGeneArchive; +using CacheUtils.GFF; using CommandLine.Builders; using VariantAnnotation.Interface; -using CacheUtils.ExtractTranscripts; namespace CacheUtils { internal static class CacheUtilsMain { - static int Main(string[] args) + private static int Main(string[] args) { var ops = new Dictionary { - ["exttran"] = new TopLevelOption("extracts transcripts", ExtractTranscriptMain.Run) + ["create"] = new TopLevelOption("create Nirvana cache files", CreateNirvanaDatabaseMain.Run), + ["gene"] = new TopLevelOption("updates the universal gene archive", UniversalGeneArchiveMain.Run), + ["gff"] = new TopLevelOption("export transcripts to GFF", CreateGffMain.Run), + ["parse"] = new TopLevelOption("parses the VEP cache files", ParseVepCacheDirectoryMain.Run), + ["rgff"] = new TopLevelOption("export regulatory regions to GFF", CreateRegulatoryGffMain.Run), + ["extract"] = new TopLevelOption("extracts transcripts", ExtractTranscriptsMain.Run), + ["combine"] = new TopLevelOption("combine cache directories", CombineCacheDirectoriesMain.Run), + ["genbank"] = new TopLevelOption("parse Genbank data", ParseGenbankMain.Run) }; var exitCode = new TopLevelAppBuilder(args, ops) diff --git a/CacheUtils/CacheUtils.csproj b/CacheUtils/CacheUtils.csproj index 5d42c219..8d25598c 100644 --- a/CacheUtils/CacheUtils.csproj +++ b/CacheUtils/CacheUtils.csproj @@ -6,10 +6,22 @@ Full + + + PreserveNewest + + + + + + + + + \ No newline at end of file diff --git a/CacheUtils/CacheUtils.dll.gene.json b/CacheUtils/CacheUtils.dll.gene.json new file mode 100644 index 00000000..7d62c26e --- /dev/null +++ b/CacheUtils/CacheUtils.dll.gene.json @@ -0,0 +1,12 @@ +{ + "GRCh37":{ + "ReferencePath":"E:\\Data\\Nirvana\\References\\5\\Homo_sapiens.GRCh37.Nirvana.dat", + "EnsemblCachePath":"E:\\Data\\Nirvana\\IntermediateCache\\Ensembl90_GRCh37.transcripts.gz", + "RefSeqCachePath":"E:\\Data\\Nirvana\\IntermediateCache\\RefSeq90_GRCh37.transcripts.gz" + }, + "GRCh38": { + "ReferencePath": "E:\\Data\\Nirvana\\References\\5\\Homo_sapiens.GRCh38.Nirvana.dat", + "EnsemblCachePath": "E:\\Data\\Nirvana\\IntermediateCache\\Ensembl90_GRCh38.transcripts.gz", + "RefSeqCachePath": "E:\\Data\\Nirvana\\IntermediateCache\\RefSeq90_GRCh38.transcripts.gz" + } +} \ No newline at end of file diff --git a/CacheUtils/Commands/CombineCacheDirectories/CombineCacheDirectoriesMain.cs b/CacheUtils/Commands/CombineCacheDirectories/CombineCacheDirectoriesMain.cs new file mode 100644 index 00000000..c63e199e --- /dev/null +++ b/CacheUtils/Commands/CombineCacheDirectories/CombineCacheDirectoriesMain.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using CacheUtils.Helpers; +using CacheUtils.PredictionCache; +using CacheUtils.TranscriptCache; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using Compression.Algorithms; +using Compression.FileHandling; +using ErrorHandling; +using VariantAnnotation.Caches; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; +using VariantAnnotation.Logger; +using VariantAnnotation.Providers; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Commands.CombineCacheDirectories +{ + public static class CombineCacheDirectoriesMain + { + private static string _inputPrefix; + private static string _inputPrefix2; + private static string _outputPrefix; + private static string _refSequencePath; + + private static ExitCodes ProgramExecution() + { + var sequenceData = SequenceHelper.GetDictionaries(_refSequencePath); + var logger = new ConsoleLogger(); + + var caches = LoadTranscriptCaches(logger, CacheConstants.TranscriptPath(_inputPrefix), + CacheConstants.TranscriptPath(_inputPrefix2), sequenceData.refIndexToChromosome); + + if (caches.Cache.TranscriptIntervalArrays.Length != caches.Cache2.TranscriptIntervalArrays.Length) + throw new InvalidDataException($"Expected the number of reference sequences in cache 1 ({caches.Cache.TranscriptIntervalArrays.Length}) and cache 2 ({caches.Cache2.TranscriptIntervalArrays.Length}) to be the same."); + + int numRefSeqs = caches.Cache.TranscriptIntervalArrays.Length; + var combinedIntervalArrays = new IntervalArray[numRefSeqs]; + var siftPredictionsPerRef = new Prediction[numRefSeqs][]; + var polyphenPredictionsPerRef = new Prediction[numRefSeqs][]; + + PredictionCacheReader.PredictionHeader siftHeader; + PredictionCacheReader.PredictionHeader polyphenHeader; + + using (var siftReader = new PredictionCacheReader(FileUtilities.GetReadStream(CacheConstants.SiftPath(_inputPrefix)), PredictionCacheReader.SiftDescriptions)) + using (var siftReader2 = new PredictionCacheReader(FileUtilities.GetReadStream(CacheConstants.SiftPath(_inputPrefix2)), PredictionCacheReader.SiftDescriptions)) + using (var polyphenReader = new PredictionCacheReader(FileUtilities.GetReadStream(CacheConstants.PolyPhenPath(_inputPrefix)), PredictionCacheReader.PolyphenDescriptions)) + using (var polyphenReader2 = new PredictionCacheReader(FileUtilities.GetReadStream(CacheConstants.PolyPhenPath(_inputPrefix2)), PredictionCacheReader.PolyphenDescriptions)) + { + siftHeader = siftReader.Header; + polyphenHeader = polyphenReader.Header; + + for (ushort refIndex = 0; refIndex < numRefSeqs; refIndex++) + { + var chromosome = sequenceData.refIndexToChromosome[refIndex]; + + Console.ForegroundColor = ConsoleColor.Yellow; + logger.WriteLine($"\n{chromosome.UcscName}:"); + Console.ResetColor(); + + var sift = CombinePredictions(logger, chromosome, "SIFT", siftReader, siftReader2); + siftPredictionsPerRef[refIndex] = sift.Predictions; + + var polyphen = CombinePredictions(logger, chromosome, "PolyPhen", polyphenReader, polyphenReader2); + polyphenPredictionsPerRef[refIndex] = polyphen.Predictions; + + var transcriptIntervalArray = caches.Cache.TranscriptIntervalArrays[refIndex]; + var transcriptIntervalArray2 = caches.Cache2.TranscriptIntervalArrays[refIndex]; + + combinedIntervalArrays[refIndex] = CombineTranscripts(logger, transcriptIntervalArray, + transcriptIntervalArray2, sift.Offset, polyphen.Offset); + } + } + + logger.WriteLine(); + WritePredictions(logger, "SIFT", CacheConstants.SiftPath(_outputPrefix), siftHeader, siftPredictionsPerRef); + WritePredictions(logger, "PolyPhen", CacheConstants.PolyPhenPath(_outputPrefix), polyphenHeader, polyphenPredictionsPerRef); + WriteTranscripts(logger, GetHeader(caches.Cache.Header), combinedIntervalArrays, + caches.Cache.RegulatoryRegionIntervalArrays); + + return ExitCodes.Success; + } + + private static void WriteTranscripts(ILogger logger, CacheHeader header, + IntervalArray[] transcriptIntervalArrays, + IntervalArray[] regulatoryRegionIntervalArrays) + { + var staging = TranscriptCacheStaging.GetStaging(header, transcriptIntervalArrays, regulatoryRegionIntervalArrays); + + logger.Write("- writing transcripts... "); + staging.Write(FileUtilities.GetCreateStream(CacheConstants.TranscriptPath(_outputPrefix))); + logger.WriteLine("finished."); + } + + private static void WritePredictions(ILogger logger, string description, string filePath, + PredictionCacheReader.PredictionHeader header, Prediction[][] predictionsPerRef) + { + logger.Write($"- writing {description} predictions... "); + + using (var stream = new BlockStream(new Zstandard(), FileUtilities.GetCreateStream(filePath), CompressionMode.Compress)) + using (var writer = new PredictionCacheWriter(stream, GetHeader(header.Header))) + { + writer.Write(header.Lut, predictionsPerRef); + } + + logger.WriteLine("finished."); + } + + private static IntervalArray CombineTranscripts(ILogger logger, + IntervalArray intervalArray, IntervalArray intervalArray2, + int siftOffset, int polyphenOffset) + { + logger.Write("- combine transcripts... "); + + int numCombinedTranscripts = GetNumCombinedTranscripts(intervalArray, intervalArray2); + var combinedIntervals = new Interval[numCombinedTranscripts]; + + var combinedIndex = 0; + CopyItems(intervalArray?.Array, combinedIntervals, ref combinedIndex, interval => interval); + CopyItems(intervalArray2?.Array, combinedIntervals, ref combinedIndex, interval => GetUpdatedTranscript(interval, siftOffset, polyphenOffset)); + + logger.WriteLine("finished."); + + return new IntervalArray(combinedIntervals.OrderBy(x => x.Begin).ThenBy(x => x.End).ToArray()); + } + + private static int GetNumCombinedTranscripts(IntervalArray intervalArray, + IntervalArray intervalArray2) + { + int numIntervals = intervalArray?.Array.Length ?? 0; + int numIntervals2 = intervalArray2?.Array.Length ?? 0; + return numIntervals + numIntervals2; + } + + // ReSharper disable SuggestBaseTypeForParameter + private static void CopyItems(T[] src, T[] dest, ref int destIndex, Func updateFunc) + // ReSharper restore SuggestBaseTypeForParameter + { + if (src == null) return; + foreach (var item in src) dest[destIndex++] = updateFunc(item); + } + + private static Interval GetUpdatedTranscript(Interval interval, int siftOffset, + int polyphenOffset) + { + var transcript = interval.Value; + if (transcript.SiftIndex == -1 && transcript.PolyPhenIndex == -1) return interval; + + int newSiftIndex = transcript.SiftIndex == -1 ? -1 : transcript.SiftIndex + siftOffset; + int newPolyphenIndex = transcript.PolyPhenIndex == -1 ? -1 : transcript.PolyPhenIndex + polyphenOffset; + + var updatedTranscript = transcript.UpdatePredictions(newSiftIndex, newPolyphenIndex); + return new Interval(transcript.Start, transcript.End, updatedTranscript); + } + + private static CacheHeader GetHeader(CacheHeader header) => new CacheHeader(CacheConstants.Identifier, + header.SchemaVersion, header.DataVersion, Source.BothRefSeqAndEnsembl, DateTime.Now.Ticks, + header.GenomeAssembly, header.CustomHeader); + + private static (Prediction[] Predictions, int Offset) CombinePredictions(ILogger logger, IChromosome chromosome, + string description, PredictionCacheReader reader, PredictionCacheReader reader2) + { + logger.Write($"- load {description} predictions... "); + var predictions = reader.GetPredictions(chromosome.Index); + var predictions2 = reader2.GetPredictions(chromosome.Index); + logger.WriteLine("finished."); + + var combinedPredictions = CombinePredictions(logger, description, predictions, predictions2); + return (combinedPredictions, predictions.Length); + } + + private static Prediction[] CombinePredictions(ILogger logger, string description, Prediction[] predictions, + Prediction[] predictions2) + { + logger.Write($"- combine {description} predictions... "); + + int numCombinedPredictions = predictions.Length + predictions2.Length; + var combinedPredictions = new Prediction[numCombinedPredictions]; + + var combinedIndex = 0; + CopyItems(predictions, combinedPredictions, ref combinedIndex, x => x); + CopyItems(predictions2, combinedPredictions, ref combinedIndex, x => x); + + logger.WriteLine("finished."); + + return combinedPredictions; + } + + private static (TranscriptCacheData Cache, TranscriptCacheData Cache2) LoadTranscriptCaches(ILogger logger, + string transcriptPath, string transcriptPath2, IDictionary refIndexToChromosome) + { + TranscriptCacheData cache; + TranscriptCacheData cache2; + + logger.Write("- loading transcript caches... "); + + using (var transcriptReader = new TranscriptCacheReader(FileUtilities.GetReadStream(transcriptPath))) + using (var transcriptReader2 = new TranscriptCacheReader(FileUtilities.GetReadStream(transcriptPath2))) + { + cache = transcriptReader.Read(refIndexToChromosome); + cache2 = transcriptReader2.Read(refIndexToChromosome); + } + + logger.WriteLine("finished."); + return (cache, cache2); + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "in|1=", + "input cache {prefix}", + v => _inputPrefix = v + }, + { + "in2|2=", + "input cache 2 {prefix}", + v => _inputPrefix2 = v + }, + { + "out|o=", + "output cache {prefix}", + v => _outputPrefix = v + }, + { + "ref|r=", + "input reference {path}", + v => _refSequencePath = v + } + }; + + string commandLineExample = $"{command} --in --in2 --out --ref "; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .CheckInputFilenameExists(_refSequencePath, "reference sequence", "--ref") + .HasRequiredParameter(_inputPrefix, "input cache", "--in") + .HasRequiredParameter(_inputPrefix2, "input cache 2", "--in2") + .HasRequiredParameter(_outputPrefix, "output cache", "--out") + .SkipBanner() + .ShowHelpMenu("Combines two cache sets into one cache.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/Commands/CreateCache/CreateNirvanaDatabaseMain.cs b/CacheUtils/Commands/CreateCache/CreateNirvanaDatabaseMain.cs new file mode 100644 index 00000000..9cf179cc --- /dev/null +++ b/CacheUtils/Commands/CreateCache/CreateNirvanaDatabaseMain.cs @@ -0,0 +1,177 @@ +using System.Collections.Generic; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.IO; +using CacheUtils.Genes.Utilities; +using CacheUtils.Helpers; +using CacheUtils.IntermediateIO; +using CacheUtils.PredictionCache; +using CacheUtils.TranscriptCache; +using CacheUtils.Utilities; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using Compression.Utilities; +using ErrorHandling; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; +using VariantAnnotation.Logger; +using VariantAnnotation.Providers; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Commands.CreateCache +{ + public static class CreateNirvanaDatabaseMain + { + private static string _inputPrefix; + private static string _inputReferencePath; + private static string _inputUgaPath; + + private static string _outputCacheFilePrefix; + + private static ExitCodes ProgramExecution() + { + var logger = new ConsoleLogger(); + var transcriptPath = _inputPrefix + ".transcripts.gz"; + var siftPath = _inputPrefix + ".sift.gz"; + var polyphenPath = _inputPrefix + ".polyphen.gz"; + var regulatoryPath = _inputPrefix + ".regulatory.gz"; + + var (refIndexToChromosome, refNameToChromosome, numRefSeqs) = SequenceHelper.GetDictionaries(_inputReferencePath); + + using (var transcriptReader = new MutableTranscriptReader(GZipUtilities.GetAppropriateReadStream(transcriptPath), refIndexToChromosome)) + using (var regulatoryReader = new RegulatoryRegionReader(GZipUtilities.GetAppropriateReadStream(regulatoryPath), refIndexToChromosome)) + using (var siftReader = new PredictionReader(GZipUtilities.GetAppropriateReadStream(siftPath), refIndexToChromosome, IntermediateIoCommon.FileType.Sift)) + using (var polyphenReader = new PredictionReader(GZipUtilities.GetAppropriateReadStream(polyphenPath), refIndexToChromosome, IntermediateIoCommon.FileType.Polyphen)) + using (var geneReader = new UgaGeneReader(GZipUtilities.GetAppropriateReadStream(_inputUgaPath), refNameToChromosome)) + { + var genomeAssembly = transcriptReader.Header.GenomeAssembly; + var source = transcriptReader.Header.Source; + var vepReleaseTicks = transcriptReader.Header.VepReleaseTicks; + + logger.Write("- loading universal gene archive file... "); + var genes = geneReader.GetGenes(); + var geneForest = CreateGeneForest(genes, numRefSeqs, genomeAssembly); + logger.WriteLine($"{genes.Length:N0} loaded."); + + logger.Write("- loading regulatory region file... "); + var regulatoryRegions = regulatoryReader.GetRegulatoryRegions(); + logger.WriteLine($"{regulatoryRegions.Length:N0} loaded."); + + logger.Write("- loading transcript file... "); + var transcripts = transcriptReader.GetTranscripts(); + var transcriptsByRefIndex = transcripts.GetMultiValueDict(x => x.Chromosome.Index); + logger.WriteLine($"{transcripts.Length:N0} loaded."); + + MarkCanonicalTranscripts(logger, transcripts); + + var predictionBuilder = new PredictionCacheBuilder(logger, genomeAssembly); + var predictionCaches = predictionBuilder.CreatePredictionCaches(transcriptsByRefIndex, siftReader, polyphenReader, numRefSeqs); + + logger.Write("- writing SIFT prediction cache... "); + predictionCaches.Sift.Write(FileUtilities.GetCreateStream(CacheConstants.SiftPath(_outputCacheFilePrefix))); + logger.WriteLine("finished."); + + logger.Write("- writing PolyPhen prediction cache... "); + predictionCaches.PolyPhen.Write(FileUtilities.GetCreateStream(CacheConstants.PolyPhenPath(_outputCacheFilePrefix))); + logger.WriteLine("finished."); + + var transcriptBuilder = new TranscriptCacheBuilder(logger, genomeAssembly, source, vepReleaseTicks); + var transcriptStaging = transcriptBuilder.CreateTranscriptCache(transcripts, regulatoryRegions, geneForest, numRefSeqs); + + logger.Write("- writing transcript cache... "); + transcriptStaging.Write(FileUtilities.GetCreateStream(CacheConstants.TranscriptPath(_outputCacheFilePrefix))); + logger.WriteLine("finished."); + } + + return ExitCodes.Success; + } + + private static IIntervalForest CreateGeneForest(IEnumerable genes, int numRefSeqs, GenomeAssembly genomeAssembly) + { + var useGrch37 = genomeAssembly == GenomeAssembly.GRCh37; + var intervalLists = new List>[numRefSeqs]; + + for (var i = 0; i < numRefSeqs; i++) intervalLists[i] = new List>(); + + foreach (var gene in genes) + { + var coords = useGrch37 ? gene.GRCh37 : gene.GRCh38; + if (coords.Start == -1 && coords.End == -1) continue; + intervalLists[gene.Chromosome.Index].Add(new Interval(coords.Start, coords.End, gene)); + } + + var refIntervalArrays = new IntervalArray[numRefSeqs]; + for (var i = 0; i < numRefSeqs; i++) + { + refIntervalArrays[i] = new IntervalArray(intervalLists[i].OrderBy(x => x.Begin).ThenBy(x => x.End).ToArray()); + } + + return new IntervalForest(refIntervalArrays); + } + + private static void MarkCanonicalTranscripts(ILogger logger, MutableTranscript[] transcripts) + { + var fileList = new List(); + var ccdsFile = new RemoteFile("CCDS file (2016-09-08)", "ftp://ftp.ncbi.nlm.nih.gov/pub/CCDS/current_human/CCDS2Sequence.20160908.txt", false); + var lrgFile = new RemoteFile("latest LRG file", "http://ftp.ebi.ac.uk/pub/databases/lrgex/list_LRGs_transcripts_xrefs.txt"); + + fileList.Add(ccdsFile); + fileList.Add(lrgFile); + + fileList.Execute(logger, "downloads", file => file.Download(logger)); + + var ccdsIdToEnsemblId = CcdsReader.GetCcdsIdToEnsemblId(ccdsFile.FilePath); + var lrgTranscriptIds = LrgReader.GetTranscriptIds(lrgFile.FilePath, ccdsIdToEnsemblId); + + logger.Write("- marking canonical transcripts... "); + var canonical = new CanonicalTranscriptMarker(lrgTranscriptIds); + int numCanonicalTranscripts = canonical.MarkTranscripts(transcripts); + logger.WriteLine($"{numCanonicalTranscripts:N0} marked."); + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "genes|g=", + "universal genes archive {filename}", + v => _inputUgaPath = v + }, + { + "in|i=", + "input filename {prefix}", + v => _inputPrefix = v + }, + { + "out|o=", + "output cache file {prefix}", + v => _outputCacheFilePrefix = v + }, + { + "ref|r=", + "input reference {filename}", + v => _inputReferencePath = v + } + }; + + var commandLineExample = $"{command} --in --out --genes --ref --lrg "; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .HasRequiredParameter(_inputPrefix, "intermediate cache", "--in") + .CheckInputFilenameExists(_inputReferencePath, "compressed reference", "--ref") + .CheckInputFilenameExists(_inputUgaPath, "UGA genes", "--genes") + .HasRequiredParameter(_outputCacheFilePrefix, "Nirvana", "--out") + .SkipBanner() + .ShowHelpMenu("Converts *deserialized* VEP cache files to Nirvana cache format.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/Commands/ExtractTranscripts/ExtractTranscriptsMain.cs b/CacheUtils/Commands/ExtractTranscripts/ExtractTranscriptsMain.cs new file mode 100644 index 00000000..a023f062 --- /dev/null +++ b/CacheUtils/Commands/ExtractTranscripts/ExtractTranscriptsMain.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.MiniCache; +using CacheUtils.PredictionCache; +using CacheUtils.Sequence; +using CacheUtils.TranscriptCache; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using CommonUtilities; +using ErrorHandling; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Caches; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; +using VariantAnnotation.Logger; +using VariantAnnotation.Providers; +using VariantAnnotation.Sequence; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Commands.ExtractTranscripts +{ + public static class ExtractTranscriptsMain + { + private static string _inputPrefix; + private static string _inputReferencePath; + private static string _outputDirectory; + + private static string _referenceName; + + private static int _referencePosition = -1; + private static int _referenceEndPosition = -1; + + private static ExitCodes ProgramExecution() + { + var logger = new ConsoleLogger(); + var bundle = DataBundle.GetDataBundle(_inputReferencePath, _inputPrefix); + int numRefSeqs = bundle.SequenceReader.NumRefSeqs; + var chromosome = ReferenceNameUtilities.GetChromosome(bundle.SequenceReader.RefNameToChromosome, _referenceName); + bundle.Load(chromosome); + + var outputStub = GetOutputStub(chromosome, bundle.Source); + var interval = new ChromosomeInterval(chromosome, _referencePosition, _referenceEndPosition); + var transcripts = GetTranscripts(logger, bundle, interval); + + var sift = GetPredictionStaging(logger, "SIFT", transcripts, chromosome, bundle.SiftPredictions, bundle.SiftReader, x => x.SiftIndex, numRefSeqs); + var polyphen = GetPredictionStaging(logger, "PolyPhen", transcripts, chromosome, bundle.PolyPhenPredictions, bundle.PolyPhenReader, x => x.PolyPhenIndex, numRefSeqs); + var referenceBases = GetReferenceBases(logger, bundle.SequenceReader, interval); + + var regulatoryRegionIntervalArrays = GetRegulatoryRegionIntervalArrays(logger, bundle.TranscriptCache, interval, numRefSeqs); + var transcriptIntervalArrays = PredictionUtilities.UpdateTranscripts(transcripts, bundle.SiftPredictions, + sift.Predictions, bundle.PolyPhenPredictions, polyphen.Predictions, numRefSeqs); + + var transcriptStaging = GetTranscriptStaging(bundle.TranscriptCacheData.Header, transcriptIntervalArrays, regulatoryRegionIntervalArrays); + + WriteCache(logger, FileUtilities.GetCreateStream(CacheConstants.TranscriptPath(outputStub)), transcriptStaging, "transcript"); + WriteCache(logger, FileUtilities.GetCreateStream(CacheConstants.SiftPath(outputStub)), sift.Staging, "SIFT"); + WriteCache(logger, FileUtilities.GetCreateStream(CacheConstants.PolyPhenPath(outputStub)), polyphen.Staging, "PolyPhen"); + WriteReference(logger, CacheConstants.BasesPath(outputStub), bundle.SequenceReader, chromosome, + referenceBases, interval.Start); + + return ExitCodes.Success; + } + + private static TranscriptCacheStaging GetTranscriptStaging(CacheHeader header, + IntervalArray[] transcriptIntervalArrays, + IntervalArray[] regulatoryRegionIntervalArrays) => + TranscriptCacheStaging.GetStaging(header, transcriptIntervalArrays, regulatoryRegionIntervalArrays); + + private static void WriteReference(ILogger logger, string outputPath, CompressedSequenceReader reader, + IChromosome chromosome, string referenceBases, int offset) + { + logger.Write("- writing reference bases... "); + var cytogeneticBands = new CytogeneticBands(reader.CytogeneticBands); + + using (var writer = new CompressedSequenceWriter(FileUtilities.GetCreateStream(outputPath), + reader.ReferenceMetadataList, cytogeneticBands, reader.Assembly)) + { + writer.Write(chromosome.EnsemblName, referenceBases, offset); + } + logger.WriteLine("finished."); + } + + private static void WriteCache(ILogger logger, Stream stream, IStaging staging, string description) + { + logger.Write($"- writing {description} cache... "); + staging.Write(stream); + logger.WriteLine("finished."); + } + + private static string GetOutputStub(IChromosome chromosome, Source source) => Path.Combine(_outputDirectory, + $"{chromosome.UcscName}_{_referencePosition}_{_referenceEndPosition}_{GetSource(source)}{CacheConstants.VepVersion}"); + + private static string GetSource(Source source) => + source != Source.BothRefSeqAndEnsembl ? source.ToString() : "Both"; + + private static string GetReferenceBases(ILogger logger, CompressedSequenceReader reader, IChromosomeInterval interval) + { + logger.Write("- retrieving reference bases... "); + reader.GetCompressedSequence(interval.Chromosome); + var referenceBases = reader.Sequence.Substring(interval.Start, interval.End - interval.Start + 1); + logger.WriteLine($"{referenceBases.Length} bases extracted."); + + return referenceBases; + } + + private static (PredictionCacheStaging Staging, Prediction[] Predictions) GetPredictionStaging(ILogger logger, + string description, IEnumerable transcripts, IChromosome chromosome, IReadOnlyList oldPredictions, + PredictionCacheReader reader, Func indexFunc, int numRefSeqs) + { + logger.Write($"- retrieving {description} predictions... "); + + var indexSet = GetUniqueIndices(transcripts, indexFunc); + var lut = reader.Header.Lut; + var predictionsPerRef = GetPredictions(indexSet, chromosome, numRefSeqs, oldPredictions); + var staging = new PredictionCacheStaging(reader.Header.Header, lut, predictionsPerRef); + + logger.WriteLine($"found {indexSet.Count} predictions."); + return (staging, predictionsPerRef[chromosome.Index]); + } + + private static Prediction[][] GetPredictions(ICollection indexSet, IChromosome chromosome, int numRefSeqs, + IReadOnlyList oldPredictions) + { + var refPredictions = new Prediction[indexSet.Count]; + + int predIdx = 0; + foreach (var index in indexSet) refPredictions[predIdx++] = oldPredictions[index]; + + var predictions = new Prediction[numRefSeqs][]; + predictions[chromosome.Index] = refPredictions; + return predictions; + } + + private static HashSet GetUniqueIndices(IEnumerable transcripts, Func indexFunc) + { + var indexSet = new HashSet(); + foreach (var transcript in transcripts) + { + var index = indexFunc(transcript); + if (index == -1) continue; + indexSet.Add(index); + } + return indexSet; + } + + private static IntervalArray[] GetRegulatoryRegionIntervalArrays(ILogger logger, + ITranscriptCache cache, IChromosomeInterval interval, int numRefSeqs) + { + logger.Write("- retrieving regulatory regions... "); + var regulatoryRegions = cache.GetOverlappingRegulatoryRegions(interval); + logger.WriteLine($"found {regulatoryRegions.Length} regulatory regions."); + return regulatoryRegions.ToIntervalArrays(numRefSeqs); + } + + private static List GetTranscripts(ILogger logger, DataBundle bundle, IChromosomeInterval interval) + { + logger.Write("- retrieving transcripts... "); + var transcripts = TranscriptCacheUtilities.GetTranscripts(bundle, interval); + logger.WriteLine($"found {transcripts.Count} transcripts."); + + if (transcripts.Count == 0) throw new InvalidDataException("Expected at least one transcript, but found none."); + return transcripts; + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "in|i=", + "input cache {prefix}", + v => _inputPrefix = v + }, + { + "name|n=", + "reference {name}", + v => _referenceName = v + }, + { + "out|o=", + "output {directory}", + v => _outputDirectory = v + }, + { + "pos|p=", + "reference {position}", + (int v) => _referencePosition = v + }, + { + "endpos=", + "reference end {position}", + (int v) => _referenceEndPosition = v + }, + { + "ref|r=", + "input reference {filename}", + v => _inputReferencePath = v + } + }; + + var commandLineExample = $"{command} --in --out -r --chr -p --endpos \n"; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .HasRequiredParameter(_inputPrefix, "Nirvana cache", "--in") + .CheckInputFilenameExists(_inputReferencePath, "compressed reference sequence", "--ref") + .CheckDirectoryExists(_outputDirectory, "output cache", "--out") + .SkipBanner() + .ShowHelpMenu("Extracts transcripts from Nirvana cache files.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/Commands/ParseGenbank/ParseGenbankMain.cs b/CacheUtils/Commands/ParseGenbank/ParseGenbankMain.cs new file mode 100644 index 00000000..05510095 --- /dev/null +++ b/CacheUtils/Commands/ParseGenbank/ParseGenbankMain.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.Commands.UniversalGeneArchive; +using CacheUtils.Genbank; +using CacheUtils.IntermediateIO; +using CacheUtils.Utilities; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using Compression.Utilities; +using ErrorHandling; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Logger; +using VariantAnnotation.Providers; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Commands.ParseGenbank +{ + public static class ParseGenbankMain + { + private static string _outputGenbankPath; + + private static ExitCodes ProgramExecution() + { + var logger = new ConsoleLogger(); + if (!_outputGenbankPath.EndsWith(".gz")) _outputGenbankPath += ".gz"; + + var genbankFiles = GetGenbankFiles(logger); + genbankFiles.Execute(logger, "downloads", file => file.Download()); + genbankFiles.Execute(logger, "file parsing", file => file.Parse()); + + var genbankEntries = GetIdToGenbankEntryDict(genbankFiles); + WriteDictionary(logger, genbankEntries); + + return ExitCodes.Success; + } + + private static List GetGenbankFiles(ILogger logger) + { + int numGenbankFiles = GetNumGenbankFiles(logger); + var genbankFiles = new List(numGenbankFiles); + + for (int i = 0; i < numGenbankFiles; i++) genbankFiles.Add(new GenbankFile(logger, i + 1)); + return genbankFiles; + } + + private static IEnumerable GetIdToGenbankEntryDict(IEnumerable files) => + files.SelectMany(file => file.GenbankDict.Values).OrderBy(x => x.TranscriptId).ToList(); + + private static int GetNumGenbankFiles(ILogger logger) + { + var fileList = new RemoteFile("RefSeq filelist", "ftp://ftp.ncbi.nlm.nih.gov/refseq/H_sapiens/mRNA_Prot/human.files.installed"); + fileList.Download(logger); + + int maxNum = 0; + + using (var reader = new StreamReader(FileUtilities.GetReadStream(fileList.FilePath))) + { + while (true) + { + var line = reader.ReadLine(); + if (line == null) break; + + var filename = line.Split('\t')[1]; + if (!filename.EndsWith(".rna.gbff.gz")) continue; + + int num = int.Parse(filename.Substring(6, filename.Length - 18)); + if (num > maxNum) maxNum = num; + } + } + + return maxNum; + } + + private static void WriteDictionary(ILogger logger, IEnumerable entries) + { + var header = new IntermediateIoHeader(0, 0, Source.None, GenomeAssembly.Unknown, 0); + + logger.Write($"- writing output file ({Path.GetFileName(_outputGenbankPath)})... "); + using (var writer = new GenbankWriter(GZipUtilities.GetStreamWriter(_outputGenbankPath), header)) + { + foreach (var entry in entries) writer.Write(entry); + } + logger.WriteLine("finished."); + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "out|o=", + "output intermediate Genbank {path}", + v => _outputGenbankPath = v + } + }; + + var commandLineExample = $"{command} --out "; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .HasRequiredParameter(_outputGenbankPath, "output Genbank path", "--out") + .SkipBanner() + .ShowHelpMenu("Parses Genbank data to allow other CacheUtils tools to access the data", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/ParseVepCacheDirectoryMain.cs b/CacheUtils/Commands/ParseVepCacheDirectory/ParseVepCacheDirectoryMain.cs new file mode 100644 index 00000000..8a92eed4 --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/ParseVepCacheDirectoryMain.cs @@ -0,0 +1,221 @@ +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.IntermediateIO; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using Compression.Utilities; +using ErrorHandling; +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genbank; +using CacheUtils.Logger; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Logger; +using VariantAnnotation.Providers; +using VariantAnnotation.Sequence; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public static class ParseVepCacheDirectoryMain + { + private static string _inputVepDirectory; + private static string _inputReferencePath; + private static string _inputGenbankPath; + + private static string _outputStub; + + private static string _vepReleaseDate; + private static string _genomeAssembly; + private static string _transcriptSource; + private static ushort _vepVersion; + + private static ExitCodes ProgramExecution() + { + var logger = new ConsoleLogger(); + + var transcriptSource = GetSource(_transcriptSource); + var sequenceReader = new CompressedSequenceReader(FileUtilities.GetReadStream(_inputReferencePath)); + var vepRootDirectory = new VepRootDirectory(sequenceReader.RefNameToChromosome); + var refIndexToVepDir = vepRootDirectory.GetRefIndexToVepDir(_inputVepDirectory); + + var genomeAssembly = GenomeAssemblyHelper.Convert(_genomeAssembly); + var vepReleaseTicks = DateTime.Parse(_vepReleaseDate).Ticks; + var idToGenbank = GetIdToGenbank(logger, genomeAssembly, transcriptSource); + + // ========================= + // create the pre-cache file + // ========================= + + // process each VEP directory + int numRefSeqs = sequenceReader.NumRefSeqs; + var header = new IntermediateIoHeader(_vepVersion, vepReleaseTicks, transcriptSource, genomeAssembly, numRefSeqs); + + var siftPath = _outputStub + ".sift.gz"; + var polyphenPath = _outputStub + ".polyphen.gz"; + var transcriptPath = _outputStub + ".transcripts.gz"; + var regulatoryPath = _outputStub + ".regulatory.gz"; + + using (var mergeLogger = new TranscriptMergerLogger(FileUtilities.GetCreateStream(_outputStub + ".merge_transcripts.log"))) + using (var siftWriter = new PredictionWriter(GZipUtilities.GetStreamWriter(siftPath), header, IntermediateIoCommon.FileType.Sift)) + using (var polyphenWriter = new PredictionWriter(GZipUtilities.GetStreamWriter(polyphenPath), header, IntermediateIoCommon.FileType.Polyphen)) + using (var transcriptWriter = new MutableTranscriptWriter(GZipUtilities.GetStreamWriter(transcriptPath), header)) + using (var regulatoryRegionWriter = new RegulatoryRegionWriter(GZipUtilities.GetStreamWriter(regulatoryPath), header)) + { + var converter = new VepCacheParser(transcriptSource); + var emptyPredictionDict = new Dictionary>(); + + for (ushort refIndex = 0; refIndex < numRefSeqs; refIndex++) + { + var chromosome = sequenceReader.RefIndexToChromosome[refIndex]; + + if (!refIndexToVepDir.TryGetValue(refIndex, out var vepSubDir)) + { + siftWriter.Write(chromosome, emptyPredictionDict); + polyphenWriter.Write(chromosome, emptyPredictionDict); + continue; + } + + Console.WriteLine("Parsing reference sequence [{0}]:", chromosome.UcscName); + //if (vepSubDir.Chromosome.Index < 2) continue; + + var rawData = converter.ParseDumpDirectory(chromosome, vepSubDir); + var mergedTranscripts = TranscriptMerger.Merge(mergeLogger, rawData.Transcripts, idToGenbank); + var mergedRegulatoryRegions = RegulatoryRegionMerger.Merge(rawData.RegulatoryRegions); + + int numRawTranscripts = rawData.Transcripts.Count; + int numMergedTranscripts = mergedTranscripts.Count; + Console.WriteLine($"- # merged transcripts: {numMergedTranscripts}, # total transcripts: {numRawTranscripts}"); + + WriteTranscripts(transcriptWriter, mergedTranscripts); + WriteRegulatoryRegions(regulatoryRegionWriter, mergedRegulatoryRegions); + WritePredictions(siftWriter, mergedTranscripts, x => x.SiftData, chromosome); + WritePredictions(polyphenWriter, mergedTranscripts, x => x.PolyphenData, chromosome); + } + } + + Console.WriteLine("\n{0} directories processed.", refIndexToVepDir.Count); + + return ExitCodes.Success; + } + + private static Dictionary GetIdToGenbank(ILogger logger, GenomeAssembly assembly, Source source) + { + if (assembly != GenomeAssembly.GRCh37 || source != Source.RefSeq) return null; + if (string.IsNullOrEmpty(_inputGenbankPath)) throw new InvalidDataException("When parsing RefSeq GRCh37 data, data from the CacheUtils genbank tool is required (--genbank)"); + + logger.Write("- loading the intermediate Genbank file... "); + + Dictionary genbankDict; + using (var reader = new IntermediateIO.GenbankReader(GZipUtilities.GetAppropriateReadStream(_inputGenbankPath))) + { + genbankDict = reader.GetIdToGenbank(); + } + + logger.WriteLine($"{genbankDict.Count} entries loaded."); + return genbankDict; + } + + private static void WriteRegulatoryRegions(RegulatoryRegionWriter writer, IEnumerable regulatoryRegions) + { + foreach (var regulatoryRegion in regulatoryRegions) writer.Write(regulatoryRegion); + } + + private static void WriteTranscripts(MutableTranscriptWriter writer, IEnumerable transcripts) + { + foreach (var transcript in transcripts) writer.Write(transcript); + } + + private static void WritePredictions(PredictionWriter writer, IReadOnlyList transcripts, + Func predictionFunc, IChromosome chromosome) + { + var predictionDict = new Dictionary>(StringComparer.Ordinal); + + for (int transcriptIndex = 0; transcriptIndex < transcripts.Count; transcriptIndex++) + { + var transcript = transcripts[transcriptIndex]; + var predictionData = predictionFunc(transcript); + if (predictionData == null) continue; + + if (predictionDict.TryGetValue(predictionData, out var transcriptIdList)) transcriptIdList.Add(transcriptIndex); + else predictionDict[predictionData] = new List { transcriptIndex }; + } + + writer.Write(chromosome, predictionDict); + } + + private static Source GetSource(string source) + { + source = source.ToLower(); + if (source.StartsWith("ensembl")) return Source.Ensembl; + if (source.StartsWith("refseq")) return Source.RefSeq; + return source.StartsWith("both") ? Source.BothRefSeqAndEnsembl : Source.None; + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "date=", + "VEP release {date}", + v => _vepReleaseDate = v + }, + { + "source|s=", + "transcript {source}", + v => _transcriptSource = v + }, + { + "ga=", + "genome assembly {version}", + v => _genomeAssembly = v + }, + { + "genbank=", + "intermediate genbank {path}", + v => _inputGenbankPath = v + }, + { + "in|i=", + "input VEP {directory}", + v => _inputVepDirectory = v + }, + { + "out|o=", + "output filename {stub}", + v => _outputStub = v + }, + { + "ref|r=", + "input reference {filename}", + v => _inputReferencePath = v + }, + { + "vep=", + "VEP {version}", + (ushort v) => _vepVersion = v + } + }; + + var commandLineExample = $"{command} --in --out --vep "; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .CheckDirectoryExists(_inputVepDirectory, "VEP", "--in") + .CheckInputFilenameExists(_inputReferencePath, "compressed reference sequence", "--ref") + .HasRequiredParameter(_outputStub, "output stub", "--out") + .HasRequiredParameter(_vepVersion, "VEP version", "--vep") + .HasRequiredParameter(_genomeAssembly, "genome assembly", "--ga") + .HasRequiredDate(_vepReleaseDate, "VEP release date", "--date") + .HasRequiredParameter(_transcriptSource, "transcript source", "--source") + .SkipBanner() + .ShowHelpMenu("Converts *deserialized* VEP cache files to a Nirvana pre-cache file.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/RegulatoryRegionMerger.cs b/CacheUtils/Commands/ParseVepCacheDirectory/RegulatoryRegionMerger.cs new file mode 100644 index 00000000..7e68eea8 --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/RegulatoryRegionMerger.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.TranscriptCache.Comparers; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public static class RegulatoryRegionMerger + { + public static IEnumerable Merge(IEnumerable regulatoryRegions) + { + var regulatoryDict = new Dictionary(); + var comparer = new RegulatoryRegionComparer(); + + foreach (var currentRegion in regulatoryRegions) + { + if (currentRegion.Id.IsEmpty()) throw new InvalidOperationException("Found a regulatory region without an ID."); + + var regulatoryKey = $"{currentRegion.Id}.{currentRegion.Start}.{currentRegion.End}"; + + if (regulatoryDict.TryGetValue(regulatoryKey, out var previousRegion)) + { + MergeRegulatoryRegion(previousRegion, currentRegion, comparer); + } + else + { + regulatoryDict[regulatoryKey] = currentRegion; + } + } + + return regulatoryDict.Values.OrderBy(x => x.Chromosome.Index).ThenBy(x => x.Start).ThenBy(x => x.End) + .ToList(); + } + + private static void MergeRegulatoryRegion(IRegulatoryRegion previous, IRegulatoryRegion current, + RegulatoryRegionComparer comparer) + { + if (comparer.Equals(previous, current)) return; + throw new InvalidDataException("Found different regulatory regions"); + } + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptFilter.cs b/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptFilter.cs new file mode 100644 index 00000000..8f75d230 --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptFilter.cs @@ -0,0 +1,345 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.DataDumperImport.Utilities; +using CacheUtils.Genbank; +using CacheUtils.Genes.Utilities; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public static class TranscriptFilter + { + private static readonly MutableTranscriptComparer Comparer = new MutableTranscriptComparer(); + + private static void Log(this ILogger logger, string transcriptId, string description) => + logger.WriteLine($"{transcriptId}\t{description}"); + + public static List PickSpecificTranscript( + this List transcripts, ILogger logger, string transcriptId) + { + if (transcripts.Count == 1) return transcripts; + + List filteredTranscripts; + string logMessage; + + switch (transcriptId) + { + case "NM_001005786": + filteredTranscripts = transcripts.Where(transcript => transcript.CdnaMaps[9].Start == 25419007).ToList(); + logMessage = $"Filtered on exon 9 start: {transcriptId}"; + break; + default: + return transcripts; + } + + if (filteredTranscripts.Count == 0) return transcripts; + logger.Log(transcriptId, logMessage); + + return filteredTranscripts.Unique(); + } + + public static List ChooseEditedTranscripts( + this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var filteredTranscripts = transcripts.Where(transcript => transcript.RnaEdits != null).ToList(); + if (filteredTranscripts.Count == 0) return transcripts; + + logger.Log(transcripts[0].Id, "Filtered transcripts without RNA edits"); + return filteredTranscripts.Unique(); + } + + public static List RemoveTranscriptsWithLowestVersion( + this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var versionToTranscript = transcripts.GetMultiValueDict(x => x.Version); + if (versionToTranscript.Count == 1) return transcripts; + + var maxVersion = versionToTranscript.Keys.Max(); + transcripts.RemoveAll(x => x.Version != maxVersion); + + logger.Log(transcripts[0].Id, "Filtered transcripts with lower versions"); + return transcripts.Unique(); + } + + public static List RemoveTranscriptsWithDifferentExons( + this List transcripts, ILogger logger, + IReadOnlyDictionary idToGenbankEntry, string transcriptId) + { + if (transcripts.Count == 1 || idToGenbankEntry == null || !idToGenbankEntry.TryGetValue(transcriptId, out var genbankEntry)) return transcripts; + + var filteredTranscripts = transcripts.Where(transcript => CdnaMapsMatch(transcript.CdnaMaps, genbankEntry.Exons)).ToList(); + if (filteredTranscripts.Count == 0) return transcripts; + + logger.Log(transcripts[0].Id, "Removed transcripts with different exons"); + return filteredTranscripts.Unique(); + } + + private static bool CdnaMapsMatch(IReadOnlyList cdnaMaps, IReadOnlyList exons) + { + if (cdnaMaps.Count != exons.Count) return false; + // ReSharper disable once LoopCanBeConvertedToQuery + for (int i = 0; i < cdnaMaps.Count; i++) if (!CdnaMapMatches(cdnaMaps[i], exons[i])) return false; + return true; + } + + private static bool CdnaMapMatches(ICdnaCoordinateMap cdnaMap, IInterval exon) => + cdnaMap.CdnaStart == exon.Start && cdnaMap.CdnaEnd == exon.End; + + public static List Unique(this IEnumerable transcripts) + { + var set = new HashSet(Comparer); + foreach (var transcript in transcripts) set.Add(transcript); + return set.ToList(); + } + + public static List FixCodingRegionCdnaStart(this List transcripts, + ILogger logger, IReadOnlyDictionary idToGenbankEntry, string transcriptId) + { + if (transcripts.Count == 1 || idToGenbankEntry == null || !idToGenbankEntry.TryGetValue(transcriptId, out var genbankEntry)) return transcripts; + + var cdnaStartToTranscript = transcripts.GetMultiValueDict(x => x.CodingRegion.CdnaStart); + if (cdnaStartToTranscript.Count == 1) return transcripts; + + if (!cdnaStartToTranscript.TryGetValue(genbankEntry.CodingRegion.Start, out var filteredTranscripts)) + return transcripts; + + logger.Log(transcripts[0].Id, "Filtered transcripts by coding region cDNA start"); + return filteredTranscripts.Unique(); + } + + public static List FixCodingRegionCdnaEnd(this List transcripts, + ILogger logger, IReadOnlyDictionary idToGenbankEntry, string transcriptId) + { + if (transcripts.Count == 1 || idToGenbankEntry == null || !idToGenbankEntry.TryGetValue(transcriptId, out var genbankEntry)) return transcripts; + + var cdnaEndToTranscript = transcripts.GetMultiValueDict(x => x.CodingRegion.CdnaEnd); + if (cdnaEndToTranscript.Count == 1) return transcripts; + + if (!cdnaEndToTranscript.TryGetValue(genbankEntry.CodingRegion.End, out var filteredTranscripts)) + return transcripts; + + logger.Log(transcripts[0].Id, "Filtered transcripts by coding region cDNA end"); + return filteredTranscripts.Unique(); + } + + public static List FixCdnaMapExonInconsistency(this List transcripts, + ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var cdnaMapLengths = transcripts.GetSet(x => x.CdnaMaps.Length); + var exonLengths = transcripts.GetSet(x => x.Exons.Length); + if (cdnaMapLengths.Count == 1 && exonLengths.Count == 1) return transcripts; + + var filteredTranscripts = transcripts.Where(transcript => transcript.CdnaMaps.Length == transcript.Exons.Length).ToList(); + if (filteredTranscripts.Count == 0) return transcripts; + + logger.Log(transcripts[0].Id, "Filtered transcripts with inconsistent # of exons and cDNA maps"); + return filteredTranscripts.Unique(); + } + + public static List FixGeneSymbolSource(this List transcripts, + ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var symbolSources = transcripts.GetSet(x => x.Gene.SymbolSource); + if (symbolSources.Count == 1) return transcripts; + + if (symbolSources.Contains(GeneSymbolSource.Unknown)) symbolSources.Remove(GeneSymbolSource.Unknown); + if (symbolSources.Count != 1) throw new NotImplementedException("Cannot handle multiple gene symbol sources at this time"); + + var targetSymbolSource = symbolSources.First(); + foreach (var transcript in transcripts) transcript.Gene.SymbolSource = targetSymbolSource; + logger.Log(transcripts[0].Id, "Normalized gene symbol source"); + return transcripts.Unique(); + } + + public static List FixBioType(this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var biotypes = transcripts.GetSet(x => x.BioType); + if (biotypes.Count != 2) return transcripts; + + var biotype = GetDesiredBioType(biotypes); + if (biotype == BioType.Unknown) return transcripts; + + foreach (var transcript in transcripts) transcript.BioType = biotype; + logger.Log(transcripts[0].Id, "Normalized biotype"); + return transcripts.Unique(); + } + + private static BioType GetDesiredBioType(ICollection biotypes) + { + if (biotypes.Contains(BioType.misc_RNA)) + { + if (biotypes.Contains(BioType.antisense_RNA)) return BioType.antisense_RNA; + if (biotypes.Contains(BioType.miRNA)) return BioType.miRNA; + if (biotypes.Contains(BioType.pseudogene)) return BioType.pseudogene; + if (biotypes.Contains(BioType.lncRNA)) return BioType.lncRNA; + if (biotypes.Contains(BioType.protein_coding)) return BioType.protein_coding; + if (biotypes.Contains(BioType.rRNA)) return BioType.rRNA; + if (biotypes.Contains(BioType.SRP_RNA)) return BioType.SRP_RNA; + if (biotypes.Contains(BioType.vaultRNA)) return BioType.vaultRNA; + if (biotypes.Contains(BioType.Y_RNA)) return BioType.Y_RNA; + } + + if (biotypes.Contains(BioType.lncRNA)) + { + if (biotypes.Contains(BioType.antisense_RNA)) return BioType.lncRNA; + if (biotypes.Contains(BioType.pseudogene)) return BioType.lncRNA; + } + + if (biotypes.Contains(BioType.mRNA) && biotypes.Contains(BioType.protein_coding)) + return BioType.protein_coding; + + return BioType.Unknown; + } + + public static List FixGeneId(this List transcripts, ILogger logger, + Dictionary idToGenbankEntry, string transcriptId) + { + if (transcripts.Count == 1 || idToGenbankEntry == null || !idToGenbankEntry.TryGetValue(transcriptId, out var genbankEntry)) return transcripts; + + var geneIds = transcripts.GetSet(x => x.Gene.GeneId); + if (geneIds.Count == 1) return transcripts; + + if (!geneIds.Contains(genbankEntry.GeneId)) throw new InvalidDataException($"Could not find the Genbank gene ID ({genbankEntry.GeneId}) within the transcripts."); + + foreach (var transcript in transcripts) transcript.Gene.GeneId = genbankEntry.GeneId; + logger.Log(transcripts[0].Id, "Normalized gene ID"); + return transcripts.Unique(); + } + + public static List UnsupervisedFixGeneId(this List transcripts, + ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var geneIds = transcripts.GetSet(x => x.Gene.GeneId).ToList(); + if (geneIds.Count == 1) return transcripts; + + var geneId = geneIds[0]; + foreach (var transcript in transcripts) transcript.Gene.GeneId = geneId; + logger.Log(transcripts[0].Id, "Normalized gene ID (unsupervised)"); + return transcripts.Unique(); + } + + public static List FixGeneSymbols(this List transcripts, ILogger logger, + Dictionary idToGenbankEntry, string transcriptId) + { + if (transcripts.Count == 1) return transcripts; + + var symbols = transcripts.GetSet(x => x.Gene.Symbol); + if (symbols.Count == 1) return transcripts; + if (symbols.Contains(null)) symbols.Remove(null); + + if (idToGenbankEntry == null || !idToGenbankEntry.TryGetValue(transcriptId, out var genbankEntry)) + return transcripts.UnsupervisedFixGeneSymbols(logger, symbols.ToList()); + + if (!symbols.Contains(genbankEntry.Symbol)) return transcripts.UnsupervisedFixGeneSymbols(logger, symbols.ToList()); + + foreach (var transcript in transcripts) transcript.Gene.Symbol = genbankEntry.Symbol; + logger.Log(transcripts[0].Id, "Normalized gene symbol"); + return transcripts.Unique(); + } + + public static List FixCanonical(this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var canonicals = transcripts.GetSet(x => x.IsCanonical); + if (canonicals.Count == 1) return transcripts; + + foreach (var transcript in transcripts) transcript.IsCanonical = false; + logger.Log(transcripts[0].Id, "Normalized canonical flag"); + return transcripts.Unique(); + } + + public static List FixHgncId(this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var hgncIds = transcripts.GetSet(x => x.Gene.HgncId); + if (hgncIds.Count == 1) return transcripts; + + if (hgncIds.Contains(-1)) hgncIds.Remove(-1); + var hgncId = hgncIds.First(); + + foreach (var transcript in transcripts) transcript.Gene.HgncId = hgncId; + logger.Log(transcripts[0].Id, "Normalized HGNC ID"); + return transcripts.Unique(); + } + + public static List FixGeneStart(this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var geneStarts = transcripts.GetSet(x => x.Gene.Start); + if (geneStarts.Count == 1) return transcripts; + + var transcriptStarts = transcripts.GetSet(x => x.Start).ToArray(); + if (transcriptStarts.Length > 1) return transcripts; + + int closestStart = GetClosest(geneStarts, transcriptStarts[0]); + foreach (var transcript in transcripts) transcript.Gene.Start = closestStart; + logger.Log(transcripts[0].Id, "Normalized gene start"); + return transcripts.Unique(); + } + + public static List FixGeneEnd(this List transcripts, ILogger logger) + { + if (transcripts.Count == 1) return transcripts; + + var geneEnds = transcripts.GetSet(x => x.Gene.End); + if (geneEnds.Count == 1) return transcripts; + + var transcriptEnds = transcripts.GetSet(x => x.End).ToArray(); + if (transcriptEnds.Length > 1) return transcripts; + + int closestEnd = GetClosest(geneEnds, transcriptEnds[0]); + foreach (var transcript in transcripts) transcript.Gene.End = closestEnd; + logger.Log(transcripts[0].Id, "Normalized gene end"); + return transcripts.Unique(); + } + + private static List UnsupervisedFixGeneSymbols(this IReadOnlyList transcripts, + ILogger logger, List symbols) + { + var nonLocGeneSymbols = symbols.FindAll(x => !string.IsNullOrEmpty(x) && !x.StartsWith("LOC")); + var symbol = nonLocGeneSymbols.Count > 0 ? nonLocGeneSymbols[0] : symbols[0]; + + foreach (var transcript in transcripts) transcript.Gene.Symbol = symbol; + logger.Log(transcripts[0].Id, "Normalized gene symbol (unsupervised)"); + return transcripts.Unique(); + } + + private static int GetClosest(IEnumerable values, int targetValue) + { + int bestDelta = int.MaxValue; + int bestValue = -1; + + foreach (var value in values) + { + int delta = Math.Abs(value - targetValue); + if (delta >= bestDelta) continue; + + bestDelta = delta; + bestValue = value; + } + + return bestValue; + } + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptIdFilter.cs b/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptIdFilter.cs new file mode 100644 index 00000000..cf09a447 --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptIdFilter.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public sealed class TranscriptIdFilter + { + private readonly string[] _whitelist; + + public TranscriptIdFilter(Source source) + { + // ReSharper disable once SwitchStatementMissingSomeCases + switch (source) + { + case Source.Ensembl: + _whitelist = new[] { "ENSE0", "ENSG0", "ENSP0", "ENST0" }; + break; + case Source.RefSeq: + _whitelist = new[] { "NG_", "NM_", "NP_", "NR_", "XM_", "XP_", "XR_", "YP_" }; + break; + default: + throw new InvalidDataException($"Unhandled import mode found: {source}"); + } + } + + public bool Pass(MutableTranscript transcript) => _whitelist.Any(prefix => transcript.Id.StartsWith(prefix)) && !transcript.Id.Contains("dupl"); + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptMerger.cs b/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptMerger.cs new file mode 100644 index 00000000..f6dfeecd --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/TranscriptMerger.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genbank; +using CacheUtils.Genes.Utilities; +using VariantAnnotation.Interface; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public static class TranscriptMerger + { + /// + /// separates the transcripts by ID and clusters the transcripts into overlapping + /// islands. From there we can resolve differences and return a unique transcript + /// for each cluster. + /// + public static List Merge(ILogger logger, IEnumerable transcripts, + Dictionary idToGenbankEntry) + { + var idToTranscripts = transcripts.GetMultiValueDict(x => x.Id + "|" + x.Start + "|" + x.End); + var mergedTranscripts = idToTranscripts.Select(kvp => Merge(logger, kvp.Value, idToGenbankEntry)).ToList(); + return mergedTranscripts.OrderBy(x => x.Start).ThenBy(x => x.End).ToList(); + } + + private static MutableTranscript Merge(ILogger logger, IReadOnlyList transcripts, + Dictionary idToGenbankEntry) + { + if (transcripts.Count == 1) return transcripts[0]; + + var transcriptId = transcripts[0].Id; + + var filteredTranscripts = transcripts + .Unique() + .ChooseEditedTranscripts(logger) + .RemoveTranscriptsWithLowestVersion(logger) + .FixCodingRegionCdnaStart(logger, idToGenbankEntry, transcriptId) + .FixCodingRegionCdnaEnd(logger, idToGenbankEntry, transcriptId) + .FixGeneSymbolSource(logger) + .FixBioType(logger) + .FixGeneId(logger, idToGenbankEntry, transcriptId) + .FixCanonical(logger) + .FixHgncId(logger) + .FixGeneStart(logger) + .FixGeneEnd(logger) + .FixCdnaMapExonInconsistency(logger) + .FixGeneSymbols(logger, idToGenbankEntry, transcriptId) + .RemoveTranscriptsWithDifferentExons(logger, idToGenbankEntry, transcriptId) + .UnsupervisedFixGeneId(logger) + .PickSpecificTranscript(logger, transcriptId); + + if (filteredTranscripts.Count == 1) return filteredTranscripts[0]; + + throw new NotImplementedException($"Could not merge down to one transcript: {filteredTranscripts.Count} transcripts ({transcriptId})"); + } + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/VepCacheParser.cs b/CacheUtils/Commands/ParseVepCacheDirectory/VepCacheParser.cs new file mode 100644 index 00000000..c031e9b8 --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/VepCacheParser.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.DataDumperImport.Import; +using CacheUtils.DataDumperImport.IO; +using Compression.Utilities; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public sealed class VepCacheParser + { + private readonly Source _source; + private readonly TranscriptIdFilter _filter; + + public VepCacheParser(Source source) + { + _source = source; + _filter = new TranscriptIdFilter(source); + } + + public (List RegulatoryRegions, List Transcripts) ParseDumpDirectory( + IChromosome chromosome, string dirPath) + { + var regulatoryRegions = ParseRegulatoryFiles(chromosome, dirPath); + var transcripts = ParseTranscriptFiles(chromosome, dirPath); + return (regulatoryRegions, transcripts); + } + + private static List ParseRegulatoryFiles(IChromosome chromosome, string dirPath) + { + var files = Directory.GetFiles(dirPath, "*_reg_regulatory_regions_data_dumper.txt.gz"); + var regulatoryRegions = new List(); + + foreach (string dumpPath in VepRootDirectory.GetSortedFiles(files)) + { + ParseRegulatoryDumpFile(chromosome, dumpPath, regulatoryRegions); + } + + return regulatoryRegions; + } + + private List ParseTranscriptFiles(IChromosome chromosome, string dirPath) + { + var files = Directory.GetFiles(dirPath, "*_transcripts_data_dumper.txt.gz"); + var transcripts = new List(); + + foreach (string dumpPath in VepRootDirectory.GetSortedFiles(files)) + { + //if (!dumpPath.Contains("88000001-89000000_transcripts_data_dumper.txt.gz") && !dumpPath.Contains("89000001-90000000_transcripts_data_dumper.txt.gz")) continue; + ParseTranscriptDumpFile(chromosome, dumpPath, transcripts); + } + + return transcripts; + } + + private static void ParseRegulatoryDumpFile(IChromosome chromosome, string filePath, + ICollection regulatoryRegions) + { + Console.WriteLine("- processing {0}", Path.GetFileName(filePath)); + + using (var reader = new DataDumperReader(GZipUtilities.GetAppropriateReadStream(filePath))) + { + foreach (var ad in reader.GetRootNode().Value.Values) + { + if (!(ad is ObjectKeyValueNode objectKeyValue)) continue; + + foreach (var featureGroup in objectKeyValue.Value.Values) + { + switch (featureGroup.Key) + { + case "MotifFeature": + // not used + break; + case "RegulatoryFeature": + ParseRegulatoryRegions(chromosome, featureGroup, regulatoryRegions); + break; + default: + throw new InvalidDataException("Found an unexpected feature group (" + featureGroup.Key + ") in the regulatory regions file."); + } + } + } + } + } + + private void ParseTranscriptDumpFile(IChromosome chromosome, string filePath, + ICollection transcripts) + { + Console.WriteLine("- processing {0}", Path.GetFileName(filePath)); + + using (var reader = new DataDumperReader(GZipUtilities.GetAppropriateReadStream(filePath))) + { + foreach (var node in reader.GetRootNode().Value.Values) + { + if (!(node is ListObjectKeyValueNode transcriptNodes)) continue; + + foreach (var tNode in transcriptNodes.Values) + { + if (!(tNode is ObjectValueNode transcriptNode)) throw new InvalidOperationException("Expected a transcript object value node, but the current node is not an object value."); + if (transcriptNode.Type != "Bio::EnsEMBL::Transcript") throw new InvalidOperationException($"Expected a transcript node, but the current data type is: [{transcriptNode.Type}]"); + + var transcript = ImportTranscript.Parse(transcriptNode, chromosome, _source); + if (_filter.Pass(transcript)) transcripts.Add(transcript); + } + } + } + } + + private static void ParseRegulatoryRegions(IChromosome chromosome, IImportNode featureGroupNode, + ICollection regulatoryRegions) + { + if (!(featureGroupNode is ListObjectKeyValueNode regulatoryFeatureNodes)) return; + + foreach (var node in regulatoryFeatureNodes.Values) + { + if (!(node is ObjectValueNode regulatoryFeatureNode)) throw new InvalidOperationException("Expected a regulatory region object value node, but the current node is not an object value."); + if (regulatoryFeatureNode.Type != "Bio::EnsEMBL::Funcgen::RegulatoryFeature") throw new InvalidOperationException($"Expected a regulatory region node, but the current data type is: [{regulatoryFeatureNode.Type}]"); + + var regulatoryRegion = ImportRegulatoryFeature.Parse(regulatoryFeatureNode, chromosome); + regulatoryRegions.Add(regulatoryRegion); + } + } + } +} diff --git a/CacheUtils/Commands/ParseVepCacheDirectory/VepRootDirectory.cs b/CacheUtils/Commands/ParseVepCacheDirectory/VepRootDirectory.cs new file mode 100644 index 00000000..abacf158 --- /dev/null +++ b/CacheUtils/Commands/ParseVepCacheDirectory/VepRootDirectory.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CommonUtilities; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Commands.ParseVepCacheDirectory +{ + public sealed class VepRootDirectory + { + private readonly IDictionary _refNameToChromosome; + + public VepRootDirectory(IDictionary refNameToChromosome) + { + _refNameToChromosome = refNameToChromosome; + } + + public Dictionary GetRefIndexToVepDir(string dirPath) + { + var vepDirectories = Directory.GetDirectories(dirPath); + var referenceDict = new Dictionary(); + + foreach (var dir in vepDirectories) + { + var referenceName = Path.GetFileName(dir); + var chromosome = ReferenceNameUtilities.GetChromosome(_refNameToChromosome, referenceName); + if (chromosome.IsEmpty()) continue; + + referenceDict[chromosome.Index] = dir; + } + + return referenceDict; + } + + public static IEnumerable GetSortedFiles(IEnumerable filePaths) + { + var sortedPaths = new SortedDictionary(); + + foreach (string filePath in filePaths) + { + var fileName = Path.GetFileName(filePath); + if (fileName == null) continue; + + int hyphenPos = fileName.IndexOf("-", StringComparison.Ordinal); + if (hyphenPos == -1) throw new InvalidDataException($"Could not find the hyphen in: [{fileName}]"); + + int position = int.Parse(fileName.Substring(0, hyphenPos)); + sortedPaths[position] = filePath; + } + + return sortedPaths.Values.ToArray(); + } + } +} diff --git a/CacheUtils/Commands/RegulatoryGFF/CreateRegulatoryGffMain.cs b/CacheUtils/Commands/RegulatoryGFF/CreateRegulatoryGffMain.cs new file mode 100644 index 00000000..2906c550 --- /dev/null +++ b/CacheUtils/Commands/RegulatoryGFF/CreateRegulatoryGffMain.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; +using CacheUtils.Helpers; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using Compression.Utilities; +using ErrorHandling; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.IO.Caches; +using VariantAnnotation.Providers; + +namespace CacheUtils.Commands.RegulatoryGFF +{ + public static class CreateRegulatoryGffMain + { + private static string _referencePath; + private static string _inputPrefix; + private static string _outputFileName; + + private static ExitCodes ProgramExecution() + { + using (var writer = GZipUtilities.GetStreamWriter(_outputFileName)) + { + var cachePath = CacheConstants.TranscriptPath(_inputPrefix); + var sequenceData = SequenceHelper.GetDictionaries(_referencePath); + + // load the cache + Console.Write("- reading {0}... ", Path.GetFileName(cachePath)); + var cache = TranscriptCacheHelper.GetCache(cachePath, sequenceData.refIndexToChromosome); + Console.WriteLine("found {0:N0} regulatory regions. ", cache.RegulatoryRegionIntervalArrays.Length); + + Console.Write("- writing GFF entries... "); + foreach (var intervalArray in cache.RegulatoryRegionIntervalArrays) foreach (var interval in intervalArray.Array) WriteRegulatoryFeature(writer, interval.Value); + Console.WriteLine("finished."); + } + + return ExitCodes.Success; + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "in|i=", + "input cache {prefix}", + v => _inputPrefix = v + }, + { + "out|o=", + "output {file name}", + v => _outputFileName = v + }, + { + "ref|r=", + "reference {file}", + v => _referencePath = v + } + }; + + var commandLineExample = $"{command} --in --out "; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .HasRequiredParameter(_inputPrefix, "input cache prefix", "--in") + .CheckOutputFilenameSuffix(_outputFileName, ".gz", "GFF", "--out") + .SkipBanner() + .ShowHelpMenu("Outputs regulatory regions in a database.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + + private static void WriteRegulatoryFeature(TextWriter writer, IRegulatoryRegion regulatoryRegion) + { + writer.Write($"{regulatoryRegion.Chromosome.UcscName}\t.\tregulatory feature\t{regulatoryRegion.Start}\t{regulatoryRegion.End}\t.\t.\t.\t"); + WriteGeneralAttributes(writer, regulatoryRegion); + writer.WriteLine(); + } + + private static void WriteGeneralAttributes(TextWriter writer, IRegulatoryRegion regulatoryRegion) + { + if (!regulatoryRegion.Id.IsEmpty()) writer.Write($"regulatory_feature_id \"{regulatoryRegion.Id}\"; "); + writer.Write($"regulatory_feature_type \"{regulatoryRegion.Type}\"; "); + } + } +} diff --git a/CacheUtils/Commands/UniversalGeneArchive/FilePaths.cs b/CacheUtils/Commands/UniversalGeneArchive/FilePaths.cs new file mode 100644 index 00000000..08cbb994 --- /dev/null +++ b/CacheUtils/Commands/UniversalGeneArchive/FilePaths.cs @@ -0,0 +1,24 @@ +namespace CacheUtils.Commands.UniversalGeneArchive +{ + // ReSharper disable UnusedAutoPropertyAccessor.Global + public sealed class FilePaths + { + public string HgncPath { get; set; } + public string GeneInfoPath { get; set; } + public AssemblySpecificPaths GRCh37 { get; set; } + public AssemblySpecificPaths GRCh38 { get; set; } + + // ReSharper disable once ClassNeverInstantiated.Global + public class AssemblySpecificPaths + { + public string ReferencePath { get; set; } + public string AssemblyInfoPath { get; set; } + public string EnsemblCachePath { get; set; } + public string RefSeqCachePath { get; set; } + public string EnsemblGtfPath { get; set; } + public string RefSeqGenomeGffPath { get; set; } + public string RefSeqGffPath { get; set; } + } + } + // ReSharper restore UnusedAutoPropertyAccessor.Global +} diff --git a/CacheUtils/Commands/UniversalGeneArchive/GenbankFile.cs b/CacheUtils/Commands/UniversalGeneArchive/GenbankFile.cs new file mode 100644 index 00000000..2c96ce55 --- /dev/null +++ b/CacheUtils/Commands/UniversalGeneArchive/GenbankFile.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genbank; +using CacheUtils.Utilities; +using Compression.Utilities; +using VariantAnnotation.Interface; + +namespace CacheUtils.Commands.UniversalGeneArchive +{ + public sealed class GenbankFile + { + private readonly ILogger _logger; + private readonly RemoteFile _remoteFile; + public readonly Dictionary GenbankDict; + + public GenbankFile(ILogger logger, int num) + { + _logger = logger; + _remoteFile = new RemoteFile($"RefSeq Genbank {num}", $"ftp://ftp.ncbi.nlm.nih.gov/refseq/H_sapiens/mRNA_Prot/human.{num}.rna.gbff.gz", false); + GenbankDict = new Dictionary(); + } + + public void Download() => _remoteFile.Download(_logger); + + public void Parse() + { + _logger.WriteLine($"- parsing {Path.GetFileName(_remoteFile.FilePath)}"); + + using (var reader = new GenbankReader(GZipUtilities.GetAppropriateStreamReader(_remoteFile.FilePath))) + { + while (true) + { + var entry = reader.GetGenbankEntry(); + if (entry == null) break; + GenbankDict[entry.TranscriptId] = entry; + } + } + } + } +} diff --git a/CacheUtils/Commands/UniversalGeneArchive/UniversalGeneArchiveMain.cs b/CacheUtils/Commands/UniversalGeneArchive/UniversalGeneArchiveMain.cs new file mode 100644 index 00000000..9558a93c --- /dev/null +++ b/CacheUtils/Commands/UniversalGeneArchive/UniversalGeneArchiveMain.cs @@ -0,0 +1,229 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using CacheUtils.Genes; +using CacheUtils.Genes.DataStores; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.IO; +using CacheUtils.Helpers; +using CacheUtils.Utilities; +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using CommandLine.Utilities; +using Compression.FileHandling; +using Compression.Utilities; +using ErrorHandling; +using VariantAnnotation.Providers; +using Microsoft.Extensions.Configuration; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Logger; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Commands.UniversalGeneArchive +{ + public static class UniversalGeneArchiveMain + { + private static string _configPath; + private static string _outputPath; + + private static ExitCodes ProgramExecution() + { + var jsonPath = string.IsNullOrEmpty(_configPath) ? "CacheUtils.dll.gene.json" : _configPath; + var filePaths = GetFilePaths(jsonPath); + var logger = new ConsoleLogger(); + + if (!_outputPath.EndsWith(".gz")) _outputPath += ".gz"; + + DownloadFiles(logger, filePaths); + var ds = LoadDataStores(logger, filePaths); + + var grch37GenesByRef = ds.Assembly37.UpdateHgncIds(ds.Hgnc).MergeByHgnc(true); + var grch38GenesByRef = ds.Assembly38.UpdateHgncIds(ds.Hgnc).MergeByHgnc(false); + + var universalGenes = CombineGenomeAssemblies(logger, grch37GenesByRef, grch38GenesByRef).UpdateGeneSymbols(logger, + ds.Hgnc.HgncIdToSymbol, ds.GeneInfoData.EntrezGeneIdToSymbol, + ds.Assembly38.EnsemblGtf.EnsemblIdToSymbol, ds.Assembly37.RefSeqGff.EntrezGeneIdToSymbol); + + WriteGenes(logger, universalGenes); + + return ExitCodes.Success; + } + + private static void DownloadFiles(ILogger logger, FilePaths filePaths) + { + var fileList = new List(); + AddCommonFiles(fileList, filePaths); + AddGrch37Files(fileList, filePaths.GRCh37); + AddGrch38Files(fileList, filePaths.GRCh38); + + fileList.Execute(logger, "downloads", file => file.Download(logger)); + } + + private static void AddCommonFiles(ICollection fileList, FilePaths filePaths) + { + var hgncFile = new RemoteFile("latest HGNC gene symbols", "ftp://ftp.ebi.ac.uk/pub/databases/genenames/new/tsv/hgnc_complete_set.txt"); + var geneInfoFile = new RemoteFile("latest gene_info", "ftp://ftp.ncbi.nlm.nih.gov/gene/DATA/gene_info.gz"); + + filePaths.HgncPath = hgncFile.FilePath; + filePaths.GeneInfoPath = geneInfoFile.FilePath; + + fileList.Add(hgncFile); + fileList.Add(geneInfoFile); + } + + private static void AddGrch37Files(ICollection fileList, FilePaths.AssemblySpecificPaths grch37) + { + var assemblyFile = new RemoteFile("assembly report (GRCh37.p13)", "ftp://ftp.ncbi.nih.gov/genomes/refseq/vertebrate_mammalian/Homo_sapiens/all_assembly_versions/GCF_000001405.25_GRCh37.p13/GCF_000001405.25_GRCh37.p13_assembly_report.txt", false); + var ensemblGtfFile = new RemoteFile("Ensembl 75 GTF (GRCh37)", "ftp://ftp.ensembl.org/pub/release-75/gtf/homo_sapiens/Homo_sapiens.GRCh37.75.gtf.gz", false); + var refSeqGenomeGffFile = new RemoteFile("RefSeq genomic GFF (GRCh37.p13)", "ftp://ftp.ncbi.nih.gov/genomes/refseq/vertebrate_mammalian/Homo_sapiens/all_assembly_versions/GCF_000001405.25_GRCh37.p13/GCF_000001405.25_GRCh37.p13_genomic.gff.gz", false); + var refSeqGffFile = new RemoteFile("RefSeq GFF3 (GRCh37.p13)", "ftp://ftp.ncbi.nih.gov/genomes/H_sapiens/ARCHIVE/ANNOTATION_RELEASE.105/GFF/ref_GRCh37.p13_top_level.gff3.gz", false); + + grch37.AssemblyInfoPath = assemblyFile.FilePath; + grch37.EnsemblGtfPath = ensemblGtfFile.FilePath; + grch37.RefSeqGenomeGffPath = refSeqGenomeGffFile.FilePath; + grch37.RefSeqGffPath = refSeqGffFile.FilePath; + + fileList.Add(assemblyFile); + fileList.Add(ensemblGtfFile); + fileList.Add(refSeqGenomeGffFile); + fileList.Add(refSeqGffFile); + } + + private static void AddGrch38Files(ICollection fileList, FilePaths.AssemblySpecificPaths grch38) + { + var assemblyFile = new RemoteFile("assembly report (GRCh38.p11)", "ftp://ftp.ncbi.nih.gov/genomes/refseq/vertebrate_mammalian/Homo_sapiens/all_assembly_versions/GCF_000001405.37_GRCh38.p11/GCF_000001405.37_GRCh38.p11_assembly_report.txt", false); + var ensemblGtfFile = new RemoteFile("Ensembl 90 GTF (GRCh38)", "ftp://ftp.ensembl.org/pub/release-90/gtf/homo_sapiens/Homo_sapiens.GRCh38.90.gtf.gz", false); + var refSeqGenomeGffFile = new RemoteFile("RefSeq genomic GFF (GRCh38.p11)", "ftp://ftp.ncbi.nih.gov/genomes/refseq/vertebrate_mammalian/Homo_sapiens/all_assembly_versions/GCF_000001405.37_GRCh38.p11/GCF_000001405.37_GRCh38.p11_genomic.gff.gz", false); + var refSeqGffFile = new RemoteFile("RefSeq GFF3 (GRCh38.p7)", "ftp://ftp.ncbi.nih.gov/genomes/H_sapiens/GFF/ref_GRCh38.p7_top_level.gff3.gz", false); + + grch38.AssemblyInfoPath = assemblyFile.FilePath; + grch38.EnsemblGtfPath = ensemblGtfFile.FilePath; + grch38.RefSeqGenomeGffPath = refSeqGenomeGffFile.FilePath; + grch38.RefSeqGffPath = refSeqGffFile.FilePath; + + fileList.Add(assemblyFile); + fileList.Add(ensemblGtfFile); + fileList.Add(refSeqGenomeGffFile); + fileList.Add(refSeqGffFile); + } + + private static (GeneInfoData GeneInfoData, AssemblyDataStore Assembly37, AssemblyDataStore Assembly38, Hgnc Hgnc) + LoadDataStores(ILogger logger, FilePaths filePaths) + { + logger.Write("- loading datastores... "); + var loadBenchmark = new Benchmark(); + + var dicts = GetSequenceDictionaries(filePaths.GRCh38.ReferencePath, filePaths.GRCh37.AssemblyInfoPath, filePaths.GRCh38.AssemblyInfoPath); + + var geneInfoData = GeneInfoData.Create(filePaths.GeneInfoPath); + var dataStore37 = AssemblyDataStore.Create("GRCh37", logger, filePaths.GRCh37, dicts.RefNameToChromosome, dicts.Accession37); + var dataStore38 = AssemblyDataStore.Create("GRCh38", logger, filePaths.GRCh38, dicts.RefNameToChromosome, dicts.Accession38); + var hgnc = Hgnc.Create(filePaths.HgncPath, dicts.RefNameToChromosome); + + logger.WriteLine($"{Benchmark.ToHumanReadable(loadBenchmark.GetElapsedTime())}"); + + return (geneInfoData, dataStore37, dataStore38, hgnc); + } + + private static (IDictionary RefNameToChromosome, IDictionary + Accession37, IDictionary Accession38) GetSequenceDictionaries(string referencePath, + string assemblyInfo37Path, string assemblyInfo38Path) + { + var (_, refNameToChromosome, _) = SequenceHelper.GetDictionaries(referencePath); + var accession37Dict = AssemblyReader.GetAccessionToChromosome(GZipUtilities.GetAppropriateStreamReader(assemblyInfo37Path), refNameToChromosome); + var accession38Dict = AssemblyReader.GetAccessionToChromosome(GZipUtilities.GetAppropriateStreamReader(assemblyInfo38Path), refNameToChromosome); + return (refNameToChromosome, accession37Dict, accession38Dict); + } + + private static UgaGene[] CombineGenomeAssemblies(ILogger logger, Dictionary> genesByRef37, Dictionary> genesByRef38) + { + logger.WriteLine(); + logger.WriteLine("*** Global ***"); + logger.Write("- combining genes from GRCh37 and GRCh38... "); + var combinedGenes = UgaAssemblyCombiner.Combine(genesByRef37, genesByRef38); + logger.WriteLine($"{combinedGenes.Length} genes."); + + return combinedGenes; + } + + private static UgaGene[] UpdateGeneSymbols(this UgaGene[] genes, ILogger logger, + Dictionary hgncIdToSymbol, Dictionary entrezGeneIdToSymbol, + Dictionary ensemblIdToSymbol, Dictionary refseqGeneIdToSymbol) + { + var updater = new GeneSymbolUpdater(logger, hgncIdToSymbol, entrezGeneIdToSymbol, ensemblIdToSymbol, refseqGeneIdToSymbol); + updater.Update(genes); + return genes; + } + + private static void WriteGenes(ILogger logger, UgaGene[] genes) + { + logger.Write($"- writing genes to {Path.GetFileName(_outputPath)}... "); + + using (var stream = new BlockGZipStream(FileUtilities.GetCreateStream(_outputPath), CompressionMode.Compress)) + using (var writer = new UgaGeneWriter(stream)) + { + writer.Write(genes); + } + + logger.WriteLine("finished"); + } + + private static FilePaths GetFilePaths(string jsonPath) + { + var builder = new ConfigurationBuilder(); + builder.AddJsonFile(jsonPath); + + var configuration = builder.Build(); + + var filePaths = new FilePaths(); + configuration.Bind(filePaths); + + CheckPaths(filePaths.GRCh37); + CheckPaths(filePaths.GRCh38); + + return filePaths; + } + + private static void CheckPath(string filePath, string description) + { + if (string.IsNullOrEmpty(filePath)) throw new InvalidDataException($"No value was found for the {description} key."); + if (!File.Exists(filePath)) throw new FileNotFoundException($"Unable to find the following file: {filePath}"); + } + + private static void CheckPaths(FilePaths.AssemblySpecificPaths paths) + { + CheckPath(paths.EnsemblCachePath, "Ensembl intermediate cache"); + CheckPath(paths.RefSeqCachePath, "RefSeq intermediate cache"); + CheckPath(paths.ReferencePath, "reference"); + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "config|c=", + "config {path}", + v => _configPath = v + }, + { + "out|o=", + "output {path}", + v => _outputPath = v + } + }; + + var commandLineExample = $"{command} [options]"; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .HasRequiredParameter(_outputPath, "output", "--out") + .SkipBanner() + .ShowHelpMenu("Creates the universal gene archive", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/GenomeSymbolSource.cs b/CacheUtils/DataDumperImport/DataStructures/GenomeSymbolSource.cs new file mode 100644 index 00000000..d9461887 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/GenomeSymbolSource.cs @@ -0,0 +1,18 @@ +namespace CacheUtils.DataDumperImport.DataStructures +{ + public enum GeneSymbolSource : byte + { + // ReSharper disable InconsistentNaming + Unknown, + CloneBasedEnsemblGene, + CloneBasedVegaGene, + EntrezGene, + HGNC, + LRG, + NCBI, + miRBase, + RFAM, + UniProtGeneName + // ReSharper restore InconsistentNaming + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/IImportNode.cs b/CacheUtils/DataDumperImport/DataStructures/Import/IImportNode.cs new file mode 100644 index 00000000..c29fd33b --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/IImportNode.cs @@ -0,0 +1,9 @@ +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public interface IImportNode + { + string Key { get; } + } + + public interface IListMember : IImportNode { } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensions.cs b/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensions.cs new file mode 100644 index 00000000..141c7f07 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensions.cs @@ -0,0 +1,44 @@ +using System.IO; + +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public static class ImportNodeExtensions + { + public static int GetInt32(this IImportNode node) + { + var s = GetString(node); + if (s == null) return -1; + + if (!int.TryParse(s, out var ret)) + { + throw new InvalidDataException($"Unable to convert the string ({s}) to an integer."); + } + + return ret; + } + + public static bool GetBool(this IImportNode node) + { + int num = GetInt32(node); + return num == 1; + } + + public static string GetString(this IImportNode node) + { + if (!(node is StringKeyValueNode stringKeyValue)) + { + throw new InvalidDataException($"Unable to convert the AbstractData type to a StringKeyValue type: [{node.Key}]"); + } + + var s = stringKeyValue.Value; + if (s == "" || s == "-") s = null; + return s; + } + + public static bool IsUndefined(this IImportNode node) + { + if (!(node is StringKeyValueNode stringKeyValue)) return false; + return stringKeyValue.Value == null; + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/ListObjectKeyValueNode.cs b/CacheUtils/DataDumperImport/DataStructures/Import/ListObjectKeyValueNode.cs new file mode 100644 index 00000000..e42be43e --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/ListObjectKeyValueNode.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public sealed class ListObjectKeyValueNode : IImportNode + { + public string Key { get; } + public List Values { get; } = new List(); + + public ListObjectKeyValueNode(string key) => Key = key; + public void Add(IListMember node) => Values.Add(node); + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/ObjectKeyValueNode.cs b/CacheUtils/DataDumperImport/DataStructures/Import/ObjectKeyValueNode.cs new file mode 100644 index 00000000..a0c3c913 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/ObjectKeyValueNode.cs @@ -0,0 +1,14 @@ +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public sealed class ObjectKeyValueNode : IImportNode + { + public string Key { get; } + public ObjectValueNode Value { get; } + + public ObjectKeyValueNode(string key, ObjectValueNode value) + { + Key = key; + Value = value; + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/ObjectValueNode.cs b/CacheUtils/DataDumperImport/DataStructures/Import/ObjectValueNode.cs new file mode 100644 index 00000000..6a840938 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/ObjectValueNode.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public sealed class ObjectValueNode : IListMember + { + public string Type { get; } + public string Key { get; } + public List Values { get; } + + internal ObjectValueNode(string type, List values) + { + Key = null; + Type = type; + Values = values; + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/StringKeyValueNode.cs b/CacheUtils/DataDumperImport/DataStructures/Import/StringKeyValueNode.cs new file mode 100644 index 00000000..0c1a2fac --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/StringKeyValueNode.cs @@ -0,0 +1,14 @@ +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public sealed class StringKeyValueNode : IImportNode + { + public string Key { get; } + public string Value { get; } + + public StringKeyValueNode(string key, string value) + { + Key = key; + Value = value; + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Import/StringValueNode.cs b/CacheUtils/DataDumperImport/DataStructures/Import/StringValueNode.cs new file mode 100644 index 00000000..40d21494 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Import/StringValueNode.cs @@ -0,0 +1,8 @@ +namespace CacheUtils.DataDumperImport.DataStructures.Import +{ + public sealed class StringValueNode : IListMember + { + public string Key { get; } + public StringValueNode(string key) => Key = key; + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableExon.cs b/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableExon.cs new file mode 100644 index 00000000..bc56c000 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableExon.cs @@ -0,0 +1,41 @@ +using System; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.DataDumperImport.DataStructures.Mutable +{ + public sealed class MutableExon : IEquatable + { + private readonly IChromosome _chromosome; + public readonly int Start; + public readonly int End; + public readonly int Phase; + + public MutableExon(IChromosome chromosome, int start, int end, int phase) + { + _chromosome = chromosome; + Start = start; + End = end; + Phase = phase; + } + + public bool Equals(MutableExon other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return _chromosome.Index == other._chromosome.Index && Start == other.Start && End == other.End && + Phase == other.Phase; + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = _chromosome.Index.GetHashCode(); + hashCode = (hashCode * 397) ^ Start; + hashCode = (hashCode * 397) ^ End; + hashCode = (hashCode * 397) ^ Phase.GetHashCode(); + return hashCode; + } + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableGene.cs b/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableGene.cs new file mode 100644 index 00000000..985458f4 --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableGene.cs @@ -0,0 +1,81 @@ +using System; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.DataDumperImport.DataStructures.Mutable +{ + public sealed class MutableGene : IEquatable, IFlatGene + { + public IChromosome Chromosome { get; set; } + public int Start { get; set; } + public int End { get; set; } + public bool OnReverseStrand { get; } + public string GeneId { get; set; } + public string Symbol { get; set; } + public int HgncId { get; set; } + public GeneSymbolSource SymbolSource { get; set; } + + public MutableGene(IChromosome chromosome, int start, int end, bool onReverseStrand, string symbol, + GeneSymbolSource symbolSource, string geneId, int hgncId) + { + Chromosome = chromosome; + Start = start; + End = end; + OnReverseStrand = onReverseStrand; + Symbol = symbol; + SymbolSource = symbolSource; + GeneId = geneId; + HgncId = hgncId; + } + + public override string ToString() + { + var strand = OnReverseStrand ? "R" : "F"; + return $"{GeneId}: {Chromosome.UcscName} {Start}-{End} {strand} symbol: {Symbol} ({SymbolSource}), HGNC ID: {HgncId}"; + } + + public bool Equals(MutableGene other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return Chromosome.Index == other.Chromosome.Index && + Start == other.Start && + End == other.End && + OnReverseStrand == other.OnReverseStrand && + Symbol == other.Symbol && + GeneId == other.GeneId; + } + + public override int GetHashCode() + { + unchecked + { + // ReSharper disable NonReadonlyMemberInGetHashCode + var hashCode = Chromosome.Index.GetHashCode(); + hashCode = (hashCode * 397) ^ Start; + hashCode = (hashCode * 397) ^ End; + hashCode = (hashCode * 397) ^ OnReverseStrand.GetHashCode(); + hashCode = (hashCode * 397) ^ Symbol.GetHashCode(); + hashCode = (hashCode * 397) ^ GeneId.GetHashCode(); + // ReSharper restore NonReadonlyMemberInGetHashCode + return hashCode; + } + } + + public MutableGene Clone() => new MutableGene(Chromosome, Start, End, OnReverseStrand, Symbol, SymbolSource, + GeneId, HgncId); + + public UgaGene ToUgaGene(bool isGrch37) + { + var (ensemblGeneId, entrezGeneId) = GeneId.StartsWith("ENSG") ? (GeneId, null as string) : (null, GeneId); + + IInterval interval = new Interval(Start, End); + var (grch37, grch38) = isGrch37 ? (interval, null as IInterval) : (null, interval); + + return new UgaGene(Chromosome, grch37, grch38, OnReverseStrand, entrezGeneId, ensemblGeneId, Symbol, + HgncId); + } + } +} diff --git a/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableTranscript.cs b/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableTranscript.cs new file mode 100644 index 00000000..09c105dd --- /dev/null +++ b/CacheUtils/DataDumperImport/DataStructures/Mutable/MutableTranscript.cs @@ -0,0 +1,114 @@ +using System; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.DataDumperImport.DataStructures.Mutable +{ + public sealed class MutableTranscript : IEquatable + { + public readonly IChromosome Chromosome; + public readonly int Start; + public readonly int End; + public readonly string Id; + public readonly byte Version; + public readonly string CcdsId; + public readonly string RefSeqId; + public readonly Source Source; + public readonly MutableGene Gene; + public readonly IInterval[] MicroRnas; + public readonly bool CdsStartNotFound; + public readonly bool CdsEndNotFound; + public readonly int[] SelenocysteinePositions; + public readonly int StartExonPhase; + public readonly IRnaEdit[] RnaEdits; + public readonly ICdnaCoordinateMap CodingRegion; + public readonly string ProteinId; + public readonly byte ProteinVersion; + public readonly string PeptideSequence; + public readonly MutableExon[] Exons; + public readonly int TotalExonLength; + public readonly IInterval[] Introns; + public readonly string TranslateableSequence; + public readonly ICdnaCoordinateMap[] CdnaMaps; + + // mutable + public BioType BioType; + public bool IsCanonical; + public Gene UpdatedGene; + + public readonly string SiftData; + public readonly string PolyphenData; + public int SiftIndex = -1; + public int PolyPhenIndex = -1; + + public MutableTranscript(IChromosome chromosome, int start, int end, string id, byte version, string ccdsId, + string refSeqId, BioType bioType, bool isCanonical, CdnaCoordinateMap codingRegion, string proteinId, + byte proteinVersion, string peptideSequence, Source source, MutableGene gene, MutableExon[] exons, + int startExonPhase, int totalExonLength, IInterval[] introns, ICdnaCoordinateMap[] cdnaMaps, + string siftData, string polyphenData, string translateableSequence, IInterval[] microRnas, + bool cdsStartNotFound, bool cdsEndNotFound, int[] selenocysteinePositions, IRnaEdit[] rnaEdits) + { + Chromosome = chromosome; + Start = start; + End = end; + Id = id; + Version = version; + CcdsId = ccdsId; + RefSeqId = refSeqId; + BioType = bioType; + IsCanonical = isCanonical; + CodingRegion = codingRegion; + ProteinId = proteinId; + ProteinVersion = proteinVersion; + PeptideSequence = peptideSequence; + Source = source; + Gene = gene; + Exons = exons; + StartExonPhase = startExonPhase; + TotalExonLength = totalExonLength; + Introns = introns; + CdnaMaps = cdnaMaps; + SiftData = siftData; + PolyphenData = polyphenData; + TranslateableSequence = translateableSequence; + MicroRnas = microRnas; + CdsStartNotFound = cdsStartNotFound; + CdsEndNotFound = cdsEndNotFound; + SelenocysteinePositions = selenocysteinePositions; + RnaEdits = rnaEdits; + } + + public bool Equals(MutableTranscript other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return Chromosome.Index == other.Chromosome.Index && + Start == other.Start && + End == other.End && + Id == other.Id && + Version == other.Version && + BioType == other.BioType && + Source == other.Source; + } + + public override int GetHashCode() + { + unchecked + { + // ReSharper disable NonReadonlyMemberInGetHashCode + var hashCode = Chromosome.Index.GetHashCode(); + hashCode = (hashCode * 397) ^ Start; + hashCode = (hashCode * 397) ^ End; + hashCode = (hashCode * 397) ^ Id.GetHashCode(); + hashCode = (hashCode * 397) ^ Version.GetHashCode(); + hashCode = (hashCode * 397) ^ (int) BioType; + hashCode = (hashCode * 397) ^ (int) Source; + return hashCode; + // ReSharper restore NonReadonlyMemberInGetHashCode + } + } + } +} diff --git a/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTree.cs b/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTree.cs new file mode 100644 index 00000000..ae581e51 --- /dev/null +++ b/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTree.cs @@ -0,0 +1,103 @@ +using System; +using System.Linq; +using CacheUtils.DataDumperImport.IO; + +namespace CacheUtils.DataDumperImport.FauxRegex +{ + internal static class RegexDecisionTree + { + internal static (EntryType Type, string Key, string Value) GetEntryType(string s) + { + s = s.Trim().TrimEnd(','); + + int fatArrowPos = s.IndexOf("=>", StringComparison.Ordinal); + return fatArrowPos != -1 + ? GetEntryTypeFatArrow(s, fatArrowPos) + : GetEntryTypeNoArrow(s); + } + + private static (EntryType Type, string Key, string Value) GetEntryTypeNoArrow(string s) + { + int varPos = s.IndexOf("$VAR", StringComparison.Ordinal); + return varPos != -1 ? GetEntryTypeVar(s) : GetEntryTypeNoVar(s); + } + + private static (EntryType Type, string Key, string Value) GetEntryTypeNoVar(string s) + { + s = s.TrimEnd(';'); + + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (s == "}") return (EntryType.EndBraces, null, null); + if (s == "bless( {") return (EntryType.OpenBraces, null, null); + + int endBracePos = s.IndexOf("}, 'Bio::", StringComparison.Ordinal); + if (endBracePos != -1) return GetEntryTypeDataPos(s, endBracePos + 4); + + s = s.Trim('\''); + if (OnlyDigits(s)) return (EntryType.DigitKey, s, null); + + throw new NotImplementedException($"Unable to match the non-$VAR regexes: [{s}]"); + } + + private static (EntryType Type, string Key, string Value) GetEntryTypeDataPos(string s, + int afterFirstQuote) + { + return (EntryType.EndBracesWithDataType, GetForwardString(s, afterFirstQuote), null); + } + + private static (EntryType Type, string Key, string Value) GetEntryTypeVar(string s) + { + if (!s.EndsWith(" = {")) throw new NotImplementedException("Unable to match the $VAR regexes: [{s}]"); + + int spacePos = s.IndexOf(' '); + return (EntryType.RootObjectKeyValue, s.Substring(0, spacePos), null); + } + + private static (EntryType, string Key, string Value) GetEntryTypeFatArrow(string s, int fatArrowPos) + { + var key = GetKey(s, fatArrowPos - 2); + + int firstPosAfterFatArrow = fatArrowPos + 3; + if (s[firstPosAfterFatArrow] == '\'') return GetEntryTypeStringKeyValue(s, firstPosAfterFatArrow + 1, key); + if (s[s.Length - 1] == '{') return (EntryType.ObjectKeyValue, key, null); + + var afterFatArrow = s.Substring(firstPosAfterFatArrow); + + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (afterFatArrow == "undef") return (EntryType.UndefKeyValue, key, null); + if (afterFatArrow == "{}") return (EntryType.EmptyValueKeyValue, key, null); + if (afterFatArrow == "[]") return (EntryType.EmptyListKeyValue, key, null); + if (afterFatArrow.StartsWith("$VAR")) return (EntryType.ReferenceStringKeyValue, key, afterFatArrow); + + if (s[firstPosAfterFatArrow] == '[') return (EntryType.ListObjectKeyValue, key, null); + if (OnlyDigits(afterFatArrow)) return (EntryType.DigitKeyValue, key, afterFatArrow); + + throw new NotImplementedException(); + } + + private static (EntryType, string Key, string Value) GetEntryTypeStringKeyValue(string s, int afterFirstQuote, string key) + { + int secondQuotePos = s.IndexOf('\'', afterFirstQuote); + + return secondQuotePos == -1 + ? (EntryType.MultiLineKeyValue, key, s.Substring(afterFirstQuote)) + : (EntryType.StringKeyValue, key, s.Substring(afterFirstQuote, + secondQuotePos - afterFirstQuote)); + } + + private static string GetKey(string s, int secondQuotePos) + { + int afterFirstQuote = s.LastIndexOf('\'', secondQuotePos - 1) + 1; + return s.Substring(afterFirstQuote, secondQuotePos - afterFirstQuote); + } + + private static string GetForwardString(string s, int afterFirstQuote) + { + int secondQuotePos = s.IndexOf('\'', afterFirstQuote); + var result = s.Substring(afterFirstQuote, secondQuotePos - afterFirstQuote); + return result; + } + + internal static bool OnlyDigits(string s) => s.All(c => char.IsDigit(c) || c == '-'); + } +} diff --git a/CacheUtils/DataDumperImport/IO/DataDumperReader.cs b/CacheUtils/DataDumperImport/IO/DataDumperReader.cs new file mode 100644 index 00000000..411e9511 --- /dev/null +++ b/CacheUtils/DataDumperImport/IO/DataDumperReader.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.FauxRegex; + +namespace CacheUtils.DataDumperImport.IO +{ + public sealed class DataDumperReader : IDisposable + { + private readonly StreamReader _reader; + private readonly StringBuilder _sb = new StringBuilder(); + + public DataDumperReader(Stream stream) => _reader = new StreamReader(stream); + + private string GetNextLine() => _reader.ReadLine(); + + public ObjectKeyValueNode GetRootNode() + { + string line = GetNextLine(); + if (line == null) throw new InvalidDataException("Expected a root object node, but no data was found."); + + var results = RegexDecisionTree.GetEntryType(line); + if (results.Type != EntryType.RootObjectKeyValue) throw new InvalidDataException($"Expected a root object node, but found a {results.Type} node."); + + return new ObjectKeyValueNode(results.Key, GetObjectValue()); + } + + private static StringValueNode GetDigitKey(string key) => new StringValueNode(key); + + private ListObjectKeyValueNode GetListObjectKeyValue(string key) + { + var listObjectKeyValue = new ListObjectKeyValueNode(key); + + while (true) + { + string line = GetNextLine().Trim().TrimEnd(','); + if (line == "]") break; + + var results = RegexDecisionTree.GetEntryType(line); + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (results.Type) + { + case EntryType.OpenBraces: + listObjectKeyValue.Add(GetObjectValue()); + break; + case EntryType.DigitKey: + listObjectKeyValue.Add(GetDigitKey(line)); + break; + default: + throw new InvalidDataException($"Unhandled entry type encountered: {results.Type}"); + } + } + + return listObjectKeyValue; + } + + private StringKeyValueNode GetMultiLineKeyValue(string key, string value) + { + _sb.Clear(); + _sb.Append(value); + + while (true) + { + string line = GetNextLine().Trim(); + if (line.StartsWith("\'")) break; + _sb.Append(' '); + _sb.Append(line); + } + + return new StringKeyValueNode(key, _sb.ToString()); + } + + private ObjectValueNode GetObjectValue() + { + string type = "(unknown)"; + var nodes = new List(); + + while (true) + { + string line = GetNextLine(); + var results = RegexDecisionTree.GetEntryType(line); + + if (results.Type == EntryType.EndBraces || results.Type == EntryType.EndBracesWithDataType) + { + if (results.Type == EntryType.EndBracesWithDataType) type = results.Key; + break; + } + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (results.Type) + { + case EntryType.ObjectKeyValue: + nodes.Add(new ObjectKeyValueNode(results.Key, GetObjectValue())); + break; + case EntryType.ListObjectKeyValue: + nodes.Add(GetListObjectKeyValue(results.Key)); + break; + case EntryType.DigitKeyValue: + case EntryType.StringKeyValue: + case EntryType.ReferenceStringKeyValue: + nodes.Add(new StringKeyValueNode(results.Key, results.Value)); + break; + case EntryType.UndefKeyValue: + case EntryType.EmptyListKeyValue: + case EntryType.EmptyValueKeyValue: + nodes.Add(new StringKeyValueNode(results.Key, null)); + break; + case EntryType.MultiLineKeyValue: + nodes.Add(GetMultiLineKeyValue(results.Key, results.Value)); + break; + default: + throw new InvalidDataException($"Unhandled entry type encountered in GetObjectValue: {results.Type}: [{line}]"); + } + } + + return new ObjectValueNode(type, nodes); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/DataDumperImport/IO/EntryType.cs b/CacheUtils/DataDumperImport/IO/EntryType.cs new file mode 100644 index 00000000..0627c16f --- /dev/null +++ b/CacheUtils/DataDumperImport/IO/EntryType.cs @@ -0,0 +1,20 @@ +namespace CacheUtils.DataDumperImport.IO +{ + internal enum EntryType + { + DigitKeyValue, + DigitKey, + EmptyListKeyValue, + EmptyValueKeyValue, + EndBraces, + EndBracesWithDataType, + ListObjectKeyValue, + MultiLineKeyValue, + ObjectKeyValue, + OpenBraces, + ReferenceStringKeyValue, + RootObjectKeyValue, + StringKeyValue, + UndefKeyValue + } +} \ No newline at end of file diff --git a/CacheUtils/DataDumperImport/Import/Attribute.cs b/CacheUtils/DataDumperImport/Import/Attribute.cs new file mode 100644 index 00000000..d64eca75 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/Attribute.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class Attribute + { + private static readonly HashSet KnownKeys; + private static readonly Regex RangeRegex; + + static Attribute() + { + KnownKeys = new HashSet + { + ImportKeys.Name, + ImportKeys.Description, + ImportKeys.Code, + ImportKeys.Value + }; + + RangeRegex = new Regex("(\\d+)-(\\d+)", RegexOptions.Compiled); + } + + /// + /// returns an array of miRNAs given a list of ObjectValues (AbstractData) + /// + public static (IInterval[] MicroRnas, IRnaEdit[] RnaEdits, bool CdsStartNotFound, bool CdsEndNotFound) ParseList( + IEnumerable importNodes) + { + var microRnaList = new List(); + var rnaEditList = new List(); + bool cdsStartNotFound = false; + bool cdsEndNotFound = false; + + foreach (var node in importNodes) + { + if (!(node is ObjectValueNode objectValue)) + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectValue: [{node.GetType()}]"); + + (var key, var value) = ParseKeyValue(objectValue); + if (key == null) continue; + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (key) + { + case "miRNA": + microRnaList.Add(GetInterval(value)); + break; + case "_rna_edit": + rnaEditList.Add(GetRnaEdit(value)); + break; + case "cds_start_NF": + cdsStartNotFound = true; + break; + case "cds_end_NF": + cdsEndNotFound = true; + break; + } + } + + var microRnas = microRnaList.Count == 0 ? null : microRnaList.ToArray(); + var rnaEdits = rnaEditList.Count == 0 ? null : rnaEditList.ToArray(); + return (microRnas, rnaEdits, cdsStartNotFound, cdsEndNotFound); + } + + private static IInterval GetInterval(string s) + { + var rangeMatch = RangeRegex.Match(s); + if (!rangeMatch.Success) throw new InvalidDataException($"Unable to convert the Attribute to a miRNA object. The value string failed the regex: {s}"); + + int start = int.Parse(rangeMatch.Groups[1].Value); + int end = int.Parse(rangeMatch.Groups[2].Value); + + return new Interval(start, end); + } + + private static RnaEdit GetRnaEdit(string s) + { + var cols = s.Split(' '); + if (cols.Length != 3) throw new InvalidDataException($"Expected 3 columns but found {cols.Length} when parsing RNA edit"); + + int start = int.Parse(cols[0]); + int end = int.Parse(cols[1]); + string bases = cols[2]; + + return new RnaEdit(start, end, bases); + } + + private static (string Key, string Value) ParseKeyValue(ObjectValueNode objectValue) + { + string key = null; + string value = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper attribute object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Name: + case ImportKeys.Description: + // not used + break; + case ImportKeys.Code: + key = node.GetString(); + break; + case ImportKeys.Value: + value = node.GetString(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return (key, value); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportExon.cs b/CacheUtils/DataDumperImport/Import/ImportExon.cs new file mode 100644 index 00000000..2c769622 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportExon.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportExon + { + private static readonly HashSet KnownKeys; + + static ImportExon() + { + KnownKeys = new HashSet + { + ImportKeys.End, + ImportKeys.EndPhase, + ImportKeys.Phase, + ImportKeys.StableId, + ImportKeys.Start, + ImportKeys.Strand + }; + } + + /// + /// returns a new exon given an ObjectValue + /// + public static MutableExon Parse(ObjectValueNode objectValue, IChromosome currentChromosome) + { + int start = -1; + int end = -1; + int phase = int.MinValue; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper mapper object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Strand: + case ImportKeys.StableId: + case ImportKeys.EndPhase: + // not used + break; + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.Phase: + phase = node.GetInt32(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return new MutableExon(currentChromosome, start, end, phase); + } + + /// + /// returns an array of exons given a list of ObjectValues (AbstractData) + /// + public static MutableExon[] ParseList(List abstractDataList, IChromosome chromosome) + { + var exons = new MutableExon[abstractDataList.Count]; + + for (int exonIndex = 0; exonIndex < abstractDataList.Count; exonIndex++) + { + if (abstractDataList[exonIndex] is ObjectValueNode objectValue) + { + exons[exonIndex] = Parse(objectValue, chromosome); + } + else + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectValue: [{abstractDataList[exonIndex].GetType()}]"); + } + } + + return exons; + } + } +} \ No newline at end of file diff --git a/CacheUtils/DataDumperImport/Import/ImportGene.cs b/CacheUtils/DataDumperImport/Import/ImportGene.cs new file mode 100644 index 00000000..d2e4a0b2 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportGene.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.Utilities; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportGene + { + private static readonly HashSet KnownKeys; + + static ImportGene() + { + KnownKeys = new HashSet + { + ImportKeys.End, + ImportKeys.StableId, + ImportKeys.Start, + ImportKeys.Strand + }; + } + + /// + /// returns a new gene given an ObjectValue + /// + public static (int Start, int End, string Id, bool OnReverseStrand) Parse(ObjectValueNode objectValue) + { + int start = -1; + int end = -1; + string stableId = null; + bool onReverseStrand = false; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper gene object: {node.Key}"); + } + + // handle each key + switch (node.Key) + { + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.StableId: + stableId = node.GetString(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + case ImportKeys.Strand: + onReverseStrand = TranscriptUtilities.GetStrand(node); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return (start, end, stableId, onReverseStrand); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportIntron.cs b/CacheUtils/DataDumperImport/Import/ImportIntron.cs new file mode 100644 index 00000000..4e9a458c --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportIntron.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportIntron + { + private static readonly HashSet KnownKeys; + + static ImportIntron() + { + KnownKeys = new HashSet + { + ImportKeys.Analysis, + ImportKeys.Adaptor, + ImportKeys.DbId, + ImportKeys.End, + ImportKeys.Next, + ImportKeys.Prev, + ImportKeys.SeqName, + ImportKeys.Slice, + ImportKeys.Start, + ImportKeys.Strand + }; + } + + /// + /// returns a new exon given an ObjectValue + /// + private static IInterval Parse(ObjectValueNode objectValue) + { + int start = -1; + int end = -1; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper mapper object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Analysis: + case ImportKeys.Adaptor: + case ImportKeys.DbId: + case ImportKeys.Next: + case ImportKeys.Prev: + case ImportKeys.SeqName: + case ImportKeys.Strand: + case ImportKeys.Slice: + // not used + break; + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return new Interval(start, end); + } + + /// + /// parses the relevant data from each intron object + /// + public static IInterval[] ParseList(List members) + { + var introns = new IInterval[members.Count]; + + for (int intronIndex = 0; intronIndex < members.Count; intronIndex++) + { + if (!(members[intronIndex] is ObjectValueNode objectValue)) + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectValue: [{members[intronIndex].GetType()}]"); + } + + introns[intronIndex] = Parse(objectValue); + } + + return introns; + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportKeys.cs b/CacheUtils/DataDumperImport/Import/ImportKeys.cs new file mode 100644 index 00000000..98922ecc --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportKeys.cs @@ -0,0 +1,113 @@ +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportKeys + { + internal const string Adaptor = "adaptor"; + internal const string AltSeq = "alt_seq"; + internal const string Analysis = "analysis"; + internal const string AnalysisId = "_analysis_id"; + internal const string Attributes = "attributes"; + internal const string BamEditStatus = "_bam_edit_status"; + internal const string Biotype = "biotype"; + internal const string BoundLengths = "_bound_lengths"; + internal const string Ccds = "_ccds"; + internal const string CdnaCodingEnd = "cdna_coding_end"; + internal const string CdnaCodingStart = "cdna_coding_start"; + internal const string CellTypeCount = "cell_type_count"; + internal const string CellTypes = "cell_types"; + internal const string Code = "code"; + internal const string CodingDnaCodingEnd = "cdna_coding_end"; + internal const string CodingDnaCodingStart = "cdna_coding_start"; + internal const string CodingRegionEnd = "coding_region_end"; + internal const string CodingRegionStart = "coding_region_start"; + internal const string CodonTable = "codon_table"; + internal const string CreatedDate = "created_date"; + internal const string DbId = "dbID"; + internal const string Description = "description"; + internal const string DisplayLabel = "display_label"; + internal const string DisplayXref = "display_xref"; + internal const string End = "end"; + internal const string EndExon = "end_exon"; + internal const string EndPhase = "end_phase"; + internal const string EpigenomeCount = "epigenome_count"; + internal const string ExonCoordinateMapper = "exon_coord_mapper"; + internal const string ExternalDb = "external_db"; + internal const string ExternalDisplayName = "external_display_name"; + internal const string ExternalName = "external_name"; + internal const string ExternalStatus = "external_status"; + internal const string FeatureType = "feature_type"; + internal const string FivePrimeUtr = "five_prime_utr"; + internal const string From = "from"; + internal const string FromCoordSystem = "from_cs"; + internal const string FromName = "from"; + internal const string Gene = "_gene"; + internal const string GeneHgnc = "_gene_hgnc"; + internal const string GeneHgncId = "_gene_hgnc_id"; + internal const string GenePhenotype = "_gene_phenotype"; + internal const string GeneStableId = "_gene_stable_id"; + internal const string GeneSymbol = "_gene_symbol"; + internal const string GeneSymbolSource = "_gene_symbol_source"; + internal const string Genomic = "GENOME"; + internal const string HasEvidence = "has_evidence"; + internal const string Id = "id"; + internal const string Introns = "introns"; + internal const string IsCanonical = "is_canonical"; + internal const string IsMatrixCompressed = "matrix_compressed"; + internal const string IsSorted = "_is_sorted"; + internal const string Mapper = "mapper"; + internal const string Matrix = "matrix"; + internal const string ModifiedDate = "modified_date"; + internal const string Name = "name"; + internal const string Next = "next"; + internal const string Ori = "ori"; + internal const string PairCodingDna = "_pair_cdna"; + internal const string PairCount = "pair_count"; + internal const string PairGenomic = "_pair_genomic"; + internal const string Peptide = "peptide"; + internal const string PeptideLength = "peptide_length"; + internal const string Phase = "phase"; + internal const string PolyPhen = "polyphen"; + internal const string PolyPhenHumDiv = "polyphen_humdiv"; + internal const string PolyPhenHumVar = "polyphen_humvar"; + internal const string Prev = "prev"; + internal const string Projected = "projected"; + internal const string Protein = "_protein"; + internal const string ProteinFeatures = "protein_features"; + internal const string ProteinFunctionPredictions = "protein_function_predictions"; + internal const string Refseq = "_refseq"; + internal const string RegulatoryBuildId = "regulatory_build_id"; + internal const string Selenocysteines = "selenocysteines"; + internal const string SeqEdits = "seq_edits"; + internal const string SeqName = "seqname"; + internal const string Sequence = "seq"; + internal const string Set = "set"; + internal const string Sift = "sift"; + internal const string Slice = "slice"; + internal const string SortedExons = "sorted_exons"; + internal const string Source = "source"; + internal const string SplicedSequence = "spliced_seq"; + internal const string StableId = "stable_id"; + internal const string Start = "start"; + internal const string StartExon = "start_exon"; + internal const string StartPhase = "start_phase"; + internal const string Strand = "strand"; + internal const string SubAnalysis = "sub_analysis"; + internal const string SwissProt = "_swissprot"; + internal const string ThreePrimeUtr = "three_prime_utr"; + internal const string To = "to"; + internal const string ToCoordSystem = "to_cs"; + internal const string ToName = "to"; + internal const string TransExonArray = "_trans_exon_array"; + internal const string Transcript = "transcript"; + internal const string TranslateableSeq = "translateable_seq"; + internal const string Translation = "translation"; + internal const string TranslationMd5 = "translation_md5"; + internal const string Trembl = "_trembl"; + internal const string UniParc = "_uniparc"; + internal const string Value = "value"; + internal const string VariationEffectFeatureCache = "_variation_effect_feature_cache"; + internal const string VepFeatureType = "_vep_feature_type"; + internal const string VepLazyLoaded = "_vep_lazy_loaded"; + internal const string Version = "version"; + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportMapper.cs b/CacheUtils/DataDumperImport/Import/ImportMapper.cs new file mode 100644 index 00000000..76ce6d8a --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportMapper.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportMapper + { + private static readonly HashSet KnownKeys; + + static ImportMapper() + { + KnownKeys = new HashSet + { + ImportKeys.FromCoordSystem, + ImportKeys.FromName, + ImportKeys.IsSorted, + ImportKeys.PairCodingDna, + ImportKeys.PairCount, + ImportKeys.PairGenomic, + ImportKeys.ToCoordSystem, + ImportKeys.ToName + }; + } + + /// + /// parses the relevant data from each exon coordinate mapper object + /// + public static ICdnaCoordinateMap[] Parse(ObjectValueNode objectValue) + { + ICdnaCoordinateMap[] cdnaMaps = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper mapper object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.ToName: + case ImportKeys.PairCount: + case ImportKeys.PairCodingDna: + case ImportKeys.FromCoordSystem: + case ImportKeys.FromName: + case ImportKeys.IsSorted: + case ImportKeys.ToCoordSystem: + // not used + break; + case ImportKeys.PairGenomic: + if (node is ObjectKeyValueNode pairGenomicNode) + { + cdnaMaps = ImportPairGenomic.Parse(pairGenomicNode.Value); + } + else if (!node.IsUndefined()) + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return cdnaMaps; + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportMapperPair.cs b/CacheUtils/DataDumperImport/Import/ImportMapperPair.cs new file mode 100644 index 00000000..f74eaea7 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportMapperPair.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportMapperPair + { + private static readonly HashSet KnownKeys; + + static ImportMapperPair() + { + KnownKeys = new HashSet + { + ImportKeys.From, + ImportKeys.Ori, + ImportKeys.To + }; + } + + /// + /// parses the relevant data from each mapper pairs object + /// + private static ICdnaCoordinateMap Parse(ObjectValueNode objectValue) + { + int fromStart = -1; + int fromEnd = -1; + var fromType = MapperUnitType.Unknown; + int toStart = -1; + int toEnd = -1; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the mapper pair object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Ori: + // not used + break; + case ImportKeys.From: + if (node is ObjectKeyValueNode fromKeyNode) + { + (fromStart, fromEnd, fromType) = ImportMapperUnit.Parse(fromKeyNode.Value); + } + else + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + case ImportKeys.To: + if (node is ObjectKeyValueNode toKeyNode) + { + (toStart, toEnd, _) = ImportMapperUnit.Parse(toKeyNode.Value); + } + else + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return GetCdnaMap(fromStart, fromEnd, fromType, toStart, toEnd); + } + + private static ICdnaCoordinateMap GetCdnaMap(int fromStart, int fromEnd, MapperUnitType fromType, int toStart, int toEnd) + { + return fromType == MapperUnitType.Genomic + ? new CdnaCoordinateMap(fromStart, fromEnd, toStart, toEnd) + : new CdnaCoordinateMap(toStart, toEnd, fromStart, fromEnd); + } + + /// + /// parses the relevant data from each mapper pairs object + /// + public static ICdnaCoordinateMap[] ParseList(List listMembers) + { + var cdnaMaps = new List(listMembers.Count); + + foreach (var entry in listMembers) + { + if (!(entry is ObjectValueNode mapperPairNode)) throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectValue: [{entry.GetType()}]"); + if (mapperPairNode.Type != "Bio::EnsEMBL::Mapper::Pair") throw new InvalidDataException($"Expected a mapper pair data type, but found the following data type: [{mapperPairNode.Type}]"); + + cdnaMaps.Add(Parse(mapperPairNode)); + } + + return cdnaMaps.ToArray(); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportMapperUnit.cs b/CacheUtils/DataDumperImport/Import/ImportMapperUnit.cs new file mode 100644 index 00000000..b3740b69 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportMapperUnit.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.Utilities; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportMapperUnit + { + private static readonly HashSet KnownKeys; + + static ImportMapperUnit() + { + KnownKeys = new HashSet + { + ImportKeys.End, + ImportKeys.Id, + ImportKeys.Start + }; + } + + /// + /// parses the relevant data from each mapper unit object + /// + public static (int Start, int End, MapperUnitType Type) Parse(ObjectValueNode objectValue) + { + int start = -1; + int end = -1; + var type = MapperUnitType.Unknown; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the mapper unit object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Id: + type = TranscriptUtilities.GetMapperUnitType(node); + break; + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return (start, end, type); + } + } + + public enum MapperUnitType : byte + { + Unknown, + CodingDna, + Genomic + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportPairGenomic.cs b/CacheUtils/DataDumperImport/Import/ImportPairGenomic.cs new file mode 100644 index 00000000..02775e6e --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportPairGenomic.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportPairGenomic + { + private static readonly HashSet KnownKeys; + + static ImportPairGenomic() + { + KnownKeys = new HashSet + { + ImportKeys.Genomic + }; + } + + /// + /// parses the relevant data from each pair genomic object + /// + public static ICdnaCoordinateMap[] Parse(ObjectValueNode objectValue) + { + ICdnaCoordinateMap[] cdnaMaps = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the pair genomic object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Genomic: + if (node is ListObjectKeyValueNode genomicNode) + { + cdnaMaps = ImportMapperPair.ParseList(genomicNode.Values); + } + else if (!node.IsUndefined()) + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return cdnaMaps; + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportPrediction.cs b/CacheUtils/DataDumperImport/Import/ImportPrediction.cs new file mode 100644 index 00000000..e7bf79a1 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportPrediction.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportPrediction + { + private static readonly HashSet KnownKeys; + + static ImportPrediction() + { + KnownKeys = new HashSet + { + ImportKeys.Analysis, + ImportKeys.IsMatrixCompressed, + ImportKeys.Matrix, + ImportKeys.PeptideLength, + ImportKeys.SubAnalysis, + ImportKeys.TranslationMd5 + }; + } + + /// + /// parses the relevant data from each prediction object + /// + public static string Parse(ObjectValueNode objectValue) + { + string predictionData = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper prediction object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.Analysis: + case ImportKeys.IsMatrixCompressed: + case ImportKeys.PeptideLength: + case ImportKeys.SubAnalysis: + case ImportKeys.TranslationMd5: + break; + case ImportKeys.Matrix: + predictionData = node.GetString(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return predictionData; + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportProteinFunctionPredictions.cs b/CacheUtils/DataDumperImport/Import/ImportProteinFunctionPredictions.cs new file mode 100644 index 00000000..b0e63cf5 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportProteinFunctionPredictions.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportProteinFunctionPredictions + { + private static readonly HashSet KnownKeys; + + static ImportProteinFunctionPredictions() + { + KnownKeys = new HashSet + { + ImportKeys.PolyPhenHumVar, + ImportKeys.PolyPhenHumDiv, + ImportKeys.PolyPhen, + ImportKeys.Sift + }; + } + + /// + /// parses the relevant data from each protein function predictions object + /// + public static (string SiftMatrix, string PolyphenMatrix) Parse(ObjectValueNode objectValue) + { + string siftData = null; + string polyphenData = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper mapper object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.PolyPhenHumDiv: + // not used + break; + case ImportKeys.PolyPhen: + if (node.IsUndefined()) + { + // do nothing + } + else + { + throw new InvalidDataException($"Could not handle the PolyPhen key: [{node.GetType()}]"); + } + break; + case ImportKeys.PolyPhenHumVar: + // used by default + if (node is ObjectKeyValueNode polyPhenHumVarNode) + { + polyphenData = ImportPrediction.Parse(polyPhenHumVarNode.Value); + } + else if (!node.IsUndefined()) + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + case ImportKeys.Sift: + if (node is ObjectKeyValueNode siftNode) + { + siftData = ImportPrediction.Parse(siftNode.Value); + } + else if (!node.IsUndefined()) + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return (siftData, polyphenData); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeature.cs b/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeature.cs new file mode 100644 index 00000000..21b733a6 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeature.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.Helpers; +using VariantAnnotation.AnnotatedPositions.Transcript; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.DataDumperImport.Import +{ + public static class ImportRegulatoryFeature + { + private static readonly HashSet KnownKeys; + + static ImportRegulatoryFeature() + { + KnownKeys = new HashSet + { + ImportKeys.AnalysisId, + ImportKeys.BoundLengths, + ImportKeys.CellTypeCount, + ImportKeys.CellTypes, + ImportKeys.DbId, + ImportKeys.DisplayLabel, + ImportKeys.End, + ImportKeys.EpigenomeCount, + ImportKeys.FeatureType, + ImportKeys.HasEvidence, + ImportKeys.Projected, + ImportKeys.RegulatoryBuildId, + ImportKeys.Set, + ImportKeys.StableId, + ImportKeys.Start, + ImportKeys.Strand, + ImportKeys.Slice, + ImportKeys.VepFeatureType + }; + } + + /// + /// parses the relevant data from each regulatory element + /// + public static IRegulatoryRegion Parse(ObjectValueNode objectValue, IChromosome chromosome) + { + int start = -1; + int end = -1; + string stableId = null; + string type = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper regulatory element object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.AnalysisId: + case ImportKeys.BoundLengths: + case ImportKeys.CellTypeCount: + case ImportKeys.CellTypes: + case ImportKeys.DbId: + case ImportKeys.DisplayLabel: + case ImportKeys.EpigenomeCount: + case ImportKeys.HasEvidence: + case ImportKeys.Projected: + case ImportKeys.RegulatoryBuildId: + case ImportKeys.Set: + case ImportKeys.Strand: + case ImportKeys.Slice: + case ImportKeys.VepFeatureType: + // not used + break; + case ImportKeys.FeatureType: + type = node.GetString(); + break; + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.StableId: + stableId = node.GetString(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return new RegulatoryRegion(chromosome, start, end, CompactId.Convert(stableId), + RegulatoryRegionTypeHelper.GetRegulatoryRegionType(type)); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportSeqEdits.cs b/CacheUtils/DataDumperImport/Import/ImportSeqEdits.cs new file mode 100644 index 00000000..4bca8185 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportSeqEdits.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportSeqEdits + { + private static readonly HashSet KnownKeys; + + static ImportSeqEdits() + { + KnownKeys = new HashSet + { + ImportKeys.AltSeq, + ImportKeys.Code, + ImportKeys.Description, + ImportKeys.End, + ImportKeys.Name, + ImportKeys.Start + }; + } + + /// + /// parses the relevant data from each seqedits object + /// + public static int[] Parse(IEnumerable members) + { + var selenocysteineList = new List(); + + foreach (var seqEditNode in members) + { + if (!(seqEditNode is ObjectValueNode seListNode)) continue; + + string code = null; + int start = -1; + + foreach (var node in seListNode.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper seq_edits object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.AltSeq: + case ImportKeys.Description: + case ImportKeys.End: + case ImportKeys.Name: + // not used + break; + case ImportKeys.Code: + code = node.GetString(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + if (code != null && code == "_selenocysteine") selenocysteineList.Add(start); + } + + return selenocysteineList.Count == 0 ? null : selenocysteineList.ToArray(); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportTranscript.cs b/CacheUtils/DataDumperImport/Import/ImportTranscript.cs new file mode 100644 index 00000000..7d81a587 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportTranscript.cs @@ -0,0 +1,277 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Helpers; +using CacheUtils.Utilities; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using TranscriptUtilities = CacheUtils.DataDumperImport.Utilities.TranscriptUtilities; + +namespace CacheUtils.DataDumperImport.Import +{ + public static class ImportTranscript + { + private static readonly HashSet KnownKeys; + + static ImportTranscript() + { + KnownKeys = new HashSet + { + ImportKeys.Attributes, + ImportKeys.BamEditStatus, + ImportKeys.Biotype, + ImportKeys.Ccds, + ImportKeys.CdnaCodingEnd, + ImportKeys.CdnaCodingStart, + ImportKeys.CodingRegionEnd, + ImportKeys.CodingRegionStart, + ImportKeys.CreatedDate, + ImportKeys.DbId, + ImportKeys.Description, + ImportKeys.DisplayXref, + ImportKeys.End, + ImportKeys.ExternalDb, + ImportKeys.ExternalDisplayName, + ImportKeys.ExternalName, + ImportKeys.ExternalStatus, + ImportKeys.Gene, + ImportKeys.GeneHgnc, + ImportKeys.GeneHgncId, + ImportKeys.GenePhenotype, + ImportKeys.GeneStableId, + ImportKeys.GeneSymbol, + ImportKeys.GeneSymbolSource, + ImportKeys.IsCanonical, + ImportKeys.ModifiedDate, + ImportKeys.Protein, + ImportKeys.Refseq, + ImportKeys.Slice, + ImportKeys.Source, + ImportKeys.StableId, + ImportKeys.Start, + ImportKeys.Strand, + ImportKeys.SwissProt, + ImportKeys.TransExonArray, + ImportKeys.Translation, + ImportKeys.Trembl, + ImportKeys.UniParc, + ImportKeys.VariationEffectFeatureCache, + ImportKeys.VepLazyLoaded, + ImportKeys.Version + }; + } + + /// + /// parses the relevant data from each transcript + /// + public static MutableTranscript Parse(ObjectValueNode objectValue, IChromosome chromosome, Source source) + { + // IDs + string transcriptId = null; + byte transcriptVersion = 1; + string proteinId = null; + byte proteinVersion = 0; + string ccdsId = null; + string refSeqId = null; + string geneId = null; + int hgncId = -1; + + // gene + int geneStart = -1; + int geneEnd = -1; + bool geneOnReverseStrand = false; + string geneSymbol = null; + var geneSymbolSource = GeneSymbolSource.Unknown; + + // translation + int translationStart = -1; + int translationEnd = -1; + MutableExon translationStartExon = null; + MutableExon translationEndExon = null; + + // predictions + string siftData = null; + string polyphenData = null; + + var bioType = BioType.Unknown; + IInterval[] microRnas = null; + ICdnaCoordinateMap[] cdnaMaps = null; + IInterval[] introns = null; + string peptideSequence = null; + string translateableSequence = null; + bool isCanonical = false; + int compDnaCodingStart = -1; + int compDnaCodingEnd = -1; + int start = -1; + int end = -1; + MutableExon[] exons = null; + bool cdsStartNotFound = false; + bool cdsEndNotFound = false; + int[] selenocysteinePositions = null; + IRnaEdit[] rnaEdits = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper transcript object: {node.Key}"); + } + + // handle each key + switch (node.Key) + { + case ImportKeys.BamEditStatus: + case ImportKeys.CodingRegionEnd: + case ImportKeys.CodingRegionStart: + case ImportKeys.CreatedDate: + case ImportKeys.DbId: + case ImportKeys.Description: + case ImportKeys.DisplayXref: + case ImportKeys.ExternalDb: + case ImportKeys.ExternalDisplayName: + case ImportKeys.ExternalName: + case ImportKeys.ExternalStatus: + case ImportKeys.GenePhenotype: + case ImportKeys.GeneStableId: + case ImportKeys.ModifiedDate: + case ImportKeys.Protein: + case ImportKeys.Slice: + case ImportKeys.Source: + case ImportKeys.Strand: + case ImportKeys.SwissProt: + case ImportKeys.Trembl: + case ImportKeys.UniParc: + case ImportKeys.VepLazyLoaded: + // not used + break; + case ImportKeys.Attributes: + if (node is ListObjectKeyValueNode attributesList) (microRnas, rnaEdits, cdsStartNotFound, cdsEndNotFound) = Attribute.ParseList(attributesList.Values); + break; + case ImportKeys.Biotype: + bioType = TranscriptUtilities.GetBiotype(node); + break; + case ImportKeys.Ccds: + ccdsId = node.GetString(); + break; + case ImportKeys.CdnaCodingEnd: + compDnaCodingEnd = node.GetInt32(); + break; + case ImportKeys.CdnaCodingStart: + compDnaCodingStart = node.GetInt32(); + break; + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.GeneHgncId: + var hgnc = node.GetString(); + if (hgnc != null && hgnc.StartsWith("HGNC:")) hgnc = hgnc.Substring(5); + if (hgnc != null) hgncId = int.Parse(hgnc); + break; + case ImportKeys.GeneSymbol: + case ImportKeys.GeneHgnc: // older key + geneSymbol = node.GetString(); + break; + case ImportKeys.GeneSymbolSource: + geneSymbolSource = GeneSymbolSourceHelper.GetGeneSymbolSource(node.GetString()); + break; + case ImportKeys.Gene: + if (node is ObjectKeyValueNode geneNode) + { + (geneStart, geneEnd, geneId, geneOnReverseStrand) = ImportGene.Parse(geneNode.Value); + } + break; + case ImportKeys.IsCanonical: + isCanonical = node.GetBool(); + break; + case ImportKeys.Refseq: + refSeqId = node.GetString(); + break; + case ImportKeys.StableId: + transcriptId = node.GetString(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + case ImportKeys.TransExonArray: + if (node is ListObjectKeyValueNode exonsList) + { + exons = ImportExon.ParseList(exonsList.Values, chromosome); + } + break; + case ImportKeys.Translation: + if (node is ObjectKeyValueNode translationNode) + { + (translationStart, translationEnd, proteinId, proteinVersion, translationStartExon, translationEndExon) = ImportTranslation.Parse(translationNode.Value, chromosome); + } + break; + case ImportKeys.VariationEffectFeatureCache: + if (node is ObjectKeyValueNode cacheNode) + { + (cdnaMaps, introns, peptideSequence, translateableSequence, siftData, polyphenData, selenocysteinePositions) = ImportVariantEffectFeatureCache.Parse(cacheNode.Value); + } + break; + case ImportKeys.Version: + transcriptVersion = (byte)node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + var fixedTranscript = AccessionUtilities.GetMaxVersion(transcriptId, transcriptVersion); + var fixedProtein = AccessionUtilities.GetMaxVersion(proteinId, proteinVersion); + + var gene = new MutableGene(chromosome, geneStart, geneEnd, geneOnReverseStrand, geneSymbol, + geneSymbolSource, geneId, hgncId); + + var codingRegion = new CdnaCoordinateMap( + GetCodingRegionStart(geneOnReverseStrand, translationStartExon, translationEndExon, translationStart, translationEnd), + GetCodingRegionEnd(geneOnReverseStrand, translationStartExon, translationEndExon, translationStart, translationEnd), + compDnaCodingStart, compDnaCodingEnd); + + var totalExonLength = GetTotalExonLength(exons); + var startExonPhase = translationStartExon?.Phase ?? int.MinValue; + + return new MutableTranscript(chromosome, start, end, fixedTranscript.Id, fixedTranscript.Version, ccdsId, + refSeqId, bioType, isCanonical, codingRegion, fixedProtein.Id, fixedProtein.Version, + peptideSequence, source, gene, exons, startExonPhase, totalExonLength, introns, cdnaMaps, + siftData, polyphenData, translateableSequence, microRnas, cdsStartNotFound, cdsEndNotFound, + selenocysteinePositions, rnaEdits); + } + + /// + /// returns the start position of the coding region. Returns -1 if no translation was possible. + /// + private static int GetCodingRegionStart(bool onReverseStrand, MutableExon startExon, MutableExon endExon, + int translationStart, int translationEnd) + { + if (startExon == null || endExon == null) return -1; + return onReverseStrand + ? endExon.End - translationEnd + 1 + : startExon.Start + translationStart - 1; + } + + /// + /// returns the start position of the coding region. Returns -1 if no translation was possible. + /// + private static int GetCodingRegionEnd(bool onReverseStrand, MutableExon startExon, MutableExon endExon, + int translationStart, int translationEnd) + { + if (startExon == null || endExon == null) return -1; + return onReverseStrand + ? startExon.End - translationStart + 1 + : endExon.Start + translationEnd - 1; + } + + /// + /// returns the sum of the exon lengths + /// + private static int GetTotalExonLength(IEnumerable exons) => exons.Sum(exon => exon.End - exon.Start + 1); + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportTranscriptMapper.cs b/CacheUtils/DataDumperImport/Import/ImportTranscriptMapper.cs new file mode 100644 index 00000000..554cfd9f --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportTranscriptMapper.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportTranscriptMapper + { + private static readonly HashSet KnownKeys; + + static ImportTranscriptMapper() + { + KnownKeys = new HashSet + { + ImportKeys.CodingDnaCodingEnd, + ImportKeys.CodingDnaCodingStart, + ImportKeys.ExonCoordinateMapper, + ImportKeys.StartPhase + }; + } + + /// + /// parses the relevant data from each transcript mapper + /// + public static ICdnaCoordinateMap[] Parse(ObjectValueNode objectValue) + { + ICdnaCoordinateMap[] cdnaMaps = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper transcript mapper object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.CodingDnaCodingEnd: + case ImportKeys.CodingDnaCodingStart: + case ImportKeys.StartPhase: + break; + case ImportKeys.ExonCoordinateMapper: + if (node is ObjectKeyValueNode exonCoordMapperNode) + { + cdnaMaps = ImportMapper.Parse(exonCoordMapperNode.Value); + } + else + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return cdnaMaps; + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportTranslation.cs b/CacheUtils/DataDumperImport/Import/ImportTranslation.cs new file mode 100644 index 00000000..98eb95e8 --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportTranslation.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportTranslation + { + private static readonly HashSet KnownKeys; + + static ImportTranslation() + { + KnownKeys = new HashSet + { + ImportKeys.Adaptor, + ImportKeys.DbId, + ImportKeys.EndExon, + ImportKeys.End, + ImportKeys.Sequence, + ImportKeys.StableId, + ImportKeys.StartExon, + ImportKeys.Start, + ImportKeys.Transcript, + ImportKeys.Version + }; + } + + /// + /// parses the relevant data from each translation object + /// + public static (int Start, int End, string ProteinId, byte ProteinVersion, MutableExon startExon, MutableExon + endExon) Parse(ObjectValueNode objectValue, IChromosome currentChromosome) + { + int start = -1; + int end = -1; + string proteinId = null; + byte proteinVersion = 0; + MutableExon startExon = null; + MutableExon endExon = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper mapper object: {node.Key}"); + } + + ObjectKeyValueNode exonNode; + + switch (node.Key) + { + case ImportKeys.Adaptor: + case ImportKeys.Sequence: + case ImportKeys.DbId: + case ImportKeys.Transcript: + // skip this key + break; + case ImportKeys.StartExon: + exonNode = node as ObjectKeyValueNode; + if (exonNode != null) startExon = ImportExon.Parse(exonNode.Value, currentChromosome); + break; + case ImportKeys.EndExon: + exonNode = node as ObjectKeyValueNode; + if (exonNode != null) endExon = ImportExon.Parse(exonNode.Value, currentChromosome); + break; + case ImportKeys.StableId: + proteinId = node.GetString(); + break; + case ImportKeys.End: + end = node.GetInt32(); + break; + case ImportKeys.Start: + start = node.GetInt32(); + break; + case ImportKeys.Version: + proteinVersion = (byte)node.GetInt32(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return (start, end, proteinId, proteinVersion, startExon, endExon); + } + } +} diff --git a/CacheUtils/DataDumperImport/Import/ImportVariantEffectFeatureCache.cs b/CacheUtils/DataDumperImport/Import/ImportVariantEffectFeatureCache.cs new file mode 100644 index 00000000..e407a2be --- /dev/null +++ b/CacheUtils/DataDumperImport/Import/ImportVariantEffectFeatureCache.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.DataDumperImport.Import +{ + internal static class ImportVariantEffectFeatureCache + { + private static readonly HashSet KnownKeys; + + static ImportVariantEffectFeatureCache() + { + KnownKeys = new HashSet + { + ImportKeys.CodonTable, + ImportKeys.FivePrimeUtr, + ImportKeys.Introns, + ImportKeys.Mapper, + ImportKeys.Peptide, + ImportKeys.ProteinFeatures, + ImportKeys.ProteinFunctionPredictions, + ImportKeys.Selenocysteines, + ImportKeys.SeqEdits, + ImportKeys.SplicedSequence, + ImportKeys.SortedExons, + ImportKeys.ThreePrimeUtr, + ImportKeys.TranslateableSeq + }; + } + + /// + /// parses the relevant data from each variant effect feature cache + /// + public static (ICdnaCoordinateMap[] CdnaMaps, IInterval[] Introns, string PeptideSequence, string + TranslateableSequence, string SiftData, string PolyPhenData, int[] SelenocysteinePositions) Parse(ObjectValueNode objectValue) + { + ICdnaCoordinateMap[] cdnaMaps = null; + IInterval[] introns = null; + string peptideSequence = null; + string translateableSequence = null; + string siftData = null; + string polyphenData = null; + int[] selenocysteinePositions = null; + + foreach (var node in objectValue.Values) + { + // sanity check: make sure we know about the keys are used for + if (!KnownKeys.Contains(node.Key)) + { + throw new InvalidDataException($"Encountered an unknown key in the dumper variant effect feature cache object: {node.Key}"); + } + + switch (node.Key) + { + case ImportKeys.CodonTable: + case ImportKeys.FivePrimeUtr: + case ImportKeys.ProteinFeatures: + case ImportKeys.Selenocysteines: + case ImportKeys.SortedExons: + case ImportKeys.SplicedSequence: + case ImportKeys.ThreePrimeUtr: + // not used + break; + case ImportKeys.Introns: + if (node is ListObjectKeyValueNode intronsList) + { + introns = ImportIntron.ParseList(intronsList.Values); + } + else if (!node.IsUndefined()) + { + throw new InvalidDataException($"Could not transform the AbstractData object into a ListObjectKeyValue: [{node.GetType()}]"); + } + break; + case ImportKeys.Mapper: + if (node is ObjectKeyValueNode mapperNode) + { + cdnaMaps = ImportTranscriptMapper.Parse(mapperNode.Value); + } + else + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + case ImportKeys.Peptide: + peptideSequence = node.GetString(); + break; + case ImportKeys.ProteinFunctionPredictions: + if (node is ObjectKeyValueNode predictionsNode) + { + (siftData, polyphenData) = ImportProteinFunctionPredictions.Parse(predictionsNode.Value); + } + else + { + throw new InvalidDataException($"Could not transform the AbstractData object into an ObjectKeyValue: [{node.GetType()}]"); + } + break; + case ImportKeys.SeqEdits: + if (node is ListObjectKeyValueNode seqEditsNodes) + { + selenocysteinePositions = ImportSeqEdits.Parse(seqEditsNodes.Values); + } + break; + case ImportKeys.TranslateableSeq: + translateableSequence = node.GetString(); + break; + default: + throw new InvalidDataException($"Unknown key found: {node.Key}"); + } + } + + return (cdnaMaps, introns, peptideSequence, translateableSequence, siftData, polyphenData, selenocysteinePositions); + } + } +} diff --git a/CacheUtils/DataDumperImport/Utilities/MutableTranscriptComparer.cs b/CacheUtils/DataDumperImport/Utilities/MutableTranscriptComparer.cs new file mode 100644 index 00000000..4b35654e --- /dev/null +++ b/CacheUtils/DataDumperImport/Utilities/MutableTranscriptComparer.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.DataDumperImport.Utilities +{ + internal sealed class MutableTranscriptComparer : EqualityComparer + { + private static bool GeneEquals(MutableGene x, MutableGene y) + { + return x.Chromosome.Index == y.Chromosome.Index && + x.Start == y.Start && + x.End == y.End && + x.OnReverseStrand == y.OnReverseStrand && + x.GeneId == y.GeneId && + x.Symbol == y.Symbol && + x.HgncId == y.HgncId && + x.SymbolSource == y.SymbolSource; + } + + private static bool ExonEquals(MutableExon x, MutableExon y) + { + return x.Start == y.Start && + x.End == y.End && + x.Phase == y.Phase; + } + + private static bool IntervalEquals(IInterval x, IInterval y) + { + return x.Start == y.Start && + x.End == y.End; + } + + private static bool CdnaMapEquals(ICdnaCoordinateMap x, ICdnaCoordinateMap y) + { + return x.Start == y.Start && + x.End == y.End && + x.CdnaStart == y.CdnaStart && + x.CdnaEnd == y.CdnaEnd; + } + + // ReSharper disable SuggestBaseTypeForParameter + private static bool ArrayEquals(T[] x, T[] y, Func equals) + // ReSharper restore SuggestBaseTypeForParameter + { + if (x == null && y == null) return true; + if (x == null || y == null) return false; + if (x.Length != y.Length) return false; + // ReSharper disable once LoopCanBeConvertedToQuery + for (int i = 0; i < x.Length; i++) if (!equals(x[i], y[i])) return false; + return true; + } + + private static bool IntEquals(int x, int y) => x == y; + + public override bool Equals(MutableTranscript x, MutableTranscript y) + { + return x.Chromosome.Index == y.Chromosome.Index && + x.Start == y.Start && + x.End == y.End && + x.Id == y.Id && + x.Version == y.Version && + x.CcdsId == y.CcdsId && + x.RefSeqId == y.RefSeqId && + x.Source == y.Source && + x.TotalExonLength == y.TotalExonLength && + x.TranslateableSequence == y.TranslateableSequence && + x.CdsStartNotFound == y.CdsStartNotFound && + x.CdsEndNotFound == y.CdsEndNotFound && + x.StartExonPhase == y.StartExonPhase && + x.BioType == y.BioType && + x.IsCanonical == y.IsCanonical && + x.ProteinId == y.ProteinId && + x.ProteinVersion == y.ProteinVersion && + x.PeptideSequence == y.PeptideSequence && + x.SiftData == y.SiftData && + x.PolyphenData == y.PolyphenData && + GeneEquals(x.Gene, y.Gene) && + ArrayEquals(x.Exons, y.Exons, ExonEquals) && + ArrayEquals(x.Introns, y.Introns, IntervalEquals) && + ArrayEquals(x.MicroRnas, y.MicroRnas, IntervalEquals) && + ArrayEquals(x.SelenocysteinePositions, y.SelenocysteinePositions, IntEquals) && + ArrayEquals(x.CdnaMaps, y.CdnaMaps, CdnaMapEquals) && + CdnaMapEquals(x.CodingRegion, y.CodingRegion); + } + + public override int GetHashCode(MutableTranscript x) + { + unchecked + { + var hashCode = x.Chromosome.Index.GetHashCode(); + hashCode = (hashCode * 397) ^ x.Start.GetHashCode(); + hashCode = (hashCode * 397) ^ x.End.GetHashCode(); + hashCode = (hashCode * 397) ^ x.Id.GetHashCode(); + hashCode = (hashCode * 397) ^ x.Version.GetHashCode(); + hashCode = (hashCode * 397) ^ x.BioType.GetHashCode(); + hashCode = (hashCode * 397) ^ x.Source.GetHashCode(); + return hashCode; + } + } + } +} diff --git a/CacheUtils/DataDumperImport/Utilities/TranscriptUtilities.cs b/CacheUtils/DataDumperImport/Utilities/TranscriptUtilities.cs new file mode 100644 index 00000000..ab18ad45 --- /dev/null +++ b/CacheUtils/DataDumperImport/Utilities/TranscriptUtilities.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.Import; +using CacheUtils.Helpers; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.DataDumperImport.Utilities +{ + public static class TranscriptUtilities + { + private const string CodingDnaMapperUnitTypeKey = "cdna"; + private const string GenomeMapperUnitTypeKey = "genome"; + + private static readonly Dictionary MapperUnitTypes; + + static TranscriptUtilities() + { + MapperUnitTypes = new Dictionary + { + [CodingDnaMapperUnitTypeKey] = MapperUnitType.CodingDna, + [GenomeMapperUnitTypeKey] = MapperUnitType.Genomic + }; + } + + /// + /// returns the biotype given the specialized string key/value type + /// + public static BioType GetBiotype(IImportNode node) => BioTypeHelper.GetBioType(node.GetString()); + + /// + /// returns the mapper unit type given the specialized string key/value type + /// + public static MapperUnitType GetMapperUnitType(IImportNode node) + { + string mapperUnitTypeString = node.GetString(); + + if (!MapperUnitTypes.TryGetValue(mapperUnitTypeString, out var ret)) + { + throw new InvalidDataException($"Unable to find the specified mapper unit type ({mapperUnitTypeString}) in the MapperUnitType dictionary."); + } + + return ret; + } + + /// + /// returns true if the annotation is on the reverse strand, false otherwise + /// + public static bool GetStrand(IImportNode node) + { + int strandNum = node.GetInt32(); + + // sanity check: make sure the value is either 1 or -1 + if (strandNum != -1 && strandNum != 1) + { + throw new InvalidDataException($"Expected the strand number to be either -1 or 1. Found: {strandNum}."); + } + + return strandNum == -1; + } + } +} diff --git a/CacheUtils/ExtractTranscripts/ExtractTranscriptMain.cs b/CacheUtils/ExtractTranscripts/ExtractTranscriptMain.cs deleted file mode 100644 index c9b97925..00000000 --- a/CacheUtils/ExtractTranscripts/ExtractTranscriptMain.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using ErrorHandling; - -namespace CacheUtils.ExtractTranscripts -{ - public static class ExtractTranscriptMain - { - public static ExitCodes Run(string command, string[] args) - { - Console.WriteLine("SUCCESS"); - return ExitCodes.Success; - } - } -} diff --git a/CacheUtils/GFF/CreateGffMain.cs b/CacheUtils/GFF/CreateGffMain.cs new file mode 100644 index 00000000..ca9d6d4a --- /dev/null +++ b/CacheUtils/GFF/CreateGffMain.cs @@ -0,0 +1,56 @@ +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using ErrorHandling; +using VariantAnnotation.Providers; + +namespace CacheUtils.GFF +{ + public static class CreateGffMain + { + private static string _compressedReferencePath; + private static string _inputPrefix; + private static string _outputFileName; + + private static ExitCodes ProgramExecution() + { + var creator = new GffCreator(_inputPrefix, _compressedReferencePath); + creator.Create(_outputFileName); + + return ExitCodes.Success; + } + + public static ExitCodes Run(string command, string[] args) + { + var ops = new OptionSet + { + { + "in|i=", + "input cache {prefix}", + v => _inputPrefix = v + }, + { + "out|o=", + "output {file name}", + v => _outputFileName = v + }, + { + "ref|r=", + "reference {file}", + v => _compressedReferencePath = v + } + }; + + var commandLineExample = $"{command} --in --out "; + + return new ConsoleAppBuilder(args, ops) + .UseVersionProvider(new VersionProvider()) + .Parse() + .HasRequiredParameter(_inputPrefix, "input cache prefix", "--in") + .CheckOutputFilenameSuffix(_outputFileName, ".gz", "GFF", "--out") + .SkipBanner() + .ShowHelpMenu("Outputs exon coordinates for all transcripts in a database.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + } + } +} diff --git a/CacheUtils/GFF/GffCreator.cs b/CacheUtils/GFF/GffCreator.cs new file mode 100644 index 00000000..8435748a --- /dev/null +++ b/CacheUtils/GFF/GffCreator.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Helpers; +using CacheUtils.TranscriptCache.Comparers; +using Compression.Utilities; +using ErrorHandling.Exceptions; +using VariantAnnotation.Algorithms; +using VariantAnnotation.AnnotatedPositions.Transcript; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.IO.Caches; + +namespace CacheUtils.GFF +{ + public sealed class GffCreator + { + private readonly string _cachePrefix; + private readonly string _referencePath; + private readonly HashSet _observedGenes; + private readonly Dictionary _internalGeneId; + + public GffCreator(string cachePrefix, string referencePath) + { + _cachePrefix = cachePrefix; + _referencePath = referencePath; + + var geneComparer = new GeneComparer(); + _observedGenes = new HashSet(geneComparer); + _internalGeneId = new Dictionary(geneComparer); + } + + public void Create(string outputPath) + { + using (var writer = GZipUtilities.GetStreamWriter(outputPath)) + { + var sequenceData = SequenceHelper.GetDictionaries(_referencePath); + + Console.Write("- reading {0}... ", Path.GetFileName(_cachePrefix)); + var cache = TranscriptCacheHelper.GetCache(CacheConstants.TranscriptPath(_cachePrefix), sequenceData.refIndexToChromosome); + Console.WriteLine("found {0:N0} transcripts.", cache.TranscriptIntervalArrays.Length); + + AddGenesToDictionary(cache.Genes); + + Console.Write("- writing GFF entries... "); + foreach (var transcriptArray in cache.TranscriptIntervalArrays) + { + foreach (var interval in transcriptArray.Array) Write(writer, interval.Value); + } + Console.WriteLine("finished."); + } + } + + private void AddGenesToDictionary(IReadOnlyList genes) + { + for (int geneIndex = 0; geneIndex < genes.Count; geneIndex++) + { + var gene = genes[geneIndex]; + + if (_internalGeneId.TryGetValue(gene, out var oldGeneIndex)) + { + throw new UserErrorException($"Found a duplicate gene in the dictionary: {genes[geneIndex]} ({geneIndex} vs {oldGeneIndex})"); + } + + _internalGeneId[gene] = geneIndex; + } + } + + private void Write(TextWriter writer, ITranscript transcript) + { + WriteGene(writer, transcript.Source, transcript.Gene); + WriteTranscript(writer, transcript); + + var exonPhases = GetExonPhases(transcript.StartExonPhase, transcript.Gene.OnReverseStrand, transcript.CdnaMaps); + + for (int exonIndex = 0; exonIndex < transcript.CdnaMaps.Length; exonIndex++) + WriteExon(writer, transcript, transcript.CdnaMaps[exonIndex], exonIndex, exonPhases[exonIndex]); + } + + private void WriteGene(TextWriter writer, Source source, IGene gene) + { + if (_observedGenes.Contains(gene)) return; + _observedGenes.Add(gene); + + var strand = gene.OnReverseStrand ? '-' : '+'; + writer.Write($"{gene.Chromosome.UcscName}\t{source}\tgene\t{gene.Start}\t{gene.End}\t.\t{strand}\t.\t"); + + var geneId = source == Source.Ensembl + ? gene.EnsemblId.ToString() + : gene.EntrezGeneId.ToString(); + + if (!string.IsNullOrEmpty(geneId)) writer.Write($"gene_id \"{geneId}\"; "); + if (!gene.EntrezGeneId.IsEmpty()) writer.Write($"entrez_gene_id \"{gene.EntrezGeneId}\"; "); + if (!gene.EnsemblId.IsEmpty()) writer.Write($"ensembl_gene_id \"{gene.EnsemblId}\"; "); + if (!string.IsNullOrEmpty(gene.Symbol)) writer.Write($"gene_name \"{gene.Symbol}\"; "); + writer.WriteLine($"internal_gene_id \"{_internalGeneId[gene]}\"; "); + } + + private static byte[] GetExonPhases(byte currentPhase, bool onReverseStrand, IReadOnlyList cdnaMaps) + { + var exonPhases = new byte[cdnaMaps.Count]; + + if (onReverseStrand) + { + for (int index = cdnaMaps.Count - 1; index >= 0; index--) + { + exonPhases[index] = currentPhase; + currentPhase = (byte)((currentPhase + GetExonLength(cdnaMaps[index]) % 3) % 3); + } + return exonPhases; + } + + for (int index = 0; index < cdnaMaps.Count; index++) + { + exonPhases[index] = currentPhase; + currentPhase = (byte)((currentPhase + GetExonLength(cdnaMaps[index]) % 3) % 3); + } + + return exonPhases; + } + + private static int GetExonLength(IInterval cdnaMap) => cdnaMap.End - cdnaMap.Start + 1; + + private void WriteTranscript(TextWriter writer, ITranscript transcript) + { + var strand = transcript.Gene.OnReverseStrand ? '-' : '+'; + writer.Write($"{transcript.Chromosome.UcscName}\t{transcript.Source}\ttranscript\t{transcript.Start}\t{transcript.End}\t.\t{strand}\t.\t"); + + WriteGeneralAttributes(writer, transcript); + writer.WriteLine($"internal_gene_id \"{_internalGeneId[transcript.Gene]}\"; "); + } + + private static void WriteGeneralAttributes(TextWriter writer, ITranscript transcript) + { + var geneId = transcript.Source == Source.Ensembl + ? transcript.Gene.EnsemblId.ToString() + : transcript.Gene.EntrezGeneId.ToString(); + + if (!string.IsNullOrEmpty(geneId)) writer.Write($"gene_id \"{geneId}\"; "); + if (!string.IsNullOrEmpty(transcript.Gene.Symbol)) writer.Write($"gene_name \"{transcript.Gene.Symbol}\"; "); + + if (!transcript.Id.IsEmpty()) writer.Write($"transcript_id \"{transcript.Id.WithVersion}\"; "); + writer.Write($"transcript_type \"{AnnotatedTranscript.GetBioType(transcript.BioType)}\"; "); + + if (transcript.IsCanonical) writer.Write("tag \"canonical\"; "); + + var proteinId = transcript.Translation?.ProteinId.WithVersion; + if (!string.IsNullOrEmpty(proteinId)) writer.Write($"protein_id \"{proteinId}\"; "); + } + + private void WriteExon(TextWriter writer, ITranscript transcript, IInterval exon, int exonIndex, + byte exonPhase) + { + var strand = transcript.Gene.OnReverseStrand ? '-' : '+'; + + // write the exon entry + WriteExonEntry(writer, transcript, "exon", exon.Start, exon.End, strand, exonIndex, exonPhase); + if (transcript.Translation == null) return; + + var codingRegion = transcript.Translation.CodingRegion; + + // write the CDS entry + if (HasCds(exon, codingRegion.Start, codingRegion.End)) + { + GetCdsCoordinates(exon, codingRegion.Start, codingRegion.End, out var cdsStart, out var cdsEnd); + WriteExonEntry(writer, transcript, "CDS", cdsStart, cdsEnd, strand, exonIndex, exonPhase); + } + + // write the UTR entry + // ReSharper disable once InvertIf + if (HasUtr(exon, codingRegion.Start, codingRegion.End)) + { + // check before CDS + if (exon.Start < codingRegion.Start) + { + int utrEnd = codingRegion.Start - 1; + if (utrEnd > exon.End) utrEnd = exon.End; + WriteExonEntry(writer, transcript, "UTR", exon.Start, utrEnd, strand, exonIndex, exonPhase); + } + + // check after CDS + // ReSharper disable once InvertIf + if (exon.End > codingRegion.End) + { + int utrStart = codingRegion.End + 1; + if (utrStart < exon.Start) utrStart = exon.Start; + WriteExonEntry(writer, transcript, "UTR", utrStart, exon.End, strand, exonIndex, exonPhase); + } + } + } + + private static void GetCdsCoordinates(IInterval exon, int codingRegionStart, int codingRegionEnd, + out int cdsStart, out int cdsEnd) + { + cdsStart = exon.Start; + cdsEnd = exon.End; + + if (cdsStart < codingRegionStart) cdsStart = codingRegionStart; + if (cdsEnd > codingRegionEnd) cdsEnd = codingRegionEnd; + } + + private static bool HasCds(IInterval exon, int codingRegionStart, int codingRegionEnd) + { + if (codingRegionStart == -1 || codingRegionEnd == -1) return false; + return exon.Overlaps(codingRegionStart, codingRegionEnd); + } + + private static bool HasUtr(IInterval exon, int codingRegionStart, int codingRegionEnd) + { + if (codingRegionStart == -1 || codingRegionEnd == -1) return false; + return exon.Start < codingRegionStart || exon.End > codingRegionEnd; + } + + private void WriteExonEntry(TextWriter writer, ITranscript transcript, string featureType, int start, int end, + char strand, int exonIndex, byte exonPhase) + { + writer.Write($"{transcript.Chromosome.UcscName}\t{transcript.Source}\t{featureType}\t{start}\t{end}\t.\t{strand}\t{exonPhase}\t"); + + WriteGeneralAttributes(writer, transcript); + + var exonNumber = transcript.Gene.OnReverseStrand ? transcript.CdnaMaps.Length - exonIndex : exonIndex + 1; + writer.Write($"exon_number {exonNumber}; "); + writer.WriteLine($"internal_gene_id \"{_internalGeneId[transcript.Gene]}\"; "); + } + } +} diff --git a/CacheUtils/Genbank/GenbankEntry.cs b/CacheUtils/Genbank/GenbankEntry.cs new file mode 100644 index 00000000..c1650bed --- /dev/null +++ b/CacheUtils/Genbank/GenbankEntry.cs @@ -0,0 +1,29 @@ +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.Genbank +{ + public sealed class GenbankEntry + { + public readonly string TranscriptId; + public readonly byte TranscriptVersion; + public readonly string ProteinId; + public readonly byte ProteinVersion; + public readonly string GeneId; + public readonly string Symbol; + public readonly IInterval CodingRegion; + public readonly IInterval[] Exons; + + public GenbankEntry(string transcriptId, byte transcriptVersion, string proteinId, byte proteinVersion, + string geneId, string symbol, IInterval codingRegion, IInterval[] exons) + { + TranscriptId = transcriptId; + TranscriptVersion = transcriptVersion; + ProteinId = proteinId; + ProteinVersion = proteinVersion; + GeneId = geneId; + Symbol = symbol; + CodingRegion = codingRegion; + Exons = exons; + } + } +} diff --git a/CacheUtils/Genbank/GenbankReader.cs b/CacheUtils/Genbank/GenbankReader.cs new file mode 100644 index 00000000..d351eac9 --- /dev/null +++ b/CacheUtils/Genbank/GenbankReader.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Genbank +{ + public sealed class GenbankReader : IDisposable + { + private readonly StreamReader _reader; + + // ftp://ftp.ncbi.nlm.nih.gov/refseq/H_sapiens/mRNA_Prot/human.*.rna.gbff.gz + + private const string LocusTag = "LOCUS"; + private const string VersionTag = "VERSION"; + private const string FeaturesTag = "FEATURES"; + private const string OriginTag = "ORIGIN"; + private const string TerminatorTag = "//"; + + private const string GeneFeatureTag = "gene"; + private const string CdsFeatureTag = "CDS"; + private const string ExonFeatureTag = "exon"; + + private const string ProteinIdTag = "/protein_id="; + private const string GeneIdTag = "/db_xref=\"GeneID:"; + private const string GeneSymbolTag = "/gene="; + + private const int FeatureColumnLength = 21; + + public GenbankReader(StreamReader reader) => _reader = reader; + + public GenbankEntry GetGenbankEntry() + { + string transcriptId = null; + string proteinId = null; + string geneId = null; + string geneSymbol = null; + IInterval codingRegion = null; + var exons = new List(); + byte transcriptVersion = 0; + byte proteinVersion = 0; + + var currentState = GenbankState.Header; + var featureState = FeaturesState.Unknown; + + // assert that the record starts with LOCUS + if (!HasLocus()) return null; + + while (true) + { + var line = _reader.ReadLine(); + if (line.StartsWith(TerminatorTag)) break; + + if (line.StartsWith(FeaturesTag)) currentState = GenbankState.Features; + else if (line.StartsWith(OriginTag)) currentState = GenbankState.Origin; + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (currentState) + { + case GenbankState.Header: + if (line.StartsWith(VersionTag)) (transcriptId, transcriptVersion) = ParseVersion(line); + break; + + case GenbankState.Features: + bool isNewState; + (featureState, isNewState) = GetFeatureState(featureState, line); + var info = line.Substring(FeatureColumnLength); + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (featureState) + { + case FeaturesState.Gene: + if (info.StartsWith(GeneIdTag)) geneId = ParseGeneId(info); + if (info.StartsWith(GeneSymbolTag)) geneSymbol = ParseGeneSymbol(info); + break; + case FeaturesState.Cds: + if (isNewState) codingRegion = GetInterval(info); + if (info.StartsWith(ProteinIdTag)) (proteinId, proteinVersion) = ParseProteinId(info); + break; + case FeaturesState.Exon: + if (isNewState) exons.Add(GetInterval(info)); + break; + } + break; + } + } + + return transcriptId == null + ? null + : new GenbankEntry(transcriptId, transcriptVersion, proteinId, proteinVersion, geneId, geneSymbol, + codingRegion, exons.Count == 0 ? null : exons.ToArray()); + } + + private static string ParseGeneSymbol(string info) => info.Substring(GeneSymbolTag.Length).Trim('"'); + private static string ParseGeneId(string info) => info.Substring(GeneIdTag.Length).Trim('"'); + + private static (string Id, byte Version) ParseProteinId(string info) + { + var rawId = info.Substring(ProteinIdTag.Length).Trim('"'); + return FormatUtilities.SplitVersion(rawId); + } + + private static IInterval GetInterval(string info) + { + if (info.StartsWith("join")) return GetJoinInterval(info); + + var coordinates = info.Split(".."); + if (coordinates.Length != 2) throw new InvalidDataException("Expected two coordinates in the exon feature line."); + + var start = int.Parse(coordinates[0].TrimStart('<')); + var end = int.Parse(coordinates[1].TrimStart('>')); + return new Interval(start, end); + } + + private static IInterval GetJoinInterval(string info) + { + var cols = info.Substring(5, info.Length - 6).Split(','); + var start = int.Parse(cols[0].Split("..")[0]); + var end = int.Parse(cols[1].Split("..")[1]); + return new Interval(start, end); + } + + private static (FeaturesState State, bool IsNewState) GetFeatureState(FeaturesState featureState, string line) + { + var label = line.Substring(0, FeatureColumnLength).Trim(); + if (string.IsNullOrEmpty(label)) return (featureState, false); + + if (label.StartsWith(GeneFeatureTag)) return (FeaturesState.Gene, true); + if (label.StartsWith(ExonFeatureTag)) return (FeaturesState.Exon, true); + return label.StartsWith(CdsFeatureTag) ? (FeaturesState.Cds, true) : (FeaturesState.Unknown, true); + } + + private bool HasLocus() + { + var line = _reader.ReadLine(); + return line != null && line.StartsWith(LocusTag); + } + + private static (string TranscriptId, byte TranscriptVersion) ParseVersion(string line) + { + var accession = line.Substring(12).Trim(); + return FormatUtilities.SplitVersion(accession); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/Genbank/GenbankState.cs b/CacheUtils/Genbank/GenbankState.cs new file mode 100644 index 00000000..dd24ae04 --- /dev/null +++ b/CacheUtils/Genbank/GenbankState.cs @@ -0,0 +1,17 @@ +namespace CacheUtils.Genbank +{ + internal enum GenbankState : byte + { + Header, + Features, + Origin + } + + internal enum FeaturesState : byte + { + Unknown, + Cds, + Exon, + Gene + } +} diff --git a/CacheUtils/Genes/Combiners/CombinerUtils.cs b/CacheUtils/Genes/Combiners/CombinerUtils.cs new file mode 100644 index 00000000..2b208578 --- /dev/null +++ b/CacheUtils/Genes/Combiners/CombinerUtils.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; + +namespace CacheUtils.Genes.Combiners +{ + public static class CombinerUtils + { + public static UgaGene Merge(UgaGene gene37, UgaGene gene38) + { + var ensemblId = CombineField(gene37.EnsemblId, gene38.EnsemblId); + var entrezGeneId = CombineField(gene37.EntrezGeneId, gene38.EntrezGeneId); + var hgncId = CombineField(gene37.HgncId, gene38.HgncId); + return new UgaGene(gene37.Chromosome, gene37.GRCh37, gene38.GRCh38, gene37.OnReverseStrand, entrezGeneId, + ensemblId, gene37.Symbol, hgncId); + } + + private static T CombineField(T grch37, T grch38) + { + if (grch37 == null) return grch38; + if (grch38 == null) return grch37; + if (!grch37.Equals(grch38)) throw new InvalidDataException($"Found two different values: {grch37} & {grch38}"); + return grch37; + } + + internal static void RemoveGenes(IEnumerable genes, ICollection remainingGenes) + { + foreach (var gene in genes) remainingGenes.Remove(gene); + } + + internal static void AddOrphans(ICollection combinedGenes, IEnumerable genes) + { + foreach (var gene in genes) combinedGenes.Add(gene); + } + } +} diff --git a/CacheUtils/Genes/Combiners/HgncIdCombiner.cs b/CacheUtils/Genes/Combiners/HgncIdCombiner.cs new file mode 100644 index 00000000..3b895195 --- /dev/null +++ b/CacheUtils/Genes/Combiners/HgncIdCombiner.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.Utilities; + +namespace CacheUtils.Genes.Combiners +{ + public sealed class HgncIdCombiner : ICombiner + { + public void Combine(List combinedGenes, HashSet remainingGenes37, + HashSet remainingGenes38) + { + var hgncIds = GetHgncIds(remainingGenes37, remainingGenes38); + var genesByHgnc37 = remainingGenes37.GetMultiValueDict(x => x.HgncId); + var genesByHgnc38 = remainingGenes38.GetMultiValueDict(x => x.HgncId); + + foreach (var hgncId in hgncIds) + { + var genes37 = GetGenesByHgncId(genesByHgnc37, hgncId); + var genes38 = GetGenesByHgncId(genesByHgnc38, hgncId); + + CombinerUtils.RemoveGenes(genes37, remainingGenes37); + CombinerUtils.RemoveGenes(genes38, remainingGenes38); + + // merge if we have one gene on each genome assembly and they're on the same strand + if (genes37.Count == 1 && genes38.Count == 1) + { + var gene37 = genes37[0]; + var gene38 = genes38[0]; + + if (gene37.OnReverseStrand == gene38.OnReverseStrand) + { + var mergedGene = CombinerUtils.Merge(gene37, gene38); + combinedGenes.Add(mergedGene); + continue; + } + } + + // the following situations happen if we have: + // - one gene from GRCh37 and none from GRCh38 (or vice versa) + // - there is a mixture of genes forward and reverse strands (13 occurrences) + CombinerUtils.AddOrphans(combinedGenes, genes37); + CombinerUtils.AddOrphans(combinedGenes, genes38); + } + } + + private static List GetGenesByHgncId(IReadOnlyDictionary> genesByHgnc, int hgncId) => + genesByHgnc.TryGetValue(hgncId, out var genes) ? genes : UgaAssemblyCombiner.EmptyUgaGenes; + + private static IEnumerable GetHgncIds(IEnumerable remainingUga37, IEnumerable remainingUga38) + { + var hgncIds = new HashSet(); + foreach (var gene in remainingUga37) if (gene.HgncId != -1) hgncIds.Add(gene.HgncId); + foreach (var gene in remainingUga38) if (gene.HgncId != -1) hgncIds.Add(gene.HgncId); + return hgncIds; + } + } +} diff --git a/CacheUtils/Genes/Combiners/ICombiner.cs b/CacheUtils/Genes/Combiners/ICombiner.cs new file mode 100644 index 00000000..48243479 --- /dev/null +++ b/CacheUtils/Genes/Combiners/ICombiner.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using CacheUtils.Genes.DataStructures; + +namespace CacheUtils.Genes.Combiners +{ + public interface ICombiner + { + void Combine(List combinedGenes, HashSet remainingGenes37, HashSet remainingGenes38); + } +} diff --git a/CacheUtils/Genes/Combiners/PartitionCombiner.cs b/CacheUtils/Genes/Combiners/PartitionCombiner.cs new file mode 100644 index 00000000..47870385 --- /dev/null +++ b/CacheUtils/Genes/Combiners/PartitionCombiner.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.Utilities; + +namespace CacheUtils.Genes.Combiners +{ + public sealed class PartitionCombiner : ICombiner + { + public void Combine(List combinedGenes, HashSet remainingGenes37, + HashSet remainingGenes38) + { + var grch37 = Partition(remainingGenes37); + var grch38 = Partition(remainingGenes38); + + CombineSet(combinedGenes, grch37.Both, grch38.Both, remainingGenes37, remainingGenes38); + CombineSet(combinedGenes, grch37.EntrezGeneOnly, grch38.EntrezGeneOnly, remainingGenes37, remainingGenes38); + CombineSet(combinedGenes, grch37.EnsemblOnly, grch38.EnsemblOnly, remainingGenes37, remainingGenes38); + } + + private static void CombineSet(ICollection combinedGenes, IEnumerable uga37, + IEnumerable uga38, ICollection remainingGenes37, ICollection remainingGenes38) + { + var keyToGene37 = uga37.GetMultiValueDict(GetKey); + var keyToGene38 = uga38.GetMultiValueDict(GetKey); + var keys = GetAllKeys(keyToGene37.Keys, keyToGene38.Keys); + + foreach (var key in keys) + { + var genes37 = GetGenesByKey(keyToGene37, key); + var genes38 = GetGenesByKey(keyToGene38, key); + + CombinerUtils.RemoveGenes(genes37, remainingGenes37); + CombinerUtils.RemoveGenes(genes38, remainingGenes38); + + // this happens for both Entrez Gene Only & Ensembl Only + if (genes37.Count == 1 && genes38.Count == 1) + { + var gene37 = genes37[0]; + var gene38 = genes38[0]; + + var mergedGene = CombinerUtils.Merge(gene37, gene38); + combinedGenes.Add(mergedGene); + continue; + } + + // the following situations happen if we have: + // - one gene from GRCh37 and none from GRCh38 (or vice versa) + // - two or more non-overlapping genes on the same assembly (14 occurrences) + CombinerUtils.AddOrphans(combinedGenes, genes37); + CombinerUtils.AddOrphans(combinedGenes, genes38); + } + } + + private static List GetGenesByKey(IReadOnlyDictionary> genesByKey, string key) => + genesByKey.TryGetValue(key, out var genes) ? genes : UgaAssemblyCombiner.EmptyUgaGenes; + + private static IEnumerable GetAllKeys(IEnumerable keys37, IEnumerable keys38) + { + var keys = new HashSet(); + foreach (var key in keys37) keys.Add(key); + foreach (var key in keys38) keys.Add(key); + return keys; + } + + private static string GetKey(UgaGene gene) => + gene.EnsemblId + '|' + gene.EntrezGeneId + '|' + (gene.OnReverseStrand ? "R" : "F"); + + private static (List EnsemblOnly, List Both, List EntrezGeneOnly) Partition( + IEnumerable remainingGenes) + { + var ensemblOnly = new List(); + var both = new List(); + var entrezGeneOnly = new List(); + + foreach (var gene in remainingGenes) + { + if (gene.EntrezGeneId != null && gene.EnsemblId != null) both.Add(gene); + else if (gene.EntrezGeneId != null) entrezGeneOnly.Add(gene); + else ensemblOnly.Add(gene); + } + + return (ensemblOnly, both, entrezGeneOnly); + } + } +} diff --git a/CacheUtils/Genes/DataStores/AssemblyDataStore.cs b/CacheUtils/Genes/DataStores/AssemblyDataStore.cs new file mode 100644 index 00000000..378b1461 --- /dev/null +++ b/CacheUtils/Genes/DataStores/AssemblyDataStore.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using CacheUtils.Commands.UniversalGeneArchive; +using CacheUtils.Helpers; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class AssemblyDataStore + { + private readonly string _description; + private readonly ILogger _logger; + public readonly EnsemblGtf EnsemblGtf; + public readonly RefSeqGff RefSeqGff; + private readonly GlobalCache _globalCache; + + private AssemblyDataStore(string description, ILogger logger, EnsemblGtf ensemblGtf, RefSeqGff refSeqGff, GlobalCache globalCache) + { + _description = description; + _logger = logger; + EnsemblGtf = ensemblGtf; + RefSeqGff = refSeqGff; + _globalCache = globalCache; + } + + public static AssemblyDataStore Create(string description, ILogger logger, + FilePaths.AssemblySpecificPaths paths, IDictionary refNameToChromosome, + IDictionary accessionToChromosome) + { + var ensemblGtf = EnsemblGtf.Create(paths.EnsemblGtfPath, refNameToChromosome); + var refSeqGff = RefSeqGff.Create(paths.RefSeqGffPath, paths.RefSeqGenomeGffPath, accessionToChromosome); + + var (refIndexToChromosome, _, _) = SequenceHelper.GetDictionaries(paths.ReferencePath); + var globalCache = GlobalCache.Create(paths.RefSeqCachePath, paths.EnsemblCachePath, refIndexToChromosome, refNameToChromosome); + + return new AssemblyDataStore(description, logger, ensemblGtf, refSeqGff, globalCache); + } + + public IUpdateHgncData UpdateHgncIds(Hgnc oldHgnc) + { + _logger.WriteLine(); + _logger.WriteLine($"*** {_description} ***"); + + var hgnc = oldHgnc.Clone(); + + _logger.Write("- removing duplicate gene IDs from HGNC... "); + (int numEntrezGeneIdsRemoved, int numEnsemblIdsRemoved) = hgnc.RemoveDuplicateEntries(); + _logger.WriteLine($"{numEntrezGeneIdsRemoved} Entrez Gene, {numEnsemblIdsRemoved} Ensembl."); + + _logger.Write("- adding coordinates to the HGNC entries... "); + int numEntriesWithCoordinates = hgnc.AddCoordinates(EnsemblGtf, RefSeqGff); + _logger.WriteLine($"{numEntriesWithCoordinates} with coordinates."); + + _logger.Write("- updating HGNC IDs for RefSeq genes... "); + int numGenesWithHgncId = hgnc.HgncGenes.Update(_globalCache.RefSeqGenesByRef, x => x.EntrezGeneId).Consolidate(); + _logger.WriteLine($"{numGenesWithHgncId} genes have HGNC ID."); + + _logger.Write("- updating HGNC IDs for Ensembl genes... "); + numGenesWithHgncId = hgnc.HgncGenes.Update(_globalCache.EnsemblGenesByRef, x => x.EnsemblId).Consolidate(); + _logger.WriteLine($"{numGenesWithHgncId} genes have HGNC ID."); + + return new UpdateHgncData(_globalCache.EnsemblGenesByRef, _globalCache.RefSeqGenesByRef, _logger); + } + } +} diff --git a/CacheUtils/Genes/DataStores/EnsemblGtf.cs b/CacheUtils/Genes/DataStores/EnsemblGtf.cs new file mode 100644 index 00000000..c4172247 --- /dev/null +++ b/CacheUtils/Genes/DataStores/EnsemblGtf.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.IO; +using CacheUtils.Genes.Utilities; +using Compression.Utilities; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class EnsemblGtf + { + public readonly Dictionary EnsemblIdToGene; + public readonly Dictionary EnsemblIdToSymbol; + + private EnsemblGtf(Dictionary ensemblIdToGene, Dictionary ensemblIdToSymbol) + { + EnsemblIdToGene = ensemblIdToGene; + EnsemblIdToSymbol = ensemblIdToSymbol; + } + + public static EnsemblGtf Create(string filePath, IDictionary refNameToChromosome) + { + var ensemblGenes = LoadEnsemblGenes(GZipUtilities.GetAppropriateStreamReader(filePath), refNameToChromosome); + var ensemblIdToGene = ensemblGenes.GetSingleValueDict(x => x.GeneId); + var ensemblIdToSymbol = ensemblGenes.GetKeyValueDict(x => x.GeneId, x => x.Symbol); + return new EnsemblGtf(ensemblIdToGene, ensemblIdToSymbol); + } + + private static EnsemblGene[] LoadEnsemblGenes(StreamReader streamReader, + IDictionary refNameToChromosome) + { + EnsemblGene[] genes; + using (var reader = new EnsemblGtfReader(streamReader, refNameToChromosome)) genes = reader.GetGenes(); + return genes; + } + } +} diff --git a/CacheUtils/Genes/DataStores/GeneInfoData.cs b/CacheUtils/Genes/DataStores/GeneInfoData.cs new file mode 100644 index 00000000..470bb07b --- /dev/null +++ b/CacheUtils/Genes/DataStores/GeneInfoData.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.IO; +using CacheUtils.Genes.Utilities; +using Compression.Utilities; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class GeneInfoData + { + public readonly Dictionary EntrezGeneIdToSymbol; + + private GeneInfoData(Dictionary entrezGeneIdToSymbol) + { + EntrezGeneIdToSymbol = entrezGeneIdToSymbol; + } + + public static GeneInfoData Create(string filePath) + { + var entrezGeneIdToSymbol = LoadGeneInfoGenes(filePath) + .GetKeyValueDict(x => x.EntrezGeneId, x => x.Symbol); + return new GeneInfoData(entrezGeneIdToSymbol); + } + + private static IEnumerable LoadGeneInfoGenes(string filePath) + { + GeneInfo[] genes; + using (var streamReader = GZipUtilities.GetAppropriateStreamReader(filePath)) + using (var reader = new GeneInfoReader(streamReader)) genes = reader.GetGenes(); + return genes; + } + } +} diff --git a/CacheUtils/Genes/DataStores/GlobalCache.cs b/CacheUtils/Genes/DataStores/GlobalCache.cs new file mode 100644 index 00000000..2229a6ce --- /dev/null +++ b/CacheUtils/Genes/DataStores/GlobalCache.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes.Utilities; +using CacheUtils.IntermediateIO; +using Compression.Utilities; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class GlobalCache + { + public readonly Dictionary> EnsemblGenesByRef; + public readonly Dictionary> RefSeqGenesByRef; + + private GlobalCache(Dictionary> ensemblGenesByRef, + Dictionary> refSeqGenesByRef) + { + EnsemblGenesByRef = ensemblGenesByRef; + RefSeqGenesByRef = refSeqGenesByRef; + } + + public static GlobalCache Create(string refSeqCachePath, string ensemblCachePath, + IDictionary refIndexToChromosome, IDictionary refNameToChromosome38) + { + var ensemblGenesByRef = FlattenGenes(LoadGenes(GZipUtilities.GetAppropriateReadStream(ensemblCachePath), refIndexToChromosome, refNameToChromosome38)); + var refSeqGenesByRef = FlattenGenes(LoadGenes(GZipUtilities.GetAppropriateReadStream(refSeqCachePath), refIndexToChromosome, refNameToChromosome38)); + + return new GlobalCache(ensemblGenesByRef, refSeqGenesByRef); + } + + private static Dictionary> FlattenGenes(IEnumerable genes) + { + var genesByRef = genes.GetMultiValueDict(x => x.Chromosome.Index); + var result = new Dictionary>(); + + foreach (var kvp in genesByRef.OrderBy(x => x.Key)) + { + result[kvp.Key] = kvp.Value.GetMultiValueDict(x => x.GeneId).FlattenGeneList(); + } + + return result; + } + + private static IEnumerable LoadGenes(Stream stream, + IDictionary refIndexToChromosome, + IDictionary refNameToChromosome38) + { + var geneDict = new Dictionary(); + + using (var reader = new MutableTranscriptReader(stream, refIndexToChromosome)) + { + var transcripts = reader.GetTranscripts(); + + foreach (var transcript in transcripts) + { + var gene = transcript.Gene; + var key = GetGeneKey(gene); + if (geneDict.ContainsKey(key)) continue; + + gene.Chromosome = refNameToChromosome38[gene.Chromosome.UcscName]; + geneDict[key] = gene; + } + } + + return geneDict.Values.OrderBy(x => x.Chromosome.Index).ThenBy(x => x.Start).ThenBy(x => x.End); + } + + private static string GetGeneKey(MutableGene gene) => gene.GeneId + '|' + gene.Chromosome.UcscName + '|' + + gene.Start + '|' + gene.End + '|' + + (gene.OnReverseStrand ? 'R' : 'F'); + } +} diff --git a/CacheUtils/Genes/DataStores/Hgnc.cs b/CacheUtils/Genes/DataStores/Hgnc.cs new file mode 100644 index 00000000..3ee6ce1d --- /dev/null +++ b/CacheUtils/Genes/DataStores/Hgnc.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.IO; +using CacheUtils.Genes.Utilities; +using VariantAnnotation.Algorithms; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class Hgnc + { + public readonly HgncGene[] HgncGenes; + public readonly Dictionary HgncIdToSymbol; + + private Hgnc(HgncGene[] hgncGenes, Dictionary hgncIdToSymbol) + { + HgncGenes = hgncGenes; + HgncIdToSymbol = hgncIdToSymbol; + } + + public static Hgnc Create(string filePath, IDictionary refNameToChromosome) + { + var hgncGenes = LoadHgncGenes(FileUtilities.GetReadStream(filePath), refNameToChromosome); + var hgncIdToSymbol = hgncGenes.GetKeyValueDict(x => x.HgncId, x => x.Symbol); + return new Hgnc(hgncGenes, hgncIdToSymbol); + } + + private static HgncGene[] LoadHgncGenes(Stream stream, IDictionary refNameToChromosome) + { + HgncGene[] genes; + using (var reader = new HgncReader(stream, refNameToChromosome)) genes = reader.GetGenes(); + return genes; + } + + public int AddCoordinates(EnsemblGtf ensemblGtf, RefSeqGff refSeqGff) + { + foreach (var hgncGene in HgncGenes) + { + var (refSeqGenes, ensemblGene, numMatches) = GetGenes(hgncGene.EntrezGeneId, + refSeqGff.EntrezGeneIdToGene, hgncGene.EnsemblId, ensemblGtf.EnsemblIdToGene); + + switch (numMatches) + { + case 0: + break; + + case 1: + if (ensemblGene == null) AddCoordinatesFromGene(hgncGene, refSeqGenes[0]); + else AddCoordinatesFromGene(hgncGene, ensemblGene); + break; + + default: + AddCoordinatesFromMultipleGenes(hgncGene, ensemblGene, refSeqGenes); + break; + } + } + + return HgncGenes.Count(hgncGene => hgncGene.Start != 1 && hgncGene.End != -1); + } + + private static void AddCoordinatesFromMultipleGenes(HgncGene hgncGene, EnsemblGene ensemblGene, IEnumerable refSeqGenes) + { + if (ensemblGene == null) return; + + AddCoordinatesFromGene(hgncGene, ensemblGene); + + foreach (var refSeqGene in refSeqGenes) + { + if (!IntervalUtilities.Overlaps(hgncGene.Start, hgncGene.End, refSeqGene.Start, refSeqGene.End)) continue; + AddCoordinatesFromGene(hgncGene, refSeqGene); + } + } + + private static void AddCoordinatesFromGene(HgncGene hgncGene, IFlatGene flatGene) where T : IFlatGene + { + hgncGene.Start = hgncGene.Start == -1 ? flatGene.Start : Math.Min(hgncGene.Start, flatGene.Start); + hgncGene.End = hgncGene.End == -1 ? flatGene.End : Math.Max(hgncGene.End, flatGene.End); + } + + private static (List RefSeqGenes, EnsemblGene EnsemblGene, int NumMatches) GetGenes( + string entrezGeneId, IReadOnlyDictionary> entrezGeneIdToGene, string ensemblId, + IReadOnlyDictionary ensemblIdToGene) + { + var refSeqGenes = GetRefSeqGenes(entrezGeneId, entrezGeneIdToGene); + var ensemblGene = GetEnsemblGene(ensemblId, ensemblIdToGene); + int numMatches = (ensemblGene != null ? 1 : 0) + refSeqGenes.Count; + return (refSeqGenes, ensemblGene, numMatches); + } + + public Hgnc Clone() + { + var newGenes = new HgncGene[HgncGenes.Length]; + for (int i = 0; i < HgncGenes.Length; i++) newGenes[i] = HgncGenes[i].Clone(); + return new Hgnc(newGenes, HgncIdToSymbol); + } + + private static EnsemblGene GetEnsemblGene(string ensemblId, IReadOnlyDictionary ensemblIdToGene) + { + if (string.IsNullOrEmpty(ensemblId)) return null; + return ensemblIdToGene.TryGetValue(ensemblId, out var ensemblGene) ? ensemblGene : null; + } + + private static readonly List EmptyList = new List(); + + private static List GetRefSeqGenes(string entrezGeneId, IReadOnlyDictionary> entrezGeneIdToGene) + { + if (string.IsNullOrEmpty(entrezGeneId)) return EmptyList; + return entrezGeneIdToGene.TryGetValue(entrezGeneId, out var geneList) ? geneList : EmptyList; + } + + public (int NumEntrezGeneIdsRemoved, int NumEnsemblIdsRemoved) RemoveDuplicateEntries() + { + int numEntrezGeneIdsRemoved = RemoveDuplicatesByTranscriptSource(HgncGenes, x => x.EntrezGeneId, x => x.EntrezGeneId = null); + int numEnsemblIdsRemoved = RemoveDuplicatesByTranscriptSource(HgncGenes, x => x.EnsemblId, x => x.EnsemblId = null); + return (numEntrezGeneIdsRemoved, numEnsemblIdsRemoved); + } + + private static int RemoveDuplicatesByTranscriptSource(IEnumerable newHgncGenes, + Func idFunc, Action nullAction) + { + var hgncByGeneId = newHgncGenes.GetMultiValueDict(idFunc); + int numGeneIdsRemoved = 0; + + foreach (var kvp in hgncByGeneId) + { + if (kvp.Value.Count == 1) continue; + foreach (var hgncGene in kvp.Value) nullAction(hgncGene); + numGeneIdsRemoved++; + } + + return numGeneIdsRemoved; + } + } +} diff --git a/CacheUtils/Genes/DataStores/IUpdateHgncData.cs b/CacheUtils/Genes/DataStores/IUpdateHgncData.cs new file mode 100644 index 00000000..4ec980fd --- /dev/null +++ b/CacheUtils/Genes/DataStores/IUpdateHgncData.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface; + +namespace CacheUtils.Genes.DataStores +{ + public interface IUpdateHgncData + { + Dictionary> EnsemblGenesByRef { get; } + Dictionary> RefSeqGenesByRef { get; } + ILogger Logger { get; } + } +} diff --git a/CacheUtils/Genes/DataStores/RefSeqGff.cs b/CacheUtils/Genes/DataStores/RefSeqGff.cs new file mode 100644 index 00000000..47955f20 --- /dev/null +++ b/CacheUtils/Genes/DataStores/RefSeqGff.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.IO; +using CacheUtils.Genes.Utilities; +using Compression.Utilities; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class RefSeqGff + { + public readonly Dictionary> EntrezGeneIdToGene; + public readonly Dictionary EntrezGeneIdToSymbol; + + private RefSeqGff(Dictionary> entrezGeneIdToGene, Dictionary entrezGeneIdToSymbol) + { + EntrezGeneIdToGene = entrezGeneIdToGene; + EntrezGeneIdToSymbol = entrezGeneIdToSymbol; + } + + public static RefSeqGff Create(string gcfGffPath, string refGffPath, IDictionary accessionToChromosome) + { + var refSeqGenes = LoadRefSeqGffGenes(GZipUtilities.GetAppropriateStreamReader(gcfGffPath), + GZipUtilities.GetAppropriateStreamReader(refGffPath), accessionToChromosome); + + var entrezGeneIdToGene = refSeqGenes + .GetMultiValueDict(x => x.GeneId) + .FlattenGeneList() + .GetMultiValueDict(x => x.GeneId); + + var entrezGeneIdToSymbol = refSeqGenes.GetKeyValueDict(x => x.GeneId, x => x.Symbol); + + return new RefSeqGff(entrezGeneIdToGene, entrezGeneIdToSymbol); + } + + private static List LoadRefSeqGffGenes(StreamReader gcfGffReader, StreamReader refGffReader, IDictionary accessionToChromosome) + { + var refSeqGenes = new List(); + + LoadRefSeqGff(gcfGffReader, refSeqGenes, accessionToChromosome); + LoadRefSeqGff(refGffReader, refSeqGenes, accessionToChromosome); + + return refSeqGenes.OrderBy(x => x.Chromosome.Index).ThenBy(x => x.Start).ThenBy(x => x.End).ToList(); + } + + private static void LoadRefSeqGff(StreamReader streamReader, List refSeqGenes, IDictionary accessionToChromosome) + { + using (var reader = new RefSeqGffReader(streamReader, accessionToChromosome)) + { + reader.AddGenes(refSeqGenes); + } + } + } +} diff --git a/CacheUtils/Genes/DataStores/UpdateHgncData.cs b/CacheUtils/Genes/DataStores/UpdateHgncData.cs new file mode 100644 index 00000000..9ce858d1 --- /dev/null +++ b/CacheUtils/Genes/DataStores/UpdateHgncData.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface; + +namespace CacheUtils.Genes.DataStores +{ + public sealed class UpdateHgncData : IUpdateHgncData + { + public Dictionary> EnsemblGenesByRef { get; } + public Dictionary> RefSeqGenesByRef { get; } + public ILogger Logger { get; } + + public UpdateHgncData(Dictionary> ensemblGenesByRef, + Dictionary> refSeqGenesByRef, ILogger logger) + { + EnsemblGenesByRef = ensemblGenesByRef; + RefSeqGenesByRef = refSeqGenesByRef; + Logger = logger; + } + } +} diff --git a/CacheUtils/Genes/DataStructures/EnsemblGene.cs b/CacheUtils/Genes/DataStructures/EnsemblGene.cs new file mode 100644 index 00000000..0756550b --- /dev/null +++ b/CacheUtils/Genes/DataStructures/EnsemblGene.cs @@ -0,0 +1,25 @@ +using System; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStructures +{ + public sealed class EnsemblGene : IFlatGene + { + public IChromosome Chromosome { get; } + public int Start { get; } + public int End { get; set; } + public string GeneId { get; } + public string Symbol { get; } + + public EnsemblGene(IChromosome chromosome, int start, int end, string geneId, string symbol) + { + Chromosome = chromosome; + Start = start; + End = end; + GeneId = geneId; + Symbol = symbol; + } + + public EnsemblGene Clone() => throw new NotImplementedException(); + } +} diff --git a/CacheUtils/Genes/DataStructures/GeneInfo.cs b/CacheUtils/Genes/DataStructures/GeneInfo.cs new file mode 100644 index 00000000..f43521cb --- /dev/null +++ b/CacheUtils/Genes/DataStructures/GeneInfo.cs @@ -0,0 +1,14 @@ +namespace CacheUtils.Genes.DataStructures +{ + public sealed class GeneInfo + { + public string Symbol { get; } + public string EntrezGeneId { get; } + + public GeneInfo(string symbol, string entrezGeneId) + { + Symbol = symbol; + EntrezGeneId = entrezGeneId; + } + } +} diff --git a/CacheUtils/Genes/DataStructures/HgncGene.cs b/CacheUtils/Genes/DataStructures/HgncGene.cs new file mode 100644 index 00000000..8f88d347 --- /dev/null +++ b/CacheUtils/Genes/DataStructures/HgncGene.cs @@ -0,0 +1,30 @@ +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStructures +{ + public sealed class HgncGene : IChromosomeInterval + { + public IChromosome Chromosome { get; } + public int Start { get; set; } + public int End { get; set; } + public string Symbol { get; } + public string EntrezGeneId { get; set; } + public string EnsemblId { get; set; } + public readonly int HgncId; + + public HgncGene(IChromosome chromosome, int start, int end, string symbol, string entrezGeneId, + string ensemblId, int hgncId) + { + Chromosome = chromosome; + Start = start; + End = end; + Symbol = symbol; + EntrezGeneId = entrezGeneId; + EnsemblId = ensemblId; + HgncId = hgncId; + } + + public HgncGene Clone() => new HgncGene(Chromosome, -1, -1, Symbol, EntrezGeneId, EnsemblId, HgncId); + } +} diff --git a/CacheUtils/Genes/DataStructures/IFlatGene.cs b/CacheUtils/Genes/DataStructures/IFlatGene.cs new file mode 100644 index 00000000..0cfab8eb --- /dev/null +++ b/CacheUtils/Genes/DataStructures/IFlatGene.cs @@ -0,0 +1,12 @@ +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStructures +{ + public interface IFlatGene + { + IChromosome Chromosome { get; } + int Start { get; } + int End { get; set; } + T Clone(); + } +} diff --git a/CacheUtils/Genes/DataStructures/RefSeqGene.cs b/CacheUtils/Genes/DataStructures/RefSeqGene.cs new file mode 100644 index 00000000..ff9ef77c --- /dev/null +++ b/CacheUtils/Genes/DataStructures/RefSeqGene.cs @@ -0,0 +1,29 @@ +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStructures +{ + public sealed class RefSeqGene : IFlatGene + { + public IChromosome Chromosome { get; } + public int Start { get; } + public int End { get; set; } + private bool OnReverseStrand { get; } + public string GeneId { get; } + public string Symbol { get; } + private int HgncId { get; } + + public RefSeqGene(IChromosome chromosome, int start, int end, bool onReverseStrand, string entrezGeneId, + string symbol, int hgncId) + { + Chromosome = chromosome; + Start = start; + End = end; + OnReverseStrand = onReverseStrand; + GeneId = entrezGeneId; + Symbol = symbol; + HgncId = hgncId; + } + + public RefSeqGene Clone() => new RefSeqGene(Chromosome, Start, End, OnReverseStrand, GeneId, Symbol, HgncId); + } +} diff --git a/CacheUtils/Genes/DataStructures/UgaGene.cs b/CacheUtils/Genes/DataStructures/UgaGene.cs new file mode 100644 index 00000000..e5ebfa50 --- /dev/null +++ b/CacheUtils/Genes/DataStructures/UgaGene.cs @@ -0,0 +1,50 @@ +using VariantAnnotation.AnnotatedPositions.Transcript; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.DataStructures +{ + public sealed class UgaGene + { + public readonly IChromosome Chromosome; + public readonly IInterval GRCh37; + public readonly IInterval GRCh38; + public readonly bool OnReverseStrand; + public readonly int HgncId; + + public string Symbol { get; set; } + public string EntrezGeneId { get; } + public string EnsemblId { get; } + + public UgaGene(IChromosome chromosome, IInterval grch37, IInterval grch38, bool onReverseStrand, + string entrezGeneId, string ensemblId, string symbol, int hgncId) + { + Chromosome = chromosome; + GRCh37 = grch37; + GRCh38 = grch38; + EntrezGeneId = entrezGeneId; + EnsemblId = ensemblId; + Symbol = symbol; + OnReverseStrand = onReverseStrand; + HgncId = hgncId; + } + + public override string ToString() + { + var interval37 = GetInterval(GRCh37); + var interval38 = GetInterval(GRCh38); + string strand = OnReverseStrand ? "R" : "F"; + return $"{Chromosome.UcscName}\t{Chromosome.EnsemblName}\t{Symbol}\t{interval37}\t{interval38}\t{strand}\t{HgncId}\t{EnsemblId}\t{EntrezGeneId}"; + } + + private static string GetInterval(IInterval interval) => + interval == null ? "-1\t-1" : $"{interval.Start}\t{interval.End}"; + + public Gene ToGene(GenomeAssembly genomeAssembly) + { + var interval = genomeAssembly == GenomeAssembly.GRCh37 ? GRCh37 : GRCh38; + return new Gene(Chromosome, interval.Start, interval.End, OnReverseStrand, Symbol, HgncId, CompactId.Convert(EntrezGeneId), CompactId.Convert(EnsemblId)); + } + } +} diff --git a/CacheUtils/Genes/GeneFlattener.cs b/CacheUtils/Genes/GeneFlattener.cs new file mode 100644 index 00000000..e7a33c7e --- /dev/null +++ b/CacheUtils/Genes/GeneFlattener.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Algorithms; + +namespace CacheUtils.Genes +{ + public static class GeneFlattener + { + public static List FlattenGeneList(this Dictionary> genesById) where T : IFlatGene + { + var genesList = new List(); + + foreach (var genes in genesById.Values) + { + var flatGenes = FlattenWithSameId(genes); + genesList.AddRange(flatGenes); + } + + return genesList.OrderBy(x => x.Chromosome.Index).ThenBy(x => x.Start).ThenBy(x => x.End).ToList(); + } + + internal static List FlattenWithSameId(List genes) where T : IFlatGene + { + if (genes == null || genes.Count == 1) return genes; + + var flatGenes = new List(); + var seedGene = genes[0].Clone(); + + foreach (var gene in genes) + { + if (IntervalUtilities.Overlaps(seedGene.Start, seedGene.End, gene.Start, gene.End)) + { + seedGene.End = Math.Max(seedGene.End, gene.End); + continue; + } + + flatGenes.Add(seedGene); + seedGene = gene.Clone(); + } + + flatGenes.Add(seedGene); + return flatGenes; + } + } +} diff --git a/CacheUtils/Genes/GeneMerger.cs b/CacheUtils/Genes/GeneMerger.cs new file mode 100644 index 00000000..ad7e97fc --- /dev/null +++ b/CacheUtils/Genes/GeneMerger.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes.DataStores; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.Utilities; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.Genes +{ + public static class GeneMerger + { + public static Dictionary> MergeByHgnc(this IUpdateHgncData data, bool isGrch37) + { + data.Logger.Write("- merging RefSeq & Ensembl genes... "); + + var genesByRef = new Dictionary>(); + var mergedGenesByRef = new Dictionary>(); + + AddGenes(data.EnsemblGenesByRef, genesByRef); + AddGenes(data.RefSeqGenesByRef, genesByRef); + + int totalOrphanEntries = 0; + int totalMergedEntries = 0; + + foreach (var kvp in genesByRef) + { + var hgncIdToGenes = kvp.Value.GetMultiValueDict(x => x.HgncId.ToString() + '|' + (x.OnReverseStrand ? 'R' : 'F')); + var (mergedGenes, numOrphanEntries, numMergedEntries) = GetMergedGenes(hgncIdToGenes, isGrch37); + + mergedGenesByRef[kvp.Key] = mergedGenes; + + totalOrphanEntries += numOrphanEntries; + totalMergedEntries += numMergedEntries; + } + + data.Logger.WriteLine($"orphans: {totalOrphanEntries}, merged: {totalMergedEntries}"); + + return mergedGenesByRef; + } + + private static void AddGenes(Dictionary> source, + IDictionary> target) + { + foreach (var kvp in source) + { + if (target.TryGetValue(kvp.Key, out var targetGeneList)) + { + targetGeneList.AddRange(kvp.Value); + } + else + { + var geneList = new List(); + geneList.AddRange(kvp.Value); + target[kvp.Key] = geneList; + } + } + } + + private static (List MergedGenes, int NumOrphanEntries, int NumMergedEntries) GetMergedGenes( + Dictionary> hgncIdToGenes, bool isGrch37) + { + var mergedGenes = new List(); + int numOrphanEntries = 0; + int numMergedEntries = 0; + + foreach (var kvp in hgncIdToGenes) + { + if (kvp.Key.StartsWith("-1|") || kvp.Value.Count == 1) + { + var convertedGenes = ConvertToUgaGenes(kvp.Value, isGrch37); + mergedGenes.AddRange(convertedGenes); + numOrphanEntries += convertedGenes.Count; + continue; + } + + if (kvp.Value.Count > 2) throw new InvalidDataException("Found more than two genes when merging Ensembl and RefSeq genes."); + mergedGenes.Add(GetMergedGene(kvp.Value[0], kvp.Value[1], isGrch37)); + numMergedEntries++; + } + + return (mergedGenes, numOrphanEntries, numMergedEntries); + } + + private static List ConvertToUgaGenes(IEnumerable genes, bool isGrch37) + { + var ugaGenes = new List(); + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var gene in genes) + { + if (gene.GeneId == null) continue; + ugaGenes.Add(gene.ToUgaGene(isGrch37)); + } + + return ugaGenes; + } + + private static UgaGene GetMergedGene(MutableGene geneA, MutableGene geneB, bool isGrch37) + { + var (ensemblGene, refSeqGene) = geneA.GeneId.StartsWith("ENSG") ? (geneA, geneB) : (geneB, geneA); + + if (ensemblGene.Chromosome.Index != refSeqGene.Chromosome.Index) throw new InvalidDataException($"The two genes are on different chromosomes: {geneA.GeneId} & {geneB.GeneId}"); + if (ensemblGene.OnReverseStrand != refSeqGene.OnReverseStrand) throw new InvalidDataException($"Both genes do not have the same orientation: {geneA.GeneId} & {geneB.GeneId}"); + + IInterval interval = GetMergedInterval(ensemblGene, refSeqGene); + var (grch37, grch38) = isGrch37 ? (interval, null as IInterval) : (null, interval); + + return new UgaGene(ensemblGene.Chromosome, grch37, grch38, ensemblGene.OnReverseStrand, refSeqGene.GeneId, + ensemblGene.GeneId, ensemblGene.Symbol, ensemblGene.HgncId); + } + + private static IInterval GetMergedInterval(MutableGene geneA, MutableGene geneB) => + new Interval(Math.Min(geneA.Start, geneB.Start), Math.Max(geneA.End, geneB.End)); + } +} diff --git a/CacheUtils/Genes/GeneSymbolUpdater.cs b/CacheUtils/Genes/GeneSymbolUpdater.cs new file mode 100644 index 00000000..77b4672c --- /dev/null +++ b/CacheUtils/Genes/GeneSymbolUpdater.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface; + +namespace CacheUtils.Genes +{ + public sealed class GeneSymbolUpdater + { + private int _numUpdatedByHgncId; + private int _numUpdatedByEntrezGeneId; + private int _numUpdatedByEnsemblId; + private int _numUpdatedByRefSeqGff; + + private readonly ILogger _logger; + private readonly Dictionary _hgncIdToSymbol; + private readonly Dictionary _entrezGeneIdToSymbol; + private readonly Dictionary _ensemblIdToSymbol; + private readonly Dictionary _refseqGeneIdToSymbol; + + public GeneSymbolUpdater(ILogger logger, Dictionary hgncIdToSymbol, + Dictionary entrezGeneIdToSymbol, Dictionary ensemblIdToSymbol, + Dictionary refseqGeneIdToSymbol) + { + _logger = logger; + _hgncIdToSymbol = hgncIdToSymbol; + _entrezGeneIdToSymbol = entrezGeneIdToSymbol; + _ensemblIdToSymbol = ensemblIdToSymbol; + _refseqGeneIdToSymbol = refseqGeneIdToSymbol; + } + + public void Update(UgaGene[] mergedGenes) + { + _logger.Write("- updating gene symbols... "); + foreach (var gene in mergedGenes) UpdateGeneSymbol(gene); + _logger.WriteLine($"{_numUpdatedByHgncId} by HGNC id, {_numUpdatedByEntrezGeneId} by Entrez Gene ID, {_numUpdatedByEnsemblId} by Ensembl ID, {_numUpdatedByRefSeqGff} by RefSeq GFF"); + + int numGenesMissingSymbol = mergedGenes.Count(gene => string.IsNullOrEmpty(gene.Symbol)); + if (numGenesMissingSymbol > 0) throw new InvalidDataException($"{numGenesMissingSymbol} genes are missing symbols."); + } + + private void UpdateGeneSymbol(UgaGene gene) + { + string originalSymbol = gene.Symbol; + var isUpdated = UpdateBySymbolDict(gene, x => x.HgncId, x => x == -1, _hgncIdToSymbol); + + if (isUpdated) + { + if (gene.Symbol != originalSymbol) _numUpdatedByHgncId++; + return; + } + + isUpdated = UpdateBySymbolDict(gene, x => x.EntrezGeneId, string.IsNullOrEmpty, _entrezGeneIdToSymbol); + + if (isUpdated) + { + if (gene.Symbol != originalSymbol) _numUpdatedByEntrezGeneId++; + return; + } + + isUpdated = UpdateBySymbolDict(gene, x => x.EnsemblId, string.IsNullOrEmpty, _ensemblIdToSymbol); + + if (isUpdated) + { + if (gene.Symbol != originalSymbol) _numUpdatedByEnsemblId++; + return; + } + + isUpdated = UpdateBySymbolDict(gene, x => x.EntrezGeneId, string.IsNullOrEmpty, _refseqGeneIdToSymbol); + + // ReSharper disable once InvertIf + if (isUpdated) + { + if (gene.Symbol != originalSymbol) _numUpdatedByRefSeqGff++; + } + } + + private static bool UpdateBySymbolDict(UgaGene gene, Func idFunc, Func isEmpty, IReadOnlyDictionary idToSymbol) + { + var key = idFunc(gene); + if (isEmpty(key)) return false; + + if (!idToSymbol.TryGetValue(idFunc(gene), out var symbol)) return false; + gene.Symbol = symbol; + return true; + } + } +} diff --git a/CacheUtils/Genes/GeneUtilities.cs b/CacheUtils/Genes/GeneUtilities.cs new file mode 100644 index 00000000..41cf9a7f --- /dev/null +++ b/CacheUtils/Genes/GeneUtilities.cs @@ -0,0 +1,49 @@ +//using System.Collections.Generic; +//using System.Linq; +//using VariantAnnotation.Interface.AnnotatedPositions; + +//namespace CacheUtils.Genes +//{ +// public static class GeneUtilities +// { +// public static Dictionary> GetGenesById(List genes, bool isEnsembl) +// { +// var genesById = new Dictionary>(); + +// foreach (var gene in genes) +// { +// var geneId = isEnsembl +// ? gene.EnsemblId.ToString() +// : gene.EntrezGeneId.ToString(); + +// if (genesById.TryGetValue(geneId, out var oldGenes)) oldGenes.Add(gene); +// else genesById[geneId] = new List { gene }; +// } + +// return genesById; +// } + +// public static MutableGene GetRefSeqGeneById(List genes, string entrezGeneId) +// { +// return genes.FirstOrDefault(gene => gene.EntrezGeneId.ToString() == entrezGeneId); +// } + +// public static Dictionary> GetGenesBySymbol(List genes) +// { +// var genesBySymbol = new Dictionary>(); + +// foreach (var gene in genes) +// { +// if (genesBySymbol.TryGetValue(gene.Symbol, out var oldGenes)) oldGenes.Add(gene); +// else genesBySymbol[gene.Symbol] = new List { gene }; +// } + +// return genesBySymbol; +// } + +// public static List GetGenesByDataSource(List genes, Source desiredDataSource) +// { +// return genes.Where(gene => gene.Source == desiredDataSource).ToList(); +// } +// } +//} diff --git a/CacheUtils/Genes/HgncIdConsolidator.cs b/CacheUtils/Genes/HgncIdConsolidator.cs new file mode 100644 index 00000000..ec173bc3 --- /dev/null +++ b/CacheUtils/Genes/HgncIdConsolidator.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes.Utilities; + +namespace CacheUtils.Genes +{ + public static class HgncIdConsolidator + { + public static int Consolidate(this Dictionary> genesByRef) + { + int numHgncIds = 0; + + foreach (var refKvp in genesByRef.OrderBy(x => x.Key)) + { + var genesByHgncId = refKvp.Value.Where(gene => gene.HgncId != -1).GetMultiValueDict(x => x.HgncId); + + foreach (var kvp in genesByHgncId) + { + if (kvp.Value.Count <= 1) continue; + CreateAggregateGene(kvp.Value.OrderBy(x => x.Start).ThenBy(x => x.End).ToList()); + } + + numHgncIds += refKvp.Value.Count(gene => gene.HgncId != -1); + } + + return numHgncIds; + } + + private static void CreateAggregateGene(IReadOnlyList genes) + { + var seedGene = genes[0]; + for (int i = 1; i < genes.Count; i++) + { + genes[i].GeneId = null; + genes[i].HgncId = -1; + seedGene.End = Math.Max(seedGene.End, genes[i].End); + } + } + } +} diff --git a/CacheUtils/Genes/HgncIdUpdater.cs b/CacheUtils/Genes/HgncIdUpdater.cs new file mode 100644 index 00000000..543520ef --- /dev/null +++ b/CacheUtils/Genes/HgncIdUpdater.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.Utilities; +using VariantAnnotation.Algorithms; + +namespace CacheUtils.Genes +{ + public static class HgncIdUpdater + { + public static Dictionary> Update(this IEnumerable hgncGenes, + Dictionary> genesByRef, Func idFunc) + { + var geneIdToHgncId = hgncGenes.GetSingleValueDict(idFunc); + foreach (var kvp in genesByRef) ReplaceHgncIds(kvp.Value, geneIdToHgncId); + return genesByRef; + } + + private static void ReplaceHgncIds(IEnumerable genes, IReadOnlyDictionary geneIdToHgncGene) + { + foreach (var gene in genes) + { + gene.HgncId = -1; + if (!geneIdToHgncGene.TryGetValue(gene.GeneId, out var hgncGene)) continue; + if (!IntervalUtilities.Overlaps(hgncGene.Start, hgncGene.End, gene.Start, gene.End)) continue; + + gene.HgncId = hgncGene.HgncId; + } + } + } +} diff --git a/CacheUtils/Genes/IO/AssemblyReader.cs b/CacheUtils/Genes/IO/AssemblyReader.cs new file mode 100644 index 00000000..4a1ae37a --- /dev/null +++ b/CacheUtils/Genes/IO/AssemblyReader.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.IO +{ + public static class AssemblyReader + { + private const int AccessionIndex = 6; + private const int UcscIndex = 9; + + public static IDictionary GetAccessionToChromosome(StreamReader reader, + IDictionary refNameToChromosome) + { + var accessionToChromosome = new Dictionary(); + + while (true) + { + var line = reader.ReadLine(); + if (line == null) break; + + if (line.StartsWith("#")) continue; + + var cols = line.Split('\t'); + var accession = cols[AccessionIndex]; + var ucscName = cols[UcscIndex]; + + if (!refNameToChromosome.TryGetValue(ucscName, out IChromosome chromosome)) continue; + accessionToChromosome[accession] = chromosome; + } + + return accessionToChromosome; + } + } +} diff --git a/CacheUtils/Genes/IO/EnsemblGtfReader.cs b/CacheUtils/Genes/IO/EnsemblGtfReader.cs new file mode 100644 index 00000000..5c7287cb --- /dev/null +++ b/CacheUtils/Genes/IO/EnsemblGtfReader.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; +using ErrorHandling.Exceptions; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.IO +{ + public sealed class EnsemblGtfReader : IDisposable + { + private readonly IDictionary _refNameToChromosome; + private readonly StreamReader _reader; + + private const int ChromosomeIndex = 0; + private const int FeatureTypeIndex = 2; + private const int StartIndex = 3; + private const int EndIndex = 4; + private const int InfoIndex = 8; + + public EnsemblGtfReader(StreamReader reader, IDictionary refNameToChromosome) + { + _refNameToChromosome = refNameToChromosome; + _reader = reader; + _reader.ReadLine(); + } + + public EnsemblGene[] GetGenes() + { + var genes = new List(); + + while (true) + { + var line = _reader.ReadLine(); + if (line == null) break; + + if (line.StartsWith("#")) continue; + + var cols = line.Split('\t'); + if (cols.Length != 9) throw new InvalidDataException($"Expected 9 columns but found {cols.Length} when parsing the GFF entry."); + + var featureType = cols[FeatureTypeIndex]; + if (featureType != "gene") continue; + + AddGene(cols, genes); + } + + return genes.ToArray(); + } + + private void AddGene(string[] cols, ICollection genes) + { + var chromosome = RefSeqGffReader.GetChromosome(cols[ChromosomeIndex], _refNameToChromosome); + if (chromosome == null) return; + + try + { + int start = int.Parse(cols[StartIndex]); + int end = int.Parse(cols[EndIndex]); + var infoCols = cols[InfoIndex].Split(';', StringSplitOptions.RemoveEmptyEntries); + var info = GetGffFields(infoCols); + + var gene = new EnsemblGene(chromosome, start, end, info.EnsemblGeneId, info.Name); + genes.Add(gene); + } + catch (Exception) + { + Console.WriteLine(); + Console.WriteLine("Offending line: {0}", string.Join('\t', cols)); + for (int i = 0; i < cols.Length; i++) Console.WriteLine("- col {0}: [{1}]", i, cols[i]); + throw; + } + } + + private static (string EnsemblGeneId, string Name) GetGffFields(string[] cols) + { + string ensemblId = null; + string symbol = null; + + foreach (var col in cols) + { + var kvp = col.Trim().Split(' '); + var key = kvp[0]; + var value = kvp[1].Trim('\"'); + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (key) + { + case "gene_id": + ensemblId = value; + break; + case "gene_name": + symbol = value; + break; + } + } + + if (string.IsNullOrEmpty(ensemblId) || string.IsNullOrEmpty(symbol)) + { + throw new UserErrorException(string.Join('\t', cols)); + } + + return (ensemblId, symbol); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/Genes/IO/GeneInfoReader.cs b/CacheUtils/Genes/IO/GeneInfoReader.cs new file mode 100644 index 00000000..e02f00c2 --- /dev/null +++ b/CacheUtils/Genes/IO/GeneInfoReader.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; + +namespace CacheUtils.Genes.IO +{ + public sealed class GeneInfoReader : IDisposable + { + private readonly StreamReader _reader; + + private int _entrezGeneIndex = -1; + private int _symbolIndex = -1; + private int _dbXrefsIndex = -1; + + public GeneInfoReader(StreamReader reader) + { + _reader = reader; + var headerLine = _reader.ReadLine(); + SetColumnIndices(headerLine); + } + + private void SetColumnIndices(string line) + { + if (line.StartsWith("#Format: ")) line = line.Substring(9); + if (line.StartsWith("#")) line = line.Substring(1); + + var cols = line.Split('\t'); + if (cols.Length == 1) cols = line.Split(' '); + + for (int index = 0; index < cols.Length; index++) + { + var header = cols[index]; + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (header) + { + case "dbXrefs": + _dbXrefsIndex = index; + break; + case "GeneID": + _entrezGeneIndex = index; + break; + case "Symbol": + _symbolIndex = index; + break; + } + } + + // ReSharper disable once InvertIf + if (_entrezGeneIndex == -1 || _symbolIndex == -1) { + Console.WriteLine("_dbXrefsIndex: {0}", _dbXrefsIndex); + Console.WriteLine("_entrezGeneIndex: {0}", _entrezGeneIndex); + Console.WriteLine("_symbolIndex: {0}", _symbolIndex); + + throw new InvalidDataException("Not all of the indices were set."); + } + } + + /// + /// retrieves the next gene. Returns false if there are no more genes available + /// + private GeneInfo Next() + { + string line = _reader.ReadLine(); + if (line == null) return null; + + if (!line.StartsWith("9606")) return null; + + var cols = line.Split('\t'); + if (cols.Length != 16) throw new InvalidDataException($"Expected 16 columns but found {cols.Length} when parsing the gene entry:\n[{line}]"); + + try + { + var entrezGeneId = cols[_entrezGeneIndex]; + var symbol = cols[_symbolIndex]; + + return new GeneInfo(symbol, entrezGeneId); + } + catch (Exception) + { + Console.WriteLine("Offending line: {0}", line); + for (int i = 0; i < cols.Length; i++) Console.WriteLine("- col {0}: [{1}]", i, cols[i]); + throw; + } + } + + public GeneInfo[] GetGenes() + { + var list = new List(); + + while (true) + { + var gene = Next(); + if (gene == null) break; + list.Add(gene); + } + + return list.ToArray(); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/Genes/IO/HgncReader.cs b/CacheUtils/Genes/IO/HgncReader.cs new file mode 100644 index 00000000..aedec4b5 --- /dev/null +++ b/CacheUtils/Genes/IO/HgncReader.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; +using CommonUtilities; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.IO +{ + public sealed class HgncReader : IDisposable + { + private readonly IDictionary _refNameToChromosome; + private readonly StreamReader _reader; + + private const int HgncIdIndex = 0; + private const int SymbolIndex = 1; + private const int LocationIndex = 6; + private const int EntrezIdIndex = 18; + private const int EnsemblIdIndex = 19; + + public HgncReader(Stream stream, IDictionary refNameToChromosome) + { + _refNameToChromosome = refNameToChromosome; + _reader = new StreamReader(stream); + _reader.ReadLine(); + } + + /// + /// retrieves the next gene. Returns false if there are no more genes available + /// + private HgncGene Next() + { + string line = _reader.ReadLine(); + if (line == null) return null; + + var cols = line.Split('\t'); + if (cols.Length != 49) throw new InvalidDataException($"Expected 48 columns but found {cols.Length} when parsing the gene entry:[{line}]"); + + try + { + var hgncId = int.Parse(cols[HgncIdIndex].Substring(5)); + var symbol = cols[SymbolIndex]; + var chromosome = GetChromosome(cols[LocationIndex]); + var entrezGeneId = GetId(cols[EntrezIdIndex]); + var ensemblId = GetId(cols[EnsemblIdIndex]); + + return new HgncGene(chromosome, -1, -1, symbol, entrezGeneId, ensemblId, hgncId); + } + catch (Exception) + { + Console.WriteLine("Offending line: {0}", line); + for (int i = 0; i < cols.Length; i++) Console.WriteLine("- col {0}: [{1}]", i, cols[i]); + throw; + } + } + + public HgncGene[] GetGenes() + { + var list = new List(); + + while (true) + { + var gene = Next(); + if (gene == null) break; + list.Add(gene); + } + + return list.ToArray(); + } + + private IChromosome GetChromosome(string cytogeneticBand) + { + int armPos = GetArmPos(cytogeneticBand); + if (armPos == -1) return new EmptyChromosome(cytogeneticBand); + + var chrName = cytogeneticBand.Substring(0, armPos); + return ReferenceNameUtilities.GetChromosome(_refNameToChromosome, chrName); + } + + private static int GetArmPos(string cytogeneticBand) + { + int pos = cytogeneticBand.IndexOf('p'); + if (pos != -1) return pos; + + pos = cytogeneticBand.IndexOf('q'); + return pos; + } + + private static string GetId(string s) => string.IsNullOrEmpty(s) ? null : s; + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/Genes/IO/RefSeqGffReader.cs b/CacheUtils/Genes/IO/RefSeqGffReader.cs new file mode 100644 index 00000000..b0345079 --- /dev/null +++ b/CacheUtils/Genes/IO/RefSeqGffReader.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.IO +{ + public sealed class RefSeqGffReader : IDisposable + { + private readonly IDictionary _accessionIdToChromosome; + private readonly StreamReader _reader; + + private const int AccessionIndex = 0; + private const int FeatureTypeIndex = 2; + private const int StartIndex = 3; + private const int EndIndex = 4; + private const int StrandIndex = 6; + private const int InfoIndex = 8; + + public RefSeqGffReader(StreamReader reader, IDictionary accessionIdToChromosome) + { + _accessionIdToChromosome = accessionIdToChromosome; + _reader = reader; + _reader.ReadLine(); + } + + public void AddGenes(List refSeqGenes) + { + while (true) + { + var line = _reader.ReadLine(); + if (line == null) break; + + if (line.StartsWith("#")) continue; + + var cols = line.Split('\t'); + if (cols.Length != 9) throw new InvalidDataException($"Expected 9 columns but found {cols.Length} when parsing the GFF entry."); + + var featureType = cols[FeatureTypeIndex]; + if (featureType == "gene") AddGene(cols, refSeqGenes); + } + } + + private void AddGene(string[] cols, ICollection refSeqGenes) + { + var chromosome = GetChromosome(cols[AccessionIndex], _accessionIdToChromosome); + if (chromosome == null) return; + + try + { + int start = int.Parse(cols[StartIndex]); + int end = int.Parse(cols[EndIndex]); + bool onReverseStrand = cols[StrandIndex] == "-"; + var infoCols = cols[InfoIndex].Split(';'); + var info = GetGffFields(infoCols); + + var gene = new RefSeqGene(chromosome, start, end, onReverseStrand, info.EntrezGeneId, info.Name, info.HgncId); + refSeqGenes.Add(gene); + } + catch (Exception) + { + Console.WriteLine(); + Console.WriteLine("Offending line: {0}", string.Join('\t', cols)); + for (int i = 0; i < cols.Length; i++) Console.WriteLine("- col {0}: [{1}]", i, cols[i]); + throw; + } + } + + internal static IChromosome GetChromosome(string referenceName, IDictionary refNameToChromosome) + { + refNameToChromosome.TryGetValue(referenceName, out var chromosome); + return chromosome; + } + + private static (string Name, string EntrezGeneId, int HgncId) + GetGffFields(IEnumerable cols) + { + string entrezGeneId = null; + string name = null; + int hgncId = -1; + + foreach (var col in cols) + { + var kvp = col.Split('='); + var key = kvp[0]; + var value = kvp[1]; + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (key) + { + case "Dbxref": + var ids = value.Split(','); + (entrezGeneId, hgncId) = GetIds(ids); + break; + case "Name": + name = value; + break; + } + } + + return (name, entrezGeneId, hgncId); + } + + private static (string EntrezGeneId, int HgncId) GetIds(IEnumerable ids) + { + string entrezGeneId = null; + int hgncId = -1; + + foreach (var idPair in ids) + { + var cols = idPair.Split(':'); + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (cols[0]) + { + case "HGNC": + int lastIndex = cols.Length - 1; + if (cols[lastIndex] != "HGNC") hgncId = int.Parse(cols[lastIndex]); + break; + case "GeneID": + entrezGeneId = cols[1]; + break; + } + } + + return (entrezGeneId, hgncId); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/Genes/IO/UgaGeneReader.cs b/CacheUtils/Genes/IO/UgaGeneReader.cs new file mode 100644 index 00000000..fb05ec99 --- /dev/null +++ b/CacheUtils/Genes/IO/UgaGeneReader.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using CacheUtils.Genes.DataStructures; +using CommonUtilities; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.Genes.IO +{ + public sealed class UgaGeneReader : IDisposable + { + private readonly IDictionary _refNameToChromosome; + private readonly StreamReader _reader; + + public UgaGeneReader(Stream stream, IDictionary refNameToChromosome, bool leaveOpen = false) + { + _refNameToChromosome = refNameToChromosome; + _reader = new StreamReader(stream, Encoding.ASCII, leaveOpen); + _reader.ReadLine(); + } + + public void Dispose() => _reader.Dispose(); + + public UgaGene[] GetGenes() + { + var genes = new List(); + + while (true) + { + var gene = GetNextGene(); + if (gene == null) break; + genes.Add(gene); + } + + return genes.ToArray(); + } + + private UgaGene GetNextGene() + { + var line = _reader.ReadLine(); + if (line == null) return null; + + var cols = line.Split('\t'); + if (cols.Length != 11) throw new InvalidDataException($"Expected 11 columns, but found {cols.Length} columns."); + + var ucscRefName = cols[0]; + var chromosome = ReferenceNameUtilities.GetChromosome(_refNameToChromosome, ucscRefName); + var symbol = cols[2]; + var start37 = int.Parse(cols[3]); + var end37 = int.Parse(cols[4]); + var start38 = int.Parse(cols[5]); + var end38 = int.Parse(cols[6]); + var onReverseStrand = cols[7] == "R"; + var hgncId = int.Parse(cols[8]); + var ensemblId = cols[9]; + var entrezGeneId = cols[10]; + + var grch37 = new Interval(start37, end37); + var grch38 = new Interval(start38, end38); + return new UgaGene(chromosome, grch37, grch38, onReverseStrand, entrezGeneId, ensemblId, symbol, hgncId); + } + } +} diff --git a/CacheUtils/Genes/IO/UgaGeneWriter.cs b/CacheUtils/Genes/IO/UgaGeneWriter.cs new file mode 100644 index 00000000..9853024f --- /dev/null +++ b/CacheUtils/Genes/IO/UgaGeneWriter.cs @@ -0,0 +1,25 @@ +using System; +using System.IO; +using System.Text; +using CacheUtils.Genes.DataStructures; + +namespace CacheUtils.Genes.IO +{ + public sealed class UgaGeneWriter : IDisposable + { + private readonly StreamWriter _writer; + + public UgaGeneWriter(Stream stream, bool leaveOpen = false) + { + _writer = new StreamWriter(stream, Encoding.ASCII, 1024, leaveOpen); + } + + public void Dispose() => _writer.Dispose(); + + public void Write(UgaGene[] genes) + { + _writer.WriteLine(genes.Length); + foreach (var gene in genes) _writer.WriteLine(gene.ToString()); + } + } +} diff --git a/CacheUtils/Genes/UgaAssemblyCombiner.cs b/CacheUtils/Genes/UgaAssemblyCombiner.cs new file mode 100644 index 00000000..fbea41cc --- /dev/null +++ b/CacheUtils/Genes/UgaAssemblyCombiner.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.Genes.Combiners; +using CacheUtils.Genes.DataStructures; +using CacheUtils.TranscriptCache.Comparers; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.Genes +{ + public static class UgaAssemblyCombiner + { + internal static readonly List EmptyUgaGenes = new List(); + + public static UgaGene[] Combine(Dictionary> genesByRef37, + Dictionary> genesByRef38) + { + var referenceIndices = GetReferenceIndices(genesByRef37.Keys, genesByRef38.Keys); + var combinedGenes = new List(); + + var combiners = GetCombiners(); + + foreach (var refIndex in referenceIndices.OrderBy(x => x)) + { + var ugaGenesByRef = CombineByReference(GetUgaGenesByRef(genesByRef37, refIndex), + GetUgaGenesByRef(genesByRef38, refIndex), combiners); + combinedGenes.AddRange(ugaGenesByRef); + } + + return combinedGenes.OrderBy(x => x.Chromosome.Index).ThenBy(x => MinCoordinate(x, y => y.Start)) + .ThenBy(x => MinCoordinate(x, y => y.End)).ToArray(); + } + + private static List GetCombiners() => + new List {new HgncIdCombiner(), new PartitionCombiner()}; + + private static IEnumerable GetReferenceIndices(IEnumerable keysA, IEnumerable keysB) + { + var referenceIndices = new HashSet(); + foreach (var key in keysA) referenceIndices.Add(key); + foreach (var key in keysB) referenceIndices.Add(key); + return referenceIndices.OrderBy(x => x); + } + + private static IEnumerable CombineByReference(IEnumerable uga37, IEnumerable uga38, + IEnumerable combiners) + { + var combinedGenes = new List(); + + var remainingUga37 = GetRemainingGenes(uga37); + var remainingUga38 = GetRemainingGenes(uga38); + + foreach (var combiner in combiners) combiner.Combine(combinedGenes, remainingUga37, remainingUga38); + + if (remainingUga37.Count > 0 || remainingUga38.Count > 0) + throw new InvalidDataException($"Expected the combiners to handle all genes, but some still remain. GRCh37: {remainingUga37.Count}, GRCh38: {remainingUga38.Count}"); + + return combinedGenes; + } + + private static HashSet GetRemainingGenes(IEnumerable genes) + { + var comparer = new UgaGeneComparer(); + var geneSet = new HashSet(comparer); + foreach (var gene in genes) geneSet.Add(gene); + return geneSet; + } + + private static IEnumerable GetUgaGenesByRef(IReadOnlyDictionary> refIndexToUgaGenes, + ushort refIndex) => refIndexToUgaGenes.TryGetValue(refIndex, out var genes) ? genes : EmptyUgaGenes; + + private static int MinCoordinate(UgaGene gene, Func coordFunc) => coordFunc(gene.GRCh37 ?? gene.GRCh38); + } +} diff --git a/CacheUtils/Genes/Utilities/DictionaryUtilities.cs b/CacheUtils/Genes/Utilities/DictionaryUtilities.cs new file mode 100644 index 00000000..f955ca4f --- /dev/null +++ b/CacheUtils/Genes/Utilities/DictionaryUtilities.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace CacheUtils.Genes.Utilities +{ + public static class DictionaryUtilities + { + public static Dictionary GetSingleValueDict(this IEnumerable elements, Func idFunc) + { + var dict = new Dictionary(); + foreach (var element in elements) + { + var key = idFunc(element); + if (key == null) continue; + if (dict.ContainsKey(key)) throw new InvalidDataException($"Multiple entries for [{key}] already exist in the dictionary."); + dict[key] = element; + } + return dict; + } + + public static Dictionary> GetMultiValueDict(this IEnumerable elements, Func idFunc) + { + var dict = new Dictionary>(); + foreach (var element in elements) + { + var key = idFunc(element); + if (key == null) continue; + if (dict.TryGetValue(key, out var geneList)) geneList.Add(element); + else dict[key] = new List { element }; + } + return dict; + } + + public static Dictionary GetKeyValueDict(this IEnumerable elements, Func keyFunc, Func valueFunc) + { + var dict = new Dictionary(); + foreach (var element in elements) + { + var key = keyFunc(element); + var value = valueFunc(element); + if (key == null || value == null) continue; + dict[key] = value; + } + return dict; + } + + public static HashSet GetSet(this IEnumerable elements, Func idFunc) + { + var set = new HashSet(); + foreach (var element in elements) + { + var key = idFunc(element); + set.Add(key); + } + return set; + } + + public static Dictionary CreateIndex(this IEnumerable elements) + { + var index = new Dictionary(); + int currentIndex = 0; + foreach (var element in elements) index[element] = currentIndex++; + return index; + } + } +} diff --git a/CacheUtils/Helpers/BioTypeHelper.cs b/CacheUtils/Helpers/BioTypeHelper.cs new file mode 100644 index 00000000..02e6398b --- /dev/null +++ b/CacheUtils/Helpers/BioTypeHelper.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.Helpers +{ + public static class BioTypeHelper + { + private static readonly Dictionary StringToBioTypes; + + static BioTypeHelper() + { + StringToBioTypes = new Dictionary + { + ["ambiguous_orf"] = BioType.ambiguous_orf, + ["antisense"] = BioType.antisense, + ["antisense_RNA"] = BioType.antisense_RNA, + ["bidirectional_promoter_lncrna"] = BioType.bidirectional_promoter_lncrna, + ["guide_RNA"] = BioType.guide_RNA, + ["IG_C_gene"] = BioType.IG_C_gene, + ["IG_C_pseudogene"] = BioType.IG_C_pseudogene, + ["IG_D_gene"] = BioType.IG_D_gene, + ["IG_J_gene"] = BioType.IG_J_gene, + ["IG_J_pseudogene"] = BioType.IG_J_pseudogene, + ["IG_V_gene"] = BioType.IG_V_gene, + ["IG_V_pseudogene"] = BioType.IG_V_pseudogene, + ["lincRNA"] = BioType.lincRNA, + ["lncRNA"] = BioType.lncRNA, + ["macro_lncRNA"] = BioType.macro_lncRNA, + ["mRNA"] = BioType.mRNA, + ["miRNA"] = BioType.miRNA, + ["misc_RNA"] = BioType.misc_RNA, + ["Mt_rRNA"] = BioType.Mt_rRNA, + ["Mt_tRNA"] = BioType.Mt_tRNA, + ["non_coding"] = BioType.non_coding, + ["nonsense_mediated_decay"] = BioType.nonsense_mediated_decay, + ["non_stop_decay"] = BioType.non_stop_decay, + ["polymorphic_pseudogene"] = BioType.polymorphic_pseudogene, + ["processed_pseudogene"] = BioType.processed_pseudogene, + ["processed_transcript"] = BioType.processed_transcript, + ["protein_coding"] = BioType.protein_coding, + ["pseudogene"] = BioType.pseudogene, + ["retained_intron"] = BioType.retained_intron, + ["retrotransposed"] = BioType.retrotransposed, + ["RNase_MRP_RNA"] = BioType.RNase_MRP_RNA, + ["RNase_P_RNA"] = BioType.RNase_P_RNA, + ["rRNA"] = BioType.rRNA, + ["ribozyme"] = BioType.ribozyme, + ["sense_intronic"] = BioType.sense_intronic, + ["sense_overlapping"] = BioType.sense_overlapping, + ["SRP_RNA"] = BioType.SRP_RNA, + ["sRNA"] = BioType.sRNA, + ["scRNA"] = BioType.scRNA, + ["scaRNA"] = BioType.scaRNA, + ["snRNA"] = BioType.snRNA, + ["snoRNA"] = BioType.snoRNA, + ["telomerase_RNA"] = BioType.telomerase_RNA, + ["three_prime_overlapping_ncrna"] = BioType.three_prime_overlapping_ncrna, + ["transcribed_processed_pseudogene"] = BioType.transcribed_processed_pseudogene, + ["translated_unprocessed_pseudogene"] = BioType.translated_unprocessed_pseudogene, + ["transcribed_unitary_pseudogene"] = BioType.transcribed_unitary_pseudogene, + ["TEC"] = BioType.TEC, + ["tRNA"] = BioType.tRNA, + ["translated_processed_pseudogene"] = BioType.translated_processed_pseudogene, + ["transcribed_unprocessed_pseudogene"] = BioType.transcribed_unprocessed_pseudogene, + ["TR_C_gene"] = BioType.TR_C_gene, + ["TR_D_gene"] = BioType.TR_D_gene, + ["TR_J_gene"] = BioType.TR_J_gene, + ["TR_J_pseudogene"] = BioType.TR_J_pseudogene, + ["TR_V_gene"] = BioType.TR_V_gene, + ["TR_V_pseudogene"] = BioType.TR_V_pseudogene, + ["unitary_pseudogene"] = BioType.unitary_pseudogene, + ["unprocessed_pseudogene"] = BioType.unprocessed_pseudogene, + ["vaultRNA"] = BioType.vaultRNA, + ["Y_RNA"] = BioType.Y_RNA + }; + } + + public static BioType GetBioType(string s) + { + if (s == null) return BioType.Unknown; + return !StringToBioTypes.TryGetValue(s, out var ret) ? BioType.Unknown : ret; + } + } +} diff --git a/CacheUtils/Helpers/GeneSymbolSourceHelper.cs b/CacheUtils/Helpers/GeneSymbolSourceHelper.cs new file mode 100644 index 00000000..cd56e36b --- /dev/null +++ b/CacheUtils/Helpers/GeneSymbolSourceHelper.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using CacheUtils.DataDumperImport.DataStructures; + +namespace CacheUtils.Helpers +{ + public static class GeneSymbolSourceHelper + { + private static readonly Dictionary StringToGeneSymbolSources; + + static GeneSymbolSourceHelper() + { + StringToGeneSymbolSources = new Dictionary + { + ["Clone_based_ensembl_gene"] = GeneSymbolSource.CloneBasedEnsemblGene, + ["Clone_based_vega_gene"] = GeneSymbolSource.CloneBasedVegaGene, + ["EntrezGene"] = GeneSymbolSource.EntrezGene, + ["HGNC"] = GeneSymbolSource.HGNC, + ["LRG"] = GeneSymbolSource.LRG, + ["miRBase"] = GeneSymbolSource.miRBase, + ["NCBI"] = GeneSymbolSource.NCBI, + ["RFAM"] = GeneSymbolSource.RFAM, + ["Uniprot_gn"] = GeneSymbolSource.UniProtGeneName + }; + } + + public static GeneSymbolSource GetGeneSymbolSource(string s) + { + if (s == null) return GeneSymbolSource.Unknown; + return !StringToGeneSymbolSources.TryGetValue(s, out var ret) ? GeneSymbolSource.Unknown : ret; + } + } +} diff --git a/CacheUtils/Helpers/RegulatoryRegionTypeHelper.cs b/CacheUtils/Helpers/RegulatoryRegionTypeHelper.cs new file mode 100644 index 00000000..c5374ca2 --- /dev/null +++ b/CacheUtils/Helpers/RegulatoryRegionTypeHelper.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Interface.Caches; + +namespace CacheUtils.Helpers +{ + public static class RegulatoryRegionTypeHelper + { + private static readonly Dictionary StringToRegulatoryRegionTypes; + + static RegulatoryRegionTypeHelper() + { + StringToRegulatoryRegionTypes = new Dictionary + { + ["CTCF_binding_site"] = RegulatoryRegionType.CTCF_binding_site, + ["TF_binding_site"] = RegulatoryRegionType.TF_binding_site, + ["enhancer"] = RegulatoryRegionType.enhancer, + ["open_chromatin_region"] = RegulatoryRegionType.open_chromatin_region, + ["promoter"] = RegulatoryRegionType.promoter, + ["promoter_flanking_region"] = RegulatoryRegionType.promoter_flanking_region + }; + } + + public static RegulatoryRegionType GetRegulatoryRegionType(string s) + { + if (!StringToRegulatoryRegionTypes.TryGetValue(s, out var ret)) + { + throw new InvalidDataException($"Unable to convert [{s}] to a regulatory region"); + } + + return ret; + } + } +} diff --git a/CacheUtils/Helpers/SequenceHelper.cs b/CacheUtils/Helpers/SequenceHelper.cs new file mode 100644 index 00000000..07d3dbc5 --- /dev/null +++ b/CacheUtils/Helpers/SequenceHelper.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Sequence; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Helpers +{ + public static class SequenceHelper + { + private static CompressedSequenceReader GetSequenceReader(string referencePath) => + new CompressedSequenceReader(FileUtilities.GetReadStream(referencePath)); + + public static (IDictionary refIndexToChromosome, IDictionary + refNameToChromosome, int numRefSeqs) GetDictionaries(string referencePath) + { + IDictionary refIndexToChromosome; + IDictionary refNameToChromosome; + int numRefSeqs; + + using (var reader = GetSequenceReader(referencePath)) + { + refIndexToChromosome = reader.RefIndexToChromosome; + refNameToChromosome = reader.RefNameToChromosome; + numRefSeqs = reader.NumRefSeqs; + } + + return (refIndexToChromosome, refNameToChromosome, numRefSeqs); + } + } +} diff --git a/CacheUtils/Helpers/TranscriptCacheHelper.cs b/CacheUtils/Helpers/TranscriptCacheHelper.cs new file mode 100644 index 00000000..8223ae87 --- /dev/null +++ b/CacheUtils/Helpers/TranscriptCacheHelper.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Caches; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Helpers +{ + public static class TranscriptCacheHelper + { + public static TranscriptCacheData GetCache(string cachePath, + IDictionary refIndexToChromosome) + { + if (!File.Exists(cachePath)) throw new FileNotFoundException($"Could not find {cachePath}"); + + TranscriptCacheData cache; + using (var reader = new TranscriptCacheReader(FileUtilities.GetReadStream(cachePath))) cache = reader.Read(refIndexToChromosome); + return cache; + } + } +} diff --git a/CacheUtils/IO/Caches/TranscriptCacheWriter.cs b/CacheUtils/IO/Caches/TranscriptCacheWriter.cs deleted file mode 100644 index 1b80a62f..00000000 --- a/CacheUtils/IO/Caches/TranscriptCacheWriter.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Text; -using Compression.Algorithms; -using Compression.FileHandling; -using VariantAnnotation.Interface.AnnotatedPositions; -using VariantAnnotation.Interface.Intervals; -using VariantAnnotation.Interface.IO; -using VariantAnnotation.IO; -using VariantAnnotation.IO.Caches; - -namespace CacheUtils.IO.Caches -{ - public sealed class TranscriptCacheWriter : IDisposable - { - private readonly BlockStream _blockStream; - private readonly ExtendedBinaryWriter _writer; - private readonly CacheHeader _header; - private readonly bool _leaveOpen; - - public TranscriptCacheWriter(Stream stream, CacheHeader header, bool leaveOpen = false) - { - _blockStream = new BlockStream(new Zstandard(), stream, CompressionMode.Compress); - _writer = new ExtendedBinaryWriter(_blockStream, Encoding.UTF8, leaveOpen); - _header = header; - _leaveOpen = leaveOpen; - } - - public void Dispose() - { - if (!_leaveOpen) _blockStream.Dispose(); - _writer.Dispose(); - } - - /// - /// writes the annotations to the current database file - /// - public void Write(ITranscript[] transcripts, IRegulatoryRegion[] regulatoryRegions, IGene[] genes, - IInterval[] introns, IInterval[] mirnas, string[] peptideSeqs) - { - _blockStream.WriteHeader(_header.Write); - - WriteItems(_writer, regulatoryRegions, x => x.Write(_writer)); - WriteItems(_writer, genes, x => x.Write(_writer)); - WriteItems(_writer, introns, x => GetInterval(x).Write(_writer)); - WriteItems(_writer, mirnas, x => GetInterval(x).Write(_writer)); - WriteItems(_writer, peptideSeqs, x => _writer.WriteOptAscii(x)); - - var geneIndices = CreateIndex(genes); - var intronIndices = CreateIndex(introns); - var microRnaIndices = CreateIndex(mirnas); - var peptideIndices = CreateIndex(peptideSeqs); - - WriteItems(_writer, transcripts, x => x.Write(_writer, geneIndices, intronIndices, microRnaIndices, peptideIndices)); - } - - private static Interval GetInterval(IInterval tempInterval) => (Interval)tempInterval; - - internal static void WriteItems(IExtendedBinaryWriter writer, T[] items, Action writeMethod) - { - if (items == null) - { - writer.WriteOpt(0); - } - else - { - writer.WriteOpt(items.Length); - foreach (var item in items) writeMethod(item); - } - - writer.Write(CacheConstants.GuardInt); - } - - /// - /// creates an index out of a array - /// - internal static Dictionary CreateIndex(T[] array) - { - var index = new Dictionary(); - if (array == null) return index; - - for (int currentIndex = 0; currentIndex < array.Length; currentIndex++) - index[array[currentIndex]] = currentIndex; - - return index; - } - } -} diff --git a/CacheUtils/IntermediateIO/CcdsReader.cs b/CacheUtils/IntermediateIO/CcdsReader.cs new file mode 100644 index 00000000..77fc75b7 --- /dev/null +++ b/CacheUtils/IntermediateIO/CcdsReader.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Utilities; + +namespace CacheUtils.IntermediateIO +{ + public static class CcdsReader + { + private const int CcdsIdIndex = 0; + private const int NucleotideIdIndex = 4; + + public static Dictionary> GetCcdsIdToEnsemblId(string ccdsPath) + { + var ccdsIdToEnsemblId = new Dictionary>(); + + using (var reader = new StreamReader(FileUtilities.GetReadStream(ccdsPath))) + { + while (true) + { + var line = reader.ReadLine(); + if (line == null) break; + if (line.StartsWith("#")) continue; + + var cols = line.Split('\t'); + if (cols.Length != 8) throw new InvalidDataException($"Expected 8 columns, but found {cols.Length}: [{line}]"); + + var nucleotideId = cols[NucleotideIdIndex]; + if (!nucleotideId.StartsWith("ENST")) continue; + + var ccds = FormatUtilities.SplitVersion(cols[CcdsIdIndex]); + var ensembl = FormatUtilities.SplitVersion(nucleotideId); + + if (ccdsIdToEnsemblId.TryGetValue(ccds.Id, out var ensemblList)) ensemblList.Add(ensembl.Id); + else ccdsIdToEnsemblId[ccds.Id] = new List { ensembl.Id }; + } + } + + return ccdsIdToEnsemblId; + } + } +} diff --git a/CacheUtils/IntermediateIO/GenbankReader.cs b/CacheUtils/IntermediateIO/GenbankReader.cs new file mode 100644 index 00000000..c80b09f8 --- /dev/null +++ b/CacheUtils/IntermediateIO/GenbankReader.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genbank; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class GenbankReader : IDisposable + { + private readonly StreamReader _reader; + + internal GenbankReader(Stream stream) + { + _reader = new StreamReader(stream); + IntermediateIoCommon.ReadHeader(_reader, IntermediateIoCommon.FileType.Genbank); + } + + public Dictionary GetIdToGenbank() + { + var genbankDict = new Dictionary(); + + while (true) + { + var entry = GetNextEntry(); + if (entry == null) break; + genbankDict[entry.TranscriptId] = entry; + } + + return genbankDict; + } + + private GenbankEntry GetNextEntry() + { + var line = _reader.ReadLine(); + if (line == null) return null; + + var info = ReadTranscriptInfo(line); + var exons = ReadExons(info.NumExons); + + return new GenbankEntry(info.TranscriptId, info.TranscriptVersion, info.ProteinId, info.ProteinVersion, + info.GeneId, info.GeneSymbol, info.CodingRegion, exons); + } + + private IInterval[] ReadExons(int numExons) + { + if (numExons == 0) return null; + + var line = _reader.ReadLine(); + if (line == null) throw new InvalidOperationException("Unexpected null line when parsing exons"); + + var cols = line.Split('\t'); + if (cols[0] != "Exons") throw new InvalidDataException($"Expected the first keyword to be Exons, but found something different: {line}"); + + var exons = new IInterval[numExons]; + int colIndex = 1; + + for (int i = 0; i < numExons; i++) + { + int start = int.Parse(cols[colIndex++]); + int end = int.Parse(cols[colIndex++]); + exons[i] = new Interval(start, end); + } + + return exons; + } + + private static (string TranscriptId, byte TranscriptVersion, string ProteinId, byte ProteinVersion, string + GeneId, string GeneSymbol, IInterval CodingRegion, int NumExons) ReadTranscriptInfo(string line) + { + var cols = line.Split('\t'); + if (cols.Length != 9) throw new InvalidDataException($"Expected 9 columns, but found {cols.Length} columns instead."); + + var transcriptId = cols[0]; + var transcriptVersion = byte.Parse(cols[1]); + var proteinId = cols[2]; + var proteinVersion = byte.Parse(cols[3]); + var geneId = cols[4]; + var geneSymbol = cols[5]; + var start = int.Parse(cols[6]); + var end = int.Parse(cols[7]); + var numExons = int.Parse(cols[8]); + + var codingRegion = new Interval(start, end); + return (transcriptId, transcriptVersion, proteinId, proteinVersion, geneId, geneSymbol, codingRegion, + numExons); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/GenbankWriter.cs b/CacheUtils/IntermediateIO/GenbankWriter.cs new file mode 100644 index 00000000..0d63ef8f --- /dev/null +++ b/CacheUtils/IntermediateIO/GenbankWriter.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using CacheUtils.Genbank; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class GenbankWriter : IDisposable + { + private readonly StreamWriter _writer; + + internal GenbankWriter(StreamWriter writer, IntermediateIoHeader header) + { + _writer = writer; + _writer.NewLine = "\n"; + header.Write(_writer, IntermediateIoCommon.FileType.Genbank); + } + + internal void Write(GenbankEntry entry) + { + var numExons = entry.Exons?.Length ?? 0; + + var codingRegionStart = entry.CodingRegion?.Start ?? -1; + var codingRegionEnd = entry.CodingRegion?.End ?? -1; + + var proteinId = entry.ProteinId ?? ""; + var proteinVersion = entry.ProteinVersion; + + _writer.WriteLine($"{entry.TranscriptId}\t{entry.TranscriptVersion}\t{proteinId}\t{proteinVersion}\t{entry.GeneId}\t{entry.Symbol}\t{codingRegionStart}\t{codingRegionEnd}\t{numExons}"); + if (entry.Exons == null) return; + + _writer.Write("Exons"); + foreach (var exon in entry.Exons) _writer.Write($"\t{exon.Start}\t{exon.End}"); + _writer.WriteLine(); + } + + public void Dispose() => _writer.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/IntermediateIoCommon.cs b/CacheUtils/IntermediateIO/IntermediateIoCommon.cs new file mode 100644 index 00000000..e6881324 --- /dev/null +++ b/CacheUtils/IntermediateIO/IntermediateIoCommon.cs @@ -0,0 +1,26 @@ +using System.IO; + +namespace CacheUtils.IntermediateIO +{ + public static class IntermediateIoCommon + { + public const string Header = "NirvanaIntermediateIo"; + + public enum FileType : byte + { + Genbank, + Polyphen, + Regulatory, + Sift, + Transcript + } + + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global + public static IntermediateIoHeader ReadHeader(StreamReader reader, FileType expectedType) + { + (string id, FileType type, IntermediateIoHeader header) = IntermediateIoHeader.Read(reader); + if (id != Header || type != expectedType) throw new InvalidDataException("Could not verify the header tag or the file type in the header."); + return header; + } + } +} diff --git a/CacheUtils/IntermediateIO/IntermediateIoHeader.cs b/CacheUtils/IntermediateIO/IntermediateIoHeader.cs new file mode 100644 index 00000000..01b7fbaf --- /dev/null +++ b/CacheUtils/IntermediateIO/IntermediateIoHeader.cs @@ -0,0 +1,49 @@ +using System.IO; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.IntermediateIO +{ + public sealed class IntermediateIoHeader + { + private readonly ushort _vepVersion; + public readonly long VepReleaseTicks; + public readonly Source Source; + public readonly GenomeAssembly GenomeAssembly; + private readonly int _numRefSeqs; + + public IntermediateIoHeader(ushort vepVersion, long vepReleaseTicks, Source transcriptSource, + GenomeAssembly genomeAssembly, int numRefSeqs) + { + _vepVersion = vepVersion; + VepReleaseTicks = vepReleaseTicks; + Source = transcriptSource; + GenomeAssembly = genomeAssembly; + _numRefSeqs = numRefSeqs; + } + + internal void Write(StreamWriter writer, IntermediateIoCommon.FileType fileType) + { + writer.WriteLine($"{IntermediateIoCommon.Header}\t{(byte)fileType}"); + writer.WriteLine($"{_vepVersion}\t{VepReleaseTicks}\t{(byte)Source}\t{(byte)GenomeAssembly}\t{_numRefSeqs}"); + } + + internal static (string Id, IntermediateIoCommon.FileType Type, IntermediateIoHeader Header) Read(StreamReader reader) + { + var cols = reader.ReadLine().Split('\t'); + var cols2 = reader.ReadLine().Split('\t'); + + var id = cols[0]; + var type = (IntermediateIoCommon.FileType)byte.Parse(cols[1]); + + var vepVersion = ushort.Parse(cols2[0]); + var vepReleaseTicks = long.Parse(cols2[1]); + var source = (Source)byte.Parse(cols2[2]); + var genomeAssembly = (GenomeAssembly)byte.Parse(cols2[3]); + var numRefSeqs = int.Parse(cols2[4]); + + var header = new IntermediateIoHeader(vepVersion, vepReleaseTicks, source, genomeAssembly, numRefSeqs); + return (id, type, header); + } + } +} diff --git a/CacheUtils/IntermediateIO/LrgReader.cs b/CacheUtils/IntermediateIO/LrgReader.cs new file mode 100644 index 00000000..29ff2ee1 --- /dev/null +++ b/CacheUtils/IntermediateIO/LrgReader.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Utilities; + +namespace CacheUtils.IntermediateIO +{ + public static class LrgReader + { + private const int RefSeqTranscriptIndex = 4; + private const int EnsemblTranscriptIndex = 5; + private const int CccdsIndex = 6; + + public static HashSet GetTranscriptIds(string lrgPath, Dictionary> ccdsIdToEnsemblId) + { + var transcriptIds = new HashSet(); + + using (var reader = new StreamReader(FileUtilities.GetReadStream(lrgPath))) + { + while (true) + { + var line = reader.ReadLine(); + if (line == null) break; + if (line.StartsWith("#")) continue; + + var cols = line.Split('\t'); + if (cols.Length != 7) throw new InvalidDataException($"Expected 7 columns, but found {cols.Length}: [{line}]"); + + var refSeqTranscript = FormatUtilities.SplitVersion(Sanitize(cols[RefSeqTranscriptIndex])); + var ccds = FormatUtilities.SplitVersion(Sanitize(cols[CccdsIndex])); + var ensemblTranscriptIds = GetEnsemblTranscriptIds(ccds.Id, ccdsIdToEnsemblId, Sanitize(cols[EnsemblTranscriptIndex])); + + if (refSeqTranscript.Id != null) transcriptIds.Add(refSeqTranscript.Id); + // ReSharper disable once InvertIf + if (ensemblTranscriptIds != null) foreach (var id in ensemblTranscriptIds) transcriptIds.Add(id); + } + } + + return transcriptIds; + } + + private static List GetEnsemblTranscriptIds(string ccdsId, + IReadOnlyDictionary> ccdsIdToEnsemblId, string ensemblId) + { + if (!string.IsNullOrEmpty(ensemblId)) return new List { ensemblId }; + if (string.IsNullOrEmpty(ccdsId)) return null; + return !ccdsIdToEnsemblId.TryGetValue(ccdsId, out var ensemblList) ? null : ensemblList; + } + + private static string Sanitize(string s) => s == "-" ? null : s; + } +} diff --git a/CacheUtils/IntermediateIO/MutableTranscriptReader.cs b/CacheUtils/IntermediateIO/MutableTranscriptReader.cs new file mode 100644 index 00000000..575f77de --- /dev/null +++ b/CacheUtils/IntermediateIO/MutableTranscriptReader.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Helpers; +using CommonUtilities; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class MutableTranscriptReader : IDisposable + { + private readonly IDictionary _refIndexToChromosome; + private readonly StreamReader _reader; + public readonly IntermediateIoHeader Header; + + internal MutableTranscriptReader(Stream stream, IDictionary refIndexToChromosome) + { + _refIndexToChromosome = refIndexToChromosome; + _reader = new StreamReader(stream); + Header = IntermediateIoCommon.ReadHeader(_reader, IntermediateIoCommon.FileType.Transcript); + } + + public MutableTranscript[] GetTranscripts() + { + var transcripts = new List(); + + while (true) + { + var transcript = GetNextTranscript(); + if (transcript == null) break; + transcripts.Add(transcript); + } + + return transcripts.ToArray(); + } + + private MutableTranscript GetNextTranscript() + { + var line = _reader.ReadLine(); + if (line == null) return null; + + var transcriptInfo = ReadTranscriptInfo(line); + var gene = ReadGene(transcriptInfo.Chromosome); + var translation = ReadTranslation(); + var exons = ReadExons(transcriptInfo.Chromosome); + var introns = ReadIntervals("Introns"); + var cdnaMaps = ReadCdnaMaps(); + var mirnas = ReadIntervals("miRNAs"); + var selenocysteines = ReadSelenocysteines(); + var rnaEdits = ReadRnaEdits(); + + return new MutableTranscript(transcriptInfo.Chromosome, transcriptInfo.Start, transcriptInfo.End, + transcriptInfo.Id, transcriptInfo.Version, transcriptInfo.CcdsId, transcriptInfo.RefSeqId, + transcriptInfo.BioType, transcriptInfo.IsCanonical, translation.CodingRegion, translation.Id, + translation.Version, translation.PeptideSeq, transcriptInfo.Source, gene, exons, + transcriptInfo.StartExonPhase, transcriptInfo.TotalExonLength, introns, cdnaMaps, null, null, + transcriptInfo.TranslateableSequence, mirnas, transcriptInfo.CdsStartNotFound, + transcriptInfo.CdsEndNotFound, selenocysteines, rnaEdits); + } + + private int[] ReadSelenocysteines() + { + var cols = GetColumns("Sec"); + + int numPositions = int.Parse(cols[1]); + if (numPositions == 0) return null; + + var positions = new int[numPositions]; + int colIndex = 2; + + for (int i = 0; i < numPositions; i++) positions[i] = int.Parse(cols[colIndex++]); + return positions; + } + + private IRnaEdit[] ReadRnaEdits() + { + var cols = GetColumns("RnaEdits"); + + int numRnaEdits = int.Parse(cols[1]); + if (numRnaEdits == 0) return null; + + var rnaEdits = new IRnaEdit[numRnaEdits]; + int colIndex = 2; + + for (int i = 0; i < numRnaEdits; i++) + { + var start = int.Parse(cols[colIndex++]); + var end = int.Parse(cols[colIndex++]); + var bases = cols[colIndex++]; + rnaEdits[i] = new RnaEdit(start, end, bases); + } + + return rnaEdits; + } + + private ICdnaCoordinateMap[] ReadCdnaMaps() + { + var cols = GetColumns("cDNA"); + + int numCdnaMaps = int.Parse(cols[1]); + if (numCdnaMaps == 0) return null; + + var cdnaMaps = new ICdnaCoordinateMap[numCdnaMaps]; + int colIndex = 2; + + for (int i = 0; i < numCdnaMaps; i++) + { + int start = int.Parse(cols[colIndex++]); + int end = int.Parse(cols[colIndex++]); + int cdnaStart = int.Parse(cols[colIndex++]); + int cdnaEnd = int.Parse(cols[colIndex++]); + cdnaMaps[i] = new CdnaCoordinateMap(start, end, cdnaStart, cdnaEnd); + } + + return cdnaMaps; + } + + private IInterval[] ReadIntervals(string description) + { + var cols = GetColumns(description); + + int numIntervals = int.Parse(cols[1]); + if (numIntervals == 0) return null; + + var intervals = new IInterval[numIntervals]; + int colIndex = 2; + + for (int i = 0; i < numIntervals; i++) + { + int start = int.Parse(cols[colIndex++]); + int end = int.Parse(cols[colIndex++]); + intervals[i] = new Interval(start, end); + } + + return intervals; + } + + private MutableExon[] ReadExons(IChromosome chromosome) + { + var cols = GetColumns("Exons"); + + int numExons = int.Parse(cols[1]); + if (numExons == 0) return null; + + var exons = new MutableExon[numExons]; + int colIndex = 2; + + for (int i = 0; i < numExons; i++) + { + int start = int.Parse(cols[colIndex++]); + int end = int.Parse(cols[colIndex++]); + byte phase = (byte)(int.Parse(cols[colIndex++]) + 1); + exons[i] = new MutableExon(chromosome, start, end, phase); + } + + return exons; + } + + private (string Id, byte Version, CdnaCoordinateMap CodingRegion, string PeptideSeq) ReadTranslation() + { + var cols = GetColumns("Translation"); + + var id = cols[1]; + var version = byte.Parse(cols[2]); + var start = int.Parse(cols[3]); + var end = int.Parse(cols[4]); + var cdnaStart = int.Parse(cols[5]); + var cdnaEnd = int.Parse(cols[6]); + var peptideSeq = cols[7]; + + var codingRegion = new CdnaCoordinateMap(start, end, cdnaStart, cdnaEnd); + return (id, version, codingRegion, peptideSeq); + } + + private MutableGene ReadGene(IChromosome chromosome) + { + var cols = GetColumns("Gene"); + + var id = cols[1]; + var start = int.Parse(cols[4]); + var end = int.Parse(cols[5]); + var onReverseStrand = cols[6] == "R"; + var symbol = cols[7]; + var symbolSource = GeneSymbolSourceHelper.GetGeneSymbolSource(cols[8]); + var hgncId = int.Parse(cols[9]); + + return new MutableGene(chromosome, start, end, onReverseStrand, symbol, symbolSource, id, hgncId); + } + + private (string Id, byte Version, IChromosome Chromosome, int Start, int End, BioType BioType, bool IsCanonical, + int TotalExonLength, string CcdsId, string RefSeqId, Source Source, bool CdsStartNotFound, bool + CdsEndNotFound, string TranslateableSequence, int StartExonPhase) ReadTranscriptInfo(string line) + { + var cols = GetColumns("Transcript", line); + + var id = cols[1]; + var version = byte.Parse(cols[2]); + var referenceIndex = ushort.Parse(cols[4]); + var start = int.Parse(cols[5]); + var end = int.Parse(cols[6]); + var biotype = (BioType)byte.Parse(cols[8]); + var isCanonical = cols[9] == "Y"; + var totalExonLength = int.Parse(cols[10]); + var ccdsId = cols[11]; + var refSeqId = cols[12]; + var source = (Source)byte.Parse(cols[13]); + var cdsStartNotFound = cols[14] == "Y"; + var cdsEndNotFound = cols[15] == "Y"; + var startExonPhase = int.Parse(cols[16]); + + var translateableSequence = _reader.ReadLine(); + var chromosome = ReferenceNameUtilities.GetChromosome(_refIndexToChromosome, referenceIndex); + + return (id, version, chromosome, start, end, biotype, isCanonical, totalExonLength, ccdsId, refSeqId, source + , cdsStartNotFound, cdsEndNotFound, translateableSequence, startExonPhase); + } + + private string[] GetColumns(string keyword, string line = null) + { + if (line == null) line = _reader.ReadLine(); + var cols = line.Split('\t'); + if (cols[0] != keyword) throw new InvalidDataException($"Could not find the {keyword} keyword in the transcripts file."); + return cols; + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/MutableTranscriptWriter.cs b/CacheUtils/IntermediateIO/MutableTranscriptWriter.cs new file mode 100644 index 00000000..e61a0abb --- /dev/null +++ b/CacheUtils/IntermediateIO/MutableTranscriptWriter.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class MutableTranscriptWriter : IDisposable + { + private readonly StreamWriter _writer; + + internal MutableTranscriptWriter(StreamWriter writer, IntermediateIoHeader header) + { + _writer = writer; + _writer.NewLine = "\n"; + header.Write(_writer, IntermediateIoCommon.FileType.Transcript); + } + + internal void Write(MutableTranscript transcript) + { + WriteTranscriptInfo(transcript); + WriteGene(_writer, transcript.Gene); + WriteTranslation(transcript.CodingRegion, transcript.ProteinId, transcript.ProteinVersion, transcript.PeptideSequence); + WriteExons(transcript.Exons); + WriteIntervals(transcript.Introns, "Introns"); + WriteCdnaMaps(transcript.CdnaMaps); + WriteIntervals(transcript.MicroRnas, "miRNAs"); + WriteSelenocysteines(transcript.SelenocysteinePositions); + WriteRnaEdits(transcript.RnaEdits); + } + + private void WriteRnaEdits(IReadOnlyCollection rnaEdits) + { + if (rnaEdits == null) + { + _writer.WriteLine("RnaEdits\t0"); + return; + } + + _writer.Write($"RnaEdits\t{rnaEdits.Count}"); + foreach (var rnaEdit in rnaEdits) _writer.Write($"\t{rnaEdit.Start}\t{rnaEdit.End}\t{rnaEdit.Bases}"); + _writer.WriteLine(); + } + + private void WriteSelenocysteines(IReadOnlyCollection positions) + { + if (positions == null) + { + _writer.WriteLine("Sec\t0"); + return; + } + + _writer.Write($"Sec\t{positions.Count}"); + foreach (var pos in positions) _writer.Write($"\t{pos}"); + _writer.WriteLine(); + } + + private void WriteCdnaMaps(IReadOnlyCollection cdnaMaps) + { + _writer.Write($"cDNA\t{cdnaMaps.Count}"); + foreach (var cdnaMap in cdnaMaps) _writer.Write($"\t{cdnaMap.Start}\t{cdnaMap.End}\t{cdnaMap.CdnaStart}\t{cdnaMap.CdnaEnd}"); + _writer.WriteLine(); + } + + private void WriteIntervals(IReadOnlyCollection intervals, string description) + { + if (intervals == null) + { + _writer.WriteLine($"{description}\t0"); + return; + } + + _writer.Write($"{description}\t{intervals.Count}"); + foreach (var interval in intervals) _writer.Write($"\t{interval.Start}\t{interval.End}"); + _writer.WriteLine(); + } + + private void WriteExons(IReadOnlyCollection exons) + { + _writer.Write($"Exons\t{exons.Count}"); + foreach (var exon in exons) _writer.Write($"\t{exon.Start}\t{exon.End}\t{exon.Phase}"); + _writer.WriteLine(); + } + + private void WriteTranslation(ICdnaCoordinateMap codingRegion, string proteinId, byte proteinVersion, string peptideSequence) => + _writer.WriteLine($"Translation\t{proteinId}\t{proteinVersion}\t{codingRegion.Start}\t{codingRegion.End}\t{codingRegion.CdnaStart}\t{codingRegion.CdnaEnd}\t{peptideSequence}"); + + private static void WriteGene(TextWriter writer, MutableGene gene) + { + var strand = gene.OnReverseStrand ? 'R' : 'F'; + writer.WriteLine($"Gene\t{gene.GeneId}\t{gene.Chromosome.UcscName}\t{gene.Chromosome.Index}\t{gene.Start}\t{gene.End}\t{strand}\t{gene.Symbol}\t{gene.SymbolSource}\t{gene.HgncId}"); + } + + private void WriteTranscriptInfo(MutableTranscript transcript) + { + _writer.WriteLine($"Transcript\t{transcript.Id}\t{transcript.Version}\t{transcript.Chromosome.UcscName}\t{transcript.Chromosome.Index}\t{transcript.Start}\t{transcript.End}\t{transcript.BioType}\t{(byte)transcript.BioType}\t{BoolToChar(transcript.IsCanonical)}\t{transcript.TotalExonLength}\t{transcript.CcdsId}\t{transcript.RefSeqId}\t{(byte)transcript.Source}\t{BoolToChar(transcript.CdsStartNotFound)}\t{BoolToChar(transcript.CdsEndNotFound)}\t{transcript.StartExonPhase}"); + _writer.WriteLine(transcript.TranslateableSequence); + } + + private static char BoolToChar(bool b) => b ? 'Y' : 'N'; + + public void Dispose() => _writer.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/PredictionReader.cs b/CacheUtils/IntermediateIO/PredictionReader.cs new file mode 100644 index 00000000..5fa20722 --- /dev/null +++ b/CacheUtils/IntermediateIO/PredictionReader.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CommonUtilities; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.IntermediateIO +{ + public sealed class PredictionReader : IDisposable + { + private readonly IDictionary _refIndexToChromosome; + private readonly StreamReader _reader; + + public PredictionReader(Stream stream, IDictionary refIndexToChromosome, + IntermediateIoCommon.FileType expectedFileType) + { + _refIndexToChromosome = refIndexToChromosome; + _reader = new StreamReader(stream); + IntermediateIoCommon.ReadHeader(_reader, expectedFileType); + } + + public (string[] PredictionData, Dictionary TranscriptToPredictionIndex, IChromosome Chromosome) + GetPredictionData() + { + var chromosomeHeader = GetChromosomeHeader(); + var predictionData = new string[chromosomeHeader.NumPredictions]; + var transcriptToPredictionIndex = new Dictionary(chromosomeHeader.NumPredictions); + + for (int predictionIndex = 0; predictionIndex < chromosomeHeader.NumPredictions; predictionIndex++) + { + var prediction = GetNextPrediction(); + predictionData[predictionIndex] = prediction.PredictionData; + foreach (var index in prediction.TranscriptIndices) + transcriptToPredictionIndex[index] = predictionIndex; + } + + return (predictionData, transcriptToPredictionIndex, chromosomeHeader.Chromosome); + } + + private (IChromosome Chromosome, int NumPredictions) GetChromosomeHeader() + { + var line = _reader.ReadLine(); + var cols = line.Split('\t'); + if (cols.Length != 3) throw new InvalidDataException($"Expected 3 columns in the chromosome header, but found {cols.Length}"); + + ushort referenceIndex = ushort.Parse(cols[1]); + var chromosome = ReferenceNameUtilities.GetChromosome(_refIndexToChromosome, referenceIndex); + int numPredictions = int.Parse(cols[2]); + + return (chromosome, numPredictions); + } + + private (List TranscriptIndices, string PredictionData) GetNextPrediction() + { + var line = _reader.ReadLine(); + if (line == null) throw new InvalidDataException("Found an unexpected empty line while parsing the prediction file."); + + var cols = line.Split('\t'); + if (cols.Length != 2) throw new InvalidDataException($"Expected 2 columns in the prediction entry, but found {cols.Length}"); + + var transcriptIndices = GetTranscriptIndices(cols[0]); + var predictionData = cols[1]; + + return (transcriptIndices, predictionData); + } + + private static List GetTranscriptIndices(string s) + { + var indexStrings = s.Split(','); + var indices = new int[indexStrings.Length]; + for (int i = 0; i < indexStrings.Length; i++) indices[i] = int.Parse(indexStrings[i]); + return indices.ToList(); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/PredictionWriter.cs b/CacheUtils/IntermediateIO/PredictionWriter.cs new file mode 100644 index 00000000..4cba778a --- /dev/null +++ b/CacheUtils/IntermediateIO/PredictionWriter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class PredictionWriter : IDisposable + { + private readonly StreamWriter _writer; + + internal PredictionWriter(StreamWriter writer, IntermediateIoHeader header, + IntermediateIoCommon.FileType fileType) + { + _writer = writer; + _writer.NewLine = "\n"; + header.Write(_writer, fileType); + } + + internal void Write(IChromosome chromosome, Dictionary> predictionDict) + { + _writer.WriteLine($"{chromosome.UcscName}\t{chromosome.Index}\t{predictionDict.Count}"); + foreach (var kvp in predictionDict) WritePrediction(kvp.Value, kvp.Key); + } + + private void WritePrediction(IEnumerable transcriptIds, string predictionData) + { + var transcriptIdString = string.Join(',', transcriptIds); + _writer.WriteLine($"{transcriptIdString}\t{predictionData}"); + } + + public void Dispose() => _writer.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/RegulatoryRegionReader.cs b/CacheUtils/IntermediateIO/RegulatoryRegionReader.cs new file mode 100644 index 00000000..d521d43f --- /dev/null +++ b/CacheUtils/IntermediateIO/RegulatoryRegionReader.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CommonUtilities; +using VariantAnnotation.AnnotatedPositions.Transcript; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Caches; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class RegulatoryRegionReader : IDisposable + { + private readonly IDictionary _refIndexToChromosome; + private readonly StreamReader _reader; + + internal RegulatoryRegionReader(Stream stream, IDictionary refIndexToChromosome) + { + _refIndexToChromosome = refIndexToChromosome; + _reader = new StreamReader(stream); + IntermediateIoCommon.ReadHeader(_reader, IntermediateIoCommon.FileType.Regulatory); + } + + public IRegulatoryRegion[] GetRegulatoryRegions() + { + var regulatoryRegions = new List(); + + while (true) + { + var regulatoryRegion = GetNextRegulatoryRegion(); + if (regulatoryRegion == null) break; + regulatoryRegions.Add(regulatoryRegion); + } + + return regulatoryRegions.ToArray(); + } + + private IRegulatoryRegion GetNextRegulatoryRegion() + { + var line = _reader.ReadLine(); + if (line == null) return null; + + var cols = line.Split('\t'); + var referenceIndex = ushort.Parse(cols[1]); + var start = int.Parse(cols[2]); + var end = int.Parse(cols[3]); + var id = CompactId.Convert(cols[4]); + var type = (RegulatoryRegionType)byte.Parse(cols[6]); + + var chromosome = ReferenceNameUtilities.GetChromosome(_refIndexToChromosome, referenceIndex); + return new RegulatoryRegion(chromosome, start, end, id, type); + } + + public void Dispose() => _reader.Dispose(); + } +} diff --git a/CacheUtils/IntermediateIO/RegulatoryRegionWriter.cs b/CacheUtils/IntermediateIO/RegulatoryRegionWriter.cs new file mode 100644 index 00000000..f8450603 --- /dev/null +++ b/CacheUtils/IntermediateIO/RegulatoryRegionWriter.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.IntermediateIO +{ + internal sealed class RegulatoryRegionWriter : IDisposable + { + private readonly StreamWriter _writer; + + internal RegulatoryRegionWriter(StreamWriter writer, IntermediateIoHeader header) + { + _writer = writer; + _writer.NewLine = "\n"; + header.Write(_writer, IntermediateIoCommon.FileType.Regulatory); + } + + internal void Write(IRegulatoryRegion region) => _writer.WriteLine( + $"{region.Chromosome.UcscName}\t{region.Chromosome.Index}\t{region.Start}\t{region.End}\t{region.Id}\t{region.Type}\t{(byte) region.Type}"); + + public void Dispose() => _writer.Dispose(); + } +} diff --git a/CacheUtils/Logger/TranscriptMergerLogger.cs b/CacheUtils/Logger/TranscriptMergerLogger.cs new file mode 100644 index 00000000..301da07b --- /dev/null +++ b/CacheUtils/Logger/TranscriptMergerLogger.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; +using VariantAnnotation.Interface; + +namespace CacheUtils.Logger +{ + public sealed class TranscriptMergerLogger : ILogger, IDisposable + { + private readonly StreamWriter _writer; + + public TranscriptMergerLogger(Stream stream) => _writer = new StreamWriter(stream); + + public void WriteLine() => _writer.WriteLine(); + public void WriteLine(string s) => _writer.WriteLine(s); + public void Write(string s) => _writer.Write(s); + + public void SetBold() { } + public void ResetColor() { } + + public void Dispose() => _writer.Dispose(); + } +} diff --git a/CacheUtils/MiniCache/DataBundle.cs b/CacheUtils/MiniCache/DataBundle.cs new file mode 100644 index 00000000..9d5fefd4 --- /dev/null +++ b/CacheUtils/MiniCache/DataBundle.cs @@ -0,0 +1,71 @@ +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; +using VariantAnnotation.Sequence; +using VariantAnnotation.Utilities; +using VC = VariantAnnotation.Caches; + +namespace CacheUtils.MiniCache +{ + /// + /// the bundle of cache and reference data objects that correspond to a + /// specific genome assembly and transcript data source + /// + public sealed class DataBundle + { + public readonly CompressedSequenceReader SequenceReader; + public readonly VC.TranscriptCacheData TranscriptCacheData; + public readonly VC.TranscriptCache TranscriptCache; + + public readonly PredictionCacheReader SiftReader; + public readonly PredictionCacheReader PolyPhenReader; + + private IChromosome _currentChromosome = new EmptyChromosome(string.Empty); + + public Prediction[] SiftPredictions; + public Prediction[] PolyPhenPredictions; + public readonly Source Source; + + private DataBundle(CompressedSequenceReader sequenceReader, PredictionCacheReader siftReader, + PredictionCacheReader polyPhenReader, VC.TranscriptCacheData cacheData, VC.TranscriptCache transcriptCache, + Source source) + { + SequenceReader = sequenceReader; + TranscriptCacheData = cacheData; + TranscriptCache = transcriptCache; + Source = source; + SiftReader = siftReader; + PolyPhenReader = polyPhenReader; + } + + public void Load(IChromosome chromosome) + { + if (_currentChromosome.Index == chromosome.Index) return; + SequenceReader.GetCompressedSequence(chromosome); + SiftPredictions = SiftReader.GetPredictions(chromosome.Index); + PolyPhenPredictions = PolyPhenReader.GetPredictions(chromosome.Index); + _currentChromosome = chromosome; + } + + public static DataBundle GetDataBundle(string referencePath, string cachePrefix) + { + var sequenceReader = new CompressedSequenceReader(FileUtilities.GetReadStream(referencePath)); + var siftReader = new PredictionCacheReader(FileUtilities.GetReadStream(CacheConstants.SiftPath(cachePrefix)), PredictionCacheReader.SiftDescriptions); + var polyPhenReader = new PredictionCacheReader(FileUtilities.GetReadStream(CacheConstants.PolyPhenPath(cachePrefix)), PredictionCacheReader.PolyphenDescriptions); + + VC.TranscriptCacheData cacheData; + VC.TranscriptCache cache; + Source source; + + using (var transcriptReader = new TranscriptCacheReader(FileUtilities.GetReadStream(CacheConstants.TranscriptPath(cachePrefix)))) + { + cacheData = transcriptReader.Read(sequenceReader.RefIndexToChromosome); + cache = cacheData.GetCache(); + source = transcriptReader.Header.TranscriptSource; + } + + return new DataBundle(sequenceReader, siftReader, polyPhenReader, cacheData, cache, source); + } + } +} \ No newline at end of file diff --git a/CacheUtils/MiniCache/IStaging.cs b/CacheUtils/MiniCache/IStaging.cs new file mode 100644 index 00000000..efac96d8 --- /dev/null +++ b/CacheUtils/MiniCache/IStaging.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace CacheUtils.MiniCache +{ + public interface IStaging + { + void Write(Stream stream); + } +} diff --git a/CacheUtils/PredictionCache/PredictionCacheBuilder.cs b/CacheUtils/PredictionCache/PredictionCacheBuilder.cs new file mode 100644 index 00000000..51ce5a3d --- /dev/null +++ b/CacheUtils/PredictionCache/PredictionCacheBuilder.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.IntermediateIO; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; + +namespace CacheUtils.PredictionCache +{ + public sealed class PredictionCacheBuilder + { + private readonly ILogger _logger; + private readonly GenomeAssembly _genomeAssembly; + private readonly long _currentTicks; + + public PredictionCacheBuilder(ILogger logger, GenomeAssembly genomeAssembly) + { + _logger = logger; + _genomeAssembly = genomeAssembly; + _currentTicks = DateTime.Now.Ticks; + } + + public (PredictionCacheStaging Sift, PredictionCacheStaging PolyPhen) CreatePredictionCaches( + Dictionary> transcriptsByRefIndex, PredictionReader siftReader, + PredictionReader polyphenReader, int numRefSeqs) + { + _logger.Write("- converting prediction strings... "); + + var siftRoundedPredictionsPerRef = new RoundedEntryPrediction[numRefSeqs][]; + var polyPhenRoundedPredictionsPerRef = new RoundedEntryPrediction[numRefSeqs][]; + + for (ushort refIndex = 0; refIndex < numRefSeqs; refIndex++) + { + var sift = siftReader.GetPredictionData(); + var polyphen = polyphenReader.GetPredictionData(); + + if (sift.Chromosome.Index != refIndex || polyphen.Chromosome.Index != refIndex) + throw new InvalidDataException( + $"Found mismatch between transcript chromosome index ({refIndex}) and prediction chromosome indices (SIFT: {sift.Chromosome.Index}, PolyPhen: {polyphen.Chromosome.Index}."); + + if (!transcriptsByRefIndex.TryGetValue(refIndex, out var refTranscripts)) continue; + + var (siftPredictions, polyPhenPredictions) = ProcessReference(refTranscripts, + sift.TranscriptToPredictionIndex, polyphen.TranscriptToPredictionIndex, sift.PredictionData, + polyphen.PredictionData); + + siftRoundedPredictionsPerRef[refIndex] = siftPredictions; + polyPhenRoundedPredictionsPerRef[refIndex] = polyPhenPredictions; + } + + _logger.WriteLine("finished."); + + var siftStaging = BuildCacheStaging("SIFT", siftRoundedPredictionsPerRef, numRefSeqs); + var polyPhenStaging = BuildCacheStaging("PolyPhen", polyPhenRoundedPredictionsPerRef, numRefSeqs); + + return (siftStaging, polyPhenStaging); + } + + private PredictionCacheStaging BuildCacheStaging(string description, + IReadOnlyList roundedPredictionsPerRef, int numReferenceSeqs) + { + _logger.Write($"- calculating {description} LUT... "); + var (lut, roundedEntryToLutIndex) = CreateLut(roundedPredictionsPerRef); + _logger.WriteLine($"{lut.Length} entries."); + + _logger.Write($"- converting {description} rounded entries... "); + var predictionsPerRef = ConvertPredictions(roundedPredictionsPerRef, roundedEntryToLutIndex, lut); + _logger.WriteLine("finished."); + + var header = CreateHeader(numReferenceSeqs); + return new PredictionCacheStaging(header, lut, predictionsPerRef); + } + + private CacheHeader CreateHeader(int numReferenceSeqs) + { + var customHeader = new PredictionCacheCustomHeader(new IndexEntry[numReferenceSeqs]); + return new CacheHeader(CacheConstants.Identifier, CacheConstants.SchemaVersion, CacheConstants.DataVersion, + Source.None, _currentTicks, _genomeAssembly, customHeader); + } + + private static Prediction[][] ConvertPredictions(IReadOnlyList roundedPredictionsPerRef, + Dictionary roundedEntryToLutIndex, Prediction.Entry[] lut) + { + int numReferenceSeqs = roundedPredictionsPerRef.Count; + var predictionsPerRef = new Prediction[numReferenceSeqs][]; + + for (int i = 0; i < numReferenceSeqs; i++) + { + predictionsPerRef[i] = ConvertReferencePredictions(roundedPredictionsPerRef[i], roundedEntryToLutIndex, lut); + } + + return predictionsPerRef; + } + + private static Prediction[] ConvertReferencePredictions(IReadOnlyList roundedEntryPredictions, + Dictionary roundedEntryToLutIndex, Prediction.Entry[] lut) + { + if (roundedEntryPredictions == null) return null; + + int numPredictions = roundedEntryPredictions.Count; + var predictions = new Prediction[numPredictions]; + + for (int i = 0; i < numPredictions; i++) + predictions[i] = roundedEntryPredictions[i].Convert(roundedEntryToLutIndex, lut); + + return predictions; + } + + private static (Prediction.Entry[] Lut, Dictionary RoundedEntryToLutIndex) CreateLut( + IEnumerable roundedPredictionsPerRef) + { + var scores = new HashSet(); + + foreach (var roundedPredictions in roundedPredictionsPerRef) + { + if (roundedPredictions == null) continue; + + foreach (var roundedPrediction in roundedPredictions) + { + foreach (var roundedEntry in roundedPrediction.Entries) + { + if (roundedEntry.Score > 1000) continue; + scores.Add(roundedEntry); + } + } + } + + if (scores.Count > 255) throw new InvalidDataException($"Unable to create lookup table, too many LUT entries: {scores.Count} (max 255)."); + + var lut = new Prediction.Entry[scores.Count]; + var roundedEntryToLutIndex = new Dictionary(); + + int currentIndex = 0; + foreach (var entry in scores.OrderBy(x => x.EnumIndex).ThenBy(x => x.Score)) + { + roundedEntryToLutIndex[entry] = (byte)currentIndex; + lut[currentIndex++] = new Prediction.Entry(entry.Score / 1000.0, entry.EnumIndex); + } + + return (lut, roundedEntryToLutIndex); + } + + private static (RoundedEntryPrediction[] Sift, RoundedEntryPrediction[] PolyPhen) ProcessReference( + IReadOnlyList transcripts, Dictionary siftTranscriptToPredictionIndex, + Dictionary polyphenTranscriptToPredictionIndex, string[] siftPredictionData, + string[] polyphenPredictionData) + { + AssignPredictionIndices(transcripts, siftTranscriptToPredictionIndex, polyphenTranscriptToPredictionIndex); + + var siftPredictions = siftPredictionData.GetRoundedEntryPredictions(); + var polyPhenPredictions = polyphenPredictionData.GetRoundedEntryPredictions(); + + return (siftPredictions, polyPhenPredictions); + } + + private static void AssignPredictionIndices(IReadOnlyList transcripts, + Dictionary siftTranscriptToPredictionIndex, + Dictionary polyphenTranscriptToPredictionIndex) + { + foreach (var kvp in siftTranscriptToPredictionIndex) transcripts[kvp.Key].SiftIndex = kvp.Value; + foreach (var kvp in polyphenTranscriptToPredictionIndex) transcripts[kvp.Key].PolyPhenIndex = kvp.Value; + } + } +} diff --git a/CacheUtils/PredictionCache/PredictionCacheStaging.cs b/CacheUtils/PredictionCache/PredictionCacheStaging.cs new file mode 100644 index 00000000..8215c2d7 --- /dev/null +++ b/CacheUtils/PredictionCache/PredictionCacheStaging.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.IO.Compression; +using CacheUtils.MiniCache; +using Compression.Algorithms; +using Compression.FileHandling; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.IO.Caches; + +namespace CacheUtils.PredictionCache +{ + public sealed class PredictionCacheStaging : IStaging + { + private readonly Prediction.Entry[] _lookupTable; + private readonly Prediction[][] _predictionsPerRef; + private readonly CacheHeader _header; + + internal PredictionCacheStaging(CacheHeader header, Prediction.Entry[] lut, Prediction[][] predictionsPerRef) + { + _header = header; + _lookupTable = lut; + _predictionsPerRef = predictionsPerRef; + } + + public void Write(Stream stream) + { + using (var blockStream = new BlockStream(new Zstandard(), stream, CompressionMode.Compress)) + using (var writer = new PredictionCacheWriter(blockStream, _header)) + { + writer.Write(_lookupTable, _predictionsPerRef); + } + } + } +} diff --git a/CacheUtils/PredictionCache/PredictionCacheWriter.cs b/CacheUtils/PredictionCache/PredictionCacheWriter.cs new file mode 100644 index 00000000..73e29ab4 --- /dev/null +++ b/CacheUtils/PredictionCache/PredictionCacheWriter.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Compression.FileHandling; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.IO.Caches; + +namespace CacheUtils.PredictionCache +{ + public sealed class PredictionCacheWriter : IDisposable + { + private readonly BinaryWriter _writer; + private readonly BlockStream _blockStream; + private readonly CacheHeader _header; + private readonly bool _leaveOpen; + + public PredictionCacheWriter(BlockStream blockStream, CacheHeader header, bool leaveOpen = false) + { + _blockStream = blockStream; + _writer = new BinaryWriter(blockStream); + _header = header; + _leaveOpen = leaveOpen; + } + + public void Dispose() + { + if (!_leaveOpen) _blockStream.Dispose(); + _writer.Dispose(); + } + + /// + /// writes the annotations to the current database file + /// + public void Write(Prediction.Entry[] lut, Prediction[][] predictionsPerRef) + { + _blockStream.WriteHeader(_header.Write); + WriteLookupTable(_writer, lut); + _blockStream.Flush(); + WritePredictions(predictionsPerRef); + } + + private void WritePredictions(IReadOnlyList predictionsPerRef) + { + // ReSharper disable once UsePatternMatching + var customHeader = _header.CustomHeader as PredictionCacheCustomHeader; + if (customHeader == null) throw new InvalidCastException(); + + var indexEntries = customHeader.Entries; + var blockPosition = new BlockStream.BlockPosition(); + + for (int i = 0; i < predictionsPerRef.Count; i++) + { + var refPredictions = predictionsPerRef[i]; + + _blockStream.GetBlockPosition(blockPosition); + indexEntries[i].FileOffset = blockPosition.FileOffset; + indexEntries[i].Count = refPredictions?.Length ?? 0; + + if (refPredictions != null) + { + foreach (var prediction in refPredictions) prediction.Write(_writer); + } + + _blockStream.Flush(); + } + } + + private static void WriteLookupTable(BinaryWriter writer, IReadOnlyCollection lut) + { + writer.Write(lut.Count); + foreach (var entry in lut) entry.Write(writer); + } + } +} diff --git a/CacheUtils/PredictionCache/PredictionExtensions.cs b/CacheUtils/PredictionCache/PredictionExtensions.cs new file mode 100644 index 00000000..bf86bb3f --- /dev/null +++ b/CacheUtils/PredictionCache/PredictionExtensions.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; + +namespace CacheUtils.PredictionCache +{ + public static class PredictionExtensions + { + public static RoundedEntryPrediction[] GetRoundedEntryPredictions(this string[] predictionStrings) + { + var predictions = new RoundedEntryPrediction[predictionStrings.Length]; + int currentIndex = 0; + foreach (var s in predictionStrings) predictions[currentIndex++] = s.GetRoundedEntryPrediction(); + return predictions; + } + + private static RoundedEntryPrediction GetRoundedEntryPrediction(this string predictionString) + { + // convert the base 64 string representation to our compressed prediction data + var uncompressedDataWithHeader = Convert.FromBase64String(predictionString); + const int headerLength = 3; + + // skip the 'VEP' header + int newLength = uncompressedDataWithHeader.Length - headerLength; + + // sanity check: we should have an even number of bytes + if ((newLength & 1) != 0) + { + throw new InvalidDataException($"Expected an even number of bytes when serializing the protein function prediction matrix: {newLength}"); + } + + var data = new ushort[newLength / 2]; + Buffer.BlockCopy(uncompressedDataWithHeader, headerLength, data, 0, newLength); + + var roundedEntries = new RoundedEntry[data.Length]; + for (int i = 0; i < data.Length; i++) roundedEntries[i] = new RoundedEntry(data[i]); + return new RoundedEntryPrediction(roundedEntries); + } + } +} diff --git a/CacheUtils/PredictionCache/PredictionUtilities.cs b/CacheUtils/PredictionCache/PredictionUtilities.cs new file mode 100644 index 00000000..ee85ca85 --- /dev/null +++ b/CacheUtils/PredictionCache/PredictionUtilities.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.Utilities; +using CacheUtils.TranscriptCache; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.PredictionCache +{ + public static class PredictionUtilities + { + internal static IntervalArray[] UpdateTranscripts(IEnumerable transcripts, + Prediction[] oldSiftPredictions, IEnumerable siftPredictions, + Prediction[] oldPolyPhenPredictions, IEnumerable polyPhenPredictions, int numRefSeqs) + { + var siftDict = siftPredictions.CreateIndex(); + var polyphenDict = polyPhenPredictions.CreateIndex(); + var newTranscripts = new List(); + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var transcript in transcripts) + { + var siftIndex = GetNewIndex(oldSiftPredictions, transcript.SiftIndex, siftDict); + var polyphenIndex = GetNewIndex(oldPolyPhenPredictions, transcript.PolyPhenIndex, polyphenDict); + newTranscripts.Add(transcript.UpdatePredictions(siftIndex, polyphenIndex)); + } + + return newTranscripts.ToIntervalArrays(numRefSeqs); + } + + internal static ITranscript UpdatePredictions(this ITranscript t, int siftIndex, int polyphenIndex) + { + return new Transcript(t.Chromosome, t.Start, t.End, t.Id, t.Translation, t.BioType, t.Gene, + t.TotalExonLength, t.StartExonPhase, t.IsCanonical, t.Introns, t.MicroRnas, t.CdnaMaps, siftIndex, + polyphenIndex, t.Source, t.CdsStartNotFound, t.CdsEndNotFound, t.Selenocysteines, t.RnaEdits); + } + + private static int GetNewIndex(IReadOnlyList oldPredictions, int index, + IReadOnlyDictionary dict) + { + if (index == -1) return -1; + var prediction = oldPredictions[index]; + if (!dict.TryGetValue(prediction, out int newIndex)) throw new InvalidDataException("Unable to find the prediction in the dictionary."); + return newIndex; + } + } +} diff --git a/CacheUtils/PredictionCache/RoundedEntry.cs b/CacheUtils/PredictionCache/RoundedEntry.cs new file mode 100644 index 00000000..a188c3d9 --- /dev/null +++ b/CacheUtils/PredictionCache/RoundedEntry.cs @@ -0,0 +1,25 @@ +using System; + +namespace CacheUtils.PredictionCache +{ + public struct RoundedEntry : IEquatable + { + public readonly ushort Score; + public readonly byte EnumIndex; + + public RoundedEntry(ushort data) + { + Score = Round((ushort)(data & 0x3ff)); + EnumIndex = (byte)((data & 0xc000) >> 14); + } + + private static ushort Round(ushort us) => (ushort)((ushort)Math.Round(us / 5.0) * 5); + + public bool Equals(RoundedEntry other) => Score == other.Score && EnumIndex == other.EnumIndex; + + public override int GetHashCode() + { + unchecked { return (Score.GetHashCode() * 397) ^ EnumIndex.GetHashCode(); } + } + } +} diff --git a/CacheUtils/PredictionCache/RoundedEntryPrediction.cs b/CacheUtils/PredictionCache/RoundedEntryPrediction.cs new file mode 100644 index 00000000..b7344597 --- /dev/null +++ b/CacheUtils/PredictionCache/RoundedEntryPrediction.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using VariantAnnotation.Caches.DataStructures; + +namespace CacheUtils.PredictionCache +{ + public sealed class RoundedEntryPrediction + { + public readonly RoundedEntry[] Entries; + public RoundedEntryPrediction(RoundedEntry[] entries) => Entries = entries; + + public Prediction Convert(Dictionary lutDict, Prediction.Entry[] lut) + { + var numEntries = Entries.Length; + var lutIndices = new byte[numEntries]; + + int index = 0; + foreach (var entry in Entries) lutIndices[index++] = entry.Score > 1000 ? (byte) 255 : lutDict[entry]; + return new Prediction(lutIndices, lut); + } + } +} diff --git a/CacheUtils/Properties/launchSettings.json b/CacheUtils/Properties/launchSettings.json new file mode 100644 index 00000000..ebcd8615 --- /dev/null +++ b/CacheUtils/Properties/launchSettings.json @@ -0,0 +1,44 @@ +{ + "profiles": { + "CacheUtils": { + "commandName": "Project", + "commandLineArgs": "create -g VEP90_genes_2017-10-26.tsv.gz -i IntermediateCache\\Ensembl90_GRCh37 -r References\\5\\Homo_sapiens.GRCh37.Nirvana.dat -o test", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Create": { + "commandName": "Project", + "commandLineArgs": "create -g VEP90_genes_2017-11-17.tsv.gz -i IntermediateCache\\Ensembl90_GRCh37 -r References\\5\\Homo_sapiens.GRCh37.Nirvana.dat -o Cache\\25\\GRCh37\\Ensembl90", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Gene": { + "commandName": "Project", + "commandLineArgs": "gene -o VEP90_genes_2017-10-27.tsv.gz", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Parse": { + "commandName": "Project", + "commandLineArgs": "parse --date 2017-09-04 -s refseq --ga GRCh37 --vep 90 -r References\\5\\Homo_sapiens.GRCh37.Nirvana.dat -i VepCache\\90\\homo_sapiens_refseq\\90_GRCh37 -o IntermediateCache\\RefSeq90_GRCh37 --genbank RefSeq_Genbank_Transcripts_1_43.gz", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Genbank": { + "commandName": "Project", + "commandLineArgs": "genbank -o test", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Parse Ensembl": { + "commandName": "Project", + "commandLineArgs": "parse --date 2017-09-04 -s ensembl --ga GRCh37 --vep 90 -r References\\5\\Homo_sapiens.GRCh37.Nirvana.dat -i VepCache\\90\\homo_sapiens\\90_GRCh37 -o IntermediateCache\\Ensembl90_GRCh37", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Combine": { + "commandName": "Project", + "commandLineArgs": "combine -1 Cache\\25\\GRCh38\\Ensembl90 -2 Cache\\25\\GRCh38\\RefSeq90 -r References\\5\\Homo_sapiens.GRCh38.Nirvana.dat -o Cache\\25\\GRCh38\\Both90", + "workingDirectory": "E:\\Data\\Nirvana" + }, + "MS_Extract": { + "commandName": "Project", + "commandLineArgs": "extract -i Cache\\25\\GRCh37\\Both90 -o e:\\Data\\Nirvana -n chr12 -p 7018490 --endpos 7086889 -r References\\5\\Homo_sapiens.GRCh37.Nirvana.dat", + "workingDirectory": "E:\\Data\\Nirvana" + } + } +} \ No newline at end of file diff --git a/CacheUtils/Sequence/CompressedSequenceWriter.cs b/CacheUtils/Sequence/CompressedSequenceWriter.cs new file mode 100644 index 00000000..d7f0c787 --- /dev/null +++ b/CacheUtils/Sequence/CompressedSequenceWriter.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO; +using VariantAnnotation.Sequence; + +namespace CacheUtils.Sequence +{ + public sealed class CompressedSequenceWriter : IDisposable + { + private readonly ExtendedBinaryWriter _writer; + private readonly Stream _stream; + private readonly List _refSeqNames; + private List _refSeqIndex; + private List> _refSeqMaskedIntervals; + private TwoBitSequence _twoBitSequence; + private SequenceCompressor _sequenceCompressor; + private long _headerDataOffset; + private long _indexOffset; + private long _maskedIntervalsOffset; + + public CompressedSequenceWriter(Stream stream, IReadOnlyCollection referenceMetadataList, + ICytogeneticBands genomeCytobands, GenomeAssembly genomeAssembly) + { + _stream = stream; + _writer = new ExtendedBinaryWriter(_stream); + _refSeqNames = new List(); + _refSeqIndex = new List(); + _twoBitSequence = new TwoBitSequence(); + _sequenceCompressor = new SequenceCompressor(); + _refSeqMaskedIntervals = new List>(); + + WriteHeader(referenceMetadataList, genomeCytobands, genomeAssembly); + } + + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + WriteReferenceSequenceIndex(); + WriteMaskedIntervals(); + WriteEofTag(); + _stream.Dispose(); + } + + _refSeqIndex = null; + _refSeqMaskedIntervals = null; + _twoBitSequence = null; + _sequenceCompressor = null; + + _headerDataOffset = 0L; + _indexOffset = 0L; + _maskedIntervalsOffset = 0L; + } + + public void Write(string name, string bases, int seqOffset = 0) + { + _writer.Flush(); + long position = _stream.Position; + + _refSeqNames.Add(name); + _refSeqIndex.Add(new SequenceIndexEntry + { + NumBases = bases.Length, + FileOffset = position, + SequenceOffset = seqOffset + }); + + _sequenceCompressor.Compress(bases, _twoBitSequence); + + // sort the masked intervals + var sortedMaskedIntervals = _twoBitSequence.MaskedIntervals.OrderBy(x => x.Begin).ThenBy(x => x.End).ToList(); + + _refSeqMaskedIntervals.Add(sortedMaskedIntervals); + _writer.Write(_twoBitSequence.Buffer, 0, _twoBitSequence.NumBufferBytes); + } + + private void WriteReferenceSequenceIndex() + { + _writer.Flush(); + _indexOffset = _stream.Position; + + _writer.WriteOpt(_refSeqIndex.Count); + + for (int i = 0; i < _refSeqIndex.Count; i++) + { + _writer.WriteOptAscii(_refSeqNames[i]); + var indexEntry = _refSeqIndex[i]; + + _writer.WriteOpt(EncodeNumBases(indexEntry.NumBases, indexEntry.SequenceOffset)); + _writer.WriteOpt(indexEntry.FileOffset); + if (indexEntry.SequenceOffset != 0) _writer.WriteOpt(indexEntry.SequenceOffset); + } + } + + private static int EncodeNumBases(int numBases, int sequenceOffset) + { + if (sequenceOffset == 0) return numBases; + return numBases | CompressedSequenceCommon.SequenceOffsetBit; + } + + private void WriteMaskedIntervals() + { + _writer.Flush(); + _maskedIntervalsOffset = _stream.Position; + + _writer.WriteOpt(_refSeqMaskedIntervals.Count); + + foreach (var list in _refSeqMaskedIntervals) + { + _writer.WriteOpt(list.Count); + foreach (var maskedEntry in list) + { + _writer.WriteOpt(maskedEntry.Begin); + _writer.WriteOpt(maskedEntry.End); + } + } + } + + private void WriteEofTag() + { + _writer.Write(CompressedSequenceCommon.EofTag); + _writer.Flush(); + + // update the index and masked intervals offsets + _stream.Position = _headerDataOffset; + _writer.Write(_indexOffset); + _writer.Write(_maskedIntervalsOffset); + } + + private void WriteHeader(IReadOnlyCollection referenceMetadataList, ICytogeneticBands genomeCytobands, GenomeAssembly genomeAssembly) + { + _writer.Write(CompressedSequenceCommon.HeaderTag); + _writer.Write(CompressedSequenceCommon.HeaderVersion); + _writer.Flush(); + + _headerDataOffset = _stream.Position; + + // grab the index and masked intervals offsets + _writer.Write(_indexOffset); + _writer.Write(_maskedIntervalsOffset); + + // grab the creation time + _writer.WriteOpt(DateTime.UtcNow.Ticks); + + // write the reference metadata + _writer.WriteOpt(referenceMetadataList.Count); + foreach (var refMetadata in referenceMetadataList) refMetadata.Write(_writer); + + // write the genome cytobands + genomeCytobands.Write(_writer); + + // write the genome assembly + _writer.Write((byte)genomeAssembly); + + // write the data start tag + _writer.Write(CompressedSequenceCommon.DataStartTag); + } + } +} diff --git a/CacheUtils/Sequence/SequenceCompressor.cs b/CacheUtils/Sequence/SequenceCompressor.cs new file mode 100644 index 00000000..2ab2c532 --- /dev/null +++ b/CacheUtils/Sequence/SequenceCompressor.cs @@ -0,0 +1,61 @@ +using VariantAnnotation.Sequence; + +namespace CacheUtils.Sequence +{ + internal sealed class SequenceCompressor + { + private readonly byte[] _convertBaseToNumber; + + public SequenceCompressor() + { + _convertBaseToNumber = new byte[256]; + + for (int index = 0; index < 256; ++index) + _convertBaseToNumber[index] = 10; + + for (int index = 0; index < "GCTA".Length; ++index) + { + _convertBaseToNumber["GCTA"[index]] = (byte)index; + _convertBaseToNumber[char.ToLower("GCTA"[index])] = (byte)index; + } + } + + public void Compress(string bases, TwoBitSequence twoBitSequence) + { + twoBitSequence.Allocate(bases.Length); + byte num1 = 0; + int index1 = 0; + int num2 = 0; + + foreach (var index2 in bases) + { + byte num3 = _convertBaseToNumber[index2]; + if (num3 == 10) num3 = 0; + num1 = (byte)((uint)num1 << 2 | num3); + ++num2; + + if (num2 != 4) continue; + + twoBitSequence.Buffer[index1] = num1; + num1 = 0; + num2 = 0; + ++index1; + } + + if (num2 != 0) twoBitSequence.Buffer[index1] = (byte)((uint)num1 << (4 - num2) * 2); + + for (int index2 = 0; index2 < bases.Length; ++index2) + { + if (bases[index2] != 'N') continue; + + int begin = index2; + int end = index2; + + for (++index2; index2 < bases.Length && bases[index2] == 'N'; ++index2) end = index2; + + var maskedEntry = new MaskedEntry(begin, end); + twoBitSequence.MaskedIntervals.Add(maskedEntry); + } + } + } +} \ No newline at end of file diff --git a/CacheUtils/Sequence/TwoBitSequence.cs b/CacheUtils/Sequence/TwoBitSequence.cs new file mode 100644 index 00000000..532e9875 --- /dev/null +++ b/CacheUtils/Sequence/TwoBitSequence.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using VariantAnnotation.Sequence; + +namespace CacheUtils.Sequence +{ + internal sealed class TwoBitSequence + { + public byte[] Buffer; + public int NumBufferBytes; + public readonly List MaskedIntervals; + + public TwoBitSequence() + { + MaskedIntervals = new List(); + } + + private static int GetNumBufferBytes(int numBases) + { + return (int)(numBases / 4.0 + 1.0); + } + + public void Allocate(int numBases) + { + NumBufferBytes = GetNumBufferBytes(numBases); + if (Buffer == null || Buffer.Length < NumBufferBytes) + Buffer = new byte[NumBufferBytes]; + MaskedIntervals.Clear(); + } + } +} diff --git a/CacheUtils/TranscriptCache/CanonicalTranscriptMarker.cs b/CacheUtils/TranscriptCache/CanonicalTranscriptMarker.cs new file mode 100644 index 00000000..1f996b24 --- /dev/null +++ b/CacheUtils/TranscriptCache/CanonicalTranscriptMarker.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Utilities; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Utilities; + +namespace CacheUtils.TranscriptCache +{ + public sealed class CanonicalTranscriptMarker + { + private readonly HashSet _lrgTranscriptIds; + + public CanonicalTranscriptMarker(HashSet lrgTranscriptIds) + { + _lrgTranscriptIds = lrgTranscriptIds; + } + + public int MarkTranscripts(MutableTranscript[] transcripts) + { + var transcriptsByGeneId = GetTranscriptsByEntrezGeneId(transcripts); + var canonicalTranscriptsByGeneId = GetCanonicalTranscriptsByGeneId(transcriptsByGeneId); + return SetCanonicalFlags(canonicalTranscriptsByGeneId, transcripts); + } + + private SortedDictionary> GetTranscriptsByEntrezGeneId(IEnumerable transcripts) + { + var genes = new SortedDictionary>(); + + foreach (var transcript in transcripts) + { + string transcriptId = RemoveVersionFromTranscriptId(transcript.Id); + int cdsLength = GetCdsLength(transcript.CodingRegion); + int transcriptLength = transcript.End - transcript.Start + 1; + var isLrg = _lrgTranscriptIds.Contains(transcriptId); + + var metadata = new TranscriptMetadata(transcriptId, transcriptLength, cdsLength, isLrg); + int geneId = ConvertGeneIdToInt(transcript.Gene.GeneId); + + if (genes.TryGetValue(geneId, out var observedMetadata)) observedMetadata.Add(metadata); + else genes[geneId] = new HashSet { metadata }; + } + + return genes; + } + + private static string RemoveVersionFromTranscriptId(string transcriptId) + { + var (id, _) = FormatUtilities.SplitVersion(transcriptId); + return id; + } + + private static SortedDictionary GetCanonicalTranscriptsByGeneId(SortedDictionary> genes) + { + // - Order all of the overlapping transcripts by cds length + // - Pick the longest transcript that has an associated Locus Reference Genome (LRG) sequence + // - If no LRGs exist for the set of transcripts, pick the longest transcript that is coding + // - If there is a tie, pick the transcript with the smaller accession id number + var canonicalTranscripts = new SortedDictionary(); + + foreach (var kvp in genes) + { + var sortedTranscripts = GetSortedTrustedTranscripts(kvp.Value); + + // pick the transcript with the smallest accession + if (sortedTranscripts.Count > 0) canonicalTranscripts[kvp.Key] = sortedTranscripts[0].TranscriptId; + } + + return canonicalTranscripts; + } + + private static int GetCdsLength(ICdnaCoordinateMap codingRegion) + { + if (codingRegion == null) return 0; + return codingRegion.CdnaEnd - codingRegion.CdnaStart + 1; + } + + private static int ConvertGeneIdToInt(string geneId) + { + if (string.IsNullOrEmpty(geneId)) throw new InvalidDataException("Expected a non-empty Entrez gene ID during canonical aggregation."); + if (geneId.StartsWith("ENSG")) geneId = geneId.Substring(4); + if (!int.TryParse(geneId, out var geneIdNumber)) throw new InvalidDataException($"Unable to convert Entrez gene ID ({geneId}) to an integer."); + return geneIdNumber; + } + + private static int SetCanonicalFlags(IReadOnlyDictionary canonicalTranscriptsByGeneId, IEnumerable transcripts) + { + int numCanonicalTranscripts = 0; + + foreach (var transcript in transcripts) + { + int geneId = ConvertGeneIdToInt(transcript.Gene.GeneId); + transcript.IsCanonical = false; + + // no canonical transcript + if (!canonicalTranscriptsByGeneId.TryGetValue(geneId, out var canonicalTranscriptId)) continue; + if (transcript.Id != canonicalTranscriptId) continue; + + // mark the transcript canonical + transcript.IsCanonical = true; + numCanonicalTranscripts++; + } + + return numCanonicalTranscripts; + } + + /// + /// returns a sorted list of all the transcripts that have an ENST, NM_, or NR_ prefix + /// + private static List GetSortedTrustedTranscripts(IEnumerable transcripts) + { + var selectedTranscripts = + transcripts.Where( + transcript => transcript.TranscriptId.StartsWith("ENST") || + transcript.TranscriptId.StartsWith("NM_") || + transcript.TranscriptId.StartsWith("NR_")).ToList(); + + return selectedTranscripts.OrderByDescending(x => x.IsLrg) + .ThenByDescending(x => x.CdsLength) + .ThenByDescending(x => x.TranscriptLength) + .ThenBy(x => x.Accession) + .ToList(); + } + + public sealed class TranscriptMetadata : IEquatable + { + public readonly string TranscriptId; + public readonly int CdsLength; + public readonly int TranscriptLength; + public readonly bool IsLrg; + public readonly int Accession; + + public TranscriptMetadata(string transcriptId, int transcriptLength, int cdsLength, bool isLrg) + { + TranscriptId = transcriptId; + TranscriptLength = transcriptLength; + CdsLength = cdsLength; + IsLrg = isLrg; + Accession = AccessionUtilities.GetAccessionNumber(transcriptId); + } + + public bool Equals(TranscriptMetadata other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(TranscriptId, other.TranscriptId) && CdsLength == other.CdsLength && + TranscriptLength == other.TranscriptLength && IsLrg == other.IsLrg && + Accession == other.Accession; + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = TranscriptId != null ? TranscriptId.GetHashCode() : 0; + hashCode = (hashCode * 397) ^ CdsLength; + hashCode = (hashCode * 397) ^ TranscriptLength; + hashCode = (hashCode * 397) ^ IsLrg.GetHashCode(); + hashCode = (hashCode * 397) ^ Accession; + return hashCode; + } + } + } + } +} diff --git a/CacheUtils/TranscriptCache/ChromosomeInterval.cs b/CacheUtils/TranscriptCache/ChromosomeInterval.cs new file mode 100644 index 00000000..1a9c2988 --- /dev/null +++ b/CacheUtils/TranscriptCache/ChromosomeInterval.cs @@ -0,0 +1,19 @@ +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; + +namespace CacheUtils.TranscriptCache +{ + public sealed class ChromosomeInterval : IChromosomeInterval + { + public IChromosome Chromosome { get; } + public int Start { get; } + public int End { get; } + + public ChromosomeInterval(IChromosome chromosome, int start, int end) + { + Chromosome = chromosome; + Start = start; + End = end; + } + } +} diff --git a/CacheUtils/TranscriptCache/Comparers/GeneComparer.cs b/CacheUtils/TranscriptCache/Comparers/GeneComparer.cs new file mode 100644 index 00000000..a05b44e9 --- /dev/null +++ b/CacheUtils/TranscriptCache/Comparers/GeneComparer.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.TranscriptCache.Comparers +{ + internal sealed class GeneComparer : EqualityComparer + { + public override bool Equals(IGene x, IGene y) + { + return x.Start == y.Start && + x.End == y.End && + x.Chromosome.Index == y.Chromosome.Index && + x.OnReverseStrand == y.OnReverseStrand && + x.Symbol == y.Symbol && + x.EntrezGeneId.WithVersion == y.EntrezGeneId.WithVersion && + x.EnsemblId.WithVersion == y.EnsemblId.WithVersion && + x.HgncId == y.HgncId; + } + + public override int GetHashCode(IGene x) + { + var entrezGeneId = x.EntrezGeneId.WithVersion; + var ensemblId = x.EnsemblId.WithVersion; + + unchecked + { + var hashCode = x.Start; + hashCode = (hashCode * 397) ^ x.End; + hashCode = (hashCode * 397) ^ x.Chromosome.Index; + hashCode = (hashCode * 397) ^ x.OnReverseStrand.GetHashCode(); + hashCode = (hashCode * 397) ^ x.Symbol.GetHashCode(); + if (entrezGeneId != null) hashCode = (hashCode * 397) ^ entrezGeneId.GetHashCode(); + if (ensemblId != null) hashCode = (hashCode * 397) ^ ensemblId.GetHashCode(); + hashCode = (hashCode * 397) ^ x.HgncId; + return hashCode; + } + } + } +} diff --git a/CacheUtils/TranscriptCache/Comparers/IntervalComparer.cs b/CacheUtils/TranscriptCache/Comparers/IntervalComparer.cs new file mode 100644 index 00000000..8811481b --- /dev/null +++ b/CacheUtils/TranscriptCache/Comparers/IntervalComparer.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.TranscriptCache.Comparers +{ + internal sealed class IntervalComparer : EqualityComparer + { + public override bool Equals(IInterval x, IInterval y) => x.Start == y.Start && x.End == y.End; + + public override int GetHashCode(IInterval x) + { + unchecked + { + return (x.Start * 397) ^ x.End; + } + } + } +} diff --git a/CacheUtils/TranscriptCache/Comparers/RegulatoryRegionComparer.cs b/CacheUtils/TranscriptCache/Comparers/RegulatoryRegionComparer.cs new file mode 100644 index 00000000..60d15c7a --- /dev/null +++ b/CacheUtils/TranscriptCache/Comparers/RegulatoryRegionComparer.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.TranscriptCache.Comparers +{ + internal sealed class RegulatoryRegionComparer : EqualityComparer + { + public override bool Equals(IRegulatoryRegion x, IRegulatoryRegion y) + { + return x.Start == y.Start && + x.End == y.End && + x.Chromosome.Index == y.Chromosome.Index && + x.Id.WithoutVersion == y.Id.WithoutVersion && + x.Type == y.Type; + } + + public override int GetHashCode(IRegulatoryRegion x) + { + unchecked + { + var hashCode = x.Start; + hashCode = (hashCode * 397) ^ x.End; + hashCode = (hashCode * 397) ^ x.Chromosome.Index.GetHashCode(); + hashCode = (hashCode * 397) ^ x.Id.WithoutVersion.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)x.Type; + return hashCode; + } + } + } +} diff --git a/CacheUtils/TranscriptCache/Comparers/UgaGeneComparer.cs b/CacheUtils/TranscriptCache/Comparers/UgaGeneComparer.cs new file mode 100644 index 00000000..70257769 --- /dev/null +++ b/CacheUtils/TranscriptCache/Comparers/UgaGeneComparer.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.TranscriptCache.Comparers +{ + public sealed class UgaGeneComparer : EqualityComparer + { + public override bool Equals(UgaGene x, UgaGene y) + { + if (ReferenceEquals(null, y)) return false; + if (ReferenceEquals(x, y)) return true; + return x.Chromosome.Index == y.Chromosome.Index && + Equals(x.GRCh37, y.GRCh37) && + Equals(x.GRCh38, y.GRCh38) && + x.GRCh38 == y.GRCh38 && + x.OnReverseStrand == y.OnReverseStrand && + x.HgncId == y.HgncId && + x.Symbol == y.Symbol && + x.EntrezGeneId == y.EntrezGeneId && + x.EnsemblId == y.EnsemblId; + } + + private static bool Equals(IInterval x, IInterval y) + { + if (x == null && y == null) return true; + if (x == null || y == null) return false; + return x.Start == y.Start && x.End == y.End; + } + + private static int GetHashCode(IInterval x) + { + unchecked { return (x.Start * 397) ^ x.End; } + } + + + public override int GetHashCode(UgaGene x) + { + unchecked + { + var hashCode = x.Chromosome.Index.GetHashCode(); + if (x.GRCh37 != null) hashCode = (hashCode * 397) ^ GetHashCode(x.GRCh37); + if (x.GRCh38 != null) hashCode = (hashCode * 397) ^ GetHashCode(x.GRCh38); + hashCode = (hashCode * 397) ^ x.OnReverseStrand.GetHashCode(); + hashCode = (hashCode * 397) ^ x.HgncId; + if (x.Symbol != null) hashCode = (hashCode * 397) ^ x.Symbol.GetHashCode(); + if (x.EntrezGeneId != null) hashCode = (hashCode * 397) ^ x.EntrezGeneId.GetHashCode(); + if (x.EnsemblId != null) hashCode = (hashCode * 397) ^ x.EnsemblId.GetHashCode(); + return hashCode; + } + } + } +} diff --git a/CacheUtils/TranscriptCache/SortExtensions.cs b/CacheUtils/TranscriptCache/SortExtensions.cs new file mode 100644 index 00000000..ebc883b5 --- /dev/null +++ b/CacheUtils/TranscriptCache/SortExtensions.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.TranscriptCache +{ + public static class SortExtensions + { + public static IOrderedEnumerable Sort(this IEnumerable elements) where T : IChromosomeInterval => + elements.OrderBy(x => x.Chromosome.Index).ThenBy(x => x.Start).ThenBy(x => x.End); + + public static IOrderedEnumerable SortInterval(this IEnumerable elements) where T : IInterval => + elements.OrderBy(x => x.Start).ThenBy(x => x.End); + } +} diff --git a/CacheUtils/TranscriptCache/TranscriptCacheBuilder.cs b/CacheUtils/TranscriptCache/TranscriptCacheBuilder.cs new file mode 100644 index 00000000..4c54305d --- /dev/null +++ b/CacheUtils/TranscriptCache/TranscriptCacheBuilder.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.Utilities; +using VariantAnnotation.Interface; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.IO.Caches; + +namespace CacheUtils.TranscriptCache +{ + public sealed class TranscriptCacheBuilder + { + private readonly ILogger _logger; + private readonly GenomeAssembly _genomeAssembly; + private readonly Source _source; + private readonly long _vepReleaseTicks; + + public TranscriptCacheBuilder(ILogger logger, GenomeAssembly genomeAssembly, Source source, long vepReleaseTicks) + { + _logger = logger; + _genomeAssembly = genomeAssembly; + _source = source; + _vepReleaseTicks = vepReleaseTicks; + } + + public TranscriptCacheStaging CreateTranscriptCache(MutableTranscript[] mutableTranscripts, + IEnumerable regulatoryRegions, IIntervalForest geneForest, int numRefSeqs) + { + _logger.Write("- assigning UGA genes to transcripts... "); + AssignUgaGenesToTranscripts(mutableTranscripts, geneForest); + _logger.WriteLine("finished."); + + var transcriptIntervalArrays = mutableTranscripts.ToTranscripts().ToIntervalArrays(numRefSeqs); + var regulatoryRegionIntervalArrays = regulatoryRegions.ToIntervalArrays(numRefSeqs); + + var customHeader = new TranscriptCacheCustomHeader(CacheConstants.VepVersion, _vepReleaseTicks); + var header = new CacheHeader(CacheConstants.Identifier, CacheConstants.SchemaVersion, + CacheConstants.DataVersion, _source, DateTime.Now.Ticks, _genomeAssembly, customHeader); + + return TranscriptCacheStaging.GetStaging(header, transcriptIntervalArrays, regulatoryRegionIntervalArrays); + } + + private void AssignUgaGenesToTranscripts(IEnumerable transcripts, IIntervalForest geneForest) + { + foreach (var transcript in transcripts) + { + var originalGene = transcript.Gene; + var ugaGenes = geneForest.GetAllOverlappingValues(originalGene.Chromosome.Index, originalGene.Start, originalGene.End); + + if (ugaGenes == null) + { + var strand = originalGene.OnReverseStrand ? "R" : "F"; + throw new InvalidDataException($"Found a transcript ({transcript.Id}) that does not have an overlapping UGA gene: gene ID: {originalGene.GeneId} {originalGene.Chromosome.UcscName} {originalGene.Start} {originalGene.End} {strand}"); + } + + transcript.UpdatedGene = PickGeneById(ugaGenes, originalGene.GeneId).ToGene(_genomeAssembly); + } + } + + private UgaGene PickGeneById(IReadOnlyList genes, string geneId) + { + if (genes.Count == 1) return genes[0]; + + var genesById = genes.GetMultiValueDict(x => _source == Source.Ensembl ? x.EnsemblId : x.EntrezGeneId); + if (!genesById.TryGetValue(geneId, out var idGenes)) throw new InvalidDataException($"Could not find {geneId} in the UGA genes list."); + + if (idGenes.Count == 1) return idGenes[0]; + throw new InvalidDataException($"Found multiple entries for {geneId} in the UGA genes list."); + } + } +} diff --git a/CacheUtils/TranscriptCache/TranscriptCacheStaging.cs b/CacheUtils/TranscriptCache/TranscriptCacheStaging.cs new file mode 100644 index 00000000..9678bced --- /dev/null +++ b/CacheUtils/TranscriptCache/TranscriptCacheStaging.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CacheUtils.MiniCache; +using CacheUtils.TranscriptCache.Comparers; +using VariantAnnotation.Caches; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.IO.Caches; + +namespace CacheUtils.TranscriptCache +{ + public sealed class TranscriptCacheStaging : IStaging + { + private readonly TranscriptCacheData _cacheData; + + private TranscriptCacheStaging(TranscriptCacheData cacheData) + { + _cacheData = cacheData; + } + + public void Write(Stream stream) + { + using (var writer = new TranscriptCacheWriter(stream, _cacheData.Header)) writer.Write(_cacheData); + } + + public static TranscriptCacheStaging GetStaging(CacheHeader header, + IntervalArray[] transcriptIntervalArrays, + IntervalArray[] regulatoryRegionIntervalArrays) + { + var uniqueData = GetUniqueData(transcriptIntervalArrays); + + var cacheData = new TranscriptCacheData(header, uniqueData.Genes, uniqueData.Introns, uniqueData.Mirnas, + uniqueData.PeptideSeqs, transcriptIntervalArrays, regulatoryRegionIntervalArrays); + + return new TranscriptCacheStaging(cacheData); + } + + private static (IGene[] Genes, IInterval[] Introns, IInterval[] Mirnas, string[] PeptideSeqs) GetUniqueData( + IEnumerable> intervalArrays) + { + var intervalComparer = new IntervalComparer(); + var geneComparer = new GeneComparer(); + + var geneSet = new HashSet(geneComparer); + var intronSet = new HashSet(intervalComparer); + var mirnaSet = new HashSet(intervalComparer); + var peptideSet = new HashSet(); + + foreach (var intervalArray in intervalArrays) + { + if (intervalArray == null) continue; + + foreach (var interval in intervalArray.Array) + { + var transcript = interval.Value; + geneSet.Add(transcript.Gene); + if (transcript.Translation?.PeptideSeq != null) peptideSet.Add(transcript.Translation.PeptideSeq); + if (transcript.Introns != null) foreach (var intron in transcript.Introns) intronSet.Add(intron); + // ReSharper disable once InvertIf + if (transcript.MicroRnas != null) foreach (var mirna in transcript.MicroRnas) mirnaSet.Add(mirna); + } + } + + var genes = geneSet.Count > 0 ? geneSet.Sort().ToArray() : null; + var introns = intronSet.Count > 0 ? intronSet.SortInterval().ToArray() : null; + var mirnas = mirnaSet.Count > 0 ? mirnaSet.SortInterval().ToArray() : null; + var peptideSeqs = peptideSet.Count > 0 ? peptideSet.OrderBy(x => x).ToArray() : null; + + return (genes, introns, mirnas, peptideSeqs); + } + } +} diff --git a/CacheUtils/TranscriptCache/TranscriptCacheUtilities.cs b/CacheUtils/TranscriptCache/TranscriptCacheUtilities.cs new file mode 100644 index 00000000..b6d82802 --- /dev/null +++ b/CacheUtils/TranscriptCache/TranscriptCacheUtilities.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using CacheUtils.Genes.Utilities; +using CacheUtils.MiniCache; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; + +namespace CacheUtils.TranscriptCache +{ + public static class TranscriptCacheUtilities + { + public static List GetTranscripts(DataBundle bundle, IChromosomeInterval interval) + { + var overlappingTranscripts = bundle.TranscriptCache.GetOverlappingTranscripts(interval.Chromosome, interval.Start, interval.End); + return overlappingTranscripts?.ToList() ?? new List(); + } + + public static IntervalArray[] ToIntervalArrays(this IEnumerable items, int numRefSeqs) where T : IChromosomeInterval + { + var intervalArrays = new IntervalArray[numRefSeqs]; + var itemsByRef = items.GetMultiValueDict(x => x.Chromosome.Index); + + foreach (var refIndex in itemsByRef.Keys.OrderBy(x => x)) + { + var unsortedItems = itemsByRef[refIndex]; + var intervals = unsortedItems.OrderBy(x => x.Start).ThenBy(x => x.End).ToIntervals(unsortedItems.Count); + intervalArrays[refIndex] = new IntervalArray(intervals); + } + + return intervalArrays; + } + + private static Interval[] ToIntervals(this IEnumerable items, int numItems) where T : IChromosomeInterval + { + var intervals = new Interval[numItems]; + int i = 0; + + foreach (var item in items) + { + intervals[i++] = new Interval(item.Start, item.End, item); + } + + return intervals; + } + } +} diff --git a/CacheUtils/TranscriptCache/TranscriptCacheWriter.cs b/CacheUtils/TranscriptCache/TranscriptCacheWriter.cs new file mode 100644 index 00000000..27652b32 --- /dev/null +++ b/CacheUtils/TranscriptCache/TranscriptCacheWriter.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using CacheUtils.TranscriptCache.Comparers; +using Compression.Algorithms; +using Compression.FileHandling; +using VariantAnnotation.Caches; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.IO; +using VariantAnnotation.IO; +using VariantAnnotation.IO.Caches; + + +namespace CacheUtils.TranscriptCache +{ + public sealed class TranscriptCacheWriter : IDisposable + { + private readonly BlockStream _blockStream; + private readonly ExtendedBinaryWriter _writer; + private readonly CacheHeader _header; + private readonly bool _leaveOpen; + + public TranscriptCacheWriter(Stream stream, CacheHeader header, bool leaveOpen = false) + { + _blockStream = new BlockStream(new Zstandard(), stream, CompressionMode.Compress); + _writer = new ExtendedBinaryWriter(_blockStream, Encoding.UTF8, leaveOpen); + _header = header; + _leaveOpen = leaveOpen; + } + + public void Dispose() + { + if (!_leaveOpen) _blockStream.Dispose(); + _writer.Dispose(); + } + + /// + /// writes the annotations to the current database file + /// + public void Write(TranscriptCacheData cacheData) + { + _blockStream.WriteHeader(_header.Write); + + WriteItems(_writer, cacheData.Genes, x => x.Write(_writer)); + WriteItems(_writer, cacheData.Introns, x => ((ISerializable)x).Write(_writer)); + WriteItems(_writer, cacheData.Mirnas, x => ((ISerializable)x).Write(_writer)); + WriteItems(_writer, cacheData.PeptideSeqs, x => _writer.WriteOptAscii(x)); + + var geneComparer = new GeneComparer(); + var intervalComparer = new IntervalComparer(); + + var geneIndices = CreateIndex(cacheData.Genes, geneComparer); + var intronIndices = CreateIndex(cacheData.Introns, intervalComparer); + var microRnaIndices = CreateIndex(cacheData.Mirnas, intervalComparer); + var peptideIndices = CreateIndex(cacheData.PeptideSeqs, EqualityComparer.Default); + + WriteIntervals(_writer, cacheData.RegulatoryRegionIntervalArrays, x => x.Write(_writer)); + WriteIntervals(_writer, cacheData.TranscriptIntervalArrays, x => x.Write(_writer, geneIndices, intronIndices, microRnaIndices, peptideIndices)); + } + + private static void WriteIntervals(IExtendedBinaryWriter writer, IReadOnlyCollection> intervalArrays, + Action writeMethod) + { + writer.WriteOpt(intervalArrays.Count); + + foreach (var intervalArray in intervalArrays) + { + if (intervalArray == null) + { + writer.WriteOpt(0); + continue; + } + + writer.WriteOpt(intervalArray.Array.Length); + foreach (var interval in intervalArray.Array) writeMethod(interval.Value); + } + + writer.Write(CacheConstants.GuardInt); + } + + internal static void WriteItems(IExtendedBinaryWriter writer, IReadOnlyCollection items, Action writeMethod) + { + if (items == null) + { + writer.WriteOpt(0); + } + else + { + writer.WriteOpt(items.Count); + foreach (var item in items) writeMethod(item); + } + + writer.Write(CacheConstants.GuardInt); + } + + /// + /// creates an index out of a array + /// + internal static Dictionary CreateIndex(IReadOnlyList array, IEqualityComparer comparer) + { + var index = new Dictionary(comparer); + if (array == null) return index; + + for (int currentIndex = 0; currentIndex < array.Count; currentIndex++) + index[array[currentIndex]] = currentIndex; + + return index; + } + } +} diff --git a/CacheUtils/TranscriptCache/TranscriptConversionExtensions.cs b/CacheUtils/TranscriptCache/TranscriptConversionExtensions.cs new file mode 100644 index 00000000..2db2bde0 --- /dev/null +++ b/CacheUtils/TranscriptCache/TranscriptConversionExtensions.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using VariantAnnotation.AnnotatedPositions.Transcript; +using VariantAnnotation.Caches.DataStructures; +using VariantAnnotation.Interface.AnnotatedPositions; + +namespace CacheUtils.TranscriptCache +{ + public static class TranscriptConversionExtensions + { + public static IEnumerable ToTranscripts(this MutableTranscript[] mutableTranscripts) + { + var transcripts = new List(mutableTranscripts.Length); + transcripts.AddRange(mutableTranscripts.Select(mt => mt.ToTranscript())); + return transcripts; + } + + private static ITranscript ToTranscript(this MutableTranscript mt) + { + var translation = mt.CodingRegion.IsNull ? null : new Translation(mt.CodingRegion, CompactId.Convert(mt.ProteinId, mt.ProteinVersion), + mt.PeptideSequence); + + var startExonPhase = mt.StartExonPhase < 0 ? (byte)0 : (byte)mt.StartExonPhase; + var sortedCdnaMaps = mt.CdnaMaps.OrderBy(x => x.Start).ToArray(); + + return new Transcript(mt.Chromosome, mt.Start, mt.End, CompactId.Convert(mt.Id, mt.Version), translation, + mt.BioType, mt.UpdatedGene, mt.TotalExonLength, startExonPhase, mt.IsCanonical, mt.Introns, + mt.MicroRnas, sortedCdnaMaps, mt.SiftIndex, mt.PolyPhenIndex, mt.Source, mt.CdsStartNotFound, + mt.CdsEndNotFound, mt.SelenocysteinePositions, mt.RnaEdits); + } + } +} diff --git a/CacheUtils/Utilities/AccessionUtilities.cs b/CacheUtils/Utilities/AccessionUtilities.cs new file mode 100644 index 00000000..db96786b --- /dev/null +++ b/CacheUtils/Utilities/AccessionUtilities.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Utilities +{ + internal static class AccessionUtilities + { + internal static (string Id, byte Version) GetMaxVersion(string originalId, byte originalVersion) + { + var (pureId, idVersion) = FormatUtilities.SplitVersion(originalId); + return (pureId, Math.Max(originalVersion, idVersion)); + } + + public static int GetAccessionNumber(string s) + { + if (string.IsNullOrEmpty(s)) return -1; + return s.StartsWith("ENS") ? GetEnsemblAccessionNumber(s) : GetRefSeqAccessionNumber(s); + } + + private static int GetRefSeqAccessionNumber(string s) + { + int firstUnderlinePos = s.IndexOf('_'); + if (firstUnderlinePos == -1) throw new InvalidDataException("Expected an underline in the transcript ID, but didn't find any."); + + string id = s.Substring(firstUnderlinePos + 1); + return int.Parse(id); + } + + private static int GetEnsemblAccessionNumber(string s) + { + string id = s.Substring(4); + return int.Parse(id); + } + } +} diff --git a/CacheUtils/Utilities/RemoteFile.cs b/CacheUtils/Utilities/RemoteFile.cs new file mode 100644 index 00000000..32335967 --- /dev/null +++ b/CacheUtils/Utilities/RemoteFile.cs @@ -0,0 +1,66 @@ +using System; +using System.IO; +using System.Net; +using VariantAnnotation.Interface; +using VariantAnnotation.Utilities; + +namespace CacheUtils.Utilities +{ + public sealed class RemoteFile + { + private readonly string _description; + public readonly string FilePath; + private readonly string _url; + + static RemoteFile() => ServicePointManager.DefaultConnectionLimit = int.MaxValue; + + public RemoteFile(string description, string url, bool addDate = true) + { + _description = description; + _url = url; + FilePath = Path.Combine(Path.GetTempPath(), GetFilename(url, addDate)); + } + + internal static string GetFilename(string url, bool addDate) + { + int lastSlashPos = url.LastIndexOf('/'); + string originalFilename = url.Substring(lastSlashPos + 1); + + if (!addDate) return originalFilename; + + string extension = Path.GetExtension(originalFilename); + string filenameStub = Path.GetFileNameWithoutExtension(originalFilename); + + return $"{filenameStub}_{Date.GetDate(DateTime.Now.Ticks)}{extension}"; + } + + public void Download(ILogger logger) + { + if (File.Exists(FilePath)) return; + + logger.WriteLine($"- downloading the {_description}"); + while (!SuccessfulDownload()) + { + logger.WriteLine($"- requeueing download of the {_description}"); + } + } + + private bool SuccessfulDownload() + { + try + { + using (var client = new WebClient()) + { + client.Proxy = null; + client.DownloadFileTaskAsync(_url, FilePath).Wait(); + } + } + catch (Exception) + { + return false; + } + + return true; + } + } +} diff --git a/CacheUtils/Utilities/TaskExtensions.cs b/CacheUtils/Utilities/TaskExtensions.cs new file mode 100644 index 00000000..cd221329 --- /dev/null +++ b/CacheUtils/Utilities/TaskExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CommandLine.Utilities; +using VariantAnnotation.Interface; + +namespace CacheUtils.Utilities +{ + public static class TaskExtensions + { + public static void Execute(this IReadOnlyList items, ILogger logger, string description, + Action executeAction, int numThreads = 5) + { + var bench = new Benchmark(); + var tasks = new Task[items.Count]; + var maxThread = new SemaphoreSlim(numThreads); + + for (int i = 0; i < items.Count; i++) + { + maxThread.Wait(); + var item = items[i]; + tasks[i] = Task.Factory.StartNew(() => executeAction(item), TaskCreationOptions.LongRunning) + .ContinueWith(task => maxThread.Release()); + } + + Task.WaitAll(tasks); + logger.WriteLine($"- all {description} finished ({Benchmark.ToHumanReadable(bench.GetElapsedTime())}).\n"); + } + } +} diff --git a/CommandLine/Builders/ConsoleAppBuilder.cs b/CommandLine/Builders/ConsoleAppBuilder.cs index a71e252a..7079b196 100644 --- a/CommandLine/Builders/ConsoleAppBuilder.cs +++ b/CommandLine/Builders/ConsoleAppBuilder.cs @@ -88,6 +88,8 @@ public IConsoleAppBanner ShowBanner(string authors) else if (!Data.DisableOutput) CommandLineUtilities.DisplayBanner(authors); return new ConsoleAppBanner(Data); } + + public IConsoleAppBanner SkipBanner() => new ConsoleAppBanner(Data); } public sealed class ConsoleAppBanner : IConsoleAppBanner @@ -118,7 +120,7 @@ public IConsoleAppErrors ShowErrors() { if (_data.Errors.Count > 0) { - Console.WriteLine("Some problems were encountered when parsing the command line options:"); + Console.WriteLine("\nSome problems were encountered when parsing the command line options:"); PrintErrors(); Console.WriteLine("\nFor a complete list of command line options, type \"dotnet {0} -h\"", CommandLineUtilities.CommandFileName); } diff --git a/CommandLine/Builders/IConsoleAppBuilder.cs b/CommandLine/Builders/IConsoleAppBuilder.cs index 54e15f37..1887c51d 100644 --- a/CommandLine/Builders/IConsoleAppBuilder.cs +++ b/CommandLine/Builders/IConsoleAppBuilder.cs @@ -20,6 +20,7 @@ public interface IConsoleAppValidator { IConsoleAppValidator DisableOutput(bool condition); IConsoleAppBanner ShowBanner(string authors); + IConsoleAppBanner SkipBanner(); IConsoleAppBuilderData Data { get; } bool SkipValidation { get; } } diff --git a/CommandLine/Builders/TopLevelAppBuilder.cs b/CommandLine/Builders/TopLevelAppBuilder.cs index 4d73fae1..e1d23beb 100644 --- a/CommandLine/Builders/TopLevelAppBuilder.cs +++ b/CommandLine/Builders/TopLevelAppBuilder.cs @@ -49,8 +49,7 @@ public sealed class TopLevelAppValidator : ITopLevelAppValidator public ITopLevelAppBanner ShowBanner(string authors) { - if(_data.ShowHelpMenu || _data.Errors.Count >0) - CommandLineUtilities.DisplayBanner(authors); + CommandLineUtilities.DisplayBanner(authors); return new TopLevelAppBanner(_data); } } @@ -148,13 +147,12 @@ public ExitCodes Execute() { if (!Continue) return _data.ExitCode; - var benchmark = new Benchmark(); ExitCodes exitCode; try { - exitCode = _data.ExecuteMethod(_data.Command, _data.Arguments.Skip(1).ToArray()); - ShowPerformanceData(benchmark); + var arguments = _data.Arguments.Skip(1).ToArray(); + exitCode = _data.ExecuteMethod(_data.Command, arguments); } catch (Exception e) { @@ -163,16 +161,6 @@ public ExitCodes Execute() return exitCode; } - - private static void ShowPerformanceData(Benchmark benchmark) - { - var peakMemoryUsageBytes = MemoryUtilities.GetPeakMemoryUsage(); - var wallTimeSpan = benchmark.GetElapsedTime(); - - Console.WriteLine(); - if (peakMemoryUsageBytes > 0) Console.WriteLine("Peak memory usage: {0}", MemoryUtilities.ToHumanReadable(peakMemoryUsageBytes)); - Console.WriteLine("Time: {0}", Benchmark.ToHumanReadable(wallTimeSpan)); - } } public sealed class TopLevelAppBuilderData : ITopLevelAppBuilderData @@ -187,8 +175,7 @@ public sealed class TopLevelAppBuilderData : ITopLevelAppBuilderData public bool ShowHelpMenu { get; set; } public Func ExecuteMethod { get; set; } - - + public TopLevelAppBuilderData(string[] arguments, Dictionary ops) { Arguments = arguments; diff --git a/CommandLine/Builders/ValidationExtensions.cs b/CommandLine/Builders/ValidationExtensions.cs index 2369f56a..925b0ca3 100644 --- a/CommandLine/Builders/ValidationExtensions.cs +++ b/CommandLine/Builders/ValidationExtensions.cs @@ -27,8 +27,6 @@ public static IConsoleAppValidator CheckNonZero(this IConsoleAppValidator valida /// /// check if each file exists /// - - /// public static IConsoleAppValidator CheckEachFilenameExists(this IConsoleAppValidator validator, List filePaths, string description, string commandLineOption, bool isRequired = true) { @@ -62,6 +60,69 @@ public static IConsoleAppValidator CheckInputFilenameExists(this IConsoleAppVali return validator; } + /// + /// checks if an input file exists + /// + public static IConsoleAppValidator CheckInputFilenamesExist(this IConsoleAppValidator validator, + List filePaths, string description, string commandLineOption) + { + if (validator.SkipValidation) return validator; + + if (filePaths == null || filePaths.Count == 0) + { + validator.Data.AddError( + $"A {description} file was not specified. Please use the {commandLineOption} parameter.", + ExitCodes.MissingCommandLineOption); + } + else + { + foreach (var filePath in filePaths) + validator.CheckInputFilenameExists(filePath, description, commandLineOption); + } + + return validator; + } + + /// + /// checks if an input file exists and has the appropriate filename suffix + /// + public static IConsoleAppValidator CheckOutputFilenameSuffix(this IConsoleAppValidator validator, + string filePath, string fileSuffix, string description, string commandLineOption) + { + if (validator.SkipValidation) return validator; + + validator.CheckInputFilenameExists(filePath, description, commandLineOption); + + if (!filePath.EndsWith(fileSuffix)) + { + validator.Data.AddError($"The {description} file ({filePath}) does not end with a {fileSuffix}.", ExitCodes.BadArguments); + } + + return validator; + } + + /// + /// checks if an input directory exists + /// + public static IConsoleAppValidator CheckDirectoryExists(this IConsoleAppValidator validator, string dirPath, + string description, string commandLineOption) + { + if (validator.SkipValidation) return validator; + + if (string.IsNullOrEmpty(dirPath)) + { + validator.Data.AddError( + $"The {description} directory was not specified. Please use the {commandLineOption} parameter.", + ExitCodes.MissingCommandLineOption); + } + else if (!Directory.Exists(dirPath)) + { + validator.Data.AddError($"The {description} directory ({dirPath}) does not exist.", ExitCodes.PathNotFound); + } + + return validator; + } + /// /// checks if an input directory exists /// @@ -83,17 +144,6 @@ public static IConsoleAppValidator CheckEachDirectoryContainsFiles(this IConsole return validator; } - public static IConsoleAppValidator CheckDirectoryExists(this IConsoleAppValidator validator, - string directory, string description, string commandLineOption) - { - if (validator.SkipValidation) return validator; - - if (! Directory.Exists(directory)) - validator.Data.AddError($"The {description} directory ({directory} specified by {commandLineOption}) does not exist.", ExitCodes.FileNotFound); - - return validator; - } - /// /// checks if the required parameter has been set /// @@ -110,5 +160,24 @@ public static IConsoleAppValidator HasRequiredParameter(this IConsoleAppValid return validator; } + + /// + /// checks if the required date has been set and is parseable + /// + public static IConsoleAppValidator HasRequiredDate(this IConsoleAppValidator validator, string date, + string description, string commandLineOption) + { + if (validator.SkipValidation) return validator; + + validator.HasRequiredParameter(date, description, commandLineOption); + if (string.IsNullOrEmpty(date)) return validator; + + if (!DateTime.TryParse(date, out var _)) + { + validator.Data.AddError($"The {description} was not specified as a date (YYYY-MM-dd). Please use the {commandLineOption} parameter.", ExitCodes.BadArguments); + } + + return validator; + } } } diff --git a/CommandLine/VersionProviders/DefaultVersionProvider.cs b/CommandLine/VersionProviders/DefaultVersionProvider.cs index add1b06c..38d938cb 100644 --- a/CommandLine/VersionProviders/DefaultVersionProvider.cs +++ b/CommandLine/VersionProviders/DefaultVersionProvider.cs @@ -1,4 +1,4 @@ -using VariantAnnotation.Interface.Providers; +using VariantAnnotation.Interface.Providers; namespace CommandLine.VersionProviders { diff --git a/CommonUtilities/ReferenceNameUtilities.cs b/CommonUtilities/ReferenceNameUtilities.cs new file mode 100644 index 00000000..cadda1b4 --- /dev/null +++ b/CommonUtilities/ReferenceNameUtilities.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.IO; +using VariantAnnotation.Interface.Sequence; + +namespace CommonUtilities +{ + public static class ReferenceNameUtilities + { + public static IChromosome GetChromosome(IDictionary refNameToChromosome, + string referenceName) + { + if (referenceName == null) return new EmptyChromosome(string.Empty); + return !refNameToChromosome.TryGetValue(referenceName, out IChromosome chromosome) + ? new EmptyChromosome(referenceName) + : chromosome; + } + + public static IChromosome GetChromosome(IDictionary refIndexToChromosome, ushort referenceIndex) + { + if (!refIndexToChromosome.TryGetValue(referenceIndex, out IChromosome chromosome)) + { + throw new InvalidDataException($"Unable to find the reference index ({referenceIndex}) in the refIndexToChromosome dictionary."); + } + + return chromosome; + } + + public static bool IsEmpty(this IChromosome chromosome) => chromosome.Index == ushort.MaxValue; + } +} diff --git a/Compression/Algorithms/Zstandard.cs b/Compression/Algorithms/Zstandard.cs index 40a67d35..32371618 100644 --- a/Compression/Algorithms/Zstandard.cs +++ b/Compression/Algorithms/Zstandard.cs @@ -8,9 +8,6 @@ public sealed class Zstandard : ICompressionAlgorithm { private readonly int _compressionLevel; - /// - /// constructor - /// public Zstandard(int compressionLevel = 17) { _compressionLevel = compressionLevel; diff --git a/ErrorHandling/Exceptions/InconsistantGenomeAssemblyException.cs b/ErrorHandling/Exceptions/InconsistantGenomeAssemblyException.cs deleted file mode 100644 index da7a515c..00000000 --- a/ErrorHandling/Exceptions/InconsistantGenomeAssemblyException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ErrorHandling.Exceptions -{ - public sealed class InconsistantGenomeAssemblyException : Exception - { - - public InconsistantGenomeAssemblyException():base("Found more than one genome assembly represented in the selected data sources.") - { - - } - - } -} diff --git a/ErrorHandling/ExitCodes.cs b/ErrorHandling/ExitCodes.cs index 1d57cd10..fc997c26 100644 --- a/ErrorHandling/ExitCodes.cs +++ b/ErrorHandling/ExitCodes.cs @@ -10,17 +10,17 @@ public enum ExitCodes // Windows-specific // ================ - Success = 0, - InvalidFunction = 1, - FileNotFound = 2, - PathNotFound = 3, - AccessDenied = 5, - BadFormat = 11, - InvalidData = 13, - OutofMemory = 14, - SharingViolation = 32, + Success = 0, + InvalidFunction = 1, + FileNotFound = 2, + PathNotFound = 3, + AccessDenied = 5, + BadFormat = 11, + InvalidData = 13, + OutofMemory = 14, + SharingViolation = 32, CallNotImplemented = 120, - BadArguments = 160, + BadArguments = 160, // ================= // Illumina-specific @@ -34,9 +34,9 @@ public enum ExitCodes UserError = 210, // file (220 - 239) - InvalidFileFormat = 220, - FileNotSorted = 221, - MissingFilenameSuffix = 222, + InvalidFileFormat = 220, + FileNotSorted = 221, + MissingFilenameSuffix = 222, MissingCompressionLibrary = 223, // functionality (240 - 259) diff --git a/Jasix/DataStructures/Utilities.cs b/Jasix/DataStructures/Utilities.cs index abc41114..9632caa0 100644 --- a/Jasix/DataStructures/Utilities.cs +++ b/Jasix/DataStructures/Utilities.cs @@ -25,7 +25,7 @@ public static (string Chromosome, int Start, int End) ParseQuery(string position if (!match.Success) throw new UserErrorException($"region {trimmedPos} is not valid, please specify a valid region, e.g., chr1, 1, 1:1234 or 1:1234-4567"); var chromosome = match.Groups[1].ToString(); - if (!match.Groups[2].Success && !match.Groups[3].Success) return (chromosome, 1, Int32.MaxValue); + if (!match.Groups[2].Success && !match.Groups[3].Success) return (chromosome, 1, int.MaxValue); var start = Convert.ToInt32(match.Groups[2].ToString()); diff --git a/Nirvana.sln.DotSettings b/Nirvana.sln.DotSettings index 54eb781a..a6788eae 100644 --- a/Nirvana.sln.DotSettings +++ b/Nirvana.sln.DotSettings @@ -2,6 +2,7 @@ SOLUTION WARNING WARNING + DO_NOT_SHOW CNV GR IO diff --git a/Nirvana/ConfigurationSettings.cs b/Nirvana/ConfigurationSettings.cs deleted file mode 100644 index 6781b986..00000000 --- a/Nirvana/ConfigurationSettings.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace Nirvana -{ - public static class ConfigurationSettings - { - public static string InputCachePrefix; - public static readonly List SupplementaryAnnotationDirectories = new List(); - public static string VcfPath; - public static string RefSequencePath; - public static string OutputFileName; - - public static string PluginDirectory; - - public static bool Vcf; - public static bool Gvcf; - public static bool ForceMitochondrialAnnotation; - public static bool ReportAllSvOverlappingTranscripts; - public static bool EnableLoftee; - public static string ConservationScoreDir { get; set; } - } -} \ No newline at end of file diff --git a/Nirvana/Nirvana.cs b/Nirvana/Nirvana.cs index 7bcca70e..115d929d 100644 --- a/Nirvana/Nirvana.cs +++ b/Nirvana/Nirvana.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using CommandLine.Builders; using CommandLine.NDesk.Options; @@ -12,12 +11,14 @@ using VariantAnnotation; using VariantAnnotation.Interface; using VariantAnnotation.Interface.GeneAnnotation; +using VariantAnnotation.Interface.Plugins; using VariantAnnotation.Interface.Positions; using VariantAnnotation.Interface.Providers; using VariantAnnotation.Interface.Sequence; using VariantAnnotation.IO; using VariantAnnotation.IO.Caches; using VariantAnnotation.IO.VcfWriter; +using VariantAnnotation.Logger; using VariantAnnotation.Providers; using VariantAnnotation.SA; using VariantAnnotation.Utilities; @@ -26,50 +27,46 @@ namespace Nirvana { public sealed class Nirvana { - private static readonly string AnnotatorVersionTag = "Illumina Annotation Engine " + CommandLineUtilities.Version; - private readonly PerformanceMetrics _performanceMetrics = PerformanceMetrics.Instance; - private readonly VcfConversion _conversion = new VcfConversion(); + private static string _inputCachePrefix; + private static readonly List SupplementaryAnnotationDirectories = new List(); + private static string _vcfPath; + private static string _refSequencePath; + private static string _outputFileName; + private static string _pluginDirectory; - private ExitCodes ProgramExecution() - { - var sequenceProvider = ProviderUtilities.GetSequenceProvider(ConfigurationSettings.RefSequencePath); - var transcriptAnnotationProvider = ProviderUtilities.GetTranscriptAnnotationProvider(ConfigurationSettings.InputCachePrefix, sequenceProvider); - var saProvider = ProviderUtilities.GetSaProvider(ConfigurationSettings.SupplementaryAnnotationDirectories); - var conservationProvider = ProviderUtilities.GetConservationProvider(ConfigurationSettings.SupplementaryAnnotationDirectories); - var refMinorProvider = ProviderUtilities.GetRefMinorProvider(ConfigurationSettings.SupplementaryAnnotationDirectories); - var geneAnnotationProvider = ProviderUtilities.GetGeneAnnotationProviders(ConfigurationSettings.SupplementaryAnnotationDirectories); - - var pluglins = PluginUtilities.LoadPlugins(ConfigurationSettings.PluginDirectory); - var annotator = ProviderUtilities.GetAnnotator(transcriptAnnotationProvider, sequenceProvider, saProvider, conservationProvider, geneAnnotationProvider,pluglins); - - var dataSourceVersions = new List(); - dataSourceVersions.AddRange(transcriptAnnotationProvider.DataSourceVersions); - if (saProvider != null) dataSourceVersions.AddRange(saProvider.DataSourceVersions); - if (geneAnnotationProvider != null ) - { - dataSourceVersions.AddRange(geneAnnotationProvider.DataSourceVersions); - } + private static bool _vcf; + private static bool _gvcf; + private static bool _forceMitochondrialAnnotation; + private static bool _reportAllSvOverlappingTranscripts; - if (conservationProvider != null) dataSourceVersions.AddRange(conservationProvider.DataSourceVersions); - - if (pluglins != null) - { - foreach (var pluglin in pluglins) - { - var pluginDataSourceVersions = pluglin.GetDataSourceVersions(); - if (pluginDataSourceVersions != null) - dataSourceVersions.AddRange(pluginDataSourceVersions); - } - } + private readonly string _annotatorVersionTag = "Illumina Annotation Engine " + CommandLineUtilities.Version; + private readonly VcfConversion _conversion = new VcfConversion(); + private ExitCodes ProgramExecution() + { + var sequenceProvider = ProviderUtilities.GetSequenceProvider(_refSequencePath); + var transcriptAnnotationProvider = ProviderUtilities.GetTranscriptAnnotationProvider(_inputCachePrefix, sequenceProvider); + var saProvider = ProviderUtilities.GetSaProvider(SupplementaryAnnotationDirectories); + var conservationProvider = ProviderUtilities.GetConservationProvider(SupplementaryAnnotationDirectories); + var refMinorProvider = ProviderUtilities.GetRefMinorProvider(SupplementaryAnnotationDirectories); + var geneAnnotationProvider = ProviderUtilities.GetGeneAnnotationProvider(SupplementaryAnnotationDirectories); + var plugins = PluginUtilities.LoadPlugins(_pluginDirectory); + var annotator = ProviderUtilities.GetAnnotator(transcriptAnnotationProvider, sequenceProvider, saProvider, conservationProvider, geneAnnotationProvider, plugins); + + var logger = _outputFileName == "" ? (ILogger)new NullLogger() : new ConsoleLogger(); + var metrics = new PerformanceMetrics(logger); + + var dataSourceVersions = GetDataSourceVersions(plugins, transcriptAnnotationProvider, saProvider, + geneAnnotationProvider, conservationProvider); + var vepDataVersion = CacheConstants.VepVersion + "." + CacheConstants.DataVersion + "." + SaDataBaseCommon.DataVersion; - var jasixFileName = ConfigurationSettings.OutputFileName + ".json.gz" + JasixCommons.FileExt; + var jasixFileName = _outputFileName + ".json.gz" + JasixCommons.FileExt; - using (var outputWriter = ReadWriteUtilities.GetOutputWriter(ConfigurationSettings.OutputFileName)) - using (var vcfReader = ReadWriteUtilities.GetVcfReader(ConfigurationSettings.VcfPath, sequenceProvider.GetChromosomeDictionary(), refMinorProvider, ConfigurationSettings.ReportAllSvOverlappingTranscripts)) - using (var jsonWriter = new JsonWriter(outputWriter, AnnotatorVersionTag, Date.CurrentTimeStamp, vepDataVersion, dataSourceVersions, sequenceProvider.GenomeAssembly.ToString(), vcfReader.GetSampleNames())) - using (var vcfWriter = ConfigurationSettings.Vcf ? new LiteVcfWriter(ReadWriteUtilities.GetVcfOutputWriter(ConfigurationSettings.OutputFileName), vcfReader.GetHeaderLines(), AnnotatorVersionTag, vepDataVersion, dataSourceVersions) : null) - using (var gvcfWriter = ConfigurationSettings.Gvcf ? new LiteVcfWriter(ReadWriteUtilities.GetGvcfOutputWriter(ConfigurationSettings.OutputFileName), vcfReader.GetHeaderLines(), AnnotatorVersionTag, vepDataVersion, dataSourceVersions) : null) + using (var outputWriter = ReadWriteUtilities.GetOutputWriter(_outputFileName)) + using (var vcfReader = ReadWriteUtilities.GetVcfReader(_vcfPath, sequenceProvider.RefNameToChromosome, refMinorProvider, _reportAllSvOverlappingTranscripts)) + using (var jsonWriter = new JsonWriter(outputWriter, _annotatorVersionTag, Date.CurrentTimeStamp, vepDataVersion, dataSourceVersions, sequenceProvider.GenomeAssembly.ToString(), vcfReader.GetSampleNames())) + using (var vcfWriter = _vcf ? new LiteVcfWriter(ReadWriteUtilities.GetVcfOutputWriter(_outputFileName), vcfReader.GetHeaderLines(), _annotatorVersionTag, vepDataVersion, dataSourceVersions) : null) + using (var gvcfWriter = _gvcf ? new LiteVcfWriter(ReadWriteUtilities.GetGvcfOutputWriter(_outputFileName), vcfReader.GetHeaderLines(), _annotatorVersionTag, vepDataVersion, dataSourceVersions) : null) using (var jasixIndexCreator = new OnTheFlyIndexCreator(FileUtilities.GetCreateStream(jasixFileName))) { var bgzipTextWriter = outputWriter as BgzipTextWriter; @@ -80,18 +77,17 @@ private ExitCodes ProgramExecution() if (vcfReader.IsRcrsMitochondrion && annotator.GenomeAssembly == GenomeAssembly.GRCh37 || annotator.GenomeAssembly == GenomeAssembly.GRCh38 - || ConfigurationSettings.ForceMitochondrialAnnotation) + || _forceMitochondrialAnnotation) annotator.EnableMitochondrialAnnotation(); int previousChromIndex = -1; IPosition position; var sortedVcfChecker = new SortedVcfChecker(); - while ((position = vcfReader.GetNextPosition()) != null) { sortedVcfChecker.CheckVcfOrder(position.Chromosome.UcscName); - previousChromIndex = UpdatePerformanceMetrics(previousChromIndex, position.Chromosome); + previousChromIndex = UpdatePerformanceMetrics(previousChromIndex, position.Chromosome, metrics); var annotatedPosition = annotator.Annotate(position); @@ -104,19 +100,13 @@ private ExitCodes ProgramExecution() jsonWriter.WriteJsonEntry(jsonOutput); if (annotatedPosition.AnnotatedVariants?.Length > 0) vcfWriter?.Write(_conversion.Convert(annotatedPosition)); - if (annotatedPosition.AnnotatedVariants?.Length > 0) - { - gvcfWriter?.Write(_conversion.Convert(annotatedPosition)); - } - else - { - gvcfWriter?.Write(string.Join("\t", position.VcfFields)); - } - _performanceMetrics.Increment(); - } + gvcfWriter?.Write(annotatedPosition.AnnotatedVariants?.Length > 0 + ? _conversion.Convert(annotatedPosition) + : string.Join("\t", position.VcfFields)); - if (previousChromIndex != -1) _performanceMetrics.StopReference(); + metrics.Increment(); + } WriteGeneAnnotations(annotator.GetAnnotatedGenes(), jsonWriter); } @@ -127,10 +117,21 @@ private ExitCodes ProgramExecution() } } + metrics.ShowAnnotationTime(); + return ExitCodes.Success; } - private static void WriteGeneAnnotations(IList annotatedGenes, JsonWriter writer) + private static List GetDataSourceVersions(IEnumerable plugins, + params IProvider[] providers) + { + var dataSourceVersions = new List(); + if (plugins != null) foreach (var provider in plugins) if (provider.DataSourceVersions != null) dataSourceVersions.AddRange(provider.DataSourceVersions); + foreach (var provider in providers) if (provider != null) dataSourceVersions.AddRange(provider.DataSourceVersions); + return dataSourceVersions; + } + + private static void WriteGeneAnnotations(ICollection annotatedGenes, JsonWriter writer) { if (annotatedGenes.Count == 0) return; var sb = new StringBuilder(); @@ -139,13 +140,12 @@ private static void WriteGeneAnnotations(IList annotatedGenes, J writer.WriteAnnotatedGenes(sb.ToString()); } - private int UpdatePerformanceMetrics(int previousChromIndex, IChromosome chromosome) + private static int UpdatePerformanceMetrics(int previousChromIndex, IChromosome chromosome, PerformanceMetrics metrics) { + // ReSharper disable once InvertIf if (chromosome.Index != previousChromIndex) { - if (previousChromIndex != -1) _performanceMetrics.StopReference(); - _performanceMetrics.StartReference(chromosome.UcscName); - + metrics.StartAnnotatingReference(chromosome); previousChromIndex = chromosome.Index; } @@ -160,71 +160,71 @@ private static int Main(string[] args) { "cache|c=", "input cache {prefix}", - v => ConfigurationSettings.InputCachePrefix = v + v => _inputCachePrefix = v }, { "in|i=", "input VCF {path}", - v => ConfigurationSettings.VcfPath = v + v => _vcfPath = v }, { "plugin|p=", "plugin {directory}", - v => ConfigurationSettings.PluginDirectory = v + v => _pluginDirectory = v }, { "gvcf", "enables genome vcf output", - v => ConfigurationSettings.Gvcf = v != null + v => _gvcf = v != null }, { "vcf", "enables vcf output", - v => ConfigurationSettings.Vcf = v != null + v => _vcf = v != null }, { "out|o=", "output {file path}", - v => ConfigurationSettings.OutputFileName = v + v => _outputFileName = v }, { "ref|r=", "input compressed reference sequence {path}", - v => ConfigurationSettings.RefSequencePath = v + v => _refSequencePath = v }, { "sd=", "input supplementary annotation {directory}", - v => ConfigurationSettings.SupplementaryAnnotationDirectories.Add(v) + v => SupplementaryAnnotationDirectories.Add(v) }, { "force-mt", "forces to annotate mitochondrial variants", - v => ConfigurationSettings.ForceMitochondrialAnnotation = v != null + v => _forceMitochondrialAnnotation = v != null }, { "verbose-transcripts", "reports all overlapping transcripts for structural variants", - v => ConfigurationSettings.ReportAllSvOverlappingTranscripts = v != null + v => _reportAllSvOverlappingTranscripts = v != null } }; var exitCode = new ConsoleAppBuilder(args, ops) .UseVersionProvider(new VersionProvider()) .Parse() - .CheckInputFilenameExists(ConfigurationSettings.VcfPath, "vcf", "--in", true, "-") - .CheckInputFilenameExists(ConfigurationSettings.RefSequencePath, "reference sequence", "--ref") - .CheckInputFilenameExists(CacheConstants.TranscriptPath(ConfigurationSettings.InputCachePrefix), "transcript cache", "--cache") - .CheckInputFilenameExists(CacheConstants.SiftPath(ConfigurationSettings.InputCachePrefix), "SIFT cache", "--cache") - .CheckInputFilenameExists(CacheConstants.PolyPhenPath(ConfigurationSettings.InputCachePrefix), "PolyPhen cache", "--cache") - .CheckEachDirectoryContainsFiles(ConfigurationSettings.SupplementaryAnnotationDirectories, "supplementary annotation", "--sd", "*.nsa") - .HasRequiredParameter(ConfigurationSettings.OutputFileName, "output file stub", "--out") - .Enable(ConfigurationSettings.OutputFileName == "-", () => + .CheckInputFilenameExists(_vcfPath, "vcf", "--in", true, "-") + .CheckInputFilenameExists(_refSequencePath, "reference sequence", "--ref") + .CheckInputFilenameExists(CacheConstants.TranscriptPath(_inputCachePrefix), "transcript cache", "--cache") + .CheckInputFilenameExists(CacheConstants.SiftPath(_inputCachePrefix), "SIFT cache", "--cache") + .CheckInputFilenameExists(CacheConstants.PolyPhenPath(_inputCachePrefix), "PolyPhen cache", "--cache") + .CheckEachDirectoryContainsFiles(SupplementaryAnnotationDirectories, "supplementary annotation", "--sd", "*.nsa") + .HasRequiredParameter(_outputFileName, "output file stub", "--out") + .Enable(_outputFileName == "-", () => { - ConfigurationSettings.Vcf = false; - ConfigurationSettings.Gvcf = false; + _vcf = false; + _gvcf = false; }) - .DisableOutput(ConfigurationSettings.OutputFileName == "-") + .DisableOutput(_outputFileName == "-") .ShowBanner(Constants.Authors) .ShowHelpMenu("Annotates a set of variants", "-i -c --sd -r -o ") .ShowErrors() diff --git a/Nirvana/PluginUtilities.cs b/Nirvana/PluginUtilities.cs index 2a5ea1fd..e71b23da 100644 --- a/Nirvana/PluginUtilities.cs +++ b/Nirvana/PluginUtilities.cs @@ -1,6 +1,4 @@ -using System.Collections; -using System.Collections.Generic; -using System.Composition; +using System.Collections.Generic; using System.Composition.Hosting; using System.IO; using System.Linq; @@ -12,7 +10,7 @@ namespace Nirvana { public static class PluginUtilities { - public static IEnumerable LoadPlugins(string pluginDirectory) + public static IPlugin[] LoadPlugins(string pluginDirectory) { IEnumerable plugins; var executableLocation = Assembly.GetEntryAssembly().Location; @@ -24,14 +22,15 @@ public static IEnumerable LoadPlugins(string pluginDirectory) .GetFiles(path, "*.dll", SearchOption.AllDirectories) .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath) .ToList(); - var configuration = new ContainerConfiguration() - .WithAssemblies(assemblies); + + var configuration = new ContainerConfiguration().WithAssemblies(assemblies); + using (var container = configuration.CreateContainer()) { plugins = container.GetExports(); } - return plugins; + return plugins.ToArray(); } } } \ No newline at end of file diff --git a/Nirvana/ProviderUtilities.cs b/Nirvana/ProviderUtilities.cs index c84984ac..802da68a 100644 --- a/Nirvana/ProviderUtilities.cs +++ b/Nirvana/ProviderUtilities.cs @@ -12,49 +12,48 @@ namespace Nirvana { - public static class ProviderUtilities - { - public static IAnnotator GetAnnotator(IAnnotationProvider taProvider, ISequenceProvider sequenceProvider, IAnnotationProvider saProviders, IAnnotationProvider conservationProvider,IGeneAnnotationProvider geneAnnotationProviders,IEnumerable plugins =null) - { - return new Annotator(taProvider, sequenceProvider, saProviders, conservationProvider,geneAnnotationProviders,plugins); - } - - public static ISequenceProvider GetSequenceProvider(string compressedReferencePath) - { - return new ReferenceSequenceProvider(FileUtilities.GetReadStream(compressedReferencePath)); - } - - public static IAnnotationProvider GetConservationProvider(IEnumerable dirPaths) - { - if (dirPaths==null) return null; - return dirPaths.All(x => Directory.GetFiles(x, "*.npd").Length == 0) ? null : new ConservationScoreProvider(dirPaths); - } - - public static IAnnotationProvider GetSaProvider(List supplementaryAnnotationDirectories) - { - if (supplementaryAnnotationDirectories == null || supplementaryAnnotationDirectories.Count == 0) - return null; - return new SupplementaryAnnotationProvider(supplementaryAnnotationDirectories); - } - - public static IAnnotationProvider GetTranscriptAnnotationProvider(string path, ISequenceProvider sequenceProvider) - { - return new TranscriptAnnotationProvider(path, sequenceProvider); - } - - public static IRefMinorProvider GetRefMinorProvider(List supplementaryAnnotationDirectories) - { - return supplementaryAnnotationDirectories==null || supplementaryAnnotationDirectories.Count==0? null: new RefMinorProvider(supplementaryAnnotationDirectories); - } - - public static IGeneAnnotationProvider GetGeneAnnotationProviders(List supplementaryAnnotationDirectories) - { - - var reader = SaReaderUtils.GetGeneAnnotationDatabaseReader(supplementaryAnnotationDirectories); - if (reader == null) return null; + public static class ProviderUtilities + { + public static IAnnotator GetAnnotator(IAnnotationProvider taProvider, ISequenceProvider sequenceProvider, IAnnotationProvider saProviders, IAnnotationProvider conservationProvider, IGeneAnnotationProvider geneAnnotationProviders, IEnumerable plugins = null) + { + return new Annotator(taProvider, sequenceProvider, saProviders, conservationProvider, geneAnnotationProviders, plugins); + } + + public static ISequenceProvider GetSequenceProvider(string compressedReferencePath) + { + return new ReferenceSequenceProvider(FileUtilities.GetReadStream(compressedReferencePath)); + } + + public static IAnnotationProvider GetConservationProvider(IEnumerable dirPaths) + { + if (dirPaths == null) return null; + dirPaths = dirPaths.ToList(); + return dirPaths.All(x => Directory.GetFiles(x, "*.npd").Length == 0) ? null : new ConservationScoreProvider(dirPaths); + } + + public static IAnnotationProvider GetSaProvider(List supplementaryAnnotationDirectories) + { + if (supplementaryAnnotationDirectories == null || supplementaryAnnotationDirectories.Count == 0) + return null; + return new SupplementaryAnnotationProvider(supplementaryAnnotationDirectories); + } + + public static IAnnotationProvider GetTranscriptAnnotationProvider(string path, ISequenceProvider sequenceProvider) + { + return new TranscriptAnnotationProvider(path, sequenceProvider); + } + + public static IRefMinorProvider GetRefMinorProvider(List supplementaryAnnotationDirectories) + { + return supplementaryAnnotationDirectories == null || supplementaryAnnotationDirectories.Count == 0 ? null : new RefMinorProvider(supplementaryAnnotationDirectories); + } + + public static IGeneAnnotationProvider GetGeneAnnotationProvider(IEnumerable supplementaryAnnotationDirectories) + { + var reader = SaReaderUtils.GetGeneAnnotationDatabaseReader(supplementaryAnnotationDirectories); + if (reader == null) return null; var geneAnnotationProvider = new GeneAnnotationProvider(reader); - return geneAnnotationProvider; - - } - } + return geneAnnotationProvider; + } + } } \ No newline at end of file diff --git a/Nirvana/ReadWriteUtilities.cs b/Nirvana/ReadWriteUtilities.cs index c4efdb00..29a0edb9 100644 --- a/Nirvana/ReadWriteUtilities.cs +++ b/Nirvana/ReadWriteUtilities.cs @@ -14,14 +14,15 @@ public static class ReadWriteUtilities { public static StreamWriter GetOutputWriter(string outputPath) { - return ConfigurationSettings.OutputFileName == "-" + return outputPath == "-" ? new StreamWriter(Console.OpenStandardOutput()) : new BgzipTextWriter(outputPath + ".json.gz"); } - internal static IVcfReader GetVcfReader(string vcfPath, IDictionary chromosomeDictionary, IRefMinorProvider refMinorProvider, bool verboseTranscript) + internal static IVcfReader GetVcfReader(string vcfPath, IDictionary chromosomeDictionary, + IRefMinorProvider refMinorProvider, bool verboseTranscript) { - var useStdInput = ConfigurationSettings.VcfPath == "-"; + var useStdInput = vcfPath == "-"; var peekStream = new PeekStream(useStdInput @@ -33,14 +34,14 @@ internal static IVcfReader GetVcfReader(string vcfPath, IDictionary + ae.Handle(x => { Console.WriteLine(x); return true; @@ -126,7 +126,7 @@ public void CreateTsvs() private void CreateMitoMapSvTsv(List mitoMapSvFileNames) { - if (mitoMapSvFileNames.Count == 0 || mitoMapSvFileNames.Any(String.IsNullOrEmpty)) return; + if (mitoMapSvFileNames.Count == 0 || mitoMapSvFileNames.Any(string.IsNullOrEmpty)) return; var benchMark = new Benchmark(); var rootDirectory = new FileInfo(mitoMapSvFileNames[0]).Directory; var version = DataSourceVersionReader.GetSourceVersion(Path.Combine(rootDirectory.ToString(), "mitoMapSV")); @@ -151,7 +151,7 @@ private void CreateMitoMapSvTsv(List mitoMapSvFileNames) private void CreateMitoMapVarTsv(List mitoMapFileNames) { - if (mitoMapFileNames.Count == 0 || mitoMapFileNames.Any(String.IsNullOrEmpty)) return; + if (mitoMapFileNames.Count == 0 || mitoMapFileNames.Any(string.IsNullOrEmpty)) return; var benchMark = new Benchmark(); var rootDirectory = new FileInfo(mitoMapFileNames[0]).Directory; var version = DataSourceVersionReader.GetSourceVersion(Path.Combine(rootDirectory.ToString(), "mitoMapVar")); diff --git a/SAUtils/CreateIntermediateTsvs/CreateIntermediateTsvsMain.cs b/SAUtils/CreateIntermediateTsvs/CreateIntermediateTsvsMain.cs index 3c9e6a52..763fc06f 100644 --- a/SAUtils/CreateIntermediateTsvs/CreateIntermediateTsvsMain.cs +++ b/SAUtils/CreateIntermediateTsvs/CreateIntermediateTsvsMain.cs @@ -5,114 +5,111 @@ namespace SAUtils.CreateIntermediateTsvs { - public sealed class CreateIntermediateTsvsMain - { - private ExitCodes ProgramExecution() - { - // load the reference sequence - - var interimTsvCreator = + public sealed class CreateIntermediateTsvsMain + { + private ExitCodes ProgramExecution() + { + var interimTsvCreator = new CreateIntermediateTsvs( - ConfigurationSettings.CompressedReference, - ConfigurationSettings.OutputSupplementaryDirectory, - ConfigurationSettings.InputDbSnpFileName, - ConfigurationSettings.InputCosmicVcfFileName, - ConfigurationSettings.InputCosmicTsvFileName, - ConfigurationSettings.InputClinVarFileName, - ConfigurationSettings.Input1000GFileName, - ConfigurationSettings.InputEvsFile, - ConfigurationSettings.InputExacFile, - ConfigurationSettings.InputDgvFile, - ConfigurationSettings.Input1000GSvFileName, - ConfigurationSettings.InputClinGenFileName, + ConfigurationSettings.CompressedReference, + ConfigurationSettings.OutputSupplementaryDirectory, + ConfigurationSettings.InputDbSnpFileName, + ConfigurationSettings.InputCosmicVcfFileName, + ConfigurationSettings.InputCosmicTsvFileName, + ConfigurationSettings.InputClinVarFileName, + ConfigurationSettings.Input1000GFileName, + ConfigurationSettings.InputEvsFile, + ConfigurationSettings.InputExacFile, + ConfigurationSettings.InputDgvFile, + ConfigurationSettings.Input1000GSvFileName, + ConfigurationSettings.InputClinGenFileName, ConfigurationSettings.InputMitoMapVarFileNames, ConfigurationSettings.InputMitoMapSvFileNames, - ConfigurationSettings.CustomAnnotationFiles, - ConfigurationSettings.CustomIntervalFiles - ); + ConfigurationSettings.CustomAnnotationFiles, + ConfigurationSettings.CustomIntervalFiles + ); - interimTsvCreator.CreateTsvs(); + interimTsvCreator.CreateTsvs(); return ExitCodes.Success; - } - + } - public static ExitCodes Run(string command, string[] commandArgs) - { + public static ExitCodes Run(string command, string[] commandArgs) + { var creator = new CreateIntermediateTsvsMain(); - var ops = new OptionSet - { - { - "ref|r=", - "compressed reference sequence file", - v => ConfigurationSettings.CompressedReference = v - }, - { - "dbs|d=", - "input dbSNP vcf.gz file", - v => ConfigurationSettings.InputDbSnpFileName = v - }, - { - "csm|c=", - "input COSMIC vcf file", - v => ConfigurationSettings.InputCosmicVcfFileName = v - }, - { - "tsv=", - "input COSMIC TSV file", - v => ConfigurationSettings.InputCosmicTsvFileName = v - }, - { - "cvr|V=", - "input ClinVar file", - v => ConfigurationSettings.InputClinVarFileName= v - }, - { - "vcf=", - "input ClinVar no known medical importance file", - v => ConfigurationSettings.InputClinvarXml= v - }, - { - "onek|k=", - "input 1000 Genomes AF file", - v => ConfigurationSettings.Input1000GFileName= v - }, - { - "evs|e=", - "input EVS file", - v => ConfigurationSettings.InputEvsFile= v - }, - { - "exac|x=", - "input ExAc file", - v => ConfigurationSettings.InputExacFile= v - }, - { - "dgv|g=", - "input Dgv file", - v => ConfigurationSettings.InputDgvFile= v - }, - { - "cust|t=", - "input Custom annotation file", - v => ConfigurationSettings.CustomAnnotationFiles.Add(v) - }, - { - "bed=", - "input Custom interval file", - v => ConfigurationSettings.CustomIntervalFiles.Add(v) - }, - { - "onekSv|s=", - "input 1000 Genomes Structural file", - v => ConfigurationSettings.Input1000GSvFileName = v - }, + var ops = new OptionSet + { + { + "ref|r=", + "compressed reference sequence file", + v => ConfigurationSettings.CompressedReference = v + }, + { + "dbs|d=", + "input dbSNP vcf.gz file", + v => ConfigurationSettings.InputDbSnpFileName = v + }, + { + "csm|c=", + "input COSMIC vcf file", + v => ConfigurationSettings.InputCosmicVcfFileName = v + }, + { + "tsv=", + "input COSMIC TSV file", + v => ConfigurationSettings.InputCosmicTsvFileName = v + }, + { + "cvr|V=", + "input ClinVar file", + v => ConfigurationSettings.InputClinVarFileName= v + }, + { + "vcf=", + "input ClinVar no known medical importance file", + v => ConfigurationSettings.InputClinvarXml= v + }, + { + "onek|k=", + "input 1000 Genomes AF file", + v => ConfigurationSettings.Input1000GFileName= v + }, + { + "evs|e=", + "input EVS file", + v => ConfigurationSettings.InputEvsFile= v + }, + { + "exac|x=", + "input ExAc file", + v => ConfigurationSettings.InputExacFile= v + }, + { + "dgv|g=", + "input Dgv file", + v => ConfigurationSettings.InputDgvFile= v + }, + { + "cust|t=", + "input Custom annotation file", + v => ConfigurationSettings.CustomAnnotationFiles.Add(v) + }, + { + "bed=", + "input Custom interval file", + v => ConfigurationSettings.CustomIntervalFiles.Add(v) + }, + { + "onekSv|s=", + "input 1000 Genomes Structural file", + v => ConfigurationSettings.Input1000GSvFileName = v + }, + { + "clinGen|l=", + "input ClinGen file", + v => ConfigurationSettings.InputClinGenFileName = v + }, { - "clinGen|l=", - "input ClinGen file", - v => ConfigurationSettings.InputClinGenFileName = v - }, - { "mitoVar=", "input MitoMAP variant HTML file", v => ConfigurationSettings.InputMitoMapVarFileNames.Add(v) @@ -123,39 +120,39 @@ public static ExitCodes Run(string command, string[] commandArgs) v => ConfigurationSettings.InputMitoMapSvFileNames.Add(v) }, { - "out|o=", - "output Nirvana Supplementary directory", - v => ConfigurationSettings.OutputSupplementaryDirectory = v - } - }; + "out|o=", + "output Nirvana Supplementary directory", + v => ConfigurationSettings.OutputSupplementaryDirectory = v + } + }; - var commandLineExample = $"{command} [options]"; + var commandLineExample = $"{command} [options]"; - var exitCode = new ConsoleAppBuilder(commandArgs,ops) + var exitCode = new ConsoleAppBuilder(commandArgs, ops) .Parse() - .CheckInputFilenameExists(ConfigurationSettings.CompressedReference, "Compressed reference sequence file name", "--ref") - .HasRequiredParameter(ConfigurationSettings.OutputSupplementaryDirectory, "output Supplementary directory", "--out") - .CheckInputFilenameExists(ConfigurationSettings.InputDbSnpFileName, "input VCF file containing dbSNP scores", "--dbs", false) - .CheckInputFilenameExists(ConfigurationSettings.InputCosmicVcfFileName, "input unified COSMIC file", "--csm", false) - .CheckInputFilenameExists(ConfigurationSettings.InputCosmicTsvFileName, "input cosmic tsv file", "--tsv", false) - .CheckInputFilenameExists(ConfigurationSettings.InputClinVarFileName, "input ClinVar xml file", "--cvr", false) - .CheckInputFilenameExists(ConfigurationSettings.InputClinvarXml, "no known medical importance vcf file", "--cvr", false) - .CheckInputFilenameExists(ConfigurationSettings.Input1000GFileName, "input 1000 Genomes AF file", "--onek", false) - .CheckInputFilenameExists(ConfigurationSettings.InputEvsFile, "input EVS file", "--evs", false) - .CheckInputFilenameExists(ConfigurationSettings.InputExacFile, "input Exac file", "--exac", false) - .CheckInputFilenameExists(ConfigurationSettings.InputDgvFile, "input DGV file", "--dgv", false) - .CheckInputFilenameExists(ConfigurationSettings.Input1000GSvFileName, "input DGV file", "--onekSv", false) + .CheckInputFilenameExists(ConfigurationSettings.CompressedReference, "Compressed reference sequence file name", "--ref") + .HasRequiredParameter(ConfigurationSettings.OutputSupplementaryDirectory, "output Supplementary directory", "--out") + .CheckInputFilenameExists(ConfigurationSettings.InputDbSnpFileName, "input VCF file containing dbSNP scores", "--dbs", false) + .CheckInputFilenameExists(ConfigurationSettings.InputCosmicVcfFileName, "input unified COSMIC file", "--csm", false) + .CheckInputFilenameExists(ConfigurationSettings.InputCosmicTsvFileName, "input cosmic tsv file", "--tsv", false) + .CheckInputFilenameExists(ConfigurationSettings.InputClinVarFileName, "input ClinVar xml file", "--cvr", false) + .CheckInputFilenameExists(ConfigurationSettings.InputClinvarXml, "no known medical importance vcf file", "--cvr", false) + .CheckInputFilenameExists(ConfigurationSettings.Input1000GFileName, "input 1000 Genomes AF file", "--onek", false) + .CheckInputFilenameExists(ConfigurationSettings.InputEvsFile, "input EVS file", "--evs", false) + .CheckInputFilenameExists(ConfigurationSettings.InputExacFile, "input Exac file", "--exac", false) + .CheckInputFilenameExists(ConfigurationSettings.InputDgvFile, "input DGV file", "--dgv", false) + .CheckInputFilenameExists(ConfigurationSettings.Input1000GSvFileName, "input DGV file", "--onekSv", false) .CheckEachFilenameExists(ConfigurationSettings.InputMitoMapVarFileNames, "input MitoMap variant file names", "--mitoVar", false) .CheckEachFilenameExists(ConfigurationSettings.InputMitoMapSvFileNames, "input MitoMap SV file names", "--mitoSv", false) - .CheckEachFilenameExists(ConfigurationSettings.CustomAnnotationFiles, "Custom Annotation file name", "--cust", false) - .CheckEachFilenameExists(ConfigurationSettings.CustomIntervalFiles, "Custom interval file name", "--bed", false) - .CheckNonZero(ConfigurationSettings.NumberOfProvidedInputFiles(), "supplementary data source") - .ShowBanner(Constants.Authors) - .ShowHelpMenu("Reads provided supplementary data files and populates tsv files",commandLineExample) - .ShowErrors() - .Execute(creator.ProgramExecution); - - return exitCode; + .CheckEachFilenameExists(ConfigurationSettings.CustomAnnotationFiles, "Custom Annotation file name", "--cust", false) + .CheckEachFilenameExists(ConfigurationSettings.CustomIntervalFiles, "Custom interval file name", "--bed", false) + .CheckNonZero(ConfigurationSettings.NumberOfProvidedInputFiles(), "supplementary data source") + .ShowBanner(Constants.Authors) + .ShowHelpMenu("Reads provided supplementary data files and populates tsv files", commandLineExample) + .ShowErrors() + .Execute(creator.ProgramExecution); + + return exitCode; } } } diff --git a/SAUtils/CreateOmimTsv/HgncReader.cs b/SAUtils/CreateOmimTsv/HgncReader.cs index 84123f37..610d176e 100644 --- a/SAUtils/CreateOmimTsv/HgncReader.cs +++ b/SAUtils/CreateOmimTsv/HgncReader.cs @@ -1,91 +1,91 @@ -using System; -using System.IO; -using System.Linq; -using ErrorHandling.Exceptions; +using System; +using System.IO; +using System.Linq; +using ErrorHandling.Exceptions; using Compression.Utilities; -namespace SAUtils.CreateOmimTsv -{ - public sealed class HgncReader : IDisposable - { - #region members - - private readonly StreamReader _reader; - - private const int SymbolIndex = 1; - private const int PreviousSymbolIndex = 10; - - #endregion - - #region IDisposable - - public void Dispose() - { - Dispose(true); - } - - private void Dispose(bool disposing) - { - if (disposing) - { - _reader.Dispose(); - } - } - - #endregion - - /// - /// constructor - /// - public HgncReader(string filePath) - { - // sanity check - if (!File.Exists(filePath)) - { - throw new FileNotFoundException($"The specified gene_info file ({filePath}) does not exist."); - } - - // open the file and parse the header - _reader = GZipUtilities.GetAppropriateStreamReader(filePath); - var line = _reader.ReadLine(); - var cols = line.Split('\t'); - - const string symbolHeader = "symbol"; - if (cols[SymbolIndex] != symbolHeader) throw new InvalidFileFormatException($"Expected column index {SymbolIndex} to contain {symbolHeader}, but found {cols[SymbolIndex]}"); - - const string prevSymbolHeader = "prev_symbol"; - if (cols[PreviousSymbolIndex] != prevSymbolHeader) throw new InvalidFileFormatException($"Expected column index {PreviousSymbolIndex} to contain {prevSymbolHeader}, but found {cols[PreviousSymbolIndex]}"); - } - - /// - /// retrieves the next gene. Returns false if there are no more genes available - /// - public GeneSymbolSynonyms Next() - { - // get the next line - string line = _reader.ReadLine(); - if (line == null) return null; - - var cols = line.Split('\t'); - if (cols.Length < 11) throw new UserErrorException($"Expected more than 11 columns but found only {cols.Length} when parsing the gene entry."); - - try - { - var geneSymbol = cols[SymbolIndex]; - var synonyms = cols[PreviousSymbolIndex].Split('|').ToList(); - - return new GeneSymbolSynonyms - { - GeneSymbol = geneSymbol, - Synonyms = synonyms, - }; - } - catch (Exception) - { - Console.WriteLine("Offending line: {0}", line); - for (int i = 0; i < cols.Length; i++) Console.WriteLine("- col {0}: [{1}]", i, cols[i]); - throw; - } - } - } -} +namespace SAUtils.CreateOmimTsv +{ + public sealed class HgncReader : IDisposable + { + #region members + + private readonly StreamReader _reader; + + private const int SymbolIndex = 1; + private const int PreviousSymbolIndex = 10; + + #endregion + + #region IDisposable + + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _reader.Dispose(); + } + } + + #endregion + + /// + /// constructor + /// + public HgncReader(string filePath) + { + // sanity check + if (!File.Exists(filePath)) + { + throw new FileNotFoundException($"The specified gene_info file ({filePath}) does not exist."); + } + + // open the file and parse the header + _reader = GZipUtilities.GetAppropriateStreamReader(filePath); + var line = _reader.ReadLine(); + var cols = line.Split('\t'); + + const string symbolHeader = "symbol"; + if (cols[SymbolIndex] != symbolHeader) throw new InvalidFileFormatException($"Expected column index {SymbolIndex} to contain {symbolHeader}, but found {cols[SymbolIndex]}"); + + const string prevSymbolHeader = "prev_symbol"; + if (cols[PreviousSymbolIndex] != prevSymbolHeader) throw new InvalidFileFormatException($"Expected column index {PreviousSymbolIndex} to contain {prevSymbolHeader}, but found {cols[PreviousSymbolIndex]}"); + } + + /// + /// retrieves the next gene. Returns false if there are no more genes available + /// + public GeneSymbolSynonyms Next() + { + // get the next line + string line = _reader.ReadLine(); + if (line == null) return null; + + var cols = line.Split('\t'); + if (cols.Length < 11) throw new UserErrorException($"Expected more than 11 columns but found only {cols.Length} when parsing the gene entry."); + + try + { + var geneSymbol = cols[SymbolIndex]; + var synonyms = cols[PreviousSymbolIndex].Split('|').ToList(); + + return new GeneSymbolSynonyms + { + GeneSymbol = geneSymbol, + Synonyms = synonyms + }; + } + catch (Exception) + { + Console.WriteLine("Offending line: {0}", line); + for (int i = 0; i < cols.Length; i++) Console.WriteLine("- col {0}: [{1}]", i, cols[i]); + throw; + } + } + } +} diff --git a/SAUtils/DataStructures/ClinVarItem.cs b/SAUtils/DataStructures/ClinVarItem.cs index d090645b..dba678e8 100644 --- a/SAUtils/DataStructures/ClinVarItem.cs +++ b/SAUtils/DataStructures/ClinVarItem.cs @@ -55,7 +55,7 @@ public sealed class ClinVarItem : SupplementaryDataItem [ReviewStatusEnum.practice_guideline] = "practice guideline", [ReviewStatusEnum.multiple_submitters] = "classified by multiple submitters", [ReviewStatusEnum.conflicting_interpretations] = "criteria provided, conflicting interpretations", - [ReviewStatusEnum.multiple_submitters_no_conflict] = "criteria provided, multiple submitters, no conflicts", + [ReviewStatusEnum.multiple_submitters_no_conflict] = "criteria provided, multiple submitters, no conflicts" }; diff --git a/SAUtils/DataStructures/CosmicItem.cs b/SAUtils/DataStructures/CosmicItem.cs index 527072a1..be6ad1e5 100644 --- a/SAUtils/DataStructures/CosmicItem.cs +++ b/SAUtils/DataStructures/CosmicItem.cs @@ -103,7 +103,7 @@ public bool Equals(CosmicItem otherItem) if (otherItem == null) return false; // Return true if the fields match: - return string.Equals(Chromosome, otherItem.Chromosome) && + return Equals(Chromosome, otherItem.Chromosome) && Start == otherItem.Start && string.Equals(Id, otherItem.Id) && string.Equals(ReferenceAllele, otherItem.ReferenceAllele) && diff --git a/SAUtils/DataStructures/DbSnpItem.cs b/SAUtils/DataStructures/DbSnpItem.cs index cb3d9eca..a595c249 100644 --- a/SAUtils/DataStructures/DbSnpItem.cs +++ b/SAUtils/DataStructures/DbSnpItem.cs @@ -41,7 +41,7 @@ public override bool Equals(object other) if (otherItem == null) return false; // Return true if the fields match: - return string.Equals(Chromosome, otherItem.Chromosome) + return Equals(Chromosome, otherItem.Chromosome) && Start == otherItem.Start && RsId == otherItem.RsId && string.Equals(ReferenceAllele, otherItem.ReferenceAllele) diff --git a/SAUtils/DataStructures/DgvItem.cs b/SAUtils/DataStructures/DgvItem.cs index cf75b5f4..f47148d6 100644 --- a/SAUtils/DataStructures/DgvItem.cs +++ b/SAUtils/DataStructures/DgvItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using VariantAnnotation.Interface.Positions; using VariantAnnotation.Interface.Sequence; @@ -73,15 +72,15 @@ public override bool Equals(object other) if (otherItem == null) return false; // Return true if the fields match: - return string.Equals(Chromosome, otherItem.Chromosome) + return Equals(Chromosome, otherItem.Chromosome) && Start == otherItem.Start && End == otherItem.End && ObservedGains == otherItem.ObservedGains && SampleSize == otherItem.SampleSize && ObservedLosses == otherItem.ObservedLosses && string.Equals(Id, otherItem.Id) - && Object.Equals(VariantType, otherItem.VariantType) - && Object.Equals(VariantFreqAll, otherItem.VariantFreqAll); + && Equals(VariantType, otherItem.VariantType) + && Equals(VariantFreqAll, otherItem.VariantFreqAll); } public override int GetHashCode() diff --git a/SAUtils/DataStructures/EvsItem.cs b/SAUtils/DataStructures/EvsItem.cs index 36252b5c..ba2046a0 100644 --- a/SAUtils/DataStructures/EvsItem.cs +++ b/SAUtils/DataStructures/EvsItem.cs @@ -61,7 +61,7 @@ public override bool Equals(object other) if (otherItem == null) return false; // Return true if the fields match: - return string.Equals(Chromosome, otherItem.Chromosome) + return Equals(Chromosome, otherItem.Chromosome) && Start == otherItem.Start && AlternateAllele.Equals(otherItem.AlternateAllele) ; diff --git a/SAUtils/DataStructures/ExacItem.cs b/SAUtils/DataStructures/ExacItem.cs index 084131a0..0f887872 100644 --- a/SAUtils/DataStructures/ExacItem.cs +++ b/SAUtils/DataStructures/ExacItem.cs @@ -131,7 +131,7 @@ public override bool Equals(object other) if (!(other is ExacItem otherItem)) return false; // Return true if the fields match: - return string.Equals(Chromosome, otherItem.Chromosome) + return Equals(Chromosome, otherItem.Chromosome) && Start == otherItem.Start && AlternateAllele.Equals(otherItem.AlternateAllele) ; diff --git a/SAUtils/DataStructures/GnomadItem.cs b/SAUtils/DataStructures/GnomadItem.cs index 0fab368f..972122e4 100644 --- a/SAUtils/DataStructures/GnomadItem.cs +++ b/SAUtils/DataStructures/GnomadItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Text; +using System.Text; using VariantAnnotation.Interface.Sequence; using VariantAnnotation.IO; // ReSharper disable NonReadonlyMemberInGetHashCode diff --git a/SAUtils/DataStructures/OneKGenItem.cs b/SAUtils/DataStructures/OneKGenItem.cs index 0cd2018e..590d6c68 100644 --- a/SAUtils/DataStructures/OneKGenItem.cs +++ b/SAUtils/DataStructures/OneKGenItem.cs @@ -181,7 +181,7 @@ public override bool Equals(object other) if (otherItem == null) return false; // Return true if the fields match: - return string.Equals(Chromosome, otherItem.Chromosome) + return Equals(Chromosome, otherItem.Chromosome) && Start == otherItem.Start && AlternateAllele.Equals(otherItem.AlternateAllele) ; diff --git a/SAUtils/DataStructures/SaHeader.cs b/SAUtils/DataStructures/SaHeader.cs index 03195d69..c1419905 100644 --- a/SAUtils/DataStructures/SaHeader.cs +++ b/SAUtils/DataStructures/SaHeader.cs @@ -16,7 +16,7 @@ public class SaHeader public SaHeader(string name, string assembly, string version, string releaseDate, string description) { Name = name; - GenomeAssembly = GenomeAssemblyUtilities.Convert(assembly); + GenomeAssembly = GenomeAssemblyHelper.Convert(assembly); Version = version; Description = description; ReleaseDate = releaseDate; diff --git a/SAUtils/DegenerateBaseUtilities.cs b/SAUtils/DegenerateBaseUtilities.cs index ba979a3d..e058562b 100644 --- a/SAUtils/DegenerateBaseUtilities.cs +++ b/SAUtils/DegenerateBaseUtilities.cs @@ -16,10 +16,10 @@ public class DegenerateBaseUtilities {'S', new List{'C','G'}}, {'V', new List{'A','C','G'}}, {'W', new List{'A','T'}}, - {'Y', new List{'C','T'}}, + {'Y', new List{'C','T'}} }; - public static readonly HashSet BasicBases = new HashSet(){'A','C','G','T','N'}; + public static readonly HashSet BasicBases = new HashSet {'A','C','G','T','N'}; public static bool HasDegenerateBase(string sequence) => sequence.ToUpper().Any(x => DegenerateBaseNotation.ContainsKey(x)) && @@ -44,6 +44,6 @@ private static void GetSequences(string inputSequence, List outputSequen } - private static List MapBase(char inputBase) => DegenerateBaseNotation.ContainsKey(inputBase) ? DegenerateBaseNotation[inputBase] : new List() {inputBase}; + private static List MapBase(char inputBase) => DegenerateBaseNotation.ContainsKey(inputBase) ? DegenerateBaseNotation[inputBase] : new List {inputBase}; } } \ No newline at end of file diff --git a/SAUtils/ExtractMiniSa/MiniSaExtractor.cs b/SAUtils/ExtractMiniSa/MiniSaExtractor.cs index 2cb6dc8c..75af84f0 100644 --- a/SAUtils/ExtractMiniSa/MiniSaExtractor.cs +++ b/SAUtils/ExtractMiniSa/MiniSaExtractor.cs @@ -33,7 +33,7 @@ public MiniSaExtractor(string compressedRefFile, string saPath, int begin, int e _end = end; _saPath = saPath; - var refChromDict = new ReferenceSequenceProvider(FileUtilities.GetReadStream(compressedRefFile)).GetChromosomeDictionary(); + var refChromDict = new ReferenceSequenceProvider(FileUtilities.GetReadStream(compressedRefFile)).RefNameToChromosome; var referenceName = GetReferenceName(saPath, refChromDict); _miniSaPath = GetMiniSaPath(referenceName, begin, end, datasourceName, outputDir); diff --git a/SAUtils/ExtractMiniXml/ExtractMiniXmlMain.cs b/SAUtils/ExtractMiniXml/ExtractMiniXmlMain.cs index 8b856af5..4ad8d8e6 100644 --- a/SAUtils/ExtractMiniXml/ExtractMiniXmlMain.cs +++ b/SAUtils/ExtractMiniXml/ExtractMiniXmlMain.cs @@ -1,55 +1,53 @@ -using CommandLine.Builders; -using CommandLine.NDesk.Options; -using ErrorHandling; -using SAUtils.ExtractMiniXml; -using VariantAnnotation.Interface; - -namespace ExtractMiniXml -{ - public sealed class ExtractMiniXmlMain - { - +using CommandLine.Builders; +using CommandLine.NDesk.Options; +using ErrorHandling; +using ExtractMiniXml; +using VariantAnnotation.Interface; + +namespace SAUtils.ExtractMiniXml +{ + public static class ExtractMiniXmlMain + { private static ExitCodes ProgramExecution() - { - var extractor = new XmlExtractor(ConfigurationSettings.InputXmlFile, ConfigurationSettings.RcvId, ConfigurationSettings.OutputDir); - extractor.Extract(); - + { + var extractor = new XmlExtractor(ConfigurationSettings.InputXmlFile, ConfigurationSettings.RcvId, ConfigurationSettings.OutputDir); + extractor.Extract(); + return ExitCodes.Success; } public static ExitCodes Run(string command, string[] commandArgs) - { - var ops = new OptionSet() - { - { - "i|in=", - "Input XML {file}", - v => ConfigurationSettings.InputXmlFile = v - }, - { - "r|rcv=", - "RCV ID", - v => ConfigurationSettings.RcvId = v - }, - { - "o|out=", - "Output {dir}", - v => ConfigurationSettings.OutputDir = v - } - }; - - var commandLineExample = $"{command} --in --out --rcv "; - - var exitCode = new ConsoleAppBuilder(commandArgs, ops) - .Parse() - .CheckInputFilenameExists(ConfigurationSettings.InputXmlFile, "input XML file", "--in") - .HasRequiredParameter(ConfigurationSettings.OutputDir, "output directory", "--out") - .ShowBanner(Constants.Authors) - .ShowHelpMenu("Extracts mini supplementary annotations for the given range from Nirvana Supplementary Annotations files.", commandLineExample) - .ShowErrors() - .Execute(ProgramExecution); - - return exitCode; - } - - } -} + { + var ops = new OptionSet + { + { + "i|in=", + "Input XML {file}", + v => ConfigurationSettings.InputXmlFile = v + }, + { + "r|rcv=", + "RCV ID", + v => ConfigurationSettings.RcvId = v + }, + { + "o|out=", + "Output {dir}", + v => ConfigurationSettings.OutputDir = v + } + }; + + var commandLineExample = $"{command} --in --out --rcv "; + + var exitCode = new ConsoleAppBuilder(commandArgs, ops) + .Parse() + .CheckInputFilenameExists(ConfigurationSettings.InputXmlFile, "input XML file", "--in") + .HasRequiredParameter(ConfigurationSettings.OutputDir, "output directory", "--out") + .ShowBanner(Constants.Authors) + .ShowHelpMenu("Extracts mini supplementary annotations for the given range from Nirvana Supplementary Annotations files.", commandLineExample) + .ShowErrors() + .Execute(ProgramExecution); + + return exitCode; + } + } +} diff --git a/SAUtils/GeneScoresTsv/GeneScoreTsvCreator.cs b/SAUtils/GeneScoresTsv/GeneScoreTsvCreator.cs index c7fb254a..f128fee2 100644 --- a/SAUtils/GeneScoresTsv/GeneScoreTsvCreator.cs +++ b/SAUtils/GeneScoresTsv/GeneScoreTsvCreator.cs @@ -94,7 +94,7 @@ private void WriteScores(string gene, double pLi, double pRec, double pNull) jsonObject.AddDoubleValue("pNull", pNull, "0.00e0"); sb.Append(JsonObject.CloseBrace); - _writer.AddEntry(gene, new List(){sb.ToString()}); + _writer.AddEntry(gene, new List {sb.ToString()}); } private (string gene, double pLi, double pRec, double pNull) GetGeneAndScores(string line) diff --git a/SAUtils/InputFileParsers/ClinVar/ClinVarXmlReader.cs b/SAUtils/InputFileParsers/ClinVar/ClinVarXmlReader.cs index 82984a06..6899448d 100644 --- a/SAUtils/InputFileParsers/ClinVar/ClinVarXmlReader.cs +++ b/SAUtils/InputFileParsers/ClinVar/ClinVarXmlReader.cs @@ -95,7 +95,7 @@ public ClinVarXmlReader(FileInfo clinVarXmlFileInfo, ISequenceProvider sequenceP _sequenceProvider = sequenceProvider; _aligner = new VariantAligner(_sequenceProvider.Sequence); _clinVarXmlFileInfo = clinVarXmlFileInfo; - _refChromDict = sequenceProvider.GetChromosomeDictionary(); + _refChromDict = sequenceProvider.RefNameToChromosome; } private const string ClinVarSetTag = "ClinVarSet"; diff --git a/SAUtils/InputFileParsers/ClinVar/VariantAligner.cs b/SAUtils/InputFileParsers/ClinVar/VariantAligner.cs index 51f8b3ab..244dd4a3 100644 --- a/SAUtils/InputFileParsers/ClinVar/VariantAligner.cs +++ b/SAUtils/InputFileParsers/ClinVar/VariantAligner.cs @@ -52,20 +52,17 @@ public VariantAligner(ISequence compressedSequence) var newRefAllele = combinedSeq.Substring(i + 1 - repeatLength, repeatLength); return (trimmedPos, newRefAllele, ""); //alt is empty for deletion } - else - { - //insertion - combinedSeq += trimmedAltAllele; - repeatLength = trimmedAltAllele.Length; - for (i = combinedSeq.Length - 1; i >= repeatLength; i--, trimmedPos--) - { - if (combinedSeq[i] != combinedSeq[i - repeatLength]) break; - } - var newAltAllele = combinedSeq.Substring(i + 1 - repeatLength, repeatLength); - return (trimmedPos, "", newAltAllele); - } + //insertion + combinedSeq += trimmedAltAllele; + repeatLength = trimmedAltAllele.Length; + for (i = combinedSeq.Length - 1; i >= repeatLength; i--, trimmedPos--) + { + if (combinedSeq[i] != combinedSeq[i - repeatLength]) break; + } + var newAltAllele = combinedSeq.Substring(i + 1 - repeatLength, repeatLength); + return (trimmedPos, "", newAltAllele); } private string GetUpstreamSeq(int position, int length, bool isCircularGenome) @@ -76,6 +73,7 @@ private string GetUpstreamSeq(int position, int length, bool isCircularGenome) var interval = (position - length, position -1); return circularGenome.ExtractIntervalSequence(interval); } + var adjustedLength = length < position ? length : position - 1; return _compressedSequence.Substring(position - 1 - adjustedLength, adjustedLength); } diff --git a/SAUtils/InputFileParsers/CustomInterval/CustomIntervalParser.cs b/SAUtils/InputFileParsers/CustomInterval/CustomIntervalParser.cs index 8d709647..54987cde 100644 --- a/SAUtils/InputFileParsers/CustomInterval/CustomIntervalParser.cs +++ b/SAUtils/InputFileParsers/CustomInterval/CustomIntervalParser.cs @@ -222,11 +222,7 @@ private void AddInfoField(string line) { throw new Exception("duplicate index:\n" + line); } - else - { - _fieldIndex[index.Value] = json; - } - + _fieldIndex[index.Value] = json; } } diff --git a/SAUtils/InputFileParsers/MitoMAP/MitoMapSvReader.cs b/SAUtils/InputFileParsers/MitoMAP/MitoMapSvReader.cs index 164e7b51..e502925b 100644 --- a/SAUtils/InputFileParsers/MitoMAP/MitoMapSvReader.cs +++ b/SAUtils/InputFileParsers/MitoMAP/MitoMapSvReader.cs @@ -20,7 +20,7 @@ public sealed class MitoMapSvReader private readonly VariantAligner _variantAligner; - private readonly HashSet _mitoMapSvDataTypes = new HashSet() + private readonly HashSet _mitoMapSvDataTypes = new HashSet { MitoMapDataTypes.MitoMapDeletionsSingle, MitoMapDataTypes.MitoMapInsertionsSimple diff --git a/SAUtils/InputFileParsers/MitoMAP/MitoMapVariantReader.cs b/SAUtils/InputFileParsers/MitoMAP/MitoMapVariantReader.cs index 71840645..017b3cef 100644 --- a/SAUtils/InputFileParsers/MitoMAP/MitoMapVariantReader.cs +++ b/SAUtils/InputFileParsers/MitoMAP/MitoMapVariantReader.cs @@ -44,7 +44,7 @@ public sealed class MitoMapVariantReader {"-", false} }; - private readonly HashSet _mitoMapDelSymbolSet = new HashSet() { ":", "del", "d" }; + private readonly HashSet _mitoMapDelSymbolSet = new HashSet { ":", "del", "d" }; public MitoMapVariantReader(FileInfo mitoMapFileInfo, ReferenceSequenceProvider sequenceProvider) @@ -219,7 +219,7 @@ private List ExtracVariantItem(List info, int[] fields) private bool DescribedAsDuplicatedRecord(string mitomapDiseaseString) { - if (String.IsNullOrEmpty(mitomapDiseaseString)) return false; + if (string.IsNullOrEmpty(mitomapDiseaseString)) return false; var altNotationPattern1 = new Regex("alternate notation$"); var altNotationMatch = altNotationPattern1.Match(mitomapDiseaseString); if (altNotationMatch.Success) @@ -234,7 +234,7 @@ private static string GetDiseaseInfo(List info, int fieldIndex) { if (fieldIndex == -1) return null; string diseaseString = info[fieldIndex]; - if (String.IsNullOrEmpty(diseaseString)) return diseaseString; + if (string.IsNullOrEmpty(diseaseString)) return diseaseString; var regexPattern = new Regex(@"(?.+)$"); var match = regexPattern.Match(diseaseString); return match.Success ? match.Groups["disease"].Value : diseaseString; @@ -332,7 +332,7 @@ private string GetClinicalSignificance(string significanceString) var altAlleleSequences = new List(); for (int i = minRepeat; i <= maxRepeat; i++) { - altAlleleSequences.Add(new String(altBase, i)); + altAlleleSequences.Add(new string(altBase, i)); } return (match7.Groups["ref"].Value, string.Join(";", altAlleleSequences), null); } diff --git a/SAUtils/InputFileParsers/OneKGen/OneKGenReader.cs b/SAUtils/InputFileParsers/OneKGen/OneKGenReader.cs index 9be0a1d6..b551f351 100644 --- a/SAUtils/InputFileParsers/OneKGen/OneKGenReader.cs +++ b/SAUtils/InputFileParsers/OneKGen/OneKGenReader.cs @@ -274,7 +274,7 @@ private static string GetAncestralAllele(string value) } private static bool IsNucleotide(char c) { - c = Char.ToUpper(c); + c = char.ToUpper(c); return c == 'A' || c == 'C' || c == 'G' || c == 'T' || c == 'N'; } diff --git a/SAUtils/MergeInterimTsvs/InterimTsvsMerger.cs b/SAUtils/MergeInterimTsvs/InterimTsvsMerger.cs index 20995519..1f29e809 100644 --- a/SAUtils/MergeInterimTsvs/InterimTsvsMerger.cs +++ b/SAUtils/MergeInterimTsvs/InterimTsvsMerger.cs @@ -26,20 +26,17 @@ public sealed class InterimTsvsMerger private readonly List _geneHeaders; private readonly string _outputDirectory; private readonly GenomeAssembly _genomeAssembly; - private readonly IDictionary _refChromDict; + private readonly IDictionary _refNameToChromosome; private readonly HashSet _refNames; public static readonly HashSet AssembliesIgnoredInConsistancyCheck = new HashSet() { GenomeAssembly.Unknown, GenomeAssembly.rCRS }; - /// - /// constructor - /// public InterimTsvsMerger(IEnumerable annotationFiles, IEnumerable intervalFiles, string miscFile, IEnumerable geneFiles, string compressedReference, string outputDirectory) { _outputDirectory = outputDirectory; var refSequenceProvider = new ReferenceSequenceProvider(FileUtilities.GetReadStream(compressedReference)); - _genomeAssembly = refSequenceProvider.GenomeAssembly; - _refChromDict = refSequenceProvider.GetChromosomeDictionary(); + _genomeAssembly = refSequenceProvider.GenomeAssembly; + _refNameToChromosome = refSequenceProvider.RefNameToChromosome; _tsvReaders = ReaderUtilities.GetSaTsvReaders(annotationFiles); _miscReader = ReaderUtilities.GetMiscTsvReader(miscFile); @@ -184,7 +181,7 @@ private void MergeChrom(string refName) //return; var globalMajorAlleleInRefMinors = GetGlobalMajorAlleleForRefMinors(refName); - var ucscRefName = _refChromDict[refName].UcscName; + var ucscRefName = _refNameToChromosome[refName].UcscName; var dataSourceVersions = MergeUtilities.GetDataSourceVersions(_saHeaders); var header = new SupplementaryAnnotationHeader(ucscRefName, DateTime.Now.Ticks, @@ -227,7 +224,5 @@ private static InterimSaPosition GetNextInterimPosition(List saHeaders) .ToList(); if (uniqueAssemblies.Count > 1) - throw new InvalidDataException($"ERROR: The genome assembly for all data sources should be the same. Found {String.Join(", ", uniqueAssemblies.ToArray())}"); + throw new InvalidDataException($"ERROR: The genome assembly for all data sources should be the same. Found {string.Join(", ", uniqueAssemblies.ToArray())}"); } diff --git a/SAUtils/SAUtils.cs b/SAUtils/SAUtils.cs index 9ffd3fe4..32d0f98f 100644 --- a/SAUtils/SAUtils.cs +++ b/SAUtils/SAUtils.cs @@ -1,39 +1,37 @@ using System.Collections.Generic; using CommandLine.Builders; -using ExtractMiniXml; using SAUtils.CreateGnomadTsv; using SAUtils.CreateIntermediateTsvs; using SAUtils.CreateOmimTsv; using SAUtils.ExtractMiniSa; +using SAUtils.ExtractMiniXml; using SAUtils.GeneScoresTsv; using SAUtils.MergeInterimTsvs; using VariantAnnotation.Interface; namespace SAUtils { - public static class SaUtils + public static class SaUtils { - public static int Main(string[] args) { var ops = new Dictionary { - ["createSA"] = new TopLevelOption("create Nirvana supplementary annotation database", MergeIntermediateTsvs.Run), - ["createTSV"] = new TopLevelOption("create intermediate tsv file for supplementary annotation", CreateIntermediateTsvsMain.Run), - ["createOmimTsv"] = new TopLevelOption("create omim tsv file", CreateOmimTsvMain.Run), - ["geneScoresTsv"] = new TopLevelOption("create gene scores tsv file", GeneScoresMain.Run), - ["extractMiniSA"] = new TopLevelOption("extracts mini SA", ExtractMiniSaMain.Run), - ["extractMiniXml"] = new TopLevelOption("extracts mini SA", ExtractMiniXmlMain.Run), + ["createSA"] = new TopLevelOption("create Nirvana supplementary annotation database", MergeIntermediateTsvs.Run), + ["createTSV"] = new TopLevelOption("create intermediate tsv file for supplementary annotation", CreateIntermediateTsvsMain.Run), + ["createOmimTsv"] = new TopLevelOption("create omim tsv file", CreateOmimTsvMain.Run), + ["geneScoresTsv"] = new TopLevelOption("create gene scores tsv file", GeneScoresMain.Run), + ["extractMiniSA"] = new TopLevelOption("extracts mini SA", ExtractMiniSaMain.Run), + ["extractMiniXml"] = new TopLevelOption("extracts mini SA", ExtractMiniXmlMain.Run), ["createGnomadTsv"] = new TopLevelOption("create gnomAD tsv file", CreateGnomadTsvMain.Run) }; - - var exitCode = new TopLevelAppBuilder(args,ops) + var exitCode = new TopLevelAppBuilder(args, ops) .Parse() .ShowBanner(Constants.Authors) .ShowHelpMenu("Utilities focused on supplementary annotation") .ShowErrors().Execute(); - return (int) exitCode; + return (int)exitCode; } } } diff --git a/SAUtils/SAUtils.csproj b/SAUtils/SAUtils.csproj index ea3bcb46..6175f1fb 100644 --- a/SAUtils/SAUtils.csproj +++ b/SAUtils/SAUtils.csproj @@ -12,8 +12,4 @@ - - - - \ No newline at end of file diff --git a/SAUtils/TsvWriterUtilities.cs b/SAUtils/TsvWriterUtilities.cs index 2466d767..89e07401 100644 --- a/SAUtils/TsvWriterUtilities.cs +++ b/SAUtils/TsvWriterUtilities.cs @@ -42,7 +42,7 @@ public static void WriteCompleteInfo(string dataSourceDescription, string versio public static void WriteSortedItems(IEnumerable saItems, ISaItemTsvWriter writer) { var itemsMinHeap = new MinHeap(); - var currentRefIndex = Int32.MaxValue; + var currentRefIndex = int.MaxValue; var benchmark = new Benchmark(); @@ -50,7 +50,7 @@ public static void WriteSortedItems(IEnumerable saItems, { if (currentRefIndex != saItem.Chromosome.Index) { - if (currentRefIndex != Int32.MaxValue) + if (currentRefIndex != int.MaxValue) { //flushing out the remaining items in buffer WriteToPosition(writer, itemsMinHeap, int.MaxValue); diff --git a/SAUtils/TsvWriters/GeneAnnotationTsvWriter.cs b/SAUtils/TsvWriters/GeneAnnotationTsvWriter.cs index 2d117347..ec01d785 100644 --- a/SAUtils/TsvWriters/GeneAnnotationTsvWriter.cs +++ b/SAUtils/TsvWriters/GeneAnnotationTsvWriter.cs @@ -43,7 +43,7 @@ private string GetHeader(DataSourceVersion dataSourceVersion, int dataVersion, s public void AddEntry(string geneSymbol, List jsonStrings) { if (jsonStrings == null || jsonStrings.Count == 0) return; - _writer.Write($"{geneSymbol}\t{String.Join("\t",jsonStrings)}\n"); + _writer.Write($"{geneSymbol}\t{string.Join("\t",jsonStrings)}\n"); } public void Dispose() diff --git a/SAUtils/TsvWriters/SaMiscTsvWriter.cs b/SAUtils/TsvWriters/SaMiscTsvWriter.cs index 4005bbcc..4af39bd0 100644 --- a/SAUtils/TsvWriters/SaMiscTsvWriter.cs +++ b/SAUtils/TsvWriters/SaMiscTsvWriter.cs @@ -104,10 +104,10 @@ private bool ValidateReference(string chromosome, int position, string refAllele { if (_sequenceProvider == null) return true; - var refDictionary = _sequenceProvider.GetChromosomeDictionary(); + var refDictionary = _sequenceProvider.RefNameToChromosome; if (!refDictionary.ContainsKey(chromosome)) return false; - var chrom = _sequenceProvider.GetChromosomeDictionary()[chromosome]; + var chrom = refDictionary[chromosome]; _sequenceProvider.LoadChromosome(chrom); var refSequence = _sequenceProvider.Sequence.Substring(position - 1, ReferenceWindow); diff --git a/SAUtils/TsvWriters/SaTsvWriter.cs b/SAUtils/TsvWriters/SaTsvWriter.cs index b366433a..3ea908a0 100644 --- a/SAUtils/TsvWriters/SaTsvWriter.cs +++ b/SAUtils/TsvWriters/SaTsvWriter.cs @@ -130,10 +130,10 @@ private bool ValidateReference(string chromosome, int position, string refAllele { if (_sequenceProvider == null) return true; - var refDictionary = _sequenceProvider.GetChromosomeDictionary(); + var refDictionary = _sequenceProvider.RefNameToChromosome; if (!refDictionary.ContainsKey(chromosome)) return false; - var chrom = _sequenceProvider.GetChromosomeDictionary()[chromosome]; + var chrom = refDictionary[chromosome]; _sequenceProvider.LoadChromosome(chrom); var refSequence = _sequenceProvider.Sequence.Substring(position - 1, ReferenceWindow); diff --git a/UnitTests/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensionsTests.cs b/UnitTests/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensionsTests.cs new file mode 100644 index 00000000..d410d898 --- /dev/null +++ b/UnitTests/CacheUtils/DataDumperImport/DataStructures/Import/ImportNodeExtensionsTests.cs @@ -0,0 +1,101 @@ +using System.IO; +using CacheUtils.DataDumperImport.DataStructures.Import; +using Xunit; + +namespace UnitTests.CacheUtils.DataDumperImport.DataStructures.Import +{ + public sealed class ImportNodeExtensionsTests + { + [Fact] + public void GetInt32_Nominal() + { + var node = new StringKeyValueNode("bob", "123"); + var observedResult = node.GetInt32(); + Assert.Equal(123, observedResult); + } + + [Fact] + public void GetInt32_ReturnMinusOne_WhenNull() + { + var node = new StringKeyValueNode("bob", null); + var observedResult = node.GetInt32(); + Assert.Equal(-1, observedResult); + } + + [Fact] + public void GetInt32_ThrowException_When_NotNumber() + { + var node = new StringKeyValueNode("bob", "123N"); + + Assert.Throws(delegate + { + // ReSharper disable once UnusedVariable + var observedResult = node.GetInt32(); + }); + } + + [Fact] + public void GetString_ThrowException_When_NotCorrectType() + { + var node = new ObjectKeyValueNode("bob", null); + + Assert.Throws(delegate + { + // ReSharper disable once UnusedVariable + var observedResult = node.GetString(); + }); + } + + [Fact] + public void GetString_ReturnNull_IfEmptyOrMinus() + { + var node = new StringKeyValueNode("bob", "-"); + var observedResult = node.GetString(); + Assert.Null(observedResult); + + node = new StringKeyValueNode("bob", ""); + observedResult = node.GetString(); + Assert.Null(observedResult); + } + + [Fact] + public void GetBool_ReturnTrue() + { + var node = new StringKeyValueNode("bob", "1"); + var observedResult = node.GetBool(); + Assert.True(observedResult); + } + + [Fact] + public void GetBool_ReturnFalse() + { + var node = new StringKeyValueNode("bob", "0"); + var observedResult = node.GetBool(); + Assert.False(observedResult); + } + + [Fact] + public void IsUndefined_ReturnTrue() + { + var node = new StringKeyValueNode("bob", null); + var observedResult = node.IsUndefined(); + Assert.True(observedResult); + } + + [Fact] + public void IsUndefined_ReturnFalse() + { + var node = new StringKeyValueNode("bob", "test"); + var observedResult = node.IsUndefined(); + Assert.False(observedResult); + } + + [Fact] + public void IsUndefined_ReturnFalse_IncorrectType() + { + var node = new ObjectKeyValueNode("bob", null); + var observedResult = node.IsUndefined(); + Assert.False(observedResult); + } + } +} diff --git a/UnitTests/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTreeTests.cs b/UnitTests/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTreeTests.cs new file mode 100644 index 00000000..d7f8e1cc --- /dev/null +++ b/UnitTests/CacheUtils/DataDumperImport/FauxRegex/RegexDecisionTreeTests.cs @@ -0,0 +1,150 @@ +using System; +using CacheUtils.DataDumperImport.FauxRegex; +using CacheUtils.DataDumperImport.IO; +using Xunit; + +namespace UnitTests.CacheUtils.DataDumperImport.FauxRegex +{ + public sealed class RegexDecisionTreeTests + { + [Fact] + public void GetEntryType_RootObjectKeyValue() + { + var results = RegexDecisionTree.GetEntryType("$VAR1 = {"); + Assert.Equal(EntryType.RootObjectKeyValue, results.Type); + Assert.Equal("$VAR1", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_ListObjectKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" '1' => ["); + Assert.Equal(EntryType.ListObjectKeyValue, results.Type); + Assert.Equal("1", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_OpenBraces() + { + var results = RegexDecisionTree.GetEntryType(" bless( {"); + Assert.Equal(EntryType.OpenBraces, results.Type); + Assert.Null(results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_StringKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" '_ccds' => 'CCDS44137.1',"); + Assert.Equal(EntryType.StringKeyValue, results.Type); + Assert.Equal("_ccds", results.Key); + Assert.Equal("CCDS44137.1", results.Value); + } + + [Fact] + public void GetEntryType_DigitKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" 'phase' => -1,"); + Assert.Equal(EntryType.DigitKeyValue, results.Type); + Assert.Equal("phase", results.Key); + Assert.Equal("-1", results.Value); + } + + [Fact] + public void GetEntryType_EndBracesWithDataType() + { + var results = RegexDecisionTree.GetEntryType(" }, 'Bio::EnsEMBL::Exon' ),"); + Assert.Equal(EntryType.EndBracesWithDataType, results.Type); + Assert.Equal("Bio::EnsEMBL::Exon", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_EndBraces() + { + var results = RegexDecisionTree.GetEntryType(" },"); + Assert.Equal(EntryType.EndBraces, results.Type); + Assert.Null(results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_ObjectKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" 'next' => bless( {"); + Assert.Equal(EntryType.ObjectKeyValue, results.Type); + Assert.Equal("next", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_UndefKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" 'adaptor' => undef,"); + Assert.Equal(EntryType.UndefKeyValue, results.Type); + Assert.Equal("adaptor", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_EmptyListKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" 'seq_edits' => [],"); + Assert.Equal(EntryType.EmptyListKeyValue, results.Type); + Assert.Equal("seq_edits", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_EmptyValueKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" 'cell_types' => {},"); + Assert.Equal(EntryType.EmptyValueKeyValue, results.Type); + Assert.Equal("cell_types", results.Key); + Assert.Null(results.Value); + } + + [Fact] + public void GetEntryType_ReferenceStringKeyValue() + { + var results = RegexDecisionTree.GetEntryType(" 'transcript' => $VAR1->{'22'}[0],"); + Assert.Equal(EntryType.ReferenceStringKeyValue, results.Type); + Assert.Equal("transcript", results.Key); + Assert.Equal("$VAR1->{'22'}[0]", results.Value); + } + + [Fact] + public void GetEntryType_DigitKey() + { + var results = RegexDecisionTree.GetEntryType(" 0,"); + Assert.Equal(EntryType.DigitKey, results.Type); + Assert.Equal("0", results.Key); + Assert.Null(results.Value); + } + + [Theory] + [InlineData("'next' => bless( [")] + [InlineData("A.B,")] + [InlineData("$VAR1 = [")] + public void GetEntryType_ThrowsNotImplementedException(string s) + { + Assert.Throws(delegate + { + // ReSharper disable once UnusedVariable + var results = RegexDecisionTree.GetEntryType(s); + }); + } + + [Theory] + [InlineData("123", true)] + [InlineData("-123", true)] + [InlineData("12A", false)] + public void OnlyDigits(string s, bool expectedResult) + { + var observedResult = RegexDecisionTree.OnlyDigits(s); + Assert.Equal(expectedResult, observedResult); + } + } +} diff --git a/UnitTests/CacheUtils/DataDumperImport/FileHandling/DataDumperReaderTests.cs b/UnitTests/CacheUtils/DataDumperImport/FileHandling/DataDumperReaderTests.cs new file mode 100644 index 00000000..06f08c1a --- /dev/null +++ b/UnitTests/CacheUtils/DataDumperImport/FileHandling/DataDumperReaderTests.cs @@ -0,0 +1,172 @@ +using System.IO; +using System.Text; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.IO; +using Xunit; + +namespace UnitTests.CacheUtils.DataDumperImport.FileHandling +{ + public sealed class DataDumperReaderTests + { + [Fact] + public void GetRootNode_EndToEnd() + { + ObjectKeyValueNode rootNode; + + using (var ms = new MemoryStream()) + { + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.WriteLine("$VAR1 = {"); + writer.WriteLine(" '22' => {"); + writer.WriteLine(" 'RegulatoryFeature' => ["); + writer.WriteLine(" bless( {"); + writer.WriteLine(" 'seq' => 'AGGGG'"); + writer.WriteLine(" 'tmp_frequencies' => '87 167 281 56 8 744 40 107 851 5 333 54 12 56 104 372 82 117 402"); + writer.WriteLine("291 145 49 800 903 13 528 433 11 0 3 12 0 8 733 13 482 322 181"); + writer.WriteLine("76 414 449 21 0 65 334 48 32 903 566 504 890 775 5 507 307 73 266"); + writer.WriteLine("459 187 134 36 2 91 11 324 18 3 9 341 8 71 67 17 37 396 59"); + writer.WriteLine("'"); + writer.WriteLine(" 'cell_types' => {},"); + writer.WriteLine(" '_bound_lengths' => ["); + writer.WriteLine(" 0,"); + writer.WriteLine(" 0"); + writer.WriteLine(" ],"); + writer.WriteLine(" 'transcript' => $VAR1->{'1'}[0],"); + writer.WriteLine(" }, 'Bio::EnsEMBL::Funcgen::RegulatoryFeature' )"); + writer.WriteLine(" ]"); + writer.WriteLine(" }"); + writer.WriteLine(" };"); + } + + ms.Position = 0; + + using (var reader = new DataDumperReader(ms)) rootNode = reader.GetRootNode(); + } + + Assert.NotNull(rootNode); + var node = rootNode; + Assert.Equal("$VAR1", node.Key); + + var chr22Node = node.Value.Values[0] as ObjectKeyValueNode; + Assert.NotNull(chr22Node); + Assert.Equal("22", chr22Node.Key); + + var rfNode = chr22Node.Value.Values[0] as ListObjectKeyValueNode; + Assert.NotNull(rfNode); + Assert.Equal("RegulatoryFeature", rfNode.Key); + + var blessNode = rfNode.Values[0] as ObjectValueNode; + Assert.NotNull(blessNode); + Assert.Null(blessNode.Key); + Assert.Equal("Bio::EnsEMBL::Funcgen::RegulatoryFeature", blessNode.Type); + + var nodes = blessNode.Values; + var seqNode = nodes[0] as StringKeyValueNode; + Assert.NotNull(seqNode); + Assert.Equal("seq", seqNode.Key); + Assert.Equal("AGGGG", seqNode.Value); + + var tmpFreqNode = nodes[1] as StringKeyValueNode; + Assert.NotNull(tmpFreqNode); + Assert.Equal("tmp_frequencies", tmpFreqNode.Key); + Assert.Equal("87 167 281 56 8 744 40 107 851 5 333 54 12 56 104 372 82 117 402 291 145 49 800 903 13 528 433 11 0 3 12 0 8 733 13 482 322 181 76 414 449 21 0 65 334 48 32 903 566 504 890 775 5 507 307 73 266 459 187 134 36 2 91 11 324 18 3 9 341 8 71 67 17 37 396 59", tmpFreqNode.Value); + + var cellTypesNode = nodes[2] as StringKeyValueNode; + Assert.NotNull(cellTypesNode); + Assert.Equal("cell_types", cellTypesNode.Key); + Assert.Null(cellTypesNode.Value); + + var boundLengthsNode = nodes[3] as ListObjectKeyValueNode; + Assert.NotNull(boundLengthsNode); + Assert.Equal("_bound_lengths", boundLengthsNode.Key); + + var bl1Node = boundLengthsNode.Values[0] as StringValueNode; + Assert.NotNull(bl1Node); + Assert.Equal("0", bl1Node.Key); + + var bl2Node = boundLengthsNode.Values[1] as StringValueNode; + Assert.NotNull(bl2Node); + Assert.Equal("0", bl2Node.Key); + + var transcriptNode = nodes[4] as StringKeyValueNode; + Assert.NotNull(transcriptNode); + Assert.Equal("transcript", transcriptNode.Key); + Assert.Equal("$VAR1->{'1'}[0]", transcriptNode.Value); + } + + [Fact] + public void GetRootNode_ObjectValue_UnhandledEntryType_ThrowsException() + { + Assert.Throws(delegate + { + using (var ms = new MemoryStream()) + { + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.WriteLine("$VAR1 = {"); + writer.WriteLine(" bless( {"); + writer.WriteLine(" 0"); + writer.WriteLine(" }, 'Bio::EnsEMBL::Funcgen::RegulatoryFeature' )"); + writer.WriteLine(" };"); + } + + ms.Position = 0; + using (var reader = new DataDumperReader(ms)) reader.GetRootNode(); + } + }); + } + + [Fact] + public void GetRootNode_ListObjectKeyValue_UnhandledEntryType_ThrowsException() + { + Assert.Throws(delegate + { + using (var ms = new MemoryStream()) + { + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.WriteLine("$VAR1 = {"); + writer.WriteLine(" '_bound_lengths' => ["); + writer.WriteLine(" 'seq' => 'AGGGG'"); + writer.WriteLine(" ]"); + writer.WriteLine(" };"); + } + + ms.Position = 0; + using (var reader = new DataDumperReader(ms)) reader.GetRootNode(); + } + }); + } + + [Fact] + public void GetRootNode_EmptyStream_ThrowsException() + { + Assert.Throws(delegate + { + using (var ms = new MemoryStream()) + { + using (var reader = new DataDumperReader(ms)) reader.GetRootNode(); + } + }); + } + + [Fact] + public void GetRootNode_NoRootObject_ThrowsException() + { + Assert.Throws(delegate + { + using (var ms = new MemoryStream()) + { + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.WriteLine("'seq' => 'AGGGG'"); + } + + ms.Position = 0; + using (var reader = new DataDumperReader(ms)) reader.GetRootNode(); + } + }); + } + } +} diff --git a/UnitTests/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeatureTests.cs b/UnitTests/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeatureTests.cs new file mode 100644 index 00000000..ae116234 --- /dev/null +++ b/UnitTests/CacheUtils/DataDumperImport/Import/ImportRegulatoryFeatureTests.cs @@ -0,0 +1,184 @@ +using System.IO; +using System.Text; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.Import; +using CacheUtils.DataDumperImport.IO; +using VariantAnnotation.Interface.Caches; +using VariantAnnotation.Sequence; +using Xunit; + +namespace UnitTests.CacheUtils.DataDumperImport.Import +{ + public sealed class ImportRegulatoryFeatureTests + { + private readonly ObjectValueNode _regulatoryFeatureNode; + + public ImportRegulatoryFeatureTests() + { + var dataDumperOutput = GetDataDumperOutput(); + _regulatoryFeatureNode = GetObjectValueNode(dataDumperOutput); + } + + #region Data::Dumper output data + + private static string GetDataDumperOutput() + { + return @"$VAR1 = { + '22' => { + 'RegulatoryFeature' => [ + bless( { + '_analysis_id' => 16, + '_bound_lengths' => [ + 0, + 0 + ], + '_vep_feature_type' => 'RegulatoryFeature', + 'cell_types' => { + 'A549' => 'INACTIVE', + 'Aorta' => 'NA', + 'B_cells_(PB)_Roadmap' => 'NA', + 'CD14+CD16-_monocyte_(CB)' => 'NA', + 'CD14+CD16-_monocyte_(VB)' => 'NA', + 'CD4+_ab_T_cell_(VB)' => 'NA', + 'CD8+_ab_T_cell_(CB)' => 'NA', + 'CM_CD4+_ab_T_cell_(VB)' => 'NA', + 'DND-41' => 'INACTIVE', + 'EPC_(VB)' => 'NA', + 'Fetal_Adrenal_Gland' => 'NA', + 'Fetal_Intestine_Large' => 'NA', + 'Fetal_Intestine_Small' => 'NA', + 'Fetal_Muscle_Leg' => 'NA', + 'Fetal_Muscle_Trunk' => 'NA', + 'Fetal_Stomach' => 'NA', + 'Fetal_Thymus' => 'NA', + 'GM12878' => 'INACTIVE', + 'Gastric' => 'NA', + 'H1-mesenchymal' => 'NA', + 'H1-neuronal_progenitor' => 'NA', + 'H1-trophoblast' => 'NA', + 'H1ESC' => 'INACTIVE', + 'H9' => 'NA', + 'HMEC' => 'INACTIVE', + 'HSMM' => 'INACTIVE', + 'HSMMtube' => 'INACTIVE', + 'HUVEC' => 'INACTIVE', + 'HUVEC_prol_(CB)' => 'NA', + 'HeLa-S3' => 'INACTIVE', + 'HepG2' => 'REPRESSED', + 'IMR90' => 'INACTIVE', + 'K562' => 'ACTIVE', + 'Left_Ventricle' => 'NA', + 'Lung' => 'NA', + 'M0_macrophage_(CB)' => 'NA', + 'M0_macrophage_(VB)' => 'NA', + 'M1_macrophage_(CB)' => 'NA', + 'M1_macrophage_(VB)' => 'NA', + 'M2_macrophage_(CB)' => 'NA', + 'M2_macrophage_(VB)' => 'NA', + 'MSC_(VB)' => 'NA', + 'Monocytes-CD14+' => 'INACTIVE', + 'Monocytes-CD14+_(PB)_Roadmap' => 'NA', + 'NH-A' => 'INACTIVE', + 'NHDF-AD' => 'INACTIVE', + 'NHEK' => 'INACTIVE', + 'NHLF' => 'INACTIVE', + 'Natural_Killer_cells_(PB)' => 'NA', + 'Osteobl' => 'INACTIVE', + 'Ovary' => 'NA', + 'Pancreas' => 'NA', + 'Placenta' => 'NA', + 'Psoas_Muscle' => 'NA', + 'Right_Atrium' => 'NA', + 'Small_Intestine' => 'NA', + 'Spleen' => 'NA', + 'T_cells_(PB)_Roadmap' => 'NA', + 'Thymus' => 'NA', + 'eosinophil_(VB)' => 'NA', + 'erythroblast_(CB)' => 'NA', + 'iPS-20b' => 'NA', + 'iPS_DF_19.11' => 'NA', + 'iPS_DF_6.9' => 'NA', + 'naive_B_cell_(VB)' => 'NA', + 'neutrophil_(CB)' => 'NA', + 'neutrophil_(VB)' => 'NA', + 'neutrophil_myelocyte_(BM)' => 'NA' + }, + 'dbID' => '71269', + 'end' => '50555915', + 'epigenome_count' => 1, + 'feature_type' => 'TF_binding_site', + 'regulatory_build_id' => 1, + 'slice' => bless( { + 'circular' => 0, + 'coord_system' => bless( { + 'dbID' => '2', + 'default' => 1, + 'name' => 'chromosome', + 'rank' => '1', + 'sequence_level' => 0, + 'top_level' => 0, + 'version' => 'GRCh37' + }, 'Bio::EnsEMBL::CoordSystem' ), + 'end' => '51304566', + 'seq_region_length' => '51304566', + 'seq_region_name' => '22', + 'start' => 1, + 'strand' => 1 + }, 'Bio::EnsEMBL::Slice' ), + 'stable_id' => 'ENSR00000394520', + 'start' => '50555633', + 'strand' => 0 + }, 'Bio::EnsEMBL::Funcgen::RegulatoryFeature' ) + ] + } + };"; + } + + #endregion + + private static ObjectValueNode GetObjectValueNode(string dataDumperOutput) + { + ObjectKeyValueNode rootNode; + + using (var ms = new MemoryStream()) + { + using (var reader = new StringReader(dataDumperOutput)) + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + while (true) + { + var line = reader.ReadLine(); + if (line == null) break; + writer.WriteLine(line); + } + } + + ms.Position = 0; + + using (var reader = new DataDumperReader(ms)) rootNode = reader.GetRootNode(); + } + + var chr22Node = rootNode.Value.Values[0] as ObjectKeyValueNode; + Assert.NotNull(chr22Node); + + var regulatoryFeatureNodes = chr22Node.Value.Values[0] as ListObjectKeyValueNode; + Assert.NotNull(regulatoryFeatureNodes); + + return regulatoryFeatureNodes.Values[0] as ObjectValueNode; + } + + [Fact] + public void Parse_Nominal() + { + var chromosome = new Chromosome("chr1", "1", 0); + var regulatoryRegion = ImportRegulatoryFeature.Parse(_regulatoryFeatureNode, chromosome); + Assert.NotNull(regulatoryRegion); + + Assert.Equal(chromosome.Index, regulatoryRegion.Chromosome.Index); + Assert.Equal(50555633, regulatoryRegion.Start); + Assert.Equal(50555915, regulatoryRegion.End); + Assert.Equal("ENSR00000394520", regulatoryRegion.Id.WithoutVersion); + Assert.Equal(RegulatoryRegionType.TF_binding_site, regulatoryRegion.Type); + } + } +} diff --git a/UnitTests/CacheUtils/DataDumperImport/Import/ImportTranscriptTests.cs b/UnitTests/CacheUtils/DataDumperImport/Import/ImportTranscriptTests.cs new file mode 100644 index 00000000..88d0b216 --- /dev/null +++ b/UnitTests/CacheUtils/DataDumperImport/Import/ImportTranscriptTests.cs @@ -0,0 +1,685 @@ +using System.IO; +using System.Text; +using CacheUtils.DataDumperImport.DataStructures; +using CacheUtils.DataDumperImport.DataStructures.Import; +using CacheUtils.DataDumperImport.Import; +using CacheUtils.DataDumperImport.IO; +using VariantAnnotation.Interface.AnnotatedPositions; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Sequence; +using Xunit; + +namespace UnitTests.CacheUtils.DataDumperImport.Import +{ + public sealed class ImportTranscriptTests + { + private readonly ObjectValueNode _transcriptNode; + + public ImportTranscriptTests() + { + var dataDumperOutput = GetDataDumperOutput(); + _transcriptNode = GetObjectValueNode(dataDumperOutput); + } + + #region Data::Dumper output data + + private static string GetDataDumperOutput() + { + return @"$VAR1 = { + '22' => [ + bless( { + '_ccds' => 'CCDS14080.1', + '_gene' => bless( { + 'end' => '50051190', + 'stable_id' => 'ENSG00000188511', + 'start' => '49808176', + 'strand' => -1 + }, 'Bio::EnsEMBL::Gene' ), + '_gene_hgnc_id' => '28010', + '_gene_phenotype' => 0, + '_gene_stable_id' => 'ENSG00000188511', + '_gene_symbol' => 'C22orf34', + '_gene_symbol_source' => 'HGNC', + '_protein' => 'ENSP00000394865', + '_refseq' => 'NM_014577.1', + '_swissprot' => '-', + '_trans_exon_array' => [ + bless( { + 'end' => '50051152', + 'end_phase' => 1, + 'phase' => -1, + 'stable_id' => 'ENSE00001657619', + 'start' => '50051053', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49834861', + 'end_phase' => -1, + 'phase' => 1, + 'stable_id' => 'ENSE00001694252', + 'start' => '49834525', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49810577', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001775575', + 'start' => '49810464', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49810384', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001669960', + 'start' => '49810251', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49809684', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001595042', + 'start' => '49808176', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ) + ], + '_trembl' => 'F2Z342', + '_uniparc' => 'UPI00004105EF', + '_variation_effect_feature_cache' => { + 'codon_table' => 1, + 'five_prime_utr' => bless( { + '_root_verbose' => 0, + 'primary_seq' => bless( { + '_nowarnonempty' => undef, + '_root_verbose' => 0, + 'alphabet' => 'dna', + 'display_id' => 'ENST00000414287', + 'length' => 45, + 'seq' => 'GCT' + }, 'Bio::PrimarySeq' ) + }, 'Bio::Seq' ), + 'introns' => [ + bless( { + 'adaptor' => undef, + 'analysis' => undef, + 'dbID' => undef, + 'end' => '50051052', + 'next' => bless( { + 'end' => '49834861', + 'end_phase' => -1, + 'phase' => 1, + 'stable_id' => 'ENSE00001694252', + 'start' => '49834525', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'prev' => bless( { + 'end' => '50051152', + 'end_phase' => 1, + 'phase' => -1, + 'stable_id' => 'ENSE00001657619', + 'start' => '50051053', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'seqname' => undef, + 'slice' => bless( { + 'circular' => 0, + 'coord_system' => bless( { + 'dbID' => '2', + 'default' => 1, + 'name' => 'chromosome', + 'rank' => '1', + 'sequence_level' => 0, + 'top_level' => 0, + 'version' => 'GRCh37' + }, 'Bio::EnsEMBL::CoordSystem' ), + 'end' => '51304566', + 'seq_region_length' => '51304566', + 'seq_region_name' => '22', + 'start' => 1, + 'strand' => 1 + }, 'Bio::EnsEMBL::Slice' ), + 'start' => '49834862', + 'strand' => -1 + }, 'Bio::EnsEMBL::Intron' ), + bless( { + 'adaptor' => undef, + 'analysis' => undef, + 'dbID' => undef, + 'end' => '49834524', + 'next' => bless( { + 'end' => '49810577', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001775575', + 'start' => '49810464', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'prev' => bless( { + 'end' => '49834861', + 'end_phase' => -1, + 'phase' => 1, + 'stable_id' => 'ENSE00001694252', + 'start' => '49834525', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'seqname' => undef, + 'slice' => bless( { + 'circular' => 0, + 'coord_system' => bless( { + 'dbID' => '2', + 'default' => 1, + 'name' => 'chromosome', + 'rank' => '1', + 'sequence_level' => 0, + 'top_level' => 0, + 'version' => 'GRCh37' + }, 'Bio::EnsEMBL::CoordSystem' ), + 'end' => '51304566', + 'seq_region_length' => '51304566', + 'seq_region_name' => '22', + 'start' => 1, + 'strand' => 1 + }, 'Bio::EnsEMBL::Slice' ), + 'start' => '49810578', + 'strand' => -1 + }, 'Bio::EnsEMBL::Intron' ), + bless( { + 'adaptor' => undef, + 'analysis' => undef, + 'dbID' => undef, + 'end' => '49810463', + 'next' => bless( { + 'end' => '49810384', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001669960', + 'start' => '49810251', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'prev' => bless( { + 'end' => '49810577', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001775575', + 'start' => '49810464', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'seqname' => undef, + 'slice' => bless( { + 'circular' => 0, + 'coord_system' => bless( { + 'dbID' => '2', + 'default' => 1, + 'name' => 'chromosome', + 'rank' => '1', + 'sequence_level' => 0, + 'top_level' => 0, + 'version' => 'GRCh37' + }, 'Bio::EnsEMBL::CoordSystem' ), + 'end' => '51304566', + 'seq_region_length' => '51304566', + 'seq_region_name' => '22', + 'start' => 1, + 'strand' => 1 + }, 'Bio::EnsEMBL::Slice' ), + 'start' => '49810385', + 'strand' => -1 + }, 'Bio::EnsEMBL::Intron' ), + bless( { + 'adaptor' => undef, + 'analysis' => undef, + 'dbID' => undef, + 'end' => '49810250', + 'next' => bless( { + 'end' => '49809684', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001595042', + 'start' => '49808176', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'prev' => bless( { + 'end' => '49810384', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001669960', + 'start' => '49810251', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'seqname' => undef, + 'slice' => bless( { + 'circular' => 0, + 'coord_system' => bless( { + 'dbID' => '2', + 'default' => 1, + 'name' => 'chromosome', + 'rank' => '1', + 'sequence_level' => 0, + 'top_level' => 0, + 'version' => 'GRCh37' + }, 'Bio::EnsEMBL::CoordSystem' ), + 'end' => '51304566', + 'seq_region_length' => '51304566', + 'seq_region_name' => '22', + 'start' => 1, + 'strand' => 1 + }, 'Bio::EnsEMBL::Slice' ), + 'start' => '49809685', + 'strand' => -1 + }, 'Bio::EnsEMBL::Intron' ) + ], + 'mapper' => bless( { + 'cdna_coding_end' => '225', + 'cdna_coding_start' => 46, + 'exon_coord_mapper' => bless( { + '_is_sorted' => 0, + '_pair_cdna' => { + 'CDNA' => [ + bless( { + 'from' => bless( { + 'end' => 100, + 'id' => 'cdna', + 'start' => 1 + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '50051152', + 'id' => 'genome', + 'start' => '50051053' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '437', + 'id' => 'cdna', + 'start' => 101 + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49834861', + 'id' => 'genome', + 'start' => '49834525' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '551', + 'id' => 'cdna', + 'start' => '438' + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49810577', + 'id' => 'genome', + 'start' => '49810464' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '685', + 'id' => 'cdna', + 'start' => '552' + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49810384', + 'id' => 'genome', + 'start' => '49810251' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '2194', + 'id' => 'cdna', + 'start' => '686' + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49809684', + 'id' => 'genome', + 'start' => '49808176' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ) + ] + }, + '_pair_genomic' => { + 'GENOME' => [ + bless( { + 'from' => bless( { + 'end' => 100, + 'id' => 'cdna', + 'start' => 1 + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '50051152', + 'id' => 'genome', + 'start' => '50051053' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '437', + 'id' => 'cdna', + 'start' => 101 + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49834861', + 'id' => 'genome', + 'start' => '49834525' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '551', + 'id' => 'cdna', + 'start' => '438' + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49810577', + 'id' => 'genome', + 'start' => '49810464' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '685', + 'id' => 'cdna', + 'start' => '552' + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49810384', + 'id' => 'genome', + 'start' => '49810251' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ), + bless( { + 'from' => bless( { + 'end' => '2194', + 'id' => 'cdna', + 'start' => '686' + }, 'Bio::EnsEMBL::Mapper::Unit' ), + 'ori' => -1, + 'to' => bless( { + 'end' => '49809684', + 'id' => 'genome', + 'start' => '49808176' + }, 'Bio::EnsEMBL::Mapper::Unit' ) + }, 'Bio::EnsEMBL::Mapper::Pair' ) + ] + }, + 'from' => 'cdna', + 'from_cs' => undef, + 'pair_count' => 5, + 'to' => 'genomic', + 'to_cs' => undef + }, 'Bio::EnsEMBL::Mapper' ), + 'start_phase' => -1 + }, 'Bio::EnsEMBL::TranscriptMapper' ), + 'peptide' => 'MIV', + 'protein_features' => [ + bless( { + 'analysis' => bless( { + '_display_label' => 'Low complexity (Seg)' + }, 'Bio::EnsEMBL::Analysis' ), + 'end' => '58', + 'hseqname' => 'seg', + 'start' => '39' + }, 'Bio::EnsEMBL::ProteinFeature' ) + ], + 'protein_function_predictions' => { + 'polyphen_humdiv' => bless( { + 'analysis' => 'polyphen', + 'matrix' => 'VkVQ-humdiv', + 'matrix_compressed' => 1, + 'peptide_length' => undef, + 'sub_analysis' => 'humdiv', + 'translation_md5' => '84229aef711b14371f4c0c6f5ec78ebe' + }, 'Bio::EnsEMBL::Variation::ProteinFunctionPredictionMatrix' ), + 'polyphen_humvar' => bless( { + 'analysis' => 'polyphen', + 'matrix' => 'VkVQ-humvar', + 'matrix_compressed' => 1, + 'peptide_length' => undef, + 'sub_analysis' => 'humvar', + 'translation_md5' => '84229aef711b14371f4c0c6f5ec78ebe' + }, 'Bio::EnsEMBL::Variation::ProteinFunctionPredictionMatrix' ), + 'sift' => bless( { + 'analysis' => 'sift', + 'matrix' => 'VkVQ-sift', + 'matrix_compressed' => 1, + 'peptide_length' => undef, + 'sub_analysis' => undef, + 'translation_md5' => '63fc5b02b6c430f970688d120e14647c' + }, 'Bio::EnsEMBL::Variation::ProteinFunctionPredictionMatrix' ) + }, + 'seq_edits' => [ + bless( { + 'alt_seq' => 'U', + 'code' => '_selenocysteine', + 'description' => undef, + 'end' => '667', + 'name' => 'Selenocysteine', + 'start' => '667' + }, 'Bio::EnsEMBL::SeqEdit' ) + ], + 'sorted_exons' => [ + bless( { + 'end' => '49809684', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001595042', + 'start' => '49808176', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49810384', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001669960', + 'start' => '49810251', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49810577', + 'end_phase' => -1, + 'phase' => -1, + 'stable_id' => 'ENSE00001775575', + 'start' => '49810464', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '49834861', + 'end_phase' => -1, + 'phase' => 1, + 'stable_id' => 'ENSE00001694252', + 'start' => '49834525', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + bless( { + 'end' => '50051152', + 'end_phase' => 1, + 'phase' => -1, + 'stable_id' => 'ENSE00001657619', + 'start' => '50051053', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ) + ], + 'three_prime_utr' => bless( { + '_root_verbose' => 0, + 'primary_seq' => bless( { + '_nowarnonempty' => undef, + '_root_verbose' => 0, + 'alphabet' => 'dna', + 'display_id' => 'ENST00000414287', + 'length' => '1969', + 'seq' => 'CAC' + }, 'Bio::PrimarySeq' ) + }, 'Bio::Seq' ), + 'translateable_seq' => 'ATG' + }, + '_vep_lazy_loaded' => 1, + 'attributes' => [ + bless( { + 'code' => 'miRNA', + 'name' => 'Micro RNA', + 'value' => '62-83' + }, 'Bio::EnsEMBL::Attribute' ), + bless( { + 'code' => 'cds_start_NF', + 'name' => 'CDS start not found', + 'value' => '1' + }, 'Bio::EnsEMBL::Attribute' ) + ], + 'biotype' => 'nonsense_mediated_decay', + 'cdna_coding_end' => '225', + 'cdna_coding_start' => 46, + 'coding_region_end' => undef, + 'coding_region_start' => undef, + 'dbID' => '2441076', + 'description' => undef, + 'end' => '50051152', + 'is_canonical' => 1, + 'slice' => bless( { + 'circular' => 0, + 'coord_system' => bless( { + 'dbID' => '2', + 'default' => 1, + 'name' => 'chromosome', + 'rank' => '1', + 'sequence_level' => 0, + 'top_level' => 0, + 'version' => 'GRCh37' + }, 'Bio::EnsEMBL::CoordSystem' ), + 'end' => '51304566', + 'seq_region_length' => '51304566', + 'seq_region_name' => '22', + 'start' => 1, + 'strand' => 1 + }, 'Bio::EnsEMBL::Slice' ), + 'source' => 'havana', + 'stable_id' => 'ENST00000414287', + 'start' => '49808176', + 'strand' => -1, + 'translation' => bless( { + 'dbID' => '1232784', + 'end' => 125, + 'end_exon' => bless( { + 'end' => '49834861', + 'end_phase' => -1, + 'phase' => 1, + 'stable_id' => 'ENSE00001694252', + 'start' => '49834525', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'seq' => undef, + 'stable_id' => 'ENSP00000394865', + 'start' => 46, + 'start_exon' => bless( { + 'end' => '50051152', + 'end_phase' => 1, + 'phase' => 1, + 'stable_id' => 'ENSE00001657619', + 'start' => '50051053', + 'strand' => -1 + }, 'Bio::EnsEMBL::Exon' ), + 'transcript' => $VAR1->{'22'}[0], + 'version' => 1 + }, 'Bio::EnsEMBL::Translation' ), + 'version' => 1 + }, 'Bio::EnsEMBL::Transcript' ) + ] + };"; + } + + #endregion + + private static ObjectValueNode GetObjectValueNode(string dataDumperOutput) + { + ObjectKeyValueNode rootNode; + + using (var ms = new MemoryStream()) + { + using (var reader = new StringReader(dataDumperOutput)) + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + while (true) + { + var line = reader.ReadLine(); + if (line == null) break; + writer.WriteLine(line); + } + } + + ms.Position = 0; + + using (var reader = new DataDumperReader(ms)) rootNode = reader.GetRootNode(); + } + + var chr22Node = rootNode.Value.Values[0] as ListObjectKeyValueNode; + Assert.NotNull(chr22Node); + + return chr22Node.Values[0] as ObjectValueNode; + } + + [Fact] + public void Parse_Nominal() + { + var chromosome = new Chromosome("chr1", "1", 0); + var mutableTranscript = ImportTranscript.Parse(_transcriptNode, chromosome, Source.Ensembl); + Assert.NotNull(mutableTranscript); + + Assert.Equal(chromosome.Index, mutableTranscript.Chromosome.Index); + Assert.Equal(49808176, mutableTranscript.Start); + Assert.Equal(50051152, mutableTranscript.End); + Assert.Equal("ENST00000414287", mutableTranscript.Id); + Assert.Equal(1, mutableTranscript.Version); + Assert.Equal("CCDS14080.1", mutableTranscript.CcdsId); + Assert.Equal("NM_014577.1", mutableTranscript.RefSeqId); + Assert.Equal(Source.Ensembl, mutableTranscript.Source); + Assert.Equal(49808176, mutableTranscript.Gene.Start); + Assert.Equal(50051190, mutableTranscript.Gene.End); + Assert.Equal("ENSG00000188511", mutableTranscript.Gene.GeneId); + Assert.Equal("C22orf34", mutableTranscript.Gene.Symbol); + Assert.Equal(28010, mutableTranscript.Gene.HgncId); + Assert.Equal(chromosome.Index, mutableTranscript.Gene.Chromosome.Index); + Assert.True(mutableTranscript.Gene.OnReverseStrand); + Assert.Equal(GeneSymbolSource.HGNC, mutableTranscript.Gene.SymbolSource); + Assert.Equal(5, mutableTranscript.Exons.Length); + Assert.Equal(50051053, mutableTranscript.Exons[0].Start); + Assert.Equal(50051152, mutableTranscript.Exons[0].End); + Assert.Equal(-1, mutableTranscript.Exons[0].Phase); + Assert.Equal(2194, mutableTranscript.TotalExonLength); + Assert.Equal(4, mutableTranscript.Introns.Length); + Assert.Equal(49834862, mutableTranscript.Introns[0].Start); + Assert.Equal(50051052, mutableTranscript.Introns[0].End); + Assert.Equal("ATG", mutableTranscript.TranslateableSequence); + Assert.Equal(new IInterval[] { new Interval(62, 83) }, mutableTranscript.MicroRnas); + Assert.True(mutableTranscript.CdsStartNotFound); + Assert.False(mutableTranscript.CdsEndNotFound); + Assert.Equal(new[] { 667 }, mutableTranscript.SelenocysteinePositions); + Assert.Equal(1, mutableTranscript.StartExonPhase); + Assert.Equal(BioType.nonsense_mediated_decay, mutableTranscript.BioType); + Assert.True(mutableTranscript.IsCanonical); + Assert.Equal(5, mutableTranscript.CdnaMaps.Length); + Assert.Equal(50051053, mutableTranscript.CdnaMaps[0].Start); + Assert.Equal(50051152, mutableTranscript.CdnaMaps[0].End); + Assert.Equal(1, mutableTranscript.CdnaMaps[0].CdnaStart); + Assert.Equal(100, mutableTranscript.CdnaMaps[0].CdnaEnd); + Assert.Equal(49834737, mutableTranscript.CodingRegion.Start); + Assert.Equal(50051107, mutableTranscript.CodingRegion.End); + Assert.Equal(46, mutableTranscript.CodingRegion.CdnaStart); + Assert.Equal(225, mutableTranscript.CodingRegion.CdnaEnd); + Assert.Equal("ENSP00000394865", mutableTranscript.ProteinId); + Assert.Equal(1, mutableTranscript.ProteinVersion); + Assert.Equal("MIV", mutableTranscript.PeptideSequence); + Assert.Equal("VkVQ-sift", mutableTranscript.SiftData); + Assert.Equal("VkVQ-humvar", mutableTranscript.PolyphenData); + } + } +} diff --git a/UnitTests/CacheUtils/Genes/Combiners/CombinerUtilsTests.cs b/UnitTests/CacheUtils/Genes/Combiners/CombinerUtilsTests.cs new file mode 100644 index 00000000..15a8dcec --- /dev/null +++ b/UnitTests/CacheUtils/Genes/Combiners/CombinerUtilsTests.cs @@ -0,0 +1,41 @@ +using System.IO; +using CacheUtils.Genes.Combiners; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Sequence; +using Xunit; + +namespace UnitTests.CacheUtils.Genes.Combiners +{ + public sealed class CombinerUtilsTests + { + private readonly IChromosome _chr1 = new Chromosome("chr1", "1", 0); + + [Fact] + public void Merge_DifferentCombinations() + { + var interval = new Interval(17369, 17436); + var uga37 = new UgaGene(_chr1, interval, null, true, "102466751", null, "MIR6859-1", 50039); + var uga38 = new UgaGene(_chr1, null, interval, true, null, "ENSG00000278267", "MIR6859-1", 50039); + + var observedResult = CombinerUtils.Merge(uga37, uga38); + Assert.Equal("102466751", observedResult.EntrezGeneId); + Assert.Equal("ENSG00000278267", observedResult.EnsemblId); + } + + [Fact] + public void Merge_ThrowException_IfValuesDifferent() + { + var interval = new Interval(17369, 17436); + var uga37 = new UgaGene(_chr1, interval, null, true, "102466751", "ENSG00000278267", "MIR6859-1", 50039); + var uga38 = new UgaGene(_chr1, null, interval, true, "000000000", "ENSG00000278267", "MIR6859-1", 50039); + + Assert.Throws(delegate + { + // ReSharper disable once UnusedVariable + var observedResult = CombinerUtils.Merge(uga37, uga38); + }); + } + } +} diff --git a/UnitTests/CacheUtils/Genes/Combiners/HgncIdCombinerTests.cs b/UnitTests/CacheUtils/Genes/Combiners/HgncIdCombinerTests.cs new file mode 100644 index 00000000..cb333d60 --- /dev/null +++ b/UnitTests/CacheUtils/Genes/Combiners/HgncIdCombinerTests.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using CacheUtils.Genes.Combiners; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Sequence; +using Xunit; + +namespace UnitTests.CacheUtils.Genes.Combiners +{ + public sealed class HgncIdCombinerTests + { + private readonly IChromosome _chr1 = new Chromosome("chr1", "1", 0); + private readonly HgncIdCombiner _combiner = new HgncIdCombiner(); + + [Fact] + public void Combine_CombineWhenAllIdsMatch() + { + var interval = new Interval(17369, 17436); + var uga37 = new HashSet { new UgaGene(_chr1, interval, null, true, "102466751", "ENSG00000278267", "MIR6859-1", 50039) }; + var uga38 = new HashSet { new UgaGene(_chr1, null, interval, true, "102466751", "ENSG00000278267", "MIR6859-1", 50039) }; + + var observedResults = new List(); + _combiner.Combine(observedResults, uga37, uga38); + + Assert.Equal(1, observedResults.Count); + + var observedGene = observedResults[0]; + Assert.Equal("102466751", observedGene.EntrezGeneId); + Assert.Equal("ENSG00000278267", observedGene.EnsemblId); + Assert.Equal(interval, observedGene.GRCh37); + Assert.Equal(interval, observedGene.GRCh38); + } + + [Fact] + public void Combine_DoNotCombine_MixedStrands() + { + var interval = new Interval(17369, 17436); + var uga37 = new HashSet { new UgaGene(_chr1, interval, null, true, "102466751", "ENSG00000278267", "MIR6859-1", 50039) }; + var uga38 = new HashSet { new UgaGene(_chr1, null, interval, false, "102466751", "ENSG00000278267", "MIR6859-1", 50039) }; + + var observedResults = new List(); + _combiner.Combine(observedResults, uga37, uga38); + + Assert.Equal(2, observedResults.Count); + Assert.True(observedResults[0].OnReverseStrand); + Assert.False(observedResults[1].OnReverseStrand); + } + + [Fact] + public void Combine_MIR6859_CombineWhenMissingGeneId() + { + var interval = new Interval(17369, 17436); + var uga37 = new HashSet { new UgaGene(_chr1, interval, null, true, "102466751", null, "MIR6859-1", 50039) }; + var uga38 = new HashSet { new UgaGene(_chr1, null, interval, true, "102466751", "ENSG00000278267", "MIR6859-1", 50039) }; + + var observedResults = new List(); + _combiner.Combine(observedResults, uga37, uga38); + + Assert.Equal(1, observedResults.Count); + + var observedGene = observedResults[0]; + Assert.Equal("102466751", observedGene.EntrezGeneId); + Assert.Equal("ENSG00000278267", observedGene.EnsemblId); + Assert.Equal(interval, observedGene.GRCh37); + Assert.Equal(interval, observedGene.GRCh38); + } + } +} diff --git a/UnitTests/CacheUtils/Genes/Combiners/PartitionCombinerTests.cs b/UnitTests/CacheUtils/Genes/Combiners/PartitionCombinerTests.cs new file mode 100644 index 00000000..13dead09 --- /dev/null +++ b/UnitTests/CacheUtils/Genes/Combiners/PartitionCombinerTests.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using CacheUtils.Genes.Combiners; +using CacheUtils.Genes.DataStructures; +using VariantAnnotation.Interface.Intervals; +using VariantAnnotation.Interface.Sequence; +using VariantAnnotation.Sequence; +using Xunit; + +namespace UnitTests.CacheUtils.Genes.Combiners +{ + public sealed class PartitionCombinerTests + { + private readonly IChromosome _chr1 = new Chromosome("chr1", "1", 0); + private readonly PartitionCombiner _combiner = new PartitionCombiner(); + + [Fact] + public void Combine_MergeIfSameIds_EntrezGeneOnly() + { + var interval = new Interval(17369, 17436); + var uga37 = new HashSet { new UgaGene(_chr1, interval, null, true, "102466751", null, "MIR6859-1", 50039) }; + var uga38 = new HashSet { new UgaGene(_chr1, null, interval, true, "102466751", null, "MIR6859-1", 50039) }; + + var observedResults = new List(); + _combiner.Combine(observedResults, uga37, uga38); + + Assert.Equal(1, observedResults.Count); + + var observedGene = observedResults[0]; + Assert.Equal("102466751", observedGene.EntrezGeneId); + Assert.Null(observedGene.EnsemblId); + Assert.Equal(interval, observedGene.GRCh37); + Assert.Equal(interval, observedGene.GRCh38); + } + + [Fact] + public void Combine_MergeIfSameIds_EnsemblOnly() + { + var interval = new Interval(17369, 17436); + var uga37 = new HashSet { new UgaGene(_chr1, interval, null, true, null, "ENSG00000278267", "MIR6859-1", 50039) }; + var uga38 = new HashSet { new UgaGene(_chr1, null, interval, true, null, "ENSG00000278267", "MIR6859-1", 50039) }; + + var observedResults = new List(); + _combiner.Combine(observedResults, uga37, uga38); + + Assert.Equal(1, observedResults.Count); + + var observedGene = observedResults[0]; + Assert.Equal("ENSG00000278267", observedGene.EnsemblId); + Assert.Null(observedGene.EntrezGeneId); + Assert.Equal(interval, observedGene.GRCh37); + Assert.Equal(interval, observedGene.GRCh38); + } + + [Fact] + public void Combine_DoNotCombine_MixedIds() + { + var interval = new Interval(17369, 17436); + var uga37 = new HashSet { new UgaGene(_chr1, interval, null, true, "102466751", null, "MIR6859-1", 50039) }; + var uga38 = new HashSet { new UgaGene(_chr1, null, interval, true, "102466751", "ENSG00000278267", "MIR6859-1", 50039) }; + + var observedResults = new List(); + _combiner.Combine(observedResults, uga37, uga38); + + Assert.Equal(2, observedResults.Count); + Assert.Equal("ENSG00000278267", observedResults[0].EnsemblId); + Assert.Null(observedResults[1].EnsemblId); + } + } +} diff --git a/UnitTests/CacheUtils/Genes/GeneFlattenerTests.cs b/UnitTests/CacheUtils/Genes/GeneFlattenerTests.cs new file mode 100644 index 00000000..0d6272a4 --- /dev/null +++ b/UnitTests/CacheUtils/Genes/GeneFlattenerTests.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using CacheUtils.DataDumperImport.DataStructures; +using CacheUtils.DataDumperImport.DataStructures.Mutable; +using CacheUtils.Genes; +using Xunit; + +namespace UnitTests.CacheUtils.Genes +{ + public sealed class GeneFlattenerTests + { + [Fact] + public void Flatten_AllGenesShouldBeCombined() + { + var genes = new List + { + new MutableGene(null, 100, 120, false, null, GeneSymbolSource.Unknown, "test", -1), + new MutableGene(null, 110, 115, false, null, GeneSymbolSource.Unknown, "test", -1), + new MutableGene(null, 120, 130, false, null, GeneSymbolSource.Unknown, "test", -1) + }; + + var flatGenes = GeneFlattener.FlattenWithSameId(genes); + + Assert.Equal(1, flatGenes.Count); + + var flatGene = flatGenes[0]; + Assert.Equal(100, flatGene.Start); + Assert.Equal(130, flatGene.End); + } + + [Fact] + public void Flatten_ReturnSameGene_WhenListHasOneEntry() + { + var genes = new List + { + new MutableGene(null, 100, 120, false, null, GeneSymbolSource.Unknown, "test", -1) + }; + + var flatGenes = GeneFlattener.FlattenWithSameId(genes); + + Assert.Equal(1, flatGenes.Count); + Assert.Equal(genes[0].Start, flatGenes[0].Start); + Assert.Equal(genes[0].End, flatGenes[0].End); + } + + [Fact] + public void Flatten_ReturnNull_WhenInputNull() + { + var flatGenes = GeneFlattener.FlattenWithSameId(null as List); + Assert.Null(flatGenes); + } + + [Fact] + public void Flatten_NoGenesShouldBeCombined() + { + var genes = new List + { + new MutableGene(null, 100, 120, false, null, GeneSymbolSource.Unknown, "test", -1), + new MutableGene(null, 130, 140, false, null, GeneSymbolSource.Unknown, "test", -1), + new MutableGene(null, 150, 160, false, null, GeneSymbolSource.Unknown, "test", -1) + }; + + var flatGenes = GeneFlattener.FlattenWithSameId(genes); + + Assert.Equal(3, flatGenes.Count); + for (int i = 0; i < flatGenes.Count; i++) + { + Assert.Equal(genes[i].Start, flatGenes[i].Start); + Assert.Equal(genes[i].End, flatGenes[i].End); + } + } + } +} diff --git a/UnitTests/CacheUtils/Genes/Utilities/DictionaryUtilitiesTests.cs b/UnitTests/CacheUtils/Genes/Utilities/DictionaryUtilitiesTests.cs new file mode 100644 index 00000000..50bd7adb --- /dev/null +++ b/UnitTests/CacheUtils/Genes/Utilities/DictionaryUtilitiesTests.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.IO; +using CacheUtils.Genes.DataStructures; +using CacheUtils.Genes.Utilities; +using Xunit; + +namespace UnitTests.CacheUtils.Genes.Utilities +{ + public sealed class DictionaryUtilitiesTests + { + [Fact] + public void GetSingleValueDict_OneKey_OneValue() + { + var uga1 = new UgaGene(null, null, null, true, "102466751", null, "MIR6859-1", 50039); + var uga2 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var genes = new List { uga1, uga2 }; + + var observedResult = genes.GetSingleValueDict(x => x.EnsemblId); + Assert.NotNull(observedResult); + Assert.Equal(1, observedResult.Count); + Assert.True(observedResult.ContainsKey("ENSG00000278267")); + } + + [Fact] + public void GetSingleValueDict_ThrowException_IfMultipleValuesShareKey() + { + var uga1 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var uga2 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var genes = new List { uga1, uga2 }; + + Assert.Throws(delegate + { + // ReSharper disable once UnusedVariable + var observedResult = genes.GetSingleValueDict(x => x.EnsemblId); + }); + } + + [Fact] + public void GetMultiValueDict_OneKey_WithTwoValues() + { + var uga1 = new UgaGene(null, null, null, true, "102466751", null, "MIR6859-1", 50039); + var uga2 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var uga3 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var genes = new List { uga1, uga2, uga3 }; + + var observedResult = genes.GetMultiValueDict(x => x.EnsemblId); + Assert.NotNull(observedResult); + Assert.Equal(1, observedResult.Count); + + var firstEntry = observedResult["ENSG00000278267"]; + Assert.NotNull(firstEntry); + Assert.Equal(2, firstEntry.Count); + } + + [Fact] + public void GetKeyValueDict_OneKey_OneValue() + { + var uga1 = new UgaGene(null, null, null, true, "102466751", null, "MIR6859-1", 50039); + var uga2 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var uga3 = new UgaGene(null, null, null, true, null, "ENSG00000278267", "MIR6859-1", 50039); + var genes = new List { uga1, uga2, uga3 }; + + var observedResult = genes.GetKeyValueDict(x => x.EnsemblId, x => x.HgncId); + Assert.NotNull(observedResult); + Assert.Equal(1, observedResult.Count); + + var hgncId = observedResult["ENSG00000278267"]; + Assert.NotNull(hgncId); + Assert.Equal(50039, hgncId); + } + + [Fact] + public void CreateIndex_ThreeValues() + { + const string a = "tom"; + const string b = "jane"; + const string c = "sally"; + var genes = new List { a, b, c }; + + var observedResult = genes.CreateIndex(); + Assert.NotNull(observedResult); + Assert.Equal(3, observedResult.Count); + + Assert.Equal(0, observedResult[a]); + Assert.Equal(1, observedResult[b]); + Assert.Equal(2, observedResult[c]); + } + } +} diff --git a/UnitTests/CacheUtils/IO/Caches/TranscriptCacheWriterTests.cs b/UnitTests/CacheUtils/IO/Caches/TranscriptCacheWriterTests.cs index 08529afa..6bcec152 100644 --- a/UnitTests/CacheUtils/IO/Caches/TranscriptCacheWriterTests.cs +++ b/UnitTests/CacheUtils/IO/Caches/TranscriptCacheWriterTests.cs @@ -1,4 +1,5 @@ -using CacheUtils.IO.Caches; +using System.Collections.Generic; +using CacheUtils.TranscriptCache; using Xunit; namespace UnitTests.CacheUtils.IO.Caches @@ -9,7 +10,7 @@ public sealed class TranscriptCacheWriterTests public void CreateIndex_PopulatedDictionary() { var strings = new[] { "A", "B", "D", "P", "Z" }; - var dict = TranscriptCacheWriter.CreateIndex(strings); + var dict = TranscriptCacheWriter.CreateIndex(strings, EqualityComparer.Default); Assert.NotNull(dict); Assert.Equal(3, dict["P"]); } @@ -17,7 +18,7 @@ public void CreateIndex_PopulatedDictionary() [Fact] public void CreateIndex_EmptyDictionary_WhenInputNull() { - var dict = TranscriptCacheWriter.CreateIndex(null); + var dict = TranscriptCacheWriter.CreateIndex(null, EqualityComparer.Default); Assert.NotNull(dict); Assert.Equal(0, dict.Count); } diff --git a/UnitTests/CacheUtils/Utilities/AccessionUtilitiesTests.cs b/UnitTests/CacheUtils/Utilities/AccessionUtilitiesTests.cs new file mode 100644 index 00000000..4b1b425e --- /dev/null +++ b/UnitTests/CacheUtils/Utilities/AccessionUtilitiesTests.cs @@ -0,0 +1,73 @@ +using System.IO; +using CacheUtils.Utilities; +using Xunit; + +namespace UnitTests.CacheUtils.Utilities +{ + public sealed class AccessionUtilitiesTests + { + [Fact] + public void GetMaxVersion_Dupl() + { + const string expectedId = "NM_004522.2_dupl6"; + const byte expectedVersion = 1; + var observedResult = AccessionUtilities.GetMaxVersion("NM_004522.2_dupl6", 1); + Assert.Equal(expectedId, observedResult.Id); + Assert.Equal(expectedVersion, observedResult.Version); + } + + [Fact] + public void GetMaxVersion_IdVersionMax() + { + const string expectedId = "NM_004522"; + const byte expectedVersion = 2; + var observedResult = AccessionUtilities.GetMaxVersion("NM_004522.2", 1); + Assert.Equal(expectedId, observedResult.Id); + Assert.Equal(expectedVersion, observedResult.Version); + } + + [Fact] + public void GetMaxVersion_SuppliedVersionMax() + { + const string expectedId = "NM_004522"; + const byte expectedVersion = 3; + var observedResult = AccessionUtilities.GetMaxVersion("NM_004522.2", 3); + Assert.Equal(expectedId, observedResult.Id); + Assert.Equal(expectedVersion, observedResult.Version); + } + + [Fact] + public void GetAccessionNumber_ReturnNumber_RefSeq() + { + const int expectedResult = 4522; + var observedResult = AccessionUtilities.GetAccessionNumber("NM_004522"); + Assert.Equal(expectedResult, observedResult); + } + + [Fact] + public void GetAccessionNumber_ReturnNumber_Ensembl() + { + const int expectedResult = 515242; + var observedResult = AccessionUtilities.GetAccessionNumber("ENST00000515242"); + Assert.Equal(expectedResult, observedResult); + } + + [Fact] + public void GetAccessionNumber_ReturnMinusOne() + { + const int expectedResult = -1; + var observedResult = AccessionUtilities.GetAccessionNumber(null); + Assert.Equal(expectedResult, observedResult); + } + + [Fact] + public void GetAccessionNumber_ThrowException_IfUnderlineMissingRefSeq() + { + Assert.Throws(delegate + { + // ReSharper disable once UnusedVariable + var observedResult = AccessionUtilities.GetAccessionNumber("NM004522"); + }); + } + } +} diff --git a/UnitTests/CacheUtils/Utilities/RemoteFileTests.cs b/UnitTests/CacheUtils/Utilities/RemoteFileTests.cs new file mode 100644 index 00000000..852d5b40 --- /dev/null +++ b/UnitTests/CacheUtils/Utilities/RemoteFileTests.cs @@ -0,0 +1,34 @@ +using System; +using CacheUtils.Utilities; +using VariantAnnotation.Utilities; +using Xunit; + +namespace UnitTests.CacheUtils.Utilities +{ + public sealed class RemoteFileTests + { + [Fact] + public void GetFilename_WithoutUrlPrefix() + { + string expectedResult = $"ccds_1000_{Date.GetDate(DateTime.Now.Ticks)}.txt"; + var observedResult = RemoteFile.GetFilename("ccds_1000.txt", true); + Assert.Equal(expectedResult, observedResult); + } + + [Fact] + public void GetFilename_WithoutDate() + { + string expectedResult = "CCDS2Sequence.20160908.txt"; + var observedResult = RemoteFile.GetFilename("ftp://ftp.ncbi.nlm.nih.gov/pub/CCDS/current_human/CCDS2Sequence.20160908.txt", false); + Assert.Equal(expectedResult, observedResult); + } + + [Fact] + public void GetFilename_WithUrlPrefix() + { + string expectedResult = $"CCDS2Sequence.20160908_{Date.GetDate(DateTime.Now.Ticks)}.txt"; + var observedResult = RemoteFile.GetFilename("ftp://ftp.ncbi.nlm.nih.gov/pub/CCDS/current_human/CCDS2Sequence.20160908.txt", true); + Assert.Equal(expectedResult, observedResult); + } + } +} diff --git a/UnitTests/CommandLine/NDesk.Options/OptionSetTests.cs b/UnitTests/CommandLine/NDesk.Options/OptionSetTests.cs index 48cf2bf7..0607d56c 100644 --- a/UnitTests/CommandLine/NDesk.Options/OptionSetTests.cs +++ b/UnitTests/CommandLine/NDesk.Options/OptionSetTests.cs @@ -330,7 +330,7 @@ public void HaltProcessing() var optionSet = new OptionSet { { "a", "", v => {} }, - { "b", "", v => {} }, + { "b", "", v => {} } }; var e = optionSet.Parse(new[] { "-a", "-b", "--", "-a", "-b" }); @@ -372,7 +372,7 @@ public void OptionContext() new ContextCheckerOption ("a=", "a desc", "/a", "a-val", 1), new ContextCheckerOption ("b", "b desc", "--b+", "--b+", 2), new ContextCheckerOption ("c=", "c desc", "--c", "C", 3), - new ContextCheckerOption ("d", "d desc", "/d-", null, 4), + new ContextCheckerOption ("d", "d desc", "/d-", null, 4) }; Assert.Equal(optionSet.Count, 4); optionSet.Parse(new[] { "/a", "a-val", "--b+", "--c=C", "/d-" }); @@ -384,7 +384,7 @@ public void DefaultHandler() var extra = new List(); var optionSet = new OptionSet { - { "<>", "", v => extra.Add (v) }, + { "<>", "", v => extra.Add (v) } }; var e = optionSet.Parse(new[] { "-a", "b", "--c=D", "E" }); Assert.Equal(e.Count, 0); @@ -401,7 +401,7 @@ public void MixedDefaultHandler() var tests = new List(); var optionSet = new OptionSet { - { "t|<>=", "", v => tests.Add (v) }, + { "t|<>=", "", v => tests.Add (v) } }; var e = optionSet.Parse(new[] { "-tA", "-t:B", "-t=C", "D", "--E=F" }); Assert.Equal(e.Count, 0); @@ -430,7 +430,7 @@ public void DefaultHandlerRuns() formats.Add (format, f); } f.Add (v); - } }, + } } }; var e = optionSet.Parse(new[] { "a", "b", "-fbar", "c", "d", "--format=baz", "e", "f" }); diff --git a/UnitTests/EndToEndTests.cs b/UnitTests/EndToEndTests.cs index 90eac2d5..ce24d445 100644 --- a/UnitTests/EndToEndTests.cs +++ b/UnitTests/EndToEndTests.cs @@ -1,68 +1,48 @@ using System.Collections.Generic; -using System.IO; using UnitTests.TestUtilities; using Xunit; namespace UnitTests { - public class EndToEndTests + // NOTE: these tests do not include phyloP scores yet + // CACHE: v25 (VEP90) + // SA: SA38 based on intermediate TSV from /illumina/development/Nirvana/Development/IntermediateTsvs/2017-05/GRCh37 + public sealed class EndToEndTests { - //NOTE: for tests here no regulatory region and no phylop scores yet - //Cache is from cache 24, VEP84 - //SA: SA38 based on intermediate TSV from /illumina/development/Nirvana/Development/IntermediateTsvs/2017-05/GRCh37 - - private string _cacheFilePrefix = Resources.TopPath(Path.Combine("GRCh37_chr12_7018490_7086889", "chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos")); - private string _saDirectory = Resources.TopPath(Path.Combine("GRCh37_chr12_7018490_7086889", "SA")); - + private readonly string _cacheFilePrefix; + private readonly List _saDirs; + public EndToEndTests() + { + _cacheFilePrefix = Resources.EndToEnd37("chr12_7018490_7086889_Both90"); + _saDirs = new List { Resources.EndToEnd37("") }; + } [Fact] public void Annotation_RefMinor_not_annotated_when_no_SA() { - var vcfLine = "chr12 7054859 . G . 100 PASS . . ."; - var annotatedPosition = - AnnotationUtilities.GetAnnotatedPosition(_cacheFilePrefix, null, vcfLine, false); - + const string vcfLine = "chr12 7054859 . G . 100 PASS . . ."; + var annotatedPosition = AnnotationUtilities.GetAnnotatedPosition(_cacheFilePrefix, null, vcfLine, false); var output = annotatedPosition.GetJsonString(); Assert.Null(output); - } - [Theory] [InlineData("chr12 7045879 . C , . PASS SVTYPE=STR;END=7045936;REF=19;RL=57;RU=CAG;REPID=DRPLA GT:SO:CN:CI:AD_SP:AD_FL:AD_IR 1/2:SPANNING/SPANNING:14/22:9/4:19/20:0/0", "{\"chromosome\":\"chr12\",\"position\":7045879,\"repeatUnit\":\"CAG\",\"refRepeatCount\":19,\"svEnd\":7045936,\"refAllele\":\"C\",\"altAlleles\":[\"\",\"\"],\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"genotype\":\"1/2\",\"repeatNumbers\":\"14/22\",\"repeatNumberSpans\":\"9/4\"}],\"variants\":[{\"vid\":\"12:7045880:7045936:CAG:14\",\"chromosome\":\"chr12\",\"begin\":7045880,\"end\":7045936,\"refAllele\":\"C\",\"altAllele\":\"STR14\",\"variantType\":\"short_tandem_repeat_variation\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_contraction\"],\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_contraction\"],\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_contraction\"],\"proteinId\":\"NP_001931.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_contraction\"],\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_contraction\"],\"proteinId\":\"ENSP00000379915.2\"}]}},{\"vid\":\"12:7045880:7045936:CAG:22\",\"chromosome\":\"chr12\",\"begin\":7045880,\"end\":7045936,\"refAllele\":\"C\",\"altAllele\":\"STR22\",\"variantType\":\"short_tandem_repeat_variation\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_expansion\"],\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_expansion\"],\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_expansion\"],\"proteinId\":\"NP_001931.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_expansion\"],\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"exons\":\"5/10\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"short_tandem_repeat_expansion\"],\"proteinId\":\"ENSP00000379915.2\"}]}}]}")] - [InlineData("chr12 7048190 . G A 322 PASS SB=0.1234567 . .", "{\"chromosome\":\"chr12\",\"position\":7048190,\"refAllele\":\"G\",\"altAlleles\":[\"A\"],\"quality\":322,\"filters\":[\"PASS\"],\"strandBias\":0.123457,\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7048190:A\",\"chromosome\":\"chr12\",\"begin\":7048190,\"end\":7048190,\"refAllele\":\"G\",\"altAllele\":\"A\",\"variantType\":\"SNV\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3301\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_001007026.1:c.3064G>A\",\"hgvsp\":\"NP_001007027.1:p.(Ala1022Thr)\",\"isCanonical\":true,\"polyPhenScore\":0.995,\"polyPhenPrediction\":\"probably damaging\",\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3391\",\"cdsPos\":\"3061\",\"exons\":\"7/10\",\"proteinPos\":\"1021\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"XM_005253672.1:c.3061G>A\",\"hgvsp\":\"XP_005253729.1:p.(Ala1021Thr)\",\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3294\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_001940.3:c.3064G>A\",\"hgvsp\":\"NP_001931.2:p.(Ala1022Thr)\",\"polyPhenScore\":0.995,\"polyPhenPrediction\":\"probably damaging\",\"proteinId\":\"NP_001931.2\"},{\"transcript\":\"XM_005253669.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"XP_005253726.1\"},{\"transcript\":\"XM_005253670.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"XP_005253727.1\"},{\"transcript\":\"NR_023317.1\",\"bioType\":\"snRNA\",\"geneId\":\"100147744\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3301\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000356654.4:c.3064G>A\",\"hgvsp\":\"ENSP00000349076.3:p.(Ala1022Thr)\",\"isCanonical\":true,\"polyPhenScore\":0.995,\"polyPhenPrediction\":\"probably damaging\",\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3298\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000396684.2:c.3064G>A\",\"hgvsp\":\"ENSP00000379915.2:p.(Ala1022Thr)\",\"polyPhenScore\":0.995,\"polyPhenPrediction\":\"probably damaging\",\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000541029.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000538392.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000542222.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000545581.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000440602.1\"},{\"transcript\":\"ENST00000607421.1\",\"bioType\":\"antisense\",\"geneId\":\"ENSG00000272173\",\"hgnc\":\"U47924.31\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000458811.1\",\"bioType\":\"snRNA\",\"geneId\":\"ENSG00000238923\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000544681.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000475422.1\"},{\"transcript\":\"ENST00000537087.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000440937.1\"},{\"transcript\":\"ENST00000229281.5\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"ENSP00000229281.5\"}]}}]}")] - [InlineData("chr12 7054859 . G . 100 PASS . . .", - "{\"chromosome\":\"chr12\",\"position\":7054859,\"refAllele\":\"G\",\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7054859:G\",\"chromosome\":\"chr12\",\"begin\":7054859,\"end\":7054859,\"isReferenceMinorAllele\":true,\"refAllele\":\"G\",\"variantType\":\"SNV\",\"globalAllele\":{\"globalMinorAllele\":\"G\",\"globalMinorAlleleFrequency\":0.003794},\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"NP_001931.2\"},{\"transcript\":\"XM_005253669.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"XP_005253726.1\"},{\"transcript\":\"XM_005253670.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"XM_005253670.1:c.191-75A>G\",\"proteinId\":\"XP_005253727.1\"},{\"transcript\":\"NR_023317.1\",\"bioType\":\"snRNA\",\"geneId\":\"100147744\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"NM_138425.2\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_138425.2:c.230-75A>G\",\"isCanonical\":true,\"proteinId\":\"NP_612434.1\"},{\"transcript\":\"NM_080548.4\",\"bioType\":\"protein_coding\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"NP_536858.1\"},{\"transcript\":\"XM_005253719.1\",\"bioType\":\"protein_coding\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"XP_005253776.1\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000538392.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000542222.1\",\"bioType\":\"processed_transcript\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\",\"non_coding_transcript_variant\"],\"hgvsc\":\"ENST00000542222.1:n.408-75A>G\"},{\"transcript\":\"ENST00000545581.1\",\"bioType\":\"protein_coding\",\"introns\":\"3/3\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000545581.1:c.230-75A>G\",\"proteinId\":\"ENSP00000440602.1\"},{\"transcript\":\"ENST00000607421.1\",\"bioType\":\"antisense\",\"geneId\":\"ENSG00000272173\",\"hgnc\":\"U47924.31\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000458811.1\",\"bioType\":\"snRNA\",\"geneId\":\"ENSG00000238923\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000544681.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000475422.1\"},{\"transcript\":\"ENST00000537087.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000537087.1:c.143-75A>G\",\"proteinId\":\"ENSP00000440937.1\"},{\"transcript\":\"ENST00000229281.5\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000229281.5:c.230-75A>G\",\"isCanonical\":true,\"proteinId\":\"ENSP00000229281.5\"},{\"transcript\":\"ENST00000540506.2\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000540506.2:c.125-75A>G\",\"proteinId\":\"ENSP00000475635.1\"},{\"transcript\":\"ENST00000543115.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000443393.1\"},{\"transcript\":\"ENST00000542848.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000444805.1\"},{\"transcript\":\"ENST00000543120.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000399448.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000382376.1\"},{\"transcript\":\"ENST00000534900.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000447931.2\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000415979.2\"},{\"transcript\":\"ENST00000538318.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]}]}}]}")] - - [InlineData("chr12 7073931 . T 100 PASS SVTYPE=INV;END=7074100 . .", - "{\"chromosome\":\"chr12\",\"position\":7073931,\"svEnd\":7074100,\"refAllele\":\"T\",\"altAlleles\":[\"\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"clingen\":[{\"chromosome\":\"12\",\"begin\":173786,\"end\":34835837,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995956\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Decreased calvarial ossification\",\"Delayed gross motor development\",\"Feeding difficulties\",\"Frontal bossing\",\"Morphological abnormality of the central nervous system\",\"Patchy alopecia\"],\"phenotypeIds\":[\"HP:0002007\",\"HP:0002011\",\"HP:0002194\",\"HP:0002232\",\"HP:0005474\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:C1862862\",\"MedGen:CN001816\",\"MedGen:CN001820\",\"MedGen:CN001989\",\"MedGen:CN004852\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":7425202,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532325\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00002},{\"chromosome\":\"12\",\"begin\":282465,\"end\":8514342,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532326\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00002},{\"chromosome\":\"12\",\"begin\":282465,\"end\":25623263,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532324\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of cardiac morphology\",\"Agenesis of corpus callosum\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"phenotypeIds\":[\"HP:0001274\",\"HP:0001627\",\"MedGen:C1837248\",\"MedGen:CN001482\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":282465,\"end\":28568117,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531493\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34533111,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532323\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":2,\"phenotypes\":[\"Coarse facial features\",\"Abnormal facial shape\",\"Abnormality of cardiac morphology\",\"Cleft upper lip\",\"Global developmental delay\",\"Hearing impairment\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000280\",\"MedGen:C1854600\",\"HP:0000204\",\"HP:0000365\",\"HP:0001263\",\"HP:0001627\",\"HP:0001999\",\"HP:0004322\",\"MedGen:C0349588\",\"MedGen:C1384666\",\"MedGen:CN000197\",\"MedGen:CN001157\",\"MedGen:CN001482\",\"MedGen:CN001810\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756196,\"variantType\":\"copy_number_gain\",\"id\":\"nsv916406\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Ambiguous genitalia\",\"Delayed fine motor development\",\"Delayed gross motor development\",\"Delayed speech and language development\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Intellectual disability\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000062\",\"HP:0000750\",\"HP:0001249\",\"HP:0002194\",\"HP:0004322\",\"HP:0010862\",\"MedGen:C0349588\",\"MedGen:C1843367\",\"MedGen:CN000062\",\"MedGen:CN000706\",\"MedGen:CN001989\",\"MedGen:CN116596\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756209,\"variantType\":\"copy_number_gain\",\"id\":\"nsv533931\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34761006,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917315\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":133773393,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917029\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of toe\",\"Defect in the atrial septum\",\"Downslanted palpebral fissures\",\"Frontal bossing\",\"Low-set ears\",\"Overlapping fingers\",\"Patent ductus arteriosus\",\"Sacral dimple\",\"Sandal gap\",\"Single transverse palmar crease\"],\"phenotypeIds\":[\"HP:0000369\",\"HP:0000494\",\"HP:0000954\",\"HP:0000960\",\"HP:0001631\",\"HP:0001643\",\"HP:0001780\",\"HP:0001852\",\"HP:0002007\",\"HP:0010557\",\"MedGen:C0426848\",\"MedGen:C1865016\",\"MedGen:C1873502\",\"MedGen:CN000345\",\"MedGen:CN001485\",\"MedGen:CN001496\",\"MedGen:CN001615\",\"MedGen:CN001674\",\"MedGen:CN001816\",\"MedGen:CN009386\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":322142,\"end\":34079848,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532328\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":1367440,\"end\":20810511,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995558\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Feeding difficulties\",\"Laryngomalacia\"],\"phenotypeIds\":[\"HP:0001601\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:CN001457\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":2980907,\"end\":15140282,\"variantType\":\"copy_number_gain\",\"id\":\"nsv868869\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":6837831,\"end\":7858216,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531496\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00017}],\"dgv\":[{\"chromosome\":\"12\",\"begin\":6985480,\"end\":7103003,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1035811\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.00144},{\"chromosome\":\"12\",\"begin\":7005694,\"end\":7115157,\"variantType\":\"insertion\",\"variantFreqAll\":0.25,\"id\":\"nsv509453\",\"sampleSize\":4,\"observedGains\":1,\"reciprocalOverlap\":0.00154},{\"chromosome\":\"12\",\"begin\":7012055,\"end\":7163058,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1047373\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.00112}],\"variants\":[{\"vid\":\"12:7073932:7074100:Inverse\",\"chromosome\":\"chr12\",\"begin\":7073932,\"end\":7074100,\"refAllele\":\"T\",\"altAllele\":\"inversion\",\"variantType\":\"inversion\",\"overlappingGenes\":[\"EMG1\"],\"transcripts\":{\"ensembl\":[{\"transcript\":\"ENST00000607161.1\",\"bioType\":\"processed_transcript\",\"introns\":\"1/5\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_variant\"]}]}}]}")] - [InlineData("chr12 7040534 . ACATA A 100 PASS . . .", - "{\"chromosome\":\"chr12\",\"position\":7040534,\"refAllele\":\"ACATA\",\"altAlleles\":[\"A\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7040535:7040538\",\"chromosome\":\"chr12\",\"begin\":7040535,\"end\":7040538,\"refAllele\":\"CATA\",\"altAllele\":\"-\",\"variantType\":\"deletion\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001007026.1:c.-162-2468_-162-2465delCATA\",\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"XM_005253672.1:c.-162-2468_-162-2465delCATA\",\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001940.3:c.-162-2468_-162-2465delCATA\",\"proteinId\":\"NP_001931.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000356654.4:c.-162-2468_-162-2465delCATA\",\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000396684.2:c.-162-2468_-162-2465delCATA\",\"proteinId\":\"ENSP00000379915.2\"}]}}]}")] - [InlineData("chr12 7045274 . T 100 PASS SVTYPE=DEL;END=7084024 . .", "{\"chromosome\":\"chr12\",\"position\":7045274,\"svEnd\":7084024,\"refAllele\":\"T\",\"altAlleles\":[\"\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"clingen\":[{\"chromosome\":\"12\",\"begin\":147099,\"end\":7054359,\"variantType\":\"copy_number_gain\",\"id\":\"nsv498529\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00132},{\"chromosome\":\"12\",\"begin\":173786,\"end\":34835837,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995956\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Decreased calvarial ossification\",\"Delayed gross motor development\",\"Feeding difficulties\",\"Frontal bossing\",\"Morphological abnormality of the central nervous system\",\"Patchy alopecia\"],\"phenotypeIds\":[\"HP:0002007\",\"HP:0002011\",\"HP:0002194\",\"HP:0002232\",\"HP:0005474\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:C1862862\",\"MedGen:CN001816\",\"MedGen:CN001820\",\"MedGen:CN001989\",\"MedGen:CN004852\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":7425202,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532325\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00543},{\"chromosome\":\"12\",\"begin\":282465,\"end\":8514342,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532326\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00471},{\"chromosome\":\"12\",\"begin\":282465,\"end\":25623263,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532324\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of cardiac morphology\",\"Agenesis of corpus callosum\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"phenotypeIds\":[\"HP:0001274\",\"HP:0001627\",\"MedGen:C1837248\",\"MedGen:CN001482\"],\"reciprocalOverlap\":0.00153},{\"chromosome\":\"12\",\"begin\":282465,\"end\":28568117,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531493\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00137},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34533111,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532323\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":2,\"phenotypes\":[\"Coarse facial features\",\"Abnormal facial shape\",\"Abnormality of cardiac morphology\",\"Cleft upper lip\",\"Global developmental delay\",\"Hearing impairment\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000280\",\"MedGen:C1854600\",\"HP:0000204\",\"HP:0000365\",\"HP:0001263\",\"HP:0001627\",\"HP:0001999\",\"HP:0004322\",\"MedGen:C0349588\",\"MedGen:C1384666\",\"MedGen:CN000197\",\"MedGen:CN001157\",\"MedGen:CN001482\",\"MedGen:CN001810\"],\"reciprocalOverlap\":0.00113},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756196,\"variantType\":\"copy_number_gain\",\"id\":\"nsv916406\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Ambiguous genitalia\",\"Delayed fine motor development\",\"Delayed gross motor development\",\"Delayed speech and language development\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Intellectual disability\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000062\",\"HP:0000750\",\"HP:0001249\",\"HP:0002194\",\"HP:0004322\",\"HP:0010862\",\"MedGen:C0349588\",\"MedGen:C1843367\",\"MedGen:CN000062\",\"MedGen:CN000706\",\"MedGen:CN001989\",\"MedGen:CN116596\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756209,\"variantType\":\"copy_number_gain\",\"id\":\"nsv533931\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34761006,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917315\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":133773393,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917029\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of toe\",\"Defect in the atrial septum\",\"Downslanted palpebral fissures\",\"Frontal bossing\",\"Low-set ears\",\"Overlapping fingers\",\"Patent ductus arteriosus\",\"Sacral dimple\",\"Sandal gap\",\"Single transverse palmar crease\"],\"phenotypeIds\":[\"HP:0000369\",\"HP:0000494\",\"HP:0000954\",\"HP:0000960\",\"HP:0001631\",\"HP:0001643\",\"HP:0001780\",\"HP:0001852\",\"HP:0002007\",\"HP:0010557\",\"MedGen:C0426848\",\"MedGen:C1865016\",\"MedGen:C1873502\",\"MedGen:CN000345\",\"MedGen:CN001485\",\"MedGen:CN001496\",\"MedGen:CN001615\",\"MedGen:CN001674\",\"MedGen:CN001816\",\"MedGen:CN009386\"],\"reciprocalOverlap\":0.00029},{\"chromosome\":\"12\",\"begin\":322142,\"end\":34079848,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532328\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00115},{\"chromosome\":\"12\",\"begin\":1367440,\"end\":20810511,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995558\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Feeding difficulties\",\"Laryngomalacia\"],\"phenotypeIds\":[\"HP:0001601\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:CN001457\"],\"reciprocalOverlap\":0.00199},{\"chromosome\":\"12\",\"begin\":2980907,\"end\":15140282,\"variantType\":\"copy_number_gain\",\"id\":\"nsv868869\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00319},{\"chromosome\":\"12\",\"begin\":6837831,\"end\":7858216,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531496\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.03798}],\"dgv\":[{\"chromosome\":\"12\",\"begin\":6985480,\"end\":7103003,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1035811\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.32972},{\"chromosome\":\"12\",\"begin\":7005694,\"end\":7115157,\"variantType\":\"insertion\",\"variantFreqAll\":0.25,\"id\":\"nsv509453\",\"sampleSize\":4,\"observedGains\":1,\"reciprocalOverlap\":0.354},{\"chromosome\":\"12\",\"begin\":7012055,\"end\":7163058,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1047373\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.25662},{\"chromosome\":\"12\",\"begin\":7053085,\"end\":7063682,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00006,\"id\":\"nsv557262\",\"sampleSize\":17421,\"observedGains\":1,\"reciprocalOverlap\":0.2735},{\"chromosome\":\"12\",\"begin\":7054324,\"end\":7054491,\"variantType\":\"copy_number_gain\",\"id\":\"esv5830\",\"sampleSize\":1,\"reciprocalOverlap\":0.00434},{\"chromosome\":\"12\",\"begin\":7054931,\"end\":7065635,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00011,\"id\":\"dgv2324n54\",\"sampleSize\":17421,\"observedLosses\":2,\"reciprocalOverlap\":0.27626},{\"chromosome\":\"12\",\"begin\":7055492,\"end\":7070110,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00017,\"id\":\"dgv2325n54\",\"sampleSize\":17421,\"observedLosses\":3,\"reciprocalOverlap\":0.37726},{\"chromosome\":\"12\",\"begin\":7058967,\"end\":7059349,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00541,\"id\":\"esv3356433\",\"sampleSize\":185,\"observedGains\":1,\"reciprocalOverlap\":0.00988},{\"chromosome\":\"12\",\"begin\":7059100,\"end\":7059327,\"variantType\":\"insertion\",\"variantFreqAll\":1,\"id\":\"nsv513351\",\"sampleSize\":1,\"observedGains\":1,\"reciprocalOverlap\":0.00588},{\"chromosome\":\"12\",\"begin\":7059146,\"end\":7059890,\"variantType\":\"insertion\",\"variantFreqAll\":0.33333,\"id\":\"esv994245\",\"sampleSize\":3,\"observedGains\":1,\"reciprocalOverlap\":0.01923},{\"chromosome\":\"12\",\"begin\":7059221,\"end\":7059221,\"variantType\":\"insertion\",\"variantFreqAll\":0.01622,\"id\":\"esv3381477\",\"sampleSize\":185,\"observedGains\":3},{\"chromosome\":\"12\",\"begin\":7060846,\"end\":7070110,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00011,\"id\":\"dgv2326n54\",\"sampleSize\":17421,\"observedLosses\":2,\"reciprocalOverlap\":0.2391},{\"chromosome\":\"12\",\"begin\":7063701,\"end\":7070500,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":1,\"id\":\"nsv952791\",\"sampleSize\":1,\"observedLosses\":1,\"reciprocalOverlap\":0.17548}],\"variants\":[{\"vid\":\"12:7045275:7084024\",\"chromosome\":\"chr12\",\"begin\":7045275,\"end\":7084024,\"refAllele\":\"T\",\"altAllele\":\"deletion\",\"variantType\":\"deletion\",\"overlappingGenes\":[\"ATN1\",\"C12orf57\",\"U47924.31\",\"RNU7-1\",\"PTPN6\",\"EMG1\",\"LOC105369635\",\"MIR200C\",\"MIR141\",\"U47924.29\",\"PHB2\",\"SCARNA12\"],\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001007026.1}:c.1_844_EMG1{NM_006331.7}:c.411+170_734\",\"intron\":3}]},\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{XM_005253672.1}:c.1_841_EMG1{NM_006331.7}:c.411+170_734\",\"intron\":3}]},\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001940.3}:c.1_844_EMG1{NM_006331.7}:c.411+170_734\",\"intron\":3}]},\"proteinId\":\"NP_001931.2\"},{\"transcript\":\"NM_006331.7\",\"bioType\":\"protein_coding\",\"exons\":\"1-3/6\",\"introns\":\"1-3/5\",\"geneId\":\"10436\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"intron\":3,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001007026.1}:c.1_844_EMG1{NM_006331.7}:c.411+170_734\",\"exon\":5},{\"hgvsc\":\"ATN1{XM_005253672.1}:c.1_841_EMG1{NM_006331.7}:c.411+170_734\",\"exon\":5},{\"hgvsc\":\"ATN1{NM_001940.3}:c.1_844_EMG1{NM_006331.7}:c.411+170_734\",\"exon\":5}]},\"isCanonical\":true,\"proteinId\":\"NP_006322.4\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{ENST00000356654.4}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"intron\":4}]},\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{ENST00000396684.2}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"intron\":4}]},\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000541029.1\",\"bioType\":\"retained_intron\",\"exons\":\"1-2/2\",\"introns\":\"1/1\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_ablation\"]},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"exons\":\"1-3/3\",\"introns\":\"1-2/2\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_ablation\"]},{\"transcript\":\"ENST00000607161.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/6\",\"introns\":\"1-3/5\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000261406.6\",\"bioType\":\"protein_coding\",\"exons\":\"1-4/7\",\"introns\":\"1-4/6\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"intron\":4,\"fusions\":[{\"hgvsc\":\"ATN1{ENST00000356654.4}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"exon\":5},{\"hgvsc\":\"ATN1{ENST00000396684.2}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"exon\":5}]},\"isCanonical\":true,\"proteinId\":\"ENSP00000476966.1\"},{\"transcript\":\"ENST00000546220.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/6\",\"introns\":\"1-3/5\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000539440.1\",\"bioType\":\"retained_intron\",\"exons\":\"1-3/4\",\"introns\":\"1-2/3\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000564245.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/8\",\"introns\":\"1-3/7\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000451846.2\",\"bioType\":\"retained_intron\",\"exons\":\"1-2/2\",\"introns\":\"1/1\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000539535.2\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/8\",\"introns\":\"1-3/7\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000541016.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/5\",\"introns\":\"1-3/4\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000539196.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/5\",\"introns\":\"1-3/4\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]}]}}]}")] - [InlineData("chr12 7067124 . GGCC ATTG 100 PASS . . .", "{\"chromosome\":\"chr12\",\"position\":7067124,\"refAllele\":\"GGCC\",\"altAlleles\":[\"ATTG\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7067124:7067127:ATTG\",\"chromosome\":\"chr12\",\"begin\":7067124,\"end\":7067127,\"refAllele\":\"GGCC\",\"altAllele\":\"ATTG\",\"variantType\":\"MNV\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_080548.4\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1404-1407\",\"cdsPos\":\"1255-1258\",\"exons\":\"11/16\",\"proteinPos\":\"419-420\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_080548.4:c.1255_1258delGGCCinsATTG\",\"hgvsp\":\"NP_536858.1:p.(Gly419_Pro420delinsIleAla)\",\"proteinId\":\"NP_536858.1\"},{\"transcript\":\"XM_005253719.1\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1248-1251\",\"cdsPos\":\"1132-1135\",\"exons\":\"10/15\",\"proteinPos\":\"378-379\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"XM_005253719.1:c.1132_1135delGGCCinsATTG\",\"hgvsp\":\"XP_005253776.1:p.(Gly378_Pro379delinsIleAla)\",\"proteinId\":\"XP_005253776.1\"},{\"transcript\":\"NM_080549.3\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1491-1494\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_080549.3:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"NP_536859.1:p.(Gly417_Pro418delinsIleAla)\",\"isCanonical\":true,\"proteinId\":\"NP_536859.1\"},{\"transcript\":\"NM_002831.5\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1491-1494\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_002831.5:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"NP_002822.2:p.(Gly417_Pro418delinsIleAla)\",\"proteinId\":\"NP_002822.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000542848.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000444805.1\"},{\"transcript\":\"ENST00000543120.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000399448.1\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1404-1407\",\"cdsPos\":\"1255-1258\",\"exons\":\"11/16\",\"proteinPos\":\"419-420\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000399448.1:c.1255_1258delGGCCinsATTG\",\"hgvsp\":\"ENSP00000382376.1:p.(Gly419_Pro420delinsIleAla)\",\"proteinId\":\"ENSP00000382376.1\"},{\"transcript\":\"ENST00000534900.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000447931.2\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1248-1251\",\"cdsPos\":\"1132-1135\",\"exons\":\"10/15\",\"proteinPos\":\"378-379\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000447931.2:c.1132_1135delGGCCinsATTG\",\"hgvsp\":\"ENSP00000415979.2:p.(Gly378_Pro379delinsIleAla)\",\"proteinId\":\"ENSP00000415979.2\"},{\"transcript\":\"ENST00000538318.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000538715.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000438740.1\"},{\"transcript\":\"ENST00000318974.9\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1493-1496\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000318974.9:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"ENSP00000326010.9:p.(Gly417_Pro418delinsIleAla)\",\"proteinId\":\"ENSP00000326010.9\"},{\"transcript\":\"ENST00000456013.1\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1491-1494\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000456013.1:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"ENSP00000391592.1:p.(Gly417_Pro418delinsIleAla)\",\"isCanonical\":true,\"proteinId\":\"ENSP00000391592.1\"},{\"transcript\":\"ENST00000543744.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000540740.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000416215.2\",\"bioType\":\"retained_intron\",\"cdnaPos\":\"1657-1660\",\"exons\":\"10/15\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"non_coding_transcript_exon_variant\",\"non_coding_transcript_variant\"],\"hgvsc\":\"ENST00000416215.2:n.1657_1660delGGCCinsATTG\"},{\"transcript\":\"ENST00000545153.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000476175.1\"},{\"transcript\":\"ENST00000535462.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000441044.1\"},{\"transcript\":\"ENST00000541698.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000445646.1\"},{\"transcript\":\"ENST00000542462.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000440114.1\"},{\"transcript\":\"ENST00000542277.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000536013.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000446345.1\"},{\"transcript\":\"ENST00000539365.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000539029.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000542761.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000537533.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]}]}}]}")] - [InlineData("chr12 7033330 . T 100 PASS SVTYPE=INS;END=7033330;SVLEN=1350 . .", "{\"chromosome\":\"chr12\",\"position\":7033330,\"svEnd\":7033330,\"refAllele\":\"T\",\"altAlleles\":[\"\"],\"quality\":100,\"filters\":[\"PASS\"],\"svLength\":1350,\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"clingen\":[{\"chromosome\":\"12\",\"begin\":147099,\"end\":7054359,\"variantType\":\"copy_number_gain\",\"id\":\"nsv498529\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":173786,\"end\":34835837,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995956\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Decreased calvarial ossification\",\"Delayed gross motor development\",\"Feeding difficulties\",\"Frontal bossing\",\"Morphological abnormality of the central nervous system\",\"Patchy alopecia\"],\"phenotypeIds\":[\"HP:0002007\",\"HP:0002011\",\"HP:0002194\",\"HP:0002232\",\"HP:0005474\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:C1862862\",\"MedGen:CN001816\",\"MedGen:CN001820\",\"MedGen:CN001989\",\"MedGen:CN004852\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":7425202,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532325\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":8514342,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532326\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":25623263,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532324\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of cardiac morphology\",\"Agenesis of corpus callosum\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"phenotypeIds\":[\"HP:0001274\",\"HP:0001627\",\"MedGen:C1837248\",\"MedGen:CN001482\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":28568117,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531493\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34533111,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532323\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":2,\"phenotypes\":[\"Coarse facial features\",\"Abnormal facial shape\",\"Abnormality of cardiac morphology\",\"Cleft upper lip\",\"Global developmental delay\",\"Hearing impairment\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000280\",\"MedGen:C1854600\",\"HP:0000204\",\"HP:0000365\",\"HP:0001263\",\"HP:0001627\",\"HP:0001999\",\"HP:0004322\",\"MedGen:C0349588\",\"MedGen:C1384666\",\"MedGen:CN000197\",\"MedGen:CN001157\",\"MedGen:CN001482\",\"MedGen:CN001810\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756196,\"variantType\":\"copy_number_gain\",\"id\":\"nsv916406\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Ambiguous genitalia\",\"Delayed fine motor development\",\"Delayed gross motor development\",\"Delayed speech and language development\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Intellectual disability\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000062\",\"HP:0000750\",\"HP:0001249\",\"HP:0002194\",\"HP:0004322\",\"HP:0010862\",\"MedGen:C0349588\",\"MedGen:C1843367\",\"MedGen:CN000062\",\"MedGen:CN000706\",\"MedGen:CN001989\",\"MedGen:CN116596\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756209,\"variantType\":\"copy_number_gain\",\"id\":\"nsv533931\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34761006,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917315\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":133773393,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917029\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of toe\",\"Defect in the atrial septum\",\"Downslanted palpebral fissures\",\"Frontal bossing\",\"Low-set ears\",\"Overlapping fingers\",\"Patent ductus arteriosus\",\"Sacral dimple\",\"Sandal gap\",\"Single transverse palmar crease\"],\"phenotypeIds\":[\"HP:0000369\",\"HP:0000494\",\"HP:0000954\",\"HP:0000960\",\"HP:0001631\",\"HP:0001643\",\"HP:0001780\",\"HP:0001852\",\"HP:0002007\",\"HP:0010557\",\"MedGen:C0426848\",\"MedGen:C1865016\",\"MedGen:C1873502\",\"MedGen:CN000345\",\"MedGen:CN001485\",\"MedGen:CN001496\",\"MedGen:CN001615\",\"MedGen:CN001674\",\"MedGen:CN001816\",\"MedGen:CN009386\"]},{\"chromosome\":\"12\",\"begin\":322142,\"end\":34079848,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532328\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":1367440,\"end\":20810511,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995558\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Feeding difficulties\",\"Laryngomalacia\"],\"phenotypeIds\":[\"HP:0001601\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:CN001457\"]},{\"chromosome\":\"12\",\"begin\":2980907,\"end\":15140282,\"variantType\":\"copy_number_gain\",\"id\":\"nsv868869\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":6837831,\"end\":7858216,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531496\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]}],\"dgv\":[{\"chromosome\":\"12\",\"begin\":6889463,\"end\":7041469,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.02105,\"id\":\"nsv832324\",\"sampleSize\":95,\"observedLosses\":2},{\"chromosome\":\"12\",\"begin\":6948468,\"end\":7033823,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00006,\"id\":\"nsv557261\",\"sampleSize\":17421,\"observedLosses\":1},{\"chromosome\":\"12\",\"begin\":6985480,\"end\":7103003,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1035811\",\"sampleSize\":29084,\"observedGains\":1},{\"chromosome\":\"12\",\"begin\":7005694,\"end\":7115157,\"variantType\":\"insertion\",\"variantFreqAll\":0.25,\"id\":\"nsv509453\",\"sampleSize\":4,\"observedGains\":1},{\"chromosome\":\"12\",\"begin\":7012055,\"end\":7163058,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1047373\",\"sampleSize\":29084,\"observedGains\":1}],\"variants\":[{\"vid\":\"12:7033331:7033330:INS\",\"chromosome\":\"chr12\",\"begin\":7033331,\"end\":7033330,\"refAllele\":\"T\",\"altAllele\":\"insertion\",\"variantType\":\"insertion\"}]}")] + [InlineData("chr12 7048190 . G A 322 PASS SB=0.1234567 . .", "{\"chromosome\":\"chr12\",\"position\":7048190,\"refAllele\":\"G\",\"altAlleles\":[\"A\"],\"quality\":322,\"filters\":[\"PASS\"],\"strandBias\":0.123457,\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7048190:A\",\"chromosome\":\"chr12\",\"begin\":7048190,\"end\":7048190,\"refAllele\":\"G\",\"altAllele\":\"A\",\"variantType\":\"SNV\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3301\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_001007026.1:c.3064G>A\",\"hgvsp\":\"NP_001007027.1:p.(Ala1022Thr)\",\"isCanonical\":true,\"polyPhenScore\":0.36,\"polyPhenPrediction\":\"benign\",\"proteinId\":\"NP_001007027.1\",\"siftScore\":0.1,\"siftPrediction\":\"tolerated - low confidence\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3391\",\"cdsPos\":\"3061\",\"exons\":\"7/10\",\"proteinPos\":\"1021\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"XM_005253672.1:c.3061G>A\",\"hgvsp\":\"XP_005253729.1:p.(Ala1021Thr)\",\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3294\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_001940.3:c.3064G>A\",\"hgvsp\":\"NP_001931.2:p.(Ala1022Thr)\",\"polyPhenScore\":0.36,\"polyPhenPrediction\":\"benign\",\"proteinId\":\"NP_001931.2\",\"siftScore\":0.1,\"siftPrediction\":\"tolerated - low confidence\"},{\"transcript\":\"XM_005253669.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"XP_005253726.1\"},{\"transcript\":\"NM_001301836.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"NP_001288765.1\"},{\"transcript\":\"NM_001301834.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"NP_001288763.1\"},{\"transcript\":\"XM_005253670.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"XP_005253727.1\"},{\"transcript\":\"NR_023317.1\",\"bioType\":\"snRNA\",\"geneId\":\"100147744\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"NM_001301838.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"NP_001288767.1\"},{\"transcript\":\"NM_001301837.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"NP_001288766.1\"},{\"transcript\":\"NR_126035.1\",\"bioType\":\"misc_RNA\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"NM_138425.3\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"NP_612434.1\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3301\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000356654.4:c.3064G>A\",\"hgvsp\":\"ENSP00000349076.3:p.(Ala1022Thr)\",\"isCanonical\":true,\"polyPhenScore\":0.36,\"polyPhenPrediction\":\"benign\",\"proteinId\":\"ENSP00000349076.3\",\"siftScore\":0.1,\"siftPrediction\":\"tolerated - low confidence\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"codons\":\"Gca/Aca\",\"aminoAcids\":\"A/T\",\"cdnaPos\":\"3298\",\"cdsPos\":\"3064\",\"exons\":\"7/10\",\"proteinPos\":\"1022\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000396684.2:c.3064G>A\",\"hgvsp\":\"ENSP00000379915.2:p.(Ala1022Thr)\",\"polyPhenScore\":0.36,\"polyPhenPrediction\":\"benign\",\"proteinId\":\"ENSP00000379915.2\",\"siftScore\":0.1,\"siftPrediction\":\"tolerated - low confidence\"},{\"transcript\":\"ENST00000541029.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000538392.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000542222.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000545581.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000440602.1\"},{\"transcript\":\"ENST00000607421.1\",\"bioType\":\"antisense\",\"geneId\":\"ENSG00000272173\",\"hgnc\":\"U47924.2\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000458811.1\",\"bioType\":\"snRNA\",\"geneId\":\"ENSG00000238923\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000544681.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000475422.1\"},{\"transcript\":\"ENST00000537087.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000440937.1\"},{\"transcript\":\"ENST00000229281.5\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"ENSP00000229281.5\"}]}}]}")] + [InlineData("chr12 7054859 . G . 100 PASS . . .", "{\"chromosome\":\"chr12\",\"position\":7054859,\"refAllele\":\"G\",\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7054859:G\",\"chromosome\":\"chr12\",\"begin\":7054859,\"end\":7054859,\"isReferenceMinorAllele\":true,\"refAllele\":\"G\",\"variantType\":\"SNV\",\"globalAllele\":{\"globalMinorAllele\":\"G\",\"globalMinorAlleleFrequency\":0.003794},\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"NP_001931.2\"},{\"transcript\":\"XM_005253669.1\",\"bioType\":\"protein_coding\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"XP_005253726.1\"},{\"transcript\":\"NM_001301836.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001301836.1:c.191-75A>G\",\"proteinId\":\"NP_001288765.1\"},{\"transcript\":\"NM_001301834.1\",\"bioType\":\"protein_coding\",\"introns\":\"3/3\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001301834.1:c.230-75A>G\",\"isCanonical\":true,\"proteinId\":\"NP_001288763.1\"},{\"transcript\":\"XM_005253670.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"XM_005253670.1:c.191-75A>G\",\"proteinId\":\"XP_005253727.1\"},{\"transcript\":\"NR_023317.1\",\"bioType\":\"snRNA\",\"geneId\":\"100147744\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"NM_001301838.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001301838.1:c.125-75A>G\",\"proteinId\":\"NP_001288767.1\"},{\"transcript\":\"NM_001301837.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001301837.1:c.143-75A>G\",\"proteinId\":\"NP_001288766.1\"},{\"transcript\":\"NR_126035.1\",\"bioType\":\"misc_RNA\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\",\"non_coding_transcript_variant\"],\"hgvsc\":\"NR_126035.1:n.544-75A>G\"},{\"transcript\":\"NM_138425.3\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_138425.3:c.230-75A>G\",\"proteinId\":\"NP_612434.1\"},{\"transcript\":\"NM_138425.2\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"113246\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_138425.2:c.230-75A>G\",\"proteinId\":\"NP_612434.1\"},{\"transcript\":\"NM_080548.4\",\"bioType\":\"protein_coding\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"NP_536858.1\"},{\"transcript\":\"XM_005253719.1\",\"bioType\":\"protein_coding\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"XP_005253776.1\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000538392.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000542222.1\",\"bioType\":\"processed_transcript\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\",\"non_coding_transcript_variant\"],\"hgvsc\":\"ENST00000542222.1:n.408-75A>G\"},{\"transcript\":\"ENST00000545581.1\",\"bioType\":\"protein_coding\",\"introns\":\"3/3\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000545581.1:c.230-75A>G\",\"proteinId\":\"ENSP00000440602.1\"},{\"transcript\":\"ENST00000607421.1\",\"bioType\":\"antisense\",\"geneId\":\"ENSG00000272173\",\"hgnc\":\"U47924.2\",\"consequence\":[\"upstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000458811.1\",\"bioType\":\"snRNA\",\"geneId\":\"ENSG00000238923\",\"hgnc\":\"RNU7-1\",\"consequence\":[\"downstream_gene_variant\"],\"isCanonical\":true},{\"transcript\":\"ENST00000544681.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000475422.1\"},{\"transcript\":\"ENST00000537087.1\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000537087.1:c.143-75A>G\",\"proteinId\":\"ENSP00000440937.1\"},{\"transcript\":\"ENST00000229281.5\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000229281.5:c.230-75A>G\",\"isCanonical\":true,\"proteinId\":\"ENSP00000229281.5\"},{\"transcript\":\"ENST00000540506.2\",\"bioType\":\"protein_coding\",\"introns\":\"2/2\",\"geneId\":\"ENSG00000111678\",\"hgnc\":\"C12orf57\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000540506.2:c.125-75A>G\",\"proteinId\":\"ENSP00000475635.1\"},{\"transcript\":\"ENST00000543115.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000443393.1\"},{\"transcript\":\"ENST00000542848.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000444805.1\"},{\"transcript\":\"ENST00000543120.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000399448.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000382376.1\"},{\"transcript\":\"ENST00000534900.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000447931.2\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"],\"proteinId\":\"ENSP00000415979.2\"},{\"transcript\":\"ENST00000538318.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]}]}}]}")] + [InlineData("chr12 7073931 . T 100 PASS SVTYPE=INV;END=7074100 . .", "{\"chromosome\":\"chr12\",\"position\":7073931,\"svEnd\":7074100,\"refAllele\":\"T\",\"altAlleles\":[\"\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"clingen\":[{\"chromosome\":\"12\",\"begin\":173786,\"end\":34835837,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995956\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Decreased calvarial ossification\",\"Delayed gross motor development\",\"Feeding difficulties\",\"Frontal bossing\",\"Morphological abnormality of the central nervous system\",\"Patchy alopecia\"],\"phenotypeIds\":[\"HP:0002007\",\"HP:0002011\",\"HP:0002194\",\"HP:0002232\",\"HP:0005474\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:C1862862\",\"MedGen:CN001816\",\"MedGen:CN001820\",\"MedGen:CN001989\",\"MedGen:CN004852\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":7425202,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532325\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00002},{\"chromosome\":\"12\",\"begin\":282465,\"end\":8514342,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532326\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00002},{\"chromosome\":\"12\",\"begin\":282465,\"end\":25623263,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532324\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of cardiac morphology\",\"Agenesis of corpus callosum\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"phenotypeIds\":[\"HP:0001274\",\"HP:0001627\",\"MedGen:C1837248\",\"MedGen:CN001482\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":282465,\"end\":28568117,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531493\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34533111,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532323\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":2,\"phenotypes\":[\"Coarse facial features\",\"Abnormal facial shape\",\"Abnormality of cardiac morphology\",\"Cleft upper lip\",\"Global developmental delay\",\"Hearing impairment\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000280\",\"MedGen:C1854600\",\"HP:0000204\",\"HP:0000365\",\"HP:0001263\",\"HP:0001627\",\"HP:0001999\",\"HP:0004322\",\"MedGen:C0349588\",\"MedGen:C1384666\",\"MedGen:CN000197\",\"MedGen:CN001157\",\"MedGen:CN001482\",\"MedGen:CN001810\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756196,\"variantType\":\"copy_number_gain\",\"id\":\"nsv916406\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Ambiguous genitalia\",\"Delayed fine motor development\",\"Delayed gross motor development\",\"Delayed speech and language development\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Intellectual disability\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000062\",\"HP:0000750\",\"HP:0001249\",\"HP:0002194\",\"HP:0004322\",\"HP:0010862\",\"MedGen:C0349588\",\"MedGen:C1843367\",\"MedGen:CN000062\",\"MedGen:CN000706\",\"MedGen:CN001989\",\"MedGen:CN116596\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756209,\"variantType\":\"copy_number_gain\",\"id\":\"nsv533931\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34761006,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917315\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":282465,\"end\":133773393,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917029\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of toe\",\"Defect in the atrial septum\",\"Downslanted palpebral fissures\",\"Frontal bossing\",\"Low-set ears\",\"Overlapping fingers\",\"Patent ductus arteriosus\",\"Sacral dimple\",\"Sandal gap\",\"Single transverse palmar crease\"],\"phenotypeIds\":[\"HP:0000369\",\"HP:0000494\",\"HP:0000954\",\"HP:0000960\",\"HP:0001631\",\"HP:0001643\",\"HP:0001780\",\"HP:0001852\",\"HP:0002007\",\"HP:0010557\",\"MedGen:C0426848\",\"MedGen:C1865016\",\"MedGen:C1873502\",\"MedGen:CN000345\",\"MedGen:CN001485\",\"MedGen:CN001496\",\"MedGen:CN001615\",\"MedGen:CN001674\",\"MedGen:CN001816\",\"MedGen:CN009386\"],\"reciprocalOverlap\":0},{\"chromosome\":\"12\",\"begin\":322142,\"end\":34079848,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532328\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":1367440,\"end\":20810511,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995558\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Feeding difficulties\",\"Laryngomalacia\"],\"phenotypeIds\":[\"HP:0001601\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:CN001457\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":2980907,\"end\":15140282,\"variantType\":\"copy_number_gain\",\"id\":\"nsv868869\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00001},{\"chromosome\":\"12\",\"begin\":6837831,\"end\":7858216,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531496\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00017}],\"dgv\":[{\"chromosome\":\"12\",\"begin\":6985480,\"end\":7103003,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1035811\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.00144},{\"chromosome\":\"12\",\"begin\":7005694,\"end\":7115157,\"variantType\":\"insertion\",\"variantFreqAll\":0.25,\"id\":\"nsv509453\",\"sampleSize\":4,\"observedGains\":1,\"reciprocalOverlap\":0.00154},{\"chromosome\":\"12\",\"begin\":7012055,\"end\":7163058,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1047373\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.00112}],\"variants\":[{\"vid\":\"12:7073932:7074100:Inverse\",\"chromosome\":\"chr12\",\"begin\":7073932,\"end\":7074100,\"refAllele\":\"T\",\"altAllele\":\"inversion\",\"variantType\":\"inversion\",\"overlappingGenes\":[\"EMG1\"],\"transcripts\":{\"ensembl\":[{\"transcript\":\"ENST00000607161.1\",\"bioType\":\"processed_transcript\",\"introns\":\"1/5\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_variant\"]}]}}]}")] + [InlineData("chr12 7040534 . ACATA A 100 PASS . . .", "{\"chromosome\":\"chr12\",\"position\":7040534,\"refAllele\":\"ACATA\",\"altAlleles\":[\"A\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7040535:7040538\",\"chromosome\":\"chr12\",\"begin\":7040535,\"end\":7040538,\"refAllele\":\"CATA\",\"altAllele\":\"-\",\"variantType\":\"deletion\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001007026.1:c.-162-2468_-162-2465delCATA\",\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"XM_005253672.1:c.-162-2468_-162-2465delCATA\",\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"NM_001940.3:c.-162-2468_-162-2465delCATA\",\"proteinId\":\"NP_001931.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000356654.4:c.-162-2468_-162-2465delCATA\",\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"introns\":\"1/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"intron_variant\"],\"hgvsc\":\"ENST00000396684.2:c.-162-2468_-162-2465delCATA\",\"proteinId\":\"ENSP00000379915.2\"}]}}]}")] + [InlineData("chr12 7045274 . T 100 PASS SVTYPE=DEL;END=7084024 . .", "{\"chromosome\":\"chr12\",\"position\":7045274,\"svEnd\":7084024,\"refAllele\":\"T\",\"altAlleles\":[\"\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"clingen\":[{\"chromosome\":\"12\",\"begin\":147099,\"end\":7054359,\"variantType\":\"copy_number_gain\",\"id\":\"nsv498529\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00132},{\"chromosome\":\"12\",\"begin\":173786,\"end\":34835837,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995956\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Decreased calvarial ossification\",\"Delayed gross motor development\",\"Feeding difficulties\",\"Frontal bossing\",\"Morphological abnormality of the central nervous system\",\"Patchy alopecia\"],\"phenotypeIds\":[\"HP:0002007\",\"HP:0002011\",\"HP:0002194\",\"HP:0002232\",\"HP:0005474\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:C1862862\",\"MedGen:CN001816\",\"MedGen:CN001820\",\"MedGen:CN001989\",\"MedGen:CN004852\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":7425202,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532325\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00543},{\"chromosome\":\"12\",\"begin\":282465,\"end\":8514342,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532326\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00471},{\"chromosome\":\"12\",\"begin\":282465,\"end\":25623263,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532324\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of cardiac morphology\",\"Agenesis of corpus callosum\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"phenotypeIds\":[\"HP:0001274\",\"HP:0001627\",\"MedGen:C1837248\",\"MedGen:CN001482\"],\"reciprocalOverlap\":0.00153},{\"chromosome\":\"12\",\"begin\":282465,\"end\":28568117,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531493\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"],\"reciprocalOverlap\":0.00137},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34533111,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532323\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":2,\"phenotypes\":[\"Coarse facial features\",\"Abnormal facial shape\",\"Abnormality of cardiac morphology\",\"Cleft upper lip\",\"Global developmental delay\",\"Hearing impairment\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000280\",\"MedGen:C1854600\",\"HP:0000204\",\"HP:0000365\",\"HP:0001263\",\"HP:0001627\",\"HP:0001999\",\"HP:0004322\",\"MedGen:C0349588\",\"MedGen:C1384666\",\"MedGen:CN000197\",\"MedGen:CN001157\",\"MedGen:CN001482\",\"MedGen:CN001810\"],\"reciprocalOverlap\":0.00113},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756196,\"variantType\":\"copy_number_gain\",\"id\":\"nsv916406\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Ambiguous genitalia\",\"Delayed fine motor development\",\"Delayed gross motor development\",\"Delayed speech and language development\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Intellectual disability\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000062\",\"HP:0000750\",\"HP:0001249\",\"HP:0002194\",\"HP:0004322\",\"HP:0010862\",\"MedGen:C0349588\",\"MedGen:C1843367\",\"MedGen:CN000062\",\"MedGen:CN000706\",\"MedGen:CN001989\",\"MedGen:CN116596\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756209,\"variantType\":\"copy_number_gain\",\"id\":\"nsv533931\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34761006,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917315\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00112},{\"chromosome\":\"12\",\"begin\":282465,\"end\":133773393,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917029\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of toe\",\"Defect in the atrial septum\",\"Downslanted palpebral fissures\",\"Frontal bossing\",\"Low-set ears\",\"Overlapping fingers\",\"Patent ductus arteriosus\",\"Sacral dimple\",\"Sandal gap\",\"Single transverse palmar crease\"],\"phenotypeIds\":[\"HP:0000369\",\"HP:0000494\",\"HP:0000954\",\"HP:0000960\",\"HP:0001631\",\"HP:0001643\",\"HP:0001780\",\"HP:0001852\",\"HP:0002007\",\"HP:0010557\",\"MedGen:C0426848\",\"MedGen:C1865016\",\"MedGen:C1873502\",\"MedGen:CN000345\",\"MedGen:CN001485\",\"MedGen:CN001496\",\"MedGen:CN001615\",\"MedGen:CN001674\",\"MedGen:CN001816\",\"MedGen:CN009386\"],\"reciprocalOverlap\":0.00029},{\"chromosome\":\"12\",\"begin\":322142,\"end\":34079848,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532328\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00115},{\"chromosome\":\"12\",\"begin\":1367440,\"end\":20810511,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995558\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Feeding difficulties\",\"Laryngomalacia\"],\"phenotypeIds\":[\"HP:0001601\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:CN001457\"],\"reciprocalOverlap\":0.00199},{\"chromosome\":\"12\",\"begin\":2980907,\"end\":15140282,\"variantType\":\"copy_number_gain\",\"id\":\"nsv868869\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.00319},{\"chromosome\":\"12\",\"begin\":6837831,\"end\":7858216,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531496\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"reciprocalOverlap\":0.03798}],\"dgv\":[{\"chromosome\":\"12\",\"begin\":6985480,\"end\":7103003,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1035811\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.32972},{\"chromosome\":\"12\",\"begin\":7005694,\"end\":7115157,\"variantType\":\"insertion\",\"variantFreqAll\":0.25,\"id\":\"nsv509453\",\"sampleSize\":4,\"observedGains\":1,\"reciprocalOverlap\":0.354},{\"chromosome\":\"12\",\"begin\":7012055,\"end\":7163058,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1047373\",\"sampleSize\":29084,\"observedGains\":1,\"reciprocalOverlap\":0.25662},{\"chromosome\":\"12\",\"begin\":7053085,\"end\":7063682,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00006,\"id\":\"nsv557262\",\"sampleSize\":17421,\"observedGains\":1,\"reciprocalOverlap\":0.2735},{\"chromosome\":\"12\",\"begin\":7054324,\"end\":7054491,\"variantType\":\"copy_number_gain\",\"id\":\"esv5830\",\"sampleSize\":1,\"reciprocalOverlap\":0.00434},{\"chromosome\":\"12\",\"begin\":7054931,\"end\":7065635,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00011,\"id\":\"dgv2324n54\",\"sampleSize\":17421,\"observedLosses\":2,\"reciprocalOverlap\":0.27626},{\"chromosome\":\"12\",\"begin\":7055492,\"end\":7070110,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00017,\"id\":\"dgv2325n54\",\"sampleSize\":17421,\"observedLosses\":3,\"reciprocalOverlap\":0.37726},{\"chromosome\":\"12\",\"begin\":7058967,\"end\":7059349,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00541,\"id\":\"esv3356433\",\"sampleSize\":185,\"observedGains\":1,\"reciprocalOverlap\":0.00988},{\"chromosome\":\"12\",\"begin\":7059100,\"end\":7059327,\"variantType\":\"insertion\",\"variantFreqAll\":1,\"id\":\"nsv513351\",\"sampleSize\":1,\"observedGains\":1,\"reciprocalOverlap\":0.00588},{\"chromosome\":\"12\",\"begin\":7059146,\"end\":7059890,\"variantType\":\"insertion\",\"variantFreqAll\":0.33333,\"id\":\"esv994245\",\"sampleSize\":3,\"observedGains\":1,\"reciprocalOverlap\":0.01923},{\"chromosome\":\"12\",\"begin\":7059221,\"end\":7059221,\"variantType\":\"insertion\",\"variantFreqAll\":0.01622,\"id\":\"esv3381477\",\"sampleSize\":185,\"observedGains\":3},{\"chromosome\":\"12\",\"begin\":7060846,\"end\":7070110,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00011,\"id\":\"dgv2326n54\",\"sampleSize\":17421,\"observedLosses\":2,\"reciprocalOverlap\":0.2391},{\"chromosome\":\"12\",\"begin\":7063701,\"end\":7070500,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":1,\"id\":\"nsv952791\",\"sampleSize\":1,\"observedLosses\":1,\"reciprocalOverlap\":0.17548}],\"variants\":[{\"vid\":\"12:7045275:7084024\",\"chromosome\":\"chr12\",\"begin\":7045275,\"end\":7084024,\"refAllele\":\"T\",\"altAllele\":\"deletion\",\"variantType\":\"deletion\",\"regulatoryRegions\":[{\"id\":\"ENSR00000361210\",\"type\":\"promoter\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]},{\"id\":\"ENSR00000361211\",\"type\":\"promoter\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]},{\"id\":\"ENSR00000361212\",\"type\":\"CTCF_binding_site\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]},{\"id\":\"ENSR00000361213\",\"type\":\"promoter\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]},{\"id\":\"ENSR00000361214\",\"type\":\"promoter\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]},{\"id\":\"ENSR00000361215\",\"type\":\"promoter\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]},{\"id\":\"ENSR00000361216\",\"type\":\"promoter\",\"consequence\":[\"regulatory_region_ablation\",\"regulatory_region_variant\"]}],\"overlappingGenes\":[\"ATN1\",\"C12orf57\",\"U47924.2\",\"RNU7-1\",\"PTPN6\",\"MIR200CHG\",\"EMG1\",\"MIR200C\",\"MIR141\",\"U47924.1\",\"PHB2\",\"SCARNA12\"],\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001007026.1}:c.1_844_EMG1{NM_006331.7}:c.412+170_735\",\"intron\":3},{\"hgvsc\":\"ATN1{NM_001007026.1}:c.1_844_EMG1{NM_001320049.1}:c.409+170_582\",\"intron\":4}]},\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{XM_005253672.1}:c.1_841_EMG1{NM_006331.7}:c.412+170_735\",\"intron\":3},{\"hgvsc\":\"ATN1{XM_005253672.1}:c.1_841_EMG1{NM_001320049.1}:c.409+170_582\",\"intron\":4}]},\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001940.3}:c.1_844_EMG1{NM_006331.7}:c.412+170_735\",\"intron\":3},{\"hgvsc\":\"ATN1{NM_001940.3}:c.1_844_EMG1{NM_001320049.1}:c.409+170_582\",\"intron\":4}]},\"proteinId\":\"NP_001931.2\"},{\"transcript\":\"NM_006331.7\",\"bioType\":\"protein_coding\",\"exons\":\"1-4/7\",\"introns\":\"1-3/5\",\"geneId\":\"10436\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"intron\":3,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001007026.1}:c.1_844_EMG1{NM_006331.7}:c.412+170_735\",\"exon\":5},{\"hgvsc\":\"ATN1{XM_005253672.1}:c.1_841_EMG1{NM_006331.7}:c.412+170_735\",\"exon\":5},{\"hgvsc\":\"ATN1{NM_001940.3}:c.1_844_EMG1{NM_006331.7}:c.412+170_735\",\"exon\":5}]},\"isCanonical\":true,\"proteinId\":\"NP_006322.4\"},{\"transcript\":\"NM_001320049.1\",\"bioType\":\"protein_coding\",\"exons\":\"1-4/6\",\"introns\":\"1-4/5\",\"geneId\":\"10436\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"intron\":4,\"fusions\":[{\"hgvsc\":\"ATN1{NM_001007026.1}:c.1_844_EMG1{NM_001320049.1}:c.409+170_582\",\"exon\":5},{\"hgvsc\":\"ATN1{XM_005253672.1}:c.1_841_EMG1{NM_001320049.1}:c.409+170_582\",\"exon\":5},{\"hgvsc\":\"ATN1{NM_001940.3}:c.1_844_EMG1{NM_001320049.1}:c.409+170_582\",\"exon\":5}]},\"proteinId\":\"NP_001306978.1\"},{\"transcript\":\"NR_135131.1\",\"bioType\":\"misc_RNA\",\"exons\":\"1-4/9\",\"introns\":\"1-3/7\",\"geneId\":\"10436\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{ENST00000356654.4}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"intron\":4}]},\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"exons\":\"5-10/10\",\"introns\":\"5-9/9\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"exon\":5,\"fusions\":[{\"hgvsc\":\"ATN1{ENST00000396684.2}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"intron\":4}]},\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000541029.1\",\"bioType\":\"retained_intron\",\"exons\":\"1-2/2\",\"introns\":\"1/1\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_ablation\"]},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"exons\":\"1-3/3\",\"introns\":\"1-2/2\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"transcript_ablation\"]},{\"transcript\":\"ENST00000607161.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/6\",\"introns\":\"1-3/5\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000261406.6\",\"bioType\":\"protein_coding\",\"exons\":\"1-4/7\",\"introns\":\"1-4/6\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\",\"unidirectional_gene_fusion\"],\"geneFusion\":{\"intron\":4,\"fusions\":[{\"hgvsc\":\"ATN1{ENST00000356654.4}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"exon\":5},{\"hgvsc\":\"ATN1{ENST00000396684.2}:c.1_844_EMG1{ENST00000261406.6}:c.409+170_732\",\"exon\":5}]},\"isCanonical\":true,\"proteinId\":\"ENSP00000476966.1\"},{\"transcript\":\"ENST00000546220.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/6\",\"introns\":\"1-3/5\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000539440.1\",\"bioType\":\"retained_intron\",\"exons\":\"1-3/4\",\"introns\":\"1-2/3\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000564245.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/8\",\"introns\":\"1-3/7\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000451846.2\",\"bioType\":\"retained_intron\",\"exons\":\"1-2/2\",\"introns\":\"1/1\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000539535.2\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/8\",\"introns\":\"1-3/7\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000541016.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/5\",\"introns\":\"1-3/4\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]},{\"transcript\":\"ENST00000539196.1\",\"bioType\":\"processed_transcript\",\"exons\":\"1-3/5\",\"introns\":\"1-3/4\",\"geneId\":\"ENSG00000126749\",\"hgnc\":\"EMG1\",\"consequence\":[\"transcript_truncation\"]}]}}]}")] + [InlineData("chr12 7067124 . GGCC ATTG 100 PASS . . .", "{\"chromosome\":\"chr12\",\"position\":7067124,\"refAllele\":\"GGCC\",\"altAlleles\":[\"ATTG\"],\"quality\":100,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7067124:7067127:ATTG\",\"chromosome\":\"chr12\",\"begin\":7067124,\"end\":7067127,\"refAllele\":\"GGCC\",\"altAllele\":\"ATTG\",\"variantType\":\"MNV\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_080548.4\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1404-1407\",\"cdsPos\":\"1255-1258\",\"exons\":\"11/16\",\"proteinPos\":\"419-420\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_080548.4:c.1255_1258delGGCCinsATTG\",\"hgvsp\":\"NP_536858.1:p.(Gly419_Pro420delinsIleAla)\",\"proteinId\":\"NP_536858.1\"},{\"transcript\":\"XM_005253719.1\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1248-1251\",\"cdsPos\":\"1132-1135\",\"exons\":\"10/15\",\"proteinPos\":\"378-379\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"XM_005253719.1:c.1132_1135delGGCCinsATTG\",\"hgvsp\":\"XP_005253776.1:p.(Gly378_Pro379delinsIleAla)\",\"proteinId\":\"XP_005253776.1\"},{\"transcript\":\"NM_002831.5\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1491-1494\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_002831.5:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"NP_002822.2:p.(Gly417_Pro418delinsIleAla)\",\"proteinId\":\"NP_002822.2\"},{\"transcript\":\"NM_080549.3\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1491-1494\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"5777\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"NM_080549.3:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"NP_536859.1:p.(Gly417_Pro418delinsIleAla)\",\"isCanonical\":true,\"proteinId\":\"NP_536859.1\"}],\"ensembl\":[{\"transcript\":\"ENST00000542848.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000444805.1\"},{\"transcript\":\"ENST00000543120.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000399448.1\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1404-1407\",\"cdsPos\":\"1255-1258\",\"exons\":\"11/16\",\"proteinPos\":\"419-420\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000399448.1:c.1255_1258delGGCCinsATTG\",\"hgvsp\":\"ENSP00000382376.1:p.(Gly419_Pro420delinsIleAla)\",\"proteinId\":\"ENSP00000382376.1\"},{\"transcript\":\"ENST00000534900.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000447931.2\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1248-1251\",\"cdsPos\":\"1132-1135\",\"exons\":\"10/15\",\"proteinPos\":\"378-379\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000447931.2:c.1132_1135delGGCCinsATTG\",\"hgvsp\":\"ENSP00000415979.2:p.(Gly378_Pro379delinsIleAla)\",\"proteinId\":\"ENSP00000415979.2\"},{\"transcript\":\"ENST00000538318.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000538715.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000438740.1\"},{\"transcript\":\"ENST00000318974.9\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1493-1496\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000318974.9:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"ENSP00000326010.9:p.(Gly417_Pro418delinsIleAla)\",\"proteinId\":\"ENSP00000326010.9\"},{\"transcript\":\"ENST00000456013.1\",\"bioType\":\"protein_coding\",\"codons\":\"GGCCcc/ATTGcc\",\"aminoAcids\":\"GP/IA\",\"cdnaPos\":\"1491-1494\",\"cdsPos\":\"1249-1252\",\"exons\":\"11/16\",\"proteinPos\":\"417-418\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"missense_variant\"],\"hgvsc\":\"ENST00000456013.1:c.1249_1252delGGCCinsATTG\",\"hgvsp\":\"ENSP00000391592.1:p.(Gly417_Pro418delinsIleAla)\",\"isCanonical\":true,\"proteinId\":\"ENSP00000391592.1\"},{\"transcript\":\"ENST00000543744.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000540740.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000416215.2\",\"bioType\":\"retained_intron\",\"cdnaPos\":\"1657-1660\",\"exons\":\"10/15\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"non_coding_transcript_exon_variant\",\"non_coding_transcript_variant\"],\"hgvsc\":\"ENST00000416215.2:n.1657_1660delGGCCinsATTG\"},{\"transcript\":\"ENST00000545153.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000476175.1\"},{\"transcript\":\"ENST00000535462.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000441044.1\"},{\"transcript\":\"ENST00000541698.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000445646.1\"},{\"transcript\":\"ENST00000542462.1\",\"bioType\":\"protein_coding\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000440114.1\"},{\"transcript\":\"ENST00000542277.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000536013.1\",\"bioType\":\"nonsense_mediated_decay\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"],\"proteinId\":\"ENSP00000446345.1\"},{\"transcript\":\"ENST00000539365.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"downstream_gene_variant\"]},{\"transcript\":\"ENST00000539029.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000542761.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000537533.1\",\"bioType\":\"processed_transcript\",\"geneId\":\"ENSG00000111679\",\"hgnc\":\"PTPN6\",\"consequence\":[\"upstream_gene_variant\"]}]}}]}")] + [InlineData("chr12 7033330 . T 100 PASS SVTYPE=INS;END=7033330;SVLEN=1350 . .", "{\"chromosome\":\"chr12\",\"position\":7033330,\"svEnd\":7033330,\"refAllele\":\"T\",\"altAlleles\":[\"\"],\"quality\":100,\"filters\":[\"PASS\"],\"svLength\":1350,\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"clingen\":[{\"chromosome\":\"12\",\"begin\":147099,\"end\":7054359,\"variantType\":\"copy_number_gain\",\"id\":\"nsv498529\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":173786,\"end\":34835837,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995956\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Decreased calvarial ossification\",\"Delayed gross motor development\",\"Feeding difficulties\",\"Frontal bossing\",\"Morphological abnormality of the central nervous system\",\"Patchy alopecia\"],\"phenotypeIds\":[\"HP:0002007\",\"HP:0002011\",\"HP:0002194\",\"HP:0002232\",\"HP:0005474\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:C1862862\",\"MedGen:CN001816\",\"MedGen:CN001820\",\"MedGen:CN001989\",\"MedGen:CN004852\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":7425202,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532325\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":8514342,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532326\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":25623263,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532324\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of cardiac morphology\",\"Agenesis of corpus callosum\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"],\"phenotypeIds\":[\"HP:0001274\",\"HP:0001627\",\"MedGen:C1837248\",\"MedGen:CN001482\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":28568117,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531493\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Global developmental delay\"],\"phenotypeIds\":[\"HP:0001263\",\"MedGen:CN001157\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34533111,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532323\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":2,\"phenotypes\":[\"Coarse facial features\",\"Abnormal facial shape\",\"Abnormality of cardiac morphology\",\"Cleft upper lip\",\"Global developmental delay\",\"Hearing impairment\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000280\",\"MedGen:C1854600\",\"HP:0000204\",\"HP:0000365\",\"HP:0001263\",\"HP:0001627\",\"HP:0001999\",\"HP:0004322\",\"MedGen:C0349588\",\"MedGen:C1384666\",\"MedGen:CN000197\",\"MedGen:CN001157\",\"MedGen:CN001482\",\"MedGen:CN001810\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756196,\"variantType\":\"copy_number_gain\",\"id\":\"nsv916406\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Ambiguous genitalia\",\"Delayed fine motor development\",\"Delayed gross motor development\",\"Delayed speech and language development\",\"Developmental delay AND/OR other significant developmental or morphological phenotypes\",\"Intellectual disability\",\"Short stature\"],\"phenotypeIds\":[\"HP:0000062\",\"HP:0000750\",\"HP:0001249\",\"HP:0002194\",\"HP:0004322\",\"HP:0010862\",\"MedGen:C0349588\",\"MedGen:C1843367\",\"MedGen:CN000062\",\"MedGen:CN000706\",\"MedGen:CN001989\",\"MedGen:CN116596\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34756209,\"variantType\":\"copy_number_gain\",\"id\":\"nsv533931\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":34761006,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917315\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":282465,\"end\":133773393,\"variantType\":\"copy_number_gain\",\"id\":\"nsv917029\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Abnormality of toe\",\"Defect in the atrial septum\",\"Downslanted palpebral fissures\",\"Frontal bossing\",\"Low-set ears\",\"Overlapping fingers\",\"Patent ductus arteriosus\",\"Sacral dimple\",\"Sandal gap\",\"Single transverse palmar crease\"],\"phenotypeIds\":[\"HP:0000369\",\"HP:0000494\",\"HP:0000954\",\"HP:0000960\",\"HP:0001631\",\"HP:0001643\",\"HP:0001780\",\"HP:0001852\",\"HP:0002007\",\"HP:0010557\",\"MedGen:C0426848\",\"MedGen:C1865016\",\"MedGen:C1873502\",\"MedGen:CN000345\",\"MedGen:CN001485\",\"MedGen:CN001496\",\"MedGen:CN001615\",\"MedGen:CN001674\",\"MedGen:CN001816\",\"MedGen:CN009386\"]},{\"chromosome\":\"12\",\"begin\":322142,\"end\":34079848,\"variantType\":\"copy_number_gain\",\"id\":\"nsv532328\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":1367440,\"end\":20810511,\"variantType\":\"copy_number_gain\",\"id\":\"nsv995558\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"validated\":true,\"phenotypes\":[\"Feeding difficulties\",\"Laryngomalacia\"],\"phenotypeIds\":[\"HP:0001601\",\"HP:0011968\",\"MedGen:C0232466\",\"MedGen:CN001457\"]},{\"chromosome\":\"12\",\"begin\":2980907,\"end\":15140282,\"variantType\":\"copy_number_gain\",\"id\":\"nsv868869\",\"clinicalInterpretation\":\"pathogenic\",\"observedGains\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]},{\"chromosome\":\"12\",\"begin\":6837831,\"end\":7858216,\"variantType\":\"copy_number_loss\",\"id\":\"nsv531496\",\"clinicalInterpretation\":\"pathogenic\",\"observedLosses\":1,\"phenotypes\":[\"Developmental delay AND/OR other significant developmental or morphological phenotypes\"]}],\"dgv\":[{\"chromosome\":\"12\",\"begin\":6889463,\"end\":7041469,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.02105,\"id\":\"nsv832324\",\"sampleSize\":95,\"observedLosses\":2},{\"chromosome\":\"12\",\"begin\":6948468,\"end\":7033823,\"variantType\":\"copy_number_loss\",\"variantFreqAll\":0.00006,\"id\":\"nsv557261\",\"sampleSize\":17421,\"observedLosses\":1},{\"chromosome\":\"12\",\"begin\":6985480,\"end\":7103003,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1035811\",\"sampleSize\":29084,\"observedGains\":1},{\"chromosome\":\"12\",\"begin\":7005694,\"end\":7115157,\"variantType\":\"insertion\",\"variantFreqAll\":0.25,\"id\":\"nsv509453\",\"sampleSize\":4,\"observedGains\":1},{\"chromosome\":\"12\",\"begin\":7012055,\"end\":7163058,\"variantType\":\"copy_number_gain\",\"variantFreqAll\":0.00003,\"id\":\"nsv1047373\",\"sampleSize\":29084,\"observedGains\":1}],\"variants\":[{\"vid\":\"12:7033331:7033330:INS\",\"chromosome\":\"chr12\",\"begin\":7033331,\"end\":7033330,\"refAllele\":\"T\",\"altAllele\":\"insertion\",\"variantType\":\"insertion\",\"regulatoryRegions\":[{\"id\":\"ENSR00000361206\",\"type\":\"CTCF_binding_site\",\"consequence\":[\"regulatory_region_variant\"]}]}]}")] [InlineData("chr12 7043410 . C CTCC 50 PASS . . .", "{\"chromosome\":\"chr12\",\"position\":7043410,\"refAllele\":\"C\",\"altAlleles\":[\"CTCC\"],\"quality\":50,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7043411:7043410:TCC\",\"chromosome\":\"chr12\",\"begin\":7043411,\"end\":7043410,\"refAllele\":\"-\",\"altAllele\":\"TCC\",\"variantType\":\"insertion\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"codons\":\"-/TCC\",\"aminoAcids\":\"-/S\",\"cdnaPos\":\"336-337\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"inframe_insertion\"],\"hgvsc\":\"NM_001007026.1:c.100_102dupTCC\",\"hgvsp\":\"NP_001007027.1:p.(Ser34dup)\",\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"codons\":\"-/TCC\",\"aminoAcids\":\"-/S\",\"cdnaPos\":\"429-430\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"inframe_insertion\"],\"hgvsc\":\"XM_005253672.1:c.100_102dupTCC\",\"hgvsp\":\"XP_005253729.1:p.(Ser34dup)\",\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"codons\":\"-/TCC\",\"aminoAcids\":\"-/S\",\"cdnaPos\":\"329-330\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"inframe_insertion\"],\"hgvsc\":\"NM_001940.3:c.100_102dupTCC\",\"hgvsp\":\"NP_001931.2:p.(Ser34dup)\",\"proteinId\":\"NP_001931.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"codons\":\"-/TCC\",\"aminoAcids\":\"-/S\",\"cdnaPos\":\"336-337\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"inframe_insertion\"],\"hgvsc\":\"ENST00000356654.4:c.100_102dupTCC\",\"hgvsp\":\"ENSP00000349076.3:p.(Ser34dup)\",\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"codons\":\"-/TCC\",\"aminoAcids\":\"-/S\",\"cdnaPos\":\"333-334\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"inframe_insertion\"],\"hgvsc\":\"ENST00000396684.2:c.100_102dupTCC\",\"hgvsp\":\"ENSP00000379915.2:p.(Ser34dup)\",\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000541029.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"upstream_gene_variant\"]}]}}]}")] [InlineData("chr12 7043410 . CT GATG 50 PASS . . .", "{\"chromosome\":\"chr12\",\"position\":7043410,\"refAllele\":\"CT\",\"altAlleles\":[\"GATG\"],\"quality\":50,\"filters\":[\"PASS\"],\"cytogeneticBand\":\"12p13.31\",\"samples\":[{\"isEmpty\":true}],\"variants\":[{\"vid\":\"12:7043410:7043411:GATG\",\"chromosome\":\"chr12\",\"begin\":7043410,\"end\":7043411,\"refAllele\":\"CT\",\"altAllele\":\"GATG\",\"variantType\":\"indel\",\"transcripts\":{\"refSeq\":[{\"transcript\":\"NM_001007026.1\",\"bioType\":\"protein_coding\",\"codons\":\"gcCTcc/gcGATGcc\",\"aminoAcids\":\"AS/AMX\",\"cdnaPos\":\"336-337\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"frameshift_variant\"],\"hgvsc\":\"NM_001007026.1:c.99_100delCTinsGATG\",\"hgvsp\":\"NP_001007027.1:p.(Ser34MetfsTer27)\",\"isCanonical\":true,\"proteinId\":\"NP_001007027.1\"},{\"transcript\":\"XM_005253672.1\",\"bioType\":\"protein_coding\",\"codons\":\"gcCTcc/gcGATGcc\",\"aminoAcids\":\"AS/AMX\",\"cdnaPos\":\"429-430\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"frameshift_variant\"],\"hgvsc\":\"XM_005253672.1:c.99_100delCTinsGATG\",\"hgvsp\":\"XP_005253729.1:p.(Ser34MetfsTer27)\",\"proteinId\":\"XP_005253729.1\"},{\"transcript\":\"NM_001940.3\",\"bioType\":\"protein_coding\",\"codons\":\"gcCTcc/gcGATGcc\",\"aminoAcids\":\"AS/AMX\",\"cdnaPos\":\"329-330\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"1822\",\"hgnc\":\"ATN1\",\"consequence\":[\"frameshift_variant\"],\"hgvsc\":\"NM_001940.3:c.99_100delCTinsGATG\",\"hgvsp\":\"NP_001931.2:p.(Ser34MetfsTer27)\",\"proteinId\":\"NP_001931.2\"}],\"ensembl\":[{\"transcript\":\"ENST00000356654.4\",\"bioType\":\"protein_coding\",\"codons\":\"gcCTcc/gcGATGcc\",\"aminoAcids\":\"AS/AMX\",\"cdnaPos\":\"336-337\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"frameshift_variant\"],\"hgvsc\":\"ENST00000356654.4:c.99_100delCTinsGATG\",\"hgvsp\":\"ENSP00000349076.3:p.(Ser34MetfsTer27)\",\"isCanonical\":true,\"proteinId\":\"ENSP00000349076.3\"},{\"transcript\":\"ENST00000396684.2\",\"bioType\":\"protein_coding\",\"codons\":\"gcCTcc/gcGATGcc\",\"aminoAcids\":\"AS/AMX\",\"cdnaPos\":\"333-334\",\"cdsPos\":\"99-100\",\"exons\":\"3/10\",\"proteinPos\":\"33-34\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"frameshift_variant\"],\"hgvsc\":\"ENST00000396684.2:c.99_100delCTinsGATG\",\"hgvsp\":\"ENSP00000379915.2:p.(Ser34MetfsTer27)\",\"proteinId\":\"ENSP00000379915.2\"},{\"transcript\":\"ENST00000541029.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"upstream_gene_variant\"]},{\"transcript\":\"ENST00000537488.1\",\"bioType\":\"retained_intron\",\"geneId\":\"ENSG00000111676\",\"hgnc\":\"ATN1\",\"consequence\":[\"upstream_gene_variant\"]}]}}]}")] - public void Annotate_with_SA(string vcfline, string expectedOutPut) + public void Annotate_with_SA(string vcfline, string expectedResults) { - var annotatedPosition = - AnnotationUtilities.GetAnnotatedPosition(_cacheFilePrefix, new List{_saDirectory}, vcfline, - false); - - var output = annotatedPosition.GetJsonString(); - Assert.Equal(expectedOutPut, output); + var annotatedPosition = AnnotationUtilities.GetAnnotatedPosition(_cacheFilePrefix, _saDirs, vcfline, false); + var observedResults = annotatedPosition.GetJsonString(); + Assert.Equal(expectedResults, observedResults); } - - - - - - - - - - - } } \ No newline at end of file diff --git a/UnitTests/ErrorHandling/Exceptions/ExceptionsTests.cs b/UnitTests/ErrorHandling/Exceptions/ExceptionsTests.cs index 33240522..177b2887 100644 --- a/UnitTests/ErrorHandling/Exceptions/ExceptionsTests.cs +++ b/UnitTests/ErrorHandling/Exceptions/ExceptionsTests.cs @@ -18,7 +18,7 @@ private sealed class ExceptionGenerator : IEnumerable new object[] { new InvalidFileFormatException("test"), ExitCodes.InvalidFileFormat}, new object[] { new MissingCompressionLibraryException("test"), ExitCodes.MissingCompressionLibrary}, new object[] { new ProcessLockedFileException("test"), ExitCodes.SharingViolation}, - new object[] { new UserErrorException("test"), ExitCodes.UserError}, + new object[] { new UserErrorException("test"), ExitCodes.UserError} }; public IEnumerator GetEnumerator() => _data.GetEnumerator(); diff --git a/UnitTests/Jasix/JasixQueryProcessingTests.cs b/UnitTests/Jasix/JasixQueryProcessingTests.cs index 2684422f..ba65ae17 100644 --- a/UnitTests/Jasix/JasixQueryProcessingTests.cs +++ b/UnitTests/Jasix/JasixQueryProcessingTests.cs @@ -34,7 +34,7 @@ public void Combination_of_large_and_small_variants() var largeVariantLocations = index.LargeVariantPositions("chr1", 10_000, 10_020); Assert.Equal(90_000, firstSmallVarLocation); - Assert.True(largeVariantLocations.SequenceEqual(new List(){ 90_100 , 90_200 , 100_200 })); + Assert.True(largeVariantLocations.SequenceEqual(new List { 90_100 , 90_200 , 100_200 })); } @@ -60,7 +60,7 @@ public void Quiring_large_variants_overlapping_range_but_starting_before() var largeVariantBefore = index.LargeVariantPositions("chr1", 10_000, 9_999); - Assert.True(largeVariantBefore.SequenceEqual(new List() { 80_000, 90_100, 90_200 })); + Assert.True(largeVariantBefore.SequenceEqual(new List { 80_000, 90_100, 90_200 })); } [Fact] @@ -95,7 +95,7 @@ public void TestQuerySingle() Assert.Equal("\"header\":{\"annotator\":\"Illumina Annotation Engine 1.3.3.1633\",\"creationTime\":\"2016-12-09 09:49:24\",\"genomeAssembly\":\"GRCh37\",\"schemaVersion\":4,\"dataVersion\":\"84.22.36\",\"dataSources\":[{\"name\":\"VEP\",\"version\":\"84\",\"description\":\"Ensembl\",\"releaseDate\":\"2016-04-29\"},{\"name\":\"phyloP\",\"version\":\"hg19\",\"description\":\"46 way conservation score between humans and 45 other vertebrates\",\"releaseDate\":\"2009-11-10\"},{\"name\":\"OMIM\",\"version\":\"unknown\",\"description\":\"An Online Catalog of Human Genes and Genetic Disorders\",\"releaseDate\":\"2016-09-02\"},{\"name\":\"dbSNP\",\"version\":\"147\",\"description\":\"Identifiers for observed variants\",\"releaseDate\":\"2016-06-01\"},{\"name\":\"COSMIC\",\"version\":\"78\",\"description\":\"Somatic mutation and related details and information relating to human cancers\",\"releaseDate\":\"2016-09-05\"},{\"name\":\"1000 Genomes Project\",\"version\":\"Phase 3 v5a\",\"description\":\"A public catalogue of human variation and genotype data\",\"releaseDate\":\"2013-05-27\"},{\"name\":\"EVS\",\"version\":\"2\",\"releaseDate\":\"2013-11-13\"},{\"name\":\"ExAC\",\"version\":\"0.3.1\",\"description\":\"Allele frequency data from the ExAC project\",\"releaseDate\":\"2016-03-16\"},{\"name\":\"ClinVar\",\"version\":\"unknown\",\"description\":\"A freely accessible, public archive of reports of the relationships among human variations and phenotypes, with supporting evidence\",\"releaseDate\":\"2016-09-01\"},{\"name\":\"DGV\",\"version\":\"unknown\",\"description\":\"Provides a comprehensive summary of structural variation in the human genome\",\"releaseDate\":\"2016-05-15\"},{\"name\":\"ClinGen\",\"version\":\"unknown\",\"releaseDate\":\"2016-04-14\"}]}", header); var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:9775924")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1:9775924")); Assert.Equal(1, results.Count()); } } @@ -109,7 +109,7 @@ public void TestQueryMultiple() using (var qp = new QueryProcessor(new StreamReader(readStream), indexStream)) { var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:9775924-9778952")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1:9775924-9778952")); Assert.Equal(3, results.Count()); } @@ -125,7 +125,7 @@ public void TestQueryMultipleWithSkippingMiddleOne() using (var qp = new QueryProcessor(new StreamReader(readStream), indexStream)) { var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:27023180-27023190")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1:27023180-27023190")); Assert.Equal(2, results.Count()); } } @@ -139,7 +139,7 @@ public void TestQueryChr1() using (var qp = new QueryProcessor(new StreamReader(readStream), indexStream)) { var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1")); Assert.Equal(422, results.Count()); } @@ -154,12 +154,12 @@ public void Report_overlapping_small_and_extending_large_variants() using (var qp = new QueryProcessor(new StreamReader(readStream), indexStream)) { var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:16378-17000")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1:16378-17000")); Assert.Equal(3, results.Count()); results = - qp.ReadJsonLinesExtendingInto(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:16378-17000")); + qp.ReadJsonLinesExtendingInto(Utilities.ParseQuery("chr1:16378-17000")); Assert.Equal(1, results.Count()); } @@ -174,12 +174,12 @@ public void Report_overlapping_small_and_extending_multiple_large_variants() using (var qp = new QueryProcessor(new StreamReader(readStream), indexStream)) { var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:19004-20000")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1:19004-20000")); Assert.Equal(3, results.Count()); results = - qp.ReadJsonLinesExtendingInto(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:19004-20000")); + qp.ReadJsonLinesExtendingInto(Utilities.ParseQuery("chr1:19004-20000")); Assert.Equal(2, results.Count()); } @@ -194,12 +194,12 @@ public void Report_overlapping_small_and_large_variants_starting_at_same_locatio using (var qp = new QueryProcessor(new StreamReader(readStream), indexStream)) { var results = - qp.ReadOverlappingJsonLines(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:46993-50000")); + qp.ReadOverlappingJsonLines(Utilities.ParseQuery("chr1:46993-50000")); Assert.Equal(5, results.Count()); results = - qp.ReadJsonLinesExtendingInto(global::Jasix.DataStructures.Utilities.ParseQuery("chr1:46993-50000")); + qp.ReadJsonLinesExtendingInto(Utilities.ParseQuery("chr1:46993-50000")); Assert.Equal(0, results.Count()); } diff --git a/UnitTests/Jasix/JasixTests.cs b/UnitTests/Jasix/JasixTests.cs index ba90e2d7..11cfc2f2 100644 --- a/UnitTests/Jasix/JasixTests.cs +++ b/UnitTests/Jasix/JasixTests.cs @@ -72,11 +72,11 @@ public void AllFromAchromosome() index.Flush(); - var chrPos = global::Jasix.DataStructures.Utilities.ParseQuery("chr1"); + var chrPos = Utilities.ParseQuery("chr1"); Assert.Equal(100000, index.GetFirstVariantPosition(chrPos.Item1, chrPos.Item2, chrPos.Item3)); - chrPos = global::Jasix.DataStructures.Utilities.ParseQuery("chr2"); + chrPos = Utilities.ParseQuery("chr2"); Assert.Equal(100150, index.GetFirstVariantPosition(chrPos.Item1, chrPos.Item2, chrPos.Item3)); } diff --git a/UnitTests/Resources/GRCh37_chr12_7018490_7086889/SA/chr12.nsa b/UnitTests/Resources/EndToEnd/GRCh37/chr12.nsa similarity index 100% rename from UnitTests/Resources/GRCh37_chr12_7018490_7086889/SA/chr12.nsa rename to UnitTests/Resources/EndToEnd/GRCh37/chr12.nsa diff --git a/UnitTests/Resources/GRCh37_chr12_7018490_7086889/SA/chr12.nsa.idx b/UnitTests/Resources/EndToEnd/GRCh37/chr12.nsa.idx similarity index 100% rename from UnitTests/Resources/GRCh37_chr12_7018490_7086889/SA/chr12.nsa.idx rename to UnitTests/Resources/EndToEnd/GRCh37/chr12.nsa.idx diff --git a/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.bases b/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.bases new file mode 100644 index 0000000000000000000000000000000000000000..9e4b2314f336f6c2c9addc3a1ca2f95cb8f76b63 GIT binary patch literal 29436 zcmZ7e30xD``p1ta#IPAft6>urrEX|7nGr#Yf^D_--u7PGnhgOHxCx6|Sqd)2IssWl z(P{!=b0V^ed)=*0Ko*fjZ9zoTi53*J3Q`mW<@cN!`u+cZKVJ9M^ZsFJ4d$-PTT{fmf{{$6 zo#6@w?*gJy_qi|%GXaqo^CmM&Yy=`p|8fN-hF1cS`}3zU3Nr$cy}!+7l-LMFnD)E# zBQW?(EfA6aaxSAVEfAe>;1foP%|K*!)dEoHGl8g%`yxhRCLm&H56&pD7KplE?7@!^ z<1i0EWacqXMqx%EYCz}3D6t8MdPXhgMTjvIjP#>7i{XlRK4u1@PBnbWD6t8MJa=g+ zFH$Tv!bmj^e-4b9V5D^EEBTRdPht}gUDy02qcAfNb>Z)?7=@dFNaE(dc##-pgpqE2 z@o%_-&lj74$X~8~%_z(WM3?mchf%m0h)BEtUr-C@i?u-H-&ek66lMe>-=F`EQMeI^ z$a?)fC=Ylg5PfS%!YIrPM3vRc8HJmG2-AB%ek81oSPMio{_4*t_&vY+({~;QG72{X z5m!2cKwwpi09`=GfJ!lqI-(QfC7Ut z0};u^u#G@rS|Bp77`72wEF%!rdtp2%Fjo^0_3rdXjKWMn)S)-9so(`VmHpw+9_a=9!3}`?LAlEg(etjZTfV054uAj zIxWeKQJ5Ksx@BgS*aSr8wa-E(ZG@4$vmdq-GHD|aS$Aa~qr^raBKyF6KA1EtE)ZQj zBw`e11|sfPVW7~$1JMOWoKct=h)S(q$SB+dM4YW&1e*cZ!brTo?FCnmu>uio$CfY( z(*n_#29`1kHv^FeGJU`x;k%7M^z$>mjKa)7L}T7^M&Vi@%JAfKP-sqpNZq|J7=;;u z=+}K{^TAq}foR=tX!D^>1w?k3S%n#a$nqCxE5hsYP!=DBJ`@C6}O0h3*rGzD=!V6lMmZj_gI7 z3fT-0d0{{a3fU77xw}opD9i{%mH!dUDBJ`@zWhVOi^LZiVWbZ?g#mk*VI(q6u7_8F z#b|-Zq^GRHj6hVz{vViYxCw~pJp%g)_k>phk+=6pG72*S?Y=h{%_to9R9_hvEEnuk z3q<_#H>)r$5KZhK#at7cfvB9D><+_CK;)w%mT=7j<_?H>{}k;v~&J6Rw~|1)|SAoy9233`88No5Luv7Kr}vH&W=k zfhfZ%c1wampPEY>9?yeq0e4~sqUo3Hmc-0JM8%y2a1CujAo9W+TiHk zf#~b|K4TPa1|qt4eF1S1*TP8dBfbP)XoQixP`L_T1;%6q8i}P~XegFa6)*l}iLT*d z6Cict>AzVC>DJSK)8#k6W+iS0B(C? zVl9kx^4cvJx`_ z(!VCHVkK?{BvK9kV%~~tfv79{zX63J1rT}oJVYN*m=TCf+XD$6iWEj5dSLKd@cFnI zM*3koG64}ukF8m=(gKkgd%(3Lk7)#=F5PDp zW&$GnA0u-_t{sT_um`FRCKCrD=wfi^$n|J}$YCS6bL6#*KxF%SWS%g8?}fzfGzZ2c zVMO%@m=~INRDleoxiAVd0+D-$Cxb#>28cMjZwjL@EfAgf1eq*Mr5T8L(&!2bzDo;4 z_Pm|WD9i{%HZ+eIun~wdKAj2IkjDoi?)^54QJ5BpuF@iJjY1j_^?TQR*dQJz7+HUe z%pUn;Qr`{U8JWEih`4Bkgb5U`1)|UBJV3!cn}O)mH>|?UKm_>)Tr*?WKvZ+@Vo-3; zCLr?hU2jHVMj$Gy09h)kaDb=}1K?kgZJK~syG6E%?3S$SgOmvrZUmxI&Vh%;F%yh* z%2V*LC=oFOk@|zkNMUV^Ky<}HNRdF{W+3ux`D!K+fsyDr1yu(!FD(%DBpVqbvJ#-n zS%*-91STY6N@9qyz)VG4POwQkW&--Zq+QG;?L$c@r9kl)h|peR6{ZEEUYtkTt3?u!Vn9Ulc8Hg#Mkr`r4 z0nNBgWR%El(#KEDLq=(aksdye5(tQVW*}E~NB)TLMk>TfH$RCn5zoAkRc@rS& za2>Kd*f%C1GVLWwZIM|3k+%j>DT$U4h(2`yY!5k3GZ1RYFbYE*8RF@ER>2(HA4BD} zB5#O(3lL#?fI>AaqZWwlx{bUc@|!@^@NG6&!$`FsbAT(z?10FEG}Z_)BM{N|5EYil z(t(J?KSs<>3q%vO(4S#EClLAa4Dyxeo`I;NZ>K_H2fs&b0;C?Coz6 z3DAg_M6(ZGlG5iuk%>&p1cVF{JPFz|Kvd;>Pews3dhbaeeYlv3uDI9?h{8E5F%;2R zn?(K&c20vgo0g(zi_%i!gC0y0MO!TpWz2&l6-8SU5S84w97S6bjP%}oNJ{aAW*F)8 z%9X$#W*DhKGjf@*qf9__^*g94L1AW~CrQ1~)ImMK*<7k12~|+Yyn(1Q?~wBY(>DR3 zejlU6OuMf+*#=x1W`dCot7x*AuzD;7Ob1P*35Yc$P>dm=Dv1e}Fw(b;A3@xP*WqSB zGX2en#EgJ+WA#M#s@M!jWIu#XAF={15ZUp@e#9n#sGYAI|FH=`>Oz{+KQ;kKU%2G_ zk4*p)kB*OoUM&!H@()&FCLp5p@<`~_0uiUq{u6qE{y(|n)qFR&0TgC{=%gbvSW6O{ z0m=TXnIm>)1f;GXVUtIwzW`D4$l4jo9hue!V`r$fLH(A^=7-RH$zDL{FF@slcDWV^ z)k-6F21K50MX?#>AV6f|MHG!u3B?n7La`MD^x~EVn#p| zomq*a@Jwg4Ax{WfTWkg-42jEGiD?1pr){6H5;p_BF7IId9X|IT!$BSb8ICy%84i4m z35Y5wM1?qfr3r|-`^XZ-LKBQsW)p0TKH;CL&|h!blEQL%>AF zXapMZTxb`A=VCg^;B!zON9UZKiUOS(MtYCgl?ik(vW^JdgBUp?qT#R`%oIpW3rKu0 zj~EE91*G3LL#l>u-wgDB#RbHs|Nr6w$C%=xZa3?tprit%5+2Wmw<1pj#8%;G#!wYb z4JXfsFNN6@n*hm<@&&AfR#N!_D*LsFmADCzBI?0yF^&odmD-HLP^C={{K+~o+z5os z3H%6#j0sr}ybhTi5L-y2)C(2TD2g)Im>^1L{ow=N4Vj%8khu41L_$LP%7D$P7)JaRDjMkD zI~+#*l^GDFXCwX!QaRZT$Xvn&d zA4Luf)qX&%{h{;-*<l1GzJlrT~%e6OjLcNCrLogeCOZyCV`a1Jdt1kUN8`P}Fz) zQ*EGzHB=ipCvT!m4g%fBBl#5Ajt9!8=+xb)i{UYUp&5t`bjarZIoD#YVdz+kyi{(> zD6tWUjK6>~DELuEAmYFft1vAPwd+r0Jjm+-5qFNFz=vyLq!S)8e#Z?6NXU}` z5oa2p`G>3q(*lw=8k|@uHUg4=)Hs8`5gTD-awMjLH2?o{B(&HlM`BZRSZtJnFbU^1tsa;Pq$y#OQGdWVf*Vj~b)k-}zCm=TCLd>chDWRgJS#oKHIgTsW|9%N-b zI3Q$_Mj+x+2U}0#S|FNCL)|-=S2GY*IDo24OlW@X`78W-F(vNSAI1kJWNM7!NS`J+m8v#d}U&vcP^NXs= z`3SAE2}WY)UV9X8v@p_LzamEgw<0zJ(#16>?Ewi#A2p6-w;ts=AaNrgb>o9Gv+VE_ zKRA=Wv`u0ZZUmxBc7Yc`rfCMEPbMM*L5m4QrX5CE4>D;Wbb13{?}5&4$YbeX5Xci5 zf#{6Fe_|LA{qP8zX+vrYNbNn(X4){pfYe|6krP3wtqG8-d-(~h3uc0GB*#J1i*g(` z&qe_Si0U3fnKi7735dLDgl;fecd-$WK3F{>F*6`tLM>vi!to=ui1@qJlaXMDfFxPM z=0F%`1f&P5ArVJL11+lR#dPUTHV1;62PB{WzLdENo(k|ya~DJ|WK-ZaNGP|Mzyc$2 z^wB6_Oba9Z@)5EUSWhz$U3m>PonWTSKy+r24O&h!j3`rvMFJZUqf{AHV64PY3C7qA zbCt0frXz(U#&o30j@rS(pj{0_7;?seLh}YhKYYz9%nZbwk)vzO89C8WjiL>jH!UFb z>$4GwnE+AV!(PQv;zPC`a%6462t*z{j%*0o0uXWc479M&`fGvcD{X9Cf^K}

wvoL>62HFNDe^BM@x@kWj6v5 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

|529$>w@f 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 literal 0 HcmV?d00001 diff --git a/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.polyphen.ndb b/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.polyphen.ndb new file mode 100644 index 0000000000000000000000000000000000000000..f384521bd671cc0e1b2934bcd304abe0f2ae3243 GIT binary patch literal 143648 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 )-&$UDEtbEy z$T|4sOA{$xP$j?xSW=QGS@5PUi!k}h@=YI(B%92gB4*A5R2M16FKS?R@!;j* zvj;D0t!Yiul4*7F&R17RR<4Zt6n&Krr~FwRy}zhcRHtb5YDzv+RVg{hZz-4S{eHb) zke<{p_r2ba>AX1}jz^NG?>x=(#p;CI zXYPTHKk~(chX)VFq;cGX8!8V5@IcJ*-bX2yE0W8VC~|_ARD`Ms4+j6!O_L8fxOUayz?oRD8G*X#X+q}9<`3$@fK zSu9PbJQfQ=RUJ#qV`S9fkqi+_mKB-DLT0F0|(GjjG>`P@f*;rq;THXz}5gHYmz!{uQZv~#ExndG4`57 ze79*WpKWo$kfA$PbK2 zED{NeI5|KQxGqH$Y{30luPYb}nQO&YRNmS$?rZb)lt*Ry-QOYpN$ z2mo>NJDyhtpByC=9+a)LVX*^l5-Ir&+2Hkhr)_{ng;}6rLMuoR zEkCd>rT8KOC^P5PVV1kMXB3c%ACP%@%-b{V5RyF4vn22)5o@~mxv)7frInK)yWfxZ z!KVzq8<^X#v|;fOYme-;mwXe-LY?G+~}a z&ePgX`~v}ntjPr$~-o1hF_gpPcY}OnN8Jrt}6*+?gx>+T<+g;GIhN zx5IB<5x%Hf4svJSN=TVZ%uirN7=gk9X64M88bm0fn&w0OpdSh?XJ+LPvZ`BiY##pluQwDu0UQbTy6g7EYTTvL3pW(_T( zOH2Z=k`aE4oiOy#Vw>G#4HGlNLufDZ^Fp0jR>ifCotm0<_m_AFip4EYVO=7F`Y35x z(r6SaQ(Co6ij&A!b>2QTIaB#b=3Rf$n3uI*R-}XbFpCQDnFdMIxM9 zL`nzT|M1Uuz8js@juq*|qwA4Ia^|lYkK!~PIRUvu=skEkL(?LRDxZoJ0S%BvV(fU^ zZ-+^YoifOG8Zn~ArprY|uzLC;V?;UG$oHK)UEElG?};2b)BTo6HdDx;sOuCV2#Av} z0=aXhD^uz{k<{UHg|0;vX(;fA3Wg3rQ<0?%)aOhQ>)&>ifZ@=7X!NjlUWD=D^v*Uv z9Dh=ZQ8uC?+kIhQ8T!P>_Yg zu!;NpoGe#vU~vedYV9-#45YQuWUj)wq@k(!edX>Nxc$lO)7Q=jrL6gna1UPyy0afE6(X3Q$3y3&46SXsi@S5k~dnqCjkl>V@3e z)G*#OFx9AW+w#}iYvz#R(nk=iSfK#NzaaynAcF!0K)PBib-lZBL&nRe%Lo~ttQJU+ z__&#cZMBA>p;7s4l_FS)M>GVP>h}hP&~EosMd*(=w;x4IOE&^kKME>pOiiIwQ(ptc zt;UiNAvXi47@exA8-w{Bbv2_wF*=7-6QEYm;Q|;SYikOCn6VfG2FO$WmV{KKJxfBj z8c%imaig&GvtWsE87`Os22&6iGVnh{1&DSNoEZ*5u!veqnlxHPR@W01p8(4%yLfTOZ4LwIkX%C93_Uj~0$VM2ekc=ok z(8d<-Gn;HnHXFG!ZAm+y>N`Tjd?Fr7z(!LO5z}pp=y^DXh=``v*t_FF@piu{`$PJb zxAL^B2@Ap*xZY3R_66x)Yg;ZJanNZv~t!plkoGn>&QJ$ljcMTAR1z|1D_157gTKSXAT$gIJc>G^$}5=Tcq zj#6ax_|XguK6)Hc*_k4w1ljF&wgVn2g|Fuam%+>ckM1x)G{D$T5eOnKGdMFo^8}O` zKfWd&5ef}y6%h(44c|1Sp~;B~o`Ict0F;oOZ|B>+jjWesv+aCGcINY_Ap+`!FK7p~ z^J&?)G7Cl8rQ);q>IVQwR?9jwl<#jN^0xm@cGr;#TKt$lk z53m3cnG85HRKGMeeke^%Q5k=U%WX->64lLu87W7rLZ(iII`34h&@yJsQ~AX!iF~nUDR#l3yqS$Oa8%aG8s<%{QSIM*iy={ zCc}@?8P#vYkD?c{Tar2+N8Fm2R4m;rU?K1;aLrm*S61M#Ns7gR1oBqlT6d*6lZHUm z!wXkR#?n-L&+eLL*mk@3?Vff$)#nuCOVN~@Fr*UW1rISzCEWBh>896BP_W$a)wlE( zVw!bT^@_z;3y-y?^`jYx8p(Pm$yhm)W=OpGi=6-XF|W z(p&|m&pcPjKR7D_7k-F{hB|nPSNciw^P^;DXWz-@^X+`Q19n>G&bHh6f_|sc4;4#7 zOB#MlD5!2IZohvhMid#*s7^u$%!OAKoDn_(HM;npMdIClKP3D6{mLyt(ggV}A_5Kw z5VS$7xQZ($RSc+bEGZrc)Y1aeI+Lzznyi+AIp0npK~|tYR6jSMr@9HbDG+koFeC>A zPT|4hMNiX`5?M`dc#MeA^i4b*8Vopj8ycE0piN8~n$l?C8Mye~H?bAs*;cA(2o04$ zu^W6HJwef6#L)Ahi|Y|pUPwlCjWBwPppf1R8_&)(ce~&9_-w@8&K$xa2+?y| z+8L1pk=Vt7n1)DH@oFh@x`L@?wI+Jq1hUGqt_b{tB4bza2>K2|SAjt=!l|lqp|jw~ z>y`gmT9-6U4`?+hhFwUr*7Ro}Fl!@P?Z>~t zn2GLJj|PCP|7xo!tg_R!$`(t7OR^OS>CRhQU__0kLNP$Jpep8rQ?mO`)AUnMt@6%J zj{pHhpwKbBAyxw+5w`^Ps~Y74Z9_Dc;CHyh_)KZ>U!^hocZJIcs0DubCK@ukw4zUKQN46SJe)nYiC`mXpyQ z6euA~OVL@)xk27wBUWjZ=)jA^U>l&{$nPQ*24zp3=v#lzY_fLG;U2!yGj%n-Y6e`d zfT;&z3iKsaR{X89fWj=nuq<3?aVQ0-mwbO@JIfzW%X@yFUuD^gpo2W`{8+S;oV9xy z$tjvf-7q))*1*^d#@Ey@mN=GYp?8sGeL@p^E`;PY#KS##CTP-ARt+3ai+ZA3YCI0u zgysNW7=}Z`mJu(t$EVMdyqm^56XRvT(H+C^Octu!e1@{RT9VTW{&_obXWyx31HyEB zlhijbEf!aeAU_XJrS{(02iQR0`_VDizizWK3r{$fTymsf4Qu3AU;Aqw@gXQK~2qp0ibo&!Jz==q&^<_0`Xb&*U~ zm-}-O&#;i7%AIMlhu#B1fqs5x{ur0{hr5rj6z+Y|2^~P>d5cT~Qz?MTtkb-rl>$L1<3)yDOc;20cb&dd+cO~ z>$XuO{F{ibEN2~X#R6quC-$A>bGYY;b3(vrFTU34Uh8|`<-t6D*cDAeXoe&_A4oip zPN*wkJ8>ABARp_@gC!nbXjf45%_xeJ(a+4q0kG!N)!j?vOMGMefcR{9Cb|GSK*YZ; zEfltksjaRBAATD!#vf%~JmR0OZDDydN)eU}#ze^fu_WffW=WDKekAiOb2*+`4=o$? z%F5PPuq^Dor`3#{fm~-;Tf5FEQ0EGM zdHh2z_T8hCEcHr1spw^>!ntyAq@MlM^Di!a@a5U^F%uIh9dt3H{uQV(%!dX6bkr?= zHa@2tIvyqvF}hlqrmnm{F#bK$HL(v(1)cL&7%|sb*DEv`jm}R#%!UuMtBw{whshXE zowvkL{!_c5IQRA|O%V6zLlVSTinqJwE%A=0yd=gdFeb6H$e;5N%%K?ZG-n-NV9zo$ ze3Gp0eKyXfqLV4NeM@$t=PTd&%PQoZw=q8z_br79ZcU9Kj(aOe!;C&q^=6^uVX?4BWFDUv;{uRmFvBaXr@7fhIxGToIVF zlx(!M4+yWAef@zYBb4xfSJ+tK4LSVMETZoqz_H+`IJU4zjkhf>PVdRGwuGY^QcwKU z(~Kh=PQ;sPd^??a!mwebWcH!FQu-J`VSJh#ZWAVd@C{=q^T^(d4Vc)svpS1(VZ-Yn zd1=;W+gStlLX()Ri+Nw(ot>oe4u8<_lzAx}n207jquy50ZY{&hs__gB)`MdPJ`dFZ zO?)CIAON`YX5vr5E2|A1U*OsOi<1WaPIE4?{it&U&lj@AC69^hp)o_ALaZsrlKs66 zz|-pf0tirxRY0vC2ScnP)JG}|y#i59oeodSxBmmJrCm&`7__f|s|@to#2^U}#{Dj; z#Mxt<&>H}JcVCuCJXP{{;edcXUBTMG1C~@klpJ3~N7P_70AnCUh07E*VZWqknDVe- zO#bwGk8Ko7a=Q0;w_fFlpV~u!3ndjY|xOcBpxx4osU7P)T zcgh~VA@1e<&Xzih@s)b3ef2!lIj5;!^%FhkOyW5w)Rw7h?d^Hiv@Xh9gz>#n77Cv{ zc*xK8Gs#=e2q@~Dk^3w6cU~%9yyWD(6pkb=F8G*!U}?VLiAy7+cX7`$2AHkrEO+PS zrS*!$24_#j37VSa0O^QV))C&9iE(Lvmg(%Br_JeJ`v$PxEWU(RIcrESzPtN3eD5W? zI;!+{PIKe3Z&UC3A{X&>IWbx9Q2%xJZ}yuY7DmMaM+ui@&sBpM##6}31tzB-91kU&$HzW-+7CA@Kh$~ zQ4{*)mSzT$TSj7rV90xg{n+tP4t9h2s!57|ipXaov z`Jsje5`ncae)#UMN`>N6@ztedCObdLPiEEuD`{Y=>a)N&!z+07ZbZos9+Kkz#ZedI zt3-+@0)43Ys9Ks_u(1V|qaR1K_yw0Gy?aTVB;~WmYoQ7}#xJbJ*o4JUxA!MbVns-0 zqU>!@SukZH{n$hyy>#|}7+-65=|wL-a+SmJZitOIuTgK)wAG}k20dLu;6S8h>L8bDAnT$fK;WyxMwXUqJ~5};EaqH z3%h}^n$Z>H$k^Z)g3Z%TjbHU3Nl<6a#NU9u{xG(S5c=li(hAX z1Tje};%R^@N>aL0Tj1x2b#Wo$k^PF<)A(=))w)`cxgd!anmP5)L zD5ySw7G)^&PMXN5E*KS3M^0ssS5VBCKBm^1tWxiUQQ~Wel}tqLD6x~{dk(&M>zCC( zr$ogb7oS|Hd9%)1aRRQ=9LVjMrBP@v6T;;%(MWG4%c@|e;FvdDqYzq?NfKAGbXEy*z z?s$*d555Zz#M`-1F~I?n*<_i6LvI@0ldz5aAz!{H5Cau%vNaFbTvSklzm0N`a9M1gX*1;^c*Bii`mU;H0lI(T=t(j36#}K1?#g+vI-*sD5~iF&g2f zbP?b(6a+de#{{6K#lY*ajvKC2cOqmBvkF56nT^c1eZ5C@D@~OkcIG)Y<2IP&7>NO1nuUvkQyYjRMiTW9(C%A#Q;mz^njUH^-|Ca zn9~RPvmgDqreG+s=jq!(@pep1V_Yfwc2J={hX=d7&6RSzn5W%q;L%i!WD8NxrmM z$}HBw>akV*EOrq88wcB?$*LDewtv!N9nt6>@XW2}N% zauhf2SuJLFlV-3e9%W~-m!ngGz_lnZzI53Cl|wTqa|7(^%96G3{2Q2a-b#U4gXJ1L zEk8>6H*P`~#YQ-WoS%+Hzb(boK~YVE6qHI&9sz@Bp<~zk;?9=fj?lEL%-d|45wM+z9pO4d%En#nY zjm{hNCnigsX}<5Cx2wanN#s>pa|&kHkHs0R8Feo=?m1SAEzv(&3*%byo*-{&-kevt zy}Yy=oiIL{aY(=eae~h37l0>=l%s-q>Y{FrQwcAT8ti$gEROdx0Ko_*HaJn)GWevV zpkN_*0wdb+I1-w0Gcy9iEvILKx+PC$n=GP{kVmD?M%Vzd*;&yjC*h>sm{BbC%d?R| z7w~}`w#C05&Lnv)F4-&X+ZJAIk^;7l0RTw1cvCwyzqFg$8EGnA2;ph%T~?wVYLZr2 zvKn&d@oK8HTkV&zrg`{7GsY`e0miO?!Q|7Z_V>+N=+2 zclKhWi{P=jGr58fg^M`FhoDHn&B{(qsk9AS0?3(-(#Nw-I-P0jP~!Gnc=#y*Vm>p= zUn3iJBI#X-x)^-;!s8^2eR?JD&)g3PgdnywJzTj$r!14hfI%vT6^gl`t$2nE0UrZV z7Eu8rIVV;J`ChNI?=2|mf2DJNp;~N0#qHfPsZ2EL1JDm!i+wRd3Ow^fM1VDx>QzlU z;aPRFG=I3PM?$dl(GiRtUD_M-QED@GR)%k0O9-7EP4W@OaK;%Or?Zj)gd-WfAiJVo z5fD##Ed5CsQ09jzBK(IAp4{jorx&&+FH8}?B^5rqn$sn!(6^IQr>XQeNJ5j4s|u+U zG#_&o%jyHiAT?!IS^tCgfTa>NN_72Hr3O#SiBkTJ_kb0rjPM%y_@wR4@(^s?rueq7 zp)r6-{&;+(f!{gtJFOrhhQZ6sE$*yOf#_ zIEJZXItz#}t?ybT`%#zFnM~euAV9e+5yRNr*fbTHL}eY$ah~rJdMQ(<+2fWtdw*_8 z+A$bIowkd6dYHi1Vjc`MQ$Uu8sY52cuD2uKOr1n7wx_kH>A{{KhHZ(@zrVBTyqCxl z?Izrl#o2U!Jba#P?hfM`7~`v@Hr%ix1c#e45^PH}%HH)R8 zLT)My&{77AiVR2+UI-BhM0S&#mM|f{gz1$ECVaD%O6ejTlmAUxpoW#2%hb@WCR`EK z3{cGsi4%T=k&~{k0?Xp{0*S{N;WeTv$qS+u6|M7UEOgD;#n4r!Q#=eq>RJ5^WP)m8 znuqOFOGbmjD$VJtmCXo04NHn9r*(D?!$PEh^;;}>nw4l_b*=ZDbH2(5Zfse3zM_&5 z4BtwcA%b0W6|?fstJd0GEAO1Qs{OMLY7fhvx2s#(icHQ|Ip-~3Kbz$rv+}Hind?1G#d2ze z$yk18l0As#nJmBiMtw8M8k8J;Pd#nmS9S9c{D=)>L7PhF!~2uzpcl!p5lCI;TUYUi^|%6hid3y$n^1VG5 zHfjEq;^Ci*Dx0(_s8<(yY=;GN6K|_3Ox+B$1+BCR z4FWGd|H#TB_mLEa@(TGv#J!YBCd4FhS#0=a_JxYjgP;xzqE{YB#tThzT!6>*gHeXc zHU;-`bnT_uNTQV%Z^PLI>MMqqGG-+gB&&B&x(ppZFuJ6a>dTU8?}NC~ED%`7rZl zc2;;wo=yYbJ8yYUsCys{2i}3-5zmVkJgdNTBBlJx6C!`S7XX+_`Vk_^EXx;j5`eM! zzw*g^-}NE5wu_}K#=jHevWHD<-Bo7)m9I2Qb{&=JSnJQVs1MC4l zswr4PFnF_q474ph)_m|i@SPt{3`TW{EXKZ|YvRY;)BI5Z;VoHmrz}YnXo`{&m7!Qx z%JDJ1lLED$lniGEHB$nXD{LMJ3OzQ| zv>38OLGx>>1RS+z29EH(mMoED9Kb{~ma}?|qNvu`n2#NgPCS_8>DKwP3p3F3_I$+i zl^lc_+$S^ktxp;H&Uu)Nm@%MiY2pM+kGZf$eX06-|H={sOi$qPBE)(0PPKjLq7Wf_ z0a&`k-S1xf+3lGd=Lm<7?tII$P%cy*&3nkT$2| zm-FcvV29Y=X>((-0qh&v*(P`FOuLV9k+<_@>Ee9f#T3Y$d6C}Y60;~>&V{JkZ$~5r z^17lQH9RM0JLM6d#kj;XoZz<@1HM5Q^XztTmx%A~d^_^XUJeiNvuIXh5ma~$iJ)nt zq6EtG^6GugLNv>4cxZQxK6+GFt?d_Fr`eO-NZdgq#QnjX>I@`W5!Ilg|C(+ODx$}t zi+RT2KyqbP#5Ab&hN|>Q5^;TBQj_ir4K}2|GHj0?FC;{j1&r>GuG%|&)XWl~XRXPA zsS)jCEj)Yvv+PMS(euSQeiI@{+4r40dN*!2NXHF;kn2-+>E7F*3HiE9=e#!QNiL|5 zRiEO@EKP%D2%fmc9ec)j2?Ez3h2jk2mWq`KmOp9RlLMZ5EaSXG7)P1wbe(uKSt!L2nW~G0$UC$P<}8a4!=N1sw87&JpI_AJLb-C!w*S^Tq&KMhrO^viS`8 zod3I_MgrNzMKqMAdVk&`r|wSacFr7=CsHdIe4T^$f?H#&9xt)2#=M%QSpY3;d*Cb;9X^1_W@Q>|(N@1j?2vfl)#m+FM zY)T^_S1<`*OnSC0U(76X`FzhpTJgUy8QWsS|X^+ag zQ{RiYq{~byORmexEVx3=k_C;~iLrh_cDK-;-HF4`?mvY~>tuA=FsI6k&AorTDre8S zg=byN$F=;GKTR&S&^qg5b_Y8jQ(v*FQr|lLA8Ts?qN|>RJPR(YDu`vg;aW_JCWL4W zeGxs4%g@C7En~UdD zq*&dsDWSa)?F7V1O-Xk^SOGn5(LsPGOO^EXnH8DV600qbNs=@=@$L2CA?@r~u-Be& zPHY+T0&xIv|D2&^8Ln!d!7RynXUV`8{&}O?*PM0gzOJ5|V68ylR#2x~PPU$mp-=Lw z?kHTCvk1rGA4EB*9tSq49+hv%PW=q3909qsRikLsRwk=vB76e0A9-3KXD!_&#CH~x zX82cbg{HyameBnxiuzf5mZ6dDLL(^9QO~$(fzb17A{PgtSE8!5APu7aN{Cz3`0RYP zBf6+dq>ZFAx9s$D>N$1)%GtBUm*hEJ`-IQ)-rwa;;Gvh(RRhk~&X>eim$%sl^7>g zP*PxU1#^fiI&B7Xa0<9tzBa6wL|W`otB5EW@HGH zqlwn)3i~?pFT?e?{R zEib0=&3=%eYeN{}P8swELS$HFhXMg( zbOw|tqoWE<43s3r02{tHTNLm+K+EZ1%cR1Cdqd~W0HlK73nmhXjq+s>ktQxnp3*nj z`{Q@s-<80__RS;N&ePtLcVzx*KrtwVf}V;(a#p=iqic<3AoR$W6`8-K?{z#}mQf~{ zBHH@8BXzQdU$Roj8L~0Ec<%4K=B1O{R(i~s`U-|?VqQ=nR$BFo|3Jw#p8Q#pBLzw3B%PPpHqC)UWkAEQi%|^m=Sb~ zuDy#oyQgFx72#;M^88YKqdBX{6@kYzb&r#-V=J1zuUwAgnxMNXxYGhCaBE0!`ERXN z9(#LQmG=oPAW-X7cMk4Z=Uqn=k3lt5E&JeBiterAi22WCsw%3JeMQdj8Gs=Gj+R}a z&ah6Ag0C26-0K|S6RkHg&1qs~?9EZSdxypfU-=XIn)>r6#Vbpv=dnS>B*YRydQ$SF z<;UseEOGQOK6ehJ@R@{l7@yZX!hi1I5%IojMMRxWXO1%%o3_OFXaB^#5($F^f>~qo z@?v@q&ag4pz`HIKL_Rl=|JFDX%T~IaOdze!Qj=VqHewS znmxqU>3%ZO%7OmGeZ+lhqZ+n~1L6;4CW@s&A&`}HKM*)7HUl^+#@{2a`G&_Pe~^<3 z6o=)53|`m}3~Fe{E(B1dz1adrcLhZQ6gR$&0a;r-%X@s!T%S1u+pN90i-TYp81G6F z--J|{r~PY2*`*shJU-kOByMrjxnS{hv*nvM$|miiqW+#&r`!42n}~ao=k?uOi7_zU zI&Wak!0j9OU!FbBxLTN+&feH56X=1=reA}mGslCi2b*TWixvr(W&}AvrUi^GXTHcI z;JaT=j%;}HWh#6nD1gW3wrcI9O=SU4pgbC{fG$InN)r>2gaTGf(3%QvCd4=EI_@GD zG%PG2R#cXq0d(nrNwK1f(hC(Agvjx+HT~63mTVxWXwGK5PDvq-i-$V%rjCNGF%#O1 zp%90RqnqT70&~X7U~pbvS&nNxL_3(oky$_^b0kW3TPS<6%xxR;N$VOr=-H|C}Ex-$V-~I=QMK{;d9i|=qEY!oS^Sh%NEu$%#4|C?qRbz56=g(j{j{)X1`*5Hi z^QPhJRJtH6b)_p4``sK39Cw2#CLC}>V6H+bypO7~vYiIxZUsEC)V}-hB=h_0N`I|ughLqMkAM2JTkDU#he}6WcHSf<_vbI!8|Mvs zaXrX4yB*XoojactaOcD6e0C-gixcSZPhE?%L9PcIC6&;{7sa(kG!ac@{usH~=z@hF z9i=^8NqhcLT6A=vu1qOIB5KQwTA&;AzOu0&7!vtkT8;$<36M^!tkfM%7<>UQHu1ef zMdxnHG)dgU_`LR7TxY*|@_vwzbWt~k#o5lUrytPeT>=7tqy9$m?s;)1?vdfSgNc1M zJ3xDJgVD)~LqwDJ$_P5eY3g}Ex{`P=dJE31%naCF0o|=SpfJ9EjR-RtM>8i?2N_d; zCu3w_mG$0WZu}t*!KH7=k$v;tpzQ8EZRQ=i*w_H-UNdKV*d7N3=hqp{xM1wj>>QzSv`@y@`EqPCyW2R;livY7VVpVJy3ji5{?LSLUWoNKqhAn4iRZbMO ztjNO^_&(eM%s@3mCK+fzvyjqg7a0kDWkuH`8FsOlVg`s#lLBZ3%vov2GyYm-)--FG zXCm1UnDcfry-_s#f}u=wCvz+Y>+27h4Hp-SbSU%5)&GInKzLg!A(G0ZW zo9op$&|MpOCumNaphnQF2IZjSd#6^gka0-SVT+vL#c=p5Wx3}Ri&%@UEG8Pd%s6^u zIOV~+TUlkVl69_=$6S4o!#vQgx@RvybuGrH_&-3y#AMWSM$hurXE8ZU44Sd>?=vSl zDkkUQXBV#7-72>Ma;&d1l3~FKI?w?Ym!NL|rghNyRWz$%a2_$Z~^LRPP3K)St?i7ZkDgKI_3B3JEJ+TS!{~-?P*rI zaz@iqV)iMY%?LFU&%k%ovla(t>8j5f&91s%Witrdr|cJ+X)`oGQHMJ*LR`}>E{?FvQA-_+WJxJ7YEn2B3K!I5PnV3gJ8I`0XC}<)b zKmLv~W{6jIgh|;co}(!VO5P_&$P%e!iX0*2(wMFZ8U%Vl_F`yfI4XSv?%9oA4#(Z$d=$?cGFFE(NA<5|xlH>7*9+X; z-@Obl7s)bMCi)t5cLx*YSsVG(s2v5q(NJ=bM)ow|K1TVB20h(s)|m^K>R`@ha4*OtQxk>%qvh<)1O(y9aWQ`J@kCw_$Lq|@ibg3xjB z;X#AZ@qo$UgHpzjsRBlTXAiLW#R&7q-*ZZ;2lvCYyL*$I%%EH2rP$K^F;VfBENPM@ zi7~w0&X36Jjjdtg-W1}&tUzRO1Drq%pc(TWm*yz$e%Q%h-f(Now`61 zt4IjK?pc8Ye5@rxa>RtpD^c$gM7 znIOo>E<`|e(uLIr6BX<8R~8&Sr?Y|TgGqj7(k71*8ogW{9xju5ymjyIis4SIyIvbx z%&Q>Df$t(9&y!p-Oszwe%nnWf;%heM zys2adL{N=x;HO$>@eS$$@Gv#|k8GtlrfP-}{jePL3@l(Mh6WjV;z4J7f-$Rc$Do&d zV8j^Ju?fW&Tk069Fyea=;gA~?fb!^XkIT~?ow%79%KHSR$95>&h&%DYRo1}_^!#J% z;UKha8TawHPmNa!D1Ob_A9FrivIYg_pDjiy7U%~0eQ}uL& zS>j@SA_a=v*VFS9#)-x@7_0MMADp`b-xKpHROkWiNX)yu9FKEPLdGKaWD2r$Pa+i< z^R6|`zb7UyCIF`fOp6mpU}|h514sZ_E1}~(w`0llvj2cG71f0)LtBzc5si214pHsQ z^YO{U=ld|eRr{YR=pYto(sRP+HVIl&8jUYCP9Rq0hKzl1Wg1H73_0?^#<>R0+r4U7 z_q%}*nz|npk>Bsm88&-U-^+_Z7aA&{+!8Ah#}!s;g#5CYn24W%F6j>(m>-9Kx5((U zK@zbS`|{TCwl(nG${kAf?fwAGS@ku$nukWRRemiQec)bz=gcOn!FA}`_p>m9q5(Rp zvk8~MJC*^BS}9!1ST#ZDMgBrStfZ`04|LJ&{QYyxPGYVo>N< zxZL=6g$1$VSgplTqHYS)}KB=G!|rcyYfr6lhT%!Dc=~i0(wlLg8XW@y;Ke~s_G#(lNs;AQG<1Fs&fVByYGGlhCNg;fP(O0i0(w+1 z1Whv=3RNHGMpexkewl6m0?LABZ5L)~pu0wrdVW{k#W;%U;o6l|&QB82vRFXMWs#y+ zU0SJ2Y?xcJ?p_=DNiTpfm3GCCQp!$@A12qybb$;XSL|y6`CcD6EiSD{+$j~FeC>^|2Gfg`sy z^qxb*Nv4&mCm!|beUPGNacO-^cvySx>?N)5;`fm}xQEbrVkt;hII`0M#P5g^?u6hu-vl{d zNrxUDj}$dWqSBx3fF0Yp0bizTZ}@jH)t?vxF`JBJJe1#1P5@FLx+F_V^q4X(j>qBK zRM)$&p$jxxclXjo5WA}fWQtSx4j{d*c=OWso{ zOwO$|*x3FcRcvhtwGk{~FO^y9YDiI$qQcnxLh|Jx%#-$1yFO|n{`u?3rwh1enzqI@ z%PM0S=g1$nzmjKnV0>b2RMSLv*G7UK^qk{XuqGS=WH?cU!Yu6R;U06=%Nca5yh&5kTv!HfeIVsoQdmuRaVip>^YZZ zGZtRVP|S`ggKZ>g`0Ydk+}+FlcV41qv`S~8GmVJ`ji%lkFuw&Mm*ynIL#Cmz@a*e< zWwe~ru`H%l4co|-8r)oB`{NWVs0ppE(&G-5l1o*h?;*7;`00uy1oe7+lRNt9&@Ykv@VA!(^`zB$VZIE>HJLFof$r9j|&eLGuT{3FOuS(Csm zOS5O={^*Htf-NS^C-YXxkwXRNDd>(gsp%>GjQr>c#j4bRX(;pH9~o}gMBH&ikTA_C znEe4b3!9+9OL*Rzz-=~_I|^x@wPh_E9uaW`pJ@V6 za71sIRE9x?GqJFf%g}p~9d5lfa~gZVjC@5Z1m8R94W0GH$jU@ZGN?MXjCmcXcjYe1fyLYfyFc^X;B`=aSYGFik`J zuLLr%h()ZZV(#K2^Zex?r^wwC(0P`RYej@$(yJkn^05L<$xe?ha`v26R<<13CWk< z7x(blfx?{Ae0$Co=XyIK7`CLGFNRq<@V(Iz5F^Ub?2N@L9XWcLBT|U8G{mh987^3r zhGrY-1pRB8N`E)i(pkuMiRgMuf-74$+BE4l9i8#u_IU zKjAY72i;zg(oIlRlH~)7mm~M3Lj8{G#OXo-MP+Z-@v)IB4K z;7Bl#>xZv&FLBjC-$B?57HWuFn*vcD%&6w_CWKpQ4yMBM2lT`}*!Opc2`v;jjJa5L z1x|qd9ldggQzGZA2%7K5Mve(l|$mLzH9ycj*Q#Sly62NVv479BIBE<|i}uwZO^AwGMvm}fDUC}O^QU6`ib zSq9iDB}s)roee-8xU=#;YA9$ci3et~_T!f<_5jtwxcpI?xJL|koN_k6SFJ>@sAR*R z5y1qvHg1OKwD?sPk^pyhHST36X;IwasUeJ8#0aL_YBSgr(Cx$`dB1501PUINkQB;D zd3xCC*_*z~TTo!$l8x?4{^ENgS=@QzdzvQh6cNyq6PifXAc2VvLQ_?>zW~uHM{{;y z7Rzqt&5NLniyjZFhY}P{LjfMtuV)bzQQ5)VX!$a_AMprHxZXt&dyrvUbe!n%6#;Is z$L;LM0z$qZ&;x^E3<#J^B&tGIZm3gtBU&rTV7{i9C2%iOW>?(&hqWK9V2GOnqieL% zMGf;om|FX_92Av^9rA?lg5=OJaq}V7_#Znwpd>h0`|&)Cto?XG4s`M9B`1 z0^hIc7vjzMZaQ1k0rd@9lDh*p>(sj^e)1rNr$d|4y?8-{fT>gcGK_8vn@5|*e1^OM zj%{wMZc73yj`2yBb_#tBrO#-mrbug}29YEmOpxaYF-0r}fQ?<*q2`JtCwiwjMUldB ztJIB0QPw3p%~x*b5G9~blzeufy4Ei7#Uu}t7n_#IdDlphMN;Q|d!i5;!8_kZ)Q&_H z5D~$sRF3U{0c;93fVoHy{CO3n)46I;54 zNE6B^5)K(yrD2hJEMF*+76Be&LA}wmvQ|-}UHGET9P$zA1j`nR;`LJ6m7Fjl(G8p- zqE3y+i7ZvY5O1D{@>rb(9gB+nu2UZ?u1wS;45qhoq|^OK@{*BqZ)(lSNvUjxPiu_? zF~Uw08vG!YScB4q*miUz>9j@Ub-S>roo|KhFueHZz0RPrXQ@)uu)e$Vvu3@!!^O!7 zlJ$Lyb_QtzKG~nurY(gfm-A$%4RjmIB zBEg@418CXtA~ov}-NgHn>I^XCq*GzwkSO~mD30W7Ewav8z*J3I%sEXy6STLNsY7;F zbTKjgb%I6>`Av(BhA8GH4)mKelld9un9!);3@FZ&m@nZ3bke)mCCt`B)#!hLg!vmYQvq;R%Dr^04 zyhK$7x{-qKqoSEqiFcQS2o`6ZFs-Pk-#IQgh=vk&XliaZO#wF(2ktB`scX4$rOtEx zST8oqo;7_(*c7UBtt25g{CqsrlLl7tP-S-SHNwDrb=RJ_Spioi4zt)^)^8qo4w?9dl(&|{nLA{bH;o1+1?NzS5j}cj5!Fseg&`r225{4}Wlc#B zMB39=DWy3vOWcd`^IRTWVyuQSzC3%eFNk?InZ92SGpD0R10*c@Rg7)7p(PY4TF^e%G`wenIA33}pJso4e=j*&zxpCUq8y$TmIF+*t}XqOz+1?lKiZ z6#hj*an1oxLc6e{A~`&;e6jIcQdrD^DxMalkPiz0I~YL?+%s|UlfC%*qQ2J?1d9_b zz9-#U@i+wv>&=uYDB}D!mKfi_2psfZ@ql^&J7Gk{V)u|8Ro$J;UBUM4j1`py&bQV) zFA?9BkDR7H#k;r*|HIaP7L;CD{hiC3xRpDqwaDcBAzy6m(7omk&RuMa`chEAL5#gI z(WHqSK!K%N4^#oA16K|%(Bj|-MMugj%|;L~dU2(~C<}xh8xiLZb%stC=C^wIpG3I*dDtV@*k zGzb*x?B-Su|7(g^@x>LFm?JwvOBG@?0e8M99!~1#1~=tJMuo6k)9wsfQ^NtQam5H} z0@NB3soyyWB*r)}0i`{0QI@>-$?8le?=cpjUz&X@}&zU)UnjgxN$vp9s_rytme^Ev}>@~6*>R7mzj5_bHEPJ~WU!3l-HDUhn zP72-*ii?xar|Y3*}pcEQxQ=&lHi1v5@i-klR4A0`7i0ZH&<&G5|{zf~89n zmEf+Yg38SR27IWHoHztO1aD4xVtRRj^(s((QKrQ!z1Xz)O_H?NVmbRwbBE*TDED+* zoKrle*X{N$j~Y7*rwK4W` zR4}>0#Uu*#7Q~(3qqt)nHcKh1WbxCrW`>LH(v)5Bw3DPw%xF~uRuSxtAv^<(X};f0 znrN9Q@*oa(nGm(w>I%bA&Yhh;U2=oOgsasxZVIQq`}w_EV`jK#{o0S2qU zXtyC}%nPl>;;O(-XK#sf@O&Ns<&P`^qu?9k%ki^B#Ufu-wWUEz;mTi{0CZmws;d|% zpmFb^7%87oAkYYK-N3!GLl(f9(wyOPzyT}tl9N}Efs2zfPNc*P4WxURbMA6A?H{uX4LzCbcEi3d=fgd8`!j!rcS(G+vezjQ4T-4NRNludHE&GNo z)ml@q)FD%)pDYMjolO?RtcG~1eIDH}c)Qn4%8L3V(<2d3N#T^Y%cKvH~ zy1wMD6@(a)t}X{|zQ77m+&22xhcYc zgC`6-0L(c%)^kDPKr|Z30U+=zf6(*MhX=9;^P@ZGV%|D193FYsTQ)H}m;6RYl`-hr zpFvgLZ#)iK<-wEeSP6x6tXdfGBu0wY5Gg&scY@DD*L6+HCR5aU4ag?5GEqWL*glxD zAv&ED&SdxS1Y+xuU53Ky?w;2(t6yR9EBPsSk~TzOno+C_Ce2Brc$D`xsBWX8o>i`_ zl^+--waRImGz^R?p_+wJas%Nj+3FluT^*vcqW%d4sL{!2<%E+u1ZbtntPGJ?{m`?P z8NH@va+S5JKrYi$^}Y^s-a1#A$x&84Vb5?nrc7Q60Z}MuZAJrV;20$c%*#Cg>gzVT z-bQrRnJh05;#+rFkS2`PnigvL0;9eXT5!Qy-*GKT{&m2Jk+`?cV*e={(U58?KF$;+{5S# z{W@C{q{A$q(!)>Er`yQ>Bp|kN*uj$+%YM?|*-+FGGe*Ri zvBPX0VZ<;YihK!$biI=EdL@T#eJ1;OGV}8CaRca8MQ8Y@1R9Jac*5e{951P7qky+L zD&ASZ0Vb$y0t*(1E6nfr!~L*u7Ru+r;m$9Ac_v8w$^vqh*DHI9ltsjxO6G;oBwsJ-XAf2q$KVAo^nY-Lu-dQ7=&XJ-#ya^EdrO35*WRsuAScQy|DMDSfH5I z>}Nj0X>Sr*ZxV07KI(8T0(Po%Hsk4jEHzilZej?-DBURWIiwo0^b!>rgew^`V(gLg z=PlG(f|NTkebvl8SRAp*WH2J0R`62QtUky_y1Z|!tG$DHXR!=K)tr4y*210BBZ_{` z=$KyGz=|*jZY%7KE0HSUSQT#xI<>Qm?iI8!W&avcft%gtwxtSnHO+a!5j9@45!B(3 zR>k7hLW)FK-^B$d2@uX#F^#u>3sVPE!1kBWD|n^Ro*Zho-@(>|AXZ6Rl68~@QLntn zcX*owTii+xKD?BgPS%iz<&$_NK!2NpIKsy_Ty0M@Ef zsg)~j{|7P?$9?8$M6It-pxX)z5>et41L_*{#*3H>a{>G-N!f~=Ed|t`pa);ZnA#Q_ z>e$6FavH2XZx+aux<%H9N8>e%opaV#hC90k|C4MY!YO&4=Z8y%k$;{&@&tk^se0Ie zlKe`u=E?IxrFp{fUEbsJ@_0Poo!as3oJ|Q@++a~Y*&RE67iX0)oR6t&0M1Wr zf~sU<;x22yv^Y&}41SDve2nZ?-#d%x&*ZAjS0V{^#FQOEB* z7g!ljc8EIZl;=gd7^6S+#s)PSmjny?Kr0_D#1=m#tCZHe-k!v0^t3f?jRSf3l>;+a z&Pp=VP3_&e7lYU;|6Zqi%QHJsUFR&!Nu+_}Sp=~+Iw(&1qqYh^UwKLR<+7qAG(6mA zANl8X)qxjv1nQFc_*?v8sf(@sT{5dPTJ)~iL0&8Ld)~EhpU8PHY3>Rz8~A!y+=b-f z!&%qZ`+eQNcg0k1?X^7a^Ek1E6BqkVbBA%$>9fA?;cM!+H>@IpE{lQLVZ_t)LW_8& z6cfyhnh>9^5z+upK(N0-lKU8UIBgEEKx*<;WqloW2v+%!Aw$NOMt}^JozF{L=X@lm z*`c-e^>+~s!-6^m@7g)T$W4e%QVv7?SvhTzw-aEB=1AgyWX<9^sGtL2IT`l|E`+y^)+C-a7&+(q;ou+fYVazL`sNRa_i}dEPj{ z<-o$bHfyW#H)_ZQF$tSLWC+>vyi}yMZ(6ek1dMrd<|Y4P-?f;;zKbKsnWAQ)0gw$4 z+oy|$7-0)ie3nun^h?vkor=+DDm)ewGzShGXyR9L1uI@w&{SBBjLe`((?J6Ti6As+G8lbO z0SQU|mOM{;+SBZ1t-aQ3aV@HO`#%zNMs0nF)3Fs{uz?DYH^%#IS>qFf!sA1qjqusFf*L zkvpi7t@tb#(m!%ZGbQaSKUpt`VTq+BPD^wVf=BP@Q z3P(-;(Z&_uH}Ojs-p$WV=6M72I<+1De^Q z49y@II+76vT-gxPDd7Ex3*ALDf8Jj?n9%|<=J7h*><^<~k?#%QfTFq~6vc07svBWO zP)WR)TosSvtWuz7j<7U44t_GM(uuNM6Xow>01XD4PNiW=FW?wpUqwCC?fr24WwS(xTeK?2-5+_G9xnxWHA ziJ;I>r{|W2hTXPbS)P`cf%j#(d<$#52yI!i zTu5>ssQ);LAgyzkFGpR$wTLDDjFuCSeU&zP48ZACbue9CC&jjUkb#2`F zOwluZin6R&1x$lPp~=dxa7u;BoY0w>VFcVFg8366VD0T;tw7EM><>J|jOF!cG6N?X z(|DYmhp+&^lchDdT$+xZm`-AagBmn~FiHPocImbzDXy!2+4zUN|v{N0$! zGwR}JvO7zj+)s_p>-3pD@=C$@5)L*s^l@aPe^2pMENKP)HHAiz!#$@Y{WR_9mSihbokIzjRVDRCj)DI zbQPtc7deR_&&I5YS!567;OD*JhWK_-T}~X%XTI4b?X==?xtMv`E6)(B3vZ(d2o)EO zUWKHcqg%_GW{3o)N&zDDao6!m-hk^oR5x@f*^08Qu8Dh?RB!s&r6t)c?4IAlv$n@K z2nuc@*RN&N!FK*#9AsxN4Aagh-_9}_Kt_enP~3+BLoyOkV8I>=pDPBuvWhk}Svh`{ zp{4j+aIu9V5>nm``1aRKx>6WlDC;xLA8+6)$+(%x@3TZBN3}`tAu6q_4wa&f@h^xy z?a4!BJDflm%;S>kSJZkr#RpwYW#tqGR|#@cJ4_NuJa8_;91Z;!50-Hy%`LC~8n8s< zafxZ^y75i@kNls~kT5aE{F-=Ex+r`N9f7!QH< z+*6{X`4)}(ub%UZ^u$nXQxPczDg&qJIVg$L!CuDzW708BW(a^ra?@0M5x=M|9TPZ2Zb%*PN!L5`!+-Kj->!u7BQ-1qWg&~DX?;FEDXZ{` zmVz~+j2Q}{7#HtHQ!f5pY|k^OG<&7(U3=pE+$EQOVxJXUaWNm%mi-=O+$yrEX)adA=o2tD-b_z8iG$`LiKx_x7&OHspnrw%%0A)9dvth+oly zN0KLo1USpHxAF|Lca^dlj9B5(A_yGXQH4n>-T6CJx!IsAOUiU(O}`%_kW(~ARyaxw zA|U`{1#Vz26P~)|&e`JD`L$5Xrl$PQbKaQnM2M_iJU2!V;hDfQVg!$sG~cWRD#IW> z9^DC&LCUFsot197N}HrP&Dm;q+VgT5^JADlYp|;VYUVm4ERbGh*aEPTW0n9bSOp-7 zu0#=u$7s`g=^yOejs9$0XJYCgNffLzO|%#&_g4nKBv$b&I?ro^o@`-Dvb48o`y0>h zCSTi2(Bt8;wnvIZ_GofST#adrG{~OAzudCwq}e0C zT{~b@2Tezq^d91Hc$i2a3T{!qMub-s2?27$s0s7M%$aSZQ7kN?Yc}hupNsea$xF#) zJsPp2OO1M`krQc%8ql!tWCQ{U+}g`;I}_*WNgJo&c9K;uZ&OhTEKWgTWl<58?|o=j zRn{c#9iXGj6vz+C)7m}E&tmG;N+nmqRH&WD4v7spv5%8Ml} zm_EDX*)bQ`9mXaw#@CO?gg`O_3aSSMRD{`&tMAc+Smk1CL|)UL_WmxmB%F%*&$d`D zanv#^5sXmG89WHK^(@%a{yn2?jq@bUy9DE}z2=R}WUmh2>@Df%gM3pvB2;R(vvV<3 z{#fkh-tKAo&|v&6tk2!D#>lnyzp_Tj*%P>u>^nS&&p=jD>B2~t77P!;oenFevX=@1 zd59NyRunKC#|WF0m^{lTuURh0qWb`*B(0DkC`g}_`C@7(jTWPZ;q zag9tByK6}iHWVyfyW*+~qw(v(NVpq;T|vDzB3#R)rS}GXGY%m8AV6v2Y z>@cLfWIfwHVgJ!R=v3>CQEKm#`lNZLT3gO{8m&~063Px2%iot*r}dflN*Umtxoa8o z970Qz1MWWlJc4LCoc+O;$A_~zhQ_>>-qXzk@X8HrY7MCbF;w~>Q7zz3w6a$|p*OHh znC7*!^DE_f#ZST(FV~y z>$l3=x4Tt-ch{cJ*s$_Hn{swMV08>Ta6EH79!QA~vR<>Yh*`u;d;?bEN?hV@^={>7 zY=Av+4Lds?IQo z_@Cnq4Gk43@Cr<%iUf%s8;K+mB&6Jg z7y<_loQDGePS2Jw2t8YRw!j&(76a-bK=|<^#*ZIpB+#HBkRTBwX^WVGpwVbF=A0Pw z)-ae?wILedt7#c}rCS7mU}1L7zlHm3XSK=(%R7o2*|PKgG`QV!nq(Vy%0+KYQPG`I z;ZEE6YV5}}O2xmpP*PkV%uv__;Q+N30MQJz%33GUYSv|0$CMf^UazDB7a>nP06LuL z&`N|D8LDNicg{fK>)fS*8tux%vHYHM#`5iGS5|Ynv#?l9tE5|awuNc>9>tA6Y6XTQ zYgaQA)k-LIgQh?_BSvE^6Ckmb;z|pRt12p+W)`Rh{?# zY=n!>JLlkj*BfkKZM2C_G31Qd){C7vlQHH8NNmx#(n8}3m^ZpVy;8I1G)V?-m5bIf z#pTO4RqC2|J;6T<^44D)(qOZGZ|c;!`L zh-T}^0l!5hqgi?HF&*4`?6aKwmsa&&03>_j#*G^|UET|D!IKOJ&H`dK;@h)+owb&8 z78Y}vR`)J@v`lY}m6eCaM>UyiC7-L@^>L@dPd#s)Rdox)WVMI|%&&b-qrkOH?=jcz z2!eo-VY&7+4Npu-5`N8B-bxFEeT8Ky0L)tZtxEh`A)R<~{ z&PvkZPP^=^wal*2CjMDzeOeItO_$awzrSz zt*yu2NS8;q(P+fVv#Pt~VUXA*pUugiiisJ+%JiD&q*%kFn5VTXDd&$A3uBP{TUr@Bid16-Wr%)*d>U*8qB+kx{VqA+UOR|LW)WkYKBY& z^hZTA^Fzks$$)Gl5SDya!?c%9tE8F1kk)!*(ZZ#`zX-yyh>fuThH%=MMzOc*q(1dSL>iX?y6%Rm{kKuq70IYq(KQI~7^tU?2<3Hpv|v9%k102Nz8ABQ?{ZFMrmET(T{GGb(uvNDb0 zJJ`-&Bhup+gnT?EA(5SiW9cMm6C_r0*6-rcorY83t#pcd-7jQCnV{~oVu@Odm}x8& z1d*XQj!*|c+y@cm;MhD)fr2g$imDg+^5w&j8}v(;n}Q%bCMarwLJBiLPNvI}2jIy> ztv(c{e}`$4_6Q4GG6721l2jARYhY&y$pDk55~+ZwUy<^UrH)+1n6sl*Jj_m)u+>xF zAI7M8_!OjcNK9R*i~g$vO$R+Q+W9k(L{iZ|Z?0S{X0Z~v&YBNoDzgOf3+<(XR^9U&e_+zD- z4tfUMaL8k^xLwFG2^kcPh}(SVyP1L*SXZu0@p4*~glDGso39*iE|iiJ282PPf&}0U zi!q4cPY@BaN~O?fko6o>37)oUeWenWsueLxN)HXk6`cXDVD-*)1V4h#KLlBj!P7)# zb3K!o2`QYs#Jt!v-govO&~w&XhC2se>_7>J@Zc=I-IbR~l1V>-^pRDj&Keh+=6Mvv ztYDrz46f2OVaXOd-{W(}&m7`WJ-+ueuMOYc_|RHhBj!FlvD3g*ofib?;2?_m)p=l9 zSpi6}zVMh{a2`q%mpcitA4n*orJD*Olk!0i>U|i{_`;|#)1ne*okI5B-w#(gr=Z+l zvBN%z_?Z^!+85XB@6CrDfrI*y_u3k_1OD}H_jzE9Z@+uHPC=N&V-l%67o10?E&U07 z2)kNnAzsmXyAz|)*EMa8Yw^fX(CzR0DyOMyY474$O|F3-+#cMU4S3EuOQ)W(au2e? z@fc?c>HVQ{FK*{81wEmzr@#eFD7D2AmPhs zag3aFJ49Q?Q`s;v#fghp6xQ->5WA0Yi;H{sY!xlA1thq*$T4S8BHg^lcp@`|gyh>zRbCRTw!yCOuyC?L@_KmCWdEBEH=75K8m`fxKo!&*M;gv{a&b?i)4GQ@7ha*ib!e0rtXrK>F>Qv4uVLKf=h{Q)Kx0T@zaz@&))K* zm1ck+4pZ*j3wCkj4u-5U=c6j}J@53%-5vD3tY}1a1*h=PE%{+}2t|4E<>?^zt##J= zS#O?udp;opbC8GVu9jW<4kEg1>8-rAT+B;0%b&r+jMCJBV=_vkm{JkGQ2UPqeE@^5 zU4IF#EhhD#=MTT%VE2FGco#p6$&mMfc9?}`GQX?B|Z{bc^ zs438q6qWc_2~%K|)QA!-O$0vK+Gqb~-fK@Vs8#Q+aIYoWM%qz-G}}wg+t-q0RTFRc zfT71h@QEJz6~}VF(n_A>onu%1bO9k^Kz6n&pJX>lX$7(Q!VrKuxw_`g_msmX%CjnJ z1u_typ}PEvF>3{(?YlE}2`1 zd#I*HO-)6OT^WRM(CWx!PxR|d2o&Qv92IXmmHv#L=6JMmd|ou@r_PE1}GFi1QP8KJF&?VK~C zd@caAuo;z6(b|yim<&WUl?M0#Qs&2e!>X{-oH$M_0NuO8#6&z%@| zJ35)9Ucx_Wc^MKL3>`cT&wf_tW8MjD;z$qr8l(S|)o2vMD}{DbLkvi9B(cD}krxPK z-OIrD2uC9*%`T&z8Y;!1R{N>ihjqo0L{T2kruCRI5}FBIz|`LlgBy{{*QE6brb6_jM}4HU*C ziRK9VUf`*F{(2X~*zrgw`Q`R{bnmlrf6NUOrP}ttu%yp1hN<^Qi^WI-PEm!?NpbEb7@GLz7eQZ>v@1xC$G{FsM zyFui6bv-Wm!NLv-)8CGMRmT7lB%W?8REHyZ5PP7)SS)WQQ2dbZrVA6njNoTJ=hQ5toNi7_k269;QJ~p+s2Mi+ zg_vM6;W-{%JqDeLwTtP>1WW5e8n)y2>##HOowxHW%U5ribVcw))e-Roq)BS6#c#*- zS~d#zOg9u}c%=$*Ndzhr3YrMG7!mHp7YajRv9}222|S;!7aTb(@L02a^R{z`14fRU z<413Gg4U3O4smVJl;E-wu&}5{RWHC`n#FABRZH{mHBS1G_IW(pruTN)dm8=j>67q2YjpC=DL;?|kSCEHi_eQ47?1_yYh-`7yfE;^ zeQ6z0BKqWYT0u`zknuPH@iH!5kY8}}nt<`|mgj9C<9T5>5W*QqzcVs+YF5^t z36XP5bE*RsF_hzE#xq6(i%hY1htIq}^_X9HG~;X0OD>UW?GhPFk(hz}fqOgVi2Xad zBz33j2uPb@n=WBtD+#0Q#Jyf25{bSE_=|VHAx%1jaB`%0<7FuWmiKa`;>fA|?WPLg zu}Y-{5=3pvjYFA4a_r16Q-G0Gb%_3XHF}=2x^Lr|U@$|LK<5B6F3<-(GYJ;F8#hf@ zjy--aaM+289b$CO@>k$9l)c}RX^*6Uf=ai>y|E9Mjl(X*odP{MmB+BM$5}9?_;0EJ@UK`pvWO8pr8-7@{`D*0=qhX@2qa@)2ILm zO$c^NfHv#z4%M0KT{9PB8|3$8Jbus!nwAuIBLoBvW1AcCc78a!szL-|n4s(IO4KWv zp-*EZg2IA&pB=GieT1DgsYA#|zFm9~F96^_fCW@R@*XZK4wv$! z8LBvdECAXNAn<{DsWsY3e+hClBk@nQ2a63>v78w=2HTqMBL*i}#+(V0^f545qQhtK zR0Z{};;0am%s>b@gXjc2ho5msB;$qR%#gL$5Cje4enY!31SUzfekB;RPFM7R>mIG* z=lRnd-G3a_h{P%jVMq5KK4Fqo=G9&Na?B1Sl3SYQc|8y@eFN+i;nT?J$wt6<3lA~t z{b<|+F{jS;L8C)Ni%khocBcSpWk_I#+KHpz3A;6T#k$p z1U~^qSe6$?&?ryZrQ6JdRjvv^or(-GZ~hnrytq7h_WTWDAU7Av(*?m`0AO0lp!ef> zdbXYyDbt1!o$8*w1>bnyZd}Zr zO}=lEy7OA9|G-03RkFX5SK{iG2CG5NIs8jf*VcJQcrk4?K-s`Y>0_Hl+)^wE3_|eW z@wrPLLSlG@N0iwZIi!R}$;GGIj=d!=t>wArY5pkBd(SVCsC3xK_-APWIuzHM4p3CM z%489(Ik4{H?lobt#XKJv+mmQ#eUwNT!Q&ww(hB$mtTbvXV^sla2@kr}ZNf&V0cPUo zyPa2fct|J{ZlZpThbiC{zF!N{{8@~b-lW2XfOEsZu2k@aMqo!}L>LS~Xjmbc6Z*AOfYL~{*z<^x0uOj1UTETD+8{#o(GifA z9zHWy1AK8QL{XOvX5^tql@L5t&I_TbM~}#ksmD{qV@5fR$R|h`_~%%o1L$9fHXMxw zAjjaAMl#X8WX}t()^?Wbi|zS?$XWilCu#5B7FvX)C?gab64!U#1GW#y8pBR|Lg`3N z(YXgKb)!DuY3XB{dW}pp;Dm-LzAYnY3}EhlNjehCuC$RqTnVAan=@oZ7IN+V&fJc< zBJ69#2BE|Us0~xt))5ci5jetm@k!c@hNzi9p1W&_&X03{QzhEx6$hYLl7>dmNClVF zDo^?1A3yS!@SJCmYwGOfaZS>qxtNG7%3T-%XmIV2{YzeaQ@VAYaA>i`#ef~qOWC;W zlo*vRE*?Tc1&d3lL?#c(hXy3#I1~)%(7;E7J0|X%q3I;c0MYFh=XFeN6DKocl&2uGVEZr4Of^XG|VN6JroZF8utD=dH ztml2zEY)eH%Djp7+ln-(LB(~^!Gax@Z$u@_wZuMWWM(2S!x7GWR9o)C8@Q|X(&vm3 zaj3B4sHrLqfhe*Qp@#^4cq~%aogQH(9d>H?;e9?mk<^wx!jNdaNu zC@xkQg{G)5gps)*-yk-aS3H2%O*>{|^ zh{d<2>a8q+&(CCodAG+LlhpMc5VgM}AL|_{bqbZ`%dq77rvnZh4)Bl4&0xB05-jmQ z#f9I&q@98-eBiEm6h81TOzTsoKT3_NSM4cD$sV918H;j0;QlID8Q!$yEo1 zqo0-8Avr*aH#toxe!H(RjU>=^lDYoBFYNZa8gP3a({!~44db}}ekBwiPYK+@68tyk zjgBLyTK;*ZZWJ``q{B9gYZvzZggj=* z>mMF2T{>RL{b~2-sp&Pa85o|tg*#LsCLlwW4hAq7#xz$tNdG5RiL&70@;(`CvF|LO zAD(oj^0WgjxVyOAvdu(>e>8SdL(S{oZ-mSf9I@~Zvl-`O5n1_&0|1{}5QSf_-8;PC z_^ixoyXlc-I0@55{=DvMkv0i|n5P;3@?v$9$E>_^EWrN~<3ASsI97Oi%)(m#m?VGW z7$?QAuWX|?j zT{3*+&qK^R1qX=vYl5>St9kTAk1%h9WuI!L3;>i|Qef?+1r@gqt!%>qUxTGTVAoGNjh`O}^)f%^kjQ54+f@r5IC1Fuz0 z8Ij|6C~T_^5iftmGLjn{RCR-dy4&3Vg64FQC7x$E@O@Pdpx;7(&@V6#SbEl3miJ11 zbvCBMC?Mw^_+tX)jHOfZUzHb1y-0^6JAV!oVz1aAIccClJy?EK3W=BolsLNnQDKKMoI zN2?eVG;qV6fTK8A@-^sYtnVPAfABpGfE_A40)l!rq0YTy$utI9T03*9I^9Ol3G&m7SH!+kC8;@JU6jL}-5XIuaAtsc+NbiXazq5{nqiU@-$n;>DQfE@KkXqLH97Q^ zN)gcM#n$-jRLHktM^w4SFj1zjKcmoAgK&;kpt(LkBt(Zjp^1o)h-SLnxYUxgdmWdR zp-+CKV~oFvTlR5>O@nkT@5AxX8`A5_**())Y1YN9*sluWH%ixfj-IC|(uy$r)=SY6 zlXrIzu~c*F$g5rf*Cl^d!Hjq=o4H-Ol-UW}$V#<#3)vXb5PusSkW9L`paZWKD z8d%PF96(M8Zv+&>SH*Cn9wF_2uh`sHsl>nr^+QDA)vHW7$Wx|9y55{6UzMvY1QGWw z{5G`?)3;f8hNNg^kDcGVn>BNFUL5h*pwmG9(q?Esz5-f&Rh9-k&`>*!*4#s3*5k`F zkHq^KP@l%qv-1r)rj6jUr`qRfOm6~=m;^u)DOQR|xJHBC;NRZcnN}*0KP0p$O(Iv2 z7Ywa!wY15^6PnKFGi7@>CguwKKRMm|`N~T33SS2Hjw8M*oqHh~4>xa6@%|sC(?0(j zZVIZ1oViYk@%)RdKV=u3V0bWRrC2IrUw>7?zw`s;Ug8!|EAT#MJs=7lq}TK_XDHoQ zIv^0#%+I~s^xCVR6uT3nb2L~Jw*4|N?;@Z&tsywu< zVmu5~D(l^T-%v=t5Zy2O8Lt$>=!SR(wa6;dISmH=4#U$d9xb(h-ItFpiG3T}JVBRm z;OeXL+tSqM*Oo659t43$0!r^|bC+p$mpMYHG?!z3-)Z8TyugXCO4jG~i$xiO&P*}h z@)yYYC<9-QGg>JDwYGN&Pdj^j%wRqvpKtl&A7!Mxt)a0stnG_BA$=yJQfWh}Fd}&gXiBvH>4lpAJ0dTn&Con|`<8FUyT0UWCWtnrDHsA1$Bn{!ORlp-D~NIRUv)w# z-=AAc_xZqf3B!p5Bgi-jb=}w^5VsL>m5$|WKRV?SJT4|d-@kN|`%GRu|U2*kOr83*Y- zxOCrU`P7rGjbG&JtsA*?Fig(0QR`kS^lTpk0yIKhFfD)&ukQoTH{G-Wxn#lE|G!N( zJv|Nm;@VH(!!D@LW_5r3Hq~K%dqdykE2qjcqPD2nt_GhV4;5-3WF?VVl9hCez{|`r z+o%mbH{HR={Wb%f@q@s#BiIMRA9yW%qYt1#jFP8h^qXi9#4&jc1498Snf+1-MNbto z0G{6_<|aTzdmZ2-g;E1aB`B53k#m6GW;%SlfD1lRo_*jIsQ=@ z46^HTIvqkaNC@?S_$9D;gdFobQlBb$I6fSQq3r}-Ds8xtn&JCC@ReM5%7%ty=qc68 zFez>19q{b~BY@TA3qotpw+U4T1IBMapn)hQ>w{{q{KVv5{NL2eec;XX0NpNghR=6P zMNM6x72gXV$fLkv8IT-^fOsG1l~XLSE(2WMn`vbjFZ;n+B!{VESpulHEos$;KyNrj zTE9))bzh6Lq}i~gqNO~X01=8Ia{KjxTEUwt&SU~MIsz3Y{OeEjUncgLeZy3`K$SwS zMeRP&>|{6h5t+u$&Haz`S?;uA3ko2@1PT-)+6i1#5u3!x9M@#aG;g=UZf&({X;zUk+~HZG>nie5i{Xkkjv-W{t=`@Gbf;`2|wz21I8WPE|Z~*BzQ;hsD1FTMPEoRM^ z_$s$S0K4ZAeP8dJjSf|U3wis%I~nbd-mmuk&i|cQ#346+mKJ=`?hUZ$uB(0@xRcUnBQ?4uMz3*Cu4e z5cSQp{oROu#PQTY6jrOSlm5lQU~PKNUw7eaU4gXv|q+?OQqP;B$WL1L!(t5Q|+&P{=1Lyt_~oxvKa1{tV6IKeG2d zI;Y3n2i8)>LjQ9z1};&kuAkKgt}!D~i&m8AQ`F~8*MFVF1s`v9ircfNJLqo(QH5BTf_W>6e_Z-bI$3o>QJ;jYyEJQM3M@Xw3?VA5v)(;_oMN= z@Pu3^Xoy@fd`r|nw9Xa9W``_m`fMyhg%K@N@A$(RQdS}UvXLpDB0}; zh_(1sA4PV%bif|}?~K(Qaxx${6HQ+788M#Nw1|$7lirdbdD!`Q%z3Qa^g@WQXa_4c zTwCS1_vYr@EX}NuJdabjv1(L?fP8DF4h8p0GiI zh)b*k6STYbY_{kjM{-m^0lga2|3LiK-*{d}%ZMdAikMH(UJ6-V9qSA?UoAi%8UR)k zIp-v9-K~^{_(s3_JeKbl9>c1OqhVqPd|Ih&nS3)F=?L@elbdOo;T?x~?OTqu?Bo#8 z^Cs^;xNG8SNtUHCSpLXsmrK4fK57Uhek`8K;x_Uwkkrgp*>uTwiTUnAi#utdNx(S4 z<_98qe^cUb!X1(iO7q+0PS`()c73>uAIq5#Uy{2HX+D435?J@p)ahZ+6F&1^)Z1vq*(Zd8tp&IEe{-luS zFhb{mjb;Ivphh8XFN*YiK!;9k#o;;4t@uz5kx55{977y+WdUtSOgmJ4-WvP2;lEkj zN}pvz`A4;We`tPq*Uu}{bPM0TEjE5N(&g>c;b5V{1uHo8s6V$;@4Mc<(l1Na(KvH% zV9Y?Cm*PCHYA|usfocR$aTZt7e2=Gtg8$$!^D%@6OelHJvo~K-9Hoh`tyQqNKQxA@ zny4AETrIHcF?=%dW2`;Cq}ahEljG|@$n=p_f&V6rbkgWG9V~bL`ueMaW2avp@OD$p zKAF2Ntc!K+q@$UjWrgyo0RnP1w9 zHQb;v-KtVCX_xw<%1l_xik~!fU}n<_R|Q8{@jv><#gc7&jYqxeMH!sqEU)FNVsUT_gq=C1L(~yN zN!kVR)DG?A7TVV6P`ppMACCl%c?^+;AK6~6%F}Bmeb@Bj!Ka`%V!$THeIk{CY+7ny znG#2der)|@`}w%vrBKGC?FaPXK@?InSJbS721gJQ2EI(!1|CP~RAYO#9=I61o<_>K zfrfr%tOe*85I^K?{`1sfXeHuCaP8JD{iq}XV4cnX@xYQbVZ*q_{4DQ>wAP8LmXO6` zHWO(ou`&vfOzMLl8pBTLT7HIx2BOv%RD;7PWn%OtCcWo-gT^ho;i5oKC3ZGOzgEs( zrxSC^cyq(yliFvHb8dk>6nRrogk81G=*k@8<+NUGo|WjNMy$DFA;+T;T2DqpY(=W? z?_BGLCP$sAFIPlBlojG18kMYpJ@qX`?Hl|!VJK3)hgbd)BMR0xtcG4-!Z+caE4FaN z$kKm3qktoz$bKGR*NgoPZ#)NY36emL24cMoFD0-@9JxU7(X#;#Eu(Xd1Hf+qWgA7` zd(N$kam|0U<`WS3uuq=`rAx#|Re%GKolxZChxAW~th7El?U>0XKYKBDJO>`MG;i#> zUSkUdj#^g~gKISEm6-WJ3oQJs8|mXi+t-gzP9!U!$up||pCyMpPv+BG+fUuZXGgLg z2=AOf={a2e;xmE^U`(2t_qLIy0;n5w9nNZC?m4JWEsy+L^YOO21;I%HW41sU;4^s2 z$fL#z4N6R`98BG5d?W}HW7#O9h(5tT*0C#D?9u^VB%gn8>;xODo&9tJe(ojeF2v}2 z1~%G!UFwV&urF&~T?=>xj)3PdtoyU;%8>{EqR7Ly^%T*B2p77hV(#7ca0O6% zM8EXAxmRds9dX7qS$de5Jovw4F{YbsnK54U==PPx-bnviurVh_b9pE;_dP~E2VTtH zsz(-*AEggm$_19PfE}jNjkU0+mg<35H2Qn%H1F0iT$5^88-Vu$0l2|8rEb;TEF4M2 zF4ft<_@%J=;}~$=fgbq?6@!{;3y0W{@B?Iz(OjKrg?##Bhf;iQ z1iP-&O^tbC&B-D0WXbor;l0sRoX8?wvUPB(&$?1-q}}SZu`Z1xK><-9;UDd%QlZDa zotQPEz#F2y(cHSpH!15b933XRv&wS37m70x6G{Phal~~WTsINujA!y!PKm;u>liKL zxs_cfQ-^emY-zTF#auauIt_=ENNvPSrnR(~v#9jbEvqP68D8ZlBb7o(FE$?KVJWNb zHc>FJ5hbr;6+toJWQWDE4M{B&jepv72~H6%8o}QhXaE5s>5KNLgqyE?AD?(jAw*Y+ zXf#4?Y;b%3`Y;sK6dj5Y96cFM!?WUAwM^BxVL*f5xT;noqxaI$uIi zL6@%us1GC}s`BlqWVBL5eq&*6e@qq?si!{cNkmfNx-1G2iIF6NqHr~~ky57$n{a2Z znggbfHUB}ylFZyBYeC(Xbz-)q?aIk79oiCVR=4JT0D-6ms|NHR0qZlv?;`V1q}E1M zzT513`Y)g^nj1oy>$$j?e~!S6&nWYgaXs@>lIO^aHjNOUOD9a2qG+R;ji7SX3bu&E zE0yEtenKNdCDe@4WMmYX5%`7_-5J2uPsiwR#x>s7h7 z?KKc1gNeBrs4{_^&w?^N)06fDGNS%qiRzX*RE>IOu3n%QH#X}({z=@vb&`hwRN_ch zV^~P=lktwt<0UGad%Ckcg@!V6)5^|TjRoU_1}wrWQ2-`lR^p#+19w*<^Qpm)k3}A5 zI!GjSjUuz$+LL#Dp&K;0A`}|ePd<4 z^lKwd=d88n!inn!q+5G_Umfh#^d~g1!8YsUz=;hrQp36=dZ@Kd$~h-ozyQ#5^i}h3 z;@TQE>l@ugj45^}X`dRn!zu*}31C2vg9puH-aP1A%n?_EvZD-hv@mRLLA;F;3-1M3 zn$Os2U=9nl+X(~ap^q1UUJ(R7TmU&O-d-zhsV>F-W6{AjL=TyWc z2LcmeLzQ@2VBBr2MIi|`o`l5K{SD;ep@^(Cb7@eEBB(B9X&g}ea)xqOoJ8etR;JhJ zusW;+!24g0N2<3p46dclVjF^Vf}#UHFOO>erunA6@^48p7bjj3ZEgl>;r_Y!H&%*( zY`R#DL8NV_P}fi4^2cT=IOg9=Mzb*c!wc^nx6Z09sD%v=Ow0EX5Z8{|dHw{nqFNCn z2jPS)Bp`72YvBd|wALYXGu$#T@cA1EktCXTf!02PiTIcGhx~pQsr3nwumNm&r^Iip zFlFuHHuWWWcA);HGGVpFuiC^OfP{ynC4pVUgIjXk4HG6_nW%~1HccLk_nxqY-b1zrfP)frx{16>m^nC`&nrMLh*k`dOvR<&OO4x z6z>(4l*_wug_%U|Nhi}826G)@BMc?W&FJ>-`w|^I>%*{ta9QM^tk6hJaR4Xq7BzK%0kQx zCqx0slSp8iSGV!X~NpF#88^vbBu6bWE`$+)GTM>EP4uh`D?P{L2vao0h{LH;{=6C z_Gt}%RsTw}H&7RN=Uqq*DN&rS#T_c6N8o}H*QXlDDc@(iDU3`A;;es{HSH|4lxYE*P+IyhHk4$#> zrtriAd6Qzjc*|InnaDXu(55xNjWQ&aIupZmb@gi_gm6YS3yW9bd16iVV;F4W*4X?iE(6&0g>aHzUp2|OUA^<8)u+__Gzc7lN$UhqN9zG6h5YUZ*! zmSvrUa(($E5XDJCd)qU`43aWaCmJTXtPpB)dTM*3UPbgmL^N#ceGK|t#tpuLOU}W0 z)QYQ+VJsqyIA{{eVp-&bieV+2@e|u{n4U|yFKLov{du_PFtL)U2ZK;UFV%jl-rzRw zW~@z7I*qV|POS_?Ceq70B^9Us%q7#q7U)*83)M(6mrimpxKm#)F6LN{dy#I{XL;Fy ze0R~IFwIQn&mfpM%>GmcarVwMckOO&syy5;X@`U5WXh|o0vr&lg|CjlRwL0Xg7x(Z zTw7fv)J0hfbDj`>g?(5CVu2rJbEgEtl_Z-xSAq_GISS=UGS^(haBx#S(1l@my>WxP zDAPR8@yEzk$@r;^p>&x^7Gg3o7Ps}@=As7~4Tv!z-kH7)4*fPz)Cjjn!b_r{YiR{C z4k)fPRQSVp(0Ysd4#0MJmt62Id6AfCwl}Dss_GOpi~lP^6@M)b`>;k*^^SPI_S`8W z5HM737@UTCgDLR!-Y8xmtKTZyO?Mj{)G!dd;oN()bBBViRZCod>QAdW$oT8Bz7nI> zA?szeX3vfrCfy zOI)dT?1HPy9}nQO+{rrHTIULqi-6Fi8-EQ@gwXg2k&mLb0*G$EdlP!Bt9$H`v{!_! z(ZjNjKT7aert##-kLNQl5o4=HjSL^{y<17(TsDoL8$h`H)NcR~4-gek^&aHZ9eou;VAuzd2I^ANKIHy>WnCyu*A--IqL*pL*c)7=2j;?iH!s=a{ zp-1tqubX6pa9+9OxG=J)7Y_Y4*BIv9_GbxFyxf=??aIbY$-|Hj*s+Xodt=INx9qzPU zSM5Zg1(z((m_LTXnbD;f6ed{<84k8LdcUx$qH69h%U$M@nai6#ASScnQKqit1O&_q z4{IIDyP(l>!Z5YrFv)S;lQJDd|0GT)5R)-&Ihmr#=toIP!U&V2G8Gsn6s9%{TS%lC z5?|Ls34KQl=@*y6ZQH$asyaM;|80?^F5fu{`9_pNOM7GU|NX)TKY}>y? zz7^`Af)LeQQPB%ws%r{;Y!M7oe>#DIQ99S+=tBuKl`>NsS5AgP$T8$3v|_ILCL$4B zH900NYyc{X0L0h%-h{%m$ah1C1?~G5mw62%@&T)kCR@o?Q3)MN3$?YTjz_?RI=6mw z9Z+~rayYg&cLlN7Jm|~UILMbDJtktQHcL3x&z}MB)90w(|>RJ09=P-hmkr7<T6i|few|QyGSzIb`-E-X8H2$MAF-4nADfGCB5WZn0$+kb-m7dTWm$_!lWa@2MU}5iCXI25ol*@$ z)NV%<_N3pXNJd5N*xsOSmtMpAeKqJGp50CnCWEmh{mw=WiP$W{eC?>D6@dKUyWqa= zEA+80`>fsr*jT}vSzI!RjCIaA4cJ@t4YLY2iF8bUZR!IPe*4qy)D{O0?zYAyzAgi} z)lD!@(~8YSwL(G~gk}>2DhO#iFTyGf0@#*daPMHRrvVsbgWbKJt6%q4Z8mg2#K0gC zmed-CSVGj)9DX0d!SD_O!hP7(lraWYxUt#EoK-jjcFDfDVO1`t4s&0_9JJgIb19Jl z;wPc_7#Z0>`-LR2Lb8Gn5qjq8#J$n5YN+a{K|Nvp=nTJ<1eli0xou8%vSjC>9Gm%= zDY+EmFifAM2Z$9bR%rE^&J@cVRxrgDhD{htp`9HIKjN5}rpJn*kQM(8;i|S;YoQyf zQLk35);gtBUJ`Mkgk+N>+dPfkh0O`$cUkd0>q(a#Y()B3a|ORq#@2x=8D}m6E|hjI zV#qd6qwL}5gz+^TWlEKHS=FubJA%gi+`j9*q z5V4{wMa94|qd-AIi;t}3DypMUfFSAtu%dt0A;id+lPCKMe&Z{~iq4|v24lI@lvU%1 z;6shxXW^vaIloqLXYXPg%X*I0Bk-P;6+u_QK^X2a{utAg$-j||Nhtdf5^Na2(u}$b zSH;Ek(ywxIw8V zbJPaerh~DFm5fr^l}aU**hEwftq%4|x(T-qBY>8P-gmSdNJ+^tb5WX*k>1`I0LAkg z5vezE1OtoXpbUsV3+(Jfm<1U!czP~3Kmfg-KO%cZJ0n6AO%?Ha9@*OC2KXFNmsjw?&6<+9JfD&wE^cJ(zougskbR~Z8m)ULQgdf^P6&`7x3N#R}Q z{?9BjCX|`?AWtZhH<%?Dv1!FPzIqqY28DTfQs^(@Nx^8@KTZggFDf0Zi}*64)j>di zJm}00?P}1NGnr66jL9D?`4u07ODaLYMWypsE}>jUk}Eq;<2(rGRJbfAFP|IXE3}Y= zw3-?s5V;)*RL}%e^XCZYqH%W4KaD$ZD!Y*IuH+8b1}}aFj|L4qc<7u*{Gv!36%Q}C zm=IuAIE|j1YM=y?FnBCvL-qJEf+`k?M{P)2M5GugikJaw3g;aQtNCQ|`Ga(MZjupQ zSoJNbt$wZMT(?X6axYYoj)q)@<@@Z^wgwZ_UF={g^+Soek=q#MYNYM!?Kc8r2}edC zkGGqp9%%eA8h28Bsq@E_W=stUQSvyED25w`(kniVB0*F>LFG(2Jk?O?i!w-~GLcz~ z??PdVlLWC18yR9BL7WV67f$P?K6me=5Kv}vpPOAiK`j5!F|#7aAd1`9MVAV*@8w*e z8=dRIP0G1kE}V10f(CtCys!-qEFsEtsgKCdnRf*~cVjJRy0XI$e667{A zfNJtU!eD1drv~rxP+ekl**WThf$)am9WV@U9QbY5F2Q|NOwwS+Ob^R3`IksG62x-R z1`m`hzuaU1qy(3&cGOFlIT@0Xit!LBK0ZbqqMsmHOkg5`vS_1)J4iY4ttm21Lqchb zQY5mCQi1NP;?aC+Z|JJ1Lb@tishkUaNu!WFT%<+{jc%&{#zvZAq0r@bWuYQKWduP_ z-Z1-ncW>QhQ@x#=-R&MY2$b@B?-mBOEzARM_3I<(x;nj!`<+_{2Y0~j4(gD!SawD3 zU6i|dwz0SW4Zf-SO^U<$;EU{K;ey6jl!7!zSP62dY&dA~h`7P79{avjt={ju(!55# zx?Kspy1q!kn*uF3es~hI<27kTZd%N$i)o<0?x~~~51pfmVR-Mpn_Eo9WxA$A>4ergp zNZ&7MBvOXn?GCo=kp;TP%^I~f?&pGeN4wc{H(=ulk(rdDjX1~0HZl`Oa!A=vn!x#_^O5DA#&TvupX;KF%*DcG z+1G_x_UEfu^`&BiShFQe00LJvdR8w#~9y`^5!Xq_RuL@H8 ztZs)P4`kQfG0Yg}j!o@$Erd6yMV+AESKYm~_x^5iwiFz!@V?iVdtslu_xmqfwtKG; z4(|0^qSu96^?g@ZZQ-L90|w4i$--g0)zw-fvDnuvl6AF@Pdj@!D94aXNathdIfn_D z(kvf(1auvF6UAi>8p{vM5l#XU^p3p21HRnv_v_B-kXq+D(Y*_(T%8&%Bo$C;?Vv=p z)~JOgx&H>Y>wEqB-aYlc?0e(_t2|t+snpe4uM5~u*(ug_ecs^jH}(6uGVf34YN=Po zzfAu9wW^cfR`(S0r&}4n_SG4wN}(JhqZ|=3PUpdzzOb;=2Ef;;mIPdZyeVhz-QHBM z=aztjTf18SyWt9MaD6NeVH(tiLngI>8_^678i=ZTSVk%bA{wC|fJn%W1x89BBPR#2 z2ELYU==5tjGv2ZR@Il zc|1^F%Dz4pt-8iNGSW3Y!EM~|_7Eb6%X0}K?Ak%IpU(a&IOwVB5UCzoZtO^y7owDY(uC-o!TVoa%P?s;2cuwYMf71 zO?2Nx*F=la2SH-O#Lo{_m+3}EW(g*YltQhEs5l0*VBw_-g=du{4?j?43eN+9@SSM` zhz%PvIFt#2!|<{J01hLfgRQ~9;80n1Azehr0VPhvSN)pJA5ud@ESQ#jP*U|wA3E@2 zCZ;&L14rlu2o>=R8dEHR#skw^&;g6XWGq;yqScL{@qCggRd_m#hzFNM`Wy5yMgx$HeRm?G%al8f8&U+|x6kQ83c#y~|L( zKxjP<2p#4LCIYHO0B1Vc*m(4a|BDl#)ysMy;S`L@w7L)|PP9bC0D~feQvmxC@jo8) z?_6jzyky?_17;S~>p^E?n<$eWaFkJ|Cj%rfJ=1_LO39fCk9o6cH9wy@lbA1;YZQ*Y zwBk2Y&_$4OAuwDNpjsk&m0O_M%wh2bst6RQp-M6ph)|{W=A!p5gbI|tTz+9e_&COw zi2g+9irIO;!3#XO8WiRaCiB7bAYhic)z$OhSM%J1cL>Y2HYqO>VB#0jU!Zg-6huI) zToxZ9yacHaRY1w1DxfMKEUJ7k<(lx2J%GSTGCg@B!Nt31^29~&DT-+*sj1oNx#|+G ziVPe`(NmOsINr5W^upwAgjrGlA~Ps4OC+*fmKDoyPArUQ(~@WOCw;_O zG$ul~5d9kQP*p*i2uvVaUl4dMH$W(X$O&0lCmW#l;p+>SDU*Qp_5tgK zwO&=N_1miYU7>{Fnmdy@#=Br~Hy{s%^DT1^0+>s_>}$&y{GB{zu$f7~%wf#QB z$2|$76Wh6jkoeR6gip-jBSH~>zn9)%*`Isqa1L1ZF60XB*=d#}qR!&j$Xo9`Xkl3- zXj$mbqvcbC6@M(igtH4BO)mPff)*2X7!e)XWyDaT%N~6~cp1y?>3)!OBYPK<`%l z0w=EX%n&h0BpUrwtb3|?BI#{KaOQO{st2NXI zr(d!J!iSuqxNDJ}4*2Mh<3t7}pyXWHvwn(F3FW+oQu(A0=^W5t!c6(logmO)5uz6* z--%4Oa%s*+Pfitd1=RHDWL=F2-D@$KPx^*+tgrW?S^YkrX0?yq$R}F6^2}gj=fF-` zg5}Vap5VY!oEHJkm0o0CSy}w~Q#nsi>x0FgbBP^p-4KDD{#x2t2tEr3^*mH8>Z- zTX>iwmS$w5TH(=8{m>p2+rLrjIS||^1+}p0G_Q1{<>ux`Oy@~{Xox3^7%tht%p&Fj z6)I0ptzzM-_b%)B9lb}ZG~0Sp08U&BP(Eq?3mqMoCOx?VbPIHJ${hqAH?Z1G zcbd8zYNzRb-PS1p>`5SNbW6%79R^*Kse97C405{W@7uMwwk;Es`a?oWd;sYJF~LJY znScsnqBsOj^>7Kr)mm;T9r|96DgDYxH_)&UXL-@EVugwoKYI&j%`+<}42S-pw=#B+d$2kHih7hQ6U zCj(~U=Ve_VVgzXm^1|x`(b*)b6=Os}Z0(&mpY$jV5SKf+u0(fhcdxA8X4_@=r>~R@ z&S4Ju)T1&qBTXStSRyg!jN+a4+ahvD~4UH0ch@4de5x?deddIK1P ztDoACCK!A2Vvx&XkUDp~dwt&*xp$fYq^mbeL&N@NkJRDQ2c6IHs6;b5tRmg>f%;(w z3XX=^cLX$ec616nJC3kuKy@7foj9?_0vx-T{#FS>y0JB7@m(@~=$z*cC&kSKkX^;tRB!9$7^)G@qypiv_ zVO7JyS7qUM3%W$t#ZU$E)uB3;^yKD3*COY3!AICe!#tx|%C5bAJhGZdAA99mG_tpU2czBx>$<)j)nj**PSf4>8s@<}no?t@+@qof>(PBQ&4XmQ zxS&tA%tA#VMsOrRWh6^)>At?GSKBtq^+~m^(k-CmZg}Bc+-t4Xcc-pz*sH2<+qSx@@mqkiMQu(^PXY0=sI2MIev2^u`!yv@sfiQsiVDjgRt@!=-H4 zn34Ms8#ah#IoQGnHL_=f3mV7RG4LY!2&jw*CA0LFt`hq{SZla_VW98DMQ)$a)y4f? z+dxk0d=o2%NUF9VYs&P;iGnJ<;!6{dW!Vs=a#lGL26pUUloe z-}XIfO<=#?Z@XbOjpAXzO}Fp0&ashvI|+uLUJAwlyBq$Wu3g<7BEygA&HZG4?~@%w z>fUwTbo)|vckMLYGTisDb##(oFqgi~PcG;4k*zT~)2SC!KKY6RUX6C$bpdcq15?-C zVYGv68uhuI?$*oQ>u!SFNg7^xMk$Y-(y3Id&-7!#MMt|Uvsp&KAcnGW7RM9g=s8r3 z_wq2D^85XK4BCc8tct6gk4=Mx_6Ok+P+d0bbAQ71vb+<-3RVyzGdSq(suCL3u&ciC z!|sM}f?oJdP_z*%`S?e+!jY6BE6I?wQMBRH8?+e6KZPPq;4DJgAO4SBYz4pdjHqQ_q*_qg}2e$(Ng33b0P7dZH| zUl=9vO)IQ_8_<$j70~iK>kGTf;9f{Ib}+s=jaV z$dcZ9x8CoI+yPO{FC`V#wL^g-XgBX_DD0Rn++_f%^=4f(_BWfd?oMjq2FD@Gx(@<` z&YRYyz*Qf;!EcvzHk5a_d%s8xvp@pXx-R=tfE+f^1-g66Bf)Ty`0fwF>U(5#rNQtA zrGUamwMRuD#5RQbW&rZPbtM+Rh;Z$UD4nit@CLc8Iq&`> z@6CexMK#wpgu273f|_t@mCx%@s9r^yVe5fIQOeekKQUaAu_Y5I8#sp~C=Ssul%o_6 z`DrUPC2XhK9Z1b%2iX+)9$eq{b!pw(Nxq#Fw37gnTw01kvExDk#SVt6%Le#6=(5LW z_i<|@y?-0Z2+V_1e^qw>`$heJY6uM@k48u&>qPse*0xB4y>{~Ym?C%d+FolUM7Ajy za@A8fyyS)rI5<)+qS$C23+T$}aWtfi6ryCk6iT?(5tFrCuw_V>?zXhzY@0~JZ zF=0zfK1M5JH()X`H%Ub&4o~LB3W|@SX+xuelN^Dgl)s&h9Ah%ZXG!XqLM#dNJfJHYEaQ+JJW6}-!Aa z#qOka^GMk)cg1#p?;W+_9Pmf)p7jN`KM&Mx3l9g-RCai)`{p+Il!s>DSXLix6GuDf zA_0TMNhv1cWN}C`0aT1MBnZ^Yy(>RIE2@ygu?J)D^rOsSN)hC=>`XAsQB_qbhdyO` z!Nini5w6L1AT&ZjL8;q(SMF%RTFKVt}-nqR_x1*Z>26fH51^aE@(v zPD`=dVDjKWh#|@DgiASj8?vy=4R#?@CGK`z8sBf1W!;_h2H79Yy0hCO?=R|mD&oL{YNp74 z2ucDvUg=t);)sDOKvA-gC}Kd|lch5GWMOIa1cdl)L?9iD=a{)tOkT#xBb>;JZ@U2j zf(Q}K{-m#{A#Z`OB0jb`I?){!mCjK|N2vElogCouYYbJ@MO$>$vDS5cJw;hItEqty zGXg7SF6tIy3Kabtg73?T8C~WHbsjH8EekN{yn&QXrNHX><-|9gd5F-cwv+7ROkCP^73y zjhdp~1H?zsGx5;B%c!BGEXn2WAqy=_%|oAZxipn2aF}3I0001psh@JtKuWpP!etJ% z(po-WaxF4nI^XyRs2E@In`!7dY8^@hu@PBcIUT7XK%p;&9s<$<1Rdl&1pxx`O=-ua z*<2A6#00b9AcE54Gys57T)@!Pv-cjPL%`x{1Ds5rP7nWvV7cU1Vb5j0bn@V3%jFd= zv$$#!LqWt&P4Z*n5fZwDo+7jX$^{GF-igryB`9$RB?kl47AUD9wF3zAi=iec5&{IB zsh@HHVM@kfl8;ou!FeGR?K3c#K1k>hVtO+KN;n=|xXjGN57#*;;ap}oU{L{cBKV+y zH8eBTEEx()QWbwyk^&E)%+9lZaC!c)AUp_@`G8quD6Rsbr>Lig!q>z_5_(Th(OL3F z9P^m-$KqV(-Pz-_UO>l-*`Ggy7wGCg;+O?Ef50@s;{z=D7Qb{J8(|hGbQ57`JYT@* z`Rr^xpnU!Et4jnF3YZxLp3435Fq3Iwtav*>M2S#i~%>#4eL=RHZ*h59? zsD`o08wp$`>9ef-=CboRR%^~YhU@I+6^q5^6?L(Rb&EQvxQjA1A4cQKl?yU*C(>)8 zyhRaUx0dJ$@!J`Ny9~yp-S3Q0SU{p9W;Qk$3l=DJ;V?e}uEW5L@PauqgT^0M(Xuec zj1tpj0jf2EN#V#18oR+o`4MJWPbffj8@R4I7x=F4x*OPRz>`WcHW)Cj_!Wn1{?%5G zZb7P}{kU-Agoz4C0lr57Dgs#OiQ-hP*+_dC8W|ieAw+P7;9QaETu>s-r7ZI-d4?=+ zeNsl3iO5lUl6JrAA<`+a*xayE&d|!?l$oJ4Vx)=XxlHrd!iY?NXbvnxhOl)rE@ReZ zW+tQLQl4dAadA=NV&<|m(1d75tr+pwtoGMj*EXl@DGRG-_5KBV_v%h4POKHnp}L@l zbIF&OhY98vO+ALQoa;e#G{R&OTO~@aP_%IJ@I*Ku_%R`NNbyVNa_(Nmx}ofRuh)z6^@zG+ji{oc*as7e9xORw!h|MPlt6lsnGGO7im>K5nfER}b1g1B zIYXgfTAa__$ed(qz5SBEuxAT-IuCMzp@xi;l!s^cNU3`a;GUiPytE3WTGRos; z#2iUicE#M;x$NXg{#g8l*B`#GUf-Xq-?CS`uhH(u1OhK#dfG9WXm~`+lnZGL(Kg8WmXEZzgkl2Mt{3%#> z2&os%rC$!ql{adqc0sx}#qP)Cipje;w)lFYV8G4*QHmBnCQuV`y1WP?bj_J}yBXjB z3F`wIUJMoGizOeWj0WDXTgR01RBVWLmttL`RtL^DMf-03^jf!NQv$trd%d)EGq`Xs zszsGk@6AQtwCs_)_3qEV#U7Fi*&^Mfo0KgUlTtScsG%T0Nc|9@c1J?|k7)=Eu?|?< ztf1U)UDsk$_w5hrx3&&8too#W!&kR&NxQsVBWNrAdy{0Ns%q-qAd&1%5t+JNyLCWL zilEn3of+W}0{Fs+!fAa#wW~1Y)^=TgK-R4G_rJAGfqb0~YF*b|E2Uf8=1=!)LuiSL zaSZtRQ9^3UCO!&6Y~m*>ZTI$iQE3X@xxFa$i)|FqZIQlJ@BZ!dZn*uv=~PODIosIu zFpWlxaEOwXj1Vz?4fjY@S=ZI>t5d9AYW1Gl>3L=e2;2d8I*7kA4R^bBz)nLSf^k(Ryr1IkO@30_r0P z(4ZfNSaG@-RSCN;64n80`!96A-@3X1A!fBXVs33|RNxfty1;h@y8Acq2QLk`2$t1b zyRQWOn-$Y~8;|Tw5akUJ(DN!#Ns1aGWhDr22y7u2V-euR3nwRMtRWt1cnv^98S7pK z<*eww{pEgZ>tH~XlpASheL#($vosTb*>pbzqdx=dLZJ%+aWZZwhW*(<$9UuI;x-sW$|0h2}8rc0gc_Cqh@EkO*7{B()EAy0vXr z-~($~VAi^}L_t`;>TXKQ$EN%Edh@oFx~HUgNg1G|q@&c<1GVvhXpoM)A?sFBqWP^q zVmmeUYm@7gRQo2Pod1IZh8_sc-aw9$LPQFLD6gy)+}Xu=%<%fSD=EUs$HqkPb?aW_ z{7}l&MlpYF)aroPdT2NCgV^r(!geV1Ym+S#Q`-QBf4V1XuQ2k+qmqQ)EIrW}t;ovZb=o%8K64UUA+l8%0z5J zDeA_4)8o<*hicrupw^YzCw?mdH8Chh#@XShIS&NB0M0P29X4EI)xU|nTiyH3A?Qsx zlXECBd=!y03v${|+F4W@P%o{Z;qt>3Ko!s3Zv~Lciq!$sa4lX7L4-4*sjW%OkOvW$ z55v#p(kdHHJAiccc6mCm|GKQ}zDdG1mkV-wwWzJ4`E?C_SRPs&BjA82G(OoAMTn*A z7-12TMIq9_$q~_^#=nXVAsk&Mg$^934yLdc_89>YO8FVJyA-OLqWxyu;We=a^;hp{ zbK}M2W4c22S_AwP*-)uuaB#ada#!)i&^jP$jqnE57O8vh`+FbBOOqTbodkqJA=1(J zD3N;P9;98|%e!Q>e{ai!NtWleuhL_FCZjLN;u$)Gdb|D%+`hW%ef5jfZ*M;g!|Z5zXs)_;Je}hr~!*$*CjY`=!>P^!(?G3`f^+wbPpfMiM4@ZcX zpOE)js{|ns3SE9Q87DjvKXE!zR;-h(%IYLox+I%SGB0AimS$1i$uNO4Hq~3BYk>} z;??ecU!n_#87*L4H*H6xuaRp`#}QTeRG&KhoSf!_em}KAk9Ev=BSBRo~M+T?Hd$bGf5b7inQHqE71@n&r^CDRM&?wJk3BCl_wcx1omHL>T9s z66V|~Vuk$_IO3EO5wLdAV4I4BB7xRVX{jfoIi#}4nMKWFTpj#)PMK3gcM#%?gg=b) zk6rr0m-X?k?who>I7=T=JQ50s44V=AVaJBmhQUFiUthSv`>MO`?NAk<5$V*Q&WBZe zpN>eUYdy3umDW5HR`;bMasmDd)W@e(hv_GsX45d0(WfNd9Ox-ws(tj4PKUk0p9>_g z*Y|A0u!f6P*zKWH*Jbs~zDRBw<38(dcHOxo94r{WDt76ADpt!D)H2{T!B*DxeVq`< zYuA{VVvdI5#Tc~=4I_uYx-3s4d#T3KK;RpUSL(M`ckrhi_13!8;40ZI+qxTSyXdfA z&9O#Xm_zuoTT*`@G@@cF!ZyB%1#qJ~#p@r$lbr(Ug2wGdDX zX;{?>t-4jHS7NKn^jp0_MmuJVc{ZRxcB&EMhfbN%cc-%V`Y@#OTUQ?gw;T=|)Xe!< zAVn;QandbL#tmTnGU-%+1?ZCK1_$+)@~~f_A^3&DMAmP72ho{jGLL>^MkZq-Gp+@k zQ~DEq*hNvcC_*s)_^^xn_5+XC9$K>7}^tj8tb9I~k20x@i*5_S~^!auZJplXm!0;qZx)_t=p1jhTy^|@UJiaUY zn2WSNb9KIex0}re8lvj{nR&LK*1X;`UG+X`_{d^y_iL8R?d>(ydK2HE_I-*%ukXs+ z+xPN-@36Q=mG2BAnj&1Af<;UoO;PIP_4U&2tk&0Sv2Aq@P;&`&mPmlY9od8UMRyXS zRtiyK+|uo5sFGqemNH2NN*{4VHw`NxLhXzXY@vistWdon)G{;-)G_^!Ri|}cYOB*Z zI-Rq$hLE6~TG|2qMvMUZNf0CZS^o`cm-p_=dtc&y+m~1cUT?HkL^Wo8buF%~&e_3U z4=U}s1?=9@)lb{(cLBe9YxSy}G|SancW-HhBK=G)@uYqOfL<&Lh8T^N!mq`G zBl@X79kHqVRtKf~wH~(n?}lgEp!&)wSOmV1ggd(_VR2ps2%Oj%Dw%*4bS&h&&<7lo zn4o!x2}`C+rU(qv>Cx-;rl#g7ETGj#9f4EyyKGPIBkl&Wde_h_yN__N#M{1UP}M+; zowykp2~B9lHBYQ*8gLS6d;kJ)rAh|a^L`^rW7Uf+05cGSxtLWKEtf%c+ja+4Q@HB4 zt*c_g`-#NfZwqVpefD)*c&Bxh9&mNvbc1JkXf|cp21rw`5C|krJ{b`wiE&E{PQ54& z2=P1d$CT)W9jFsp|D1v}B^jY-7iU~$B>|)x;n9(t;fXyvSQ&P4N~!!oetwXqB*lnv zGe&WNLpCyh)+=SSKn5e3IK}zG{2^5SPAS7g7SHjMZP?-wcOfgJHA0w@`9xE81e8c# zN#TzvySU@}Ba$3>yeTBmBq)uaDWu~%psMBh@cyecydVgem1A=}_IOp|s0ZFOk3Ap^ zIB3Fk*A(s**Pb78Pmj^>K16{kCXFync5xkG{k}XJeyK2ZH5^y2Am|Zy9b8Dqll2!? zE$l_~DN$RM7b0-4)HD~N_gz`dQ#2f1g5Az_JAW~h@XUx zC}Lq@*#{(vK;kYVh;21mA=9s``qRQ9@R!r1`)MnnCJs>;A)^Rgqq=xM3F{A7Ma12z z%P%wGGZyFSf<@NkTG3PNeu9P+E~1jul3>XlhARw7yRZ%Oj7N66@-~mR?$G$nUi#x*pend5gxT5_ZF}JhY7HdihRaJF9tel1Rs+jY& z-?BGpDty16jse!NsQsc&*J25GN|-nzD59RQ|DfC@Pkb=i$wrMeL*8ajzx^_#m9*pRvSvXwvLV*{KGV8e5!X4Nt)hNm(8ZG!@dPU-x)g zjcWyO_B0tcxSn(M6-_=iM#GKll(^?hpmOeL1X&WNj+? zj2anJ_uBE$NirIFy{u2dGDgi&J)n#bEjJ8$D0bK)W&PTs(8I9BX zp|lWB_tREJjiW27ZP&WK4oBM%fo`q&yBG;^%a`C3O~&X zz$tufb}pS(WlkRPa{11_yy7u8)p+l7^P5Z__p&a_%PuY}>6|C$B=1-~R_p&A=fk$T zTJN#Ewbi$xTVz7LKl#jq%e^ys*LrSTyL?=7%7?h!lQ`^y)$eatzi#tbf2djx$jmwt`O0IB-X)&bg1djfx~1X z`3~(|W_Bj;W4*)0@ms}R$vd-mIL>@%k7x9Eo)P!nx!jrGmC6p~eMV;vi2GLON~^a* zWHOUqG-A*|z2E> z=zlnC5Q?>_^y8q^qR<_ov8;RCy5qWabg#F62W1#{KCI5WbKU(uP)D&~@lDI7mBk0% z%3@^^wZ1%Q3QINemloAUe$k%c6@@Z>CqT;h#}ob{Lhl7vP>h-M)w z8jY5{4GkFHWfsrzlH`~2ss}Hw41PFw zcBU-o2n-<7!9hW3p!>H2d>LI^Ly|-0VSgaZ>g?^n65eJkl(?<^*#<2ZJG5T<3<0#x zbhLD>l1&1eA0;k6px5~l=#tEBf(uAb96=Buu&I!Yr96_98y9SlU=&wn7C51&e}aOW z*^%=BMRTK564x17GCVy!J=Ba8N+JgxoD4cGx)#W0%K2Ia zkmctLdB!Jia2TUFGc7DNAu>Y9=u{wlh4^O(tz8>*qky;@KK)cY5r6TH$?5kVs(uH9 z6)RS(m|-&LK?-l)5B7tnNa#6kknP+qo#m>03j-Ehm;c_W@t|V#t9l=7rKoL zU^aTpU?6BF<>y`Vn>-Vdn|RzJf;uB`n5h01;hr}$8{h)O2A@Hyqz$!!4qwPG=s*Qu(5eTf)Pas5)B&| zn69M;;`!w@VZwx$3C|~4Aj?utp3|?^;zZ#1zrN(Vi#m%tjTSC)AGs0@h+; za3&15{8-U+1SNSTNjiI>klbd`0g8nILhI(8;fJ za^cuzK(T4j0w}}s!XsTUaKQ!%MiTE#&Qal~C&AArID<-1li-37EeFzzj~61vCJbpv zT7^yn0Sp^QUrvr;Gr|zqT)5C8j0g@S7*FH^Pp@)6ENDWsBE~Qb!G)-bfZ>rJ`bgqk zcrqjo;sXGvA^&3(CF$%%Xk%wbNQc&&`0()qEDR_SWs5lxV+RlmJZ4Ee07S9zpn&*+ z@TJC2(vzh3Av{as0r|5aLu!Tyqny-B`-N@kdQ7Q{e=O-?o;A|5Y4M+ zabf)q4M-wviIqh_^IAjFpC_W4*eLq}D{Sy&F|%OPPqf+SiCTzqYV|5rV!}!|VLVTC z2@DuKPoiJ^;{y>C6cmeJ0b{u0!wRTj+l~I><1g5lfN23E$^|4FH=@8Ck+pUCnQ8W; z?ME2LlDjaYSZzzVkD0D3CVv@E0!!HklPf3l)eN(OQQ6m12n>f20R|>SL{^y@PGOnC5lJMU!hP8yj)y!3NgA?oqdgWY0!r!zsuc8O8ek)97Sz#y3ZLL5F2_=wp zA6Pp*kQ9)x^D{603aiN$zJ0DcTyXxJ!wBqzy@&n5jNG~+L zf{o1?(vTq)ka~+KLyokrto+s1+8Pohy>Bh8fbkh`2bLm+khpo7$q(FCxP48S(5CZU zLtSw2;jQ5p(V4;@G>2w}%;*et!SMU)+8>$(000}__->f!hBmrC z;3EL&N+s1agL*B1H%@lD2ihb7qY#*RKCS-kzzhObTUjq@PS)NyQ|On#$`J$KH#|`> zBIIP{35b=d9vEsFIFemX2!TXKA&o?&+;||SilTTwRC9JTPrP{Xxdm2&hCPN@tmJywNJXRl$d%`j zpu>PMBANgO44>$bE(4ag*N}vTNkS2vp*~~<3lrH;ZPNS~FG?)P^;jSxhh?}x12XM} zU)8+bgZ17URwuB4JJb5U0c^sUQ;GmHNI1b*Xqh->o%yh_vH1;IPdX3~Eig`yV8HOV z>6^ApM$DWU!_5usWhm5wmYbHDg?YbhngX9qU!ZQk={ro{6o&Py34DnE=>v!lAVcD; z{^5!CwKmhgK!BV8G504hl*a z-8TVI_VxT8%;%>o6Y!61svi6+{CImTj>+v0gg zd;*BaSjWJC0UXc_I4385100X!b7%3fF6OHp$Rc2MMS}nj+v=KO_875z#X)3b-sr|x z3<Q2Zb9(L3)W2 zHh$jB=<7mFRBP;;cxZB=tLzNJ4gr8P>Cg>8KLf#r6e&_{St95_GH@6+07d_WVf_Te zf3d~Iy}ZagMiRtY8jvR-o=w9MOqN1BJ1|2Sn4P9?f_X89Z2*e)Xch)n;7)AKfCGwd z$dTm4!?4~ZjKIF_gG^1AQ0^Jt~Z#V*G&cDiXIFZauEhIA>(~0Cy0Ob(t zNwPc~P}&%S5?d$6#!ft=9nLK5!#qSUbU}&q8DUVf8Z9LWxO2Hf30ION$t5SyqH)HC z<>BDl>b>797|NO)#*Q72;i{?QiZhF|X+Wvpl*ikB%G>#p* z0mhaQ15E`^j^UwF0zyMVLr_ppKnO!RG&BU2YJjrCWO8U(854%t%U~YJ^ErU48;Sh+ z`W})+3R77jd$nQzh>d8*YjIzGycNrE@^6Ly4VAY$Dk00Etw~c+$5p~it}QwDq)hxm z?PuhU6U$7p3Z|{Kp8>&O212q&4<`8~$xI9W28ZbP{h+E=BJRA$Xy2pvXwpjNoB|gD z1VD{3piDb9gU@Q7%=*O!;3o42y-`z>Ig2s-_~3N{FxzAKYv;THlM z#4SHaPaF|L%tClX*gMfJg=5y0OaInb_TIgr;e;?4J&Y(qRm|^rHJh+vhCgCN`Zv@FxK8lGwwu> zBgSx0eO32`-&chqf*GtZk{uTy8moj|64LrKEyg-mEN@Zk()$b!V^Dq!MTdhxrx7gE z)=70O*jIozW#QiiUl!=vz|A-lJ0?}|@)5MNF}?_3+WW!`UZ@s+8DLz}t-l6mQ8JcM zy7esMh_O$<5FMXS?NOy1D%C2ZDkF7)aAQeqePqM|7c^DQiEO9Tj(`)>4dAg8r3wS zQA4S3rJeZti~fXzZs}au`X8M_j7&v>Q;f3-zm0@?*WE&4>T>U2g^c;^(HCUNF*RrK zg(rnHoxQeo*nccsMV3uY=Da-Fs^{ zg+ANu(cb&Nb~9!uzt)JH!~)L@P3BG1Yxf!!KHM)y<+Vznidv}V5C%B}5P(Hi5fj|X zYRHfwMRL51IEHIYMiD8!sCP_HB>sX{;u6qC(=Uih+kDYi%u7#C&kl>P5y~A2@rWJy zObZErZn3#3=@EY!$5O`Em=G|-fB^M+Lf;2eo|!PZmk?%$@Z?#Yg@MSaF(iv*kxfA0 zvsArJ6toTrNf38Cq#X^?(@yz9q3%Vukp^fX35CByA~DoS*_m*#d*M2k(dOFQ8y1nI zSd+~8O+KZ+h%Rfvz`(>ofC_m^(CR{*fapbHnp)5?vLSDAw_-}sBI8^!-%}L(fKb!Z zvm?_be367HJv~KVCZfX1J|^a+2G3cIDm>gojUPPy_u?ob`hLEj@ON9j9n|mlOJRV- z)^{CPhO9A}U^6IeVF_EKu;l8(XDl};KYoDmG2W%?$9`;?xp3%@)uagz9$S=VoI{rl z8gL5u8BdIjFdtJ$bRICYDjyc3f$`MnENIkFTBxZxP~=r*$$=VQrOMD5`%2)kt=qck zvo|&iWL@`(W=h5co1ma37b^PAV+A$1X__KZAQ#WfcmVN20?yB9DQ8j)0KvreMo=CL zfdb`A1Qg6n0<;I%7YOLHtbbF7OpF42Lm}_cL zU|^g?nSnrG$T$=y4zM_n<7el|k9+TB{e3A`GN4?&NE#8P71t0j-V`g)L?eJdmpyv) z=+R>?lL=sc*;?mBJoGdmFCbn(MENO65HIn|ivz9TQ%F}b!*uA-;pAalNY;miarxIG zp%D<$&S%GiWVAxM0W#9@$Pg0GJsRa#{F_|J>}*~M;T=Mcu?B?#g#tKuk0q5J52XMI zXiw1<0T616dUj-b%@_4d>FFtIQ}vGNfxzFA-cbS9rAkGm8^~r{W@@E{fi3y-YdrZg ztVZn9R;GMunx<7s-={t&pD(rPF~%s*-bxB4MOfmRA_GNdF-2cTR{4Wu+!=XMFZJXd zm5xWA6&EHJ-AGxs=MH(STji>$`xK14yt3AwFQL%Y1w}#@g)TA?u?!(I|En#|Z6s%> z5Lt#&X+!QQ0!j853n_{e{iq?KNUbLekfLYdCS(vW?K!bSWRHE;iHwagvpA#i5+p#7 zP;|y5X2jPJCR>KOR*m>OQ&uX&;$$+6Yw_5OvoKZA8?x6MvQtbwR)^go*?;mSBcyn$ zdaBC#A!XH2&d^hp4=5q9oG{~%>4@2htj7t@v5KI^$DW%BX1PvmO4-!&w%8VYR z>z_o4iAO}uo{Z?4VHOSH1%;^!4FL!qUO;*A1g<1!W}tvz>cK_D+H?68wu38Rb1L!S zRPbDZ^Xc~NPtLn|QwNVZ7uQ&m`OBqPU&4hynQk>|M13ryN-mG~`dOXxDo_0*VF4uNsJ^)td8Z+_*21B?uxT`|Mia)rZYE0=rFw%K$TM-(Vfpn=Jeyv>!aK*5EpAl|=-2x@9- z3L)q8CY%B}E}Gbab|5H3X>J8An_`whK_BE)ay_XU0mE?;uPZ2@ckm;7Cqx3$1$bmN z8d=?-qW-y!_;gLs__DB55dy!Ts>?kMRk%xvfvNC1f) zsi`$r4>DX8*x^hfZ3KpqJqMl4Zwc*fn6d1Qs2-7q@xJ2K#Xpjlw__(09*(m@~W z3kIv;C0KQxsDi#tXwc)m5I% zGoVM$8v;WXn`)9-{8sn2>3nM2vc#tPt!hARTcaI3@_q-~y8AeV-B3k6x5yb2{SP+$ z>UM*y`}=-vc)xT5o%i1K;>oU}4K8NPd_cp<#&0p@vbYzMZ_ua2?#D+#i5I8?zUup7 zaBsj0eY2cA;{iis_H!Rt#6~4XA8c zNTFro`H&Cb`eQeI20#bsmS;yKgh`1FaDyT+F~{FwF!=$wGYRMf@Sc=onx46pLqwEA z*2#L4()l3T^n{+^GG}r#{zLgo4t481Lf66IR$ncoHEto2Ra_sm#WLU*P3>T5Zv=z; z=pq=r>3QKtwoxV@RI94WPN1lU@;_Cp2nKyZbnu7o`?jq+Lo44m!+v4*=Y0iEdvCpK z+BT}TORHZh0xs9k#g+zbW*?cEE@@$36EQ5?i)Sc3IbK@bp;%QP|SPo%$ z*m$zyEibILwx}qc6x+YZGQLPIE%9W4(jQ4E6&c9ffQoG4*);Kd=xL>l!Bl=%9D9&? z7YNyk8AB)z^5V(l1`Rj>tT>PUf-8Qz689G@6s{R+BH7DeEI%ZHG$nsIGmAZbCbK84 zdNSrAvyrqRjF&;rY(k-(OBe6zG-Sy&ElF9&i!;u-4lZ+^qQ{*G(&Sb*cpT?3SE^~f zU}-?V1~h4DB+Jr6mev&6(zuMHW~iGo$&G7E+-#23yagR)kzf#hp@Z8)5Bg(DR7pTk znFLoaOVWjl-m)!vqXH-8_bV1*6}@O*S0viNN-BMF$cYMBb}YFOKEXmSVBvYj3tD~1 zsR?a%J@Q3XccpS=rn&*gAcj zh;Bs`qicqWp%WwqQU4%KF?KS;BXx9?fP0XO4?gFJXq6;M=SA8yzmGNyBZU;V*py4= zhi+ktRhgR)ApSvIRBRUc5kSMBy(>@;G-9aCmOU;Q_j5ni4Z^yBs?8R@3To?S2nMxL z2*baF5FP}FI;d_O7=~I!z`6*|um*c?ai&C*&+GnvcZlf=H?hI4H_H1*c)#y^-|f1^ zFgNenp49mcH^Nd&YQ8wGU*JucG__l3-O}>9E&5R4msM>cPkuw3kK8Ly#qM>74O!u&tVT-}eXp(Jov7)=ls`&^r(a zet>IKEzHEMACYLM5x~K3eOBHvI8|EH=C^^Qg}hEs9?`ONMCRS;hqPkp`hyD21JPleGG*ZgrJ}cjs+4iI8N8l1l&!Gb8Awio|%ZUBK>EvYTMN zP`w+*lF-q#8i_iTeo~oNCMdOGB@sa%!C(-b^o7!}Ho|WO7=Ac~f@yNCR}y6g8y~c) zBwjdl%Oz!&m-nm05?_iXwst!xy+{KlwoGhsD<;&czljcGC++RNJ1+|oEC?Mmev2Rs zHwE{brcQ6qh2ejBFzgc9APi&O1;59lP=oKfoxG~I);0Q(=V)X}#~h;)9JhUKelhdoTRDu2$Po7&dxeSWCgKdpv>D>fCnd-CM(t zy-&cP4Knz_AX4`}%!UdZ)Cw$t4Mm=u(hWYh2pojBvFL*zexKkpjzUwEF}KdlkzG7L zrukgKCO5eyi*YiUd@XThakq-ghH$1-AEBvuJ7Eh}p38$R_q-HtN+0QhT~lVk4+bAh z=^XdPRqE-24EXoKAW+}n`k`?*{2j?*TXBqCA>=Np;Je}UgGSiZz$KXH zl9sc#GPpuMb+cF)7~S4zfKD16CZnHVqo9e?u+N4!VV((&I4RyRyf< z2@WDefCZ#g!eZxzGDejP0zGsDOxlpU)dMS^kCRezjSZ_}obw4_)GBFsdwAA|%*(cK zQaU%qJYJkzIUjl6#X|Fz7NdrSJO!c~l-|hb-x9h;`MDu8v7AQ>&-7G=@CsAiSh&~QDS*{NVE5461g|x4*hq4keujz= zA)*H{qsT$BhyhW8VqNfN#nDU4U5{;$4KM(7@OO@SyGJ?&dlpn=b=!T$ZmXYpPgyy- zWri(vMK9-tZ%Kq-IQK3K^bp*2{kGEV3c9@wlXGNS0JsH%@9udiYm(Fh890)jE08fN zEl&kusd9OP?q=QnZrD{KLb>oi3FZj*-s`G9*LlCJ-mp(gP{%7DxtrUHp<>*NJmEte zQ6X^zC4>!%{aX{%I;iIO{=vIlDz^rDv%dZEigaa6%$~Uf!RbgZbE4n`g)mkIBky3& z$vMfEf=3L&!$aV*3t7fe+&k}Q9Z7Fq^}oS62hN{d5}d?c{FiUtYX)l|D>;@>cXDTk z(YrXglYb7rq~S^w@ZP^-bS@gacLkh=lVf?T4P8lc*_C|fybI6j$@9$PEpmkKaU4UD z&7iPm&?KSF?|iFs?o`6D`j!KES>@v>LMI2H{H@@pmZaJuBv%$B4#wj->P1$=q_n%`>xY z>Eus9=bz+!$=sTm$eh_Fo>2v>&mBt!uVS@a>l_LHDxJreIgev}vaCOO7GIWS@#OO? zenrDdHj`u7jK^I{G0wZW(oGmFdd8k3n^@*ZX*j$^3RTRFKL*NC0TK5Owf=1HwM`aKz=hgr#I zGGB)Mva2)sWAQMvWPctjd06L7D<0N+j6Q?N%=y*UKX1(Q(}p}_Uf$qUDD@Pry-=JLC>75Bxr%wP(f&VId{h3LIig>MDH~F z`jxTSK@9~Uaox73kGlkfwHgFv#<{aM!=6 ztmCV%v3Pa+26qdGNWn&w@@YsVQAA#h-;Bd+zWY;uK7N}`B{ZnPplv55Min|Nw`~D&XgD~5}kS4Jz5D_`)z(|_-p^2smqAe#~)FG=D3V}L?1_Fn8 zVbH(+l&}24I$n4Wy93TUlZn+o=Do&z!3pXH?NAU&3CZ7DCJprNG_U&yN0KQdz$8Yo zpQRPa`0^S;>BC>^K!O8TwB)ybp)WylCE5S#M9xVnWqU^_9>S?(>#}N1v0+2QmL8Uy|{j1qF<>^~%%!ZrANb!H>jlA$doY^~rNm6HD)R1mRcs zD*Q-#Yanp70w(n)(7G=%g`a&&!P?lpBkkTu+}0(K@wcw45z1Tcc+Uf_Bh>wAMLzYX zvtU-dX^Dn99ab|Iawr@D2Rb6=-sG87Xh6{slHK>I2%7KrM}BU@BK!1%d4f_ScHjks zhw*pNX9vCi6Uupz-j^~DbHHRWIaUxJ#`zY`#Dk*se2KmiM;V3DB8ii9x`>g~JabMW ziIdMh_ZqNdQZ3nKWfDr%gNGL&w4Q*BML-4tf~ie`4_KFzJ=)v8`djjA$MXR_Sz?yu zXUbh>?z0^Ch`WjYL-|bR78(wP@;BaMY&oP!i_QbwF(xOOz1IXu);MKh|1KLS(Yul! z_XB)~9M^O$pMO4=Xw6&F`mZ1AyLS_K1>s=^usRe_ui{cw> zmsSdogvZ+%(Uws^D^T=!T*ub!hU12kkvC-&3&OL$k&6_JW!L#04+pne1lAhbM&ir&E9opeW_j_ zQbTRE1?VX3c&?EuPjz#q>#8EUPDN}GW}SlY5tiC;6duqSa*U3o9#{a5$mY53&fZJE zdGHHWWpQI9m?Ibr0uy;TCr?jkK2_bZM|MTG1+jSKyN)J&(BwIdNAEsqeM^NfoBlpA z5BODct{V&zNwfS4zwpbXgIdEye#&*%m4%VE>0Fw!TAn=VAr5Ua9yvo5iVl@nJvI*N z(-`4};hi`5?jX;7aj)PO2uFcoy@M}<<3edxr$}||UfWA|0dEl2Qk%*I{Jz2cqhpe# z6=&Tjdzue`pV=m>nWUC15yx?Vvu*|LTit4n$0hjBh1pyKtOYAir*QgvI?9wjI(p-n zrhXV!dSP~br!*3V(|Uq&!61|x+%>&HSO=wu|0WGx5ZWk3MoF~*RsKo?0empNobKr$ z7fQo#a0mA`{BO`@eXnlzZKL{a-zav4Gwgmi_;4X=MEXwP2hk{*p=a0Sg^_+FVBH<_ zMQN%B*y#W-92``UF9a@^pHoznhfOK$qW6riG?Q&C%lYSPYhiZ zNZAy~ZZEyT-R9OPmB*`l{NO3M3)e5X%yaNH$kXoD6f7Jjq0polixDtBP7>on#El^K zBX~&<#oBJy!1KOHVCj;&FN8VSmtmbLbEWg;OCZvjZxWzJYQp-5F@Z7#x%Vlth_Jts43p`+1{w^)zhBJ<-wpS80>-nz zHGqCH8d){{MDAVP^}T0&__gkvli^rC%IlR zWqSvdDYT08l06_R^u&SuT#mY`HveudJLDvQi#wy87{ z-Zn7&Tff1equ+ZuZ+>o=M3N2Wqvg$p21oK51ZvkU$*cE1ZkUaM|AS9!t=%Q;5;uvD z)gzXyaMs7hA*~U^=mM`F4{wAHa_;?V2H?Ry`Hy6pGYn*SBlm;kUyZr9wJx zSX((tRp&WGdmb{JysHba&-cOC!ByP?kiHT~_0dKYc!MGy?$N#P%d)AJ%Y@(eNwCNB z?N_97zg8t;Lj4fPQ(RY_tPDIW3~UM!B1sPAG-rQCgeG|mQqi=SA@^9`qDDs6e{gu3 z-cNKWOi4LbI%#CGcvgaV=a`ZyPVyHs^NC|Pd!G<5=`VBXaZn`-W$)6NoZ0z5Vb0Ia zvpDb4dE}DwoSd(85WUa+XOf+%hn48EEZ0c(4($}4N=@f+-%+Du>0LhXJD^)~k1^y- zvJ#+RnmfFa7>G$v5@7%yH&;`_qQ6G<;Rj33Kx}X`<-<-nK&hj}^Ye9N$;~74%)FBE zp!W(L#tzvM)FAnv5Z!sCt;+K;Aqd= z=C14<2bXhjH3v+$=W-41#hFid7Vk1TGue3%9glQxlElo+J{r()%od^Y|{I^Qh@j z<%jVok{F5=cOE@Dp=0prT|RQ<`;rbrYeaO33>YLj)WMSp$9ylotihQMdRzmA_8vwg z%A@yZ41H)60#l}jMpP!sRJ-yr94evDaKLAoGqIjQWO;_;Ui_c%DK))+kJ9Ke%>KN> zi9n$c`cV`SIo@#dT8@MeX<($|hzOCR*vMOA*;-oK;Yb0Y5rDT`>Pf+S6>f0@R@CW) zZBUU;rBREZ)Gq?I!MVi)I6(PiTU(La2)86Lnac$SAl2?5@9B(X+urUS*F>1O%=%@)flz z@`f#Za1X#5P^vJ13lESBdRn4Bxw#Z7o8-RI<$+W*T3ZWK?r@YRj0Rw_bQwVarCLrc ziF}|+4t=r*^=Tv&7k+DqLM@u9ayh=OrJY>|E8;NXVg_l0{rR0U{6KUF2B3X1JB)lZU=7*U%6S|H^`>Ov?@E@>2J-xNa-u}?qHwx{UdM%x-9}GJsu70 zAwX)KH|X2FyZ7q#vh0rAF#PY8tZb>AgWWHNJ1BZ!5!n+cznFrSz(KuYNIj6#|!T%LW)4B!zB2;)>vko)g>N$>1l5 z&*wvC$wI}#$3aOom6EXt28)RC(#;8tN5J^y(y0IoaSh&gi`eiReA_pulQ5CRF+PqU z&t-DkmQOQyW|4mih3`sq$cMsb;79%aTAi9xT^B5fagqi(MMXv57sSK{aU6<>V_co9 zw}|1OlhzC1AQTB!W{S?S91}@K+rB5e3>!WjWs3&MT@CfJaA;GVxgRF=H!$@XGv*n= z&wVU@p@T>7Ok1xe!TyFdyl?jJYX^h%K_&!)52WxCd2WT~Cmn@ezzQ`%yVf;;yRiD^KavSEU66Nmg{)e^7D@DZxSM;s8WQ@r& z=jssM(HwKpjKA4r0PG3U*1 zavfs)gG)zfWK{Dsoj3ypM~6s5*`)d1ipiNxx>e_Kvoks8;s#owF4nC$|K8*~=mL+= zy2rf=_3mvb8(a5QocCDnvK4{O)p@eXtvF|UZloy#>~CDTK{JCzxngh`H&#Ho+&KuR zKF0vTf`tSL&xAWMsi8%I5!V}Z*P%3h_jP%DuWuFJOYCuzTUqtz$|m2fn7vzZDo)?L z0s(>r2xi~XowUdbKsd!{;UW>*4|hf^6xL29QgMvh4=!9Kb-gyyEA<9jn1!95g-7V$ z2;kSl3&ZLg1n%JuzQ#3*tSrzYJ9kr_Cy26eN|fC{dsmQhHNK6(094AMoO+?>CbK3S zk4Gt~X3_(bj*^bRDMwfN5TtrWZ=I7uEI9X&9_i|_}f2=fAj@&jupc6~RzsW5dlAy;HDV2C>rE=1(7Ab4T* z26rg!_hw7I8|K04)>4~@baJ!?5c1{d_L)^qF~{?k&zBUhTSWt^azc5 zp1nu8>yX^(+5PzP>1=qT+yGT7jT_Xk!3|JV>RdN+=>tv;udsF)_ES}~ub@>Scd|J7 za{(PEMptv-)n-LD6cu$mswIz-I}=w$$6fRd0@Dib-t3#Q?)#fv-?k^vzR0(IUnKAQ zxXm62Oqjqh(}|=-#aafKIPw8<rChxc z?v}5Yx(`$#oJrX2cESNmVTkpmpolAU$~arW(bLoOv-J~Zqii$b#~4qwVntxvwk`01 zLt3DWwQXyDR;bYKcV{bc2?W9zuxkfgT`|3!IQSksQEuKT>F{Inp_jyfU}(*MK^!u%=1E=mvYuvGGi?m_Z7qpwu0@>

q_mh-%?3D z9B0Zn2#Mb1^;v#mn*r$Dmm~fzq!1z!iA1Isy6|HXA`*Woco(9=&26jfg}4b&wq?ai znE+d5Dz>10ToxqBfr_DqNfzV`lRz!VNq*;01YGWN%#_7?@1KP7zN16^U@VvrqeZl{ z^D)b&5FuiAhE06TFuS&VG-=W#jE~JTpEaY&%&g^rv;W&0zdCSrwRV7O+1fggO!{@< zfWigSGj)(T7@0!R0R_|44!9MZc?HLW37p%D@dkWMOoo=3#}in9!I^dC6N%|qIY7V} z0h7W)3+xJCHo@M)!n!z=C;6pTGzmw$!sTyv4xJ&(bCMXWH+S%`4b9~k=kko$>X)2H zugJ-m^PhX?^H?(4d6M4b0~gjai}x%7&u5b;SP_=}!@q54F8Tb>Ws;=~Hlz8*`4zds zjvV8YHTsV%T}#V^)?3$#JG6nahjlK4acGa9N3U?@L!)85b7)V(XJ|7?my;-CE6(K^ zU8_N|^H^r`FevYDysURIto~fAM(Yr|3{qK?$MoPh~Ff71p`0b@y z-SGZ~kuJ>gU?3bseo^n0f{{TDsgd|bjFk{?G!jI?a)?q>HsT1XQoy4N!xIK&L0!r` z+^q*^IR9!`ltT8zWVnH z(7w4A{$-_26-mcluh%pmo$#do*aIR60wv35H9kHhY-9pTp%0nBgIA`xRBGzF+D+3` zs+4_J>)wWSKeoE7ZWDFu7HXYt>$I0AvAjAcfykd$T@F5YG~ zT9IvRj7=T<)b(-Ewou(RYHCPduPP0zLcub9Ra956;hmS_x*Di`qyp@oo2FZb1g|%n zN)kQ|u$pKuY*K*$2%#4o)V~3SrCylz!0vUwP2N_&@KNX5@Oo<7wy&jXa8)?9b&=fH z=;+@I?~C*L3QWEFrdw6g?%keBw~{v)<+A#H4ZOcEysqBvyQ&+?5tL6=RUk{`vpy=H zdR7AFy{^_TJEg?mB(((cke3fd^zS8GYvwr#7vn&#?qYU@r>GJebi7fMq~W!+{r zM&QshWn?O)ok#(tsU#$zt)Dl}Cw`KQINVnWFA7Dvw2iLTkE7>isi`;udsI_Toi$)VlQX(WyUR@poEQpCdcq+64!n^tW#t{Jn6|3#)JYzmHs`wASnnN_B1xG1m35 z#K6eB5&%g_N-4(wFpWRSq$%SN2B{GVQK&qU5wo%<43aS}g3QJ!ygPgUD(k;YdS&%1 z`KtG&5B~A4?poZghWou?BOH8Pq83(RuvBS}WnG_p!bj!ny>5Vu{GOE+79}dGBq}SO zE+hd^o+O?X6S65Iqe4i@#o5+{B*>Ws)(!GE{DV!~|L%0}S4zLgr)OOksE0sSXSq6{ zZi9nS@4p$?RabbZ@wfvj(BGWMevGp9tP$*C@$WR4b06T&z9_gMO)%2A3og-b8 z%^KIef50xgf46r#c=hQkJw0SL2AHav%U+FQVnC&m7}87>KTJcAO}YQMa)mU-fl5vA zI!iN3MMZVOmj%U^FF_BA6KNJSDf08C7FW*T$&N$TN+vl6I^Iae1LArZQjW6&KrzsF zq^xyI2|%*uq00%%t9^s*)i zFHL#IFLqg@j6B}T0}k#D+~EXCSz~G8#EBD0I+TtCqzzle7ckN@JPQr zye+PFS@&5hn#Z!N_UQ-u^4Hd8tRd-6=aFUCRTvd+B1-F%NtR0F6Ix_7OlVo?S|d0d z-0RT`##`OAPlc!&T5^|rClTW-SkN-jwRSp>ljR#S4eq{eK;Y`~s5`9ZQ?=Ji4;W{# zLZ7k69-x^A9BFR2^0}G%BNUD^%@GR6956>vFK8PZTwJARMJ@0l&qqL;)11;)EL0sdoS1;{_Vc*u>OpArQ4#JJEVe48OMl-$vnT)v8V zpL@tmJ`>z7iPZ4ND@>~;Eq43AJjsWZL1qB0xGqV7Nj>Yh=4jr*oH5`kI%x5mFG3T1RrUT>1RT-=Kk`_g`M?>`(AH#=UAAxjZ!@%zEUDK z3PbOUQUQoq6L1g;*6OyPhN9Q&As-A%h7}@vJ+{Ofocg1S^Rn;WuM`XK7e-_j(INdtJ_5B<0RPE5G8>= zK?VaW0M@}`#90r!8@9S?4X(Pbu=?t*RpP~kxYI!qnM5*t8=#zq(cinkuWjAECwwck z+qK=h>4SEW7|y#495$kF+^Wa$F|98tJTBDE1f4f(r8OUpSQFenkZ?mVn_?+5IZZ@F zF+B%HkcZD9a^Z%xfp_N(E=Fef;A7pv7N0TWlh2`uC#PY~mn(Wci6|QWQ_iQeYZbhb z$cd?ePk{p_-;$jJ8lO_#2p$aSJ5KqQNIzHUH}B&|9~G>&{Vk&p7-Zm~i`yRCcLG z88-$vxZNNPzZ-sqaIwhsQDkI19*>`V%5p>K>?m6U_1-!c)~oN=cirw3&B5Ko6(;Ju zhT))7Hxwf%pHEGHB$3Y|r+O+|Lc=*LP;b3;-rca-R_i?_%WastIxH_uKV@CvP&JW& z$AM?+16VN9#KdD*g~tF{Lp2UtP&KS-YWkoV*`%UrH~^10oG*JLKmddTC3n(sbi_=! z;9wCT;#p(_A`IyAB-!LYbRSKc%skV*_A?_@RUf+niGnpsk;p$_b4)ICm@omfA;Tl+ zNU;_KHb^jXu;^fB5k^EH0rOIvEX?GW2_U)~6v4haRwrV8S@(r(q4L)@E6+u(aXV=zF_*5S< zJwSybMd5|)v0oyFV)WoKVFC*g%J{`?s8@LbG4$%bnmV$q$|9M*XVocHWjE;B4jbM{ z$8_W4a0BIwbwEHq=7uHsG>xs*z4viA{P9T^J1k;gs1+zM{IL@gGkp5s={9^=IC0`c z72bsNG>e8Mw6{%nezxlS-r23 z^{fwHPaqMV5x#$TYM2LvDiY%IH1FA#sCjsr$TxU)IiE4yv&qclTHsvfY@C?PhPt7T zi4|hcnhhI$KplnM>L>DCRqxbI^++==RA|wn1&|Mn3ps!;bQ_?fA)F_hGUr;# z89bg}$>nB}Si>n0`0QqNZ^!O{`VU!USYCZVC$M~aE#c}g5ykZs$RH?!Ze zMV4)eHj{U6nD}hKucB~Fm?-(;V&6cFi!m7xyRQgfx^!Q=JlEAwL0wBE!@=|PhtBP zy}5$Tt;&$w3LidJbm`g-UtmPT3llf@P|yF6Aqou2Y@CyoEH%PGmhD$GiPQ_V}7)_qjC2Y0_3f%4qfvs6%ExE1we=s z7A!)rV8LR82n&`4F9eGXEKcz<2$!9`_p4v_*vaSKdn_mACGYYTTwzDch!FtqHW<{c3cn6+7!hFoa|nskf}o`(Lj)8C3=G1vg_l6o z(EV7bRtYNCmRONcEevb8g%JV$=N2k)LLz9b$q=ChVFWB5s8Eqn0856I zh&KY(9dzcCC!;;?Xgnz=WAIO+ai2@&M26$>i0u86i4@1TgiM`J{>%CKc`iet&}Et|%S2=tJb8o1vXaAyIp2yQsF>Nl zYTSSj#KBcr@9;$W*9t5q4c>clIWswzIa-&02IDGLkdFjh zu#}+`L1x5B4hRoph|GW4Y|1iZd47vwB$F~Y38S?BGBOf^?4+d~F#;oT;|7b^mtNV~ zK_YuTAV8LI(x$4NFMS-{kQ{4ca0amampp_iNd_`3Ol!H^Z-cFJr##k3yAmhZ?FUXJs1(y;OV-t$v$iBK4f$rh!UmSdacz%I+hS`WY zPg+?eWjt4&$y##qj8CN^B19=-B}Xs{@KaF}rkF;KQHqKXfGFi9BWe`Ic=Z&)6@4%A zT3@JP2qqm+x4wAYnzOpttzA+;9tQ+I1Cj|Mgd|eYL2-zQ;uPTfOT?rdaHrawxmbt5 zr=8v)_uz9Ay0x|Syvchsxz$#WzD59_XDuzVub6X=#HqFXLh=+yto5y~qsR;jVZVby z7=^GM6s&kw|I#4?hm&u;kS%AJ3>)&VOF$hExc=4%K&am?3jC>5N$!U&RyQgh!qiNc z7D!!^x^zi0>lu=nOsT2q=}~^nRZ$fRH9bYjrWG>@*5$7eTP2fb{3(RX$}k;aX;&QD zg(yEOtOj2~Md9HwA`+#f9x2EXkBlT$d~q-(!vGH#E?fjPJx!G<9!i>^spOLfoPU=Z zSr;0A&IOoWSY8D96DF-0nion+OetPSX2R)0LQF`&Vz3`C7EE*#S$Yf@50kH8LZ^n3 zIVU-fzbuyJWLED%0Pjp5OqBF6`!$YbEo*I!&rNdpv#rU10bPD{w{HN~2G?W@Q$LT$ zGUs!2$@$M~GMAS}_(z4=0DD_nAi@)dR;7n237ewRg>o|tdi1Ce1EnO7@uWLfq|*LG+zqucFvzYb1LKN=lz_g6(lC2(*xJ-w#r2NMZJYeh^T6n$}(Uqi9z zLm&{yJ~Q`V1D$|u`{D?@n$`N%FHgG;^tT&MdwfZTXsU)>d^*pe&gZ3nr}x_4 z!LGXBHwC^+ERbbgpgYD6ALs+J+gQ}iUAPz-6=x#3!Ve1=QBy(Zqd*ngFF)X<$TE~< z(xZwkCLqC_)Gy03ws@AecQ^Fr?IYuhnL*5;-&?M zcuShpkeLKGNb$|Ft4X;D3JT)z3!X|E5@@*j5qr2vZ?;VZ>*L;U`aF0OsS;@?=#^$9wW09MD%sjKxpux}5 zIJD0!!Lr1-@Z#FwojOA@16_jwLOLJC1`TvQe9UmZ^onV8ATsoyFx!|=E=;JBC5O?w zQtfrE7u4!$$fUzXr^6!p>IS1arfkY0U%g`0oT{=vPApeE$^1?(lgYCYu4@~}&tj-) z=_{ZB7Q-aE0t)agd0`0p=|_bGe`t{+Lx#NZtxjS~EsVwlzTsBQ>3*+nRfxNNo2bG{ z6N-A)GmdPB76`4U*2^;tOO%tvoX=P~LUm zH~HHf&ph7ZoChyLqW8WPyKTuG+Gprnk52d*sd$K#7A=yNzP>b=NfDS&{2;yi{(eS>sgk*d;BAPteyu|O)#eZPokUDs86*uBt0?rzmzQoJ@f zhzKMkWfX?YOAYavV1+kC$bN((;?B4Onjw^TP$z^?J19!_3pYo<%PMKTjYkdS=f3Mf zyD?l>JL#kbl%kMn+H0DT2GZmAdcYDwRbd7aq!1Dk`ffq3%0`ROQ`HeIRRplfSl$AY zC$+Z(O>D_G9#t?X?EF~`3MEREz|srw9k(0>B<#p$KITx>53O=dPo2(Jz-ySO^ljU! z`bquiDXpqDHn_0*gWm)dbTiEmu}CZ${m`6ynu|r9j~x{HFu=O5x}$pPKH4d}Ux=M? zJfiBIFwu2=Sc<_;)MP}6o+4tAQ(I3A zh7sRWG#v2dfM>0}OqdWMiu~jqu=OV|4xYb_QZ-UiX%}0x#AEh2^|V-dc5kZ>_`m(r zz$JG|3anb+D&E`T#;b{HN+?yKf{NGVsR-izy8lIp-|zQpX^1J>59{#PE_JOBjgJu{ zdLw*5UOJxvoz>F!FXI)IOMOUICiiSE&E;~4;LJLe0t93t8Z-*zc#SygIWGYwZXJwt zRRlMRDH-2o$X2?K3^6MhdIm8upJ$%{)snx@Y$f4PXcn?C=vE0P98C3-ZnJ8Sna<~| zv9W)`TrL5?^zn3ZnPHgtPfu`XwaL-_?D6`u$0<$_OHaQc_Y+w&KYn z%8`=d4bf`4-wm@8URZ^LtWWz~ICW}4^r>5}&xuWeTomtCEoaW7%g$eJTRP_@7WoZ{ zL$zoW;b>TxVa)DTMC)wA_wW(x(GDYsTqE{9;xGk0S|SSEJYP&&yhphge;Ere>Ne2$DBx?T0NqUUX}Cc5vd)=?g(u@?6UuqWv#iF$o-3I-lk3O) zGm`-Ky-z(Nh@Ns$A5>3n_`Vt~mq8VKqRaeqbo9Ct6r8G{3@vmSBI!eK7%bojJqbOj z;b4i7;4uhM9f5F;8$_cd$(_~<-NSk>bFI@ zF??FFS2VIFh@w~wX_1jt>e}bfq|@niPt2k0s}dUOd%viziO6!tg>uRBTI9p{re2BU#~o&AJ;c_)kFathJYymk3E7_aNfDo;-2z z)NhbZr@CZ%!~b6BpZZfZ(}krjV@L%R7gT(w(wD68IB-EI`ub^IU01hy$CYZ=E4S6i zC>GA+W)#|d-sDR7+-$0GB-_XWr?l#_L#^4C6~K+zQLQoR`Zh zXXo?0Hgo=1_@@}8qL99{$BTl*=}9SlJs^*+6`Hvk(V$6#Z>WGFQiCpjUlIZ{Tu&yn zb0(LprH{BJf#);5MX$)qTee^rB2`2O1|%IVVjW0cEqbwBn`9AYpyV?5NI?{=}7B2G~}3}Tx5R(nl_|`7`ugz$DaDW0j!QfEOUTfk3HdQn@&8FZs_G} z0@C?n9ng$#$&xBHAIT0ZQWO~aB|WI(SWASeBowCJ!+;7EFNU5^BQw#+G(${ePU`$S znw(s`Xi8HdB*%nk#fx10QdKl0nCZ}hfs~FdTjmAv77kHWI8^nxFWFLrY=%kJQT!IN zu@mwReiQ;%yXtmQha0(p?B406z9&zWf%WBlJ{H90@Fu zoXO>bfDa#Me2mZVpD{zr?At==;YOU9IgJ^ekUA;2_#Pj(;p2U?U zms(jMc-&W6=OcVcZknp9bULpeTH@SVBJ?YFm-p-PSZ`riG_2S&wQSj z^O8qA%&ow{L`m~S0_1{sqSBC)ndF!pk49TepJS&s_|hY*X2bS`Vd0!FmsfM={CUjF zne)D3mbmm&wc1vRE~~U%Je=$E%gB^U(9+aY52^J_2~w&dDqfmk1QCHBXI*gtA~{4l z1rcpTZ$@Oc;dSNF0Pc17p0xhWTjAjn%Pe#L&3VVnueiL$XC+*P|3}D|`g}kcDm6C9 zH|1lQDO-Yw2O7mAQZnF!*e4`R7i&U_53>vU>@?1v_osm~86Ap(`7jaz(sehxqgYm_ zD(mhjAW2rt-l1h&X3kbW*Wf(EY(Xm8pIi>|D5B4_m@@;=-l0;wAVHu$NJux|BLv}D zp&;UiHzeI9tlrpz7O@qImRO#Y=ZS@Z*YpSk>U6g~t$z*+PB4Kg7xqHBAUFxhG9L`;@6y-by0OLK>as3x~Vf{LasFG0do zj(^krHf?olAbY>8R)e>Oo0{ICAVTZv{xr^yN(%CSKTQM&-zv9lqBLVh{GbDeCMe=2 zZ$C5QCQITM?w-wraA!0EIfcp{(Fq~c?uUkTt)2n|l5q8>FYnjBJ^j*@ZKJ#ntNjUo zZL4C1!s_=o`vzI}evvFJboDu3kBSv)8nQn?Dk@gynZWQH^b!3Y%7loBXlExGA~{Ts z2%%!QW~5{A#3|Pd&FG%&7#coU0UL78u+HpP-?^6z&Rj<6VN5Lu^VS5e6+?=QjLZ=3 z_9S}$;e-S;b+Ih8^?E%d4}p}0bf%JuO80=tISQq>{_%nYYCmdfO_Vx{w>KXc#oRo@pn_IW zC+!Q6G1MW_s-W}nwls$0*=86yLYLW?V|{E)DGA1b%e>Fc=P`4+%VC`J;LhjEEY9UO z?v$HKI1Z5%8gi3Jfi@x{XB0q~<@C?Fa$z5GDAuG_xlYfrITy(1w%D+`o8Uure_JKG z?(g4kyLf|cdZYJdUzcTJs17q0BX9I*>FMb?@VwA_DhP0)LQhyx(M*}Fsew?X=q#p6QSXU) zng8kMsUOZq@Mj%kNghhl1Rcc{Ec=9Pjh725?Z@rMPtFSN|vis`ZX(?rQ?l1@I z@?0q1o3rX2Rq=Pwo#x#DSKivR(<_qF7!gPyB_&P07Yk;(BO)p3NMNW+?Zbx`OH@^1 z&*E7%R5Mgn#YBiHT4lqE6+$nT1<(jFsj)8!9?QC|OW|1(E!V2F(*f5NxNZOrf4c_) z)uo=Igc@IPAEFl#P}U>jtf=g)=!c1@KBJK^LBhg;kLfxwQ(`ReXk7_;B~(mIhm6jz zxR5B1$`B&IRpCn*Z%E#Z%;;iYMT|?v8k;7z)A| zZsZV9#t;KW1O`fGLI4B>vO-951l(`2Qs@~^26}+IXKhP72XYj%8zRg7gW}%%Ae!08 znH`SjD)DmDp2AT7;qr*Z$Z*cTpbJjmKhE~4jJ;DJLH#Bii0FT~(jf21^HSSc=!DS3 ze3&aNi_B5cJL!t%a_@kNEr0sW(#t58EV^edrfB6V5HmCRuUhMQjY)K~6%f!$p&BCu zgFJnRcN4aPmlTyjv~&k33Fx~|zy(J-R2s0wWyXzm0aRuYnJ_Rdo`22yAo@C0z&1|&o;b1+mB3(%! zE^P2GMVev{hhf%(0fhyPIo368?f1%}`v%24txCx_L87&1pnR%kv{0)U7UShy*q@3t ziK;NXxk4?IO|s%K+Bal0idOkz6lda-zkucivcSZefARl8Jc#mADMFu$6*5T%2cs|vxcPv| z2mVFcs+&dk`V%^isEIy1j2x0Tl7`aFzL==b$djj}c3D=1taD%)$1F>)g!D_6cKR_U zQWgTjXT4{2df1O^loe%fr+oJBidfc-3Vk#5bW{xn^gE?m3X=e}rRuQ^o(5{*eDlJ6 z6-z(0Q7c!q1|Cagjk(n?otpSmBMA(p!Erup$5eKdcOKwK2S-0;evd+3ZFW(_7j5gk zxZOPoko#IzL$kJzSpStIfM#oI^j{Y@G8!{`|M5Q6rhI$jy3c;f^9T&5Owf}Y_pTqE zpvxQ46#Buwe~=M#AznDhl`g`j_>IG|8zdM^!ECZK#52`U3dQVG8OT4ciWAj+fV}xx zrZ46{J>oy?0pGVA=9XLVlZFN#gYq^5fOh9jjRuvm7;_iAoJ@-b4=uY039wn8Q285H zb@y5{H?rqQZ*z69*8`z@uf;XR%|}$W6bhCxy`JPY{@$h+8C-Bf9mt&Zs12$A`S@-h z+)S_7al*{ z*nqgaw98ZE{g&J|WCT5%1pV#_`Ac7LJF4WO9$Ws`l&YuWd;J*$@Vdv|Ig|wyZgCzD zJd^i7(e4V{BN@t2FK6O{3&Lr>nEnjZMfWzHQJKrvfr17z6V&>Ljh!%I@VD8Hi02sF zqmln~6W;dDL~Xb92&i4TVbt?@FnzXpDEkzq`B- zvz!{3T_%*l8;6;0#nCM#`tY4LGb2MO$bAO#k8UNjY5RJj-3VS4&Akwir%6jIDsb1+&|?aBs!pRhFOzJ z@9F#-nJM8p(-WVPfRtaz%;i~gPI)4MRlFV_wu1=l9Bxzb$`xSK?}}eN1o;*hei&Bo z>^|}C302`eP~&iMH4L3&6FNBt+L{lz1cd*=v>vUP_*gq!w3(V@fltP; zN$?DTHPI&JU-)t{70KW-FMEHC3imR`G?*r_I1wPQ zQc9BKV8HW_5j9iAccnp!*qCEG<~R0t?=a0g4R3%p8N?RqM(#K|4!#*eFS=Z&M%tL- znrgxwm3gnVGr?tA!g4<^M#LsCQ>Z5{WIPn{?Xv}sKiWnKBzgCl&mrVihR=1 zx@mJ>?L-FpxdNr(Cvc~EG&bD8Xhy@_(pX@=lZ-%roZ1O1Q7Fqd21{MFKB}k?dW<#2N zy9BkDYe~)@MfdI+dmh`KBFk?J4vk6h<4Ww4-tN@NB7%diF+OH37Yy-)YizN4^U-e| zr#lhjC$}6XRN4vqI-$ouoG9R7*MLJq5V}`u|9C1qYv-{qN)T8I zs4*u;NO?i{LSrR=O3VOcW5SG&=b=&Hb1FUPcI~3ihZmyVHO)GkJ6}kW zH_Ua;#;&3c8J>Hw)TO8l$egABPuBn{CCJx)~Ya5}d1r%!XJb=@NR zc%1+uqMiEcn@rjZxkOKbXqHG^DAp;fQ7c?ZC%)e~G4zjh`!znfdj=}^@3Gsa{EiLC z?coB?I*8lP_Cl8oJ)KDbZk0qH{tmA; zfHS<6N~sHc)JUIB;ypM!p(az@qB{wkKMtn#Dg%gR;_o${e|~<`^|On%ZKAYcQMSjF zEc9bw6GC3)->5O+jh&F?S>W#S$5%2Y_fx5coMB3JRwOB_L;I1uA& zm+*W{?v%z;uzH0Ph!4=RT!olgxD4axaX56T_?iwK*_(T#Zo}=cFQ)BbA?i3t;Hc*e z_`}(ak^Ft0{X|W#M({RzQ6%8V-6I)+`!hs>)^VFzTkU%bwvT=oM~f2Fr%2TP4Q4rN zm~mRBx_nDvEu8Uae9UZiMzHE13ifRWf!+#GJc*X%l-J5t_fsb*4Sk0ViUnp_|{{80??>quTRuC=N zXKLT?4YLD>$Dutw3ykwalp5?aJ^R%Ndy5JBH`?s~3)+vRZ!)&4lY739%4{@zls$5M zgiDy_H0RIiH4nmA%WXt%W!gr8P}7Af4|-m&ed?th$ugCWwX~zqmg0@m`+>)}%U3pQ z|J$l|8%CCMTA%3(5yVxj$8yRYHus#>k|o&VM!Im}yuCG$_hIROZAZ4P^*5w-kHuFOx731me3N|C6P5~$8*fGbSRz&OpcOBc?bH3j zi5qMf$lF|%SQUsez3sgA34h@vVR20;w_kUt5DgtxUl#h8N}yds;!+50+ebv@=0$iH z;a?53q#62&MeO(+%5>TxUSdo^O2_ajQ+5k$x)g;Adho<)X8mFfxv0NXXvP4v&!x}OZq2yU=NTE~xj+4% zLq}4DeA{Mx3EVxJ2k7Gf*MCXxcK9nGbhx>&^e^Ek&fU`+j7pc%ZY!;IvdJL9ieQ*B z3^U$5%I0zM93~!>%AoPr8%Z4-1LVPg&@)q)*k@~XSwS%@w^t|jt^d-LWBnEv+xg3> zmJ+}5-c&_8{iQbNtb^5KKPi9yl@Iu-d(~t)9r?JyH=Ftzv9HqaqEigB2mWoypY)<^ z9)!@V7wcTQU_|<2L`N2h@2n&Bp`+@>WjAMjaKVw}bh?qw#?wSZdk@VzfRrKRx8oD8 zAG3T+{1j|p&aM0Vf#Io=^Z0KMXzzxI%{rpETP&FN9*&I_LY9Oa2pgKq0S>3H9t>^u`ZvHQ^!*GV0k+NTd;)~NgNMWD&*IKomBM_vj) z-Mv_Wmm;l8CJZw?CmO=<_4P=-_^bo5(ENx+oaXzJ5S01iBJ6#UDF+xL{9-f3OeOTl@WRHB=Xg>AGL5+3zH<%ZS@f zjxy1FNUEd4m{8Fxs+}&k#0(dgsbQB-j<%dX07O&L+^;NZ=$)2*U`8-Qu2d|O*ZpCn z-|`FE>ouzhhDSVv_gjmQ_a8F!Jux+a({#kVKW*Xe1QF_+O0)TfC2K_p1bp0CVf1^~11V|0t^EU;vR{T~vxYLy ztLj#!37BRbw)Y*A?L4&;&I)E;t!zGAHptaAF+7D5{Lg>wYVd_8?Nywl2Ip;fhHuWB zTSyB6ijjKa&zzhmA7o72^oc3)C8$TP27|*Fd=k@8cQl1;MN#q@WLai4oO@c8R-L9P00>XKKQz8k!8=@q-&*i{5Dp~lWKfLRv!cfj zii)&04BooE_w9j`N<>(?%KlHy0ijPr9btBgocpVtmUuObWMpS{jD!+c`ujX#?b8&y z6?++e1a*29#!h>E6<^bC`P46Z9CQzAzj>Vx`I`j{NsI6+I-e?qp8Ys<4(@<)M5Uqi zS3HyeP4CFt zKKyHsx`g|A+62yq_PJGU&^ubou(Mmu6r@AaT610Y4DBY29SR=f{WVbRE6H9MCl_zI zEFrN7R;nHX$KOn0W;DlNc^N3bJ>I0yJD!yMXYQYyT{6OdEuS9tCA?PY@KQUxJeR{KtJMqd1$9 z?>U2We@=a@`;`8d#+e)XvUklq1pzGRWThGu3+(e{U$mW_Q83%ftKh}w=pXo;#`*lz zf3Tk|D7sA~V$nMNh=20kI%mgfV*8XX+|+xE6XuWG8rQh&@;yu!_wiFzJU|X_ZK>Ju zvfKjNy78?Hux(Y5_lnyOjuAIPz~14YIQq({wpNB5WG|1OmuJ~cDyk&1YHnr=m@=OEu9YJ_u z(g`WNxHE*T0${mDCrl*GOOYj}Uzfg_vsW;g;A2&>e5dozUaFx1(Z%u;07!iuASieR zH2n6E3U1t=w^0c4h~i=2WLpL4o+=zQI)AJ9HBzaUG0*}1H!fS6-O&L4?`Q}|t4;kmaB>iO*y#Cyi_7sx!kK(Ha=DDUeR^Vti zHGd|w7=nVkZh6Jh&jeqfW*aD%)G}^cxHM^)R-UGoHg@bEhTf2x^B_VqOG z^v1lGSse&KUMVd2(rzGOoo(6;WsjYP$m2ahz{YHe%Vvmau*jguY+E^(f~l0kp1T40 zr1pXT%G?HN__#R&Ec{<&x;LyxJncmbqa!cVAzms&=>kE>X0{4NW0(2o_~O={e=siZ z8BofNxFN`oleAt7(d!nIu?qj@5uxeQ4{&yHk(rOPE(bb+#||U!g1z%J1&@3Spx*1y zaUiQyW9M6e=H_RN+hYqUFIAhHjN%06-RoGzi)y(boq*L#J%_rONsOJto47_INJxvQQEiu7;bP~{>H!=BY~Jm>1mj&Z6g+nRh~rX? z^R_wI8=O(blb!#S?{0n{)!w-@Q76L#DEp_-Ez0~`#M)#tsfo z7@Vi_j_1Nefy&nv*pv!{W#o6X6|qVxh?G`B8HZq$Z|45#x(qaLjNOx@?mr$}9@cVG z#%#&J7??$xi;-H;aA!ndjmijiq?n0Gfjp5bLU@?%R(@B#e~{xgxE;}-fbL*5<{N*u zj2(o`j@%kDw{9JbGi1C!n2h{^Fm0sD-i5fZ0o{Uiv$&3Nyzn&5eGX8VJ7NEx?H2|g zZ8H5WDo;l866ZKUo$Wahuoy>=>Ji5~mV|cZ|Ht>lbC;T4D_zjIG{Xkty>zdU<(zU) zCJcWLz0t4EbX)$24~aH>mGyU8IuqO;B(mw9+hNeE?cxi_;0bj8mHn0QsmxEd{QGa> zmk_v4izJu~iNo5kQRV9*o!FHlXC?MoZ4A&O)Z22Bu{SK_ug|*(#|pI@OcygLeC`%6 zSjL2=6_}h^fc?L+b32+f%yJgE-a0xNHVv1e>Ra^44_@xq(~NyV&fnYJeT$%xYrTn& zCM{Fjc(jaCNyoo;^E-OCNS}8ZswgCMxg@{wH@1-v^cS8Y`+5s-g7&eYwkuPbV;{ZO z0okF#DlNhW!?nQqW9iq$7d~7d_Mfl_me>C3(|0%Bl-ZGMs;59}t-4~z*q{-Iw0qs> zKqvZpsAb#LAzvfBe^7voOHU@+8;8lMf&M>#=`lDp(eQCd1*y3IAFux___jTAKZWlF z>iC|9b7)&XaKeS*vagDnSS_*I+coJHu9^?%?I0bn#jYMc!^dxZs3Z)FCh z9IdN4$wm!zSsZC9il{~8BbTN%^*+bL<~?RV$wVo(tx=hDGAy|gELUT@VXfaJlxXNw zG4ob*kzSxyZyMQ3_0ImT!SDBN-Or^foJ8E1?tt!COMAIMcxJfem8HNvu-+$wRg%I6 ehgPv;{c>|3i!o-3$}a=T{`$`U|NsC0|NsAYb?ztt literal 0 HcmV?d00001 diff --git a/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.sift.ndb b/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.sift.ndb new file mode 100644 index 0000000000000000000000000000000000000000..031cda2999e80fd145b0946c66e0f6acfcf3351e GIT binary patch literal 96367 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 F|Nk^~4CMd- literal 0 HcmV?d00001 diff --git a/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.transcripts.ndb b/UnitTests/Resources/EndToEnd/GRCh37/chr12_7018490_7086889_Both90.transcripts.ndb new file mode 100644 index 0000000000000000000000000000000000000000..dfa69aa2d128fe43e45dd7dd4f98db9d3cd72e38 GIT binary patch literal 12623 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 literal 0 HcmV?d00001 diff --git a/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.bases b/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.bases deleted file mode 100644 index 86076f52e990eb9a679e6a749896df3f44a6b893..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40572 zcmZ6z30xCb+xMRkS=0cw6$pq5QgNxZ$&7$l6l|s2WA9tqCL|b;#em>amV!%BCm^c< zZEM0J;6!9~L#-QHodH2a7Hee@Q72HqU6d-QEbnz@==1--FQ0xq-(M#QlXI?fZD(}K zf5M~Vf;I(xrwUa?t2TwGjCnj>NCEr-Khfi({+E4?>EyXbX0{VT)wYmk;ILo#YU3F9Jw1wxZi<^jWF(aU=kyY`yCi&gmHH+=gZkU zQt%b*_^r{mKk#im`Amg|ZxV^SF2r#IAa(tk5hpPkkf=N>;G{$iNK~Jgz)B2P12Vs+ zm~av^08(9@rks?}fW)4=6Im(2)qu>slao1#832jVBj%ixr~xVcDGTFB++`t70}(an zEm?)Bfv5+Ur?5&w1CjKe4?rzii0gsKzY?ujh3SEa^QG3TlBj{0KXf8NBz!Lz$u!0e zu3+#kAS&$_dsbmI5LuZ&omCP&5Lx!r0h9z@2}J%@;K(XW4@AEBV=k*CdLV-SZN70N z2A`=0A_|^=#41b;#3b(fm{k%35Sd%^2`KcLKvd6NF{>~dh#1bqStU^eQFlvRj3XsD z%mWaab!54(k^C|ga)D>Q|^LD2}Z-nbPc-070E)(07RW^{FGG^8i+i5VW}WW zBGJQ0HI01^jL|SsTFn>6QE*QZ8i=WH`I1$b0f;*P&)2NNX&{og@r@t~!}KsR?a%%P zuHXwL1|ag+D=S%r>4BKiSN~xZZU7?E@BSCmqJXD3n#W9*7)y8X<^UB+{8 zj+_v%3NrvvHy#+XNkwL)Iel@32Y;@Sb8Ap#rgL@fw|H^)Z0_2(lC;S zJX~G z5OKE#1BDhIh$+l)a2kkwek)WEg)h>>$Q)`82X-;QNbEVW9$p0&qXr_A zA8-oO15tZ^+r(bOX&~aoVc1W&8@v*Ty!l%ct1vxK#+`RDtioYW4V3f2a=|{;K*X>A za0*icF~n~Z*lQ945VijXx5IE6i0nFS0@qw%?tqAq2WY<`7X*Yt6)3d(P^2oqE@Ty^ z1|moAq8$b+pa()b40Z~R_80m0sqy`#2crJ|```TqL>4@r3h#nB)&rrSWx|F797QdP zNgJ0K4M(Q3G$8ZKkOuYR^e(OYVc3Y z1U4b^xPVOid7P7&0g&9E=E6x_56E2F;|gyD?`8lZ&kVY;7jcOmkjlEhn3EU{$YkwZ z!b#i!Nd30!Q`j7Ew=@uyH_9oD1|se^!Ul(HxEhE&#h?w2mI{cx*nu`Ux_2Pv+Ap86 z3O4`|4|lJCxQMG^B!3~k1YV?vkvw1h6}$?JNe?t0OTo}kETyWRePe>I;Sw5ieuY9P+AToiAryQs8$$cLzlWEC-AnXCovk3*|W!& zlehtp936%YkHKcr0}(atD5knCgzTza&QJ{sR^bL9Cgt{O_#TT5Fcy@b2>^Eggiqf3 z6#^F<1b~SB&p3stfymn@U|X?40EoDE0CGSHd_PGdOQZGj!HbDvlBca;`37$lvhLNe= zYm3qx1B}e||1d)5INQgJA<_#Am-eWk6DEofQZ6O z*cosQR|7GB<%*F78DJzY9mQD-f|1Pp9YPc4qKA=4ZgdBBRl|s^7}yQjFj0OQS?yvl z4j`t3UdAfS0K}ZCLUy`%A#MO-E?$H{gt8_B5b>@-jfgnO|nG3R43yNqxvBwDEM7!AhQ3>ELLH9 zAhNM#+<^5!l>UJ;Ttgloh`95|99Cg!Af`r*yfq4GK-6Ck7s3W{p<(3wF*1APk4fDl z@XpBW^*}_W9ug){xEhE#t#ttf_iO-S(grz&8Gs0K5L`2B*FaRu3wKa(&omI(ecOXo zm>!7AE<~1!DjXncbO`(_vP~L@vs+}F$ZpB{0Z5rZ;d&q{^(=T;9HU`mQXhbaMTv+3 zh}0cGMha`A2V$xYK#BwkHvo}mj;vx65g3X7lTdXa^HKv*eYwaGk(B^l%sz+`BrqW{ zTM|Qz1!gPaBLtVUV>HnJChZb7X&+8TDFuqZK!o}Vr!X}T_51`%FwklMA-{+c1>_Wo z-3ch6Kt2G7dh?u9keWRg5*II{^aAEs4TRE*e-i~jrgeB+Vg^8z(Epn#0FtjuP!5ll z4v2b~2&FJuRvL(Ez70tPGASBpJf}xH7v=Os7D^(}t_ETZqe!780HS`~4W%(MVH$|6 z&O&AiN)JTc$mbMB0}&T09Ffme!^qTSe+Y~lU}T;Ub2+0l0I>x$GDD0lpjo$xj1sv` z=J?44$S4gkGGpgZ0s)cF07U&V%qffpVscARh7Xa?07TumiCiUAO*dVc%HJW2XAKmH zY@s-X>4C^Adp*Eo!wg9DfJ|NExWo*AO#j|x+*RBFNIba8*{DPfM7+I#asagVfv6XS z$hRW<1wuIhC|G5b1rVKQyjg{*fvAFHl*l9N03x5=`8O*7V&44h25_X9+k$@+U1T+o=#p$ihOdJme@I^a1C^4XGHazM+B0 z^yetGMP>m+{`n4-l4uEmn3Ma!_K@Q=0HKx)t1#4&As*c26wJY0b1J_bc|-JFfC%~? z3e~WTY9R9AP2>%c-vpw@ZgRmIMym6e4O~HH2SgU8b4G~ifrx?osIWwq4n!o~8aF#N z5JS{Ke}?s(K;-k&$XB9!2BMC>nF)y;u7NZmU#i<1})$mG5KkdwFpkT{pa#c4h&q26`9-uzfX2Nfntkw+ zlx{y1naH$gAY_o>Nzj%7qN+#SSOu|Y#Em(6-<^%FxWoX6!Z{}~6wx`GME(wTPNN5x zmZE5j(o$kn2PTQ4ts02Z=R=Z;qAd+Xr3@@b(Uyjh*;@cfDZa=6BcrMQ0@%d>BlXUJ zTqf)&8i=WV3sog3%mCDv`~sRfs0TQgPZcJk3JRGw5Ow-3a(-a?G!W|du}Z?W`&v>a zf=k0_7`d>DCW{TL$5O#`&_vQeoFRc?4GC3AY_Nopxv762;x@bvHvp2F!EuS{0hy-S zsoYhG0g%YO51l?_1!^F&XV7}wCV;42FGc^_1R!-j-S%Ib0A$Wzu>0300Ew>SdBp3Pa3!~jUX%61;NGd&=6 z?J$=-Lj47Zl1I+YQ0~aKHds4DtqtmrTrNL^=1cA;gzh|4PH2~_fl#e9Zf8K`iFOp5 zQ4Rt`CRL(njA9@V`FIasEm-0lBfa6zTcrTLK4#hqUg*?9EE3QPY3dZ zu(c%yKq4n;IVUkSAoHN(Gfv_Lz}H85IDdzK^snI{4}lEFkc|unK86OON{dh-4!@EH zqHcGYpjb%5NM*f3-U%5p5c%6f$kovI)dMjNmyoZ54PXEws9Y2$(S8Ktd=)Y=)>j>W zgc@DQHh`FGCCEFWr2+cCGdJp7Ed)uFP0>KaFFjL{F{)uC-_=6EM8>EG8uwgi7lY?w zJIUa4P#(wZKQj{rIs=SMron*?bTD#`2;GAOIU=I*kQ2-lNK6e#j2gxb1XlwxZ(1N# zL$_}L`rqOLV$=V>xWF;CxTw$IycCpFfK+1lNAOnUiGa8&9L*T2!l|*8h44#ZHYGG5 z*>mI*PC_f`$R||pYcVHr8jvCyz-_UP3J8_jtin*GO%DCdIWb%hgv<&22!@OaSr5Dp znH>;UNTbvX71Ah*ve(!k%4Fa21n-8-&HzZlJ^Du!`?g^C8|uO6Fme`Nqf>Djoy zLirhSGz&ZwYurF&7Xw)rvK&1SmEHpNEGV1?BCp&5pM*?94@90#M)fS(OhCj74U_}O zG}J(BV~2GfsIkM`Y@H9+U=QPqM9=$@KRVs2@ObIPk>erl9(QlsyaU|aT<_$bQC!-RQmyO_J`6V zWPi+|9^}qYngT?QBqIL>kqmnFiA$I>x5p)B0AxmbkUN8`P}KMQTWz3*HB=k>PuxJ6 z90a-#$MY$$9T${OF=-j7i{Y|xkpYMcbjarZJ=bEdVdz+kyl`X^t0a0LGT}VRpx{gC zfrx#>oWj&V)b78L@gT1UM6?}6fe%;1$Ryrp{f+@fqUs>Zk&q_?B2G6#^AA}KrUoRh zH`;Piq6Z{zU9|&$Bhkai=16P>Y54zgB(&HlN8(a*SZtJ$rvpt_C8WJmrGgA~g`xXW*(!7XuK{^k6)ZR|7GpGQea|2511{GAA?(Z06K_1gcAD zFThB)w{a0nq6Z?YQn@S&(*qHQZlVZ=OcIE!yvao{I83j}|l#shn1EGc%tDu7o{j9TpqUA^X z4~Xg};e-<{EKUP5-G7cd56l2aUhIcj4qcV#0mqwP$Xh`3i>lfGK3ZoQMq<}qYZPzP zFftE+M~(t+MPdMCO0J@`2P7PQTosXzbSTdOiR%HW>!Wt;vcp##wIhG+n8qqx55$!2 z1}}n4(*VSrNJ0jJ788g}KZLR#WYR$B^ag&t3p%?Yk7>XlkSEduF?)*sjbT8{{li?Q z4XG_4wf7vCX~P5qQcr(FP6Va4G$2*~{9{-bjD~SM$3fGJavUztMgawgdNhnOYgiW= zh`gbPZZKMRi5`$SP&+O$10YjMxpG(G_>poY{%Ln(CDlV0Dci;6KydSbl2gvf<#3fu+>tj(}jS({-yQb=NKN2>g& zB`gfu)j&kf{wbi)ya6%yUvmmG0I_G}=o))QPW05GXoKcW4M_d|cwAyMAj*5Vt2j!0 z$o7LG&KC4Q<1`S88{7i0k>eQ!H4scW zrUv2`4lMw?a73>yJEsyo5UPN<88!fMt^oO6N=CM73N*$p+XF4Md&HMr(-tE)co<0a`U=C_qHbGe|bjs;PmvrVm;*)byd=KH%06 zr-7Kg4f<_ydS!6{(Z0n9)bCen~x-4!(g0*JZlP3z1Lg5F9Jq=`c z6*>(h>XOiYWG4%dY}q|7p*gnOfq9-aE^z}OL$-j=2iFKAmlB{wM=1eSRE@SO8*HmT zL{%@h=xot@?JbH2@RjsHM9vLJ7f=SL24eCrp$roxR6tDAJJe`F-8~@k-8$-3@%LLY9K1P26g?xH_$+6Em4C_Oa?V5IdLUFi!ZqZvfJknl(L?}I`+h?c4U1}gr*$@b|Cse zI;tFDkBZoQh?`oJ4v}{?+(ct~pz&N7ejCb#iMkumxrQJIBQ;Ww<`MZYAnrjFG&|@) z6sn_`+aVGfhY#pDTEZ8^^?>A|9Ai$xsacLOHFOx6EnJ1;$-|~ZU(Ubr1BmrU zoV|fRqWWJ=9-mPl=E-#~7myf$*nTW)Z>S&3MG2HApeTV3OyO%F8wR4<|3YyAtqTyP z>xGOF&8UP1M32S&JKun)Q8qr`s8dG#_7u$(GFKqf`d}4i0AgC6a>k1rfT+s+*~ou< zz$bq#8sEWsAm+r4@x0mqL|ktDcL%EhQ33^10ZYhcP$zVV|QUtGkeALt7tcJa>e7M!DnokvVHWP+~CYf1+*$ zNO*qg&q=5$#z_h4iZK^5kuO8B0*E@BfMO9^c_8*IiQSRx(_n1#f|XqJ;`QtI;7-x6 zQ3I0N7bsN0vq~>MAnVhnvR?vb91ztboMcN9s`mbPNrKaW>;q!lRH6sOE)8+h0s9${ zsC+VweJe~QV1HNg3{K(}Y&i~#%00!Cbbw8;)G(qA!?_%^Ux=0>WI@Q~05QD}Ot>#- z0AiovVZQ=;hKF@dtl1;yMBMKN=Y-5&4a77iz~(__ZvaB27nnKBH&=T>p2bP1ToSpa zG`q9tKlp6Fqtaa#vM$JN{tNYsGTFIUGUoU~oFd0lbgUt6EcXU-?X zlfme_;s!wU5C|vPr$CrT)!^pPRp{STPhf8UHr~iJ0Ffg(u$SSY1fKWInaH%90~{78u323PZ^u@S7FA(u^KRS1kEL@}pLUze2997D! zS+e_tHA~{|Uc2#aqXtA7@c6btDKK^S<@j+E4MgovLbWm4HbB(#UQS^&5S3Mq8ouy^ zQMm(ERscSaomU{{-}3+rod*!D1zhU{R|BE`KWlFi)c&XPGf;>|1_6ZHxtx>b`niAC zj~B|A0T4Y_HSU7Zb5*E=%i5a+HE_uz=l|`d05MgMP^}CrU;rW;YTb|v*275t_7{4B zWRV_5Chs^r4UD-MVC0UJP#T7gl!)REPk2A1VQL`aY437Y;c6fzf8aAv@cfVgh}0N9 zXB8aN7(QnX{`v*0a03weZWnr1c=2RDc|H4o!y+JR-So41m<-a}!OuT>;PNo|{PaUzs$n;gN_dlc+tHh2t8YBfKm` zr%&VC2AyM$J}+S(4dU@lLaL9nMJdEwE_=iBGHS1&y5VBfW;JSaRi zZfsJ@fwtMze-yO4%kv5^d{p~(W7FV(Gx7W3X)_H?LGib@D~m5K40JZv?NhbYv}X11 zmyi8%Nz#0$LO^*o%FdJNxpqar)cE+7hI=fkFAI5i|4ivNkFHhP!&U3dZVuhC@|K0> z*o9do#pRajqIR4x-fk7XLeXQS6?hG+9sY>ie#ZTcOlq=Z*Vd|QQ$ueb_-sP0y*@ta zJL%kjLHo1YWq#6MeDs&2{bXy+ZNRdlvULyN1}fRA**4H;e!Bz+;5G>W$3+ZvERdO+0pXI+7Q>AO*=)cytl#!4HpPOQL=^h*ujXHg+@7P;hlG@ zLWBI%2U=QNbB_9jYd3C9w|ZDCeS9%M^(?8_?!@b$Xhpl(`j*S``Qh5!09A!cMq_V> z=!?}`x9-~H@T?eblXtc{`ybdC<|_|1i5D?DmhXCL)wFDB(#D{s(dY-+aGd`%~9_<~z-ZNjbh}*DN>{ zTWjRL|KXB2=T;uGb<{VekiWMmzOH5CE#IGG>u1h>p=u7#xEob+&m*vCOKW)cl1rD$ zPC1lU)>qEt4}Ti72T!i};5zXsk>Z$@6s|P%$=19myE(76rP(&yC0ap76j<7A={@-{ zTw48_N2iGdM(dmX-_Tww9KzCe_e6CNv}VebK6%xu`v10B8xN{IL!CEXez)^&O|)Z% z!TJ+z?Hz%MII3A)*wSo@>Fa9oe4C&*QGs~OOxJ+0G4wOC({u1MPbvGhv9ZUZuM$j6 zSYu5_zI_(q&Q7&+U1|3sv)*FY`C3%U~Fu7_)TZtFjdF%TyiBMG|}E^ z_(ov!ybzZpIj!96+i~IkK+piS=^XFaiwakV?vejIdv>An+1qE2FIm`m3s2BTR2~C{ z_nu_m2Y)<)%kV+Z4qt#P=+iE}_OGsKAANMKyfOF1p<~$=W+6#$!W`6rd>!Ab!fcYq zR$YK^on0pHzX=VK+C%(u2qvmn{yO(e@zc!E+b#aO;w+Um=X9<`QLg@Q&clr2i15|g zWLa2WPkU0J(FUurEg{Wj9(F=^&5khtbz2WLzG}*7(Wr}+&E0PvM7M4anVce1z3}z3 z+9-3WvX9VYr-#d}-bk-)@Qq3=j`y@ODJm)}p03dk{@Anr#6cJN+ruR#J@=1khLXE% zj1P*^N%Kd;J7P@uOhSS6$J(nA)s_}<8XIAWc~s!c)lM#5#houbpQ~4GaR`guJ?}&Z z@#4P@_J5N#74`9L&iq03n6`K0PGk=>lR>wFJ>0H#t?;V;CDD4NLw$;qsA<>-Eo(-D zvL)%j$k@(zqmSQ>9e;QH@z~|JuO)#A(_0c8Dd$n(4i?->kLvuo`Vs}&BeuqPj zkKG+e4O^nYJ={t!j^&c4`r|zMW`?F&2wjiOn*S(jq^o}OSh;?`E25?kBICcAT-8uYte(HEWiT z=Vau(V~MGmg0R7pU-S%iF)_2R@0=H0qe{RBgSt(k{tCeJL?a!eO(mCWFWW5b9vvMk zq8W8QY?-SOJbwI4ar-(t@|*bkt^C20xUciZvA37E)l@mC?^z2!KH~ACS%Z6rM7ivM ziM?i%yCH76^c64TiKZp(z~y(NW1ZIvo@=H*7IxQHhF1}ER!h$lzp9+lM9E*bu4ehK zeyv>Zt}Hs}pRr-~StDVJ!`j~E`MY~}s)#vPdsMopizoW;MD9-N^NzYyeyqH<^z34z zwVUSpni#G2+o;%9k=hqC>Z}SDkCn<)+NiUQ)IsADr9o}OgIk6wJFhQe;wqN-d6;@c zwn=#Tow3=`qk-{urpDL8qUUcgZ(sUDgH}oV7M*x)+V&!#QoiMtBE-Mlo zTG(ulTe?TxAIpAmdCO>O+Ux?`3$ES8rit*Nzq*tb2zII`C^ zb(JpAKGn#6$jF)wc|R%9n)Wib+H3#55pL7Cg7D!bPZlO5;_Lo9L1Z+~qUhbgAhuYQ zntf|hYU2YsjkJq1oy8CTc-6E>v*p?HqiL9t*LOzU?;Ug$@gsjVvhw$nZdGZE{Ig_V zPn5R2_v_8~R8#F$83kS@tLAL+Ua@?2g}2Vf$J?vM-pAV}DDH>Qu=l(_^!L8y?|sMr zYfX0a4^M9t0g7U^w%RkLMqTspix6Qi zZ>jI9z%`Z%|#Uv?xEN&`*`?%Mj_K zEo*7<^$oh-8sV$e)@ZG~x4Gxp#wso&Kr4ZJ;B6m4%Vviv zi_oV@!=DhTGx*`~DG^!ABLn9dQ}cw@<(VziLS8aIGAv#`$>Fm^o!@L;4OKy8r>{Kx zzLRNMkZP8*wPVMOS@t&_Q?5SB&Gu5orEVdtV~9kJ*uOY+Yf@fnD&b(nv-n70pDMto zEl!~6`cpO@qW;@*tKiwWak8+YE}dURZV^AlQJHAHk>A^G99Sz}jPH#4XuWB_W_EaP zQDcs@HEMm*YQ^1X={42<875kl_CZhha+%!9C0eN{D)TkddDtiV_uY>D+q)(#FU-j} zIYJ~jK`HA4``@g(%CEK*G&dW^t+ym`gvZwuA^u9z z)KKeTTo+ttr>O1@jx;S+9H?oC7c8znks|hRaa&;DKj0T(|1%`jjJ)Ams&nM0hdsrq zw%Okmz0oOreT#DB!DRyyrrS(U*cI-^;k#je1_VFlyK2mf6<*^2x=@#|>`-biwmB45&jD%2wWU>EH~h zO`JAwX^pBiNIsNXT%^+ZS@o!rcW-)=zq@$0(oYz;$I|Avc+s?Z6e}6WgaH-`@->-B%80p%RfLl7GQbK2*y}kJ?-j}qeSv;D$IE1McgrsUt4bU21``g;=_-tQ(L~kTR*H5&`mEUdnZ}XeMODW4P zE=fGHL)>pCveyh+4(*&a$*JYw8OtT*B#%eTEia!B@v6`bO2J$!SpT8-w(bq{f0dvDQ63rCvsk+$_Sg>+tZrBnm{CL0tx!^x3AgY zcd(&z(upnl0+Wdc?gZMtbWcgzt5{QqK1mYis9c>`_dcGJ95;PQ=m}N)35a!H#kM7> zFR{OXn1810MC4Lk&jF`>C5r3X;bg&4y4xn$+Bj#+#rfw(2l|A{_swh#!EXZ2kRIQg z3SI4%DY4&Njn(g>aYgUOu}7ER)?yAxcUy+b3C#)*Gi+orP^oIUvIvxIUx`J~Fl)=S*pD4RAD!%EBK7wh=dn};J49E>Ij zvw1BW8k+6hRVmtxINiZXTXXW>iU&lz?%FT(pa0zP-5X_U@DfFGQsSb-*5ELmeCfJi zd9TySe5s~KUZXVgU0M`UC|8M8M(MWPe)dGQCV(It}NztbJsHQ z<6~pI*$xBUd0SH%$sD5uN6oBLVwbKt>yq`kvwun}>PR_P*$C1ULCMx$=mrbsL5(jehpzue%qm zJ2>lh`-`Btvl~7)nPttx%0i#^s@BM=wCUlh+UYSf75UmN{_{4^^^eU~KNyv?23&St z7Ck+E){ny6+b3n+yoQ0q>47g2JOiJ^hdC;9#~zEwjtL z>S1T%yC`zcab8J-^zzlq&q`C_=cg~rd7eDy{`8XbJNXCA6YW*I1FeI$kI76XtsPog z9nJ*A#?4$T7_zinHffd7_1`^QE_%kyK5#jtDR1eRZNs|mbvpAA~co0OG^uTjpLW4HjeeS*V@KR zYxVP!dDuI+>v!ZC$F8?l##$wl@`ecrr~k8<>81b%Jx8B0jih{p$U` znsv(UDygl1@0_xT*H*t4ZOtk#26w;c{k5Uaz9vxot>_KX?`hSmB1@`KX_9?iWO3GX zm;dG;rae0N=*X!>Mu!6C+L%l%yd4ss;s1S@_4hADO=l-)q+hO*_6C%d`(|ZED2kpY z+1OP0&8t0I7QQ3=m^4(oEy!ohCoQ_=`vWD?7NvVs)T%UXOL5WG7})5sF|CiZryfiX zv}*lktKa%6-A~`h6gj%MMP;Gp#i6RJMI~Ba>5klI$6O=Q27k<#j@g|lSHAGg-G3pi zx9n|Mj_zE`&hV`PIoZ!oNJqX4RP_dJc2~5-hv#&JDX04nOCzJQaC5~F@M5x-jpdTta#g2O;Pgb2{m3~Lek*n&MQ^%rcU{QG^TlDJPY1b*(% zeLZEltN445YBA4C800-ws-&k%R=x4rTT@drd*#Ncs{ZWd-h`Cz!z-Tc?WHo_W$P=A zRVa&OavyK&fP zTI_F+iL39eoNA;NdfpHuJ2^~R^#R?SSkb^liHR8=ipa6rHPy;NuOgw`h%h;_tWi*B zdBc;Jy1-$WdZ5l+8YoyKUS~QW&MMaoOcj?azw)5^1Dp>onqQ#Zm@=_7L^fZ0UU{$n z(K?*p&R3pv_((gpK_ecE9E_oaq{pyPws5g$;xf(l^^tMPnRCBYwPeZGlzslbPm)}c z+WU;6*MAgLTD#L-Tbe?wFL$LXPM6fmPHP-52qz2mr85I3^A-ffB*qdHUMQM$;So4Qp zQLgsDkACX#q7c6Yqb7VU9pGz5sg-VG(Vot51Lb+<$)He)%hOKDMM>Ei>rAh8gce%! zOAcP_rly_JbmwTSLp^4HUK~Rov2pzSSWrTsXxm5oh54nflhUuLtCT}sgb9C#d&t`p zK@yoSZs(+Guk;Jq8t^D|{i6uoyrW0>xn(DFI&{7**AmwEl}8-EUNvODE865I`6b`! zrO;C4Tl`cN`5#T2&V=o{Zn*qz=h%@|@0(3|FZh~rW1cv~R5s3hKl6icSnxaJ&-FaV&GwBiM+9PtphrksEWip;z`+&|+^X7VguQ#Ia_u5w6lw1XC82ehOkn|~>6 z6-*GTp(R~lbD~u#7u=T&_w??Bq|kG0VC1CQYcuH*dNe5|@7%nv#2U@p_VirA*wZ^P&&&b=PlvOH5lm>Et?#1S)u5#KH^91lz)kTW@DNc{b&ioX^h4++dyE zTr9oX5h8pSHGIKi_$m?Mk^e0-e@S5IoWP>|RLxwIYxnctl-A@Gg{{8OZS>A?bX(-y zxtA+PJqs2(QR|)!_gC&1E$G-Wrwj$9ay-WQOmJ*uU|qCn=33kK?V;Ablj1Y=`Y>xb z8S8x#`Idt4u3kWs%8t{5H*zRaDKOf^zC17MEL0Q zibLXKo>cm!-cDD&Omq!NC@LPDRIq2zJ!ouRWYXrq5wb}hu561+6?E_t?VINZKdiN) z8*pY~R#Zsy9=C6sFS~w*UGB!W40rRq>}O6BdQ3d!F>wERYV7m+s+qe7I@4QQ{S>O* ziw|fpb4yRLl#mLVL)^v2e3e{0@#@!6b<9pt^K81YroS>+QKj&&PKuvSh^W-mbO%9) zywslh!%VVQT;bVR{V1m_=Z|LNm8UGN{eAln#7BOJiJQW3rVr4EOKND!ZHRDlXduE)%94}MHR6v&D}NGNy}sE7LK)1>v&9c!exhq zb4_L*okH?jt;2%G{$vf)Q)p>fG5_oY%lob-(**WwmN{A0HTNi)i&gx>nb_^Nt(|8( zQ-j5p-Q|V>iSL^{OM6et0nbq^+^@JZ^0jRFW@p*=*`L~(^M!sx-Ri3!$f#%EM&AGa z>etfi`=jI@sd#s&>(*l9G^KHrkF3pC5f`Bdmu>fMyej=ID#oO?uw`p_>RtcD^9C(b zBfYnFsEF3d`7&vkZ`6JrZ+e2T+}T_@G;s}2$WJPW<{AG;Oujl@8XI>zpk!fuC~rf8 zt#t5(_vzcwMrM|){erUmqqZ0Mrr!OpTp?|>(zJx#Psvkg<<>l_S9j!dvSliN8=`YE zkr=L0E1UUiLQVW){d1CZc8X%#C@Wvhe7}c1`xl(Zj;hgxN9QU+bwMhbwpL3&@SP@& zi;zjTzX%Ohy{VDDe46JM>Z_$w6K#y7v8{^LYVX{d!I?>0Bc(;EP>8QtKK}mEVR!oi zV%ug$4cbRt{rA7V&tC^xDO8Cwiv0Z(`IBw^f7&ejJ-}He#0UOg*(q%7 zr|rC<@Rb|;es^QIK{|UCf8#(@tpBQ~Bdb1CD8m2i<>K$PY30(j{z0_TJp6^%THo&k za$~uF5Kk5<5BS2yKX#W@P#E7^e#PIuZ&0`@M0l=Iid&gUqkL)&z(;fgCOz5ZD_d}ZZ|X5ot3Jzu_WR`+$g-20Yage(FofgsTr@BO|}k7}O{$-Y#DOS=w zmwu#vX!7C6D8Ke{n~kFU=XRgc+i_*`^Zw0qZL9fTsmzvH^#)qiN&l7w6y7p*G@BM} zC2KX3-_5e#6&L38%*wc6PMXg2@9kT+ZFH3J>=S?B>x|BJ{b02_st*1VgFw7gQ1jJ4 zjVdRfuo`t~mB(r)(Nftxo|af;XCW*4ehW`-n__Jos}5=@la=hNA*=$gG=F_UWFKf_ zwaeNqZIkGV$Ua0I^=C7sHV&Mb;jdm5)MPu9q21c1jPu)BqAJqM ztxEZ^MY6sOBDAFFoK0hyN^Whs$4>|Uf?P;Pk~U*rPR+sa0^_1U|6%z#X{YbJoS&Vq z>$Kf5#+$urOZ)GZZ_4SEKGeSO+x#l6xU#Zbb8n(L+aJzU7N}?D*T&f^T(uJqIO$eH z;R0tWN9kE}0$NQvN-IkjH1f)9(v6wGoKcSU;-O&3<#D-zx;97FN(VU9^4AmYzdOjldQR)1`Mc7)|oHghy z=Yt%RGcy(5*+vJPinA40O@o3^ht&Rk$Jm#h$oHLWyn$kr@>A!`(2RAN{yyv27nLcmX&zUst7%VK9-8Z1Sv*qQm+K#1@}+ZYXt19&ucdf#`z2Lc@SLo3TAi}Owk9oB zuC(4zoE2()Fe}1bb#T73YqXXod%{~rXG}_3(`udV6kC)VYL#UjwAQgD=Ru3yI?_4z z=&GL8zM`c$t6SvZVX~mVmU}R~uxjvgjY^#G@fNedB$-^=A879)TA@rd z-kWU_Y;sPn`P0ZES>r1tN217?KjW_g);Esj1sbI~b~vB|b`vpColhFAoED93`8&2f zZiC#c^D4bMXKd%rosBKqx9^N=fPV_gSCQe6S6touu+b~XoSefa3+UB9MZh<;}oKlU>AVwh~}TZekT3)yCZng#H0 z(?F#rT9f{5{4eQth58Ub1PAAQ zYuzI7))I!|312$sR){l|@8@{0YEE&SVJ=>4@sE7F8*$N)^3XBqE1NIfqpBzMz1U=u z7qm!FWfZf*a>B&MDIQZWVlU30>Yi%6<;q-b#9IFw%WLju-?^7-8YWRrJRM@^I2+ql z{_%Bj@j}_@|1zYD<*Y-K%45nbE6-lo@@Xz_vH6d^OaFR1KfU1TpRMOMp9nE}d+hni zkmu!&-EJ>S+rKH?cungsjqAQvX8+YxMF^%=_&LVe@T&X$78fV3zOr%|0?w#&92_5L2~c(eX}<$E%N&z zewMgpRajWEB1sl8ST0SBPd%R=8jwPlBNxu0Q|5e-nc|-kTSI)?plMAzfa?3^p;xs9h7&Jc zjU21oj-E&qw5d?G2OoG@MA)#oU($W%AyD~Fp>}wfX)IN5S-yNOb=iA4l{L@ED?%eO|x?b1o z^}M@*AKlpIZD3n0U}-`t?bO&inIdK`ZjkWpl z27+7oE^Rm>7YkYK=*v84b&wz(;{-;~PN4qx^6dw58t(#_4oLl2KlxmkGF zzxq9N`VZxuQ@4KHl=mN|Z2_h$X2G!`2&`g5P0Rxps0E4z>U0L+^9k*nlFKXM2Aw7b zBs+pWBrBf?1_8} zC=p{a8JL%>;`24Aw9YOl0qt^!q`t`4J*ltKLdKf(mGKjVG=i7A17)0Q?n9wZ1?$lw znMHXjEyEa&Bk8m*t{m+URYU>Vjs}qjGxL==2v)Q&5%md)m~=_w9OzIDuXoMD=}gKa zpP+krfWxfN=uvfaR#&AcB}c}{csV%Mqs;WzZt7{eyw2?K-y%jlZ5K_3ErJhVDt*m*E=x-6Hj?n5?4tE|M(Wk~pOD!YW}8=HtPQ1!VpYD5l@L;Ey5kL) zEuK1N$VP~w<*_~lNiasKpeG zIFA>H>*l8{omQgS!2fwjOVG5jcfJF@L$ESDjck}#B6KFTOw5=+O5zkpK1+HNMdo^? zCw;=vF@}DDL51m}_1M{^fh4`={~_x;yHT;q*Zz(xP4+~efumZ&G?{JqZLrT(h}j9K z8o@RxtEH01r4WhuF?cvX2K^DLH2bqQj5efo4W^6v^tDt?!b*Vt;(!YvVzDaFR*k$b z2g8&SlkCxYYiC-`e|cQW`~mZE?3N@=c!O+t5~6}KLMbgWBPbkSJT${6pRa*34ug+) zW|an_9-=0A(bz0jcoc@i!eF!l&dd>UM9K*9_hDE#41z{LKyM)fD?iNj2Y?puJESIp z`mO&)ak5R&teeQCNs@`u+n*Rj&ke>tz2V0GhBszr!&SXKa61Om0p{K!hx?D?H4&P_ zh+8*c1DQNB4jbMMqQse#c>oe*5R%>NJvf9e9o+gxN0+|N`nNY33}xOh!l?TsI)PP& z2~66|O)8#puJVu~z$O0C#wz_O3zdg4n5h}(+NKRFBd4|9^>ts5I(wPZuHOMoTbOf{ z<7=|7`HlC**c&)!nC6i#cV_>Ze9NG>X@nZX=NscZN5~ut%vzKx2o9pS>jk6@G5KYP z+w*a{F6i3J5}MA-vmoodHvzV@dL9_d%Niuk(v2wYa*4=}&s=mT*= ztU-FlXfI(w{g)&t2RaB=;g=@ zoFF?8N2B)I`~n{`A++k)Yf_=q7Bm9rHnAG9_Pzb~_EL!??2HmvV-v4)CPEV`R(ALT zZyRQb0?u>}w?&YRw_=;(SjD7|$b6#xq9F)TFs!C9%TuZdXU8O?$lo{j0<~(vKYL2L(v(LIt#+eh~(yhF$JYfU{ z)vqZw6=&udi%1}XK1ZqE8b<&Y5<#Ft)hAiOMC>@KZ0{c#XeC}y=VUb10ON+L_Yv^I zK*d3xIP9rc;l8(n0@F$?OwH#pK0J;o&~5)CyBwE5fh&#k=E^>EjSkVL;NlxfR#@qF)i*9~&qYQNU z`SM)~ZKl}Xp_yx(SdNT(j9@V*dLV&|w@}%f-E1}u#v`dP1cSUZxEH1Mq_4`P#R)Yv z`x|hopOP*16H)PLdl-#Ic>Ob5Xp);o^8-R%7FI;uZ))1BDq(d>NdyKM-G_hOu5|cKyQ;uY z*~U0bg9TKKiHeTkU66{JDRII@JH0Mm?8hN7CHsIZFANv-r}qJhh```FDZ$~ zpk%e<2U;^#&G~$96w8N1oNGa2`Do|^m+V9fXb2jihpMLyWU+NiI_IKqP8=}`tb&65 z_XNJGSW7R)YL`Jy3n3=f0-}%h_xvtMfYkdPMQg)T$}})AVbPS{V%A-cwCv1?c_M+% z`i~en(WZF=hU4r}1=cv~V6W!>Z^0Pd8@N}WTA)4+PtM|!e^jHY(FvvvoU9Bu$J(I% z2d!N!TJ5*ujI<-|i=QHyOe$^&TbPjU|4A=K9k5sHLg&oqc6Q5EAb5k^omzwgV_G*1 z$|k|0F`ffF5q;{7`2&JytJ0fS>}M+XveF)Y4>ojq_^{?2b0yzslVK{?d6n;c;fWKx|MD_>Lzhcx>{vNEuV?VC6MZ`mrJ7AJ$4!&_(aPF4 z;chfcunGlcd?_fv7Wb3$)A{QUv3|?FHf>i2y5YJiv3GOTTCP&VBQL;AJ>g{i>>t&A zvkFB*j862kmKi-vDL=UMj|;aYL8=1m2#Z3dmEsXBUn~@_Y6_Y*V+$DY$d`Q5{d>1( zie01*S(hPn2#B~QMJQ)dxfp*qOa(AvwsbBa>9RyTlzl2xHyil}4cNXkk>o{{_b^-h?ot%~z$O;*&>GX(xoC+)zH1 zrIE4Z^pPXgkXFP9fJ+!yWwO6|3X2NYk=k$bha>dRcBnKg%QKvqoU1w~!v03JMtVp# zkiPKVUo&6wb2VE)k^tpF@EL@YF=G*AP8HmOfM^jBQZvmT{w_6^f~5&+aIo%a+V6Cz zP2y?uH@yU-LEq?6C}e;USxSx`wh2(xBSElAiSSp1i>RPmnyP6`in9UAq7;8!?T6dr z2GQ^Gzj~9exsx5fW(EppXfPlkGX&~~;Te3J+1UwAV-0&6`k;%k7^q7?Frn9}v#1@UMF`r+)z=6>39VKQ zOS!Dbn+zPgbqMkKx2fD2!0d9@wy^67LhB~{i$SwblvR4c#IQ5!C=Q97MjRT(*0E4Y zo^X1kRNHjcjX3dQr&rT{1G)an5;3K@X{5|6u-UMM0J|TR>3TgOOxc{rB4U#R^&((A zMk2%bc3XZHo7WDdqXSC^@m92_ln8VLI=he-08PUtc8LC#NDp}EKPWZ92+NzQr-^f@ z@NDcrY9q9*1*4|)5HKZQUTq_|40HZXvGroJfCu!b^r|UI880~;+|8S~(;j-)OD#Y7 zgzom!NC;XJc>jEG5+&^02vj<} z8$a*%V)H^Z?Xy>OYzyrc(HzGFk88E?=?1q9pzVNLgL<0{t-=)|x-)>FtywR-Se%Rw z3PR0LJC%ikhKz{>bB5jFEk@T|fQ|_hIzl*vzwV&&4ly=_(}s04nb&xm=jlkbvM|(F zup{W?ZdZH(T2nvJXNR$Mz8L{cG3zJn<34SuXbcCJ4O07z_ z(pnF7@n9Ri5;ssQ$p$p5tStM~^y8~Pc&MHM)#yOYb6aTeVa~m zpqi>eP9dmzLpr7?$%rxpClV*5y7` zPlyKPTF+pvCK@@Es8#2%GwAOXj!R@{xkt1df~Od3ujNcu1uekL!sKX@cS^CpRiwbn0)@~9@2Xv3^yOYUg6O4WC&27~)=^0=M6R0Bsz_QH zbZU4CiRtO~y;{b)4;c>z+LI2wfa?j4_PcfjkTth0=eKQ5lPPsqJWIM z1I_O0Ery`eZAcLpJmh_SyORwPr)Ews!imxngTCiLe(GPMTpR>y1+OVfYFd zEG85rXSJ=kw_l)=doephj#S=n0?0BZ%-Cl>0Bg2g#=l_I5qmC|WMBK=1Ja&<5^dkZC0 z>7~z*e&VXCDzW`%7HksxgL@$Wa4(d?fVn6=n+UiU{t4G`6c_7L%d53m$2O)~G6R=gA$&EBS#4oJt-}B`HqCf|s(3#5sQM3{L_KOMoYU?JdC$ zB&@m=8XjdAj3Xyy_e1kA*93nHzyy}--|K*aM=Mr z<*I=)Y;?e&gc8s&7=DO6_Z508Xr!Qi(tuSNM54xHU#`L^ddOCUN84tgt{BG&3JteA z`=eD8T#w(7*J)H4sb6U-(@&Na*k z)b$xDG5EG5PD^d#Km321x840HVe+hwp1&dEN2iH@OZpauvTCyWxRwZJLago%x->RM z=oMAUOJ$La5-zqe$Iu{6`}1#AouscfKR0OiNJ58405{igJjxPJhmTQ8(WDX1L&~3w z8$Yuz{_|}z|FTvgAo-Y9X9%iz5{yO9?!l`;gPKNz!;Q&$jd8QH*a1oEGtNy2SQFVw z&pJdr6L(+0-YJsj0e< zlI7p1Hm`f+21XN|DfC5numj$P+ED(eJ&?}vu<>5QPCQ-V5i|t4XT4nJ!dWw}L!AK* zPs?pd5}Q8pmli7jN`Ak}xR^L3nM<@8*KS?v)eZak%!yC`f4|rnz%LfVwbcCiHLWD) zf!Wp_%Ghym* zKBvV@BA@&}pVw=>3j%YB&dzxGGh(8~x3qyVz67CdBVovGUzNkQ;H#pHHSgt6wdaXJ-$hL|hey$dy#d;}U-lURdn z9Cc3L`jYyYhTiXH6Mg?S_`SyG#4QwUN1u9z6@>zMo)o2ew>OBec>b zkXxR(r?-F7ZygQ~ny|4_y0b?3S3jVrnCN+B4Dh_#B7PZg`}uR~-bc$i=GVQlkZ?2x ztn?~&JT{-ah=q<8>>_Th$%Tn}@0jm4S$@0GD8HByW5 zCdT?{KxxqT0bSuDzB-~jc58h>_HiQ z`X$m<)dXhwKU~x17}HNi7KI9JiX$>vi$>KgdVt7y&A;l zJHWpSq$#@T4D0Y2=68f_H@*Xqr|`t7@D5YJl`UqvB&f>Ci}@n4o;V|Y;W2K|OW&`s z0`E#X`K0RwU8nm${O|KKqy3z^ht-_>f9sxUdqVUXEq|f#&clKA=ah52CAJcQmQ7draAdsw)?82p$$KGPK;c=>iaYbH3;biB8?=6!N<*Mct=tv^Cv z5;M!$>60}J9P6nL{RfS2iC#}EXuSQ>f_=X5Kc%B0aj{U$eXm}WRYWkNn{=8wTMg4F zqiEIGFq{t%c<33{Vs-2qe2)b#gIHW_Pc;uWWl?VdZUiO3uaJpT;RBZ;uVq!&2kqJx6#vUfj6huCZ1*kY zoTtM1eZxraI$t?o7kI;2ybVPn6{hP81?&ea$FCl4`uMx)>^;6iu?y?^U*R2~Lj8Kn zD^gUPyNICVRVJk*Tr?V%BshM(m=rcRI!sPoo#-fovA4%Yh<8h+$@$Dc?{S}2aZ_`q z>C%N&&PJq$i5HGkN}>DaG3<<1RSkOPJb_%2st>BOG@-I4M_I|=y(lzRL`{Md6S^4= z6D9pVpu3mp?axTr|A{Ik38*#5$=S03!)fbN7Aq;p-A}Zz;8)k^&j*H1j!>OU^0tC) zDVKDVneBFPoPZWGl>CE;U6k9+cJ@zvg39;?u78$V@%%v~a#!ez%;3>I_c3n44*JC^ z`O2GZl{*`?v!j+5bdZ`z>7nMf%y_Z-ZywY3Q&CIH)G4i$C2>v)$AdgV5+DGbnw!z9 znY9%?Tz^*gS)dWAn7<>Ens?PUu1`p|qpN&DVlOM_VJD=cad!0MEFhMS5_UfnPps#r zd`U}J!61V(Ag%9?NKZ0UH3;t31XV{-w)j!S$)*0xOY}TZSYm2Mw_1=mlc!;6#<|%J z!64GTi^aw@D&-jF+6xP%0WfYnY6q<{hCE2=meIiugsgT)IC55y6=6@(EC;u(ktT~8Ql8&; z9~LHUC8QK;cyG*gn#xJVysM*Brz@K0>DGvHtEgZkLjN(Zk2iC6t#`{mU2}6dqqoCS zhuvu=_n#WyaIFfhw~DZnD|@rO_equIoayusTSL~{CPx_S-wR)zn(TZ{* zO4%i|Ff|xRBGnAYjV?wvmU@L_(3-G8i_)o=yV8Z|#14nnIc}1Q30;YLVD#l{Y;Sk) zqMuG+mo;*xT~n^C4EhRpl$PLS=;=eGDB}jWuG)zv3Cg_RE}-bj<2GUN;iceOXAi`{ z+GlY4J0=FU5ed1p&f%7HcwZP~>-9)oEYWA=2?QJ;+YB#hrXmuxR?Xib+BIJew1)gT zS~brSi)6E5yVhbb=wuB;{V-$|~{_7PP%v$3rb;h9g1#Hty|VHF`x6sAVQ< zIxy!zD^Rp8s;e`@`%%+YipL|jS@^ex7vH>cqf3m1TpbUC&KSu`WLiy_Fu*|IRTja4 zpAA|j`kS+LozZb!O#h;wsT#V?L5B@Y^Puc!FJ56UZVeG}sX8c!R1|d1sI%PNc2Or~ zV_X7VNfhtDu=l`X-0R#@%GyNjN>ZbB+9=8kogj4Q)plZbpLcB{M&-J6gXzX8Y_|cPm{aa+uu;|mBVUYBAy&u`Bq`I({8lM7$QO2a&o-Ay$i64FZr^S zvc~$11J&x5{uxINYWy6>5IEketS>$}@27?ILnJJ7t3tNAp^-5~D|?!sjBwvw~a-+JIJ;YF1X_#(+NEY{As_XdcnFGZ8vE z;lE8k5T)q#Du5M!$o`~++$*q*Y)ow1C^rQ)XbO#R1PKe?dr=C(X|itpz{K5uaySp* z@FI3)JgbQLZk{m8iEY=$s_pj`r+evj-uGX#FB*q~XTi2VQFO{R!h%zFM#Vv1`7Z=+ z6yn$J%AiM*#}gI=k^25Qjo#XF*Io_T&~P_;?7lENkm6A`lPwA^ z74f5eW8||bxL&Z(va?ucQjT4m= zjY|24Y1dPut6+qnx4xIg0;XLE5Ck^0jVK~`tJVms1B4rFQF2k>h9i-$-KlXDZyU-S ze-^L&I6);Su%+d&d+5;~$JuREiFBoHa1fb49BRVoRj%D{B{m6{Bh9hisARO;4NLfA ze|RW(^B+&=b&S@Y*!&XaY@!m=g>;q_$lm?O1aUn%+?+e=m-lDW z%AtI>Fj^`Qm$`4{TPJEt6ZaRFWb)0b8q%ZLhjZ#_sgY+ z)$kNtS^a<@sZ%p%HOSk$av^(7R$kVX;)S>JY5LwY;_PB~UHD=3LC3pZ*yYygO^9=A zE4jFTt?A6_&ibM9RxLwk^Tj5xmcg1tg9eZp!_;t%Pe)SfgkRam-}JSjuq8-qtVoa1z26MDLb_ zf!*rU6?H-*A-h7{&`{!}Okw|(6!|(@aYPJa75D$cGFTum|3pFpeWI{u#h1k#C$2C; z(ItC10DZTPK+z=wefOglGfNW8c_03@K)-q^bvR7?mJv5zL9#2VrpRy9zJI>lzxpm< zsl0mi@|%ghnZ^{2EPjrqnCCm$3|M*OEewEur3n6v0oI^jPQB$iY5wv0IU)2rvkzbc zfMRt9l5paLH$bua;LB0V_)hawdlHa8S39OyebxKr!WRW{zXO1Hbd*XUBq^dsTWbnZ z0tDhRYX1{JN?1NN`b6jW$yR|j#uReO1YjwA2x#q%x)E&X8VD}k75Hm^95+hq<&J!C zO34o&4SswEa;-fQ+k~nsvGMs zNTb7Og}pguOWp48X>^oXz6shB>o-&cr&PWKH6F+hn#XIKp4S@W%QkXOp6$*Syoi#c zU&bW9`Eg&q<1@KLfdy}5qAzyDtZtafYFYq1#uTv*o^Q|oK040dUiRweA@ zBq>Wb#Kz0?tf*Mjdb*q{`J|XzdemyuU@&GhF^gRLOsCLZ-n{vyL#6ex^tTtkq-pLL zwIz16ok>I@w@tWa=SWTiE$QVwsXKmyDS`0YxdEs26Je^*wxJ-&3|w4eW9o2kGbO97 znl0vN`mvj)nYHJ!oAaykw0}W^*x}@e=-$Gho9bM~+Nh6s$I$*etF$(smviojGhupN z{@QXOt+0=A?oEhSLkE7^`tn13Yw_mjxics3{6FcYMLQgjZpvRj4w%$D!e-aW<@K`1 zjGGsZLu++c0qJI6ANU|8Y8Z3BarrgIAQPrGW$Qs!WZ*VOtTwIwdDiR6MCKOPML6aT_G+99WlJUtc5f=$001CC`S>_coCMIMlG<;Rua;e3;KOK? zzxW~1zv31Akjh#o<+3~KKW6*m{!$}=p>^HN@(xsGm!vLQZ-;A7R+rbeQF4`2Ejy@o z+Wn-ZG7G|JL|A4gsn`;XpMU$%}KgA&;($o`9UtPmqXt$Y0zJLF?Oe-a`+T!Fs zlhLW5khCx{R(f{V?_f`&s7?JYkF}eKm=Ch@R9(lvgA}BBrdlf=RJ(Z0dI&b`98LXT>y;QdiDfYseepJ?2#(jgR;DWW|(SSxjsQes%Bd zQAzBRZo$rZo#2$!=7D$ml%!yH?Zu~WW`i$BPrQFil-FsX&s8Gka}N9;h?d+Z-^$-M zc*)9q)5{QYi%TmR8|v%%VB_TtQT#%ac+|sGR*}c7KPZk#d0Nsrt~dH1tgLr93;!n6 zLhjq0{Cq*QA)ik8)Z=iSEsJ}zozZkXo-GPY+xorkUYJRT&8Jm{ix^SaPIvQWYJfI1 z$`})_C}=91S8MYs)TLH*JXUw|1W?fG5M`&yCc#fPKFgG|IT;igr2~MRrtgd?c^qP7CZnSPA$(r>B;w46+iXs zT?pCi%5@Vr_P9iu#|I{G@0tJ$e;%5{qm5kugIWglLBC# ziVP|w$tMb&5W}ec_iY51Tm-2 z!N=lC-Ve-|S_g+C4aHe{cW2lR7;U^jg*+x?Pp}1v+tGCaJ);Lr!EfT7Tp|Teaz=y8 ztRhACN{bT1Di*JZmV5)YNvol1%_3ei5D`=Yj_Qj29n+@^nd%ceWrrLw7O$3|>SLk6 z4uVzIsGsFg3luKnG=dg;1XT{Y&&N+Wj`?-H)57#L$O>O-@i4;dpN~tja zomE!V-Fy%JxdpN)j1u4*Vd?Ae)`utN6Vr>j<>+TFd0sLvBPRK2It6C%odAR2FJHYZ z@S^ec`iBZ0^V%IpcI;?-BvOu7{OX)MYbYrDdJeG^YhlM4%TBCnSXy~o)mS#Ol;2p# z;yb$JVZB(qGK;qA48yw;;llQdTxuSlSkOj^h275{>5FekXLWc|h2|8f5{86J%csN< za8HBSc+_Dqny-_x-ir5vcts{`@L7p1f+YHjfW4r5gH(R8JbCEF^G<1yw_v{Cbb03< z8a0qQ!1Ila5LgLI+2Z@+pxnBK!SczWM!{&AMcKM!3HWH6-}@)3^TEY#?8T}5YBT@X{07BZmOYSePoPt-Y}`skfHPJ!hW?4TewiI8UJ=?Hs$_VNhoRhKzAn9 zrn*-a_w7)aIvDvx;MTk8{7Z}uYjQ1FvvUP&cxBfj*P>uh3^#1|M(&r$Nh`SENbzA&bhFotcrkcU zyeO~_8;u|V&2cA9EiO`TIIL_`v9ZJv{b87%vs#TOIk%%w@l!O}vttO$hc77wPGJEQ zfCdQ&$Vv0eM@fT303Gs5QR&IlTE@~tPV!I^GhHMO26_T#yPqkFLC|FPw95|xceb){ z1gi~K1ys!M0x$t0J_?@z{RNd;44+A$9&agf&G=Za_7Bu2ptT)TF;sO`*tmQCTAEfL z-)}77MM66XhU?*TcL9{0*ai^INuRF~I^)#Bq7)Osyka5<`YONy$d12`)JeS;3DmFV zMPM8?47~pi0~{v9ig8r&!%X!Q#sK4J-Av&fQxj1W?A;niyrDl$PiDQ@Yi1tB(Gqh7 zqf?Psn0dgca6vRA)3FLk>D{ikKSWMRWm2i#3kuAMbg$Zs@iM)p%%pM~pl>eAvzPoa z6y3APj=s5n+#VTw4IsQBOO2uKBcsF#h^`fY}I%lE8AS*nm!)nog<7uY% zf!_MNw05(4t$dwftqm9Xj6YZ>t`{6r-MZeO-F+U*Jv@|4D$n=NJQ3GX9^(C`ytgw* zm^E*|{os8;#0!5D!9mAsp-rmE6W86UiGg$QVY4l^(aYxjg+_U5E!}>m`RrlKKOAEO zDV4S3COfV+5!FsPyZUt+J@Ia`WKnOQ2l@Thm7sZ?)Ue| zS6fN$7x8rgI>-m*zQ|CyepngV%TJX6WY?6yXN_(KL|Hb9Q;(|puZUBH;*ib0xy|BL zeLAmyuC_uh+uIsK?Z|2(SsD98_Aq{*s4UTsjy2^Uyhn6lT)S^^^$Tq@7EgS7&NM;F zS2Y9rPOMak0Uy7B=vD=DT8%&nqo3_;m-O085V6DDEXU$25tgVYl^pDOuzM_RdJ9)7 z&U?8iywrK_2%oa)Xy{ZZM}jrwy_wHiHpl;@(U+83B!@^%?hN>2kY>rrLBon?ciVqk ztW5$vO!*;GGjVOWwqw47mXbXRKFWaRw)AScgI?*XNU0-j7%NJ4i@B?pzaX0r%}yB( zBBz9)8|wo%DtIyCH&x)`2~k3J_J$w+S;AJb028!m9HEh9Jhk={8z zBXm2xm*8TGA1ZSvB;Rg7yEwSFC_ZPNB%7LnycT9lt!XlkxFdmhfW0X^T=lhI=I_r{ z$x2pDHO^^J!h@^kg5(~{8zl>kvPGea5`y{iLr((a858r?{$KdzxGZfbnv&xsH2b(L zy$w7{RNyykxzj-85sy7_*m;zhEkBgY_j6XNbdo3`jeZYeQVqqTpx$Sjdp&YSyn)PV zYAZk}?jHWSE`tGwxb6*h4f>g9T+VhVDXydkdEu&1 zkGF>A7qbNa-EVIoTqv-vloZZsoxpk~*dt^6Qi+ibL4Iy^Nd_SsN1 zhuj!qq7z94MK0B@fH1w;K29KgCUWJ!A<3}U2#i6LyASFMr>G)!n<_b^>P5Y^St%{ zS(h-%>X|?lmbqL%wk`ps{IqS93E`WB<;CWM-Dmgj+VN(}?>F!lM7`k$mb3lU8_3)x zer0Gs!!_r^f31KB3KjYwrIEZ{T_7kO_4fxqBS}Y}rQR<)2O~18j(X0mRBH9i^*%A* z@b$OuFM2vo-`5%ZvU^px!e@R`TQ`W`Y`832>HWj4VlYcP^m*xvMZ@vk`Y0FS(qoxG z;f}B_5}d<8?2Hs)eIOFX0(=E#$0_>_h1&4fnj2sVDb$D$e6s@J_c^v=1s`|urt9G! z5%BTCCRVDfZa!jWN&H#Im9(4n)`Hay-N!o_bz#F(hQa$zhd`TiY{!Top@0Gvck*k6 z9fOPphK8>d1?qTcIFaOY^DmqlAb$9svvlYxSorYYeJD`JP|RDtz^he>_nSgs)772p zi?X)5bjN%%-TwGNW&ft2r*#ykZS#Bkt4XslIb0fLDKxBMc~m2$-hDJCb_U;`e>dPh zz*u4@t=VkX`-b^ew8?cA9^qw8b3<~B@acQR9c@;-Gk zwr=V`?bhZ2TlQoxVEb;*>ZL7aQSJB=!Qgp-oiHw}yRgUMa4$*pvW2&Lh0Z=_89P*0 zY~iIN=|4EAD4`;C-1ZkY(*xDlr>jjLy{`}~c7;5gi?iLaDi|$oiPzlSiJscL1Y7SC z_P>{l%4bPS!SaO_S$PAgOg2n>@@!YOIxky%36J2`;?8x#fP8IJ-_y{6E3{6hd{c<0!l|cTpI> z2w;}H`f{~iO9UgO>Eyn;;0uI9@=w9hIl*J@A-<@J7JTS#o=DX1ARDSmPft%v<4&pM z@qun166TzK+*XegR0&D^fS@ikV9~kvdh)C^Gm@Il!a8z!3 zBd`Z*t_(>!nCCczhSamO;yt1&{?~lIPN(c>Hl<1@_4PaD_p65qiSm_|#l^}#o9v+E z`PRfYorclx%ZR`oLI7rca(H33|9jxtnY@Vx0JPOdas}CwJHA&WDDnj;Ag!|k)i?z1 zPre=ZGc8Y5e7cuU01VTC(5iQO=7YzaUrdPrc8)gB|JpfvIs)@e5g-F>z?JJyya+e( z$9lYLXzpJq#4GLU14P+eg#@n(an-j~z={VL4}WPMNXdzpjRX1~ur0}hT_`;UEZhYI z^DG_2Zwg5tNGg-tGSYfq4t>zy4}rG1f5Gn;q;=o3*?rF0jJpRLp_3Oj4xZKqH@`!w z2Oj2EI$59>8xggOg#+y3r38AHASJ&E9v}vYfaZ7 zt=#-jCv=!P=#^fv_V1S!X6$|cL4R;Y@ly;uPVCb4vTZ&5Ei-luJdxMap{z2m7sI6G z=qEV)kgtWUou-Ojdh5mw3wQyN>2yp! zkH^iey5d)7;eC`Pc#IDc`Ri@s^&?08u3(X@K)2A&Ex#d`MXW)!-MkBV)FsO>A^r;b zxd@PxsM16WvHX!^6^_=l!Pzf#t(Wxo#Qp zDl@8b4NHA}@Rx9<4mD5+9)C#(>Ms%6N^K&wKg-L42PfKTaY3;=?zN6xQq@sq2qCRc zqZ6LsZ2!JCG3)hIrcLp?_pZv0U=G;KtiNSvJzbz&rpBdlYSTD5^!dTEL#t00?ha*- z+p{8sxZFs9;^JpnwausWMF_G(tkGkEIc&#*x2TrXs^IG7cD(OWsZW87yjyxd{J-3~ z?chOX#TupBJ1i6CTKYDqVgoDgny9^(mg1#q%9_j@41*E#&z4qOJe{Y#$r!AQuGp2C zM_26S9a1h*LK5`l6@?m0mqzEQ7}5QKm#-fz2iqZ3u_Md%7$P59B&l{E4j#Nvlaw;N z-^h*2KUw)`NovNOTTelPoyCMFi@F9>z3KP`N^W#e^btR)quys~@@Rrz5f+rjj$h6+ z=`f#MeS*ru}UsSAqi{FeY~?<>m-?_NjWRcO?JKU5wGw5^w% zPa8Zif2>f9Z2F1Eq`jiuHm4tfRaKa=8K4*5`S9?UcQnd&>|)vWxtIAbH=<+@E4ft- zYets@e2+kp?6*r3%Nr!qEcL0>qZQ$Qr{|xo4+0|^~Lb#txN2FWEWqD8E`Tm2; zm19{;M;AvuX56nleJMaQE)^|GTCJL3>Jg9gW|7PUgcahQS)ddv|jj5>>+a~vF4&uB$GoxnD zK24XZbt6$5>7g?FgZu7vNF(Ycvnx7k%ElfE{oGj4x0;*_Z$CK5H$**ul|Mfua;76^{A>iQNuc)ig>;oP?p@_!^07&=P zuZcc@BSWSOFkm^T@3F9bKtz?v0S$*4@T!zIs8A{YyY%8g8~h7WeCkeUQB(a){K2q# z|CF|eowPe1Aa2FK@OG52uk0tLTPQpE8x2Pegk_e_SMNP?_31^*TU{#3k%!3ngpy}X z(v6)r4J~WM)hq5zu(_D|K1Ovo1+vokyi-)?K78d*zflSy=7oM{IO}0W_K`5k>NRDH zqW2hMp2B}V6hag0?*;kf-R2((SG^t_Sv_Poi)U6=F1C(h=9ymiq`}j1-kb{tDdzcQ zwJ$s>p}iVKD&Nz`w; zF7!=A@=J!kOI`i7@Zgg6*A-HY^X4hM4Sb9|eSvy;mRiFslsC(bqAtn%4=y-@>4&d9 zE*-oe#I4JzH|Q%K&)la~JzD-H3GJ3ZpLu`7ee;p&DaiTiv|j4OG=4mBWXIO&V3YbE zu#s{v)?iZ!1a-6rf>=z+xlIq}O%1xz_;vh{7J_i ztWH8LkiE&bw0`lbxDBX|3$9doK za5rpzjjje#>XBd+p8OH?U*D@8o&9$iD()vekSFGvi6Jvl<@)Ut_yE-T!f&vj+niE) zd@A+=QgStGt^9>it0VRscY|Lv^(2H-s?3kI@a?*VhZeSXRIWK$ZQuEEmrRvHe^PQV zS5eWp0pq=cw}26Uvm^WpQo8>4zqi(Tethc&=n=#4@>QFwe|lbr{rq=S=rx!p)T(-+ m@?7P}$6gV)l$34lo@}rgC-?*&rxL6L+UE9?Kl;e0>Hh=1dLGFD diff --git a/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.polyphen.ndb b/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.polyphen.ndb deleted file mode 100644 index f739eff185601b41cdded3849592aca56eb5715e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119816 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 L|NsC0|NsC0blM^O diff --git a/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.sift.ndb b/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.sift.ndb deleted file mode 100644 index f1b1da991a48424c6442278504b89809d2f88589..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79546 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 W8d!kfx1}A*{`$`U|NsC0|NsB{s)NV? diff --git a/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.transcripts.ndb b/UnitTests/Resources/GRCh37_chr12_7018490_7086889/chr12_7018490_7086889_BothRefSeqAndEnsembl84_pos.transcripts.ndb deleted file mode 100644 index b83adcb63e06760eeb4e5729de097764d4b40a72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11614 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