diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index e79ba77ade7..00f6aa9fa53 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -13,23 +13,24 @@ jobs: with: maven_opts: -Phdp3.1,RClient -Dopencga.war.name=opencga -Dcheckstyle.skip - test: - uses: ./.github/workflows/test-analysis.yml - needs: build - with: - test_profile: runShortTests,runMediumTests - secrets: inherit +## TASK-4970: remove the test job from the develop.yml workflow because it is not needed anymore. +# test: +# uses: ./.github/workflows/test-analysis.yml +# needs: build +# with: +# test_profile: runShortTests,runMediumTests +# secrets: inherit deploy-maven: uses: opencb/java-common-libs/.github/workflows/deploy-maven-repository-workflow.yml@develop - needs: test + needs: build with: maven_opts: -P hdp3.1 -Dopencga.war.name=opencga secrets: inherit deploy-docker: uses: opencb/java-common-libs/.github/workflows/deploy-docker-hub-workflow.yml@develop - needs: test + needs: build with: cli: python3 ./build/cloud/docker/docker-build.py push --images base,init secrets: inherit diff --git a/.github/workflows/docker-aws-emr.yml b/.github/workflows/docker-aws-emr.yml deleted file mode 100644 index 392514a9a9b..00000000000 --- a/.github/workflows/docker-aws-emr.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Release new AWS EMR OpenCGA version - -on: - push: - tags: - - '*' - workflow_dispatch: - -jobs: - build: - uses: opencb/java-common-libs/.github/workflows/build-java-app-workflow.yml@develop - with: - maven_opts: -Pemr6.1 -Dopencga.war.name=opencga - - deploy-docker: - uses: opencb/java-common-libs/.github/workflows/deploy-docker-hub-workflow.yml@develop - needs: build - with: - cli: python3 ./build/cloud/docker/docker-build.py push --images base,init - secrets: inherit diff --git a/.github/workflows/long-test-analysis.yml b/.github/workflows/long-test-analysis.yml index e0c14b79175..10fdee5a0c3 100644 --- a/.github/workflows/long-test-analysis.yml +++ b/.github/workflows/long-test-analysis.yml @@ -14,8 +14,8 @@ jobs: matrix: hadoop: [ "hdp3.1", "hdi5.1", "emr6.1", "emr6.13" ] uses: ./.github/workflows/test-analysis.yml - secrets: inherit with: test_profile: runShortTests,runMediumTests,runLongTests hadoop: ${{ matrix.hadoop }} + secrets: inherit diff --git a/.github/workflows/manual-deploy-docker.yml b/.github/workflows/manual-deploy-docker.yml index 69472345e83..3ee65923466 100644 --- a/.github/workflows/manual-deploy-docker.yml +++ b/.github/workflows/manual-deploy-docker.yml @@ -51,10 +51,9 @@ jobs: name: build-folder path: build - deploy-docker: uses: opencb/java-common-libs/.github/workflows/deploy-docker-hub-workflow.yml@develop needs: build with: cli: python3 ./build/cloud/docker/docker-build.py push --images base,init --tag ${{ inputs.tag }} - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.github/workflows/manual-deploy-ext-tools.yml b/.github/workflows/manual-deploy-ext-tools.yml index 63e61914ea0..b614a520caf 100644 --- a/.github/workflows/manual-deploy-ext-tools.yml +++ b/.github/workflows/manual-deploy-ext-tools.yml @@ -46,11 +46,9 @@ jobs: name: build-folder path: build - deploy-docker-ext-tools: uses: opencb/java-common-libs/.github/workflows/deploy-docker-hub-workflow.yml@develop needs: build with: cli: python3 ./build/cloud/docker/docker-build.py push --images ext-tools --tag ${{ inputs.tag }} secrets: inherit - diff --git a/.github/workflows/pull-request-approved.yml b/.github/workflows/pull-request-approved.yml new file mode 100644 index 00000000000..753466dd67a --- /dev/null +++ b/.github/workflows/pull-request-approved.yml @@ -0,0 +1,19 @@ +name: Pull request approve workflow + +on: + pull_request_review: + types: [submitted] + +jobs: + build: + uses: opencb/java-common-libs/.github/workflows/build-java-app-workflow.yml@develop + with: + maven_opts: -Phdp3.1,RClient -Dopencga.war.name=opencga -Dcheckstyle.skip + + test: + name: "Run all tests before merging, ie. short, medium and long tests." + uses: ./.github/workflows/test-analysis.yml + needs: build + with: + test_profile: runShortTests,runMediumTests,runLongTests + secrets: inherit diff --git a/.github/workflows/pull-request-merge.yml b/.github/workflows/pull-request-merged.yml similarity index 100% rename from .github/workflows/pull-request-merge.yml rename to .github/workflows/pull-request-merged.yml diff --git a/.github/workflows/task.yml b/.github/workflows/task.yml index f27e7ce3b7f..cc8470b747a 100644 --- a/.github/workflows/task.yml +++ b/.github/workflows/task.yml @@ -17,9 +17,9 @@ jobs: test: uses: ./.github/workflows/test-analysis.yml needs: build - secrets: inherit with: test_profile: runShortTests + secrets: inherit deploy-docker: uses: opencb/java-common-libs/.github/workflows/deploy-docker-hub-workflow.yml@develop diff --git a/.github/workflows/test-analysis.yml b/.github/workflows/test-analysis.yml index bb48ccd4b40..5166e231e5d 100644 --- a/.github/workflows/test-analysis.yml +++ b/.github/workflows/test-analysis.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: '0' + fetch-depth: '10' - name: Set up JDK 11 uses: actions/setup-java@v4 with: @@ -65,7 +65,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: '0' + fetch-depth: '10' - name: Set up JDK 8 uses: actions/setup-java@v4 with: @@ -91,7 +91,7 @@ jobs: run: mvn -B clean install -DskipTests -P ${{ inputs.hadoop }} -Dcheckstyle.skip ${{ inputs.mvn_opts }} - name: Run Junit tests run: mvn -B verify surefire-report:report --fail-never -f ${{ (inputs.module == '' || inputs.module == 'all') && '.' || inputs.module }} -P ${{ inputs.hadoop }},${{ inputs.test_profile }} -Dcheckstyle.skip ${{ inputs.mvn_opts }} - - name: Publish Test Report + - name: Publish Test Report on GitHub uses: scacap/action-surefire-report@v1 env: NODE_OPTIONS: '--max_old_space_size=4096' diff --git a/opencga-analysis/pom.xml b/opencga-analysis/pom.xml index 3b96f1b246e..25ddffde6bc 100644 --- a/opencga-analysis/pom.xml +++ b/opencga-analysis/pom.xml @@ -357,6 +357,17 @@ + + + ../opencga-core/src/test/resources + + log4j2-test.xml + + + + src/test/resources + + org.apache.maven.plugins diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentCoverageAnalysis.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentCoverageAnalysis.java index 2712d22b8f3..ce7b82a7dba 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentCoverageAnalysis.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentCoverageAnalysis.java @@ -54,6 +54,10 @@ protected void check() throws Exception { super.check(); // Sanity check + if (StringUtils.isEmpty(getJobId())) { + throw new ToolException("Missing job ID"); + } + if (StringUtils.isEmpty(getStudy())) { throw new ToolException("Missing study when computing alignment coverage"); } @@ -168,20 +172,22 @@ protected void run() throws Exception { + ") was not create, please, check log files."); } - // Try to copy the BW file into the BAM file directory + // Try to move the BW file into the BAM file directory + boolean moveSuccessful = false; Path targetPath = Paths.get(bamCatalogFile.getUri()).getParent().resolve(bwPath.getFileName()); try { - Files.move(bwPath, targetPath); + Path movedPath = Files.move(bwPath, targetPath); + moveSuccessful = targetPath.equals(movedPath); } catch (Exception e) { - // Do nothing - logger.info("Moving from {} to {}: {}", bwPath, targetPath, e.getMessage()); + // Log message + logger.info("Error moving the coverage file into the BAM folder {} to {}", bwPath, targetPath, e); } - if (targetPath.toFile().exists()) { + if (moveSuccessful) { bwPath = targetPath; - logger.info("Coverage file was copied into the BAM folder: {}", bwPath); + logger.info("Coverage file was moved into the BAM folder: {}", bwPath); } else { - logger.info("Couldn't copy the coverage file into the BAM folder. The coverage file is in the job folder instead: {}", + logger.info("Couldn't move the coverage file into the BAM folder. The coverage file is in the job folder instead: {}", bwPath); } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentIndexOperation.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentIndexOperation.java index f21986c2174..fa537f5a973 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentIndexOperation.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentIndexOperation.java @@ -16,29 +16,34 @@ package org.opencb.opencga.analysis.alignment; +import org.apache.commons.lang3.StringUtils; import org.opencb.biodata.tools.alignment.BamManager; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.analysis.tools.OpenCgaTool; +import org.opencb.opencga.analysis.tools.OpenCgaToolScopeStudy; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.core.exceptions.ToolException; +import org.opencb.opencga.core.models.alignment.AlignmentIndexParams; +import org.opencb.opencga.core.models.alignment.CoverageIndexParams; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.common.InternalStatus; import org.opencb.opencga.core.models.file.*; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.tools.annotations.Tool; +import org.opencb.opencga.core.tools.annotations.ToolParams; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @Tool(id = AlignmentIndexOperation.ID, resource = Enums.Resource.ALIGNMENT, description = "Index alignment.") -public class AlignmentIndexOperation extends OpenCgaTool { +public class AlignmentIndexOperation extends OpenCgaToolScopeStudy { public static final String ID = "alignment-index-run"; public static final String DESCRIPTION = "Index a given alignment file, e.g., create a .bai file from a .bam file"; - private String study; - private String inputFile; + @ToolParams + protected final AlignmentIndexParams indexParams = new AlignmentIndexParams(); private File inputCatalogFile; private Path inputPath; @@ -48,14 +53,23 @@ public class AlignmentIndexOperation extends OpenCgaTool { protected void check() throws Exception { super.check(); + // Sanity check + if (StringUtils.isEmpty(getJobId())) { + throw new ToolException("Missing job ID"); + } + + if (StringUtils.isEmpty(getStudy())) { + throw new ToolException("Missing study when computing alignment index"); + } + OpenCGAResult fileResult; try { - fileResult = catalogManager.getFileManager().get(getStudy(), inputFile, QueryOptions.empty(), token); + fileResult = catalogManager.getFileManager().get(getStudy(), indexParams.getFileId(), QueryOptions.empty(), token); } catch (CatalogException e) { - throw new ToolException("Error accessing file '" + inputFile + "' of the study " + study + "'", e); + throw new ToolException("Error accessing file '" + indexParams.getFileId() + "' of the study " + study + "'", e); } if (fileResult.getNumResults() <= 0) { - throw new ToolException("File '" + inputFile + "' not found in study '" + study + "'"); + throw new ToolException("File '" + indexParams.getFileId() + "' not found in study '" + study + "'"); } inputCatalogFile = fileResult.getResults().get(0); @@ -64,7 +78,7 @@ protected void check() throws Exception { // Check if the input file is .bam or .cram if (!filename.endsWith(AlignmentConstants.BAM_EXTENSION) && !filename.endsWith(AlignmentConstants.CRAM_EXTENSION)) { - throw new ToolException("Invalid input alignment file '" + inputFile + "': it must be in BAM or CRAM format"); + throw new ToolException("Invalid input alignment file '" + indexParams.getFileId() + "': it must be in BAM or CRAM format"); } outputPath = getOutDir().resolve(filename + (filename.endsWith(AlignmentConstants.BAM_EXTENSION) @@ -73,6 +87,9 @@ protected void check() throws Exception { @Override protected void run() throws Exception { + setUpStorageEngineExecutor(study); + + logger.info("Running with parameters {}", indexParams); step(ID, () -> { // Compute index if necessary @@ -82,28 +99,31 @@ protected void run() throws Exception { bamManager.close(); if (!outputPath.toFile().exists()) { - throw new ToolException("Something wrong happened when computing index file for '" + inputFile + "'"); + throw new ToolException("Something wrong happened when computing index file for '" + indexParams.getFileId() + "'"); } - // Try to copy the BAI file into the BAM file directory + // Try to move the BAI file into the BAM file directory + boolean moveSuccessful = false; Path targetPath = inputPath.getParent().resolve(outputPath.getFileName()); try { - Files.move(outputPath, targetPath); + Path movedPath = Files.move(outputPath, targetPath); + moveSuccessful = targetPath.equals(movedPath); } catch (Exception e) { - // Do nothing - logger.info("Moving from {} to {}: {}", outputPath, targetPath, e.getMessage()); + // Log message + logger.info("Error moving from {} to {}", outputPath, targetPath, e); } - if (targetPath.toFile().exists()) { + if (moveSuccessful) { outputPath = targetPath; - logger.info("Alignment index file was copied into the BAM folder: {}", outputPath); + logger.info("Alignment index file was moved into the BAM folder: {}", outputPath); } else { - logger.info("Couldn't copy the alignment index file into the BAM folder. The index file is in the job folder instead: {}", + logger.info("Couldn't move the alignment index file into the BAM folder. The index file is in the job folder instead: {}", outputPath); } // Link generated BAI file and update samples info, related file - File baiCatalogFile = AlignmentAnalysisUtils.linkAndUpdate(inputCatalogFile, outputPath, getJobId(), study, catalogManager, token); + File baiCatalogFile = AlignmentAnalysisUtils.linkAndUpdate(inputCatalogFile, outputPath, getJobId(), study, catalogManager, + token); // Update BAM file internal in order to set the alignment index (BAI) FileInternalAlignmentIndex fileAlignmentIndex = new FileInternalAlignmentIndex(new InternalStatus(InternalStatus.READY), @@ -111,22 +131,4 @@ protected void run() throws Exception { catalogManager.getFileManager().updateFileInternalAlignmentIndex(study, inputCatalogFile, fileAlignmentIndex, token); }); } - - public String getStudy() { - return study; - } - - public AlignmentIndexOperation setStudy(String study) { - this.study = study; - return this; - } - - public String getInputFile() { - return inputFile; - } - - public AlignmentIndexOperation setInputFile(String inputFile) { - this.inputFile = inputFile; - return this; - } } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentStorageManager.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentStorageManager.java index 7c3396cb48e..ed151da4947 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentStorageManager.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/alignment/AlignmentStorageManager.java @@ -33,13 +33,17 @@ import org.opencb.opencga.analysis.StorageManager; import org.opencb.opencga.analysis.models.FileInfo; import org.opencb.opencga.analysis.models.StudyInfo; +import org.opencb.opencga.analysis.tools.ToolRunner; import org.opencb.opencga.catalog.db.api.FileDBAdaptor; import org.opencb.opencga.catalog.db.api.ProjectDBAdaptor; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.managers.CatalogManager; import org.opencb.opencga.catalog.utils.CatalogFqn; import org.opencb.opencga.catalog.utils.ParamUtils; +import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.exceptions.ToolException; +import org.opencb.opencga.core.models.alignment.AlignmentIndexParams; +import org.opencb.opencga.core.models.alignment.CoverageIndexParams; import org.opencb.opencga.core.models.JwtPayload; import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.project.Project; @@ -88,21 +92,18 @@ public AlignmentStorageManager(CatalogManager catalogManager, StorageEngineFacto initStatsMap(); } - //------------------------------------------------------------------------- - // INDEX - //------------------------------------------------------------------------- - - public void index(String study, String inputFile, String outdir, String token) throws ToolException { - ObjectMap params = new ObjectMap(); - - AlignmentIndexOperation indexOperation = new AlignmentIndexOperation(); - indexOperation.setUp(null, catalogManager, storageEngineFactory, params, Paths.get(outdir), jobId, token); - - indexOperation.setStudy(study); - indexOperation.setInputFile(inputFile); - - indexOperation.start(); - } +// //------------------------------------------------------------------------- +// // INDEX +// //------------------------------------------------------------------------- +// +// public void index(String study, String inputFile, String outdir, String token) throws ToolException { +// ToolRunner toolRunner = new ToolRunner("", catalogManager, storageEngineFactory); +// +// AlignmentIndexParams params = new AlignmentIndexParams(); +// params.setFileId(inputFile); +// toolRunner.execute(AlignmentIndexOperation.class, params, new ObjectMap(ParamConstants.STUDY_PARAM, study), Paths.get(outdir), +// jobId, token); +// } //------------------------------------------------------------------------- // QUERY diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java index d7756ea82a5..d9c2e7c5734 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java @@ -64,7 +64,7 @@ import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.user.User; import org.opencb.opencga.core.response.OpenCGAResult; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.StorageEngineFactory; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysis.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysis.java index 39de4806eb0..06e1c7f4838 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysis.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysis.java @@ -44,7 +44,7 @@ import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.response.OpenCGAResult; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; @@ -68,6 +68,7 @@ public class ExomiserInterpretationAnalysis extends InterpretationAnalysis { private String studyId; private String clinicalAnalysisId; private String sampleId; + private ClinicalAnalysis.Type clinicalAnalysisType; private ClinicalAnalysis clinicalAnalysis; @@ -116,6 +117,14 @@ protected void check() throws Exception { } sampleId = clinicalAnalysis.getProband().getSamples().get(0).getId(); + if (clinicalAnalysis.getType() == ClinicalAnalysis.Type.FAMILY) { + clinicalAnalysisType = ClinicalAnalysis.Type.FAMILY; + } else { + clinicalAnalysisType = ClinicalAnalysis.Type.SINGLE; + } + logger.info("The clinical analysis type is {}, so the Exomiser will be run in mode {}", clinicalAnalysis.getType(), + clinicalAnalysisType); + // Update executor params with OpenCGA home and session ID setUpStorageEngineExecutor(studyId); } @@ -128,6 +137,7 @@ protected void run() throws ToolException { getToolExecutor(ExomiserWrapperAnalysisExecutor.class) .setStudyId(studyId) .setSampleId(sampleId) + .setClinicalAnalysisType(clinicalAnalysisType) .execute(); saveInterpretation(studyId, clinicalAnalysis); @@ -181,7 +191,8 @@ private List getPrimaryFindings() throws IOException, StorageEn // Prepare variant query List sampleIds = new ArrayList<>(); - if (clinicalAnalysis.getFamily() != null && CollectionUtils.isNotEmpty(clinicalAnalysis.getFamily().getMembers())) { + if (clinicalAnalysis.getType() == ClinicalAnalysis.Type.FAMILY && clinicalAnalysis.getFamily() != null + && CollectionUtils.isNotEmpty(clinicalAnalysis.getFamily().getMembers())) { for (Individual member : clinicalAnalysis.getFamily().getMembers()) { Individual individual = IndividualQcUtils.getIndividualById(studyId, member.getId(), getCatalogManager(), getToken()); if (CollectionUtils.isNotEmpty(individual.getSamples())) { diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/tiering/CancerTieringInterpretationAnalysisExecutor.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/tiering/CancerTieringInterpretationAnalysisExecutor.java index ce89757143a..ea8dc7c3c89 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/tiering/CancerTieringInterpretationAnalysisExecutor.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/tiering/CancerTieringInterpretationAnalysisExecutor.java @@ -41,7 +41,7 @@ import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.response.OpenCGAResult; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.tools.OpenCgaToolExecutor; import org.opencb.opencga.core.tools.annotations.ToolExecutor; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/zetta/ZettaInterpretationAnalysisExecutor.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/zetta/ZettaInterpretationAnalysisExecutor.java index c45f0b484a9..cbf7e8211d2 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/zetta/ZettaInterpretationAnalysisExecutor.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/zetta/ZettaInterpretationAnalysisExecutor.java @@ -28,7 +28,7 @@ import org.opencb.opencga.analysis.clinical.ClinicalUtils; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.core.exceptions.ToolException; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.tools.OpenCgaToolExecutor; import org.opencb.opencga.core.tools.annotations.ToolExecutor; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; @@ -40,7 +40,6 @@ import java.util.Map; import static org.opencb.opencga.analysis.clinical.InterpretationAnalysis.PRIMARY_FINDINGS_FILENAME; -import static org.opencb.opencga.analysis.clinical.InterpretationAnalysis.SECONDARY_FINDINGS_FILENAME; import static org.opencb.opencga.analysis.variant.manager.VariantCatalogQueryUtils.FAMILY_SEGREGATION; @ToolExecutor(id = "opencga-local", diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/OpenCgaTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/OpenCgaTool.java index 7769315137b..c80a799a050 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/OpenCgaTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/OpenCgaTool.java @@ -460,7 +460,7 @@ protected final void step(String stepId, StepRunnable step) throws ToolException } catch (ToolException e) { throw e; } catch (Exception e) { - throw new ToolException("Exception from step " + stepId, e); + throw new ToolException("Exception from step '" + stepId + "'", e); } } else { privateLogger.info("------- Skip step " + stepId + " -------"); diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/VariantStorageManager.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/VariantStorageManager.java index 847a2bb1ac6..6fb44855fb7 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/VariantStorageManager.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/VariantStorageManager.java @@ -71,9 +71,8 @@ import org.opencb.opencga.core.models.sample.SamplePermissions; import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.study.StudyPermissions; -import org.opencb.opencga.core.models.variant.VariantPruneParams; import org.opencb.opencga.core.response.OpenCGAResult; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.tools.ToolParams; import org.opencb.opencga.storage.core.StorageEngineFactory; import org.opencb.opencga.storage.core.StoragePipelineResult; @@ -406,7 +405,6 @@ public DataResult familyIndex(String study, List familiesStr, bool throws CatalogException, StorageEngineException { return secureOperation(VariantFamilyIndexOperationTool.ID, study, params, token, engine -> { List trios = new LinkedList<>(); - List events = new LinkedList<>(); VariantStorageMetadataManager metadataManager = engine.getMetadataManager(); VariantCatalogQueryUtils catalogUtils = new VariantCatalogQueryUtils(catalogManager); if (familiesStr.size() == 1 && familiesStr.get(0).equals(VariantQueryUtils.ALL)) { @@ -423,7 +421,7 @@ public DataResult familyIndex(String study, List familiesStr, bool } DataResult dataResult = engine.familyIndex(study, trios, params); getSynchronizer(engine).synchronizeCatalogSamplesFromStorage(study, trios.stream() - .flatMap(t->t.toList().stream()) + .flatMap(t -> t.toList().stream()) .collect(Collectors.toList()), token); return dataResult; }); @@ -439,11 +437,29 @@ public DataResult familyIndexBySamples(String study, Collection sa throws CatalogException, StorageEngineException { return secureOperation(VariantFamilyIndexOperationTool.ID, study, params, token, engine -> { Collection thisSamples = samples; + boolean allSamples; if (CollectionUtils.size(thisSamples) == 1 && thisSamples.iterator().next().equals(ParamConstants.ALL)) { thisSamples = getIndexedSamples(study, token); + allSamples = true; + } else { + allSamples = false; } List trios = catalogUtils.getTriosFromSamples(study, engine.getMetadataManager(), thisSamples, token); + if (trios.isEmpty()) { + String msg; + if (thisSamples.size() > 6) { + msg = "No trios found for " + thisSamples.size() + " samples"; + } else { + msg = "No trios found for samples " + thisSamples; + } + if (allSamples) { + logger.info(msg); + return new DataResult<>(0, Collections.singletonList(new Event(Event.Type.INFO, msg)), 0, Collections.emptyList(), 0); + } else { + throw new StorageEngineException(msg); + } + } DataResult dataResult = engine.familyIndex(study, trios, params); getSynchronizer(engine).synchronizeCatalogSamplesFromStorage(study, trios.stream() .flatMap(t -> t.toList().stream()) @@ -628,6 +644,7 @@ public VariantQueryResult get(Query inputQuery, QueryOptions queryOptio @SuppressWarnings("unchecked") public VariantQueryResult get(Query query, QueryOptions queryOptions, String token, Class clazz) throws CatalogException, IOException, StorageEngineException { + VariantQueryResult result = get(query, queryOptions, token); List variants; if (clazz == Variant.class) { @@ -641,16 +658,7 @@ public VariantQueryResult get(Query query, QueryOptions queryOptions, Str } else { throw new IllegalArgumentException("Unknown variant format " + clazz); } - return new VariantQueryResult<>( - result.getTime(), - result.getNumResults(), - result.getNumMatches(), - result.getEvents(), - variants, - result.getSamples(), - result.getSource(), - result.getApproximateCount(), - result.getApproximateCountSamplingSize(), null); + return new VariantQueryResult<>(result, variants); } @@ -881,7 +889,7 @@ public DataResult getSampleData(String variant, String study, QueryOpti VariantQueryResult result = new VariantQueryResult<>( ((int) stopWatch.getTime(TimeUnit.MILLISECONDS)), - 1, 1, new ArrayList<>(), Collections.singletonList(variantResult), null, null) + 1, 1, new ArrayList<>(), Collections.singletonList(variantResult), engine.getStorageEngineId()) .setNumSamples(sampleEntries.size()); if (exactNumSamples) { result.setApproximateCount(false); diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/operations/VariantDeleteOperationManager.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/operations/VariantDeleteOperationManager.java index 550c00b027a..882240d2865 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/operations/VariantDeleteOperationManager.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/manager/operations/VariantDeleteOperationManager.java @@ -26,6 +26,7 @@ import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; +import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import java.net.URI; import java.util.ArrayList; @@ -52,6 +53,7 @@ public void removeStudy(String study, URI outdir, String token) throws CatalogEx public void removeFile(String study, List inputFiles, URI outdir, String token) throws CatalogException, StorageEngineException { // Update study metadata BEFORE executing the operation and fetching files from Catalog + boolean force = variantStorageEngine.getOptions().getBoolean(VariantStorageOptions.FORCE.key()); StudyMetadata studyMetadata = synchronizeCatalogStudyFromStorage(study, token, true); List fileNames = new ArrayList<>(); @@ -62,7 +64,15 @@ public void removeFile(String study, List inputFiles, URI outdir, String if (!catalogIndexStatus.equals(VariantIndexStatus.READY)) { // Might be partially loaded in VariantStorage. Check FileMetadata FileMetadata fileMetadata = variantStorageEngine.getMetadataManager().getFileMetadata(studyMetadata.getId(), fileStr); - if (fileMetadata == null || fileMetadata.getIndexStatus() != TaskMetadata.Status.NONE) { + boolean canBeRemoved; + if (force) { + // When forcing remove, just require the file to be registered in the storage + canBeRemoved = fileMetadata != null; + } else { + // Otherwise, require the file to be in status NONE + canBeRemoved = fileMetadata != null && fileMetadata.getIndexStatus() != TaskMetadata.Status.NONE; + } + if (!canBeRemoved) { throw new CatalogException("Unable to remove variants from file " + file.getName() + ". " + "IndexStatus = " + catalogIndexStatus); } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/mutationalSignature/MutationalSignatureLocalAnalysisExecutor.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/mutationalSignature/MutationalSignatureLocalAnalysisExecutor.java index 042784b844a..5be8325e066 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/mutationalSignature/MutationalSignatureLocalAnalysisExecutor.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/mutationalSignature/MutationalSignatureLocalAnalysisExecutor.java @@ -43,7 +43,7 @@ import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.models.variant.MutationalSignatureAnalysisParams; import org.opencb.opencga.core.response.OpenCGAResult; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.tools.annotations.ToolExecutor; import org.opencb.opencga.core.tools.variant.MutationalSignatureAnalysisExecutor; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileDeleteOperationTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileDeleteOperationTool.java index bb3cb4d3f3a..85b4f8b1b48 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileDeleteOperationTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileDeleteOperationTool.java @@ -20,7 +20,7 @@ import org.apache.solr.common.StringUtils; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.common.Enums; -import org.opencb.opencga.core.models.variant.VariantFileDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileDeleteParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; @@ -55,6 +55,7 @@ protected void check() throws Exception { throw new ToolException("Missing file/s"); } params.put(VariantStorageOptions.RESUME.key(), variantFileDeleteParams.isResume()); + params.put(VariantStorageOptions.FORCE.key(), variantFileDeleteParams.isForce()); } @Override diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileIndexJobLauncherTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileIndexJobLauncherTool.java index 1178c9a8081..3dd3f5584ea 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileIndexJobLauncherTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantFileIndexJobLauncherTool.java @@ -16,8 +16,8 @@ import org.opencb.opencga.core.models.file.FileInternal; import org.opencb.opencga.core.models.file.VariantIndexStatus; import org.opencb.opencga.core.models.job.Job; -import org.opencb.opencga.core.models.variant.VariantFileIndexJobLauncherParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileIndexJobLauncherParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantIndexOperationTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantIndexOperationTool.java index cce9fc91e8a..70a882d4d80 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantIndexOperationTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantIndexOperationTool.java @@ -17,13 +17,9 @@ package org.opencb.opencga.analysis.variant.operations; import io.jsonwebtoken.lang.Collections; -import org.opencb.commons.datastore.core.Query; -import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.catalog.db.api.FileDBAdaptor; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.common.Enums; -import org.opencb.opencga.core.models.variant.VariantIndexParams; -import org.opencb.opencga.core.response.OpenCGAResult; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; import org.opencb.opencga.storage.core.StoragePipelineResult; @@ -34,9 +30,7 @@ import java.net.URI; import java.nio.file.Files; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import static org.opencb.opencga.analysis.variant.manager.operations.VariantFileIndexerOperationManager.*; @@ -112,9 +106,6 @@ protected void check() throws Exception { protected List getSteps() { List steps = new ArrayList<>(); steps.add(getId()); - if (indexParams.isFamily()) { - steps.add("family-index"); - } return steps; } @@ -155,26 +146,5 @@ protected void run() throws Exception { } } }); - - if (indexParams.isFamily()) { - step("family-index", () -> { - if (inputFiles.isEmpty()) { - // Nothing to do! - return; - } - OpenCGAResult fileResult = getCatalogManager().getFileManager() - .search(study, - new Query(FileDBAdaptor.QueryParams.URI.key(), inputFiles), - new QueryOptions(QueryOptions.INCLUDE, FileDBAdaptor.QueryParams.SAMPLE_IDS.key()), getToken()); - - Set samples = new HashSet<>(); - for (org.opencb.opencga.core.models.file.File file : fileResult.getResults()) { - samples.addAll(file.getSampleIds()); - } - if (!samples.isEmpty()) { - variantStorageManager.familyIndexBySamples(study, samples, params, getToken()); - } - }); - } } } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantPruneOperationTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantPruneOperationTool.java index faaee5e681a..58838a2ea1e 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantPruneOperationTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantPruneOperationTool.java @@ -1,7 +1,7 @@ package org.opencb.opencga.analysis.variant.operations; import org.opencb.opencga.core.models.common.Enums; -import org.opencb.opencga.core.models.variant.VariantPruneParams; +import org.opencb.opencga.core.models.operations.variant.VariantPruneParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantSampleDeleteOperationTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantSampleDeleteOperationTool.java index 14c456cc592..067367a3f28 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantSampleDeleteOperationTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantSampleDeleteOperationTool.java @@ -20,7 +20,7 @@ import org.apache.solr.common.StringUtils; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.common.Enums; -import org.opencb.opencga.core.models.variant.VariantSampleDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantSampleDeleteParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataSynchronizeOperationTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataSynchronizeOperationTool.java index 6e49349369c..7b5962a0424 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataSynchronizeOperationTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataSynchronizeOperationTool.java @@ -1,7 +1,7 @@ package org.opencb.opencga.analysis.variant.operations; import org.opencb.opencga.core.models.common.Enums; -import org.opencb.opencga.core.models.variant.VariantStorageMetadataSynchronizeParams; +import org.opencb.opencga.core.models.operations.variant.VariantStorageMetadataSynchronizeParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStudyDeleteOperationTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStudyDeleteOperationTool.java index d24a21668e2..643c2c989d8 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStudyDeleteOperationTool.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStudyDeleteOperationTool.java @@ -19,7 +19,7 @@ import org.apache.solr.common.StringUtils; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.common.Enums; -import org.opencb.opencga.core.models.variant.VariantStudyDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantStudyDeleteParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/executors/DockerWrapperAnalysisExecutor.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/executors/DockerWrapperAnalysisExecutor.java index ae2b5072473..d68f363b36d 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/executors/DockerWrapperAnalysisExecutor.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/executors/DockerWrapperAnalysisExecutor.java @@ -31,6 +31,8 @@ public abstract class DockerWrapperAnalysisExecutor extends OpenCgaToolExecutor public static final String STDOUT_FILENAME = "stdout.txt"; public static final String STDERR_FILENAME = "stderr.txt"; + public static final String DOCKER_CLI_MSG = "Docker CLI: "; + public String getDockerImageName() { return "opencb/opencga-ext-tools"; } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysis.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysis.java index 3d8253861a4..c4a9d87d4f7 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysis.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysis.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils; import org.opencb.opencga.analysis.tools.OpenCgaToolScopeStudy; import org.opencb.opencga.core.exceptions.ToolException; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; import org.opencb.opencga.core.models.clinical.ExomiserWrapperParams; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.tools.annotations.Tool; @@ -52,6 +53,7 @@ protected void run() throws Exception { getToolExecutor(ExomiserWrapperAnalysisExecutor.class) .setStudyId(study) .setSampleId(analysisParams.getSample()) + .setClinicalAnalysisType(ClinicalAnalysis.Type.valueOf(analysisParams.getClinicalAnalysisType())) .execute(); }); } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysisExecutor.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysisExecutor.java index 86bb3760b3c..a6325932fb9 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysisExecutor.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/wrappers/exomiser/ExomiserWrapperAnalysisExecutor.java @@ -18,6 +18,7 @@ import org.opencb.opencga.catalog.managers.FamilyManager; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.exceptions.ToolExecutorException; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; import org.opencb.opencga.core.models.family.Family; import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.tools.annotations.ToolExecutor; @@ -50,6 +51,7 @@ public class ExomiserWrapperAnalysisExecutor extends DockerWrapperAnalysisExecut private String studyId; private String sampleId; + private ClinicalAnalysis.Type clinicalAnalysisType; private Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -100,22 +102,24 @@ public void run() throws ToolException { // Check multi-sample (family) analysis File pedigreeFile = null; Pedigree pedigree = null; - if (individual.getMother() != null && individual.getMother().getId() != null - && individual.getFather() != null && individual.getFather().getId() != null) { - Family family = IndividualQcUtils.getFamilyByIndividualId(getStudyId(), individual.getId(), - getVariantStorageManager().getCatalogManager(), getToken()); - if (family != null) { - pedigree = FamilyManager.getPedigreeFromFamily(family, individual.getId()); - } - - if (pedigree != null) { - if (individual.getFather() != null) { - samples.add(individual.getFather().getSamples().get(0).getId()); + if (clinicalAnalysisType == ClinicalAnalysis.Type.FAMILY) { + if (individual.getMother() != null && individual.getMother().getId() != null + && individual.getFather() != null && individual.getFather().getId() != null) { + Family family = IndividualQcUtils.getFamilyByIndividualId(getStudyId(), individual.getId(), + getVariantStorageManager().getCatalogManager(), getToken()); + if (family != null) { + pedigree = FamilyManager.getPedigreeFromFamily(family, individual.getId()); } - if (individual.getMother() != null) { - samples.add(individual.getMother().getSamples().get(0).getId()); + + if (pedigree != null) { + if (individual.getFather() != null) { + samples.add(individual.getFather().getSamples().get(0).getId()); + } + if (individual.getMother() != null) { + samples.add(individual.getMother().getSamples().get(0).getId()); + } + pedigreeFile = createPedigreeFile(family, pedigree); } - pedigreeFile = createPedigreeFile(family, pedigree); } } File sampleFile = createSampleFile(individual, hpos, pedigree); @@ -192,7 +196,9 @@ public void run() throws ToolException { .append(" --spring.config.location=/jobdir/").append(EXOMISER_PROPERTIES_TEMPLATE_FILENAME); // Execute command and redirect stdout and stderr to the files - logger.info("{}: Docker command line: {}", ID, sb); + String msg = DOCKER_CLI_MSG + sb; + logger.info(msg); + addWarning(msg); runCommandLine(sb.toString()); } @@ -483,4 +489,13 @@ public ExomiserWrapperAnalysisExecutor setSampleId(String sampleId) { this.sampleId = sampleId; return this; } + + public ClinicalAnalysis.Type getClinicalAnalysisType() { + return clinicalAnalysisType; + } + + public ExomiserWrapperAnalysisExecutor setClinicalAnalysisType(ClinicalAnalysis.Type clinicalAnalysisType) { + this.clinicalAnalysisType = clinicalAnalysisType; + return this; + } } diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/alignment/AlignmentAnalysisTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/alignment/AlignmentAnalysisTest.java index bdc744fc49c..14a5bbb63e7 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/alignment/AlignmentAnalysisTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/alignment/AlignmentAnalysisTest.java @@ -16,10 +16,7 @@ package org.opencb.opencga.analysis.alignment; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.*; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -37,8 +34,11 @@ import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.alignment.AlignmentGeneCoverageStatsParams; +import org.opencb.opencga.core.models.alignment.AlignmentIndexParams; +import org.opencb.opencga.core.models.alignment.CoverageIndexParams; import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.file.FileLinkParams; +import org.opencb.opencga.core.models.file.FileRelatedFile; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.models.user.User; @@ -49,9 +49,11 @@ import org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageTest; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Collections; import java.util.Map; import static org.junit.Assert.assertEquals; @@ -259,7 +261,6 @@ public void setUpCatalogManager() throws CatalogException { // assertEquals(2, individual.getSamples().size()); } - @Test public void geneCoverageStatsTest() throws IOException, ToolException, CatalogException { Path outdir = Paths.get(opencga.createTmpOutdir("_genecoveragestats")); @@ -277,7 +278,7 @@ public void geneCoverageStatsTest() throws IOException, ToolException, CatalogEx String geneName = "BRCA2"; params.setGenes(Arrays.asList(geneName)); - toolRunner.execute(AlignmentGeneCoverageStatsAnalysis.class, params, new ObjectMap(), outdir, null, token); + toolRunner.execute(AlignmentGeneCoverageStatsAnalysis.class, params, new ObjectMap(), outdir, "coverage-job-id", token); bamFile = catalogManager.getFileManager().link(STUDY, new FileLinkParams(bamFilename, "", "", "", null, null, null, null, null), false, token).first(); @@ -285,4 +286,150 @@ public void geneCoverageStatsTest() throws IOException, ToolException, CatalogEx assertEquals(geneName, bamFile.getQualityControl().getCoverage().getGeneCoverageStats().get(0).getGeneName()); assertEquals(10, bamFile.getQualityControl().getCoverage().getGeneCoverageStats().get(0).getStats().size()); } + + @Test + public void testNonReadOnlyAlignmentIndex() throws Exception { + Path nonReadOnlyDir = Paths.get(opencga.createTmpOutdir("_non_readonly_alignment_index")); + Path bamPath = Paths.get(opencga.getResourceUri("biofiles/HG00096.chrom20.small.bam").getPath()); + String bamFilename = "NonReadOnlyAligmentIndex_" + bamPath.getFileName(); + Files.copy(bamPath, nonReadOnlyDir.resolve(bamFilename)); + + File bamFile = catalogManager.getFileManager().link(STUDY, new FileLinkParams(nonReadOnlyDir.resolve(bamFilename).toAbsolutePath().toString(), "non_readonly_alignment_index", "", "", null, null, null, + null, null), true, token).first(); + + // Run alignment index + AlignmentIndexParams params = new AlignmentIndexParams(); + params.setFileId(bamFile.getId()); + Path alignmentIndexOutdir = Paths.get(opencga.createTmpOutdir("_alignment_index")); + toolRunner.execute(AlignmentIndexOperation.class, params, new ObjectMap(ParamConstants.STUDY_PARAM, STUDY), alignmentIndexOutdir, "jobId-non-readonly-coverage-index", token); + + // Checking BAI file + Path baiPath = nonReadOnlyDir.resolve(bamFilename + AlignmentConstants.BAI_EXTENSION); + Assert.assertTrue(Files.exists(baiPath)); + + // Checking BAI file is registered in the BAM file internals + File baiFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFilename + AlignmentConstants.BAI_EXTENSION), QueryOptions.empty(), true, token).first(); + bamFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFile.getId()), QueryOptions.empty(), true, token).first(); + Assert.assertEquals(baiFile.getId(), bamFile.getInternal().getAlignment().getIndex().getFileId()); + } + + @Test + public void testReadOnlyAlignmentIndex() throws Exception { + Path readOnlyDir = Paths.get(opencga.createTmpOutdir("_readonly_for_alignment_index")); + Path bamPath = Paths.get(opencga.getResourceUri("biofiles/HG00096.chrom20.small.bam").getPath()); + String bamFilename = "ReadOnlyAligmentIndex_" + bamPath.getFileName(); + Files.copy(bamPath, readOnlyDir.resolve(bamFilename)); + + // Make read-only + Runtime.getRuntime().exec("chmod 555 " + readOnlyDir.toAbsolutePath()); + + File bamFile = catalogManager.getFileManager().link(STUDY, new FileLinkParams(readOnlyDir.resolve(bamFilename).toAbsolutePath().toString(), "readonly_alignment_index", "", "", null, null, null, + null, null), true, token).first(); + + // Run alignment index + AlignmentIndexParams params = new AlignmentIndexParams(); + params.setFileId(bamFile.getId()); + Path alignmentIndexOutdir = Paths.get(opencga.createTmpOutdir("_alignment_index")); + toolRunner.execute(AlignmentIndexOperation.class, params, new ObjectMap(ParamConstants.STUDY_PARAM, STUDY), alignmentIndexOutdir, "jobId-readonly-coverage-index", token); + + // Checking BAI file + Path baiPath = alignmentIndexOutdir.resolve(bamFilename + AlignmentConstants.BAI_EXTENSION); + Assert.assertTrue(Files.exists(baiPath)); + + // Checking BAI file is registered in the BAM file internals + File baiFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFilename + AlignmentConstants.BAI_EXTENSION), QueryOptions.empty(), true, token).first(); + bamFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFile.getId()), QueryOptions.empty(), true, token).first(); + Assert.assertEquals(baiFile.getId(), bamFile.getInternal().getAlignment().getIndex().getFileId()); + + Runtime.getRuntime().exec("chmod 777 " + readOnlyDir.toAbsolutePath()); + } + + @Test + public void testNonReadOnlyCoverageIndex() throws Exception { + Path nonReadOnlyDir = Paths.get(opencga.createTmpOutdir("_non_readonly_for_coverage_index")); + Path bamPath = Paths.get(opencga.getResourceUri("biofiles/HG00096.chrom20.small.bam").getPath()); + String bamFilename = "NonReadOnlyCoverageIndex_" + bamPath.getFileName(); + Files.copy(bamPath, nonReadOnlyDir.resolve(bamFilename)); + + File bamFile = catalogManager.getFileManager().link(STUDY, new FileLinkParams(nonReadOnlyDir.resolve(bamFilename).toAbsolutePath().toString(), "non_readonly_alignment_coverage_index", "", "", null, null, null, + null, null), true, token).first(); + + // Run alignment index + AlignmentIndexParams indexParams = new AlignmentIndexParams(); + indexParams.setFileId(bamFile.getId()); + Path alignmentIndexOutdir = Paths.get(opencga.createTmpOutdir("_alignment_index")); + toolRunner.execute(AlignmentIndexOperation.class, indexParams, new ObjectMap(ParamConstants.STUDY_PARAM, STUDY), alignmentIndexOutdir, "jobId-non-readonly-alignment-coverage-index", token); + + // Checking BAI file + Path baiPath = nonReadOnlyDir.resolve(bamFilename + AlignmentConstants.BAI_EXTENSION); + Assert.assertTrue(Files.exists(baiPath)); + + // Checking BAI file is registered in the BAM file internals + File baiFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFilename + AlignmentConstants.BAI_EXTENSION), QueryOptions.empty(), true, token).first(); + bamFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFile.getId()), QueryOptions.empty(), true, token).first(); + Assert.assertEquals(baiFile.getId(), bamFile.getInternal().getAlignment().getIndex().getFileId()); + + // Run coverage index + CoverageIndexParams coverageOarams = new CoverageIndexParams(); + coverageOarams.setBamFileId(bamFile.getId()); + coverageOarams.setBaiFileId(baiFile.getId()); + Path coverageIndexOutdir = Paths.get(opencga.createTmpOutdir("_coverage_index")); + toolRunner.execute(AlignmentCoverageAnalysis.class, coverageOarams, new ObjectMap(ParamConstants.STUDY_PARAM, STUDY), coverageIndexOutdir, "jobId-readonly-coverage-index", token); + + // Checking BW file + Path bwPath = nonReadOnlyDir.resolve(bamFilename + AlignmentConstants.BIGWIG_EXTENSION); + Assert.assertTrue(Files.exists(bwPath)); + + // Checking BAM file is registered in the related files of BW file + File bwFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bwPath.getFileName().toString()), QueryOptions.empty(), true, token).first(); + Assert.assertEquals(bamFile.getId(), bwFile.getRelatedFiles().get(0).getFile().getId()); + Assert.assertEquals(FileRelatedFile.Relation.ALIGNMENT, bwFile.getRelatedFiles().get(0).getRelation()); + } + + @Test + public void testReadOnlyCoverageIndex() throws Exception { + Path readOnlyDir = Paths.get(opencga.createTmpOutdir("_readonly_for_coverage_index")); + Path bamPath = Paths.get(opencga.getResourceUri("biofiles/HG00096.chrom20.small.bam").getPath()); + String bamFilename = "ReadOnlyCoverageIndex_" + bamPath.getFileName(); + Files.copy(bamPath, readOnlyDir.resolve(bamFilename)); + + File bamFile = catalogManager.getFileManager().link(STUDY, new FileLinkParams(readOnlyDir.resolve(bamFilename).toAbsolutePath().toString(), "readonly_alignment_coverage_index", "", "", null, null, null, + null, null), true, token).first(); + + // Run alignment index + AlignmentIndexParams indexParams = new AlignmentIndexParams(); + indexParams.setFileId(bamFile.getId()); + Path alignmentIndexOutdir = Paths.get(opencga.createTmpOutdir("_alignment_index")); + toolRunner.execute(AlignmentIndexOperation.class, indexParams, new ObjectMap(ParamConstants.STUDY_PARAM, STUDY), alignmentIndexOutdir, "jobId-readonly-coverage-index", token); + + // Checking BAI file + Path baiPath = readOnlyDir.resolve(bamFilename + AlignmentConstants.BAI_EXTENSION); + Assert.assertTrue(Files.exists(baiPath)); + + // Checking BAI file is registered in the BAM file internals + File baiFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFilename + AlignmentConstants.BAI_EXTENSION), QueryOptions.empty(), true, token).first(); + bamFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bamFile.getId()), QueryOptions.empty(), true, token).first(); + Assert.assertEquals(baiFile.getId(), bamFile.getInternal().getAlignment().getIndex().getFileId()); + + // Make read-only + Runtime.getRuntime().exec("chmod 555 " + readOnlyDir.toAbsolutePath()); + + // Run coverage index + CoverageIndexParams coverageOarams = new CoverageIndexParams(); + coverageOarams.setBamFileId(bamFile.getId()); + coverageOarams.setBaiFileId(baiFile.getId()); + Path coverageIndexOutdir = Paths.get(opencga.createTmpOutdir("_coverage_index")); + toolRunner.execute(AlignmentCoverageAnalysis.class, coverageOarams, new ObjectMap(ParamConstants.STUDY_PARAM, STUDY), coverageIndexOutdir, "jobId-readonly-coverage-index", token); + + // Checking BW file + Path bwPath = coverageIndexOutdir.resolve(bamFilename + AlignmentConstants.BIGWIG_EXTENSION); + Assert.assertTrue(Files.exists(bwPath)); + + // Checking BAM file is registered in the related files of BW file + File bwFile = catalogManager.getFileManager().get(STUDY, Collections.singletonList(bwPath.getFileName().toString()), QueryOptions.empty(), true, token).first(); + Assert.assertEquals(bamFile.getId(), bwFile.getRelatedFiles().get(0).getFile().getId()); + Assert.assertEquals(FileRelatedFile.Relation.ALIGNMENT, bwFile.getRelatedFiles().get(0).getRelation()); + + Runtime.getRuntime().exec("chmod 777 " + readOnlyDir.toAbsolutePath()); + } } \ No newline at end of file diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisUtilsTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisUtilsTest.java index 26bf31c9dfc..f77a1025377 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisUtilsTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisUtilsTest.java @@ -76,6 +76,10 @@ public static AbstractClinicalManagerTest getClinicalTest(OpenCGATestExternalRes variantStorageManager.index(clinicalTest.studyFqn, "HG005.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); variantStorageManager.index(clinicalTest.studyFqn, "HG006.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); variantStorageManager.index(clinicalTest.studyFqn, "HG007.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); + variantStorageManager.index(clinicalTest.studyFqn, "HG104.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); + variantStorageManager.index(clinicalTest.studyFqn, "HG105.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); + variantStorageManager.index(clinicalTest.studyFqn, "HG106.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); + variantStorageManager.index(clinicalTest.studyFqn, "HG107.1k.vcf.gz", outDir.toString(), storageOptions, clinicalTest.token); return clinicalTest; } diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysisTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysisTest.java index 387b439571b..441f03d06a3 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysisTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/clinical/exomiser/ExomiserInterpretationAnalysisTest.java @@ -1,17 +1,18 @@ package org.opencb.opencga.analysis.clinical.exomiser; -import org.junit.After; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; +import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.exceptions.NonStandardCompliantSampleField; import org.opencb.biodata.tools.variant.VariantNormalizer; import org.junit.experimental.categories.Category; +import org.opencb.commons.datastore.core.Event; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.analysis.clinical.ClinicalAnalysisUtilsTest; import org.opencb.opencga.analysis.variant.OpenCGATestExternalResource; +import org.opencb.opencga.analysis.wrappers.executors.DockerWrapperAnalysisExecutor; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.managers.AbstractClinicalManagerTest; import org.opencb.opencga.core.exceptions.ToolException; @@ -24,8 +25,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.Collections; +import java.util.List; +import static com.mongodb.assertions.Assertions.assertFalse; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -47,8 +51,8 @@ public static void setUp() throws Exception { clinicalTest = ClinicalAnalysisUtilsTest.getClinicalTest(opencga); } - @After - public void tearDown() throws Exception { + @AfterClass + public static void tearDown() throws Exception { opencga.clear(); } @@ -68,7 +72,7 @@ public void testNormalization() throws NonStandardCompliantSampleField { } @Test - public void singleExomiserAnalysis() throws IOException, CatalogException, ToolException { + public void singleSingleExomiserAnalysis() throws IOException, CatalogException, ToolException { assumeThat(Paths.get("/opt/opencga/analysis/resources/exomiser").toFile().exists(), is(true)); prepareExomiserData(); @@ -92,14 +96,30 @@ public void singleExomiserAnalysis() throws IOException, CatalogException, ToolE .get(clinicalTest.studyFqn, clinicalTest.CA_ID2, QueryOptions.empty(), clinicalTest.token).first(); assertEquals(1, clinicalAnalysis.getSecondaryInterpretations().size()); assertTrue(clinicalAnalysis.getSecondaryInterpretations().get(0).getPrimaryFindings().size() > 0); + + // Check Exomiser docker CLI + boolean pedFound = false; + for (Event event : result.getEvents()) { + if (event.getType() == Event.Type.WARNING && StringUtils.isNotEmpty(event.getMessage()) + && event.getMessage().startsWith(DockerWrapperAnalysisExecutor.DOCKER_CLI_MSG)) { + List splits = Arrays.asList(event.getMessage().split(" ")); + pedFound = splits.contains("--ped") && splits.contains("/jobdir/" + clinicalTest.PROBAND_ID2 + ".ped"); + } + } + assertFalse(pedFound); + + // Only proband sample is returned in primary findings + for (ClinicalVariant cv : clinicalAnalysis.getInterpretation().getPrimaryFindings()) { + assertEquals(1, cv.getStudies().get(0).getSamples().size()); + } } @Test - public void familyExomiserAnalysis() throws IOException, CatalogException, ToolException { + public void trioFamilyExomiserAnalysis() throws IOException, CatalogException, ToolException { assumeThat(Paths.get("/opt/opencga/analysis/resources/exomiser").toFile().exists(), is(true)); prepareExomiserData(); - outDir = Paths.get(opencga.createTmpOutdir("_interpretation_analysis_family")); + outDir = Paths.get(opencga.createTmpOutdir("_interpretation_analysis_trio_family")); ClinicalAnalysis clinicalAnalysis = clinicalTest.catalogManager.getClinicalAnalysisManager() .get(clinicalTest.studyFqn, clinicalTest.CA_ID3, QueryOptions.empty(), clinicalTest.token).first(); @@ -118,6 +138,67 @@ public void familyExomiserAnalysis() throws IOException, CatalogException, ToolE .get(clinicalTest.studyFqn, clinicalTest.CA_ID3, QueryOptions.empty(), clinicalTest.token).first(); assertEquals(1, clinicalAnalysis.getSecondaryInterpretations().size()); assertTrue(clinicalAnalysis.getSecondaryInterpretations().get(0).getPrimaryFindings().size() > 0); + + // Check Exomiser docker CLI + boolean pedFound = false; + for (Event event : result.getEvents()) { + if (event.getType() == Event.Type.WARNING && StringUtils.isNotEmpty(event.getMessage()) + && event.getMessage().startsWith(DockerWrapperAnalysisExecutor.DOCKER_CLI_MSG)) { + List splits = Arrays.asList(event.getMessage().split(" ")); + pedFound = splits.contains("--ped") && splits.contains("/jobdir/" + clinicalTest.PROBAND_ID3 + ".ped"); + } + } + assertTrue(pedFound); + + // All family samples are returned in primary findings + for (ClinicalVariant cv : clinicalAnalysis.getInterpretation().getPrimaryFindings()) { + assertEquals(4, cv.getStudies().get(0).getSamples().size()); + } + + System.out.println("results at out dir = " + outDir.toAbsolutePath()); + } + + @Test + public void trioSingleExomiserAnalysis() throws IOException, CatalogException, ToolException { + assumeThat(Paths.get("/opt/opencga/analysis/resources/exomiser").toFile().exists(), is(true)); + + prepareExomiserData(); + outDir = Paths.get(opencga.createTmpOutdir("_interpretation_analysis_trio_single")); + + ClinicalAnalysis clinicalAnalysis = clinicalTest.catalogManager.getClinicalAnalysisManager() + .get(clinicalTest.studyFqn, clinicalTest.CA_ID4, QueryOptions.empty(), clinicalTest.token).first(); + assertEquals(0, clinicalAnalysis.getSecondaryInterpretations().size()); + + ExomiserInterpretationAnalysis exomiser = new ExomiserInterpretationAnalysis(); + + exomiser.setUp(opencga.getOpencgaHome().toAbsolutePath().toString(), new ObjectMap(), outDir, clinicalTest.token); + exomiser.setStudyId(clinicalTest.studyFqn) + .setClinicalAnalysisId(clinicalTest.CA_ID4); + + ExecutionResult result = exomiser.start(); + + // Refresh clinical analysis + clinicalAnalysis = clinicalTest.catalogManager.getClinicalAnalysisManager() + .get(clinicalTest.studyFqn, clinicalTest.CA_ID4, QueryOptions.empty(), clinicalTest.token).first(); + assertEquals(1, clinicalAnalysis.getSecondaryInterpretations().size()); + assertTrue(clinicalAnalysis.getSecondaryInterpretations().get(0).getPrimaryFindings().size() > 0); + + // Check Exomiser docker CLI + boolean pedFound = false; + for (Event event : result.getEvents()) { + if (event.getType() == Event.Type.WARNING && StringUtils.isNotEmpty(event.getMessage()) + && event.getMessage().startsWith(DockerWrapperAnalysisExecutor.DOCKER_CLI_MSG)) { + List splits = Arrays.asList(event.getMessage().split(" ")); + pedFound = splits.contains("--ped") && splits.contains("/jobdir/" + clinicalTest.PROBAND_ID4 + ".ped"); + } + } + assertFalse(pedFound); + + // Only proband sample is returned in primary findings + for (ClinicalVariant cv : clinicalAnalysis.getInterpretation().getPrimaryFindings()) { + assertEquals(1, cv.getStudies().get(0).getSamples().size()); + } + System.out.println("results at out dir = " + outDir.toAbsolutePath()); } diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java index 59d2ba43d5d..740040d19bc 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java @@ -74,6 +74,7 @@ import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.individual.IndividualInternal; import org.opencb.opencga.core.models.individual.Location; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.models.project.ProjectCreateParams; diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/VariantOperationsTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/VariantOperationsTest.java index d92fe79abfc..6dc54c598ec 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/VariantOperationsTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/VariantOperationsTest.java @@ -55,8 +55,8 @@ import org.opencb.opencga.core.models.project.ProjectCreateParams; import org.opencb.opencga.core.models.project.ProjectOrganism; import org.opencb.opencga.core.models.sample.*; -import org.opencb.opencga.core.models.variant.VariantIndexParams; -import org.opencb.opencga.core.models.variant.VariantStorageMetadataSynchronizeParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantStorageMetadataSynchronizeParams; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.testclassification.duration.LongTests; import org.opencb.opencga.core.tools.result.ExecutionResult; @@ -369,12 +369,25 @@ public void testVariantSecondarySampleIndex() throws Exception { assertEquals(sample, 1, sampleIndex.getVersion().intValue()); } + try { + toolRunner.execute(VariantSecondarySampleIndexOperationTool.class, STUDY, + new VariantSecondarySampleIndexParams() + .setFamilyIndex(true) + .setSample(Arrays.asList(mother)), + Paths.get(opencga.createTmpOutdir()), "index", token); + fail("Expected to fail"); + } catch (ToolException e) { + assertEquals("Exception from step 'familyIndex'", e.getMessage()); + assertEquals("No trios found for samples [" + mother + "]", e.getCause().getMessage()); + } + // Run family index. The family index status should be READY on offspring - toolRunner.execute(VariantSecondarySampleIndexOperationTool.class, STUDY, + ExecutionResult result = toolRunner.execute(VariantSecondarySampleIndexOperationTool.class, STUDY, new VariantSecondarySampleIndexParams() .setFamilyIndex(true) .setSample(Arrays.asList(ParamConstants.ALL)), Paths.get(opencga.createTmpOutdir()), "index", token); + assertEquals(0, result.getEvents().size()); for (String sample : samples) { SampleInternalVariantSecondarySampleIndex sampleIndex = catalogManager.getSampleManager().get(STUDY, sample, new QueryOptions(), token).first().getInternal().getVariant().getSecondarySampleIndex(); diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/PlatinumFileIndexerTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/PlatinumFileIndexerTest.java index 4396a3d6e99..f62d332470b 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/PlatinumFileIndexerTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/PlatinumFileIndexerTest.java @@ -33,8 +33,8 @@ import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.job.Job; -import org.opencb.opencga.core.models.variant.VariantFileIndexJobLauncherParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileIndexJobLauncherParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.testclassification.duration.MediumTests; import org.opencb.opencga.core.tools.result.ExecutionResult; import org.opencb.opencga.storage.core.StorageEngineFactory; @@ -107,9 +107,8 @@ public void testIndexFamily() throws Exception { ExecutionResult er = toolRunner.execute(VariantIndexOperationTool.class, params.toObjectMap() .append(ParamConstants.STUDY_PARAM, studyId), outDir, null, sessionId); - assertEquals(2, er.getSteps().size()); + assertEquals(1, er.getSteps().size()); assertEquals("variant-index", er.getSteps().get(0).getId()); - assertEquals("family-index", er.getSteps().get(1).getId()); variantManager.iterator(new Query(VariantQueryParam.STUDY.key(), studyId), new QueryOptions(), sessionId).forEachRemaining(variant -> { System.out.println("variant = " + variant); diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantFileIndexerOperationManagerTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantFileIndexerOperationManagerTest.java index 124e03ff1b9..e47380db655 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantFileIndexerOperationManagerTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantFileIndexerOperationManagerTest.java @@ -26,7 +26,6 @@ import org.mockito.Mockito; import org.opencb.biodata.models.variant.metadata.Aggregation; import org.opencb.biodata.models.variant.metadata.VariantSetStats; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.Event; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; @@ -43,8 +42,8 @@ import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.file.FileInternalVariantIndex; import org.opencb.opencga.core.models.file.VariantIndexStatus; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.models.study.Study; -import org.opencb.opencga.core.models.variant.VariantIndexParams; import org.opencb.opencga.core.testclassification.duration.MediumTests; import org.opencb.opencga.core.tools.result.ExecutionResult; import org.opencb.opencga.storage.core.StorageEngineFactory; @@ -186,7 +185,7 @@ public void testDeleteIndexedFile() throws Exception { Study study = catalogManager.getFileManager().getStudy(ORGANIZATION, inputFile, sessionId); thrown.expect(CatalogException.class); - thrown.expectMessage("The status is READY"); + thrown.expectMessage("Could not unlink file '" + inputFile.getId() + "'"); catalogManager.getFileManager().unlink(study.getFqn(), inputFile.getId(), sessionId); } @@ -200,7 +199,7 @@ public void testDeleteSampleFromIndexedFile() throws Exception { Query query = new Query(SampleDBAdaptor.QueryParams.ID.key(), inputFile.getSampleIds().get(100)); thrown.expect(CatalogException.class); thrown.expectMessage("Sample associated to the files"); - DataResult delete = catalogManager.getSampleManager().delete(studyFqn, query, null, sessionId); + catalogManager.getSampleManager().delete(studyFqn, query, null, sessionId); } @Test diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantVirtualFileIndexTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantVirtualFileIndexTest.java index 594e86af3dd..efa7e27fad7 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantVirtualFileIndexTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/manager/operations/VariantVirtualFileIndexTest.java @@ -26,7 +26,7 @@ import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.file.FileLinkParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.testclassification.duration.MediumTests; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/AlignmentCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/AlignmentCommandExecutor.java index 07ed1b36c1f..436fff7b061 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/AlignmentCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/AlignmentCommandExecutor.java @@ -18,6 +18,7 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.opencga.analysis.alignment.AlignmentCoverageAnalysis; +import org.opencb.opencga.analysis.alignment.AlignmentIndexOperation; import org.opencb.opencga.analysis.alignment.AlignmentStorageManager; import org.opencb.opencga.analysis.alignment.qc.AlignmentGeneCoverageStatsAnalysis; import org.opencb.opencga.analysis.alignment.qc.AlignmentQcAnalysis; @@ -119,9 +120,12 @@ public void execute() throws Exception { private void indexRun() throws Exception { AlignmentCommandOptions.IndexAlignmentCommandOptions cliOptions = alignmentCommandOptions.indexAlignmentCommandOptions; - AlignmentStorageManager alignmentManager = new AlignmentStorageManager(catalogManager, storageEngineFactory, alignmentCommandOptions.internalJobOptions.jobId); + ObjectMap params = new AlignmentIndexParams( + cliOptions.fileId, + cliOptions.overwrite + ).toObjectMap(cliOptions.commonOptions.params).append(ParamConstants.STUDY_PARAM, cliOptions.study); - alignmentManager.index(cliOptions.study, cliOptions.file, cliOptions.outdir, cliOptions.commonOptions.token); + toolRunner.execute(AlignmentIndexOperation.class, params, Paths.get(cliOptions.outdir), jobId, token); } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/VariantInternalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/VariantInternalCommandExecutor.java index e502850d9ee..b39a1387329 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/VariantInternalCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/VariantInternalCommandExecutor.java @@ -62,6 +62,7 @@ import org.opencb.opencga.core.common.YesNoAuto; import org.opencb.opencga.core.exceptions.AnalysisExecutionException; import org.opencb.opencga.core.exceptions.ToolException; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; import org.opencb.opencga.core.models.clinical.ExomiserWrapperParams; import org.opencb.opencga.core.models.common.mixins.GenericRecordAvroJsonMixin; import org.opencb.opencga.core.models.operations.variant.*; @@ -1017,6 +1018,7 @@ private void exomiser() throws Exception { ObjectMap params = new ExomiserWrapperParams( cliOptions.sample, + cliOptions.clinicalAnalysisType, cliOptions.outdir) .toObjectMap(cliOptions.commonOptions.params).append(ParamConstants.STUDY_PARAM, cliOptions.study); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/AlignmentCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/AlignmentCommandOptions.java index 99f09597f4b..c9d92e51d4f 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/AlignmentCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/AlignmentCommandOptions.java @@ -104,8 +104,11 @@ public class IndexAlignmentCommandOptions extends GeneralCliOptions.StudyOption @ParametersDelegate public Object internalJobOptions = internalJobOptionsObject; - @Parameter(names = {"--file"}, description = FILE_ID_DESCRIPTION, required = true, arity = 1) - public String file; + @Parameter(names = {"--file-id"}, description = FILE_ID_DESCRIPTION, required = true, arity = 1) + public String fileId; + + @Parameter(names = {"--overwrite"}, description = "Force to overwrite the alignment index file", arity = 0) + public boolean overwrite; @Parameter(names = {"-o", "--outdir"}, description = OUTPUT_DIRECTORY_DESCRIPTION) public String outdir; diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/VariantCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/VariantCommandOptions.java index bad1beff087..aa25fe630e1 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/VariantCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/VariantCommandOptions.java @@ -48,6 +48,7 @@ import org.opencb.opencga.app.cli.internal.InternalCliOptionsParser; import org.opencb.opencga.core.api.FieldConstants; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; import org.opencb.opencga.core.models.variant.AnnotationVariantQueryParams; import org.opencb.opencga.core.models.variant.SampleVariantFilterParams; import org.opencb.opencga.core.tools.variant.IndividualQcAnalysisExecutor; @@ -1841,10 +1842,13 @@ public class ExomiserAnalysisCommandOptions { @Parameter(names = {"--study"}, description = "Study where all the samples belong to.") public String study; - @Parameter(names = {"--sample"}, description = "Sample ID.", required = true) + @Parameter(names = {"--sample"}, description = FieldConstants.SAMPLE_ID_DESCRIPTION, required = true) public String sample; - @Parameter(names = {"-o", "--outdir"}, description = "Output directory.") + @Parameter(names = {"--clinical-analysis-type"}, description = FieldConstants.EXOMISER_CLINICAL_ANALYSIS_TYPE_DESCRIPTION) + public String clinicalAnalysisType = ClinicalAnalysis.Type.SINGLE.name(); + + @Parameter(names = {"-o", "--outdir"}, description = FieldConstants.JOB_OUT_DIR_DESCRIPTION) public String outdir; } } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java index d6bd902fff1..d4ff662a7c3 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java @@ -89,7 +89,7 @@ public abstract class OpenCgaCompleter implements Completer { .map(Candidate::new) .collect(toList()); - private List organizationsList = asList( "create","notes-create","notes-search","notes-delete","notes-update","info","update") + private List organizationsList = asList( "create","notes-create","notes-search","notes-delete","notes-update","configuration-update","info","update") .stream() .map(Candidate::new) .collect(toList()); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java index 33d53de3bf8..e0b9f455343 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java @@ -272,6 +272,7 @@ public OpencgaCliOptionsParser() { organizationsSubCommands.addCommand("notes-search", organizationsCommandOptions.searchNotesCommandOptions); organizationsSubCommands.addCommand("notes-delete", organizationsCommandOptions.deleteNotesCommandOptions); organizationsSubCommands.addCommand("notes-update", organizationsCommandOptions.updateNotesCommandOptions); + organizationsSubCommands.addCommand("configuration-update", organizationsCommandOptions.updateConfigurationCommandOptions); organizationsSubCommands.addCommand("info", organizationsCommandOptions.infoCommandOptions); organizationsSubCommands.addCommand("update", organizationsCommandOptions.updateCommandOptions); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisAlignmentCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisAlignmentCommandExecutor.java index 217b7421f32..88c7345e9da 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisAlignmentCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisAlignmentCommandExecutor.java @@ -387,7 +387,7 @@ private RestResponse runIndex() throws Exception { .readValue(new java.io.File(commandOptions.jsonFile), AlignmentIndexParams.class); } else { ObjectMap beanParams = new ObjectMap(); - putNestedIfNotEmpty(beanParams, "file",commandOptions.file, true); + putNestedIfNotEmpty(beanParams, "fileId",commandOptions.fileId, true); putNestedIfNotNull(beanParams, "overwrite",commandOptions.overwrite, true); alignmentIndexParams = JacksonUtils.getDefaultObjectMapper().copy() diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisVariantCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisVariantCommandExecutor.java index 33dfbc54a41..de60333d151 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisVariantCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisVariantCommandExecutor.java @@ -28,6 +28,7 @@ import org.opencb.opencga.core.models.analysis.knockout.KnockoutByIndividual; import org.opencb.opencga.core.models.clinical.ExomiserWrapperParams; import org.opencb.opencga.core.models.job.Job; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsExportParams; import org.opencb.opencga.core.models.variant.AnnotationVariantQueryParams; import org.opencb.opencga.core.models.variant.CircosAnalysisParams; @@ -50,7 +51,6 @@ import org.opencb.opencga.core.models.variant.SampleVariantFilterParams; import org.opencb.opencga.core.models.variant.SampleVariantStatsAnalysisParams; import org.opencb.opencga.core.models.variant.VariantExportParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; import org.opencb.opencga.core.models.variant.VariantStatsAnalysisParams; import org.opencb.opencga.core.response.QueryType; import org.opencb.opencga.core.response.RestResponse; @@ -427,6 +427,7 @@ private RestResponse runExomiser() throws Exception { } else { ObjectMap beanParams = new ObjectMap(); putNestedIfNotEmpty(beanParams, "sample",commandOptions.sample, true); + putNestedIfNotEmpty(beanParams, "clinicalAnalysisType",commandOptions.clinicalAnalysisType, true); putNestedIfNotEmpty(beanParams, "outdir",commandOptions.outdir, true); exomiserWrapperParams = JacksonUtils.getDefaultObjectMapper().copy() diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OperationsVariantStorageCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OperationsVariantStorageCommandExecutor.java index 068b4a3792a..f37a119b904 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OperationsVariantStorageCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OperationsVariantStorageCommandExecutor.java @@ -22,21 +22,21 @@ import org.opencb.opencga.core.models.operations.variant.VariantAggregateParams; import org.opencb.opencga.core.models.operations.variant.VariantAnnotationIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantAnnotationSaveParams; +import org.opencb.opencga.core.models.operations.variant.VariantConfigureParams; import org.opencb.opencga.core.models.operations.variant.VariantFamilyIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileIndexJobLauncherParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantPruneParams; +import org.opencb.opencga.core.models.operations.variant.VariantSampleDeleteParams; import org.opencb.opencga.core.models.operations.variant.VariantScoreIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantSecondaryAnnotationIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantSecondarySampleIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsDeleteParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStorageMetadataRepairToolParams; -import org.opencb.opencga.core.models.variant.VariantConfigureParams; -import org.opencb.opencga.core.models.variant.VariantFileDeleteParams; -import org.opencb.opencga.core.models.variant.VariantFileIndexJobLauncherParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; -import org.opencb.opencga.core.models.variant.VariantPruneParams; -import org.opencb.opencga.core.models.variant.VariantSampleDeleteParams; -import org.opencb.opencga.core.models.variant.VariantStorageMetadataSynchronizeParams; -import org.opencb.opencga.core.models.variant.VariantStudyDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantStorageMetadataSynchronizeParams; +import org.opencb.opencga.core.models.operations.variant.VariantStudyDeleteParams; import org.opencb.opencga.core.response.QueryType; import org.opencb.opencga.core.response.RestResponse; @@ -394,6 +394,7 @@ private RestResponse deleteVariant() throws Exception { ObjectMap beanParams = new ObjectMap(); putNestedIfNotNull(beanParams, "file",commandOptions.file, true); putNestedIfNotNull(beanParams, "resume",commandOptions.resume, true); + putNestedIfNotNull(beanParams, "force",commandOptions.force, true); variantFileDeleteParams = JacksonUtils.getDefaultObjectMapper().copy() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java index 991f44fd9d8..a74a27358a5 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java @@ -12,6 +12,7 @@ import org.opencb.opencga.app.cli.main.options.OrganizationsCommandOptions; import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException; import org.opencb.opencga.catalog.utils.ParamUtils.AddRemoveAction; +import org.opencb.opencga.catalog.utils.ParamUtils.UpdateAction; import org.opencb.opencga.client.exceptions.ClientException; import org.opencb.opencga.core.common.JacksonUtils; import org.opencb.opencga.core.config.Optimizations; @@ -75,6 +76,9 @@ public void execute() throws Exception { case "notes-update": queryResponse = updateNotes(); break; + case "configuration-update": + queryResponse = updateConfiguration(); + break; case "info": queryResponse = info(); break; @@ -223,6 +227,41 @@ private RestResponse updateNotes() throws Exception { return openCGAClient.getOrganizationClient().updateNotes(commandOptions.id, noteUpdateParams, queryParams); } + private RestResponse updateConfiguration() throws Exception { + logger.debug("Executing updateConfiguration in Organizations command line"); + + OrganizationsCommandOptions.UpdateConfigurationCommandOptions commandOptions = organizationsCommandOptions.updateConfigurationCommandOptions; + + ObjectMap queryParams = new ObjectMap(); + queryParams.putIfNotEmpty("include", commandOptions.include); + queryParams.putIfNotEmpty("exclude", commandOptions.exclude); + queryParams.putIfNotNull("includeResult", commandOptions.includeResult); + queryParams.putIfNotNull("authenticationOriginsAction", commandOptions.authenticationOriginsAction); + + + OrganizationConfiguration organizationConfiguration = null; + if (commandOptions.jsonDataModel) { + RestResponse res = new RestResponse<>(); + res.setType(QueryType.VOID); + PrintUtils.println(getObjectAsJSON(categoryName,"/{apiVersion}/organizations/{organization}/configuration/update")); + return res; + } else if (commandOptions.jsonFile != null) { + organizationConfiguration = JacksonUtils.getDefaultObjectMapper() + .readValue(new java.io.File(commandOptions.jsonFile), OrganizationConfiguration.class); + } else { + ObjectMap beanParams = new ObjectMap(); + putNestedIfNotNull(beanParams, "optimizations.simplifyPermissions",commandOptions.optimizationsSimplifyPermissions, true); + putNestedIfNotEmpty(beanParams, "token.algorithm",commandOptions.tokenAlgorithm, true); + putNestedIfNotEmpty(beanParams, "token.secretKey",commandOptions.tokenSecretKey, true); + putNestedIfNotNull(beanParams, "token.expiration",commandOptions.tokenExpiration, true); + + organizationConfiguration = JacksonUtils.getDefaultObjectMapper().copy() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .readValue(beanParams.toJson(), OrganizationConfiguration.class); + } + return openCGAClient.getOrganizationClient().updateConfiguration(commandOptions.organization, organizationConfiguration, queryParams); + } + private RestResponse info() throws Exception { logger.debug("Executing info in Organizations command line"); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/io/VcfOutputWriter.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/io/VcfOutputWriter.java index 6093c975a32..0c398dd2ef6 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/io/VcfOutputWriter.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/io/VcfOutputWriter.java @@ -9,7 +9,7 @@ import org.opencb.biodata.models.variant.metadata.VariantStudyMetadata; import org.opencb.biodata.models.variant.protobuf.VariantProto; import org.opencb.opencga.core.response.RestResponse; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.io.VcfDataWriter; import java.io.PrintStream; diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisAlignmentCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisAlignmentCommandOptions.java index 46abae24683..f43709391f7 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisAlignmentCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisAlignmentCommandOptions.java @@ -142,14 +142,14 @@ public class RunCoverageIndexCommandOptions { @Parameter(names = {"--job-tags"}, description = "Job tags", required = false, arity = 1) public String jobTags; - @Parameter(names = {"--bam-file-id"}, description = "The body web service bamFileId parameter", required = false, arity = 1) + @Parameter(names = {"--bam-file-id"}, description = "BAM file ID.", required = false, arity = 1) public String bamFileId; - @Parameter(names = {"--bai-file-id"}, description = "The body web service baiFileId parameter", required = false, arity = 1) + @Parameter(names = {"--bai-file-id"}, description = "BAI file ID.", required = false, arity = 1) public String baiFileId; - @Parameter(names = {"--window-size"}, description = "The body web service windowSize parameter", required = false, arity = 1) - public Integer windowSize; + @Parameter(names = {"--window-size"}, description = "Window size (i.e., the size of the bins, in bases, for the output of the BIGWIG file).", required = false, arity = 1) + public Integer windowSize = 50; } @@ -387,10 +387,10 @@ public class RunIndexCommandOptions { @Parameter(names = {"--job-tags"}, description = "Job tags", required = false, arity = 1) public String jobTags; - @Parameter(names = {"--file"}, description = "The body web service file parameter", required = false, arity = 1) - public String file; + @Parameter(names = {"--file-id"}, description = "File ID, (i.e., BAM/CRAM file ID).", required = false, arity = 1) + public String fileId; - @Parameter(names = {"--overwrite"}, description = "The body web service overwrite parameter", required = false, help = true, arity = 0) + @Parameter(names = {"--overwrite"}, description = "Flag to force indexing.", required = false, help = true, arity = 0) public boolean overwrite = false; } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java index fcfe1679c19..ac3fab3eba6 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java @@ -1721,7 +1721,7 @@ public class QueryVariantCommandOptions { @Parameter(names = {"--include-interpretation"}, description = "Interpretation ID to include the fields related to this interpretation", required = false, arity = 1) public String includeInterpretation; - @Parameter(names = {"--id"}, description = "List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T", required = false, arity = 1) + @Parameter(names = {"--id"}, description = "List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T", required = false, arity = 1) public String id; @Parameter(names = {"--region"}, description = "List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000", required = false, arity = 1) @@ -1802,7 +1802,7 @@ public class QueryVariantCommandOptions { @Parameter(names = {"--ct"}, description = "List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' and 'protein_altering'", required = false, arity = 1) public String ct; - @Parameter(names = {"--xref"}, description = "List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ...", required = false, arity = 1) + @Parameter(names = {"--xref"}, description = "List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ...", required = false, arity = 1) public String xref; @Parameter(names = {"--biotype"}, description = "List of biotypes, e.g. protein_coding", required = false, arity = 1) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisVariantCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisVariantCommandOptions.java index 82b9edc17a4..eaaff6d1e6f 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisVariantCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisVariantCommandOptions.java @@ -177,7 +177,7 @@ public class AggregationStatsCommandOptions { @Parameter(names = {"--ct"}, description = "List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' and 'protein_altering'", required = false, arity = 1) public String ct; - @Parameter(names = {"--xref"}, description = "List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ...", required = false, arity = 1) + @Parameter(names = {"--xref"}, description = "List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ...", required = false, arity = 1) public String xref; @Parameter(names = {"--biotype"}, description = "List of biotypes, e.g. protein_coding", required = false, arity = 1) @@ -259,7 +259,7 @@ public class QueryAnnotationCommandOptions { @ParametersDelegate public CommonCommandOptions commonOptions = commonCommandOptions; - @Parameter(names = {"--id"}, description = "List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T", required = false, arity = 1) + @Parameter(names = {"--id"}, description = "List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T", required = false, arity = 1) public String id; @Parameter(names = {"--region"}, description = "List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000", required = false, arity = 1) @@ -410,10 +410,13 @@ public class RunExomiserCommandOptions { @Parameter(names = {"--job-tags"}, description = "Job tags", required = false, arity = 1) public String jobTags; - @Parameter(names = {"--sample"}, description = "The body web service sample parameter", required = false, arity = 1) + @Parameter(names = {"--sample"}, description = "Sample ID.", required = false, arity = 1) public String sample; - @Parameter(names = {"--outdir"}, description = "The body web service outdir parameter", required = false, arity = 1) + @Parameter(names = {"--clinical-analysis-type"}, description = "Clinical analysis type: SINGLE or FAMILY.", required = false, arity = 1) + public String clinicalAnalysisType = "SINGLE"; + + @Parameter(names = {"--outdir"}, description = "Output dir for the job.", required = false, arity = 1) public String outdir; } @@ -993,7 +996,7 @@ public class RunHrDetectCommandOptions { @Parameter(names = {"--description"}, description = "Decription for these particular HRDetect results.", required = false, arity = 1) public String description; - @Parameter(names = {"--sample-id"}, description = "Sample data model hosts information about any biological material, normally extracted from an _Individual_, that is used for a particular analysis. This is the main data model, it stores the most basic and important information.", required = false, arity = 1) + @Parameter(names = {"--sample-id"}, description = "Sample ID.", required = false, arity = 1) public String sampleId; @Parameter(names = {"--snv-fitting-id"}, description = "Mutational signature fitting ID for SNV.", required = false, arity = 1) @@ -1079,7 +1082,7 @@ public class RunIndexCommandOptions { @Parameter(names = {"--fail-on-malformed-lines"}, description = "Fail when encountering malformed lines. (yes, no, auto) [auto]", required = false, arity = 1) public String failOnMalformedLines; - @Parameter(names = {"--family"}, description = "Indicate that the files to be loaded are part of a family. This will set 'load-hom-ref' to YES if it was in AUTO and execute 'family-index' afterwards", required = false, help = true, arity = 0) + @Parameter(names = {"--family"}, description = "Indicate that the files to be loaded are part of a family. This will set 'load-hom-ref' to YES if it was in AUTO", required = false, help = true, arity = 0) public boolean family = false; @Parameter(names = {"--somatic"}, description = "Indicate that the files to be loaded contain somatic samples. This will set 'load-hom-ref' to YES if it was in AUTO.", required = false, help = true, arity = 0) @@ -1611,7 +1614,7 @@ public class QueryCommandOptions { @Parameter(names = {"--saved-filter"}, description = "Use a saved filter at User level", required = false, arity = 1) public String savedFilter; - @Parameter(names = {"--id"}, description = "List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T", required = false, arity = 1) + @Parameter(names = {"--id"}, description = "List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T", required = false, arity = 1) public String id; @Parameter(names = {"--region"}, description = "List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000", required = false, arity = 1) @@ -1737,7 +1740,7 @@ public class QueryCommandOptions { @Parameter(names = {"--ct"}, description = "List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' and 'protein_altering'", required = false, arity = 1) public String ct; - @Parameter(names = {"--xref"}, description = "List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ...", required = false, arity = 1) + @Parameter(names = {"--xref"}, description = "List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ...", required = false, arity = 1) public String xref; @Parameter(names = {"--biotype"}, description = "List of biotypes, e.g. protein_coding", required = false, arity = 1) @@ -2035,7 +2038,7 @@ public class RunSampleQcCommandOptions { @Parameter(names = {"--job-tags"}, description = "Job tags", required = false, arity = 1) public String jobTags; - @Parameter(names = {"--sample"}, description = "Sample data model hosts information about any biological material, normally extracted from an _Individual_, that is used for a particular analysis. This is the main data model, it stores the most basic and important information.", required = false, arity = 1) + @Parameter(names = {"--sample"}, description = "Sample ID.", required = false, arity = 1) public String sample; @Parameter(names = {"--vs-id"}, description = "Variant stats ID.", required = false, arity = 1) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OperationsVariantStorageCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OperationsVariantStorageCommandOptions.java index babd0e312f2..1edc7179439 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OperationsVariantStorageCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OperationsVariantStorageCommandOptions.java @@ -160,10 +160,10 @@ public class AggregateVariantCommandOptions { @Parameter(names = {"--study", "-s"}, description = "Study [[organization@]project:]study where study and project can be either the ID or UUID", required = false, arity = 1) public String study; - @Parameter(names = {"--overwrite"}, description = "The body web service overwrite parameter", required = false, help = true, arity = 0) + @Parameter(names = {"--overwrite"}, description = "Overwrite aggregation for all files and variants. Repeat operation for already processed variants.", required = false, help = true, arity = 0) public boolean overwrite = false; - @Parameter(names = {"--resume"}, description = "The body web service resume parameter", required = false, help = true, arity = 0) + @Parameter(names = {"--resume"}, description = "Resume a previously failed index operation", required = false, help = true, arity = 0) public boolean resume = false; } @@ -280,7 +280,7 @@ public class SaveVariantAnnotationCommandOptions { @Parameter(names = {"--project", "-p"}, description = "Project [organization@]project where project can be either the ID or the alias", required = false, arity = 1) public String project; - @Parameter(names = {"--annotation-id"}, description = "The body web service annotationId parameter", required = false, arity = 1) + @Parameter(names = {"--annotation-id"}, description = "New Variant Annotation identifier", required = false, arity = 1) public String annotationId; } @@ -341,6 +341,9 @@ public class DeleteVariantCommandOptions { @Parameter(names = {"--resume"}, description = "The body web service resume parameter", required = false, help = true, arity = 0) public boolean resume = false; + @Parameter(names = {"--force"}, description = "The body web service force parameter", required = false, help = true, arity = 0) + public boolean force = false; + } @Parameters(commandNames = {"variant-family-aggregate"}, commandDescription ="Find variants where not all the samples are present, and fill the empty values.") @@ -370,13 +373,13 @@ public class AggregateVariantFamilyCommandOptions { @Parameter(names = {"--study", "-s"}, description = "Study [[organization@]project:]study where study and project can be either the ID or UUID", required = false, arity = 1) public String study; - @Parameter(names = {"--samples"}, description = "The body web service samples parameter", required = false, arity = 1) + @Parameter(names = {"--samples"}, description = "Samples within the same study to aggregate", required = false, arity = 1) public String samples; - @Parameter(names = {"--gaps-genotype"}, description = "The body web service gapsGenotype parameter", required = false, arity = 1) + @Parameter(names = {"--gaps-genotype"}, description = "Genotype to be used in gaps. Either 0/0, ./. or ?/?", required = false, arity = 1) public String gapsGenotype; - @Parameter(names = {"--resume"}, description = "The body web service resume parameter", required = false, help = true, arity = 0) + @Parameter(names = {"--resume"}, description = "Resume a previously failed index operation", required = false, help = true, arity = 0) public boolean resume = false; } @@ -473,7 +476,7 @@ public class IndexVariantCommandOptions { @Parameter(names = {"--fail-on-malformed-lines"}, description = "Fail when encountering malformed lines. (yes, no, auto) [auto]", required = false, arity = 1) public String failOnMalformedLines; - @Parameter(names = {"--family"}, description = "Indicate that the files to be loaded are part of a family. This will set 'load-hom-ref' to YES if it was in AUTO and execute 'family-index' afterwards", required = false, help = true, arity = 0) + @Parameter(names = {"--family"}, description = "Indicate that the files to be loaded are part of a family. This will set 'load-hom-ref' to YES if it was in AUTO", required = false, help = true, arity = 0) public boolean family = false; @Parameter(names = {"--somatic"}, description = "Indicate that the files to be loaded contain somatic samples. This will set 'load-hom-ref' to YES if it was in AUTO.", required = false, help = true, arity = 0) @@ -607,7 +610,7 @@ public class LauncherVariantIndexCommandOptions { @Parameter(names = {"--index-params-fail-on-malformed-lines"}, description = "Fail when encountering malformed lines. (yes, no, auto) [auto]", required = false, arity = 1) public String indexParamsFailOnMalformedLines; - @Parameter(names = {"--index-params-family"}, description = "Indicate that the files to be loaded are part of a family. This will set 'load-hom-ref' to YES if it was in AUTO and execute 'family-index' afterwards", required = false, help = true, arity = 0) + @Parameter(names = {"--index-params-family"}, description = "Indicate that the files to be loaded are part of a family. This will set 'load-hom-ref' to YES if it was in AUTO", required = false, help = true, arity = 0) public boolean indexParamsFamily = false; @Parameter(names = {"--index-params-somatic"}, description = "Indicate that the files to be loaded contain somatic samples. This will set 'load-hom-ref' to YES if it was in AUTO.", required = false, help = true, arity = 0) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java index c2ab1e89a90..08120624c87 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java @@ -38,6 +38,7 @@ public class OrganizationsCommandOptions { public SearchNotesCommandOptions searchNotesCommandOptions; public DeleteNotesCommandOptions deleteNotesCommandOptions; public UpdateNotesCommandOptions updateNotesCommandOptions; + public UpdateConfigurationCommandOptions updateConfigurationCommandOptions; public InfoCommandOptions infoCommandOptions; public UpdateCommandOptions updateCommandOptions; @@ -51,6 +52,7 @@ public OrganizationsCommandOptions(CommonCommandOptions commonCommandOptions, JC this.searchNotesCommandOptions = new SearchNotesCommandOptions(); this.deleteNotesCommandOptions = new DeleteNotesCommandOptions(); this.updateNotesCommandOptions = new UpdateNotesCommandOptions(); + this.updateConfigurationCommandOptions = new UpdateConfigurationCommandOptions(); this.infoCommandOptions = new InfoCommandOptions(); this.updateCommandOptions = new UpdateCommandOptions(); @@ -216,6 +218,47 @@ public class UpdateNotesCommandOptions { } + @Parameters(commandNames = {"configuration-update"}, commandDescription ="Update the Organization configuration attributes") + public class UpdateConfigurationCommandOptions { + + @ParametersDelegate + public CommonCommandOptions commonOptions = commonCommandOptions; + + @Parameter(names = {"--json-file"}, description = "File with the body data in JSON format. Note, that using this parameter will ignore all the other parameters.", required = false, arity = 1) + public String jsonFile; + + @Parameter(names = {"--json-data-model"}, description = "Show example of file structure for body data.", help = true, arity = 0) + public Boolean jsonDataModel = false; + + @Parameter(names = {"--include", "-I"}, description = "Fields included in the response, whole JSON path must be provided", required = false, arity = 1) + public String include; + + @Parameter(names = {"--exclude", "-E"}, description = "Fields excluded in the response, whole JSON path must be provided", required = false, arity = 1) + public String exclude; + + @Parameter(names = {"--organization"}, description = "Organization id", required = true, arity = 1) + public String organization; + + @Parameter(names = {"--include-result"}, description = "Flag indicating to include the created or updated document result in the response", required = false, help = true, arity = 0) + public boolean includeResult = false; + + @Parameter(names = {"--authentication-origins-action"}, description = "Action to be performed if the array of authenticationOrigins is being updated.", required = false, arity = 1) + public String authenticationOriginsAction = "ADD"; + + @Parameter(names = {"--optimizations-simplify-permissions"}, description = "The body web service simplifyPermissions parameter", required = false, help = true, arity = 0) + public boolean optimizationsSimplifyPermissions = false; + + @Parameter(names = {"--token-algorithm"}, description = "The body web service algorithm parameter", required = false, arity = 1) + public String tokenAlgorithm; + + @Parameter(names = {"--token-secret-key"}, description = "The body web service secretKey parameter", required = false, arity = 1) + public String tokenSecretKey; + + @Parameter(names = {"--token-expiration"}, description = "The body web service expiration parameter", required = false, arity = 1) + public Long tokenExpiration; + + } + @Parameters(commandNames = {"info"}, commandDescription ="Return the organization information") public class InfoCommandOptions { diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_4/catalog/FixStatusIndexesMigration.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_4/catalog/FixStatusIndexesMigration.java new file mode 100644 index 00000000000..ae73bcdc578 --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_4/catalog/FixStatusIndexesMigration.java @@ -0,0 +1,46 @@ +package org.opencb.opencga.app.migrations.v2_12_4.catalog; + +import org.bson.Document; +import org.opencb.opencga.catalog.db.mongodb.OrganizationMongoDBAdaptorFactory; +import org.opencb.opencga.catalog.migration.Migration; +import org.opencb.opencga.catalog.migration.MigrationTool; + +import java.util.Arrays; + +@Migration(id = "fix_status_indexes" , + description = "Replace 'status.name' indexes for 'status.id'", + version = "2.12.4", + domain = Migration.MigrationDomain.CATALOG, + language = Migration.MigrationLanguage.JAVA, + date = 20240328 +) +public class FixStatusIndexesMigration extends MigrationTool { + + @Override + protected void run() throws Exception { + Document statusIndex = new Document() + .append("status.name", 1) + .append("studyUid", 1); + dropIndex(Arrays.asList(OrganizationMongoDBAdaptorFactory.JOB_COLLECTION, OrganizationMongoDBAdaptorFactory.FILE_COLLECTION, + OrganizationMongoDBAdaptorFactory.SAMPLE_COLLECTION, OrganizationMongoDBAdaptorFactory.SAMPLE_ARCHIVE_COLLECTION, + OrganizationMongoDBAdaptorFactory.COHORT_COLLECTION, OrganizationMongoDBAdaptorFactory.INDIVIDUAL_COLLECTION, + OrganizationMongoDBAdaptorFactory.INDIVIDUAL_ARCHIVE_COLLECTION, OrganizationMongoDBAdaptorFactory.FAMILY_COLLECTION, + OrganizationMongoDBAdaptorFactory.FAMILY_ARCHIVE_COLLECTION, OrganizationMongoDBAdaptorFactory.PANEL_COLLECTION, + OrganizationMongoDBAdaptorFactory.PANEL_ARCHIVE_COLLECTION), statusIndex); + + Document internalStatusIndex = new Document() + .append("internal.status.name", 1) + .append("studyUid", 1); + dropIndex(Arrays.asList(OrganizationMongoDBAdaptorFactory.JOB_COLLECTION, OrganizationMongoDBAdaptorFactory.FILE_COLLECTION, + OrganizationMongoDBAdaptorFactory.SAMPLE_COLLECTION, OrganizationMongoDBAdaptorFactory.SAMPLE_ARCHIVE_COLLECTION, + OrganizationMongoDBAdaptorFactory.COHORT_COLLECTION, OrganizationMongoDBAdaptorFactory.INDIVIDUAL_COLLECTION, + OrganizationMongoDBAdaptorFactory.INDIVIDUAL_ARCHIVE_COLLECTION, OrganizationMongoDBAdaptorFactory.FAMILY_COLLECTION, + OrganizationMongoDBAdaptorFactory.FAMILY_ARCHIVE_COLLECTION, OrganizationMongoDBAdaptorFactory.PANEL_COLLECTION, + OrganizationMongoDBAdaptorFactory.PANEL_ARCHIVE_COLLECTION, OrganizationMongoDBAdaptorFactory.CLINICAL_ANALYSIS_COLLECTION, + OrganizationMongoDBAdaptorFactory.INTERPRETATION_COLLECTION, OrganizationMongoDBAdaptorFactory.INTERPRETATION_ARCHIVE_COLLECTION), + internalStatusIndex); + + catalogManager.installIndexes(token); + } + +} diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_5/storage/AddAllelesColumnToPhoenix.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_5/storage/AddAllelesColumnToPhoenix.java new file mode 100644 index 00000000000..4efa260d965 --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_5/storage/AddAllelesColumnToPhoenix.java @@ -0,0 +1,27 @@ +package org.opencb.opencga.app.migrations.v2_12_5.storage; + +import org.opencb.opencga.app.migrations.StorageMigrationTool; +import org.opencb.opencga.catalog.migration.Migration; +import org.opencb.opencga.storage.core.variant.VariantStorageEngine; + +@Migration(id="add_missing_column_to_phoenix_TASK-6005", description = "Add missing ALLELES column to phoenix #TASK-6005", + version = "2.12.5", domain = Migration.MigrationDomain.STORAGE, date = 20240510 +) +public class AddAllelesColumnToPhoenix extends StorageMigrationTool { + + @Override + protected void run() throws Exception { + for (String project : getVariantStorageProjects(organizationId)) { + VariantStorageEngine engine = getVariantStorageEngineByProject(project); + if (engine.getStorageEngineId().equals("hadoop")) { + logger.info("Adding missing columns (if any) for project " + project); + // Using same class for both migrations + Class aClass = Class.forName("org.opencb.opencga.storage.hadoop.variant.migration.v2_3_0.AddMissingColumns"); + Runnable runnable = (Runnable) aClass + .getConstructor(Object.class) + .newInstance(engine); + runnable.run(); + } + } + } +} \ No newline at end of file diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_5/storage/DetectIllegalConcurrentFileLoadingsMigration.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_5/storage/DetectIllegalConcurrentFileLoadingsMigration.java new file mode 100644 index 00000000000..132e1357f1b --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_5/storage/DetectIllegalConcurrentFileLoadingsMigration.java @@ -0,0 +1,360 @@ +package org.opencb.opencga.app.migrations.v2_12_5.storage; + +import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.commons.datastore.core.Query; +import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.analysis.variant.metadata.CatalogStorageMetadataSynchronizer; +import org.opencb.opencga.app.migrations.StorageMigrationTool; +import org.opencb.opencga.catalog.db.api.FileDBAdaptor; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.migration.Migration; +import org.opencb.opencga.core.common.TimeUtils; +import org.opencb.opencga.core.common.UriUtils; +import org.opencb.opencga.core.models.common.IndexStatus; +import org.opencb.opencga.core.models.file.File; +import org.opencb.opencga.core.models.file.FileInternalVariantIndex; +import org.opencb.opencga.core.models.file.FileUpdateParams; +import org.opencb.opencga.core.models.file.VariantIndexStatus; +import org.opencb.opencga.core.models.sample.SampleUpdateParams; +import org.opencb.opencga.storage.core.exceptions.StorageEngineException; +import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; +import org.opencb.opencga.storage.core.metadata.models.FileMetadata; +import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; +import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; +import org.opencb.opencga.storage.core.variant.VariantStorageEngine; +import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; +import org.opencb.opencga.storage.core.variant.adaptors.VariantField; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; + +import java.nio.file.Paths; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +@Migration(id = "illegal_concurrent_file_loadings" , + description = "Detect illegal concurrent file loadings and fix them by setting 'status' to 'INVALID' or 'READY'", + version = "2.12.5", + domain = Migration.MigrationDomain.STORAGE, + language = Migration.MigrationLanguage.JAVA, + date = 20240424 +) +public class DetectIllegalConcurrentFileLoadingsMigration extends StorageMigrationTool { + + @Override + protected void run() throws Exception { + for (String project : getVariantStorageProjects(organizationId)) { + VariantStorageEngine engine = getVariantStorageEngineByProject(project); + if (!engine.getStorageEngineId().equals("hadoop")) { + continue; + } + logger.info("Checking project '{}'", project); + for (String study : engine.getMetadataManager().getStudyNames()) { + checkStudy(engine, study); + } + } + } + + private void checkStudy(VariantStorageEngine engine, String study) throws StorageEngineException, CatalogException { + VariantStorageMetadataManager metadataManager = engine.getMetadataManager(); + boolean repeatMigration = params.getBoolean("repeat-migration"); + + logger.info("Checking study '{}'", study); + int studyId = metadataManager.getStudyId(study); + + Set> fileSets = getFileWithSharedSamples(engine, studyId); + Set fileIds = fileSets.stream().flatMap(Collection::stream).collect(Collectors.toSet()); + + if (fileSets.isEmpty()) { + logger.info("No concurrent file loadings found in study '{}'", study); + return; + } + + Map fileTasks = new HashMap<>(); + for (TaskMetadata taskMetadata : metadataManager.taskIterable(studyId)) { + if (taskMetadata.getType() == TaskMetadata.Type.LOAD) { + for (Integer fileId : taskMetadata.getFileIds()) { + if (fileIds.contains(fileId)) { + TaskMetadata old = fileTasks.put(fileId, taskMetadata); + if (old != null) { + throw new IllegalStateException("File '" + fileId + "' is being loaded by more than one task." + + " Tasks '" + old.getName() + "'(" + old.getId() + ") and" + + " '" + taskMetadata.getName() + "'(" + taskMetadata.getId() + ")"); + } + } + } + } + } + + Set> fileSetsToInvalidate = new HashSet<>(); + Set affectedFiles = new HashSet<>(); + Set affectedSamples = new HashSet<>(); + for (Set fileSet : fileSets) { + // Check if any task from this file set overlaps in time + List tasks = new ArrayList<>(); + for (Integer fileId : fileSet) { + TaskMetadata task = fileTasks.get(fileId); + if (task != null) { + tasks.add(task); + } + } + if (tasks.size() > 1) { + logger.info("Found {} tasks loading files {}", tasks.size(), fileSet); + for (int i = 0; i < tasks.size(); i++) { + TaskMetadata task1 = tasks.get(i); + Date task1start = task1.getStatus().firstKey(); + Date task1end = task1.getStatus().lastKey(); + for (int f = i + 1; f < tasks.size(); f++) { + TaskMetadata task2 = tasks.get(f); + Date task2start = task2.getStatus().firstKey(); + Date task2end = task2.getStatus().lastKey(); + if (task1start.before(task2end) && task1end.after(task2start)) { + fileSetsToInvalidate.add(fileSet); + affectedFiles.addAll(task1.getFileIds()); + + List task1Files = task1.getFileIds().stream().map(fileId -> "'" + metadataManager.getFileName(studyId, fileId) + "'(" + fileId + ")").collect(Collectors.toList()); + List task2Files = task2.getFileIds().stream().map(fileId -> "'" + metadataManager.getFileName(studyId, fileId) + "'(" + fileId + ")").collect(Collectors.toList()); + + logger.info("Tasks '{}'({}) and '{}'({}) overlap in time", task1.getName(), task1.getId(), task2.getName(), task2.getId()); + logger.info("Task1: {} - {} loading files {}", task1start, task1end, task1Files); + logger.info("Task2: {} - {} loading files {}", task2start, task2end, task2Files); + Set task1Samples = task1.getFileIds().stream().flatMap(file -> metadataManager.getSampleIdsFromFileId(studyId, file).stream()).collect(Collectors.toSet()); + Set task2Samples = task2.getFileIds().stream().flatMap(file -> metadataManager.getSampleIdsFromFileId(studyId, file).stream()).collect(Collectors.toSet()); + for (Integer task1Sample : task1Samples) { + if (task2Samples.contains(task1Sample)) { + String sampleName = metadataManager.getSampleName(studyId, task1Sample); + affectedSamples.add(task1Sample); + logger.info("Sample '{}'({}) is shared between tasks", sampleName, task1Sample); + } + } + } + } + } + } + + Set invalidFiles = new HashSet<>(); + List invalidSampleIndexes = new ArrayList<>(); + for (Integer sampleId : affectedSamples) { + String sampleName = metadataManager.getSampleName(studyId, sampleId); + SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(studyId, sampleId); + if (!sampleMetadata.isMultiFileSample()) { + logger.warn("Sample '{}'({}) is not a multi-file sample but has multiple files loaded", sampleName, sampleId); + for (Integer file : sampleMetadata.getFiles()) { + if (metadataManager.isFileIndexed(studyId, file)) { + invalidFiles.add(file); + logger.info(" - Invalidating file '{}'({}). Must be deleted and then indexed.", + metadataManager.getFileName(studyId, file), file); + } + } + } else if (sampleMetadata.getSampleIndexStatus(Optional.of(sampleMetadata.getSampleIndexVersion()).orElse(-1)) == TaskMetadata.Status.READY) { + for (Integer fileId : sampleMetadata.getFiles()) { + if (affectedFiles.contains(fileId)) { + FileMetadata fileMetadata = metadataManager.getFileMetadata(studyId, fileId); + String fileName = fileMetadata.getName(); + + long actualCount = engine.get(new VariantQuery().study(study).sample(sampleName).file(fileName), + new QueryOptions(QueryOptions.INCLUDE, VariantField.ID).append(QueryOptions.COUNT, true)) + .getNumMatches(); + + File catalogFile = catalogManager.getFileManager().search(study, + new Query(FileDBAdaptor.QueryParams.URI.key(), UriUtils.createUriSafe(fileMetadata.getPath())), + new QueryOptions(), token).first(); + if (catalogFile == null) { + logger.warn("File '{}'({}) not found in catalog", fileName, fileId); + logger.warn("Sample '{}'({}) invalidated, as file '{}'({}) is not found in catalog", sampleName, sampleId, fileName, fileId); + logger.info(" - Invalidating sample index for sample '{}'({})", sampleName, sampleId); + invalidSampleIndexes.add(sampleId); + continue; + } + if (fileMetadata.getSamples().size() == 1 && catalogFile.getSampleIds().size() == 1) { + long expectedCount = 0; + for (Map.Entry entry : catalogFile.getQualityControl().getVariant() + .getVariantSetMetrics().getGenotypeCount().entrySet()) { + if (GenotypeClass.MAIN_ALT.test(entry.getKey())) { + expectedCount += entry.getValue(); + } + } + if (expectedCount == 0) { + expectedCount = catalogFile.getQualityControl().getVariant().getVariantSetMetrics().getVariantCount(); + } + if (expectedCount != actualCount) { + invalidSampleIndexes.add(sampleId); + logger.warn("Sample '{}'({}) was expected to have {} variants in the sample index of file '{}'({}) but has {}", + sampleName, sampleId, expectedCount, fileName, fileId, actualCount); + logger.info(" - Invalidating sample index for sample '{}'({})", sampleName, sampleId); + } + } else { + Map pipelineResult = (Map) catalogFile.getAttributes().get("storagePipelineResult"); + long loadedVariants = ((Number) ((Map) pipelineResult.get("loadStats")).get("loadedVariants")).longValue(); + if (loadedVariants != actualCount) { + invalidSampleIndexes.add(sampleId); + logger.warn("Sample '{}'({}) was expected to have {} variants in the sample index but has {}", + sampleName, sampleId, loadedVariants, actualCount); + logger.info(" - Invalidating sample index for sample '{}'({})", sampleName, sampleId); + } + } + } + } + } else { + invalidSampleIndexes.add(sampleId); + } + } + + if (params.getBoolean("dry-run")) { + if (invalidFiles.isEmpty() && invalidSampleIndexes.isEmpty()) { + logger.info("Dry-run mode. No files or samples to invalidate"); + } else { + logger.info("Dry-run mode. Skipping invalidation of files and samples"); + + Set invalidSamples = new HashSet<>(); + for (Integer fileId : invalidFiles) { + invalidSamples.addAll(metadataManager.getSampleIdsFromFileId(studyId, fileId)); + } + + logger.info("Affected files: {}", invalidFiles); + logger.info("Affected samples: {}", invalidSamples); + logger.info("Affected sample indexes: {}", invalidSampleIndexes); + } + } else { + ObjectMap event = new ObjectMap() + .append("patch", getAnnotation().patch()) + .append("description", "affected_invalid_sample") + .append("dateStr", TimeUtils.getTime()) + .append("date", Date.from(Instant.now())); + for (Integer sampleId : invalidSampleIndexes) { + invalidateSecondarySampleIndex(study, sampleId, event, metadataManager, studyId, repeatMigration); + } + Set invalidSamples = new HashSet<>(); + for (Integer fileId : invalidFiles) { + invalidateFileIndex(study, fileId, event, metadataManager, studyId, invalidSamples, repeatMigration); + } + for (Integer sampleId : invalidSamples) { + invalidateSampleIndex(study, sampleId, event, metadataManager, studyId, repeatMigration); + } + + Set allSampleIds = new HashSet<>(); + allSampleIds.addAll(affectedSamples); + allSampleIds.addAll(invalidSamples); + List allSampleNames = allSampleIds.stream() + .map(sampleId -> metadataManager.getSampleName(studyId, sampleId)) + .collect(Collectors.toList()); + + if (!allSampleNames.isEmpty()) { + new CatalogStorageMetadataSynchronizer(catalogManager, metadataManager) + .synchronizeCatalogSamplesFromStorage(study, allSampleNames, token); + } + + } + } + } + + private void invalidateFileIndex(String study, Integer fileId, ObjectMap event, VariantStorageMetadataManager metadataManager, int studyId, Set invalidSamples, boolean repeatMigration) throws StorageEngineException, CatalogException { + ObjectMap thisEvent = new ObjectMap(event); + String filePath = metadataManager.updateFileMetadata(studyId, fileId, fileMetadata -> { + invalidSamples.addAll(fileMetadata.getSamples()); + if (fileMetadata.getAttributes().containsKey("TASK-6078") && !repeatMigration) { + logger.info("File '{}'({}) already has the attribute 'TASK-6078'. Skip", + fileMetadata.getName(), fileMetadata.getId()); + } else { + Map oldStatus = new HashMap<>(fileMetadata.getStatus()); + fileMetadata.setIndexStatus(TaskMetadata.Status.INVALID); + fileMetadata.getAttributes().put("TASK-6078", thisEvent.append("oldStatus", oldStatus)); + } + }).getPath(); + String fileUri = Paths.get(filePath).toUri().toString(); + Query query = new Query(FileDBAdaptor.QueryParams.URI.key(), fileUri); + File file = catalogManager.getFileManager() + .search(study, query, new QueryOptions(), token) + .first(); + catalogManager.getFileManager().update(study, file.getId(), + new FileUpdateParams().setAttributes(new ObjectMap("TASK-6078", thisEvent)), QueryOptions.empty(), token); + catalogManager.getFileManager().updateFileInternalVariantIndex(study, file, new FileInternalVariantIndex() + .setStatus(new VariantIndexStatus(IndexStatus.INVALID, "Invalid status - TASK-6078 - affected_invalid_sample - " + + "File must be deleted and then indexed")), token); + } + + private void invalidateSecondarySampleIndex(String study, Integer sampleId, ObjectMap event, VariantStorageMetadataManager metadataManager, int studyId, boolean repeatMigration) throws StorageEngineException, CatalogException { + ObjectMap thisEvent = new ObjectMap(event); + String sampleName = metadataManager.updateSampleMetadata(studyId, sampleId, sampleMetadata -> { + if (sampleMetadata.getAttributes().containsKey("TASK-6078") && !repeatMigration) { + logger.info("Sample '{}'({}) already has the attribute 'TASK-6078'. Skip", + sampleMetadata.getName(), sampleMetadata.getId()); + } else { + Map oldStatus = new HashMap<>(sampleMetadata.getStatus()); + Map oldAttributes = new HashMap<>(sampleMetadata.getAttributes()); + + for (Integer v : sampleMetadata.getSampleIndexVersions()) { + sampleMetadata.setSampleIndexStatus(TaskMetadata.Status.NONE, v); + } + for (Integer v : sampleMetadata.getSampleIndexAnnotationVersions()) { + sampleMetadata.setSampleIndexAnnotationStatus(TaskMetadata.Status.NONE, v); + } + for (Integer v : sampleMetadata.getFamilyIndexVersions()) { + sampleMetadata.setFamilyIndexStatus(TaskMetadata.Status.NONE, v); + } + + sampleMetadata.setIndexStatus(TaskMetadata.Status.INVALID); + thisEvent.append("oldStatus", oldStatus); + thisEvent.append("oldAttributes", oldAttributes); + thisEvent.append("newStatus", sampleMetadata.getStatus()); + sampleMetadata.getAttributes().put("TASK-6078", thisEvent); + } + }).getName(); + catalogManager.getSampleManager().update(study, sampleName, + new SampleUpdateParams().setAttributes(new ObjectMap("TASK-6078", thisEvent)), QueryOptions.empty(), token); + } + + private void invalidateSampleIndex(String study, Integer sampleId, ObjectMap event, VariantStorageMetadataManager metadataManager, int studyId, boolean repeatMigration) throws StorageEngineException, CatalogException { + ObjectMap thisEvent = new ObjectMap(event); + String sampleName = metadataManager.updateSampleMetadata(studyId, sampleId, sampleMetadata -> { + if (sampleMetadata.getAttributes().containsKey("TASK-6078") && !repeatMigration) { + logger.info("Sample '{}'({}) already has the attribute 'TASK-6078'. Skip", + sampleMetadata.getName(), sampleMetadata.getId()); + } else { + Map oldStatus = new HashMap<>(sampleMetadata.getStatus()); + Map oldAttributes = new HashMap<>(sampleMetadata.getAttributes()); + sampleMetadata.setIndexStatus(TaskMetadata.Status.INVALID); + + for (Integer v : sampleMetadata.getSampleIndexVersions()) { + sampleMetadata.setSampleIndexStatus(TaskMetadata.Status.NONE, v); + } + for (Integer v : sampleMetadata.getSampleIndexAnnotationVersions()) { + sampleMetadata.setSampleIndexAnnotationStatus(TaskMetadata.Status.NONE, v); + } + for (Integer v : sampleMetadata.getFamilyIndexVersions()) { + sampleMetadata.setFamilyIndexStatus(TaskMetadata.Status.NONE, v); + } + thisEvent.append("oldStatus", oldStatus); + thisEvent.append("oldAttributes", oldAttributes); + thisEvent.append("newStatus", sampleMetadata.getStatus()); + sampleMetadata.getAttributes().put("TASK-6078", thisEvent); + } + }).getName(); + catalogManager.getSampleManager().update(study, sampleName, + new SampleUpdateParams().setAttributes(new ObjectMap("TASK-6078", thisEvent)), QueryOptions.empty(), token); + } + + private Set> getFileWithSharedSamples(VariantStorageEngine engine, int studyId) throws StorageEngineException { + VariantStorageMetadataManager metadataManager = engine.getMetadataManager(); + + Set> fileSets = new HashSet<>(); + // Check if there are any sample with more than one file + for (SampleMetadata sampleMetadata : metadataManager.sampleMetadataIterable(studyId)) { + if (sampleMetadata.getFiles().size() > 1) { + if (sampleMetadata.getSplitData() == VariantStorageEngine.SplitData.CHROMOSOME || sampleMetadata.getSplitData() == VariantStorageEngine.SplitData.REGION) { + logger.debug("Sample '{}' is split by chromosome or region. Skip", sampleMetadata.getName()); + continue; + } + ArrayList sampleFileIds = new ArrayList<>(sampleMetadata.getFiles()); + + sampleFileIds.removeIf(fileId -> !metadataManager.isFileIndexed(studyId, fileId)); + + if (sampleFileIds.size() > 1) { + logger.info("Sample '{}' has more than one indexed file with split data '{}'", sampleMetadata.getName(), sampleMetadata.getSplitData()); + + fileSets.add(new HashSet<>(sampleFileIds)); + } + } + } + return fileSets; + } +} diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/NoteMigration.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/NoteMigration.java index c5fb031f1da..c3e49a2d85e 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/NoteMigration.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/NoteMigration.java @@ -6,34 +6,23 @@ import com.mongodb.client.model.RenameCollectionOptions; import com.mongodb.client.model.Updates; import com.mongodb.client.result.UpdateResult; -import org.apache.commons.lang3.StringUtils; import org.bson.Document; import org.bson.conversions.Bson; import org.opencb.commons.datastore.mongodb.MongoDataStore; import org.opencb.opencga.catalog.db.api.NoteDBAdaptor; -import org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptorFactory; import org.opencb.opencga.catalog.db.mongodb.OrganizationMongoDBAdaptorFactory; -import org.opencb.opencga.catalog.exceptions.CatalogDBException; -import org.opencb.opencga.catalog.io.IOManagerFactory; -import org.opencb.opencga.catalog.managers.CatalogManager; import org.opencb.opencga.catalog.migration.Migration; import org.opencb.opencga.catalog.migration.MigrationTool; -import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.models.notes.Note; -import java.util.Collections; -import java.util.List; - @Migration(id = "migrate_notes", description = "Migrate notes #TASK-5836", version = "3.1.0", language = Migration.MigrationLanguage.JAVA, domain = Migration.MigrationDomain.CATALOG, date = 20240315) public class NoteMigration extends MigrationTool { @Override protected void run() throws Exception { - IOManagerFactory ioManagerFactory = new IOManagerFactory(); - dbAdaptorFactory = new MongoDBAdaptorFactory(configuration, ioManagerFactory); // First migrate to add the new values - MongoCollection collection = getMongoCollection(ParamConstants.ADMIN_ORGANIZATION, "notes"); + MongoCollection collection = getMongoCollection(organizationId, "notes"); Bson query = Filters.exists(NoteDBAdaptor.QueryParams.STUDY_UID.key(), false); Bson update = Updates.combine( Updates.set(NoteDBAdaptor.QueryParams.STUDY_UID.key(), -1L), @@ -43,41 +32,20 @@ protected void run() throws Exception { ); UpdateResult updateResult = collection.updateMany(query, update); if (updateResult.getModifiedCount() == 0) { - // Check there are at least 2 organizations present - logger.info("Note data model could not be updated. Detected organizations are: {}", StringUtils.join(dbAdaptorFactory.getOrganizationIds(), ",")); - if (dbAdaptorFactory.getOrganizationIds().size() == 2) { - logger.info("Nothing to migrate"); - return; - } else { - throw new CatalogDBException("Notes could not be found to migrate."); - } + logger.info("Note data model could not be updated. Notes found in organization '{}': {}", organizationId, updateResult.getMatchedCount()); } - renameNoteCollection(Collections.singletonList(ParamConstants.ADMIN_ORGANIZATION)); - - dbAdaptorFactory.close(); - dbAdaptorFactory = new MongoDBAdaptorFactory(configuration, ioManagerFactory); - // We run it a second time because the first time it will only rename the "opencga" org as OpenCGA will not be able to know - // which other organizations are present in the installation (trying to fetch the information from "note" instead of old "notes") - List organizationIds = dbAdaptorFactory.getOrganizationIds(); - organizationIds.remove(ParamConstants.ADMIN_ORGANIZATION); - renameNoteCollection(organizationIds); - - // Reload catalog manager to install missing indexes - catalogManager = new CatalogManager(configuration); - catalogManager.installIndexes(token); - } - private void renameNoteCollection(List organizationIds) throws CatalogDBException { + // Rename Note collection RenameCollectionOptions options = new RenameCollectionOptions().dropTarget(true); // Rename collection - for (String organizationId : organizationIds) { - String databaseName = dbAdaptorFactory.getMongoDataStore(organizationId).getDatabaseName(); - logger.info("Renaming notes collection for organization '{}' -> Database: '{}'", organizationId, databaseName); - MongoDataStore mongoDataStore = dbAdaptorFactory.getMongoDataStore(organizationId); - mongoDataStore.getDb().getCollection("notes").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_COLLECTION), options); - mongoDataStore.getDb().getCollection("notes_archive").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_ARCHIVE_COLLECTION), options); - mongoDataStore.getDb().getCollection("notes_deleted").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.DELETED_NOTE_COLLECTION), options); - } + String databaseName = dbAdaptorFactory.getMongoDataStore(organizationId).getDatabaseName(); + logger.info("Renaming notes collection for organization '{}' -> Database: '{}'", organizationId, databaseName); + MongoDataStore mongoDataStore = dbAdaptorFactory.getMongoDataStore(organizationId); + mongoDataStore.getDb().getCollection("notes").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_COLLECTION), options); + mongoDataStore.getDb().getCollection("notes_archive").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_ARCHIVE_COLLECTION), options); + mongoDataStore.getDb().getCollection("notes_deleted").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.DELETED_NOTE_COLLECTION), options); + + catalogManager.installIndexes(organizationId, token); } } diff --git a/opencga-catalog/pom.xml b/opencga-catalog/pom.xml index d15b4768e59..975b4100c94 100644 --- a/opencga-catalog/pom.xml +++ b/opencga-catalog/pom.xml @@ -247,6 +247,12 @@ template.zip + + ../opencga-core/src/test/resources + + log4j2-test.xml + + diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AuthenticationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AuthenticationManager.java index 345ac2977b7..d93cf48c4a7 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AuthenticationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AuthenticationManager.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import javax.crypto.spec.SecretKeySpec; +import java.io.Closeable; import java.security.Key; import java.util.Collections; import java.util.Date; @@ -37,7 +38,7 @@ /** * @author Jacobo Coll <jacobo167@gmail.com> */ -public abstract class AuthenticationManager { +public abstract class AuthenticationManager implements Closeable { protected JwtManager jwtManager; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AzureADAuthenticationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AzureADAuthenticationManager.java index b0f525af072..f104f8fdf93 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AzureADAuthenticationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/AzureADAuthenticationManager.java @@ -146,6 +146,15 @@ public AzureADAuthenticationManager(AuthenticationOrigin authenticationOrigin) t Configurator.setLevel("com.microsoft.aad.adal4j.AuthenticationAuthority", Level.WARN); } + public static void validateAuthenticationOriginConfiguration(AuthenticationOrigin authenticationOrigin) throws CatalogException { + if (authenticationOrigin.getType() != AuthenticationOrigin.AuthenticationType.AzureAD) { + throw new CatalogException("Unknown authentication type. Expected type '" + AuthenticationOrigin.AuthenticationType.AzureAD + + "' but received '" + authenticationOrigin.getType() + "'."); + } + AzureADAuthenticationManager azureADAuthenticationManager = new AzureADAuthenticationManager(authenticationOrigin); + azureADAuthenticationManager.close(); + } + private OIDCProviderMetadata getProviderMetadata(String host) throws IOException, ParseException { URL providerConfigurationURL = new URL(host); InputStream stream = providerConfigurationURL.openStream(); @@ -420,4 +429,8 @@ public String createNonExpiringToken(String organizationId, String userId, Map claims, long expiration) { - return jwtManager.createJWTToken(organizationId, userId, claims, expiration); + return jwtManager.createJWTToken(organizationId, AuthenticationOrigin.AuthenticationType.OPENCGA, userId, claims, expiration); } @Override public String createNonExpiringToken(String organizationId, String userId, Map claims) { - return jwtManager.createJWTToken(organizationId, userId, claims, 0L); + return jwtManager.createJWTToken(organizationId, AuthenticationOrigin.AuthenticationType.OPENCGA, userId, claims, 0L); } @Override @@ -131,7 +142,7 @@ public OpenCGAResult resetPassword(String organizationId, String userId) throws String mailHost = this.emailConfig.getHost(); String mailPort = this.emailConfig.getPort(); try { - MailUtils.sendResetPasswordMail(email, newPassword, mailUser, mailPassword, mailHost, mailPort); + MailUtils.sendResetPasswordMail(email, newPassword, mailUser, mailPassword, mailHost, mailPort, userId); result = dbAdaptorFactory.getCatalogUserDBAdaptor(organizationId).resetPassword(userId, email, newPassword); } catch (Exception e) { throw new CatalogException("Email could not be sent.", e); @@ -145,4 +156,8 @@ public static AuthenticationOrigin createOpencgaAuthenticationOrigin() { .setId(CatalogAuthenticationManager.OPENCGA) .setType(AuthenticationOrigin.AuthenticationType.OPENCGA); } + + @Override + public void close() { + } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/JwtManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/JwtManager.java index 8cbb28decca..fd0462e3a98 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/JwtManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/JwtManager.java @@ -18,6 +18,7 @@ import io.jsonwebtoken.*; import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException; +import org.opencb.opencga.core.config.AuthenticationOrigin; import org.opencb.opencga.core.models.JwtPayload; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,8 @@ import java.util.List; import java.util.Map; +import static org.opencb.opencga.core.models.JwtPayload.AUTH_ORIGIN; + public class JwtManager { private SignatureAlgorithm algorithm; @@ -84,13 +87,18 @@ public JwtManager setPublicKey(Key publicKey) { return this; } - public String createJWTToken(String organizationId, String userId, Map claims, long expiration) { + public String createJWTToken(String organizationId, AuthenticationOrigin.AuthenticationType type, String userId, + Map claims, long expiration) { long currentTime = System.currentTimeMillis(); JwtBuilder jwtBuilder = Jwts.builder(); if (claims != null && !claims.isEmpty()) { jwtBuilder.setClaims(claims); } + if (type != null) { + jwtBuilder.addClaims(Collections.singletonMap(AUTH_ORIGIN, type)); + } + jwtBuilder.setSubject(userId) .setAudience(organizationId) .setIssuer("OpenCGA") @@ -115,14 +123,24 @@ public void validateToken(String token, Key publicKey) throws CatalogAuthenticat public JwtPayload getPayload(String token) throws CatalogAuthenticationException { Claims body = parseClaims(token, publicKey).getBody(); - return new JwtPayload(body.getSubject(), body.getAudience(), body.getIssuer(), body.getIssuedAt(), body.getExpiration(), token); + return new JwtPayload(body.getSubject(), body.getAudience(), getAuthOrigin(body), body.getIssuer(), body.getIssuedAt(), + body.getExpiration(), token); } public JwtPayload getPayload(String token, Key publicKey) throws CatalogAuthenticationException { Claims body = parseClaims(token, publicKey).getBody(); - return new JwtPayload(body.getSubject(), body.getAudience(), body.getIssuer(), body.getIssuedAt(), body.getExpiration(), token); + return new JwtPayload(body.getSubject(), body.getAudience(), getAuthOrigin(body), body.getIssuer(), body.getIssuedAt(), + body.getExpiration(), token); } + private AuthenticationOrigin.AuthenticationType getAuthOrigin(Claims claims) { + String o = claims.get(AUTH_ORIGIN, String.class); + if (o != null) { + return AuthenticationOrigin.AuthenticationType.valueOf(o); + } else { + return null; + } + } public String getAudience(String token) throws CatalogAuthenticationException { return getAudience(token, this.publicKey); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/LDAPAuthenticationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/LDAPAuthenticationManager.java index 516a54702c2..35fe615d161 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/LDAPAuthenticationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/LDAPAuthenticationManager.java @@ -24,8 +24,10 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException; import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.AuthenticationOrigin; +import org.opencb.opencga.core.models.organizations.TokenConfiguration; import org.opencb.opencga.core.models.user.*; import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.LoggerFactory; @@ -124,6 +126,24 @@ protected static String envToStringRedacted(Hashtable env) { return string; } + public static void validateAuthenticationOriginConfiguration(AuthenticationOrigin authenticationOrigin) throws CatalogException { + if (authenticationOrigin.getType() != AuthenticationType.LDAP) { + throw new CatalogException("Unknown authentication type. Expected type '" + AuthenticationType.LDAP + "' but received '" + + authenticationOrigin.getType() + "'."); + } + ParamUtils.checkParameter(authenticationOrigin.getHost(), AuthenticationType.LDAP + " host."); + + TokenConfiguration defaultTokenConfig = TokenConfiguration.init(); + LDAPAuthenticationManager ldapAuthenticationManager = new LDAPAuthenticationManager(authenticationOrigin, + defaultTokenConfig.getAlgorithm(), defaultTokenConfig.getSecretKey(), defaultTokenConfig.getExpiration()); + DirContext dirContext = ldapAuthenticationManager.getDirContext(ldapAuthenticationManager.getDefaultEnv(), 1); + if (dirContext == null) { + throw new CatalogException("LDAP: Could not connect to the LDAP server using the provided configuration."); + } + ldapAuthenticationManager.closeDirContextAndSuppress(dirContext, new Exception()); + ldapAuthenticationManager.close(); + } + @Override public AuthenticationResponse authenticate(String organizationId, String userId, String password) throws CatalogAuthenticationException { @@ -213,12 +233,12 @@ public void newPassword(String organizationId, String userId, String newPassword @Override public String createToken(String organizationId, String userId, Map claims, long expiration) { - return jwtManager.createJWTToken(organizationId, userId, claims, expiration); + return jwtManager.createJWTToken(organizationId, AuthenticationType.LDAP, userId, claims, expiration); } @Override public String createNonExpiringToken(String organizationId, String userId, Map claims) { - return jwtManager.createJWTToken(organizationId, userId, claims, 0L); + return jwtManager.createJWTToken(organizationId, AuthenticationType.LDAP, userId, claims, 0L); } /* Private methods */ @@ -503,4 +523,9 @@ private String takeString(ObjectMap objectMap, String key, String defaultValue) objectMap.remove(key); return value; } + + @Override + public void close() { + executorService.shutdown(); + } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/SSOAuthenticationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/SSOAuthenticationManager.java new file mode 100644 index 00000000000..e676029e413 --- /dev/null +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/SSOAuthenticationManager.java @@ -0,0 +1,87 @@ +package org.opencb.opencga.catalog.auth.authentication; + +import io.jsonwebtoken.SignatureAlgorithm; +import org.apache.commons.lang3.NotImplementedException; +import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.core.config.AuthenticationOrigin; +import org.opencb.opencga.core.models.user.AuthenticationResponse; +import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.response.OpenCGAResult; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.security.Key; +import java.util.List; +import java.util.Map; + +public class SSOAuthenticationManager extends AuthenticationManager { + + public SSOAuthenticationManager(String algorithm, String secretKeyString, long expiration) { + super(expiration); + + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.valueOf(algorithm); + Key secretKey = this.converStringToKeyObject(secretKeyString, signatureAlgorithm.getJcaName()); + this.jwtManager = new JwtManager(signatureAlgorithm.getValue(), secretKey); + + this.logger = LoggerFactory.getLogger(SSOAuthenticationManager.class); + } + + public static void validateAuthenticationOriginConfiguration(AuthenticationOrigin authenticationOrigin) throws CatalogException { + if (authenticationOrigin.getType() != AuthenticationOrigin.AuthenticationType.SSO) { + throw new CatalogException("Unknown authentication type. Expected type '" + AuthenticationOrigin.AuthenticationType.SSO + + "' but received '" + authenticationOrigin.getType() + "'."); + } + } + + @Override + public AuthenticationResponse authenticate(String organizationId, String userId, String password) + throws CatalogAuthenticationException { + throw new NotImplementedException("Authentication should be done through SSO"); + } + + @Override + public List getUsersFromRemoteGroup(String group) throws CatalogException { + throw new NotImplementedException("Operation not implemented"); + } + + @Override + public List getRemoteUserInformation(List userStringList) throws CatalogException { + throw new NotImplementedException("Operation not implemented"); + } + + @Override + public List getRemoteGroups(String token) throws CatalogException { + throw new NotImplementedException("Operation not implemented"); + } + + @Override + public void changePassword(String organizationId, String userId, String oldPassword, String newPassword) throws CatalogException { + throw new NotImplementedException("Change password should be done through SSO"); + } + + @Override + public OpenCGAResult resetPassword(String organizationId, String userId) throws CatalogException { + throw new NotImplementedException("Reset password should be done through SSO"); + } + + @Override + public void newPassword(String organizationId, String userId, String newPassword) throws CatalogException { + throw new NotImplementedException("Setting a new password should be done through SSO"); + } + + @Override + public String createToken(String organizationId, String userId, Map claims, long expiration) { + return jwtManager.createJWTToken(organizationId, AuthenticationOrigin.AuthenticationType.SSO, userId, claims, expiration); + } + + @Override + public String createNonExpiringToken(String organizationId, String userId, Map claims) { + return jwtManager.createJWTToken(organizationId, AuthenticationOrigin.AuthenticationType.SSO, userId, claims, 0L); + } + + @Override + public void close() throws IOException { + + } +} diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java index d1b1246e468..3acc8124f97 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java @@ -2,14 +2,12 @@ import org.apache.commons.collections4.CollectionUtils; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.catalog.auth.authentication.AuthenticationManager; -import org.opencb.opencga.catalog.auth.authentication.AzureADAuthenticationManager; -import org.opencb.opencga.catalog.auth.authentication.CatalogAuthenticationManager; -import org.opencb.opencga.catalog.auth.authentication.LDAPAuthenticationManager; +import org.opencb.opencga.catalog.auth.authentication.*; import org.opencb.opencga.catalog.db.DBAdaptorFactory; import org.opencb.opencga.catalog.db.api.OrganizationDBAdaptor; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.managers.OrganizationManager; +import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.config.AuthenticationOrigin; import org.opencb.opencga.core.config.Email; import org.opencb.opencga.core.models.organizations.Organization; @@ -19,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -64,6 +63,10 @@ public void configureOrganizationAuthenticationManager(Organization organization tmpAuthenticationManagerMap.put(CatalogAuthenticationManager.INTERNAL, catalogAuthenticationManager); tmpAuthenticationManagerMap.put(CatalogAuthenticationManager.OPENCGA, catalogAuthenticationManager); break; + case SSO: + tmpAuthenticationManagerMap.put(authOrigin.getId(), new SSOAuthenticationManager(algorithm, secretKey, + expiration)); + break; default: logger.warn("Unexpected authentication origin type '{}' for id '{}' found in organization '{}'. " + "Authentication origin will be ignored.", authOrigin.getType(), organization.getId(), @@ -76,6 +79,18 @@ public void configureOrganizationAuthenticationManager(Organization organization if (tmpAuthenticationManagerMap.isEmpty()) { throw new CatalogException("No authentication origin found for organization '" + organization.getId() + "'"); } + if (authenticationManagerMap.containsKey(organization.getId())) { + for (AuthenticationManager authenticationManager : authenticationManagerMap.get(organization.getId()).values()) { + try { + logger.info("Closing previous authentication manager for organization '{}'", organization.getId()); + authenticationManager.close(); + } catch (IOException e) { + throw new CatalogException("Unable to close previous authentication manager for organization '" + organization.getId() + + "'.", e); + } + } + logger.info("Reloading new set of AuthenticationManagers for organization '{}'", organization.getId()); + } authenticationManagerMap.put(organization.getId(), tmpAuthenticationManagerMap); } @@ -147,4 +162,25 @@ public AuthenticationManager getOrganizationAuthenticationManager(String organiz return organizationAuthenticationManagers.get(authOriginId); } + public void validateAuthenticationOrigin(AuthenticationOrigin authenticationOrigin) throws CatalogException { + ParamUtils.checkParameter(authenticationOrigin.getId(), "authentication origin id"); + ParamUtils.checkObj(authenticationOrigin.getType(), "authentication origin type"); + switch (authenticationOrigin.getType()) { + case OPENCGA: + CatalogAuthenticationManager.validateAuthenticationOriginConfiguration(authenticationOrigin); + break; + case LDAP: + LDAPAuthenticationManager.validateAuthenticationOriginConfiguration(authenticationOrigin); + break; + case AzureAD: + AzureADAuthenticationManager.validateAuthenticationOriginConfiguration(authenticationOrigin); + break; + case SSO: + SSOAuthenticationManager.validateAuthenticationOriginConfiguration(authenticationOrigin); + break; + default: + throw new CatalogException("Unknown authentication origin type '" + authenticationOrigin.getType() + "'"); + } + } + } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java index b58c117d94f..7022be4a0e7 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java @@ -19,6 +19,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.Query; +import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.catalog.db.DBAdaptorFactory; import org.opencb.opencga.catalog.db.api.*; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; @@ -79,7 +80,34 @@ public void checkCanEditProject(String organizationId, long projectId, String us if (isAtLeastOrganizationOwnerOrAdmin(organizationId, userId)) { return; } - throw new CatalogAuthorizationException("Permission denied: Only the organization owner or administrators can update the project."); + try { + checkUserIsStudyAdminInAllStudiesOfProject(organizationId, projectId, userId); + } catch (CatalogException e) { + throw new CatalogAuthorizationException("Permission denied: Only the organization owner, administrators, or users who are " + + "study administrators in all studies within the project can update the project.", e); + } + } + + private void checkUserIsStudyAdminInAllStudiesOfProject(String organizationId, long projectUid, String userId) throws CatalogException { + Query query = new Query(StudyDBAdaptor.QueryParams.PROJECT_UID.key(), projectUid); + QueryOptions options = new QueryOptions(QueryOptions.INCLUDE, + Arrays.asList(StudyDBAdaptor.QueryParams.GROUPS.key(), StudyDBAdaptor.QueryParams.FQN.key())); + OpenCGAResult studyResult = dbAdaptorFactory.getCatalogStudyDBAdaptor(organizationId).get(query, options); + List nonAdminStudyIds = new ArrayList<>(); + for (Study study : studyResult.getResults()) { + for (Group group : study.getGroups()) { + if (group.getId().equals(ADMINS_GROUP)) { + if (!group.getUserIds().contains(userId)) { + nonAdminStudyIds.add(study.getFqn()); + } + break; + } + } + } + if (!nonAdminStudyIds.isEmpty()) { + throw new CatalogAuthorizationException("Permission denied: User " + userId + " is not an administrator of the following" + + " studies: " + String.join(", ", nonAdminStudyIds)); + } } @Override diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/FileDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/FileDBAdaptor.java index e138ae26dbf..2ffe6fd1ee9 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/FileDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/FileDBAdaptor.java @@ -68,6 +68,7 @@ enum QueryParams implements QueryParam { INTERNAL_STATUS_DESCRIPTION("internal.status.description", TEXT, ""), INTERNAL_STATUS_DATE("internal.status.date", TEXT, ""), RELATED_FILES("relatedFiles", TEXT_ARRAY, ""), + RELATED_FILES_FILE_UID("relatedFiles.file.uid", LONG, ""), RELATED_FILES_RELATION("relatedFiles.relation", TEXT, ""), SIZE("size", INTEGER, ""), EXPERIMENT("experiment", OBJECT, ""), diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/OrganizationDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/OrganizationDBAdaptor.java index f9175b0a908..d8d8e8a0a51 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/OrganizationDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/OrganizationDBAdaptor.java @@ -17,6 +17,7 @@ public interface OrganizationDBAdaptor extends Iterable { String IS_ORGANIZATION_ADMIN_OPTION = "isOrgAdmin"; + String AUTH_ORIGINS_FIELD = "authenticationOrigins"; enum QueryParams implements QueryParam { UID("uid", LONG, ""), @@ -28,8 +29,10 @@ enum QueryParams implements QueryParam { INTERNAL("internal", OBJECT, ""), INTERNAL_MIGRATION_EXECUTIONS("internal.migrationExecutions", OBJECT, ""), CONFIGURATION("configuration", OBJECT, ""), - CONFIGURATION_AUTHENTICATION_ORIGINS("configuration.authenticationOrigins", OBJECT, ""), - CONFIGURATION_AUTHENTICATION_ORIGINS_OPTIONS("configuration.authenticationOrigins.options", OBJECT, ""), + CONFIGURATION_OPTIMIZATIONS("configuration.optimizations", OBJECT, ""), + CONFIGURATION_AUTHENTICATION_ORIGINS("configuration." + AUTH_ORIGINS_FIELD, OBJECT, ""), + CONFIGURATION_AUTHENTICATION_ORIGINS_ID("configuration." + AUTH_ORIGINS_FIELD + ".id", STRING, ""), + CONFIGURATION_AUTHENTICATION_ORIGINS_OPTIONS("configuration." + AUTH_ORIGINS_FIELD + ".options", OBJECT, ""), CONFIGURATION_TOKEN("configuration.token", OBJECT, ""), CREATION_DATE("creationDate", DATE, ""), MODIFICATION_DATE("modificationDate", DATE, ""), diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java index a4073a8c192..a2c4848fca1 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java @@ -31,9 +31,7 @@ import org.opencb.commons.datastore.core.*; import org.opencb.commons.datastore.mongodb.MongoDBCollection; import org.opencb.commons.datastore.mongodb.MongoDBIterator; -import org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor; -import org.opencb.opencga.catalog.db.api.DBIterator; -import org.opencb.opencga.catalog.db.api.InterpretationDBAdaptor; +import org.opencb.opencga.catalog.db.api.*; import org.opencb.opencga.catalog.db.mongodb.converters.ClinicalAnalysisConverter; import org.opencb.opencga.catalog.db.mongodb.iterators.ClinicalAnalysisCatalogMongoDBIterator; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; @@ -151,6 +149,10 @@ static void fixFilesForRemoval(ObjectMap parameters, String key) { for (Object file : parameters.getAsList(key)) { if (file instanceof File) { fileParamList.add(new Document("uid", ((File) file).getUid())); + } else if (file instanceof Document) { + fileParamList.add(new Document("uid", ((Document) file).get("uid"))); + } else { + throw new IllegalArgumentException("Expected a File or Document object"); } } parameters.put(key, fileParamList); @@ -814,6 +816,25 @@ OpenCGAResult privateDelete(ClientSession clientSession, ClinicalAnalysis cli return endWrite(tmpStartTime, 1, 0, 0, 1, Collections.emptyList()); } + void removeFileReferences(ClientSession clientSession, long studyUid, long fileUid, Document file) + throws CatalogParameterException, CatalogDBException, CatalogAuthorizationException { + ObjectMap parameters = new ObjectMap(FILES.key(), Collections.singletonList(file)); + ObjectMap actionMap = new ObjectMap(FILES.key(), ParamUtils.BasicUpdateAction.REMOVE); + QueryOptions options = new QueryOptions(Constants.ACTIONS, actionMap); + + Query query = new Query() + .append(QueryParams.STUDY_UID.key(), studyUid) + .append(QueryParams.FILES_UID.key(), fileUid); + OpenCGAResult result = get(query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS); + for (ClinicalAnalysis clinicalAnalysis : result.getResults()) { + logger.debug("Removing file references from Clinical Analysis {}", clinicalAnalysis.getId()); + ClinicalAudit clinicalAudit = new ClinicalAudit("OPENCGA", ClinicalAudit.Action.UPDATE_CLINICAL_ANALYSIS, "File " + + file.getString(FileDBAdaptor.QueryParams.PATH.key()) + " was deleted. Remove file references from case.", + TimeUtils.getTime()); + transactionalUpdate(clientSession, clinicalAnalysis, parameters, null, Collections.singletonList(clinicalAudit), options); + } + } + @Override public OpenCGAResult restore(long id, QueryOptions queryOptions) throws CatalogDBException { return null; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java index 5c3b592e9b9..5b84d050083 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java @@ -40,6 +40,7 @@ import org.opencb.opencga.catalog.managers.FileUtils; import org.opencb.opencga.catalog.managers.SampleManager; import org.opencb.opencga.catalog.utils.Constants; +import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.catalog.utils.ParamUtils.BasicUpdateAction; import org.opencb.opencga.catalog.utils.UuidUtils; import org.opencb.opencga.core.api.ParamConstants; @@ -785,7 +786,8 @@ private UpdateDocument getValidatedUpdateParams(ClientSession clientSession, lon document.getSet().put(QueryParams.RELATED_FILES.key(), relatedFileDocument); break; case REMOVE: - document.getPullAll().put(QueryParams.RELATED_FILES.key(), relatedFileDocument); + List documentList = fixRelatedFilesForRemoval(relatedFileDocument); + document.getPull().put(QueryParams.RELATED_FILES.key(), documentList); break; case ADD: document.getAddToSet().put(QueryParams.RELATED_FILES.key(), relatedFileDocument); @@ -886,6 +888,18 @@ private UpdateDocument getValidatedUpdateParams(ClientSession clientSession, lon return document; } + private List fixRelatedFilesForRemoval(List relatedFiles) { + if (CollectionUtils.isEmpty(relatedFiles)) { + return Collections.emptyList(); + } + + List relatedFilesCopy = new ArrayList<>(relatedFiles.size()); + for (Document relatedFile : relatedFiles) { + relatedFilesCopy.add(new Document("file", new Document("uid", relatedFile.get("file", Document.class).get("uid")))); + } + return relatedFilesCopy; + } + @Override public OpenCGAResult delete(File file) throws CatalogDBException { throw new UnsupportedOperationException("Use delete passing status field."); @@ -1007,7 +1021,9 @@ OpenCGAResult privateDelete(ClientSession clientSession, Document fileDo Document tmpFile = iterator.next(); long tmpFileUid = tmpFile.getLong(PRIVATE_UID); + removeFileReferences(clientSession, studyUid, tmpFileUid, tmpFile); dbAdaptorFactory.getCatalogJobDBAdaptor().removeFileReferences(clientSession, studyUid, tmpFileUid, tmpFile); + dbAdaptorFactory.getClinicalAnalysisDBAdaptor().removeFileReferences(clientSession, studyUid, tmpFileUid, tmpFile); // Set status nestedPut(QueryParams.INTERNAL_STATUS.key(), getMongoDBDocument(new FileStatus(status), "status"), tmpFile); @@ -1035,6 +1051,28 @@ OpenCGAResult privateDelete(ClientSession clientSession, Document fileDo } } + void removeFileReferences(ClientSession clientSession, long studyUid, long fileUid, Document fileDoc) + throws CatalogParameterException, CatalogDBException, CatalogAuthorizationException { + File file = fileConverter.convertToDataModelType(fileDoc); + FileRelatedFile relatedFile = new FileRelatedFile(file, null); + ObjectMap parameters = new ObjectMap(QueryParams.RELATED_FILES.key(), Collections.singletonList(relatedFile)); + ObjectMap actionMap = new ObjectMap(QueryParams.RELATED_FILES.key(), ParamUtils.BasicUpdateAction.REMOVE); + QueryOptions options = new QueryOptions(Constants.ACTIONS, actionMap); + + Query query = new Query() + .append(QueryParams.STUDY_UID.key(), studyUid) + .append(QueryParams.RELATED_FILES_FILE_UID.key(), fileUid); + UpdateDocument updateDocument = getValidatedUpdateParams(clientSession, studyUid, parameters, query, options); + Document updateDoc = updateDocument.toFinalUpdateDocument(); + if (!updateDoc.isEmpty()) { + Bson bsonQuery = parseQuery(query); + OpenCGAResult result = transactionalUpdate(clientSession, studyUid, bsonQuery, updateDocument); + if (result.getNumUpdated() > 0) { + logger.debug("File '{}' removed from related files array from {} files.", file.getPath(), result.getNumUpdated()); + } + } + } + // OpenCGAResult privateDelete(ClientSession clientSession, File file, String status) throws CatalogDBException { // long tmpStartTime = startQuery(); // logger.debug("Deleting file {} ({})", file.getPath(), file.getUid()); @@ -1479,13 +1517,6 @@ private Bson parseQuery(Query query, Document extraQuery, String user) addAutoOrQuery(queryParam.key(), queryParam.key(), myQuery, queryParam.type(), andBsonList); } break; - case FORMAT: - case BIOFORMAT: - // Replace the value for an uppercase string as we know it will always be in that way - String uppercaseValue = myQuery.getString(queryParam.key()).toUpperCase(); - myQuery.put(queryParam.key(), uppercaseValue); - addAutoOrQuery(queryParam.key(), queryParam.key(), myQuery, queryParam.type(), andBsonList); - break; case UUID: case EXTERNAL: case TYPE: @@ -1493,9 +1524,12 @@ private Bson parseQuery(Query query, Document extraQuery, String user) case ID: case PATH: case RELEASE: + case FORMAT: + case BIOFORMAT: case TAGS: case SIZE: case SOFTWARE_NAME: + case RELATED_FILES_FILE_UID: case JOB_ID: addAutoOrQuery(queryParam.key(), queryParam.key(), myQuery, queryParam.type(), andBsonList); break; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptorFactory.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptorFactory.java index 47b6e96e406..621c537cddd 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptorFactory.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptorFactory.java @@ -31,16 +31,16 @@ import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; +import org.opencb.opencga.catalog.io.IOManagerFactory; import org.opencb.opencga.catalog.managers.NoteManager; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.common.JacksonUtils; -import org.opencb.opencga.catalog.io.IOManagerFactory; import org.opencb.opencga.core.config.Admin; import org.opencb.opencga.core.config.Configuration; -import org.opencb.opencga.core.models.organizations.Organization; -import org.opencb.opencga.core.models.organizations.OrganizationSummary; import org.opencb.opencga.core.models.notes.Note; import org.opencb.opencga.core.models.notes.NoteCreateParams; +import org.opencb.opencga.core.models.organizations.Organization; +import org.opencb.opencga.core.models.organizations.OrganizationSummary; import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java index 4261a247c79..4bb4d663e44 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java @@ -24,6 +24,7 @@ import org.opencb.opencga.catalog.utils.UuidUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.common.TimeUtils; +import org.opencb.opencga.core.config.AuthenticationOrigin; import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.models.organizations.Organization; import org.opencb.opencga.core.response.OpenCGAResult; @@ -137,11 +138,7 @@ private OpenCGAResult privateUpdate(ClientSession clientSession, S UpdateDocument updateDocument = getValidatedUpdateParams(clientSession, parameters, queryOptions); Document organizationUpdate = updateDocument.toFinalUpdateDocument(); -// Query tmpQuery = new Query(QueryParams.ID.key(), organization.getId()); -// Bson queryBson = parseQuery(tmpQuery); - Bson queryBson = Filters.eq(QueryParams.ID.key(), organizationId); - - if (organizationUpdate.isEmpty()) { + if (organizationUpdate.isEmpty() && CollectionUtils.isEmpty(updateDocument.getNestedUpdateList())) { if (!parameters.isEmpty()) { logger.error("Non-processed update parameters: {}", parameters.keySet()); throw new CatalogDBException("Update could not be performed. Some fields could not be processed."); @@ -149,20 +146,52 @@ private OpenCGAResult privateUpdate(ClientSession clientSession, S throw new CatalogDBException("Nothing to be updated"); } - List events = new ArrayList<>(); - logger.debug("Update organization. Query: {}, Update: {}", queryBson.toBsonDocument(), organizationUpdate.toBsonDocument()); - // Update study admins need to be executed before the actual update because we need to fetch the previous owner/admins in case // of an update on these fields. updateStudyAdmins(clientSession, parameters, queryOptions); - DataResult updateResult = organizationCollection.update(clientSession, queryBson, organizationUpdate, null); - if (updateResult.getNumMatches() == 0) { - throw new CatalogDBException("Organization not found"); + List events = new ArrayList<>(); + DataResult updateResult; + if (!organizationUpdate.isEmpty()) { + Bson queryBson = Filters.eq(QueryParams.ID.key(), organizationId); + logger.debug("Update organization. Query: {}, Update: {}", queryBson.toBsonDocument(), organizationUpdate.toBsonDocument()); + + updateResult = organizationCollection.update(clientSession, queryBson, organizationUpdate, null); + + if (updateResult.getNumMatches() == 0) { + throw new CatalogDBException("Organization not found"); + } + if (updateResult.getNumUpdated() == 0) { + events.add(new Event(Event.Type.WARNING, organizationId, "Organization was already updated")); + } } - if (updateResult.getNumUpdated() == 0) { - events.add(new Event(Event.Type.WARNING, organizationId, "Organization was already updated")); + if (CollectionUtils.isNotEmpty(updateDocument.getNestedUpdateList())) { + for (NestedArrayUpdateDocument nestedDocument : updateDocument.getNestedUpdateList()) { + Bson bsonQuery = new Document(nestedDocument.getQuery()); + logger.debug("Update nested element from Organization. Query: {}, Update: {}", bsonQuery.toBsonDocument(), + nestedDocument.getSet()); + DataResult result = organizationCollection.update(clientSession, bsonQuery, nestedDocument.getSet(), null); + if (result.getNumMatches() == 0) { + throw new CatalogDBException("Couldn't update organization. Nothing could be found for query " + + bsonQuery.toBsonDocument()); + } + } + /* + for (NestedArrayUpdateDocument nestedDocument : updateDocument.getNestedUpdateList()) { + Bson nestedBsonQuery = parseQuery(nestedDocument.getQuery() + .append(QueryParams.UID.key(), interpretation.getUid())); + logger.debug("Update nested element from interpretation. Query: {}, Update: {}", + nestedBsonQuery.toBsonDocument(), nestedDocument.getSet()); + + update = interpretationCollection.update(clientSession, nestedBsonQuery, nestedDocument.getSet(), null); + + if (update.getNumMatches() == 0) { + throw CatalogDBException.uidNotFound("Interpretation", interpretationUid); + } + } + * */ } + logger.debug("Organization {} successfully updated", organizationId); @@ -235,9 +264,34 @@ private UpdateDocument getValidatedUpdateParams(ClientSession clientSession, Obj String[] acceptedParams = { QueryParams.NAME.key() }; filterStringParams(parameters, document.getSet(), acceptedParams); - String[] acceptedObjectParams = { QueryParams.CONFIGURATION.key() }; + String[] acceptedObjectParams = { QueryParams.CONFIGURATION_OPTIMIZATIONS.key(), QueryParams.CONFIGURATION_TOKEN.key() }; filterObjectParams(parameters, document.getSet(), acceptedObjectParams); + // Authentication Origins action + if (parameters.containsKey(QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key())) { + Map actionMap = queryOptions.getMap(Constants.ACTIONS, new HashMap<>()); + ParamUtils.UpdateAction operation = ParamUtils.UpdateAction.from(actionMap, OrganizationDBAdaptor.AUTH_ORIGINS_FIELD); + String[] authOriginsParams = {QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key()}; + switch (operation) { + case SET: + filterObjectParams(parameters, document.getSet(), authOriginsParams); + break; + case REMOVE: + fixAuthOriginsForRemoval(parameters); + filterObjectParams(parameters, document.getPull(), authOriginsParams); + break; + case ADD: + filterObjectParams(parameters, document.getAddToSet(), authOriginsParams); + break; + case REPLACE: + filterReplaceParams(parameters.getAsList(QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key(), Map.class), document, + m -> String.valueOf(m.get("id")), QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS_ID.key()); + break; + default: + throw new IllegalStateException("Unknown operation " + operation); + } + } + String owner = parameters.getString(QueryParams.OWNER.key(), null); if (StringUtils.isNotEmpty(owner)) { // Check user exists @@ -312,6 +366,21 @@ private UpdateDocument getValidatedUpdateParams(ClientSession clientSession, Obj return document; } + private void fixAuthOriginsForRemoval(ObjectMap parameters) { + if (parameters.get(QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key()) == null) { + return; + } + List authOriginParamList = new LinkedList<>(); + for (Object authOrigin : parameters.getAsList(QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key())) { + if (authOrigin instanceof AuthenticationOrigin) { + authOriginParamList.add(new Document("id", ((AuthenticationOrigin) authOrigin).getId())); + } else { + authOriginParamList.add(new Document("id", ((Map) authOrigin).get("id"))); + } + } + parameters.putNested(QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key(), authOriginParamList, false); + } + @Override public OpenCGAResult delete(Organization organization) throws CatalogDBException { return null; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ProjectMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ProjectMongoDBAdaptor.java index c1c8dfeb7bd..89d90aab932 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ProjectMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ProjectMongoDBAdaptor.java @@ -542,7 +542,7 @@ public OpenCGAResult get(Query query, QueryOptions options, String user } @Override - public OpenCGAResult nativeGet(Query query, QueryOptions options) throws CatalogDBException { + public OpenCGAResult nativeGet(Query query, QueryOptions options) throws CatalogDBException { return nativeGet(null, query, options); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/VersionedMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/VersionedMongoDBAdaptor.java index 85306f33571..c50446677f7 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/VersionedMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/VersionedMongoDBAdaptor.java @@ -173,6 +173,8 @@ protected OpenCGAResult update(ClientSession session, Bson sourceQuery, L Document result = iterator.next(); entryList.add(result); + entryList.add(result); + long uid = result.get(PRIVATE_UID, Number.class).longValue(); int version = result.get(VERSION, Number.class).intValue(); String transactionId = result.getString(PRIVATE_TRANSACTION_ID); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/FileConverter.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/FileConverter.java index 267666f77f0..c4db86b7408 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/FileConverter.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/FileConverter.java @@ -107,7 +107,7 @@ public List convertRelatedFiles(List relatedFileList) if (ListUtils.isNotEmpty(relatedFileList)) { for (FileRelatedFile relatedFile : relatedFileList) { relatedFiles.add(new Document() - .append("relation", relatedFile.getRelation().name()) + .append("relation", relatedFile.getRelation() != null ? relatedFile.getRelation().name() : null) .append("file", new Document("uid", relatedFile.getFile().getUid())) ); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java index 2308b7ad123..ec8c3dcf2ad 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java @@ -301,7 +301,7 @@ public void installIndexes(String token) throws CatalogException { public void installIndexes(String organizationId, String token) throws CatalogException { JwtPayload payload = userManager.validateToken(token); - String userId = payload.getUserId(); + String userId = payload.getUserId(organizationId); if (!authorizationManager.isAtLeastOrganizationOwnerOrAdmin(organizationId, userId)) { throw CatalogAuthorizationException.notOrganizationOwnerOrAdmin(); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileManager.java index 48ee0d6adda..37be3aeeba2 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileManager.java @@ -33,10 +33,7 @@ import org.opencb.commons.utils.ListUtils; import org.opencb.opencga.catalog.auth.authorization.AuthorizationManager; import org.opencb.opencga.catalog.db.DBAdaptorFactory; -import org.opencb.opencga.catalog.db.api.DBIterator; -import org.opencb.opencga.catalog.db.api.FileDBAdaptor; -import org.opencb.opencga.catalog.db.api.SampleDBAdaptor; -import org.opencb.opencga.catalog.db.api.StudyDBAdaptor; +import org.opencb.opencga.catalog.db.api.*; import org.opencb.opencga.catalog.db.mongodb.OrganizationMongoDBAdaptorFactory; import org.opencb.opencga.catalog.exceptions.*; import org.opencb.opencga.catalog.io.IOManager; @@ -1580,7 +1577,7 @@ public OpenCGAResult delete(String studyStr, List fileIds, QueryOptions return delete(studyStr, fileIds, options, false, token); } - public OpenCGAResult delete(String studyStr, List fileIds, ObjectMap params, boolean ignoreException, String token) + public OpenCGAResult delete(String studyStr, List fileIds, QueryOptions options, boolean ignoreException, String token) throws CatalogException { JwtPayload tokenPayload = catalogManager.getUserManager().validateToken(token); CatalogFqn studyFqn = CatalogFqn.extractFqnFromStudy(studyStr, tokenPayload); @@ -1594,13 +1591,15 @@ public OpenCGAResult delete(String studyStr, List fileIds, ObjectMap par ObjectMap auditParams = new ObjectMap() .append("study", studyStr) .append("fileIds", fileIds) - .append("params", params) + .append("options", options) .append("ignoreException", ignoreException) .append("token", token); + QueryOptions queryOptions = options != null ? new QueryOptions(options) : new QueryOptions(); + // We need to avoid processing subfolders or subfiles of an already processed folder independently Set processedPaths = new HashSet<>(); - boolean physicalDelete = params.getBoolean(Constants.SKIP_TRASH, false); + boolean physicalDelete = queryOptions.getBoolean(Constants.SKIP_TRASH, false); auditManager.initAuditBatch(operationUuid); OpenCGAResult result = OpenCGAResult.empty(File.class); @@ -1888,10 +1887,10 @@ public OpenCGAResult unlink(@Nullable String studyId, String fileId, Strin study.getId(), study.getUuid(), auditParams, new AuditRecord.Status(AuditRecord.Status.Result.SUCCESS)); return result; - } catch (CatalogException e) { + } catch (Exception e) { auditManager.audit(organizationId, userId, Enums.Action.UNLINK, Enums.Resource.FILE, fileId, "", study.getId(), study.getUuid(), - auditParams, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, e.getError())); - throw e; + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, new Error(0, "", e.getMessage()))); + throw new CatalogException("Could not unlink file '" + fileId + "'", e); } } @@ -3231,6 +3230,16 @@ private void checkCanDeleteFile(String organizationId, Study study, String fileI // TODO: Validate no file/folder within any registered directory is not registered in OpenCGA } + Query clinicalQuery = new Query() + .append(ClinicalAnalysisDBAdaptor.QueryParams.STUDY_UID.key(), study.getUid()) + .append(ClinicalAnalysisDBAdaptor.QueryParams.FILES_UID.key(), file.getUid()) + .append(ClinicalAnalysisDBAdaptor.QueryParams.LOCKED.key(), true); + OpenCGAResult count = getClinicalAnalysisDBAdaptor(organizationId).count(clinicalQuery); + if (count.getNumMatches() > 0) { + throw new CatalogException("The file " + file.getName() + " is part of " + count.getNumMatches() + " clinical analyses"); + } + + // Check the original files are not being indexed at the moment if (!indexFiles.isEmpty()) { Query query = new Query(FileDBAdaptor.QueryParams.UID.key(), new ArrayList<>(indexFiles)); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/JobManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/JobManager.java index 7a8f4ad0f73..524265e69c0 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/JobManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/JobManager.java @@ -42,6 +42,7 @@ import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.Configuration; +import org.opencb.opencga.core.config.Execution; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.AclParams; import org.opencb.opencga.core.models.JwtPayload; @@ -589,7 +590,7 @@ private OpenCGAResult getJobToReuse(String organizationId, Study study, Str private boolean jobEligibleToReuse(String inputJobId, Job job) { boolean enabled = configuration.getAnalysis().getExecution().getOptions() - .getBoolean("jobs.reuse.enabled", true); + .getBoolean(Execution.JOBS_REUSE_ENABLED, Execution.JOBS_REUSE_ENABLED_DEFAULT); if (!enabled) { return false; } @@ -600,9 +601,9 @@ private boolean jobEligibleToReuse(String inputJobId, Job job) { } List availableTools = configuration.getAnalysis().getExecution().getOptions() - .getAsStringList("jobs.reuse.tools"); + .getAsStringList(Execution.JOBS_REUSE_TOOLS); if (availableTools.isEmpty()) { - availableTools = Collections.singletonList("variant-.*"); + availableTools = Execution.JOBS_REUSE_TOOLS_DEFAULT; } String toolId = job.getTool().getId(); boolean validTool = false; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java index f89d0595c30..a1a49273086 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java @@ -1,6 +1,7 @@ package org.opencb.opencga.catalog.managers; import com.fasterxml.jackson.core.JsonProcessingException; +import io.jsonwebtoken.security.InvalidKeyException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.Event; @@ -17,6 +18,8 @@ import org.opencb.opencga.catalog.exceptions.CatalogIOException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.catalog.io.CatalogIOManager; +import org.opencb.opencga.catalog.utils.Constants; +import org.opencb.opencga.catalog.utils.JwtUtils; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.catalog.utils.UuidUtils; import org.opencb.opencga.core.api.ParamConstants; @@ -34,6 +37,9 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.stream.Collectors; + +import static org.opencb.opencga.core.common.JacksonUtils.getUpdateObjectMapper; public class OrganizationManager extends AbstractManager { @@ -257,33 +263,6 @@ public OpenCGAResult update(String organizationId, OrganizationUpd // We set the proper values for the audit organizationId = organization.getId(); - // Avoid duplicated authentication origin ids. - // Validate all mandatory authentication origin fields are filled in. - if (updateParams.getConfiguration() != null && updateParams.getConfiguration().getAuthenticationOrigins() != null) { - String authOriginsPrefixKey = OrganizationDBAdaptor.QueryParams.CONFIGURATION_AUTHENTICATION_ORIGINS.key(); - boolean internal = false; - Set authenticationOriginIds = new HashSet<>(); - for (AuthenticationOrigin authenticationOrigin : updateParams.getConfiguration().getAuthenticationOrigins()) { - if (authenticationOrigin.getType().equals(AuthenticationOrigin.AuthenticationType.OPENCGA)) { - if (internal) { - throw new CatalogException("Found duplicated authentication origin of type OPENCGA."); - } - internal = true; - // Set id to INTERNAL - authenticationOrigin.setId(CatalogAuthenticationManager.OPENCGA); - } - ParamUtils.checkIdentifier(authenticationOrigin.getId(), authOriginsPrefixKey + ".id"); - ParamUtils.checkObj(authenticationOrigin.getType(), authOriginsPrefixKey + ".type"); - if (authenticationOriginIds.contains(authenticationOrigin.getId())) { - throw new CatalogException("Found duplicated authentication origin id '" + authenticationOrigin.getId() + "'."); - } - authenticationOriginIds.add(authenticationOrigin.getId()); - } - if (!internal) { - throw new CatalogException("Missing mandatory AuthenticationOrigin of type OPENCGA."); - } - } - result = getOrganizationDBAdaptor(organizationId).update(organizationId, updateMap, options); auditManager.auditUpdate(organizationId, userId, Enums.Resource.ORGANIZATION, organization.getId(), organization.getUuid(), "", @@ -309,6 +288,180 @@ public OpenCGAResult update(String organizationId, OrganizationUpd return result; } + public OpenCGAResult updateConfiguration(String organizationId, OrganizationConfiguration updateParams, + QueryOptions options, String token) throws CatalogException { + JwtPayload tokenPayload = catalogManager.getUserManager().validateToken(token); + String userId = tokenPayload.getUserId(organizationId); + + ObjectMap auditParams = new ObjectMap() + .append("organizationId", organizationId) + .append("updateParams", updateParams) + .append("options", options) + .append("token", token); + + QueryOptions queryOptions = options != null ? new QueryOptions(options) : new QueryOptions(); + OpenCGAResult result = OpenCGAResult.empty(OrganizationConfiguration.class); + try { + authorizationManager.checkIsAtLeastOrganizationOwnerOrAdmin(organizationId, userId); + + ParamUtils.checkObj(updateParams, "OrganizationConfiguration"); + ObjectMap updateConfigurationMap; + try { + updateConfigurationMap = new ObjectMap(getUpdateObjectMapper().writeValueAsString(updateParams)); + } catch (JsonProcessingException e) { + throw new CatalogException("Could not parse OrganizationConfiguration object: " + e.getMessage(), e); + } + + OpenCGAResult internalResult = get(organizationId, INCLUDE_ORGANIZATION_CONFIGURATION, token); + if (internalResult.getNumResults() == 0) { + throw new CatalogException("Organization '" + organizationId + "' not found"); + } + Organization organization = internalResult.first(); + + // We set the proper values for the audit + organizationId = organization.getId(); + + if (CollectionUtils.isNotEmpty(updateParams.getAuthenticationOrigins())) { + // Check action + ParamUtils.UpdateAction authOriginsAction = null; + Map map = queryOptions.getMap(Constants.ACTIONS); + if (map == null || map.get(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD) == null) { + // Write default option + authOriginsAction = ParamUtils.UpdateAction.ADD; + Map actionMap = new HashMap<>(); + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, authOriginsAction); + queryOptions.put(Constants.ACTIONS, actionMap); + } else { + authOriginsAction = ParamUtils.UpdateAction.valueOf(map.get(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD).toString()); + } + + Set currentAuthOriginIds = organization.getConfiguration().getAuthenticationOrigins() + .stream() + .map(AuthenticationOrigin::getId) + .collect(Collectors.toSet()); + + Set updateAuthOriginIds = new HashSet<>(); + StringBuilder authOriginUpdateBuilder = new StringBuilder(); + List authenticationOrigins = updateParams.getAuthenticationOrigins(); + for (int i = 0; i < authenticationOrigins.size(); i++) { + AuthenticationOrigin authenticationOrigin = authenticationOrigins.get(i); + ParamUtils.checkParameter(authenticationOrigin.getId(), "AuthenticationOrigin id"); + ParamUtils.checkObj(authenticationOrigin.getType(), "AuthenticationOrigin type"); + if (updateAuthOriginIds.contains(authenticationOrigin.getId())) { + throw new CatalogParameterException("Found duplicated authentication origin id '" + authenticationOrigin.getId() + + "'."); + } + // Check authOrigin OPENCGA-OPENCGA + if ((authenticationOrigin.getType().equals(AuthenticationOrigin.AuthenticationType.OPENCGA) + && !CatalogAuthenticationManager.OPENCGA.equals(authenticationOrigin.getId())) + || (!authenticationOrigin.getType().equals(AuthenticationOrigin.AuthenticationType.OPENCGA) + && CatalogAuthenticationManager.OPENCGA.equals(authenticationOrigin.getId()))) { + throw new CatalogParameterException("AuthenticationOrigin type '" + AuthenticationOrigin.AuthenticationType.OPENCGA + + "' must go together with id '" + CatalogAuthenticationManager.OPENCGA + "'."); + } + updateAuthOriginIds.add(authenticationOrigin.getId()); + if (i > 0) { + authOriginUpdateBuilder.append(", "); + } + authOriginUpdateBuilder.append(authenticationOrigin.getType()).append(": ").append(authenticationOrigin.getId()); + + if (authOriginsAction != ParamUtils.UpdateAction.REMOVE) { + // Validate configuration is correct and can be used + authenticationFactory.validateAuthenticationOrigin(authenticationOrigin); + } + } + + switch (authOriginsAction) { + case ADD: + for (AuthenticationOrigin authenticationOrigin : updateParams.getAuthenticationOrigins()) { + if (currentAuthOriginIds.contains(authenticationOrigin.getId())) { + throw new CatalogException("Authentication origin '" + authenticationOrigin.getId() + "' already exists. " + + "Please, set the authOriginsAction to 'REPLACE' to replace the current configuration."); + } + } + logger.debug("Adding new list of Authentication Origins: {}.", authOriginUpdateBuilder); + break; + case SET: + boolean userAuthOriginFound = false; + for (AuthenticationOrigin authenticationOrigin : updateParams.getAuthenticationOrigins()) { + if (tokenPayload.getAuthOrigin().equals(authenticationOrigin.getType())) { + userAuthOriginFound = true; + } + } + if (!userAuthOriginFound) { + throw new CatalogException("User authentication origin not found in the list of authentication origins. " + + "Please, add an AuthenticationOrigin of type '" + tokenPayload.getAuthOrigin() + "' to the list."); + } + logger.debug("Set new list of Authentication Origins: {}.", authOriginUpdateBuilder); + break; + case REMOVE: + for (AuthenticationOrigin authenticationOrigin : updateParams.getAuthenticationOrigins()) { + if (!currentAuthOriginIds.contains(authenticationOrigin.getId())) { + throw new CatalogException("Authentication origin '" + authenticationOrigin.getId() + "' does not exist. " + + "The current available authentication origin ids are: '" + + StringUtils.join(currentAuthOriginIds, ", ") + "'."); + } + if (tokenPayload.getAuthOrigin().equals(authenticationOrigin.getType())) { + throw new CatalogException("Removing the authentication origin '" + tokenPayload.getAuthOrigin() + "' " + + "not allowed. Your user account uses that AuthenticationOrigin."); + } + } + logger.debug("Removing list of Authentication Origins: {}.", authOriginUpdateBuilder); + break; + case REPLACE: + for (AuthenticationOrigin authenticationOrigin : updateParams.getAuthenticationOrigins()) { + if (!currentAuthOriginIds.contains(authenticationOrigin.getId())) { + throw new CatalogException("Authentication origin '" + authenticationOrigin.getId() + "' not found." + + "Please, set the authOriginsAction to 'ADD' to add the new AuthenticationOrigin."); + } + } + logger.debug("Replace list of Authentication Origins: {}.", authOriginUpdateBuilder); + break; + default: + throw new CatalogParameterException("Unknown authentication origins action " + authOriginsAction); + } + } + + if (updateParams.getToken() != null) { + try { + JwtUtils.validateJWTKey(updateParams.getToken().getAlgorithm(), updateParams.getToken().getSecretKey()); + } catch (InvalidKeyException e) { + throw new CatalogParameterException("Invalid secret key - algorithm for JWT token: " + e.getMessage(), e); + } + if (updateParams.getToken().getExpiration() <= 0) { + throw new CatalogParameterException("Invalid expiration for JWT token. It must be a positive number."); + } + } + + ObjectMap updateMap = new ObjectMap(OrganizationDBAdaptor.QueryParams.CONFIGURATION.key(), updateConfigurationMap); + OpenCGAResult update = getOrganizationDBAdaptor(organizationId).update(organizationId, updateMap, queryOptions); + result.append(update); + auditManager.auditUpdate(organizationId, userId, Enums.Resource.ORGANIZATION, organization.getId(), organization.getUuid(), "", + "", auditParams, new AuditRecord.Status(AuditRecord.Status.Result.SUCCESS)); + + organization = null; + if (queryOptions.getBoolean(ParamConstants.INCLUDE_RESULT_PARAM)) { + // Fetch updated organization + organization = getOrganizationDBAdaptor(organizationId).get(INCLUDE_ORGANIZATION_CONFIGURATION).first(); + result.setResults(Collections.singletonList(organization.getConfiguration())); + } + + if (CollectionUtils.isNotEmpty(updateParams.getAuthenticationOrigins()) || updateParams.getToken() != null) { + if (organization == null) { + organization = getOrganizationDBAdaptor(organizationId).get(INCLUDE_ORGANIZATION_CONFIGURATION).first(); + } + authenticationFactory.configureOrganizationAuthenticationManager(organization); + } + + } catch (Exception e) { + auditManager.auditUpdate(organizationId, userId, Enums.Resource.ORGANIZATION, organizationId, organizationId, "", "", + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, new Error(0, "Update organization", + e.getMessage()))); + throw e; + } + return result; + } + private void validateOrganizationForCreation(Organization organization, String userId) throws CatalogParameterException { ParamUtils.checkParameter(organization.getId(), OrganizationDBAdaptor.QueryParams.ID.key()); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/migration/MigrationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/migration/MigrationManager.java index 10b247a6d0d..fa8fd7b9ee0 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/migration/MigrationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/migration/MigrationManager.java @@ -125,14 +125,20 @@ public void runMigration(String version, Collection d public void runMigration(String version, Collection domains, Collection languages, boolean offline, String appHome, ObjectMap params, String token) throws CatalogException, IOException { - // Migrate all organizations - for (String organizationId : dbAdaptorFactory.getOrganizationIds()) { - if (!ParamConstants.ADMIN_ORGANIZATION.equals(organizationId)) { - runMigration(organizationId, version, domains, languages, offline, appHome, params, token); + runMigration(ParamConstants.ADMIN_ORGANIZATION, version, domains, languages, offline, appHome, params, token); + + // ***** Starts code to remove in future versions. Reload MongoDBAdaptorFactory to avoid Notes migration issue. *****/ + try (MongoDBAdaptorFactory mongoDBAdaptorFactory = new MongoDBAdaptorFactory(configuration, catalogManager.getIoManagerFactory())) { + for (String organizationId : mongoDBAdaptorFactory.getOrganizationIds()) { + // ***** Finish code to remove in future versions. Reload MongoDBAdaptorFactory to avoid Notes migration issue. *****/ + + // Migrate all organizations + // for (String organizationId : dbAdaptorFactory.getOrganizationIds()) { + if (!ParamConstants.ADMIN_ORGANIZATION.equals(organizationId)) { + runMigration(organizationId, version, domains, languages, offline, appHome, params, token); + } } } - // Lastly, migrate the admin organization - runMigration(ParamConstants.ADMIN_ORGANIZATION, version, domains, languages, offline, appHome, params, token); } public void runMigration(String organizationId, String version, Collection domains, diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java index 7d6eb326acc..c39d111d8c7 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java @@ -124,9 +124,9 @@ public static void checkValidUserId(String userId) throws CatalogParameterExcept if (userId.equals(ParamConstants.ANONYMOUS_USER_ID) || userId.equals(ParamConstants.REGISTERED_USERS)) { throw new CatalogParameterException("User id cannot be one of the reserved OpenCGA users."); } - if (!userId.matches("^[A-Za-z0-9]([-_.]?[A-Za-z0-9])*$")) { + if (!userId.matches("^[A-Za-z0-9]([-_.@]?[A-Za-z0-9])*$")) { throw new CatalogParameterException("Invalid user id. Id needs to start by any character and might contain single '-', '_', " - + "'.', symbols followed by any character or number."); + + "'.' or '@' symbols followed by any character or number."); } } diff --git a/opencga-catalog/src/main/resources/catalog-indexes.txt b/opencga-catalog/src/main/resources/catalog-indexes.txt index 6cabeed184d..557a660716a 100644 --- a/opencga-catalog/src/main/resources/catalog-indexes.txt +++ b/opencga-catalog/src/main/resources/catalog-indexes.txt @@ -37,8 +37,8 @@ {"collections": ["job"], "fields": {"outDir.uid": 1, "studyUid": 1}, "options": {}} {"collections": ["job"], "fields": {"tags": 1, "studyUid": 1}, "options": {}} {"collections": ["job"], "fields": {"visited": 1, "studyUid": 1}, "options": {}} -{"collections": ["job"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} -{"collections": ["job"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["job"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} +{"collections": ["job"], "fields": {"internal.status.id": 1, "_priority": 1, "_creationDate": 1}, "options": {}} {"collections": ["job"], "fields": {"_priority": 1, "_creationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["job"], "fields": {"_priority": 1, "studyUid": 1}, "options": {}} {"collections": ["job"], "fields": {"_creationDate": 1, "studyUid": 1}, "options": {}} @@ -65,8 +65,8 @@ {"collections": ["file"], "fields": {"_creationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["file"], "fields": {"_modificationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["file"], "fields": {"jobId": 1, "studyUid": 1}, "options": {}} -{"collections": ["file"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} -{"collections": ["file"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["file"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} +{"collections": ["file"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["file"], "fields": {"internal.variant.index.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["file"], "fields": {"studyUid": 1, "_acl": 1}, "options": {}} {"collections": ["file"], "fields": {"studyUid": 1, "release": 1, "_acl": 1}, "options": {}} @@ -104,8 +104,8 @@ {"collections": ["sample", "sample_archive"], "fields": {"_ias.as": 1, "studyUid": 1}, "options": {}} {"collections": ["sample", "sample_archive"], "fields": {"_ias.vs": 1, "studyUid": 1}, "options": {}} {"collections": ["sample", "sample_archive"], "fields": {"_ias.id": 1, "_ias.value": 1, "studyUid": 1}, "options": {}} -{"collections": ["sample", "sample_archive"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} -{"collections": ["sample", "sample_archive"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["sample", "sample_archive"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} +{"collections": ["sample", "sample_archive"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["sample", "sample_archive"], "fields": {"internal.rga.status": 1, "studyUid": 1}, "options": {}} {"collections": ["individual", "individual_archive"], "fields": {"uuid": 1, "version": 1}, "options": {"unique": true}} @@ -138,8 +138,8 @@ {"collections": ["individual", "individual_archive"], "fields": {"_ias.as": 1, "studyUid": 1}, "options": {}} {"collections": ["individual", "individual_archive"], "fields": {"_ias.vs": 1, "studyUid": 1}, "options": {}} {"collections": ["individual", "individual_archive"], "fields": {"_ias.id": 1, "_ias.value": 1, "studyUid": 1}, "options": {}} -{"collections": ["individual", "individual_archive"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} -{"collections": ["individual", "individual_archive"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["individual", "individual_archive"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} +{"collections": ["individual", "individual_archive"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["cohort"], "fields": {"uuid": 1}, "options": {"unique": true}} {"collections": ["cohort"], "fields": {"uid": 1}, "options": {"unique": true}} @@ -158,8 +158,8 @@ {"collections": ["cohort"], "fields": {"_ias.as": 1, "studyUid": 1}, "options": {}} {"collections": ["cohort"], "fields": {"_ias.vs": 1, "studyUid": 1}, "options": {}} {"collections": ["cohort"], "fields": {"_ias.id": 1, "_ias.value": 1, "studyUid": 1}, "options": {}} -{"collections": ["cohort"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} -{"collections": ["cohort"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["cohort"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} +{"collections": ["cohort"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["family", "family_archive"], "fields": {"uuid": 1, "version": 1}, "options": {"unique": true}} {"collections": ["family", "family_archive"], "fields": {"uid": 1, "version": 1}, "options": {"unique": true}} @@ -182,8 +182,8 @@ {"collections": ["family", "family_archive"], "fields": {"_ias.as": 1, "studyUid": 1}, "options": {}} {"collections": ["family", "family_archive"], "fields": {"_ias.vs": 1, "studyUid": 1}, "options": {}} {"collections": ["family", "family_archive"], "fields": {"_ias.id": 1, "_ias.value": 1, "studyUid": 1}, "options": {}} -{"collections": ["family", "family_archive"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} -{"collections": ["family", "family_archive"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["family", "family_archive"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} +{"collections": ["family", "family_archive"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["panel", "panel_archive"], "fields": {"uuid": 1, "version": 1}, "options": {"unique": true}} {"collections": ["panel", "panel_archive"], "fields": {"uid": 1, "version": 1}, "options": {"unique": true}} @@ -201,11 +201,11 @@ {"collections": ["panel", "panel_archive"], "fields": {"categories.name": 1, "studyUid": 1}, "options": {}} {"collections": ["panel", "panel_archive"], "fields": {"_creationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["panel", "panel_archive"], "fields": {"_modificationDate": 1, "studyUid": 1}, "options": {}} -{"collections": ["panel", "panel_archive"], "fields": {"status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["panel", "panel_archive"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["panel", "panel_archive"], "fields": {"studyUid": 1, "_lastOfVersion": 1, "_acl": 1}, "options": {}} {"collections": ["panel", "panel_archive"], "fields": {"studyUid": 1, "_releaseFromVersion": 1, "_lastOfRelease": 1}, "options": {}} {"collections": ["panel", "panel_archive"], "fields": {"studyUid": 1, "release": 1, "_acl": 1}, "options": {}} -{"collections": ["panel", "panel_archive"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["panel", "panel_archive"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["clinical"], "fields": {"id": 1, "studyUid": 1}, "options": {"unique": true}} {"collections": ["clinical"], "fields": {"uuid": 1}, "options": {"unique": true}} @@ -231,7 +231,7 @@ {"collections": ["clinical"], "fields": {"_modificationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["clinical"], "fields": {"studyUid": 1, "_acl": 1}, "options": {}} {"collections": ["clinical"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} -{"collections": ["clinical"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["clinical"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"uuid": 1, "version": 1}, "options": {"unique": true}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"uid": 1, "version": 1}, "options": {"unique": true}} @@ -246,7 +246,7 @@ {"collections": ["interpretation", "interpretation_archive"], "fields": {"_creationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"_modificationDate": 1, "studyUid": 1}, "options": {}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"status.id": 1, "studyUid": 1}, "options": {}} -{"collections": ["interpretation", "interpretation_archive"], "fields": {"internal.status.name": 1, "studyUid": 1}, "options": {}} +{"collections": ["interpretation", "interpretation_archive"], "fields": {"internal.status.id": 1, "studyUid": 1}, "options": {}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"studyUid": 1, "_lastOfVersion": 1}, "options": {}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"studyUid": 1, "_releaseFromVersion": 1, "_lastOfRelease": 1}, "options": {}} {"collections": ["interpretation", "interpretation_archive"], "fields": {"studyUid": 1, "release": 1}, "options": {}} diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/auth/authentication/JwtSessionManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/auth/authentication/JwtSessionManagerTest.java index 2e9887f85fb..c9d2ca8aec4 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/auth/authentication/JwtSessionManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/auth/authentication/JwtSessionManagerTest.java @@ -53,7 +53,7 @@ public void setUp() throws Exception { @Test public void testCreateJWTToken() throws Exception { - jwtToken = jwtSessionManager.createJWTToken(organizationId, "testUser", Collections.emptyMap(), 60L); + jwtToken = jwtSessionManager.createJWTToken(organizationId, null, "testUser", Collections.emptyMap(), 60L); } @Test @@ -81,7 +81,7 @@ public void testInvalidSecretKey() throws CatalogAuthenticationException { @Test public void testNonExpiringToken() throws CatalogException { - String nonExpiringToken = jwtSessionManager.createJWTToken(organizationId, "System", null, -1L); + String nonExpiringToken = jwtSessionManager.createJWTToken(organizationId, null, "System", null, -1L); assertEquals(jwtSessionManager.getUser(nonExpiringToken), "System"); assertNull(jwtSessionManager.getExpiration(nonExpiringToken)); } diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractClinicalManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractClinicalManagerTest.java index 3af2d35be82..2de27beb3da 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractClinicalManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractClinicalManagerTest.java @@ -16,6 +16,7 @@ package org.opencb.opencga.catalog.managers; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.experimental.categories.Category; @@ -23,6 +24,7 @@ import org.opencb.biodata.models.clinical.Disorder; import org.opencb.biodata.models.clinical.Phenotype; import org.opencb.biodata.models.core.SexOntologyTermAnnotation; +import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.test.GenericTest; import org.opencb.opencga.catalog.exceptions.CatalogException; @@ -35,6 +37,7 @@ import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.models.study.Study; +import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; import java.io.IOException; @@ -46,6 +49,8 @@ import java.util.Collections; import java.util.List; +import static org.opencb.commons.datastore.core.QueryOptions.INCLUDE; + @Category(MediumTests.class) public class AbstractClinicalManagerTest extends GenericTest { @@ -57,7 +62,10 @@ public class AbstractClinicalManagerTest extends GenericTest { public final static String PROBAND_ID2 = "manuel_individual"; public final static String CA_ID3 = "clinical-analysis-3"; - public final static String PROBAND_ID3 = "HG005_individual"; + public final static String PROBAND_ID3 = "HG005"; + + public final static String CA_ID4 = "clinical-analysis-4"; + public final static String PROBAND_ID4 = "HG105"; @Rule public ExpectedException thrown = ExpectedException.none(); @@ -135,7 +143,7 @@ public void setUpCatalogManager() throws IOException, CatalogException, URISynta catalogUploadFile("/biofiles/exomiser.vcf.gz"); //--------------------------------------------------------------------- - // Chinese trio (clinicalAnalysis3) + // Chinese trio: FAMILY (clinicalAnalysis3) //--------------------------------------------------------------------- Individual hg006Individual = new Individual().setId("HG006_individual") @@ -154,7 +162,7 @@ public void setUpCatalogManager() throws IOException, CatalogException, URISynta .setFather(hg006Individual) .setMother(hg007Individual) .setSex(SexOntologyTermAnnotation.initMale()) - .setSamples(Collections.singletonList(new Sample().setId("HG005"))); + .setSamples(Collections.singletonList(new Sample().setId(PROBAND_ID3))); Individual hg004Individual = new Individual().setId("HG004_individual") .setFather(hg006Individual) @@ -162,14 +170,14 @@ public void setUpCatalogManager() throws IOException, CatalogException, URISynta .setSex(SexOntologyTermAnnotation.initFemale()) .setSamples(Collections.singletonList(new Sample().setId("HG004"))); - Family chineseFamily = new Family("chinese_family", "chinese_family", null, null, + Family chineseFamily = new Family("chinese_trio_family", "chinese_trio_family", null, null, Arrays.asList(hg005Individual, hg006Individual, hg007Individual, hg004Individual), "", 4, Collections.emptyList(), Collections.emptyMap()); catalogManager.getFamilyManager().create(studyFqn, chineseFamily, QueryOptions.empty(), token).first(); auxClinicalAnalysis = new ClinicalAnalysis() .setId(CA_ID3) - .setDescription("My description - exomiser - trio") + .setDescription("My description - exomiser - trio - family") .setType(ClinicalAnalysis.Type.FAMILY) .setDueDate("20180510100000") .setDisorder(getDisorder()) @@ -183,6 +191,60 @@ public void setUpCatalogManager() throws IOException, CatalogException, URISynta catalogUploadFile("/biofiles/HG005.1k.vcf.gz"); catalogUploadFile("/biofiles/HG006.1k.vcf.gz"); catalogUploadFile("/biofiles/HG007.1k.vcf.gz"); + + //--------------------------------------------------------------------- + // Chinese trio: SINGLE (clinicalAnalysis4) + //--------------------------------------------------------------------- + + Individual hg106Individual = new Individual().setId("HG106_individual") + .setPhenotypes(Collections.emptyList()) + .setSex(SexOntologyTermAnnotation.initMale()) + .setSamples(Collections.singletonList(new Sample().setId("HG106"))); + + Individual hg107Individual = new Individual().setId("HG107_individual") + .setPhenotypes(Collections.emptyList()) + .setSex(SexOntologyTermAnnotation.initFemale()) + .setSamples(Collections.singletonList(new Sample().setId("HG107"))); + + Individual hg105Individual = new Individual().setId("HG105_individual") + .setDisorders(Collections.singletonList(getDisorder())) + .setPhenotypes(getPhenotypes()) + .setFather(hg106Individual) + .setMother(hg107Individual) + .setSex(SexOntologyTermAnnotation.initMale()) + .setSamples(Collections.singletonList(new Sample().setId(PROBAND_ID4))); + + Individual hg104Individual = new Individual().setId("HG104_individual") + .setFather(hg106Individual) + .setMother(hg107Individual) + .setSex(SexOntologyTermAnnotation.initFemale()) + .setSamples(Collections.singletonList(new Sample().setId("HG104"))); + + Family chineseSingle = new Family("chinese_trio_single", "chinese_trio_single", null, null, + Arrays.asList(hg105Individual, hg106Individual, hg107Individual, hg104Individual), "", 4, Collections.emptyList(), + Collections.emptyMap()); + catalogManager.getFamilyManager().create(studyFqn, chineseSingle, QueryOptions.empty(), token).first(); + + auxClinicalAnalysis = new ClinicalAnalysis() + .setId(CA_ID4) + .setDescription("My description - exomiser - trio - single") + .setType(ClinicalAnalysis.Type.SINGLE) + .setDueDate("20180510100000") + .setDisorder(getDisorder()) + .setProband(hg105Individual) + .setFamily(chineseSingle); + + catalogManager.getClinicalAnalysisManager().create(studyFqn, auxClinicalAnalysis, QueryOptions.empty(), token) + .first(); + + catalogUploadFile("/biofiles/HG104.1k.vcf.gz"); + catalogUploadFile("/biofiles/HG105.1k.vcf.gz"); + catalogUploadFile("/biofiles/HG106.1k.vcf.gz"); + catalogUploadFile("/biofiles/HG107.1k.vcf.gz"); + + + OpenCGAResult sampleResult = catalogManager.getSampleManager().search(studyFqn, new Query(), new QueryOptions(INCLUDE, "id"), token); + Assert.assertEquals(12, sampleResult.getNumResults()); } private void catalogUploadFile(String path) throws IOException, CatalogException { diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java index 21d0322207b..a57747acd49 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java @@ -427,6 +427,15 @@ private String getAdminToken() throws CatalogException, IOException { return catalogManager.getUserManager().loginAsAdmin("admin").getToken(); } + @Test + public void createUserUsingMailAsId() throws CatalogException { + catalogManager.getUserManager().create(new User().setId("hello.mail@mymail.org").setName("Hello"), TestParamConstants.PASSWORD, ownerToken); + AuthenticationResponse login = catalogManager.getUserManager().login(organizationId, "hello.mail@mymail.org", TestParamConstants.PASSWORD); + assertNotNull(login); + User user = catalogManager.getUserManager().get(organizationId, "hello.mail@mymail.org", new QueryOptions(), login.getToken()).first(); + assertEquals("hello.mail@mymail.org", user.getId()); + } + @Test public void getGroupsTest() throws CatalogException { Group group = new Group("groupId", Arrays.asList(normalUserId2, normalUserId3)).setSyncedFrom(new Group.Sync("ldap", "bio")); @@ -656,6 +665,82 @@ public void testModifyProject() throws CatalogException { catalogManager.getProjectManager().update(project1, options, null, ownerToken); } + @Test + public void updateProjectPermissionTest() throws CatalogException { + ObjectMap params = new ObjectMap() + .append(ProjectDBAdaptor.QueryParams.DESCRIPTION.key(), "my new description"); + Project project = catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, ownerToken).first(); + assertEquals("my new description", project.getDescription()); + + params.put(ProjectDBAdaptor.QueryParams.DESCRIPTION.key(), "my new description 2"); + project = catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, orgAdminToken2).first(); + assertEquals("my new description 2", project.getDescription()); + + params.put(ProjectDBAdaptor.QueryParams.DESCRIPTION.key(), "my new description 3"); + project = catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, studyAdminToken1).first(); + assertEquals("my new description 3", project.getDescription()); + + // Make normalUser1 admin of first study + catalogManager.getStudyManager().updateGroup(studyFqn, ParamConstants.ADMINS_GROUP, ParamUtils.BasicUpdateAction.ADD, + new GroupUpdateParams(Collections.singletonList(normalUserId1)), ownerToken); + // And remove normalUser1 from the admins group of the second study (just in case) + catalogManager.getStudyManager().updateGroup(studyFqn2, ParamConstants.ADMINS_GROUP, ParamUtils.BasicUpdateAction.REMOVE, + new GroupUpdateParams(Collections.singletonList(normalUserId1)), ownerToken); + + CatalogAuthorizationException catalogAuthorizationException = assertThrows(CatalogAuthorizationException.class, + () -> catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, normalToken1)); + assertFalse(catalogAuthorizationException.getCause().getMessage().contains(studyFqn)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn2)); + + catalogAuthorizationException = assertThrows(CatalogAuthorizationException.class, + () -> catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, normalToken2)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn2)); + + // Remove orgAdminUser1 from the administrators group of the organization + Map actionMap = new HashMap<>(); + actionMap.put(OrganizationDBAdaptor.QueryParams.ADMINS.key(), ParamUtils.BasicUpdateAction.REMOVE); + QueryOptions options = new QueryOptions(Constants.ACTIONS, actionMap); + catalogManager.getOrganizationManager().update(organizationId, new OrganizationUpdateParams() + .setAdmins(Collections.singletonList(orgAdminUserId1)), options, ownerToken); + + catalogAuthorizationException = assertThrows(CatalogAuthorizationException.class, + () -> catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, orgAdminToken1)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn2)); + + // Create a third study + catalogManager.getStudyManager().create(project1, new Study().setId("study_3"), null, ownerToken); + catalogAuthorizationException = assertThrows(CatalogAuthorizationException.class, + () -> catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, orgAdminToken1)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn2)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains("study_3")); + + // Add orgAdminUser1 to the administrators group of the third study + catalogManager.getStudyManager().updateGroup("study_3", ParamConstants.ADMINS_GROUP, ParamUtils.BasicUpdateAction.ADD, + new GroupUpdateParams(Collections.singletonList(orgAdminUserId1)), ownerToken); + catalogAuthorizationException = assertThrows(CatalogAuthorizationException.class, + () -> catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, orgAdminToken1)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn2)); + assertFalse(catalogAuthorizationException.getCause().getMessage().contains("study_3")); + + // Add orgAdminUser1 to the administrators group of the second study + catalogManager.getStudyManager().updateGroup(studyFqn2, ParamConstants.ADMINS_GROUP, ParamUtils.BasicUpdateAction.ADD, + new GroupUpdateParams(Collections.singletonList(orgAdminUserId1)), ownerToken); + catalogAuthorizationException = assertThrows(CatalogAuthorizationException.class, + () -> catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, orgAdminToken1)); + assertTrue(catalogAuthorizationException.getCause().getMessage().contains(studyFqn)); + assertFalse(catalogAuthorizationException.getCause().getMessage().contains(studyFqn2)); + assertFalse(catalogAuthorizationException.getCause().getMessage().contains("study_3")); + + // Add orgAdminUser1 to the administrators group of the remaining study + catalogManager.getStudyManager().updateGroup(studyFqn, ParamConstants.ADMINS_GROUP, ParamUtils.BasicUpdateAction.ADD, + new GroupUpdateParams(Collections.singletonList(orgAdminUserId1)), ownerToken); + catalogManager.getProjectManager().update(project1, params, INCLUDE_RESULT, orgAdminToken1); + } + @Test public void updatePrivateParamsFromProjectTest() throws CatalogException { catalogManager.getProjectManager().setDatastoreVariant(projectFqn1, new DataStore(), opencgaToken); diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/FileManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/FileManagerTest.java index 65af29ca14a..5e655998abd 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/FileManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/FileManagerTest.java @@ -40,11 +40,17 @@ import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.common.JacksonUtils; import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.common.UriUtils; import org.opencb.opencga.core.models.AclEntryList; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysisUpdateParams; import org.opencb.opencga.core.models.common.AnnotationSet; +import org.opencb.opencga.core.models.family.Family; import org.opencb.opencga.core.models.file.*; +import org.opencb.opencga.core.models.individual.Individual; +import org.opencb.opencga.core.models.panel.Panel; import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.models.study.*; import org.opencb.opencga.core.response.OpenCGAResult; @@ -189,6 +195,32 @@ public void testLinkFileWithoutReadPermissions() throws IOException, CatalogExce fileManager.link(studyFqn, new FileLinkParams().setUri(file.getPath()), false, ownerToken); } + @Test + public void filterByFormatTest() throws CatalogException { + Query query = new Query(FileDBAdaptor.QueryParams.FORMAT.key(), "PLAIN"); + OpenCGAResult search = catalogManager.getFileManager().search(studyFqn, query, QueryOptions.empty(), ownerToken); + assertEquals(3, search.getNumResults()); + + query = new Query(FileDBAdaptor.QueryParams.FORMAT.key(), "plain"); + search = catalogManager.getFileManager().search(studyFqn, query, QueryOptions.empty(), ownerToken); + assertEquals(0, search.getNumResults()); + + // Case sensitive search in lower case + query = new Query(FileDBAdaptor.QueryParams.FORMAT.key(), "~/^pla/"); + search = catalogManager.getFileManager().search(studyFqn, query, QueryOptions.empty(), ownerToken); + assertEquals(0, search.getNumResults()); + + // Case sensitive in upper case + query = new Query(FileDBAdaptor.QueryParams.FORMAT.key(), "~/^PLA/"); + search = catalogManager.getFileManager().search(studyFqn, query, QueryOptions.empty(), ownerToken); + assertEquals(3, search.getNumResults()); + + // Case insensitive search + query = new Query(FileDBAdaptor.QueryParams.FORMAT.key(), "~/^pla/i"); + search = catalogManager.getFileManager().search(studyFqn, query, QueryOptions.empty(), ownerToken); + assertEquals(3, search.getNumResults()); + } + @Test public void createDirectoryTest() throws CatalogException { FileCreateParams params = new FileCreateParams() @@ -677,6 +709,7 @@ public void testMoveFiles() throws CatalogException { file = catalogManager.getFileManager().update(studyFqn, "A/B/C/D/hello2.txt", new FileUpdateParams().setSampleIds(Collections.singletonList("sam2")), INCLUDE_RESULT, ownerToken).first(); fileMap.put(file.getUid(), file); file = catalogManager.getFileManager().update(studyFqn, "A/B/C/D/hello3.txt", new FileUpdateParams().setSampleIds(Collections.singletonList("sam3")), INCLUDE_RESULT, ownerToken).first(); + fileMap.put(file.getUid(), file); Map sampleVersionMap = new HashMap<>(); @@ -766,6 +799,7 @@ public void testMoveFiles() throws CatalogException { catalogManager.getFileManager().move(studyFqn, "A/C/D/hello3.txt", "A/C/D/otherName.txt", QueryOptions.empty(), ownerToken); assertThrows(CatalogException.class, () -> catalogManager.getFileManager().get(studyFqn, "A/C/D/hello3.txt", QueryOptions.empty(), ownerToken)); file = catalogManager.getFileManager().get(studyFqn, "A/C/D/otherName.txt", QueryOptions.empty(), ownerToken).first(); + assertTrue(file.getId().endsWith(":otherName.txt")); assertTrue(file.getPath().endsWith("/otherName.txt")); assertTrue(file.getUri().toString().endsWith("/otherName.txt")); @@ -1027,7 +1061,7 @@ public void testUnlinkFile() throws CatalogException, IOException { // We send the unlink command again thrown.expect(CatalogException.class); - thrown.expectMessage("not found"); + thrown.expectMessage("not unlink"); fileManager.unlink(studyFqn, "myDirectory/data/test/folder/test_0.5K.txt", ownerToken); } @@ -2035,6 +2069,97 @@ public void deleteFolderTest4() throws CatalogException, IOException { } } + @Test + public void deleteFileInClinicalAnalysis() throws CatalogException, IOException { + // START DATA PREPARATION FOR TEST !!! + String bamFile = getClass().getResource("/biofiles/NA19600.chrom20.small.bam").getFile(); + File file = fileManager.link(studyFqn, new FileLinkParams(bamFile, "", "", "", null, null, null, null, null), false, ownerToken).first(); + + Family family1 = DummyModelUtils.getDummyFamily("familyId1"); + catalogManager.getFamilyManager().create(studyFqn, family1, QueryOptions.empty(), ownerToken); + + // Associate BAM file to sample + String sampleId = family1.getMembers().get(0).getSamples().get(0).getId(); + catalogManager.getFileManager().update(studyFqn, file.getId(), new FileUpdateParams().setSampleIds(Collections.singletonList(sampleId)), + QueryOptions.empty(), ownerToken); + + Panel myPanel = DummyModelUtils.getDummyPanel("myPanel"); + catalogManager.getPanelManager().create(studyFqn, myPanel, QueryOptions.empty(), ownerToken); + + Family copy = JacksonUtils.copy(family1, Family.class); + for (Individual member : copy.getMembers()) { + // Only use the first sample + member.setSamples(Collections.singletonList(member.getSamples().get(0))); + } + + ClinicalAnalysis clinicalAnalysis1 = DummyModelUtils.getDummyClinicalAnalysis(copy.getMembers().get(0), copy, Collections.singletonList(myPanel)); + clinicalAnalysis1 = catalogManager.getClinicalAnalysisManager().create(studyFqn, clinicalAnalysis1, INCLUDE_RESULT, ownerToken).first(); + assertEquals(1, clinicalAnalysis1.getFiles().size()); + assertEquals(file.getPath(), clinicalAnalysis1.getFiles().get(0).getPath()); + assertFalse(clinicalAnalysis1.isLocked()); + + ClinicalAnalysis clinicalAnalysis2 = DummyModelUtils.getDummyClinicalAnalysis(copy.getMembers().get(0), copy, Collections.singletonList(myPanel)); + clinicalAnalysis2 = catalogManager.getClinicalAnalysisManager().create(studyFqn, clinicalAnalysis2, INCLUDE_RESULT, ownerToken).first(); + assertEquals(1, clinicalAnalysis2.getFiles().size()); + assertEquals(file.getPath(), clinicalAnalysis2.getFiles().get(0).getPath()); + assertFalse(clinicalAnalysis2.isLocked()); + + // Lock clinicalAnalysis2 + clinicalAnalysis2 = catalogManager.getClinicalAnalysisManager().update(studyFqn, clinicalAnalysis2.getId(), + new ClinicalAnalysisUpdateParams().setLocked(true), INCLUDE_RESULT, ownerToken).first(); + assertTrue(clinicalAnalysis2.isLocked()); + // END DATA PREPARATION FOR TEST !!! + + // Mark as pending delete + catalogManager.getFileManager().getFileDBAdaptor(organizationId).update(file.getUid(), new ObjectMap(FileDBAdaptor.QueryParams.INTERNAL_STATUS_ID.key(), FileStatus.PENDING_DELETE), QueryOptions.empty()); + CatalogException catalogException = assertThrows(CatalogException.class, () -> catalogManager.getFileManager().unlink(studyFqn, file.getId(), ownerToken)); + assertTrue(catalogException.getMessage().contains("Could not unlink")); + assertTrue(catalogException.getCause().getMessage().contains("clinical analyses")); + + // Unlock clinicalAnalysis2 + clinicalAnalysis2 = catalogManager.getClinicalAnalysisManager().update(studyFqn, clinicalAnalysis2.getId(), + new ClinicalAnalysisUpdateParams().setLocked(false), INCLUDE_RESULT, ownerToken).first(); + assertFalse(clinicalAnalysis2.isLocked()); + + // Unlink file + catalogManager.getFileManager().unlink(studyFqn, file.getId(), ownerToken); + + Sample sample = catalogManager.getSampleManager().get(studyFqn, sampleId, QueryOptions.empty(), ownerToken).first(); + assertEquals(0, sample.getFileIds().size()); + + OpenCGAResult search = catalogManager.getClinicalAnalysisManager().search(studyFqn, new Query(), QueryOptions.empty(), ownerToken); + assertEquals(2, search.getNumResults()); + for (ClinicalAnalysis clinicalAnalysis : search.getResults()) { + assertEquals(0, clinicalAnalysis.getFiles().size()); + assertEquals("OPENCGA", clinicalAnalysis.getAudit().get(clinicalAnalysis.getAudit().size() - 1).getAuthor()); + assertTrue(clinicalAnalysis.getAudit().get(clinicalAnalysis.getAudit().size() - 1).getMessage().contains("was deleted. Remove file references from case")); + } + } + + @Test + public void deleteFileUserInRelatedFilesTest() throws CatalogException { + fileManager.update(studyFqn, "data/test/folder/test_1K.txt.gz", + new FileUpdateParams().setRelatedFiles(Collections.singletonList( + new SmallRelatedFileParams("data/test/folder/test_0.5K.txt", FileRelatedFile.Relation.PART_OF_PAIR))), + null, ownerToken); + File file = fileManager.get(studyFqn, "data/test/folder/test_1K.txt.gz", QueryOptions.empty(), ownerToken).first(); + assertFalse(file.getRelatedFiles().isEmpty()); + assertEquals(1, file.getRelatedFiles().size()); + assertEquals("data/test/folder/test_0.5K.txt", file.getRelatedFiles().get(0).getFile().getPath()); + + file = fileManager.get(studyFqn, "data/test/folder/test_0.5K.txt", FileManager.INCLUDE_FILE_IDS, ownerToken).first(); + + // Mark as pending delete + catalogManager.getFileManager().getFileDBAdaptor(organizationId).update(file.getUid(), new ObjectMap(FileDBAdaptor.QueryParams.INTERNAL_STATUS_ID.key(), FileStatus.PENDING_DELETE), QueryOptions.empty()); + // Delete test_0.5K file + QueryOptions options = new QueryOptions(Constants.SKIP_TRASH, true); + fileManager.delete(studyFqn, Collections.singletonList("data/test/folder/test_0.5K.txt"), options, ownerToken); + + // Ensure there are no more references to test_0.5K file + file = fileManager.get(studyFqn, "data/test/folder/test_1K.txt.gz", QueryOptions.empty(), ownerToken).first(); + assertTrue(file.getRelatedFiles().isEmpty()); + } + private File createBasicDirectoryFileTestEnvironment(List folderFiles) throws CatalogException { File folder = fileManager.createFolder(studyFqn, Paths.get("folder").toString(), false, null, QueryOptions.empty(), ownerToken).first(); diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/IndividualManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/IndividualManagerTest.java index 929ac84e55e..8755cc50c4a 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/IndividualManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/IndividualManagerTest.java @@ -1120,8 +1120,10 @@ public void viewSampleFilesFromIndividualTest() throws CatalogException { // Create individual catalogManager.getIndividualManager().create(studyFqn, new Individual().setId("individual"), Collections.singletonList(sample.getId()), + QueryOptions.empty(), ownerToken); Individual individual = catalogManager.getIndividualManager().get(studyFqn, "individual", QueryOptions.empty(), ownerToken).first(); + assertEquals(1, individual.getSamples().get(0).getFileIds().size()); assertEquals("variant-test-file.vcf.gz", individual.getSamples().get(0).getFileIds().get(0)); diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java index b408fec74c1..a6d3bd3540f 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java @@ -3,17 +3,20 @@ import org.apache.commons.collections4.CollectionUtils; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.catalog.auth.authentication.CatalogAuthenticationManager; import org.opencb.opencga.catalog.db.api.OrganizationDBAdaptor; import org.opencb.opencga.catalog.db.api.StudyDBAdaptor; +import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.config.AuthenticationOrigin; +import org.opencb.opencga.core.config.Optimizations; import org.opencb.opencga.core.models.organizations.*; import org.opencb.opencga.core.models.project.Project; import org.opencb.opencga.core.models.study.Group; @@ -21,10 +24,7 @@ import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import static org.junit.Assert.*; @@ -39,48 +39,125 @@ public void ensureAuthOriginExistsTest() throws CatalogException { } @Test - public void ensureAuthOriginCannotBeRemovedTest() throws CatalogException { - OrganizationUpdateParams updateParams = new OrganizationUpdateParams().setConfiguration(new OrganizationConfiguration( - Collections.emptyList(), null, new TokenConfiguration())); - thrown.expect(CatalogException.class); - thrown.expectMessage("OPENCGA"); - catalogManager.getOrganizationManager().update(organizationId, updateParams, INCLUDE_RESULT, ownerToken); + public void updateConfigurationAuthorizationTest() throws CatalogException { + OrganizationConfiguration configuration = new OrganizationConfiguration() + .setOptimizations(new Optimizations(true)); + + // Owner can update configuration + catalogManager.getOrganizationManager().updateConfiguration(organizationId, configuration, QueryOptions.empty(), ownerToken); + + // Admin can update configuration + catalogManager.getOrganizationManager().updateConfiguration(organizationId, configuration, QueryOptions.empty(), orgAdminToken1); + + // Admin can update configuration + catalogManager.getOrganizationManager().updateConfiguration(organizationId, configuration, QueryOptions.empty(), orgAdminToken2); + + // Study admin cannot update configuration + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getOrganizationManager().updateConfiguration(organizationId, + configuration, QueryOptions.empty(), studyAdminToken1)); + + // Normal user cannot update configuration + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getOrganizationManager().updateConfiguration(organizationId, + configuration, QueryOptions.empty(), normalToken1)); } @Test - public void avoidDuplicatedOPENCGAAuthOriginTest() throws CatalogException { - AuthenticationOrigin authOrigin = CatalogAuthenticationManager.createOpencgaAuthenticationOrigin(); - AuthenticationOrigin authOrigin2 = CatalogAuthenticationManager.createOpencgaAuthenticationOrigin(); - OrganizationUpdateParams updateParams = new OrganizationUpdateParams().setConfiguration(new OrganizationConfiguration( - Arrays.asList(authOrigin, authOrigin2), null, new TokenConfiguration())); - - thrown.expect(CatalogException.class); - thrown.expectMessage("OPENCGA"); - catalogManager.getOrganizationManager().update(organizationId, updateParams, null, ownerToken); + public void ensureAuthOriginCannotBeRemovedTest() throws CatalogException { + Organization organization = catalogManager.getOrganizationManager().get(organizationId, null, ownerToken).first(); + + OrganizationConfiguration configuration = new OrganizationConfiguration() + .setAuthenticationOrigins(organization.getConfiguration().getAuthenticationOrigins()); + Map actionMap = new HashMap<>(); + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, ParamUtils.UpdateAction.REMOVE); + QueryOptions options = new QueryOptions(Constants.ACTIONS, actionMap); + + CatalogException catalogException = assertThrows(CatalogException.class, () -> catalogManager.getOrganizationManager() + .updateConfiguration(organizationId, configuration, options, ownerToken)); + assertTrue(catalogException.getMessage().contains("user account uses")); } @Test - public void avoidDuplicatedAuthOriginIdTest() throws CatalogException { - AuthenticationOrigin authOrigin = CatalogAuthenticationManager.createOpencgaAuthenticationOrigin(); - AuthenticationOrigin authOrigin2 = CatalogAuthenticationManager.createOpencgaAuthenticationOrigin(); - authOrigin2.setType(AuthenticationOrigin.AuthenticationType.LDAP); - OrganizationUpdateParams updateParams = new OrganizationUpdateParams().setConfiguration(new OrganizationConfiguration( - Arrays.asList(authOrigin, authOrigin2), null, new TokenConfiguration())); - - thrown.expect(CatalogException.class); - thrown.expectMessage("origin id"); - catalogManager.getOrganizationManager().update(organizationId, updateParams, null, ownerToken); + public void authOriginActionTest() throws CatalogException { + OrganizationConfiguration configuration = new OrganizationConfiguration() + .setAuthenticationOrigins(Collections.singletonList(new AuthenticationOrigin("myId", + AuthenticationOrigin.AuthenticationType.SSO, null, null))); + Map actionMap = new HashMap<>(); + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, ParamUtils.UpdateAction.ADD); + QueryOptions options = new QueryOptions() + .append(ParamConstants.INCLUDE_RESULT_PARAM, true) + .append(Constants.ACTIONS, actionMap); + + OrganizationConfiguration configurationResult = catalogManager.getOrganizationManager().updateConfiguration(organizationId, + configuration, options, ownerToken).first(); + assertEquals(2, configurationResult.getAuthenticationOrigins().size()); + for (AuthenticationOrigin authenticationOrigin : configurationResult.getAuthenticationOrigins()) { + if (authenticationOrigin.getId().equals("myId")) { + assertEquals(AuthenticationOrigin.AuthenticationType.SSO, authenticationOrigin.getType()); + } else { + assertEquals(AuthenticationOrigin.AuthenticationType.OPENCGA, authenticationOrigin.getType()); + } + } + + // Remove authOrigin + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, ParamUtils.UpdateAction.REMOVE); + options.put(Constants.ACTIONS, actionMap); + configurationResult = catalogManager.getOrganizationManager().updateConfiguration(organizationId, configuration, options, + ownerToken).first(); + assertEquals(1, configurationResult.getAuthenticationOrigins().size()); + assertEquals(AuthenticationOrigin.AuthenticationType.OPENCGA, configurationResult.getAuthenticationOrigins().get(0).getType()); + + // Set authOrigin + List authenticationOriginList = new ArrayList<>(); + authenticationOriginList.add(new AuthenticationOrigin("myId", AuthenticationOrigin.AuthenticationType.SSO, null, null)); + authenticationOriginList.add(configurationResult.getAuthenticationOrigins().get(0)); + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, ParamUtils.UpdateAction.SET); + options.put(Constants.ACTIONS, actionMap); + configuration.setAuthenticationOrigins(authenticationOriginList); + configurationResult = catalogManager.getOrganizationManager().updateConfiguration(organizationId, configuration, options, + ownerToken).first(); + assertEquals(2, configurationResult.getAuthenticationOrigins().size()); + for (AuthenticationOrigin authenticationOrigin : configurationResult.getAuthenticationOrigins()) { + if (authenticationOrigin.getId().equals("myId")) { + assertEquals(AuthenticationOrigin.AuthenticationType.SSO, authenticationOrigin.getType()); + } else { + assertEquals(AuthenticationOrigin.AuthenticationType.OPENCGA, authenticationOrigin.getType()); + } + } + + // Add existing authOrigin + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, ParamUtils.UpdateAction.ADD); + options.put(Constants.ACTIONS, actionMap); + CatalogException catalogException = assertThrows(CatalogException.class, () -> catalogManager.getOrganizationManager() + .updateConfiguration(organizationId, configuration, options, ownerToken)); + assertTrue(catalogException.getMessage().contains("REPLACE")); + + // Replace existing authOrigin + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, ParamUtils.UpdateAction.REPLACE); + options.put(Constants.ACTIONS, actionMap); + configuration.setAuthenticationOrigins(Collections.singletonList( + new AuthenticationOrigin(CatalogAuthenticationManager.OPENCGA, AuthenticationOrigin.AuthenticationType.OPENCGA, null, new ObjectMap("key", "value")))); + configurationResult = catalogManager.getOrganizationManager().updateConfiguration(organizationId, configuration, options, ownerToken).first(); + assertEquals(2, configurationResult.getAuthenticationOrigins().size()); + for (AuthenticationOrigin authenticationOrigin : configurationResult.getAuthenticationOrigins()) { + if (authenticationOrigin.getId().equals("myId")) { + assertEquals(AuthenticationOrigin.AuthenticationType.SSO, authenticationOrigin.getType()); + } else { + assertEquals(AuthenticationOrigin.AuthenticationType.OPENCGA, authenticationOrigin.getType()); + assertTrue(authenticationOrigin.getOptions().containsKey("key")); + assertEquals("value", authenticationOrigin.getOptions().get("key")); + } + } } @Test - public void updateAuthOriginTest() throws CatalogException { - AuthenticationOrigin authOrigin = CatalogAuthenticationManager.createOpencgaAuthenticationOrigin(); - OrganizationUpdateParams updateParams = new OrganizationUpdateParams().setConfiguration(new OrganizationConfiguration( - Collections.singletonList(authOrigin), null, new TokenConfiguration())); - - Organization organization = catalogManager.getOrganizationManager().update(organizationId, updateParams, INCLUDE_RESULT, ownerToken).first(); - assertEquals(authOrigin.getId(), organization.getConfiguration().getAuthenticationOrigins().get(0).getId()); - assertEquals(authOrigin.getType(), organization.getConfiguration().getAuthenticationOrigins().get(0).getType()); + public void tokenUpdateTest() throws CatalogException { + TokenConfiguration tokenConfiguration = TokenConfiguration.init(); + OrganizationConfiguration configuration = new OrganizationConfiguration().setToken(tokenConfiguration); + OrganizationConfiguration configurationResult = catalogManager.getOrganizationManager().updateConfiguration(organizationId, + configuration, INCLUDE_RESULT, ownerToken).first(); + assertEquals(tokenConfiguration.getSecretKey(), configurationResult.getToken().getSecretKey()); + + assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getOrganizationManager().get(organizationId, null, ownerToken)); } @Test diff --git a/opencga-catalog/src/test/resources/biofiles/HG104.1k.vcf.gz b/opencga-catalog/src/test/resources/biofiles/HG104.1k.vcf.gz new file mode 100644 index 00000000000..4237b056dfa Binary files /dev/null and b/opencga-catalog/src/test/resources/biofiles/HG104.1k.vcf.gz differ diff --git a/opencga-catalog/src/test/resources/biofiles/HG105.1k.vcf.gz b/opencga-catalog/src/test/resources/biofiles/HG105.1k.vcf.gz new file mode 100644 index 00000000000..4bb5c469f01 Binary files /dev/null and b/opencga-catalog/src/test/resources/biofiles/HG105.1k.vcf.gz differ diff --git a/opencga-catalog/src/test/resources/biofiles/HG106.1k.vcf.gz b/opencga-catalog/src/test/resources/biofiles/HG106.1k.vcf.gz new file mode 100644 index 00000000000..47cec223765 Binary files /dev/null and b/opencga-catalog/src/test/resources/biofiles/HG106.1k.vcf.gz differ diff --git a/opencga-catalog/src/test/resources/biofiles/HG107.1k.vcf.gz b/opencga-catalog/src/test/resources/biofiles/HG107.1k.vcf.gz new file mode 100644 index 00000000000..065f94379ea Binary files /dev/null and b/opencga-catalog/src/test/resources/biofiles/HG107.1k.vcf.gz differ diff --git a/opencga-client/src/main/R/R/Clinical-methods.R b/opencga-client/src/main/R/R/Clinical-methods.R index f9d9f038ae1..7ccec05e928 100644 --- a/opencga-client/src/main/R/R/Clinical-methods.R +++ b/opencga-client/src/main/R/R/Clinical-methods.R @@ -552,7 +552,7 @@ setMethod("clinicalClient", "OpencgaR", function(OpencgaR, annotationSet, clinic #' @param approximateCountSamplingSize Sampling size to get the approximate count. Larger values increase accuracy but also increase execution time. #' @param savedFilter Use a saved filter at User level. #' @param includeInterpretation Interpretation ID to include the fields related to this interpretation. - #' @param id List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + #' @param id List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. #' @param region List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000. #' @param type List of types, accepted values are SNV, MNV, INDEL, SV, COPY_NUMBER, COPY_NUMBER_LOSS, COPY_NUMBER_GAIN, INSERTION, DELETION, DUPLICATION, TANDEM_DUPLICATION, BREAKEND, e.g. SNV,INDEL. #' @param study Filter variants from the given studies, these can be either the numeric ID or the alias with the format organization@project:study. @@ -579,7 +579,7 @@ setMethod("clinicalClient", "OpencgaR", function(OpencgaR, annotationSet, clinic #' @param familyProband Specify the proband child to use for the family segregation. #' @param gene List of genes, most gene IDs are accepted (HGNC, Ensembl gene, ...). This is an alias to 'xref' parameter. #' @param ct List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' and 'protein_altering'. - #' @param xref List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ... + #' @param xref List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ... #' @param biotype List of biotypes, e.g. protein_coding. #' @param proteinSubstitution Protein substitution scores include SIFT and PolyPhen. You can query using the score {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. #' @param conservation Filter by conservation score: {conservation_score}[<|>|<=|>=]{number} e.g. phastCons>0.5,phylop<0.1,gerp>0.1. diff --git a/opencga-client/src/main/R/R/Organization-methods.R b/opencga-client/src/main/R/R/Organization-methods.R index 3efaf5eec2f..aeee2457ec2 100644 --- a/opencga-client/src/main/R/R/Organization-methods.R +++ b/opencga-client/src/main/R/R/Organization-methods.R @@ -24,6 +24,7 @@ #' | searchNotes | /{apiVersion}/organizations/notes/search | include, exclude, creationDate, modificationDate, id, scope, visibility, uuid, userId, tags, version | #' | deleteNotes | /{apiVersion}/organizations/notes/{id}/delete | id[*], includeResult | #' | updateNotes | /{apiVersion}/organizations/notes/{id}/update | include, exclude, id[*], includeResult, body[*] | +#' | updateConfiguration | /{apiVersion}/organizations/{organization}/configuration/update | include, exclude, organization[*], includeResult, authenticationOriginsAction, body[*] | #' | info | /{apiVersion}/organizations/{organization}/info | include, exclude, organization[*] | #' | update | /{apiVersion}/organizations/{organization}/update | include, exclude, organization[*], includeResult, adminsAction, body[*] | #' @@ -87,6 +88,18 @@ setMethod("organizationClient", "OpencgaR", function(OpencgaR, id, organization, updateNotes=fetchOpenCGA(object=OpencgaR, category="organizations", categoryId=NULL, subcategory="notes", subcategoryId=id, action="update", params=params, httpMethod="POST", as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/organizations/{organization}/configuration/update: + #' Update the Organization configuration attributes. + #' @param include Fields included in the response, whole JSON path must be provided. + #' @param exclude Fields excluded in the response, whole JSON path must be provided. + #' @param organization Organization id. + #' @param includeResult Flag indicating to include the created or updated document result in the response. + #' @param authenticationOriginsAction Action to be performed if the array of authenticationOrigins is being updated. Allowed values: ['ADD SET REMOVE REPLACE'] + #' @param data JSON containing the params to be updated. + updateConfiguration=fetchOpenCGA(object=OpencgaR, category="organizations", categoryId=organization, + subcategory="configuration", subcategoryId=NULL, action="update", params=params, httpMethod="POST", + as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/organizations/{organization}/info: #' Return the organization information. #' @param include Fields included in the response, whole JSON path must be provided. diff --git a/opencga-client/src/main/R/R/Variant-methods.R b/opencga-client/src/main/R/R/Variant-methods.R index 95fba46fac9..acf4d9b864c 100644 --- a/opencga-client/src/main/R/R/Variant-methods.R +++ b/opencga-client/src/main/R/R/Variant-methods.R @@ -87,7 +87,7 @@ setMethod("variantClient", "OpencgaR", function(OpencgaR, endpointName, params=N #' @param annotationExists Return only annotated variants. #' @param gene List of genes, most gene IDs are accepted (HGNC, Ensembl gene, ...). This is an alias to 'xref' parameter. #' @param ct List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' and 'protein_altering'. - #' @param xref List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ... + #' @param xref List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ... #' @param biotype List of biotypes, e.g. protein_coding. #' @param proteinSubstitution Protein substitution scores include SIFT and PolyPhen. You can query using the score {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. #' @param conservation Filter by conservation score: {conservation_score}[<|>|<=|>=]{number} e.g. phastCons>0.5,phylop<0.1,gerp>0.1. @@ -121,7 +121,7 @@ setMethod("variantClient", "OpencgaR", function(OpencgaR, endpointName, params=N #' @section Endpoint /{apiVersion}/analysis/variant/annotation/query: #' Query variant annotations from any saved versions. - #' @param id List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + #' @param id List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. #' @param region List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000. #' @param include Fields included in the response, whole JSON path must be provided. #' @param exclude Fields excluded in the response, whole JSON path must be provided. @@ -424,7 +424,7 @@ setMethod("variantClient", "OpencgaR", function(OpencgaR, endpointName, params=N #' @param approximateCount Get an approximate count, instead of an exact total count. Reduces execution time. #' @param approximateCountSamplingSize Sampling size to get the approximate count. Larger values increase accuracy but also increase execution time. #' @param savedFilter Use a saved filter at User level. - #' @param id List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + #' @param id List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. #' @param region List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000. #' @param type List of types, accepted values are SNV, MNV, INDEL, SV, COPY_NUMBER, COPY_NUMBER_LOSS, COPY_NUMBER_GAIN, INSERTION, DELETION, DUPLICATION, TANDEM_DUPLICATION, BREAKEND, e.g. SNV,INDEL. #' @param reference Reference allele. @@ -466,7 +466,7 @@ setMethod("variantClient", "OpencgaR", function(OpencgaR, endpointName, params=N #' @param annotationExists Return only annotated variants. #' @param gene List of genes, most gene IDs are accepted (HGNC, Ensembl gene, ...). This is an alias to 'xref' parameter. #' @param ct List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' and 'protein_altering'. - #' @param xref List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ... + #' @param xref List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ... #' @param biotype List of biotypes, e.g. protein_coding. #' @param proteinSubstitution Protein substitution scores include SIFT and PolyPhen. You can query using the score {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. #' @param conservation Filter by conservation score: {conservation_score}[<|>|<=|>=]{number} e.g. phastCons>0.5,phylop<0.1,gerp>0.1. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java index 976ea17a4f6..bba02128a99 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java @@ -715,7 +715,7 @@ public RestResponse search(ObjectMap params) throws ClientExce * execution time. * savedFilter: Use a saved filter at User level. * includeInterpretation: Interpretation ID to include the fields related to this interpretation. - * id: List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + * id: List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. * region: List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: * 2,3:100000-200000. * type: List of types, accepted values are SNV, MNV, INDEL, SV, COPY_NUMBER, COPY_NUMBER_LOSS, COPY_NUMBER_GAIN, INSERTION, @@ -763,7 +763,7 @@ public RestResponse search(ObjectMap params) throws ClientExce * ct: List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' * and 'protein_altering'. * xref: List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, - * dbSNP, ClinVar, HPO, Cosmic, ... + * dbSNP, ClinVar, HPO, Cosmic, HGVS ... * biotype: List of biotypes, e.g. protein_coding. * proteinSubstitution: Protein substitution scores include SIFT and PolyPhen. You can query using the score * {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java index aebe0ba61f5..e925df3b441 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java @@ -24,6 +24,7 @@ import org.opencb.opencga.core.models.notes.NoteCreateParams; import org.opencb.opencga.core.models.notes.NoteUpdateParams; import org.opencb.opencga.core.models.organizations.Organization; +import org.opencb.opencga.core.models.organizations.OrganizationConfiguration; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.response.RestResponse; @@ -134,6 +135,25 @@ public RestResponse updateNotes(String id, NoteUpdateParams data, ObjectMa return execute("organizations", null, "notes", id, "update", params, POST, Note.class); } + /** + * Update the Organization configuration attributes. + * @param organization Organization id. + * @param data JSON containing the params to be updated. + * @param params Map containing any of the following optional parameters. + * include: Fields included in the response, whole JSON path must be provided. + * exclude: Fields excluded in the response, whole JSON path must be provided. + * includeResult: Flag indicating to include the created or updated document result in the response. + * authenticationOriginsAction: Action to be performed if the array of authenticationOrigins is being updated. + * @return a RestResponse object. + * @throws ClientException ClientException if there is any server error. + */ + public RestResponse updateConfiguration(String organization, OrganizationConfiguration data, ObjectMap + params) throws ClientException { + params = params != null ? params : new ObjectMap(); + params.put("body", data); + return execute("organizations", organization, "configuration", null, "update", params, POST, OrganizationConfiguration.class); + } + /** * Return the organization information. * @param organization Organization id. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java index 93ae92a1d88..0f270a52762 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java @@ -32,6 +32,7 @@ import org.opencb.opencga.core.models.analysis.knockout.KnockoutByIndividual; import org.opencb.opencga.core.models.clinical.ExomiserWrapperParams; import org.opencb.opencga.core.models.job.Job; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsExportParams; import org.opencb.opencga.core.models.variant.CircosAnalysisParams; import org.opencb.opencga.core.models.variant.CohortVariantStatsAnalysisParams; @@ -53,7 +54,6 @@ import org.opencb.opencga.core.models.variant.SampleVariantFilterParams; import org.opencb.opencga.core.models.variant.SampleVariantStatsAnalysisParams; import org.opencb.opencga.core.models.variant.VariantExportParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; import org.opencb.opencga.core.models.variant.VariantStatsAnalysisParams; import org.opencb.opencga.core.response.RestResponse; @@ -103,7 +103,7 @@ public VariantClient(String token, ClientConfiguration configuration) { * ct: List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' * and 'protein_altering'. * xref: List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, - * dbSNP, ClinVar, HPO, Cosmic, ... + * dbSNP, ClinVar, HPO, Cosmic, HGVS ... * biotype: List of biotypes, e.g. protein_coding. * proteinSubstitution: Protein substitution scores include SIFT and PolyPhen. You can query using the score * {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. @@ -150,7 +150,7 @@ public RestResponse metadataAnnotation(ObjectMap params) throws Clien /** * Query variant annotations from any saved versions. * @param params Map containing any of the following optional parameters. - * id: List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + * id: List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. * region: List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: * 2,3:100000-200000. * include: Fields included in the response, whole JSON path must be provided. @@ -638,7 +638,7 @@ public RestResponse runPlink(PlinkWrapperParams data, ObjectMap params) thr * approximateCountSamplingSize: Sampling size to get the approximate count. Larger values increase accuracy but also increase * execution time. * savedFilter: Use a saved filter at User level. - * id: List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + * id: List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. * region: List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: * 2,3:100000-200000. * type: List of types, accepted values are SNV, MNV, INDEL, SV, COPY_NUMBER, COPY_NUMBER_LOSS, COPY_NUMBER_GAIN, INSERTION, @@ -710,7 +710,7 @@ public RestResponse runPlink(PlinkWrapperParams data, ObjectMap params) thr * ct: List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases 'loss_of_function' * and 'protein_altering'. * xref: List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, - * dbSNP, ClinVar, HPO, Cosmic, ... + * dbSNP, ClinVar, HPO, Cosmic, HGVS ... * biotype: List of biotypes, e.g. protein_coding. * proteinSubstitution: Protein substitution scores include SIFT and PolyPhen. You can query using the score * {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java index 9731eb2bcc0..e1b00a66cc5 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java @@ -28,21 +28,21 @@ import org.opencb.opencga.core.models.operations.variant.VariantAggregateParams; import org.opencb.opencga.core.models.operations.variant.VariantAnnotationIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantAnnotationSaveParams; +import org.opencb.opencga.core.models.operations.variant.VariantConfigureParams; import org.opencb.opencga.core.models.operations.variant.VariantFamilyIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantFileIndexJobLauncherParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; +import org.opencb.opencga.core.models.operations.variant.VariantPruneParams; +import org.opencb.opencga.core.models.operations.variant.VariantSampleDeleteParams; import org.opencb.opencga.core.models.operations.variant.VariantScoreIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantSecondaryAnnotationIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantSecondarySampleIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsDeleteParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStorageMetadataRepairToolParams; -import org.opencb.opencga.core.models.variant.VariantConfigureParams; -import org.opencb.opencga.core.models.variant.VariantFileDeleteParams; -import org.opencb.opencga.core.models.variant.VariantFileIndexJobLauncherParams; -import org.opencb.opencga.core.models.variant.VariantIndexParams; -import org.opencb.opencga.core.models.variant.VariantPruneParams; -import org.opencb.opencga.core.models.variant.VariantSampleDeleteParams; -import org.opencb.opencga.core.models.variant.VariantStorageMetadataSynchronizeParams; -import org.opencb.opencga.core.models.variant.VariantStudyDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantStorageMetadataSynchronizeParams; +import org.opencb.opencga.core.models.operations.variant.VariantStudyDeleteParams; import org.opencb.opencga.core.response.RestResponse; diff --git a/opencga-client/src/main/javascript/ClinicalAnalysis.js b/opencga-client/src/main/javascript/ClinicalAnalysis.js index 265e9e28a2b..728c21c8632 100644 --- a/opencga-client/src/main/javascript/ClinicalAnalysis.js +++ b/opencga-client/src/main/javascript/ClinicalAnalysis.js @@ -602,8 +602,7 @@ export default class ClinicalAnalysis extends OpenCGAParentClass { * but also increase execution time. * @param {String} [params.savedFilter] - Use a saved filter at User level. * @param {String} [params.includeInterpretation] - Interpretation ID to include the fields related to this interpretation. - * @param {String} [params.id] - List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. - * rs116600158,19:7177679:C:T. + * @param {String} [params.id] - List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. * @param {String} [params.region] - List of regions, these can be just a single chromosome name or regions in the format chr:start-end, * e.g.: 2,3:100000-200000. * @param {String} [params.type] - List of types, accepted values are SNV, MNV, INDEL, SV, COPY_NUMBER, COPY_NUMBER_LOSS, @@ -655,7 +654,7 @@ export default class ClinicalAnalysis extends OpenCGAParentClass { * @param {String} [params.ct] - List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases * 'loss_of_function' and 'protein_altering'. * @param {String} [params.xref] - List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, - * Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ... + * Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ... * @param {String} [params.biotype] - List of biotypes, e.g. protein_coding. * @param {String} [params.proteinSubstitution] - Protein substitution scores include SIFT and PolyPhen. You can query using the score * {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. diff --git a/opencga-client/src/main/javascript/Organization.js b/opencga-client/src/main/javascript/Organization.js index 4385a545eb0..a556f706193 100644 --- a/opencga-client/src/main/javascript/Organization.js +++ b/opencga-client/src/main/javascript/Organization.js @@ -105,6 +105,22 @@ export default class Organization extends OpenCGAParentClass { return this._post("organizations", null, "notes", id, "update", data, params); } + /** Update the Organization configuration attributes + * @param {String} organization - Organization id. + * @param {Object} data - JSON containing the params to be updated. + * @param {Object} [params] - The Object containing the following optional parameters: + * @param {String} [params.include] - Fields included in the response, whole JSON path must be provided. + * @param {String} [params.exclude] - Fields excluded in the response, whole JSON path must be provided. + * @param {Boolean} [params.includeResult = "false"] - Flag indicating to include the created or updated document result in the response. + * The default value is false. + * @param {"ADD SET REMOVE REPLACE"} [params.authenticationOriginsAction = "ADD"] - Action to be performed if the array of + * authenticationOrigins is being updated. The default value is ADD. + * @returns {Promise} Promise object in the form of RestResponse instance. + */ + updateConfiguration(organization, data, params) { + return this._post("organizations", organization, "configuration", null, "update", data, params); + } + /** Return the organization information * @param {String} organization - Organization id. * @param {Object} [params] - The Object containing the following optional parameters: diff --git a/opencga-client/src/main/javascript/Variant.js b/opencga-client/src/main/javascript/Variant.js index 177fdffdae3..2df5dc8d0fc 100644 --- a/opencga-client/src/main/javascript/Variant.js +++ b/opencga-client/src/main/javascript/Variant.js @@ -59,7 +59,7 @@ export default class Variant extends OpenCGAParentClass { * @param {String} [params.ct] - List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases * 'loss_of_function' and 'protein_altering'. * @param {String} [params.xref] - List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, - * Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ... + * Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ... * @param {String} [params.biotype] - List of biotypes, e.g. protein_coding. * @param {String} [params.proteinSubstitution] - Protein substitution scores include SIFT and PolyPhen. You can query using the score * {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. @@ -105,8 +105,7 @@ export default class Variant extends OpenCGAParentClass { /** Query variant annotations from any saved versions * @param {Object} [params] - The Object containing the following optional parameters: - * @param {String} [params.id] - List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. - * rs116600158,19:7177679:C:T. + * @param {String} [params.id] - List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. * @param {String} [params.region] - List of regions, these can be just a single chromosome name or regions in the format chr:start-end, * e.g.: 2,3:100000-200000. * @param {String} [params.include] - Fields included in the response, whole JSON path must be provided. @@ -521,8 +520,7 @@ export default class Variant extends OpenCGAParentClass { * @param {Number} [params.approximateCountSamplingSize] - Sampling size to get the approximate count. Larger values increase accuracy * but also increase execution time. * @param {String} [params.savedFilter] - Use a saved filter at User level. - * @param {String} [params.id] - List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. - * rs116600158,19:7177679:C:T. + * @param {String} [params.id] - List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T. * @param {String} [params.region] - List of regions, these can be just a single chromosome name or regions in the format chr:start-end, * e.g.: 2,3:100000-200000. * @param {String} [params.type] - List of types, accepted values are SNV, MNV, INDEL, SV, COPY_NUMBER, COPY_NUMBER_LOSS, @@ -597,7 +595,7 @@ export default class Variant extends OpenCGAParentClass { * @param {String} [params.ct] - List of SO consequence types, e.g. missense_variant,stop_lost or SO:0001583,SO:0001578. Accepts aliases * 'loss_of_function' and 'protein_altering'. * @param {String} [params.xref] - List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, - * Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ... + * Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ... * @param {String} [params.biotype] - List of biotypes, e.g. protein_coding. * @param {String} [params.proteinSubstitution] - Protein substitution scores include SIFT and PolyPhen. You can query using the score * {protein_score}[<|>|<=|>=]{number} or the description {protein_score}[~=|=]{description} e.g. polyphen>0.1,sift=tolerant. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py index e66873eba68..5302173294d 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py @@ -730,8 +730,8 @@ def query_variant(self, **options): :param str saved_filter: Use a saved filter at User level. :param str include_interpretation: Interpretation ID to include the fields related to this interpretation. - :param str id: List of IDs, these can be rs IDs (dbSNP) or variants in - the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + :param str id: List of variant IDs in the format chrom:start:ref:alt, + e.g. 19:7177679:C:T. :param str region: List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000. @@ -817,7 +817,7 @@ def query_variant(self, **options): aliases 'loss_of_function' and 'protein_altering'. :param str xref: List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, - dbSNP, ClinVar, HPO, Cosmic, ... + dbSNP, ClinVar, HPO, Cosmic, HGVS ... :param str biotype: List of biotypes, e.g. protein_coding. :param str protein_substitution: Protein substitution scores include SIFT and PolyPhen. You can query using the score diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py index a3124bfcf45..bdeac02be52 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py @@ -110,6 +110,26 @@ def update_notes(self, id, data=None, **options): return self._post(category='organizations', resource='update', subcategory='notes', second_query_id=id, data=data, **options) + def update_configuration(self, organization, data=None, **options): + """ + Update the Organization configuration attributes. + PATH: /{apiVersion}/organizations/{organization}/configuration/update + + :param dict data: JSON containing the params to be updated. (REQUIRED) + :param str organization: Organization id. (REQUIRED) + :param str include: Fields included in the response, whole JSON path + must be provided. + :param str exclude: Fields excluded in the response, whole JSON path + must be provided. + :param bool include_result: Flag indicating to include the created or + updated document result in the response. + :param str authentication_origins_action: Action to be performed if + the array of authenticationOrigins is being updated. Allowed + values: ['ADD SET REMOVE REPLACE'] + """ + + return self._post(category='organizations', resource='update', query_id=organization, subcategory='configuration', data=data, **options) + def info(self, organization, **options): """ Return the organization information. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py index e1d63675309..f64f0c66e15 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py @@ -63,7 +63,7 @@ def aggregation_stats(self, **options): aliases 'loss_of_function' and 'protein_altering'. :param str xref: List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, - dbSNP, ClinVar, HPO, Cosmic, ... + dbSNP, ClinVar, HPO, Cosmic, HGVS ... :param str biotype: List of biotypes, e.g. protein_coding. :param str protein_substitution: Protein substitution scores include SIFT and PolyPhen. You can query using the score @@ -124,8 +124,8 @@ def query_annotation(self, **options): Query variant annotations from any saved versions. PATH: /{apiVersion}/analysis/variant/annotation/query - :param str id: List of IDs, these can be rs IDs (dbSNP) or variants in - the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + :param str id: List of variant IDs in the format chrom:start:ref:alt, + e.g. 19:7177679:C:T. :param str region: List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000. @@ -659,8 +659,8 @@ def query(self, **options): approximate count. Larger values increase accuracy but also increase execution time. :param str saved_filter: Use a saved filter at User level. - :param str id: List of IDs, these can be rs IDs (dbSNP) or variants in - the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T. + :param str id: List of variant IDs in the format chrom:start:ref:alt, + e.g. 19:7177679:C:T. :param str region: List of regions, these can be just a single chromosome name or regions in the format chr:start-end, e.g.: 2,3:100000-200000. @@ -785,7 +785,7 @@ def query(self, **options): aliases 'loss_of_function' and 'protein_altering'. :param str xref: List of any external reference, these can be genes, proteins or variants. Accepted IDs include HGNC, Ensembl genes, - dbSNP, ClinVar, HPO, Cosmic, ... + dbSNP, ClinVar, HPO, Cosmic, HGVS ... :param str biotype: List of biotypes, e.g. protein_coding. :param str protein_substitution: Protein substitution scores include SIFT and PolyPhen. You can query using the score diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java b/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java index c2d7cfe4961..dd81f0ff533 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java @@ -68,9 +68,7 @@ public class FieldConstants { public static final String NOTES_VISIBILITY_PARAM = "visibility"; //Sample - public static final String SAMPLE_ID_DESCRIPTION = "Sample data model hosts information about any biological material, normally " - + "extracted from an _Individual_, that is used for a particular analysis. This is the main data model, it stores the most " - + "basic and important information."; + public static final String SAMPLE_ID_DESCRIPTION = "Sample ID."; public static final String SAMPLE_PROCESSING_DESCRIPTION = "Describes how the sample was processed in the lab."; public static final String SAMPLE_SAMPLE_COLLECTION_DESCRIPTION = "Describes how the sample was collected."; public static final String SAMPLE_QUALITY_CONTROL_DESCRIPTION = "Contains different metrics to evaluate the quality of the sample."; @@ -516,6 +514,16 @@ public class FieldConstants { public static final String VARIANT_STATS_DESCRIPTION_DESCRIPTION = "Variant stats description."; public static final String VARIANT_STATS_QUERY_DESCRIPTION = "Variant stats query in JSON format."; + // Alignment index parameter descriptions + public static final String ALIGNMENT_INDEX_FILE_ID_DESCRIPTION = "File ID, (i.e., BAM/CRAM file ID)."; + public static final String ALIGNMENT_INDEX_OVERWRITE_DESCRIPTION = "Flag to force indexing."; + + // Coverage index parameter descriptions + public static final String COVERAGE_INDEX_BAM_FILE_ID_DESCRIPTION = "BAM file ID."; + public static final String COVERAGE_INDEX_BAI_FILE_ID_DESCRIPTION = "BAI file ID."; + public static final String COVERAGE_INDEX_OVERWRITE_DESCRIPTION = "Window size (i.e., the size of the bins, in bases, for the output" + + " of the BIGWIG file)."; + // Alignment QC analysis (asample-qc-run) public static final String ALIGNMENT_QC_BAM_FILE_DESCRIPTION = "ID for the BAM file to process."; public static final String ALIGNMENT_QC_SKIP_DESCRIPTION = "To skip any alignment QC metrics use the following keywords (separated by" @@ -523,4 +531,6 @@ public class FieldConstants { + AlignmentQcParams.FASTQC_METRICS_SKIP_VALUE; public static final String ALIGNMENT_QC_OVERWRITE_DESCRIPTION = "To overwrite the QC metrics already computed."; + // Exomiser + public static final String EXOMISER_CLINICAL_ANALYSIS_TYPE_DESCRIPTION = "Clinical analysis type: SINGLE or FAMILY."; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java b/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java index 6bf964e760f..4a0a279af55 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java @@ -91,6 +91,8 @@ public class ParamConstants { public static final String OPENCGA_TOKEN_CLI_PARAM = "--opencga-token"; + public static final String RESUME_DESCRIPTION = "Resume a previously failed index operation"; + public static final String CATEGORY = "category"; public static final String CATEGORY_DESCRIPTION = "Category corresponding to the id's provided."; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java b/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java index 19338531443..bf62bb7c09f 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java @@ -16,6 +16,8 @@ package org.opencb.opencga.core.common; +import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,20 +34,21 @@ public class MailUtils { private static final Logger logger = LoggerFactory.getLogger(MailUtils.class); - public static void sendResetPasswordMail(String to, String newPassword, final String mailUser, final String mailPassword, - String mailHost, String mailPort) throws Exception { - sendResetPasswordMail(to, newPassword, mailUser, mailPassword, - mailHost, mailPort, "true"); - } + public static void sendResetPasswordMail(String to, String newPassword, final String mailUser, final String mailPassword, - String mailHost, String mailPort, String ssl) throws Exception { + String mailHost, String mailPort, String userId) throws Exception { Properties props = new Properties(); - props.put("mail.smtp.auth", "true"); - props.put("mail.smtp.starttls.enable", ssl); props.put("mail.smtp.host", mailHost); props.put("mail.smtp.port", mailPort); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.ssl.enable", "true"); + props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.smtp.starttls.required", "true"); + props.put("mail.smtp.ssl.protocols", "TLSv1.2"); + props.put("mail.smtp.ssl.checkserveridentity", "true"); + props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); Session session = Session.getInstance(props, new javax.mail.Authenticator() { @@ -59,20 +62,31 @@ protected PasswordAuthentication getPasswordAuthentication() { message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); - message.setSubject("Your password has been reset"); - message.setText("Hello, \n" + - "You can now login using this new password:" + - "\n\n" + - newPassword + - "\n\n\n" + - "Please change it when you first login" + - "\n\n" + - "Best regards,\n\n" + - "Systems Genomics Laboratory" + - "\n"); + message.setSubject("XetaBase: Password Reset"); + message.setText(getEmailContent(userId,newPassword)); Transport.send(message); } + public static String getEmailContent(String userId, String temporaryPassword) { + StringBuilder sb = new StringBuilder(); + + sb.append("Hi ").append(userId).append(",\n\n"); + sb.append("We confirm that your password has been successfully reset.\n\n"); + sb.append("Please find your new login credentials below:\n\n"); + sb.append("User ID: ").append(userId).append("\n"); + sb.append("Temporary Password: ").append(temporaryPassword).append("\n\n"); + sb.append("For your security, we strongly recommend that you log in using the temporary password provided "); + sb.append("and promptly create a new password that is unique and known only to you. "); + sb.append("You can change your password by accessing \"Your Profile > Change Password\" in your User Profile.\n\n"); + sb.append("If you did not request a password reset, please contact our support team immediately at support@zettagenomics.com.\n\n"); + sb.append("Best regards,\n\n"); + sb.append("ZettaGenomics Support Team \n\n"); + + + + return sb.toString(); + } + public static void sendMail(String smtpServer, String to, String from, String subject, String body) throws Exception { Properties props = System.getProperties(); diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/AbstractServerConfiguration.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/AbstractServerConfiguration.java index 060c72ceacc..cffc7fe68b8 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/AbstractServerConfiguration.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/AbstractServerConfiguration.java @@ -16,13 +16,13 @@ package org.opencb.opencga.core.config; + /** * Created by imedina on 25/04/16. */ public abstract class AbstractServerConfiguration { protected int port; - protected String logFile; public AbstractServerConfiguration() { } @@ -35,7 +35,6 @@ public AbstractServerConfiguration(int port) { public String toString() { final StringBuilder sb = new StringBuilder("ServerConfiguration{"); sb.append("port=").append(port); - sb.append(", logFile='").append(logFile).append('\''); sb.append('}'); return sb.toString(); } @@ -49,13 +48,9 @@ public AbstractServerConfiguration setPort(int port) { return this; } - public String getLogFile() { - return logFile; - } - - public AbstractServerConfiguration setLogFile(String logFile) { - this.logFile = logFile; - return this; + @Deprecated + protected void setLogFile(Object o) { + Configuration.reportUnusedField("configuration.yml#server.[rest|grpc].logFile", o); } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/AuthenticationOrigin.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/AuthenticationOrigin.java index 2b8bfab8624..2b4962b024a 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/AuthenticationOrigin.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/AuthenticationOrigin.java @@ -31,7 +31,8 @@ public class AuthenticationOrigin { public enum AuthenticationType { OPENCGA, LDAP, - AzureAD + AzureAD, + SSO } // Possible keys of the options map diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java index bb5057a76e2..be957b160a7 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java @@ -66,6 +66,8 @@ public class Configuration { private static Logger logger; + private static final Set reportedFields = new HashSet<>(); + private static final String DEFAULT_CONFIGURATION_FORMAT = "yaml"; static { @@ -200,6 +202,16 @@ private static void overwriteWithEnvironmentVariables(Configuration configuratio } } + public static void reportUnusedField(String field, Object value) { + // Report only if the value is not null and not an empty string + if (value != null && !(value instanceof String && ((String) value).isEmpty())) { + if (reportedFields.add(field)) { + // Only log the first time a field is found + logger.warn("Ignored configuration option '{}' with value '{}'. The option was deprecated and removed.", field, value); + } + } + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("Configuration{"); @@ -246,9 +258,7 @@ public String getLogFile() { @Deprecated public Configuration setLogFile(String logFile) { - if (logFile != null) { - logger.warn("Deprecated option 'configuration.yml#logFile'"); - } + reportUnusedField("configuration.yml#logFile", logFile); return this; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Execution.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Execution.java index 2bc200fe8fb..1384d0a7df1 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Execution.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Execution.java @@ -18,6 +18,7 @@ import org.opencb.commons.datastore.core.ObjectMap; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,6 +28,16 @@ */ public class Execution { + public static final String JOBS_REUSE_ENABLED = "jobs.reuse.enabled"; + public static final boolean JOBS_REUSE_ENABLED_DEFAULT = true; + public static final String JOBS_REUSE_TOOLS = "jobs.reuse.tools"; + public static final List JOBS_REUSE_TOOLS_DEFAULT = Arrays.asList( + "variant-index", + "variant-stats-index", + "variant-annotation-index", + "variant-secondary-annotation-index", + "variant-secondary-sample-index" + ); private String id; private String defaultQueue; private String availableQueues; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/RestServerConfiguration.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/RestServerConfiguration.java index 99ea3ada10c..f87075e9a02 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/RestServerConfiguration.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/RestServerConfiguration.java @@ -16,52 +16,132 @@ package org.opencb.opencga.core.config; +import java.util.Objects; + /** * Created by imedina on 22/05/16. */ public class RestServerConfiguration extends AbstractServerConfiguration { - private int defaultLimit; - private int maxLimit; - - + public HttpConfiguration httpConfiguration = new HttpConfiguration(); public RestServerConfiguration() { } public RestServerConfiguration(int port) { - this(port, 2000, 5000); - } - - public RestServerConfiguration(int port, int defaultLimit, int maxLimit) { super(port); - this.defaultLimit = defaultLimit; - this.maxLimit = maxLimit; } @Override public String toString() { final StringBuilder sb = new StringBuilder("RestServerConfiguration{"); - sb.append("defaultLimit=").append(defaultLimit); - sb.append(", maxLimit=").append(maxLimit); + sb.append("port=").append(port); + sb.append(", httpConfiguration=").append(httpConfiguration); sb.append('}'); return sb.toString(); } - public int getDefaultLimit() { - return defaultLimit; + @Deprecated + protected void setDefaultLimit(Object o) { + Configuration.reportUnusedField("configuration.yml#server.rest.defaultLimit", o); } - public RestServerConfiguration setDefaultLimit(int defaultLimit) { - this.defaultLimit = defaultLimit; - return this; + @Deprecated + protected void setMaxLimit(Object o) { + Configuration.reportUnusedField("configuration.yml#server.rest.maxLimit", o); } - public int getMaxLimit() { - return maxLimit; + public HttpConfiguration getHttpConfiguration() { + return httpConfiguration; } - public RestServerConfiguration setMaxLimit(int maxLimit) { - this.maxLimit = maxLimit; + public RestServerConfiguration setHttpConfiguration(HttpConfiguration httpConfiguration) { + this.httpConfiguration = httpConfiguration; return this; } + + public static class HttpConfiguration { + private int outputBufferSize = -1; + private int outputAggregationSize = -1; + private int requestHeaderSize = -1; + private int responseHeaderSize = -1; + private int headerCacheSize = -1; + + public int getOutputBufferSize() { + return outputBufferSize; + } + + public HttpConfiguration setOutputBufferSize(int outputBufferSize) { + this.outputBufferSize = outputBufferSize; + return this; + } + + public int getOutputAggregationSize() { + return outputAggregationSize; + } + + public HttpConfiguration setOutputAggregationSize(int outputAggregationSize) { + this.outputAggregationSize = outputAggregationSize; + return this; + } + + public int getRequestHeaderSize() { + return requestHeaderSize; + } + + public HttpConfiguration setRequestHeaderSize(int requestHeaderSize) { + this.requestHeaderSize = requestHeaderSize; + return this; + } + + public int getResponseHeaderSize() { + return responseHeaderSize; + } + + public HttpConfiguration setResponseHeaderSize(int responseHeaderSize) { + this.responseHeaderSize = responseHeaderSize; + return this; + } + + public int getHeaderCacheSize() { + return headerCacheSize; + } + + public HttpConfiguration setHeaderCacheSize(int headerCacheSize) { + this.headerCacheSize = headerCacheSize; + return this; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HttpConfiguration{"); + sb.append("outputBufferSize=").append(outputBufferSize); + sb.append(", outputAggregationSize=").append(outputAggregationSize); + sb.append(", requestHeaderSize=").append(requestHeaderSize); + sb.append(", responseHeaderSize=").append(responseHeaderSize); + sb.append(", headerCacheSize=").append(headerCacheSize); + sb.append('}'); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HttpConfiguration that = (HttpConfiguration) o; + return outputBufferSize == that.outputBufferSize && + outputAggregationSize == that.outputAggregationSize && + requestHeaderSize == that.requestHeaderSize && + responseHeaderSize == that.responseHeaderSize && + headerCacheSize == that.headerCacheSize; + } + + @Override + public int hashCode() { + return Objects.hash(outputBufferSize, outputAggregationSize, requestHeaderSize, responseHeaderSize, headerCacheSize); + } + } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/JwtPayload.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/JwtPayload.java index 06949efb937..4d8df0f6f0a 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/JwtPayload.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/JwtPayload.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.opencga.core.common.JacksonUtils; +import org.opencb.opencga.core.config.AuthenticationOrigin; import java.util.Base64; import java.util.Date; @@ -12,15 +13,20 @@ public class JwtPayload { private final String userId; private final String organization; + private final AuthenticationOrigin.AuthenticationType authOrigin; private final String issuer; // Issuer of the JWT token. private final Date issuedAt; // Time when the JWT was issued. private final Date expirationTime; // Expiration time of the JWT. private final String token; - public JwtPayload(String userId, String organization, String issuer, Date issuedAt, Date expirationTime, String token) { + public static final String AUTH_ORIGIN = "authOrigin"; + + public JwtPayload(String userId, String organization, AuthenticationOrigin.AuthenticationType authOrigin, String issuer, Date issuedAt, + Date expirationTime, String token) { this.token = token; this.userId = userId; this.organization = organization; + this.authOrigin = authOrigin; this.issuer = issuer; this.issuedAt = issuedAt; this.expirationTime = expirationTime; @@ -56,6 +62,12 @@ public JwtPayload(String token) { this.organization = claimsMap.getString("aud"); this.issuer = claimsMap.getString("iss"); + if (claimsMap.containsKey(AUTH_ORIGIN)) { + this.authOrigin = AuthenticationOrigin.AuthenticationType.valueOf(claimsMap.getString(AUTH_ORIGIN)); + } else { + this.authOrigin = null; + } + if (claimsMap.containsKey("iat")) { long iat = 1000L * claimsMap.getLong("iat"); this.issuedAt = new Date(iat); @@ -77,6 +89,7 @@ public String toString() { final StringBuilder sb = new StringBuilder("JwtPayload{"); sb.append("userId='").append(userId).append('\''); sb.append(", organization='").append(organization).append('\''); + sb.append(", authOrigin=").append(authOrigin); sb.append(", issuer='").append(issuer).append('\''); sb.append(", issuedAt=").append(issuedAt); sb.append(", expirationTime=").append(expirationTime); @@ -101,6 +114,10 @@ public String getOrganization() { return organization; } + public AuthenticationOrigin.AuthenticationType getAuthOrigin() { + return authOrigin; + } + public String getIssuer() { return issuer; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/AlignmentIndexParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/AlignmentIndexParams.java index b4e4dc87607..43bc73f7931 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/AlignmentIndexParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/AlignmentIndexParams.java @@ -1,36 +1,41 @@ package org.opencb.opencga.core.models.alignment; +import org.opencb.commons.annotations.DataField; +import org.opencb.opencga.core.api.FieldConstants; import org.opencb.opencga.core.tools.ToolParams; public class AlignmentIndexParams extends ToolParams { public static final String DESCRIPTION = "Alignment index params"; - private String file; + @DataField(id = "fileId", description = FieldConstants.ALIGNMENT_INDEX_FILE_ID_DESCRIPTION, required = true) + private String fileId; + + @DataField(id = "overwrite", description = FieldConstants.ALIGNMENT_INDEX_OVERWRITE_DESCRIPTION) private boolean overwrite; public AlignmentIndexParams() { } - public AlignmentIndexParams(String file, boolean overwrite) { - this.file = file; + public AlignmentIndexParams(String fileId, boolean overwrite) { + this.fileId = fileId; this.overwrite = overwrite; } @Override public String toString() { final StringBuilder sb = new StringBuilder("AlignmentIndexParams{"); - sb.append("file='").append(file).append('\''); + sb.append("fileId='").append(fileId).append('\''); sb.append(", overwrite=").append(overwrite); sb.append('}'); return sb.toString(); } - public String getFile() { - return file; + public String getFileId() { + return fileId; } - public AlignmentIndexParams setFile(String file) { - this.file = file; + public AlignmentIndexParams setFileId(String fileId) { + this.fileId = fileId; return this; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/CoverageIndexParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/CoverageIndexParams.java index f655829625d..9ce861660f2 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/CoverageIndexParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/alignment/CoverageIndexParams.java @@ -2,15 +2,22 @@ import com.fasterxml.jackson.annotation.JsonProperty; +import org.opencb.commons.annotations.DataField; +import org.opencb.opencga.core.api.FieldConstants; +import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.tools.ToolParams; public class CoverageIndexParams extends ToolParams { public static final String DESCRIPTION = "Coverage computation parameters"; + @DataField(id = "bamFileId", description = FieldConstants.COVERAGE_INDEX_BAM_FILE_ID_DESCRIPTION, required = true) private String bamFileId; + + @DataField(id = "baiFileId", description = FieldConstants.COVERAGE_INDEX_BAI_FILE_ID_DESCRIPTION) private String baiFileId; - @JsonProperty(defaultValue = "1") + @DataField(id = "windowSize", description = FieldConstants.COVERAGE_INDEX_OVERWRITE_DESCRIPTION, + defaultValue = ParamConstants.COVERAGE_WINDOW_SIZE_DEFAULT) private int windowSize; public CoverageIndexParams() { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ExomiserWrapperParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ExomiserWrapperParams.java index 1fa40dfbaf7..3b836063439 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ExomiserWrapperParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ExomiserWrapperParams.java @@ -1,5 +1,7 @@ package org.opencb.opencga.core.models.clinical; +import org.opencb.commons.annotations.DataField; +import org.opencb.opencga.core.api.FieldConstants; import org.opencb.opencga.core.tools.ToolParams; import java.util.Map; @@ -7,14 +9,22 @@ public class ExomiserWrapperParams extends ToolParams { public static final String DESCRIPTION = "Exomiser parameters"; + @DataField(id = "sample", description = FieldConstants.SAMPLE_ID_DESCRIPTION) private String sample; + + @DataField(id = "clinicalAnalysisType", description = FieldConstants.EXOMISER_CLINICAL_ANALYSIS_TYPE_DESCRIPTION, + defaultValue = "SINGLE") + private String clinicalAnalysisType; + + @DataField(id = "outdir", description = FieldConstants.JOB_OUT_DIR_DESCRIPTION) private String outdir; public ExomiserWrapperParams() { } - public ExomiserWrapperParams(String sample, String outdir) { + public ExomiserWrapperParams(String sample, String clinicalAnalysisType, String outdir) { this.sample = sample; + this.clinicalAnalysisType = clinicalAnalysisType; this.outdir = outdir; } @@ -22,6 +32,7 @@ public ExomiserWrapperParams(String sample, String outdir) { public String toString() { final StringBuilder sb = new StringBuilder("ExomiserWrapperParams{"); sb.append("sample='").append(sample).append('\''); + sb.append(", clinicalAnalysisType=").append(clinicalAnalysisType); sb.append(", outdir='").append(outdir).append('\''); sb.append('}'); return sb.toString(); @@ -36,6 +47,15 @@ public ExomiserWrapperParams setSample(String sample) { return this; } + public String getClinicalAnalysisType() { + return clinicalAnalysisType; + } + + public ExomiserWrapperParams setClinicalAnalysisType(String clinicalAnalysisType) { + this.clinicalAnalysisType = clinicalAnalysisType; + return this; + } + public String getOutdir() { return outdir; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/common/IndexStatus.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/common/IndexStatus.java index 5a1518440ab..83836d0f7d4 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/common/IndexStatus.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/common/IndexStatus.java @@ -13,8 +13,9 @@ public class IndexStatus extends InternalStatus { */ public static final String NONE = "NONE"; public static final String INDEXING = "INDEXING"; + public static final String INVALID = "INVALID"; - public static final List STATUS_LIST = Arrays.asList(READY, DELETED, NONE, INDEXING); + public static final List STATUS_LIST = Arrays.asList(READY, DELETED, NONE, INDEXING, INVALID); public IndexStatus(String status, String message) { if (isValid(status)) { @@ -50,6 +51,7 @@ public static boolean isValid(String status) { return status != null && (status.equals(READY) || status.equals(DELETED) + || status.equals(INVALID) || status.equals(NONE) || status.equals(INDEXING)); } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/file/VariantIndexStatus.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/file/VariantIndexStatus.java index 3dbe5f21f7d..a6592c369bf 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/file/VariantIndexStatus.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/file/VariantIndexStatus.java @@ -19,7 +19,8 @@ public class VariantIndexStatus extends IndexStatus { public static final String TRANSFORMED = "TRANSFORMED"; public static final String LOADING = "LOADING"; - public static final List STATUS_LIST = Arrays.asList(READY, DELETED, NONE, TRANSFORMED, TRANSFORMING, LOADING, INDEXING); + public static final List STATUS_LIST = Arrays.asList(READY, DELETED, NONE, TRANSFORMED, TRANSFORMING, LOADING, INDEXING, + INVALID); public VariantIndexStatus(String status, String message) { if (isValid(status)) { @@ -56,6 +57,7 @@ public static boolean isValid(String status) { && (status.equals(READY) || status.equals(DELETED) || status.equals(NONE) + || status.equals(INVALID) || status.equals(TRANSFORMED) || status.equals(TRANSFORMING) || status.equals(LOADING) diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateFamilyParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateFamilyParams.java index 42b9073b45d..5f73c45e964 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateFamilyParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateFamilyParams.java @@ -16,14 +16,20 @@ package org.opencb.opencga.core.models.operations.variant; +import org.opencb.commons.annotations.DataField; +import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.tools.ToolParams; import java.util.List; public class VariantAggregateFamilyParams extends ToolParams { public static final String DESCRIPTION = "Variant aggregate family params."; + + @DataField(description = "Samples within the same study to aggregate") private List samples; + @DataField(description = "Genotype to be used in gaps. Either 0/0, ./. or ?/?") private String gapsGenotype; + @DataField(description = ParamConstants.RESUME_DESCRIPTION) private boolean resume; public VariantAggregateFamilyParams() { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateParams.java index 87880318218..434643acc33 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAggregateParams.java @@ -16,13 +16,17 @@ package org.opencb.opencga.core.models.operations.variant; +import org.opencb.commons.annotations.DataField; +import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.tools.ToolParams; public class VariantAggregateParams extends ToolParams { public static final String DESCRIPTION = "Variant aggregate params."; - // private String region; + // private String region + @DataField(description = "Overwrite aggregation for all files and variants. Repeat operation for already processed variants.") private boolean overwrite; + @DataField(description = ParamConstants.RESUME_DESCRIPTION) private boolean resume; public VariantAggregateParams() { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationDeleteParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationDeleteParams.java index bb1072e5f98..797ee2cbdf5 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationDeleteParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationDeleteParams.java @@ -16,10 +16,12 @@ package org.opencb.opencga.core.models.operations.variant; +import org.opencb.commons.annotations.DataField; import org.opencb.opencga.core.tools.ToolParams; public class VariantAnnotationDeleteParams extends ToolParams { + @DataField(description = "Variant Annotation identifier") private String annotationId; public VariantAnnotationDeleteParams() { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationSaveParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationSaveParams.java index 373261ea395..948f3a453c0 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationSaveParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantAnnotationSaveParams.java @@ -16,11 +16,13 @@ package org.opencb.opencga.core.models.operations.variant; +import org.opencb.commons.annotations.DataField; import org.opencb.opencga.core.tools.ToolParams; public class VariantAnnotationSaveParams extends ToolParams { public static final String DESCRIPTION = "Variant annotation save params"; + @DataField(description = "New Variant Annotation identifier") private String annotationId; public VariantAnnotationSaveParams() { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantConfigureParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantConfigureParams.java similarity index 85% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantConfigureParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantConfigureParams.java index 3365908a43f..51a22f36337 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantConfigureParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantConfigureParams.java @@ -1,4 +1,4 @@ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.commons.datastore.core.ObjectMap; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFamilyIndexParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFamilyIndexParams.java index 0a2c8d3b8b5..23f89c9bbd0 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFamilyIndexParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFamilyIndexParams.java @@ -20,6 +20,7 @@ import java.util.List; +@Deprecated public class VariantFamilyIndexParams extends ToolParams { public static final String DESCRIPTION = "Variant family index params."; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantFileDeleteParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFileDeleteParams.java similarity index 84% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantFileDeleteParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFileDeleteParams.java index 82a1daa3670..85ba64ce78c 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantFileDeleteParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFileDeleteParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.opencga.core.tools.ToolParams; @@ -34,6 +34,7 @@ public VariantFileDeleteParams(List file, boolean resume) { private List file; private boolean resume; + private boolean force; public List getFile() { return file; @@ -52,4 +53,13 @@ public VariantFileDeleteParams setResume(boolean resume) { this.resume = resume; return this; } + + public boolean isForce() { + return force; + } + + public VariantFileDeleteParams setForce(boolean force) { + this.force = force; + return this; + } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantFileIndexJobLauncherParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFileIndexJobLauncherParams.java similarity index 97% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantFileIndexJobLauncherParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFileIndexJobLauncherParams.java index 75404921751..28f3dbc8ff1 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantFileIndexJobLauncherParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantFileIndexJobLauncherParams.java @@ -1,4 +1,4 @@ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.opencga.core.tools.ToolParams; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantIndexParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantIndexParams.java similarity index 98% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantIndexParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantIndexParams.java index 1e0cd7dac49..505327de602 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantIndexParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantIndexParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.biodata.models.variant.metadata.Aggregation; import org.opencb.commons.annotations.DataField; @@ -81,7 +81,7 @@ public VariantIndexParams(String file, @DataField(description = "List of files to be indexed.") private String file; - @DataField(description = "Resume a previously failed index operation") + @DataField(description = ParamConstants.RESUME_DESCRIPTION) private boolean resume; @DataField(description = "Output directory") private String outdir; @@ -101,7 +101,7 @@ public VariantIndexParams(String file, private String failOnMalformedLines; @DataField(description = "Indicate that the files to be loaded are part of a family. " - + "This will set 'load-hom-ref' to YES if it was in AUTO and execute 'family-index' afterwards") + + "This will set 'load-hom-ref' to YES if it was in AUTO") private boolean family; @DataField(description = "Indicate that the files to be loaded contain somatic samples. " + "This will set 'load-hom-ref' to YES if it was in AUTO.") diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantPruneParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantPruneParams.java similarity index 93% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantPruneParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantPruneParams.java index ed0449871e6..d59ead10217 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantPruneParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantPruneParams.java @@ -1,4 +1,4 @@ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.opencga.core.tools.ToolParams; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantSampleDeleteParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantSampleDeleteParams.java similarity index 96% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantSampleDeleteParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantSampleDeleteParams.java index 4857a35a650..3ae43e6b61f 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantSampleDeleteParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantSampleDeleteParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.opencga.core.tools.ToolParams; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantStorageMetadataSynchronizeParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantStorageMetadataSynchronizeParams.java similarity index 92% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantStorageMetadataSynchronizeParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantStorageMetadataSynchronizeParams.java index f5a2b25f11d..002bff2f937 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantStorageMetadataSynchronizeParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantStorageMetadataSynchronizeParams.java @@ -1,4 +1,4 @@ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.opencga.core.tools.ToolParams; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantStudyDeleteParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantStudyDeleteParams.java similarity index 95% rename from opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantStudyDeleteParams.java rename to opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantStudyDeleteParams.java index 20f533e711a..1922fe78bf0 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/variant/VariantStudyDeleteParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/operations/variant/VariantStudyDeleteParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.opencb.opencga.core.models.variant; +package org.opencb.opencga.core.models.operations.variant; import org.opencb.opencga.core.tools.ToolParams; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationUpdateParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationUpdateParams.java index 679175e1ac8..8c4d42ea570 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationUpdateParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationUpdateParams.java @@ -11,7 +11,7 @@ import static org.opencb.opencga.core.common.JacksonUtils.getUpdateObjectMapper; -public class OrganizationUpdateParams { +public final class OrganizationUpdateParams { @DataField(id = "name", description = FieldConstants.ORGANIZATION_NAME_DESCRIPTION) private String name; @@ -28,9 +28,6 @@ public class OrganizationUpdateParams { @DataField(id = "modificationDate", description = FieldConstants.GENERIC_MODIFICATION_DATE_DESCRIPTION) private String modificationDate; - @DataField(id = "configuration", description = FieldConstants.ORGANIZATION_CONFIGURATION_DESCRIPTION) - private OrganizationConfiguration configuration; - @DataField(id = "attributes", description = FieldConstants.GENERIC_ATTRIBUTES_DESCRIPTION) private Map attributes; @@ -38,13 +35,12 @@ public OrganizationUpdateParams() { } public OrganizationUpdateParams(String name, String owner, List admins, String creationDate, String modificationDate, - OrganizationConfiguration configuration, Map attributes) { + Map attributes) { this.name = name; this.owner = owner; this.admins = admins; this.creationDate = creationDate; this.modificationDate = modificationDate; - this.configuration = configuration; this.attributes = attributes; } @@ -61,7 +57,6 @@ public String toString() { sb.append(", admins=").append(admins); sb.append(", creationDate='").append(creationDate).append('\''); sb.append(", modificationDate='").append(modificationDate).append('\''); - sb.append(", configuration=").append(configuration); sb.append(", attributes=").append(attributes); sb.append('}'); return sb.toString(); @@ -112,15 +107,6 @@ public OrganizationUpdateParams setModificationDate(String modificationDate) { return this; } - public OrganizationConfiguration getConfiguration() { - return configuration; - } - - public OrganizationUpdateParams setConfiguration(OrganizationConfiguration configuration) { - this.configuration = configuration; - return this; - } - public Map getAttributes() { return attributes; } diff --git a/opencga-core/src/main/resources/configuration.yml b/opencga-core/src/main/resources/configuration.yml index ac4acf283d8..6adc576a258 100644 --- a/opencga-core/src/main/resources/configuration.yml +++ b/opencga-core/src/main/resources/configuration.yml @@ -73,12 +73,20 @@ authentication: server: rest: port: ${OPENCGA.SERVER.REST.PORT} - logFile: null - defaultLimit: 2000 - maxLimit: 5000 + httpConfiguration: + # The size in bytes of the output buffer used to aggregate HTTP output + outputBufferSize: 32768 + # The maximum size in bytes for HTTP output to be aggregated + outputAggregationSize: 8192 + # The maximum allowed size in bytes for a HTTP request header + requestHeaderSize: 8192 + # The maximum allowed size in bytes for a HTTP response header + responseHeaderSize: 8192 + # The maximum allowed size in bytes for a HTTP header field cache + headerCacheSize: 4096 + grpc: port: ${OPENCGA.SERVER.GRPC.PORT} - logFile: null optimizations: simplifyPermissions: ${OPENCGA_OPTIMIZATIONS_SIMPLIFY_PERMISSIONS} @@ -140,7 +148,8 @@ analysis: maxConcurrentJobs: variant-index: 20 variant-annotation-index: 5 - variant-secondary-index: 2 + variant-secondary-annotation-index: 2 + variant-secondary-sample-index: 2 options: ## Job reuse policy. Do not create a new job if an equivalent PENDING or QUEUED job exists. jobs.reuse.enabled: true @@ -150,6 +159,7 @@ analysis: - "variant-stats-index" - "variant-annotation-index" - "variant-secondary-annotation-index" + - "variant-secondary-sample-index" ## Local executor configuration local.maxConcurrentJobs: 2 # Max number of concurrent jobs to be executed locally in the master ## Azure Batch Service configuration example diff --git a/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java b/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java index 948bb037dd8..df649837113 100644 --- a/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java +++ b/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java @@ -20,8 +20,11 @@ import org.junit.experimental.categories.Category; import org.opencb.opencga.core.testclassification.duration.ShortTests; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; import java.util.*; /** @@ -31,7 +34,7 @@ public class ConfigurationTest { @Test - public void testDefault() { + public void testDefault() throws IOException { Configuration configuration = new Configuration(); configuration.setLogLevel("INFO"); @@ -71,7 +74,7 @@ public void testDefault() { configuration.setAudit(audit); ServerConfiguration serverConfiguration = new ServerConfiguration(); - RestServerConfiguration rest = new RestServerConfiguration(1000, 100, 1000); + RestServerConfiguration rest = new RestServerConfiguration(1000); GrpcServerConfiguration grpc = new GrpcServerConfiguration(1001); serverConfiguration.setGrpc(grpc); serverConfiguration.setRest(rest); @@ -90,11 +93,10 @@ public void testDefault() { // catalogConfiguration.getStorageEngines().add(storageEngineConfiguration1); // catalogConfiguration.getStorageEngines().add(storageEngineConfiguration2); - try { - configuration.serialize(new FileOutputStream("/tmp/configuration-test.yml")); - } catch (IOException e) { - e.printStackTrace(); - } + Path outdir = Paths.get("target/test-data", "junit-opencga-" + + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss.SSS").format(new Date())); + Files.createDirectories(outdir); + configuration.serialize(Files.newOutputStream(outdir.resolve("configuration-test.yml").toFile().toPath())); } @Test diff --git a/opencga-core/src/test/resources/configuration-test.yml b/opencga-core/src/test/resources/configuration-test.yml index 4d8d3c10f25..dad67f8c5dc 100644 --- a/opencga-core/src/test/resources/configuration-test.yml +++ b/opencga-core/src/test/resources/configuration-test.yml @@ -46,7 +46,7 @@ authentication: server: rest: port: 8080 - logFile: null + logFile: "some_file_but_this_field_is_deprecated" defaultLimit: 2000 maxLimit: 5000 diff --git a/opencga-core/src/test/resources/log4j2-test.xml b/opencga-core/src/test/resources/log4j2-test.xml new file mode 100644 index 00000000000..dce741b72cb --- /dev/null +++ b/opencga-core/src/test/resources/log4j2-test.xml @@ -0,0 +1,20 @@ + + + + ${sys:opencga.log.level:-info} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/AbstractStorageServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/AbstractStorageServer.java index e1e880f2d47..d4bcb75d575 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/AbstractStorageServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/AbstractStorageServer.java @@ -16,58 +16,29 @@ package org.opencb.opencga.server; -import org.apache.commons.lang3.StringUtils; import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * Created by imedina on 02/01/16. */ public abstract class AbstractStorageServer { - @Deprecated - protected Path configDir; protected Path opencgaHome; protected int port; protected Configuration configuration; protected StorageConfiguration storageConfiguration; - /** - * This is the default StorageEngine to use when it is not provided by the client. - */ - @Deprecated - protected String defaultStorageEngine; - protected Logger logger; - - @Deprecated - public AbstractStorageServer() { - initDefaultConfigurationFiles(); - } - - @Deprecated - public AbstractStorageServer(int port, String defaultStorageEngine) { - initDefaultConfigurationFiles(); - - this.port = port; - if (StringUtils.isNotEmpty(defaultStorageEngine)) { - this.defaultStorageEngine = defaultStorageEngine; - } else { - this.defaultStorageEngine = storageConfiguration.getVariant().getDefaultEngine(); - } - } - public AbstractStorageServer(Path opencgaHome) { this(opencgaHome, 0); } @@ -93,33 +64,15 @@ private void initConfigurationFiles(Path opencgaHome) { try { if (opencgaHome != null && Files.exists(opencgaHome) && Files.isDirectory(opencgaHome) && Files.exists(opencgaHome.resolve("conf"))) { - logger.info("Loading configuration files from '{}'", opencgaHome.toString()); -// generalConfiguration = GeneralConfiguration.load(GeneralConfiguration.class.getClassLoader().getResourceAsStream("configuration.yml")); - configuration = Configuration - .load(new FileInputStream(new File(opencgaHome.resolve("conf").toFile().getAbsolutePath() + "/configuration.yml"))); - storageConfiguration = StorageConfiguration - .load(new FileInputStream(new File(opencgaHome.resolve("conf").toFile().getAbsolutePath() + "/storage-configuration.yml"))); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Deprecated - private void initDefaultConfigurationFiles() { - try { - if (System.getenv("OPENCGA_HOME") != null) { - initConfigurationFiles(Paths.get(System.getenv("OPENCGA_HOME") + "/conf")); - } else { - logger.info("Loading configuration files from inside JAR file"); + logger.info("Loading configuration files from '{}'", opencgaHome); // generalConfiguration = GeneralConfiguration.load(GeneralConfiguration.class.getClassLoader().getResourceAsStream("configuration.yml")); configuration = Configuration - .load(Configuration.class.getClassLoader().getResourceAsStream("configuration.yml")); + .load(Files.newInputStream(opencgaHome.resolve("conf").resolve("configuration.yml").toFile().toPath())); storageConfiguration = StorageConfiguration - .load(StorageConfiguration.class.getClassLoader().getResourceAsStream("storage-configuration.yml")); + .load(Files.newInputStream(opencgaHome.resolve("conf").resolve("storage-configuration.yml").toFile().toPath())); } } catch (IOException e) { - e.printStackTrace(); + throw new UncheckedIOException(e); } } @@ -133,7 +86,6 @@ private void initDefaultConfigurationFiles() { public String toString() { final StringBuilder sb = new StringBuilder("StorageServer{"); sb.append("port=").append(port); - sb.append(", defaultStorageEngine='").append(defaultStorageEngine).append('\''); sb.append('}'); return sb.toString(); } @@ -146,12 +98,4 @@ public void setPort(int port) { this.port = port; } - public String getDefaultStorageEngine() { - return defaultStorageEngine; - } - - public void setDefaultStorageEngine(String defaultStorageEngine) { - this.defaultStorageEngine = defaultStorageEngine; - } - } diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/RestServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/RestServer.java index 28bdffc8016..c3f87c68239 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/RestServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/RestServer.java @@ -16,8 +16,12 @@ package org.opencb.opencga.server; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.webapp.WebAppContext; +import org.opencb.opencga.core.config.RestServerConfiguration; import java.io.IOException; import java.nio.file.Files; @@ -44,9 +48,34 @@ public RestServer(Path opencgaHome, int port) { @Override public void start() throws Exception { - server = new Server(port); + initServer(); - WebAppContext webapp = new WebAppContext(); + Path war = getOpencgaWar(); + + initWebApp(war); + + server.start(); + logger.info("REST server started, listening on {}", server.getURI()); + + initHooks(); + +// // AdminWSServer server needs a reference to this class to cll to .stop() +// AdminRestWebService.setServer(this); + } + + protected Server initServer() { + server = new Server(); + + HttpConfiguration httpConfig = getHttpConfiguration(); + + ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); + httpConnector.setPort(port); + + server.addConnector(httpConnector); + return server; + } + + protected Path getOpencgaWar() throws Exception { Optional warPath; try (Stream stream = Files.list(opencgaHome)) { warPath = stream @@ -56,35 +85,36 @@ public void start() throws Exception { throw new Exception("Error accessing OpenCGA Home: " + opencgaHome.toString(), e); } // Check is a war file has been found in opencgaHome - if (warPath == null || !warPath.isPresent()) { + if (!warPath.isPresent()) { throw new Exception("No war file found at " + opencgaHome.toString()); } + return warPath.get(); + } - String opencgaVersion = warPath.get().toFile().getName().replace(".war", ""); + protected WebAppContext initWebApp(Path war) throws Exception { + String opencgaVersion = war.toFile().getName().replace(".war", ""); + WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/" + opencgaVersion); - webapp.setWar(warPath.get().toString()); + webapp.setWar(war.toString()); webapp.setClassLoader(this.getClass().getClassLoader()); webapp.setInitParameter("OPENCGA_HOME", opencgaHome.toFile().toString()); webapp.getServletContext().setAttribute("OPENCGA_HOME", opencgaHome.toFile().toString()); // webapp.setInitParameter("log4jConfiguration", opencgaHome.resolve("conf/log4j2.server.xml").toString()); server.setHandler(webapp); + return webapp; + } - server.start(); - logger.info("REST server started, listening on {}", port); - + protected void initHooks() { // A hook is added in case the JVM is shutting down - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - try { - if (server.isRunning()) { - stopJettyServer(); - } - } catch (Exception e) { - e.printStackTrace(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + if (server.isRunning()) { + stopJettyServer(); } + } catch (Exception e) { + logger.error("Error stopping Jetty server", e); } - }); + })); // A separated thread is launched to shut down the server new Thread(() -> { @@ -97,12 +127,30 @@ public void run() { Thread.sleep(500); } } catch (Exception e) { - e.printStackTrace(); + logger.error("Error stopping Jetty server", e); } }).start(); + } -// // AdminWSServer server needs a reference to this class to cll to .stop() -// AdminRestWebService.setServer(this); + protected HttpConfiguration getHttpConfiguration() { + HttpConfiguration httpConfig = new HttpConfiguration(); + RestServerConfiguration.HttpConfiguration restHttpConf = configuration.getServer().getRest().getHttpConfiguration(); + if (restHttpConf.getOutputBufferSize() > 0) { + httpConfig.setOutputBufferSize(restHttpConf.getOutputBufferSize()); + } + if (restHttpConf.getOutputAggregationSize() > 0) { + httpConfig.setOutputAggregationSize(restHttpConf.getOutputAggregationSize()); + } + if (restHttpConf.getRequestHeaderSize() > 0) { + httpConfig.setRequestHeaderSize(restHttpConf.getRequestHeaderSize()); + } + if (restHttpConf.getResponseHeaderSize() > 0) { + httpConfig.setResponseHeaderSize(restHttpConf.getResponseHeaderSize()); + } + if (restHttpConf.getHeaderCacheSize() > 0) { + httpConfig.setHeaderCacheSize(restHttpConf.getHeaderCacheSize()); + } + return httpConfig; } @Override diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/grpc/GrpcServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/grpc/GrpcServer.java index 76b4c350a32..32ded068042 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/grpc/GrpcServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/grpc/GrpcServer.java @@ -19,7 +19,6 @@ import io.grpc.Server; import io.grpc.ServerBuilder; import org.opencb.opencga.server.AbstractStorageServer; -import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.slf4j.LoggerFactory; import java.nio.file.Path; @@ -31,29 +30,11 @@ public class GrpcServer extends AbstractStorageServer { private Server server; - public GrpcServer() { -// this(storageConfiguration.getServer().getGrpc(), storageConfiguration.getDefaultStorageEngineId()); - } - - public GrpcServer(int port, String defaultStorageEngine) { - super(port, defaultStorageEngine); - - logger = LoggerFactory.getLogger(this.getClass()); - } - public GrpcServer(Path configDir) { super(configDir); init(); } - @Deprecated - public GrpcServer(StorageConfiguration storageConfiguration) { -// super(storageConfiguration.getServer().getGrpc(), storageConfiguration.getDefaultStorageEngineId()); - this.storageConfiguration = storageConfiguration; - - logger = LoggerFactory.getLogger(this.getClass()); - } - private void init() { logger = LoggerFactory.getLogger(this.getClass()); if (configuration != null) { diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java index 9deeb489110..cc36683e454 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java @@ -27,6 +27,7 @@ import org.opencb.opencga.core.models.notes.NoteCreateParams; import org.opencb.opencga.core.models.notes.NoteUpdateParams; import org.opencb.opencga.core.models.organizations.Organization; +import org.opencb.opencga.core.models.organizations.OrganizationConfiguration; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.response.OpenCGAResult; @@ -96,6 +97,34 @@ public Response update( } } + @POST + @Path("/{organization}/configuration/update") + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Update the Organization configuration attributes", response = OrganizationConfiguration.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = QueryOptions.INCLUDE, value = ParamConstants.INCLUDE_DESCRIPTION, dataType = "string", paramType = "query"), + @ApiImplicitParam(name = QueryOptions.EXCLUDE, value = ParamConstants.EXCLUDE_DESCRIPTION, dataType = "string", paramType = "query") + }) + public Response updateConfiguration( + @ApiParam(value = ParamConstants.ORGANIZATION_DESCRIPTION, required = true) @PathParam(ParamConstants.ORGANIZATION) String organizationId, + @ApiParam(value = ParamConstants.INCLUDE_RESULT_DESCRIPTION, defaultValue = "false") @QueryParam(ParamConstants.INCLUDE_RESULT_PARAM) boolean includeResult, + @ApiParam(value = "Action to be performed if the array of authenticationOrigins is being updated.", + allowableValues = "ADD,REMOVE,SET,REPLACE", defaultValue = "ADD") @QueryParam("authenticationOriginsAction") ParamUtils.UpdateAction authOriginsAction, + @ApiParam(value = "JSON containing the params to be updated.", required = true) OrganizationConfiguration parameters) { + try { + if (authOriginsAction == null) { + authOriginsAction = ParamUtils.UpdateAction.ADD; + } + Map actionMap = new HashMap<>(); + actionMap.put(OrganizationDBAdaptor.AUTH_ORIGINS_FIELD, authOriginsAction); + queryOptions.put(Constants.ACTIONS, actionMap); + OpenCGAResult result = catalogManager.getOrganizationManager().updateConfiguration(organizationId, parameters, queryOptions, token); + return createOkResponse(result); + } catch (Exception e) { + return createErrorResponse(e); + } + } + @POST @Path("/create") @Consumes(MediaType.APPLICATION_JSON) diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/VariantWebService.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/VariantWebService.java index a62b08e0413..750e96aa4c1 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/VariantWebService.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/VariantWebService.java @@ -16,7 +16,6 @@ package org.opencb.opencga.server.rest.analysis; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.StopWatch; @@ -28,12 +27,8 @@ import org.opencb.biodata.models.variant.metadata.VariantMetadata; import org.opencb.biodata.models.variant.metadata.VariantSetStats; import org.opencb.commons.datastore.core.*; -import org.opencb.opencga.analysis.AnalysisUtils; -import org.opencb.opencga.analysis.ResourceUtils; -import org.opencb.opencga.analysis.alignment.AlignmentConstants; import org.opencb.opencga.analysis.family.qc.FamilyQcAnalysis; import org.opencb.opencga.analysis.individual.qc.IndividualQcAnalysis; -import org.opencb.opencga.analysis.individual.qc.IndividualQcUtils; import org.opencb.opencga.analysis.sample.qc.SampleQcAnalysis; import org.opencb.opencga.analysis.variant.VariantExportTool; import org.opencb.opencga.analysis.variant.circos.CircosAnalysis; @@ -56,13 +51,10 @@ import org.opencb.opencga.analysis.variant.stats.CohortVariantStatsAnalysis; import org.opencb.opencga.analysis.variant.stats.SampleVariantStatsAnalysis; import org.opencb.opencga.analysis.variant.stats.VariantStatsAnalysis; -import org.opencb.opencga.analysis.wrappers.deeptools.DeeptoolsWrapperAnalysis; import org.opencb.opencga.analysis.wrappers.exomiser.ExomiserWrapperAnalysis; import org.opencb.opencga.analysis.wrappers.gatk.GatkWrapperAnalysis; import org.opencb.opencga.analysis.wrappers.plink.PlinkWrapperAnalysis; import org.opencb.opencga.analysis.wrappers.rvtests.RvtestsWrapperAnalysis; -import org.opencb.opencga.analysis.wrappers.samtools.SamtoolsWrapperAnalysis; -import org.opencb.opencga.catalog.db.api.FileDBAdaptor; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.utils.AvroToAnnotationConverter; import org.opencb.opencga.catalog.utils.ParamUtils; @@ -72,15 +64,14 @@ import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.exceptions.VersionException; -import org.opencb.opencga.core.models.alignment.DeeptoolsWrapperParams; -import org.opencb.opencga.core.models.alignment.SamtoolsWrapperParams; import org.opencb.opencga.core.models.analysis.knockout.KnockoutByGene; import org.opencb.opencga.core.models.analysis.knockout.KnockoutByIndividual; import org.opencb.opencga.core.models.clinical.ExomiserWrapperParams; import org.opencb.opencga.core.models.cohort.Cohort; import org.opencb.opencga.core.models.common.AnnotationSet; -import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.job.Job; +import org.opencb.opencga.core.models.operations.variant.VariantFileDeleteParams; +import org.opencb.opencga.core.models.operations.variant.VariantIndexParams; import org.opencb.opencga.core.models.operations.variant.VariantStatsExportParams; import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.models.variant.*; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/AlignmentStorageOptions.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/AlignmentStorageOptions.java index 672a7f03bca..a137d4c4e0f 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/AlignmentStorageOptions.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/AlignmentStorageOptions.java @@ -1,11 +1,11 @@ package org.opencb.opencga.storage.core.alignment; -import org.opencb.biodata.tools.alignment.BamManager; +import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.config.ConfigurationOption; public enum AlignmentStorageOptions implements ConfigurationOption { - BIG_WIG_WINDOWS_SIZE("bigWigWindowsSize", BamManager.DEFAULT_WINDOW_SIZE); + BIG_WIG_WINDOWS_SIZE("bigWigWindowsSize", ParamConstants.COVERAGE_WINDOW_SIZE_DEFAULT); private final String key; private final Object value; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/local/LocalAlignmentStoragePipeline.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/local/LocalAlignmentStoragePipeline.java index 009718fcbfb..d45ccbc130c 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/local/LocalAlignmentStoragePipeline.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/alignment/local/LocalAlignmentStoragePipeline.java @@ -87,7 +87,7 @@ public URI transform(URI input, URI pedigree, URI output) throws Exception { // 3) Create the BigWig file containing the coverage using the bamCoverage from the DeepTools package Path bwPath = workspace.resolve(path.getFileName() + BamManager.COVERAGE_BIGWIG_EXTENSION); int windowSize = configuration.getInt(AlignmentStorageOptions.BIG_WIG_WINDOWS_SIZE.key(), - AlignmentStorageOptions.BIG_WIG_WINDOWS_SIZE.defaultValue()); + Integer.parseInt(AlignmentStorageOptions.BIG_WIG_WINDOWS_SIZE.defaultValue())); bamManager.calculateBigWigCoverage(bwPath, windowSize); return input; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/exceptions/StorageEngineException.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/exceptions/StorageEngineException.java index 547b83c55af..0ac128118a9 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/exceptions/StorageEngineException.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/exceptions/StorageEngineException.java @@ -41,6 +41,11 @@ public static StorageEngineException alreadyLoaded(int fileId, String fileName) return unableToExecute("Already loaded", fileId, fileName); } + public static StorageEngineException invalidFileStatus(int fileId, String fileName) { + return unableToExecute("File is in INVALID status. Unable to load. File needs to be deleted from the variant-storage", + fileId, fileName); + } + public static StorageEngineException otherOperationInProgressException(TaskMetadata operation, String jobOperationName, List fileIds, VariantStorageMetadataManager mm) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java index 103e8568bce..680d30a20db 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java @@ -679,6 +679,7 @@ public FileMetadata updateFileMetadata(int studyId, int fi update.update(fileMetadata); lock.checkLocked(); unsecureUpdateFileMetadata(studyId, fileMetadata); + fileIdIndexedCache.put(studyId, fileId, fileMetadata.isIndexed()); return fileMetadata; } finally { lock.unlock(); @@ -807,7 +808,6 @@ public void addIndexedFiles(int studyId, List fileIds) throws StorageEn .getName(); logger.info("Register file " + name + " as INDEXED"); } - fileDBAdaptor.addIndexedFiles(studyId, fileIds); fileIdsFromSampleIdCache.clear(); fileIdIndexedCache.clear(); } @@ -821,6 +821,7 @@ public void removeIndexedFiles(int studyId, Collection fileIds) throws fileMetadata.setIndexStatus(TaskMetadata.Status.NONE); fileMetadata.setSecondaryAnnotationIndexStatus(TaskMetadata.Status.NONE); fileMetadata.setAnnotationStatus(TaskMetadata.Status.NONE); + fileMetadata.getAttributes().remove(LOAD_ARCHIVE.key()); if (fileMetadata.getType() == FileMetadata.Type.VIRTUAL) { partialFiles.addAll(fileMetadata.getAttributes().getAsIntegerList(FileMetadata.VIRTUAL_FILES)); } @@ -833,6 +834,7 @@ public void removeIndexedFiles(int studyId, Collection fileIds) throws fileMetadata.setIndexStatus(TaskMetadata.Status.NONE); fileMetadata.setSecondaryAnnotationIndexStatus(TaskMetadata.Status.NONE); fileMetadata.setAnnotationStatus(TaskMetadata.Status.NONE); + fileMetadata.getAttributes().remove(LOAD_ARCHIVE.key()); }); // deleteVariantFileMetadata(studyId, fileId); } @@ -851,7 +853,6 @@ public void removeIndexedFiles(int studyId, Collection fileIds) throws } }); } - fileDBAdaptor.removeIndexedFiles(studyId, fileIds); } public Iterable fileMetadataIterable(int studyId) { @@ -1135,6 +1136,16 @@ public Iterable getInvalidCohorts(int studyId) { return () -> Iterators.filter(cohortIterator(studyId), CohortMetadata::isInvalid); } + public Iterable getCalculatedOrPartialCohorts(int studyId) { + return () -> Iterators.filter(cohortIterator(studyId), + cohortMetadata -> { + TaskMetadata.Status status = cohortMetadata.getStatsStatus(); + return status == TaskMetadata.Status.READY + || status == TaskMetadata.Status.RUNNING + || status == TaskMetadata.Status.ERROR; + }); + } + public CohortMetadata setSamplesToCohort(int studyId, String cohortName, Collection samples) throws StorageEngineException { return updateCohortSamples(studyId, cohortName, samples, false); } @@ -1244,6 +1255,7 @@ private CohortMetadata updateCohortSamples(int studyId, String cohortName, Colle if (!oldSamples.equals(sampleIdsList) || !oldFiles.equals(fileIds)) { // Cohort has been modified! Invalidate stats cohort.setStatsStatus(TaskMetadata.Status.ERROR); + cohort.getAttributes().put(CohortMetadata.INVALID_STATS_NUM_SAMPLES, oldSamples.size()); } } } @@ -1272,14 +1284,22 @@ public TaskMetadata getTask(int studyId, String taskName, List fileIds) return task; } + public Iterable taskIterable(int studyId) { + return () -> taskIterator(studyId, null, false); + } + public Iterator taskIterator(int studyId) { - return taskIterator(studyId, null); + return taskIterator(studyId, null, false); } public Iterator taskIterator(int studyId, List statusFilter) { return taskIterator(studyId, statusFilter, false); } + public Iterator taskIterator(int studyId, TaskMetadata.Status... statusFilter) { + return taskIterator(studyId, Arrays.asList(statusFilter), false); + } + public Iterator taskIterator(int studyId, List statusFilter, boolean reversed) { return taskDBAdaptor.taskIterator(studyId, statusFilter, reversed); } @@ -1288,6 +1308,13 @@ public Iterable getRunningTasks(int studyId) { return taskDBAdaptor.getRunningTasks(studyId); } + public Iterable getActiveTasks(int studyId) { + return () -> taskIterator(studyId, + TaskMetadata.Status.RUNNING, + TaskMetadata.Status.DONE, + TaskMetadata.Status.ERROR); + } + public void unsecureUpdateTask(int studyId, TaskMetadata task) throws StorageEngineException { task.setStudyId(studyId); if (task.getId() == 0) { @@ -1735,6 +1762,9 @@ private int registerFile(int studyId, String filePath, FileMetadata.Type type) t if (fileId != null) { updateFileMetadata(studyId, fileId, fileMetadata -> { + if (fileMetadata.getIndexStatus() == TaskMetadata.Status.INVALID) { + throw StorageEngineException.invalidFileStatus(fileMetadata.getId(), fileName); + } if (fileMetadata.isIndexed()) { throw StorageEngineException.alreadyLoaded(fileMetadata.getId(), fileName); } @@ -2072,47 +2102,35 @@ private TaskMetadata getRunningTaskCompatibleOrFail(int studyId, String jobOpera TaskMetadata.Type type, Predicate allowConcurrent) throws StorageEngineException { TaskMetadata resumeTask = null; - Iterator iterator = taskIterator(studyId, Arrays.asList( - TaskMetadata.Status.DONE, - TaskMetadata.Status.RUNNING, - TaskMetadata.Status.ERROR)); - while (iterator.hasNext()) { - TaskMetadata task = iterator.next(); + for (TaskMetadata task : getActiveTasks(studyId)) { TaskMetadata.Status currentStatus = task.currentStatus(); - switch (currentStatus) { - case READY: - logger.warn("Unexpected READY task. IGNORE"); - // Ignore ready operations - break; - case DONE: - case RUNNING: - if (!resume) { - if (task.sameOperation(fileIds, type, jobOperationName)) { - throw StorageEngineException.currentOperationInProgressException(task, this); - } else { - if (allowConcurrent.test(task)) { - break; - } else { - throw StorageEngineException.otherOperationInProgressException(task, jobOperationName, fileIds, this); - } - } - } - // DO NOT BREAK!. Resuming last loading, go to error case. - case ERROR: - if (!task.sameOperation(fileIds, type, jobOperationName)) { - if (allowConcurrent.test(task)) { - break; - } else { - throw StorageEngineException.otherOperationInProgressException(task, jobOperationName, fileIds, this, resume); - } - } else { - logger.info("Resuming last batch operation \"" + task.getName() + "\" due to error."); - resumeTask = task; - } - break; - default: - throw new IllegalArgumentException("Unknown Status " + currentStatus); + if (currentStatus != TaskMetadata.Status.DONE + && currentStatus != TaskMetadata.Status.RUNNING + && currentStatus != TaskMetadata.Status.ERROR) { + logger.warn("Unexpected {} task. IGNORE", currentStatus); + // Ignore ready operations + continue; + } + + if (task.sameOperation(fileIds, type, jobOperationName)) { + if (currentStatus == TaskMetadata.Status.ERROR) { + // Automatically resume ERROR status tasks + logger.info("Resuming last batch operation \"" + task.getName() + "\" due to error."); + resumeTask = task; + } else if (resume) { + // Force resume + logger.info("Manually resuming last batch operation \"" + task.getName() + "\" in status " + currentStatus + "."); + resumeTask = task; + } else { + // Already being executed + throw StorageEngineException.currentOperationInProgressException(task, this); + } + } else { + // Check if it can be executed concurrently + if (!allowConcurrent.test(task)) { + throw StorageEngineException.otherOperationInProgressException(task, jobOperationName, fileIds, this); + } } } return resumeTask; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/FileMetadataDBAdaptor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/FileMetadataDBAdaptor.java index ca6a5964b59..8cb916df1c6 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/FileMetadataDBAdaptor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/FileMetadataDBAdaptor.java @@ -75,10 +75,6 @@ public String description() { LinkedHashSet getIndexedFiles(int studyId, boolean includePartial); - default void addIndexedFiles(int studyId, List fileIds) {} - - default void removeIndexedFiles(int studyId, Collection fileIds) {}; - default DataResult count() { return count(new Query()); } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/CohortMetadata.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/CohortMetadata.java index 4814fc82b63..a4e9de4116e 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/CohortMetadata.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/CohortMetadata.java @@ -8,6 +8,7 @@ * @author Jacobo Coll <jacobo167@gmail.com> */ public class CohortMetadata extends StudyResourceMetadata { + public static final String INVALID_STATS_NUM_SAMPLES = "invalidStatsNumSamples"; // private int studyId; // private int id; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/TaskMetadata.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/TaskMetadata.java index e7d32e12b5d..c0d0cf9376c 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/TaskMetadata.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/models/TaskMetadata.java @@ -34,10 +34,39 @@ public class TaskMetadata { public enum Status { NONE, + /** + * Active task. + * Running, but not finished + */ RUNNING, - DONE, // Finished, but some work still needed (optional) + /** + * Active task. + * Finished, but some work still needed (optional status) + */ + DONE, + /** + * Active task. + * Currently, paused. + * Errors found during the execution. Needs to be resumed or cleaned. + */ + ERROR, + /** + * Finished. + * Ready to be used + */ READY, - ERROR + /** + * Finished. + * Task was aborted, cancelled or rolled back. + * Any needed clean might be executed by other running tasks + */ + ABORTED, + /** + * Finished. + * Task finished with invalid results. + * Similar to "ERROR" status, but this can't be resumed. Needs to be cleaned first. + */ + INVALID, } public enum Type { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/utils/CellBaseUtils.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/utils/CellBaseUtils.java index ed0848a0e0b..f8dfc2d1f9f 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/utils/CellBaseUtils.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/utils/CellBaseUtils.java @@ -343,6 +343,7 @@ public List getVariants(List variantsStr) { List variants = new ArrayList<>(variantsStr.size()); List> response = null; try { + // FIXME: This method should call genomic/variant/snp/search response = checkNulls(cellBaseClient.getVariantClient().get(variantsStr, new QueryOptions(QueryOptions.INCLUDE, VariantField.CHROMOSOME.fieldName() + "," diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java index a3ab7e539b5..dd24ee1334d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java @@ -31,7 +31,7 @@ import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.core.models.operations.variant.VariantAggregateFamilyParams; import org.opencb.opencga.core.models.operations.variant.VariantAggregateParams; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.StorageEngine; import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; @@ -946,35 +946,78 @@ public void removeSamples(String study, List samples, URI outdir) throws */ protected TaskMetadata preRemove(String study, List files, List samples) throws StorageEngineException { AtomicReference batchFileOperation = new AtomicReference<>(); + boolean force = getOptions().getBoolean(FORCE.key(), FORCE.defaultValue()); + boolean resume = getOptions().getBoolean(RESUME.key(), RESUME.defaultValue()); VariantStorageMetadataManager metadataManager = getMetadataManager(); - metadataManager.updateStudyMetadata(study, studyMetadata -> { - List fileIds = new ArrayList<>(files.size()); - for (String file : files) { - FileMetadata fileMetadata = metadataManager.getFileMetadata(studyMetadata.getId(), file); - if (fileMetadata == null) { - throw VariantQueryException.fileNotFound(file, study); - } - if (fileMetadata.getType() == FileMetadata.Type.PARTIAL) { - String virtualFileName = metadataManager.getFileName( - studyMetadata.getId(), - fileMetadata.getAttributes().getInt(FileMetadata.VIRTUAL_PARENT)); - throw new StorageEngineException("Unable to remove " + FileMetadata.Type.PARTIAL + " file. " - + "Try removing its virtual file : '" + virtualFileName + "'"); - } - fileIds.add(fileMetadata.getId()); - if (!fileMetadata.isIndexed()) { + + int studyId = metadataManager.getStudyId(study); + List fileIds = new ArrayList<>(files.size()); + for (String file : files) { + FileMetadata fileMetadata = metadataManager.getFileMetadata(studyId, file); + if (fileMetadata == null) { + throw VariantQueryException.fileNotFound(file, study); + } + if (fileMetadata.getType() == FileMetadata.Type.PARTIAL) { + String virtualFileName = metadataManager.getFileName( + studyId, + fileMetadata.getAttributes().getInt(FileMetadata.VIRTUAL_PARENT)); + throw new StorageEngineException("Unable to remove " + FileMetadata.Type.PARTIAL + " file. " + + "Try removing its virtual file : '" + virtualFileName + "'"); + } + if (fileMetadata.getIndexStatus() == TaskMetadata.Status.NONE) { + if (force) { + logger.info("Force remove. Removing non indexed file: " + fileMetadata.getName()); + } else { throw new StorageEngineException("Unable to remove non indexed file: " + fileMetadata.getName()); } } + if (fileMetadata.getIndexStatus() == TaskMetadata.Status.RUNNING) { + if (force) { + logger.info("Force remove. Removing file while being indexed: " + fileMetadata.getName()); + } else { + throw new StorageEngineException("Unable to remove file while being indexed: " + fileMetadata.getName()); + } + } + fileIds.add(fileMetadata.getId()); + } - boolean resume = getOptions().getBoolean(RESUME.key(), RESUME.defaultValue()); + metadataManager.updateStudyMetadata(study, studyMetadata -> { + List tasksToAbort = new ArrayList<>(); + HashSet fileIdsSet = new HashSet<>(fileIds); batchFileOperation.set(metadataManager.addRunningTask( studyMetadata.getId(), REMOVE_OPERATION_NAME, fileIds, resume, - TaskMetadata.Type.REMOVE)); + TaskMetadata.Type.REMOVE, runningTask -> { + if (runningTask.getType() == TaskMetadata.Type.LOAD) { + if (force && fileIdsSet.containsAll(runningTask.getFileIds())) { + // Abort running load task as all files are being removed + tasksToAbort.add(runningTask); + return true; + } else { + if (force) { + logger.error("Unable to force remove the file while other files are being loaded."); + } + // Unable to remove files. Other files are being loaded. + return false; + } + } else { + // Unable to remove files. Other tasks are running. + return false; + } + })); + // If the task was successfully created, abort other tasks. + for (TaskMetadata task : tasksToAbort) { + List fileNames = task.getFileIds().stream() + .map(fileId -> metadataManager.getFileName(studyMetadata.getId(), fileId)) + .collect(Collectors.toList()); + TaskMetadata.Status status = TaskMetadata.Status.ABORTED; + logger.info("Force remove. Setting status to " + status + " of running task " + + task.getName() + "('id=" + task.getId() + ") for files " + fileNames); + metadataManager.setStatus(studyMetadata.getId(), task.getId(), status); + } return studyMetadata; }); @@ -1192,8 +1235,36 @@ public VariantQueryResult getCompoundHeterozygous(String study, String return get(query, options); } - public DataResult getSampleData(String variant, String study, QueryOptions options) throws StorageEngineException { - return new VariantSampleDataManager(getDBAdaptor()).getSampleData(variant, study, options); + public DataResult getSampleData(String variantStr, String study, QueryOptions options) throws StorageEngineException { + final Variant variant = getVariant(variantStr); + return getVariantSampleDataManager().getSampleData(variant, study, options); + } + + public Variant getVariant(String variantStr) { + final Variant variant; + if (VariantQueryUtils.isVariantId(variantStr)) { + variant = VariantQueryUtils.toVariant(variantStr, true); + } else if (VariantQueryUtils.isVariantAccession(variantStr)) { + VariantQueryResult result = get(new Query(VariantQueryParam.ANNOT_XREF.key(), variantStr), + new QueryOptions(QueryOptions.INCLUDE, VariantField.ID).append(QueryOptions.LIMIT, 1).append(QueryOptions.COUNT, true)); + if (result.getNumMatches() > 1) { + throw new VariantQueryException("Not unique variant identifier '" + variantStr + "'." + + " Found " + result.getNumMatches() + " results"); + } else if (result.getNumResults() == 1) { + variant = result.first(); + } else { + throw VariantQueryException.variantNotFound(variantStr); + } + } else { + throw new VariantQueryException("Variant not valid. Variant = '" + variantStr + "'. Supported values:" + + " {chr}:{start}:{end}:{ref}:{alt}, rs{id}"); + } + variant.setId(variant.toString()); + return variant; + } + + protected VariantSampleDataManager getVariantSampleDataManager() throws StorageEngineException { + return new VariantSampleDataManager(getDBAdaptor()); } public VariantQueryResult get(Query query, QueryOptions options) { @@ -1205,8 +1276,8 @@ public VariantQueryResult get(Query query, QueryOptions options) { } addDefaultLimit(options, getOptions()); addDefaultSampleLimit(query, getOptions()); - query = preProcessQuery(query, options); - return getVariantQueryExecutor(query, options).get(query, options); + ParsedVariantQuery variantQuery = parseQuery(query, options); + return getVariantQueryExecutor(variantQuery).get(variantQuery); } @Override @@ -1223,8 +1294,8 @@ public MultiVariantDBIterator iterator(Iterator variants, Query query, QueryO public VariantDBIterator iterator(Query query, QueryOptions options) { query = VariantQueryUtils.copy(query); options = VariantQueryUtils.copy(options); - query = preProcessQuery(query, options); - return getVariantQueryExecutor(query, options).iterator(query, options); + ParsedVariantQuery variantQuery = parseQuery(query, options); + return getVariantQueryExecutor(variantQuery).iterator(variantQuery); } public final List getVariantQueryExecutors() throws StorageEngineException { @@ -1246,7 +1317,7 @@ protected List initVariantQueryExecutors() throws StorageE executors.add(new CompoundHeterozygousQueryExecutor( getMetadataManager(), getStorageEngineId(), getOptions(), this)); executors.add(new BreakendVariantQueryExecutor( - getMetadataManager(), getStorageEngineId(), getOptions(), new DBAdaptorVariantQueryExecutor( + getStorageEngineId(), getOptions(), new DBAdaptorVariantQueryExecutor( getDBAdaptor(), getStorageEngineId(), getOptions()), getDBAdaptor())); executors.add(new SamplesSearchIndexVariantQueryExecutor( getDBAdaptor(), getVariantSearchManager(), getStorageEngineId(), dbName, configuration, getOptions())); @@ -1265,12 +1336,22 @@ protected List initVariantQueryExecutors() throws StorageE * @return VariantQueryExecutor to use */ public VariantQueryExecutor getVariantQueryExecutor(Query query, QueryOptions options) { + return getVariantQueryExecutor(parseQuery(query, options)); + } + + /** + * Determine which {@link VariantQueryExecutor} should be used to execute the given query. + * + * @param variantQuery Parsed variant query + * @return VariantQueryExecutor to use + */ + public VariantQueryExecutor getVariantQueryExecutor(ParsedVariantQuery variantQuery) { try { for (VariantQueryExecutor executor : getVariantQueryExecutors()) { - if (executor.canUseThisExecutor(query, options)) { + if (executor.canUseThisExecutor(variantQuery.getQuery(), variantQuery.getInputOptions())) { logger.info("Using VariantQueryExecutor : " + executor.getClass().getName()); - logger.info(" Query : " + VariantQueryUtils.printQuery(query)); - logger.info(" Options : " + options.toJson()); + logger.info(" Query : " + VariantQueryUtils.printQuery(variantQuery.getInputQuery())); + logger.info(" Options : " + variantQuery.getInputOptions().toJson()); return executor; } } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java index 19b0631659f..5b37d2512b1 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java @@ -61,6 +61,7 @@ import org.opencb.opencga.storage.core.io.plain.StringDataWriter; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.CohortMetadata; +import org.opencb.opencga.storage.core.metadata.models.FileMetadata; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; @@ -176,9 +177,14 @@ public URI preTransform(URI input) throws StorageEngineException, IOException, F Integer fileId = getMetadataManager().getFileId(studyMetadata.getId(), fileName, true); if (fileId != null) { // File is indexed. Mark as non indexed. - getMetadataManager().updateFileMetadata(studyMetadata.getId(), fileId, fileMetadata -> { - fileMetadata.setIndexStatus(TaskMetadata.Status.NONE); - }); + FileMetadata fileMetadata = getMetadataManager().getFileMetadata(studyMetadata.getId(), fileId); + if (fileMetadata.getIndexStatus() == TaskMetadata.Status.INVALID) { + throw StorageEngineException.invalidFileStatus(fileId, fileName); + } else if (fileMetadata.getIndexStatus() != TaskMetadata.Status.NONE) { + getMetadataManager().updateFileMetadata(studyMetadata.getId(), fileId, fm -> { + fm.setIndexStatus(TaskMetadata.Status.NONE); + }); + } logger.info("File '{}' already loaded. Force reload!", fileName); } } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptor.java index c2ee42c9b00..e54efca912a 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptor.java @@ -23,12 +23,12 @@ import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjectionParser; import org.opencb.opencga.storage.core.variant.stats.VariantStatsWrapper; @@ -37,8 +37,6 @@ import java.util.List; import java.util.Map; -import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.addSamplesMetadataIfRequested; - /** * @author Ignacio Medina * @author Jacobo Coll @@ -46,21 +44,6 @@ */ public interface VariantDBAdaptor extends VariantIterable, AutoCloseable { - /** - * This method inserts Variants into the given Study. If the Study already exists then it just adds the new Sample - * genotypes, also new variants are inserted. If it is a new Study then Sample genotypes are added to the new Study. - * - * @param variants List of variants in OpenCB data model to be inserted - * @param studyName Name or alias of the study - * @param options Query modifiers, accepted values are: include, exclude, limit, skip, sort and count - * @return A DataResult with the number of inserted variants - */ - @Deprecated - default DataResult insert(List variants, String studyName, QueryOptions options) { - throw new UnsupportedOperationException(); - } - - /** /** * Fetch all variants resulting of executing the query in the database. Returned fields are taken from * the 'include' and 'exclude' fields at options. @@ -71,26 +54,29 @@ default DataResult insert(List variants, String studyName, QueryOptions * @return A DataResult with the result of the query */ default VariantQueryResult get(Iterator variants, Query query, QueryOptions options) { - DataResult queryResult = iterator(variants, query, options).toDataResult(); - return addSamplesMetadataIfRequested(queryResult, query, options, getMetadataManager()); + ParsedVariantQuery variantQuery = new VariantQueryParser(null, getMetadataManager()).parseQuery(query, options, true); + try (VariantDBIterator iterator = iterator(variants, query, options)) { + return iterator.toDataResult(variantQuery); + } catch (Exception e) { + throw VariantQueryException.internalException(e); + } } @Deprecated default VariantDBIterator iterator(Query query, QueryOptions options) { - return iterator(new VariantQueryParser(null, getMetadataManager()).parseQuery(query, options, true), options); + return iterator(new VariantQueryParser(null, getMetadataManager()).parseQuery(query, options, true)); } - VariantDBIterator iterator(ParsedVariantQuery query, QueryOptions options); + VariantDBIterator iterator(ParsedVariantQuery query); /** * Fetch all variants resulting of executing the query in the database. Returned fields are taken from * the 'include' and 'exclude' fields at options. * * @param query Query to be executed in the database to filter variants - * @param options Query modifiers, accepted values are: include, exclude, limit, skip, sort and count * @return A DataResult with the result of the query */ - VariantQueryResult get(ParsedVariantQuery query, QueryOptions options); + VariantQueryResult get(ParsedVariantQuery query); /** * Fetch all variants resulting of executing the query in the database. Returned fields are taken from @@ -102,7 +88,7 @@ default VariantDBIterator iterator(Query query, QueryOptions options) { */ @Deprecated default VariantQueryResult get(Query query, QueryOptions options) { - return get(new VariantQueryParser(null, getMetadataManager()).parseQuery(query, options, true), options); + return get(new VariantQueryParser(null, getMetadataManager()).parseQuery(query, options, true)); } /** diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQuery.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQuery.java index 65c2bda05e6..09b1867854e 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQuery.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQuery.java @@ -50,6 +50,10 @@ public VariantQuery region(String value) { put(VariantQueryParam.REGION.key(), value); return this; } + public VariantQuery region(String... value) { + put(VariantQueryParam.REGION.key(), Arrays.asList(value)); + return this; + } public VariantQuery region(Region... value) { put(VariantQueryParam.REGION.key(), Arrays.asList(value)); return this; @@ -105,6 +109,15 @@ public VariantQuery sample(String value) { return sample(VariantQueryUtils.QueryOperation.OR, value); } + public VariantQuery sample(List value) { + return sample(VariantQueryUtils.QueryOperation.OR, value); + } + + public VariantQuery sample(VariantQueryUtils.QueryOperation operation, List value) { + put(VariantQueryParam.SAMPLE.key(), value.stream().collect(Collectors.joining(operation.separator()))); + return this; + } + public VariantQuery sample(String... value) { return sample(VariantQueryUtils.QueryOperation.OR, value); } @@ -178,8 +191,9 @@ public VariantQuery includeSampleId(boolean value) { put(VariantQueryParam.INCLUDE_SAMPLE_ID.key(), value); return this; } - public String includeSampleId() { - return getString(VariantQueryParam.INCLUDE_SAMPLE_ID.key()); + + public boolean includeSampleId() { + return getBoolean(VariantQueryParam.INCLUDE_SAMPLE_ID.key()); } public VariantQuery sampleMetadata(boolean value) { @@ -212,20 +226,24 @@ public boolean includeGenotype() { return getBoolean(VariantQueryParam.INCLUDE_GENOTYPE.key()); } - public VariantQuery sampleLimit(String value) { + public VariantQuery sampleLimit(int value) { put(VariantQueryParam.SAMPLE_LIMIT.key(), value); return this; } - public String sampleLimit() { - return getString(VariantQueryParam.SAMPLE_LIMIT.key()); + public int sampleLimit() { + return getInt(VariantQueryParam.SAMPLE_LIMIT.key()); } public VariantQuery sampleSkip(String value) { put(VariantQueryParam.SAMPLE_SKIP.key(), value); return this; } - public String sampleSkip() { - return getString(VariantQueryParam.SAMPLE_SKIP.key()); + public VariantQuery sampleSkip(int value) { + put(VariantQueryParam.SAMPLE_SKIP.key(), value); + return this; + } + public int sampleSkip() { + return getInt(VariantQueryParam.SAMPLE_SKIP.key()); } public VariantQuery file(String value) { @@ -623,12 +641,12 @@ public VariantQuery release(String value) { put(VariantQueryParam.RELEASE.key(), value); return this; } - public VariantQuery release(int value) { + public VariantQuery release(Integer value) { put(VariantQueryParam.RELEASE.key(), value); return this; } - public String release() { - return getString(VariantQueryParam.RELEASE.key()); + public Integer release() { + return getInt(VariantQueryParam.RELEASE.key()); } @Override diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryException.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryException.java index d90698f9e44..20ef94c7f2c 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryException.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryException.java @@ -217,6 +217,10 @@ public static VariantQueryException fileNotFound(Object file, Object study) { return new VariantQueryException("File " + quote(file) + " not found in study '" + study + "'"); } + public static VariantQueryException fileNotIndexed(Object file, Object study) { + return new VariantQueryException("File " + quote(file) + " not indexed in study '" + study + "'"); + } + public static VariantQueryException scoreNotFound(Object score, Object study) { return new VariantQueryException("Variant Score " + quote(score) + " not found in study '" + study + "'"); } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryParam.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryParam.java index adc0b72ea44..1973d7f383d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryParam.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryParam.java @@ -41,7 +41,7 @@ public final class VariantQueryParam implements QueryParam { private static final String ACCEPTS_AND_OR = "Accepts AND (" + AND + ") and OR (" + OR + ") operators."; public static final String ID_DESCR - = "List of IDs, these can be rs IDs (dbSNP) or variants in the format chrom:start:ref:alt, e.g. rs116600158,19:7177679:C:T"; + = "List of variant IDs in the format chrom:start:ref:alt, e.g. 19:7177679:C:T"; public static final VariantQueryParam ID = new VariantQueryParam("id", TEXT_ARRAY, ID_DESCR); public static final String REGION_DESCR @@ -122,7 +122,7 @@ public final class VariantQueryParam implements QueryParam { public static final String INCLUDE_SAMPLE_ID_DESCR = "Include sampleId on each result"; - public static final VariantQueryParam INCLUDE_SAMPLE_ID = new VariantQueryParam("includeSampleId", TEXT_ARRAY, INCLUDE_SAMPLE_ID_DESCR); + public static final VariantQueryParam INCLUDE_SAMPLE_ID = new VariantQueryParam("includeSampleId", BOOLEAN, INCLUDE_SAMPLE_ID_DESCR); public static final String SAMPLE_METADATA_DESCR = "Return the samples metadata group by study. Sample names will appear in the same order as their corresponding genotypes."; @@ -216,7 +216,7 @@ public final class VariantQueryParam implements QueryParam { public static final String ANNOT_XREF_DESCR = "List of any external reference, these can be genes, proteins or variants. " - + "Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, ..."; + + "Accepted IDs include HGNC, Ensembl genes, dbSNP, ClinVar, HPO, Cosmic, HGVS ..."; public static final VariantQueryParam ANNOT_XREF = new VariantQueryParam("xref", TEXT_ARRAY, ANNOT_XREF_DESCR); public static final String GENE_DESCR diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/LimitVariantDBIterator.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/LimitVariantDBIterator.java index 3f69fe91924..07ece2a578d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/LimitVariantDBIterator.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/LimitVariantDBIterator.java @@ -1,16 +1,32 @@ package org.opencb.opencga.storage.core.variant.adaptors.iterators; +import org.opencb.biodata.models.variant.Variant; + +import java.util.NoSuchElementException; + public class LimitVariantDBIterator extends DelegatedVariantDBIterator { private final int limit; + private int count; LimitVariantDBIterator(VariantDBIterator delegated, int limit) { super(delegated); this.limit = limit; + this.count = 0; } @Override public boolean hasNext() { - return getCount() < limit && super.hasNext(); + return count < limit && super.hasNext(); + } + + @Override + public Variant next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + ++this.count; + return super.next(); + } } } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/VariantDBIterator.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/VariantDBIterator.java index eae841fc766..f070c4c7bb1 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/VariantDBIterator.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/iterators/VariantDBIterator.java @@ -16,14 +16,14 @@ package org.opencb.opencga.storage.core.variant.adaptors.iterators; -import com.google.common.collect.Iterators; import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.VariantAvro; import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.utils.iterators.CloseableIterator; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -131,21 +131,34 @@ public final void forEachRemaining(Consumer action) { } } - public final DataResult toDataResult() { + + // TODO: The VariantDBIterator should be able to return the samples in the result + // This class should contain a ParsedVariantQuery +// public final VariantQueryResult toVariantQueryResult() { +// } + + public final VariantQueryResult toDataResult(ParsedVariantQuery variantQuery) { List result = new ArrayList<>(); this.forEachRemaining(result::add); int numResults = result.size(); int numTotalResults = -1; // Unknown numTotalResults - return new DataResult<>((int) getTimeFetching(TimeUnit.MILLISECONDS), Collections.emptyList(), numResults, result, numTotalResults); + DataResult dataResult = new DataResult<>((int) + getTimeFetching(TimeUnit.MILLISECONDS), + Collections.emptyList(), + numResults, + result, + numTotalResults); + return new VariantQueryResult<>(dataResult, variantQuery); } - public final VariantQueryResult toDataResult(Map> samples) { - return new VariantQueryResult<>(toDataResult(), samples); + public final List toList() { + List result = new ArrayList<>(); + this.forEachRemaining(result::add); + return result; } - protected interface TimeFunction { R call() throws E; } @@ -192,7 +205,11 @@ public VariantDBIterator filter(Predicate filter) { } public VariantDBIterator localSkip(int skip) { - Iterators.advance(this, skip); + int i = 0; + while (i < skip && hasNext()) { + next(); + i++; + } return this; } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/sample/VariantSampleDataManager.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/sample/VariantSampleDataManager.java index d0e09c582e5..b575d1f63e5 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/sample/VariantSampleDataManager.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/adaptors/sample/VariantSampleDataManager.java @@ -40,13 +40,13 @@ public VariantSampleDataManager(VariantDBAdaptor dbAdaptor) { } - public final DataResult getSampleData(String variant, String study, QueryOptions options) { + public final DataResult getSampleData(Variant variant, String study, QueryOptions options) { options = options == null ? new QueryOptions() : options; int sampleLimit = options.getInt(SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE_DEFAULT); return getSampleData(variant, study, options, sampleLimit); } - public final DataResult getSampleData(String variant, String study, QueryOptions options, int sampleLimit) { + public final DataResult getSampleData(Variant variant, String study, QueryOptions options, int sampleLimit) { options = options == null ? new QueryOptions() : options; @@ -77,7 +77,7 @@ public final DataResult getSampleData(String variant, String study, Que } protected DataResult getSampleData( - String variantStr, String study, QueryOptions options, List includeSamples, Set genotypes, + Variant variant, String study, QueryOptions options, List includeSamples, Set genotypes, int sampleLimit) { options = options == null ? new QueryOptions() : options; Set includeFields = VariantField.getIncludeFields(options); @@ -98,7 +98,7 @@ protected DataResult getSampleData( int queries = 0; while (true) { queries++; - Query query = new Query(VariantQueryParam.ID.key(), variantStr) + Query query = new Query(VariantQueryParam.ID.key(), variant.toString()) .append(VariantQueryParam.STUDY.key(), study) .append(VariantQueryParam.INCLUDE_GENOTYPE.key(), options.get(VariantQueryParam.INCLUDE_GENOTYPE.key())) .append(VariantQueryParam.INCLUDE_SAMPLE_DATA.key(), options.get(VariantQueryParam.INCLUDE_SAMPLE_DATA.key())) @@ -130,7 +130,7 @@ protected DataResult getSampleData( DataResult result = dbAdaptor.get(query, variantQueryOptions); if (result.getNumResults() == 0) { - throw VariantQueryException.variantNotFound(variantStr); + throw VariantQueryException.variantNotFound(variant.toString()); } dbTime += result.getTime(); Variant partialVariant = result.first(); @@ -199,7 +199,7 @@ protected DataResult getSampleData( } } - Variant variant = new Variant(variantStr); + variant = new Variant(variant.toString()); variant.setAnnotation(annotation); StudyEntry studyEntry = new StudyEntry(study); variant.addStudyEntry(studyEntry); diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/annotation/converters/VariantAnnotationModelUtils.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/annotation/converters/VariantAnnotationModelUtils.java new file mode 100644 index 00000000000..a4d40cf3d56 --- /dev/null +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/annotation/converters/VariantAnnotationModelUtils.java @@ -0,0 +1,150 @@ +package org.opencb.opencga.storage.core.variant.annotation.converters; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.opencb.biodata.models.variant.avro.*; +import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class VariantAnnotationModelUtils { + + /** + * Extracts all the XRefs from a VariantAnnotation object. + * Includes: + * - annotation.id + * - annotation.xrefs.id + * - annotation.hgvs + * - annotation.consequenceTypes.geneName + * - annotation.consequenceTypes.geneId + * - annotation.consequenceTypes.ensemblGeneId + * - annotation.consequenceTypes.transcriptId + * - annotation.consequenceTypes.ensemblTranscriptId + * - annotation.consequenceTypes.hgvs + * - annotation.consequenceTypes.proteinVariantAnnotation.proteinId + * - annotation.consequenceTypes.proteinVariantAnnotation.uniprotAccession + * - annotation.consequenceTypes.proteinVariantAnnotation.uniprotName + * - annotation.consequenceTypes.proteinVariantAnnotation.uniprotVariantId + * - annotation.consequenceTypes.proteinVariantAnnotation.features.id + * - annotation.traitAssociation.id + * - annotation.geneTraitAssociation.id + * - annotation.geneTraitAssociation.hpo + * - annotation.pharmacogenomics.id + * - annotation.pharmacogenomics.name + * + * @param variantAnnotation VariantAnnotation object + * @return Set of XRefs + */ + private static final Pattern HGVS_PATTERN = Pattern.compile("\\([^()]*\\)"); + + public Set extractXRefs(VariantAnnotation variantAnnotation) { + Set xrefs = new HashSet<>(100); + + if (variantAnnotation == null) { + return xrefs; + } + + xrefs.add(variantAnnotation.getId()); + + if (CollectionUtils.isNotEmpty(variantAnnotation.getXrefs())) { + for (Xref xref : variantAnnotation.getXrefs()) { + if (xref != null) { + xrefs.add(xref.getId()); + } + } + } + + List consequenceTypes = variantAnnotation.getConsequenceTypes(); + + if (CollectionUtils.isNotEmpty(variantAnnotation.getHgvs())) { + xrefs.addAll(variantAnnotation.getHgvs()); + + // TODO Remove this code when CellBase 6.4.0 returns the expected HGVS + for (String hgvs: variantAnnotation.getHgvs()) { + if (VariantQueryUtils.isTranscript(hgvs)) { + // 1. Remove the content between parentheses, e.g. ENST00000680783.1(ENSG00000135744):c.776T>C + if (hgvs.contains("(")) { + Matcher matcher = HGVS_PATTERN.matcher(hgvs); + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(result, ""); + } + matcher.appendTail(result); + xrefs.add(result.toString()); + } + + // 2. Add the HGVS with the Ensembl and gene name, e.g. ENSG00000135744:c.776T>C, AGT:c.776T>C + if (CollectionUtils.isNotEmpty(consequenceTypes)) { + for (ConsequenceType conseqType : consequenceTypes) { + if (conseqType != null && conseqType.getHgvs() != null && conseqType.getHgvs().contains(hgvs)) { + String[] fields = hgvs.split(":", 2); + if (StringUtils.isNotEmpty(conseqType.getGeneId())) { + xrefs.add(conseqType.getGeneId() + ":" + fields[1]); + } + if (StringUtils.isNotEmpty(conseqType.getGeneName())) { + xrefs.add(conseqType.getGeneName() + ":" + fields[1]); + } + break; + } + } + } + } + } + } + + if (CollectionUtils.isNotEmpty(consequenceTypes)) { + for (ConsequenceType conseqType : consequenceTypes) { + xrefs.add(conseqType.getGeneName()); + xrefs.add(conseqType.getGeneId()); + xrefs.add(conseqType.getTranscriptId()); + xrefs.add(conseqType.getEnsemblGeneId()); + xrefs.add(conseqType.getEnsemblTranscriptId()); + + ProteinVariantAnnotation protVarAnnotation = conseqType.getProteinVariantAnnotation(); + if (protVarAnnotation != null) { + xrefs.add(protVarAnnotation.getProteinId()); + xrefs.add(protVarAnnotation.getUniprotAccession()); + xrefs.add(protVarAnnotation.getUniprotName()); + xrefs.add(protVarAnnotation.getUniprotVariantId()); + + if (protVarAnnotation.getFeatures() != null) { + for (ProteinFeature proteinFeature : protVarAnnotation.getFeatures()) { + xrefs.add(proteinFeature.getId()); + } + } + } + } + } + + if (CollectionUtils.isNotEmpty(variantAnnotation.getTraitAssociation())) { + for (EvidenceEntry evidenceEntry : variantAnnotation.getTraitAssociation()) { + xrefs.add(evidenceEntry.getId()); + } + } + + if (CollectionUtils.isNotEmpty(variantAnnotation.getGeneTraitAssociation())) { + for (GeneTraitAssociation geneTrait : variantAnnotation.getGeneTraitAssociation()) { + xrefs.add(geneTrait.getId()); + xrefs.add(geneTrait.getHpo()); + } + } + + if (CollectionUtils.isNotEmpty(variantAnnotation.getPharmacogenomics())) { + for (Pharmacogenomics pharmacogenomics : variantAnnotation.getPharmacogenomics()) { + xrefs.add(pharmacogenomics.getId()); + xrefs.add(pharmacogenomics.getName()); + } + } + + // Remove empty strings and nulls + xrefs.remove(""); + xrefs.remove(null); + + return xrefs; + } + +} diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/io/json/mixin/ConsequenceTypeMixin.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/io/json/mixin/ConsequenceTypeMixin.java index ad643f53068..ab4946fc572 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/io/json/mixin/ConsequenceTypeMixin.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/io/json/mixin/ConsequenceTypeMixin.java @@ -4,12 +4,11 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.util.StdConverter; import org.opencb.biodata.models.variant.avro.ConsequenceType; -import org.opencb.cellbase.client.rest.ParentRestClient; import java.util.List; @JsonDeserialize( - converter = ParentRestClient.ConsequenceTypeMixin.ConsequenceTypeConverter.class + converter = ConsequenceTypeMixin.ConsequenceTypeConverter.class ) public interface ConsequenceTypeMixin { class ConsequenceTypeConverter extends StdConverter { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java index 9c89ffb5b61..8468ab34317 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java @@ -1,53 +1,76 @@ package org.opencb.opencga.storage.core.variant.query; +import org.opencb.biodata.models.core.Region; import org.opencb.biodata.models.variant.Variant; +import org.opencb.commons.datastore.core.Event; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; import org.opencb.opencga.storage.core.metadata.models.StudyResourceMetadata; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; import java.util.*; import java.util.stream.Collectors; +import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.*; import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.ID_INTERSECT; public class ParsedVariantQuery { private Query inputQuery; private QueryOptions inputOptions; - private Query query; + private VariantQuery query; private boolean optimized = false; + private List events = new ArrayList<>(); + private VariantQueryProjection projection; private final VariantStudyQuery studyQuery; -// private VariantAnnotationQuery annotationQuery; + private Integer limit; + private int skip; + private boolean count; + private int approximateCountSamplingSize; + private List geneRegions; + private List regions; + private List> clinicalCombination; + private List clinicalCombinationList; + // private VariantAnnotationQuery annotationQuery; public ParsedVariantQuery() { this.inputQuery = new Query(); this.inputOptions = new QueryOptions(); - this.query = new Query(); + this.query = new VariantQuery(); studyQuery = new VariantStudyQuery(); } public ParsedVariantQuery(Query inputQuery, QueryOptions inputOptions) { this.inputQuery = inputQuery; this.inputOptions = inputOptions; - this.query = inputQuery; + this.query = new VariantQuery(inputQuery); studyQuery = new VariantStudyQuery(); } public ParsedVariantQuery(ParsedVariantQuery other) { this.inputQuery = new Query(other.inputQuery); this.inputOptions = new QueryOptions(other.inputOptions); - this.query = new Query(other.query); + this.query = new VariantQuery(other.query); this.projection = other.projection; - this.studyQuery = other.studyQuery; + this.studyQuery = new VariantStudyQuery(other.getStudyQuery()); this.optimized = other.optimized; + this.limit = other.limit; + this.skip = other.skip; + this.count = other.count; + this.approximateCountSamplingSize = other.approximateCountSamplingSize; + this.geneRegions = new ArrayList<>(other.geneRegions); + this.regions = new ArrayList<>(other.regions); + this.clinicalCombination = new ArrayList<>(other.clinicalCombination); + this.clinicalCombinationList = new ArrayList<>(other.clinicalCombinationList); } public Query getInputQuery() { @@ -59,11 +82,11 @@ public ParsedVariantQuery setInputQuery(Query inputQuery) { return this; } - public Query getQuery() { + public VariantQuery getQuery() { return query; } - public ParsedVariantQuery setQuery(Query query) { + public ParsedVariantQuery setQuery(VariantQuery query) { this.query = query; return this; } @@ -77,6 +100,15 @@ public ParsedVariantQuery setOptimized(boolean optimized) { return this; } + public List getEvents() { + return events; + } + + public ParsedVariantQuery setEvents(List events) { + this.events = events; + return this; + } + public VariantQueryProjection getProjection() { return projection; } @@ -107,6 +139,24 @@ public VariantQueryXref getXrefs() { return VariantQueryParser.parseXrefs(query); } + public List getRegions() { + return regions; + } + + public ParsedVariantQuery setRegions(List regions) { + this.regions = regions; + return this; + } + + public List getGeneRegions() { + return geneRegions; + } + + public ParsedVariantQuery setGeneRegions(List geneRegions) { + this.geneRegions = geneRegions; + return this; + } + public List getConsequenceTypes() { return VariantQueryUtils.parseConsequenceTypes(query.getAsStringList(VariantQueryParam.ANNOT_CONSEQUENCE_TYPE.key())); } @@ -119,6 +169,76 @@ public List getTranscriptFlags() { return query.getAsStringList(VariantQueryParam.ANNOT_TRANSCRIPT_FLAG.key()); } + public Integer getLimit() { + return limit; + } + + public int getLimitOr(int defaultValue) { + return limit == null ? defaultValue : limit; + } + + public ParsedVariantQuery setLimit(Integer limit) { + this.limit = limit; + return this; + } + + public int getSkip() { + return skip; + } + + public ParsedVariantQuery setSkip(int skip) { + this.skip = skip; + return this; + } + + public boolean getCount() { + return count; + } + + public ParsedVariantQuery setCount(boolean count) { + this.count = count; + return this; + } + + public int getApproximateCountSamplingSize() { + return approximateCountSamplingSize; + } + + public ParsedVariantQuery setApproximateCountSamplingSize(int approximateCountSamplingSize) { + this.approximateCountSamplingSize = approximateCountSamplingSize; + return this; + } + + public ParsedQuery> getPopulationFrequencyAlt() { + return VariantQueryParser.parseFreqFilter(query, ANNOT_POPULATION_ALTERNATE_FREQUENCY); + } + + public ParsedQuery> getPopulationFrequencyRef() { + return VariantQueryParser.parseFreqFilter(query, ANNOT_POPULATION_REFERENCE_FREQUENCY); + } + + public ParsedQuery> getPopulationFrequencyMaf() { + return VariantQueryParser.parseFreqFilter(query, ANNOT_POPULATION_MINOR_ALLELE_FREQUENCY); + } + + public List> getClinicalCombinations() { + return clinicalCombination; + } + + public ParsedVariantQuery setClinicalCombination(List> clinicalCombination) { + this.clinicalCombination = clinicalCombination; + return this; + } + + public List getClinicalCombinationsList() { + return clinicalCombinationList; + } + + public ParsedVariantQuery setClinicalCombinationList(List clinicalCombinationList) { + this.clinicalCombinationList = clinicalCombinationList; + return this; + } + public static class VariantStudyQuery { private ParsedQuery studies; private ParsedQuery>> genotypes; @@ -131,6 +251,13 @@ public static class VariantStudyQuery { public VariantStudyQuery() { } + public VariantStudyQuery(VariantStudyQuery studyQuery) { + this.studies = studyQuery.studies; + this.genotypes = studyQuery.genotypes; + this.sampleDataQuery = studyQuery.sampleDataQuery; + this.defaultStudy = studyQuery.defaultStudy; + } + public ParsedQuery getStudies() { return studies; } @@ -140,6 +267,14 @@ public VariantStudyQuery setStudies(ParsedQuery studies) { return this; } + public String getStudyOrFail() { + if (studies == null || studies.size() != 1) { + throw new VariantQueryException("Require exactly one study"); + } else { + return studies.get(0); + } + } + public ParsedQuery>> getGenotypes() { return genotypes; } @@ -226,5 +361,16 @@ public List getIDsAndXrefs() { public boolean isEmpty() { return genes.isEmpty() && variants.isEmpty() && ids.isEmpty() && otherXrefs.isEmpty(); } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("VariantQueryXref{"); + sb.append("genes=").append(genes); + sb.append(", variants=").append(variants); + sb.append(", ids=").append(ids); + sb.append(", otherXrefs=").append(otherXrefs); + sb.append('}'); + return sb.toString(); + } } } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java index 1564ad0fabf..641e365a51d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java @@ -3,12 +3,15 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.opencb.biodata.models.core.Region; import org.opencb.biodata.models.variant.StudyEntry; import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.ClinicalSignificance; import org.opencb.biodata.models.variant.avro.VariantType; +import org.opencb.biodata.models.variant.exceptions.NonStandardCompliantSampleField; import org.opencb.biodata.models.variant.metadata.VariantFileHeaderComplexLine; +import org.opencb.biodata.tools.variant.VariantNormalizer; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.QueryParam; @@ -19,7 +22,9 @@ import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; import org.opencb.opencga.storage.core.metadata.models.VariantScoreMetadata; import org.opencb.opencga.storage.core.utils.CellBaseUtils; +import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; @@ -148,21 +153,39 @@ public ParsedVariantQuery parseQuery(Query query, QueryOptions options) { return parseQuery(query, options, false); } - public ParsedVariantQuery parseQuery(Query query, QueryOptions options, boolean skipPreProcess) { - if (query == null) { - query = new Query(); + public ParsedVariantQuery parseQuery(Query inputQuery, QueryOptions options, boolean skipPreProcess) { + if (inputQuery == null) { + inputQuery = new Query(); } if (options == null) { options = new QueryOptions(); } - ParsedVariantQuery variantQuery = new ParsedVariantQuery(new Query(query), new QueryOptions(options)); + ParsedVariantQuery variantQuery = new ParsedVariantQuery(new Query(inputQuery), new QueryOptions(options)); + int limit = options.getInt(QueryOptions.LIMIT, -1); + variantQuery.setLimit(limit == -1 ? null : limit); + variantQuery.setSkip(options.getInt(QueryOptions.SKIP, 0)); + variantQuery.setCount(options.getBoolean(QueryOptions.COUNT, false)); + variantQuery.setApproximateCountSamplingSize(options.getInt( + VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), + VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.defaultValue())); + + variantQuery.setProjection(projectionParser.parseVariantQueryProjection(inputQuery, options)); + VariantQuery query; if (!skipPreProcess) { - query = preProcessQuery(query, options); + query = new VariantQuery(preProcessQuery(inputQuery, options, variantQuery.getProjection())); + } else { + query = new VariantQuery(inputQuery); } variantQuery.setQuery(query); - variantQuery.setProjection(projectionParser.parseVariantQueryProjection(query, options)); + + List geneRegions = Region.parseRegions(query.getString(ANNOT_GENE_REGIONS.key())); + variantQuery.setGeneRegions(geneRegions == null ? Collections.emptyList() : geneRegions); + List regions = Region.parseRegions(query.region(), true); + variantQuery.setRegions(regions == null ? Collections.emptyList() : regions); + variantQuery.setClinicalCombination(VariantQueryParser.parseClinicalCombination(query, false)); + variantQuery.setClinicalCombinationList(VariantQueryParser.parseClinicalCombinationsList(query, false)); ParsedVariantQuery.VariantStudyQuery studyQuery = variantQuery.getStudyQuery(); @@ -173,7 +196,7 @@ public ParsedVariantQuery parseQuery(Query query, QueryOptions options, boolean } if (isValidParam(query, GENOTYPE)) { HashMap> map = new HashMap<>(); - QueryOperation op = VariantQueryUtils.parseGenotypeFilter(query.getString(GENOTYPE.key()), map); + QueryOperation op = VariantQueryUtils.parseGenotypeFilter(query.genotype(), map); if (defaultStudy == null) { List studyNames = metadataManager.getStudyNames(); @@ -209,13 +232,17 @@ public ParsedVariantQuery parseQuery(Query query, QueryOptions options, boolean return variantQuery; } - public Query preProcessQuery(Query originalQuery, QueryOptions options) { + public final Query preProcessQuery(Query originalQuery, QueryOptions options) { + return preProcessQuery(originalQuery, options, null); + } + + protected Query preProcessQuery(Query originalQuery, QueryOptions options, VariantQueryProjection projection) { // Copy input query! Do not modify original query! Query query = VariantQueryUtils.copy(originalQuery); preProcessAnnotationParams(query); - preProcessStudyParams(query, options); + preProcessStudyParams(query, options, projection); if (options != null && options.getLong(QueryOptions.LIMIT) < 0) { throw VariantQueryException.malformedParam(QueryOptions.LIMIT, options.getString(QueryOptions.LIMIT), @@ -361,7 +388,7 @@ private VariantType parseVariantType(String type) { } } - protected void preProcessStudyParams(Query query, QueryOptions options) { + protected void preProcessStudyParams(Query query, QueryOptions options, VariantQueryProjection projection) { StudyMetadata defaultStudy = getDefaultStudy(query); QueryOperation formatOperator = null; if (isValidParam(query, SAMPLE_DATA)) { @@ -433,6 +460,19 @@ protected void preProcessStudyParams(Query query, QueryOptions options) { } } + if (isValidParam(query, FILE)) { + ParsedQuery files = splitValue(query, FILE); + for (String file : files.getValues()) { + Pair fileIdPair = metadataManager.getFileIdPair(file, false, defaultStudy); + if (fileIdPair == null) { + throw VariantQueryException.fileNotFound(file, defaultStudy.getName()); + } + if (!metadataManager.isFileIndexed(fileIdPair.getKey(), fileIdPair.getValue())) { + throw VariantQueryException.fileNotIndexed(file, metadataManager.getStudyName(fileIdPair.getKey())); + } + } + } + QueryOperation genotypeOperator = null; VariantQueryParam genotypeParam = null; @@ -661,22 +701,23 @@ protected void preProcessStudyParams(Query query, QueryOptions options) { if (!isValidParam(query, INCLUDE_STUDY) || !isValidParam(query, INCLUDE_SAMPLE) || !isValidParam(query, INCLUDE_FILE) - || !isValidParam(query, SAMPLE_SKIP) - || !isValidParam(query, SAMPLE_LIMIT) + || isValidParam(query, SAMPLE_SKIP) + || isValidParam(query, SAMPLE_LIMIT) ) { - VariantQueryProjection selectVariantElements = - VariantQueryProjectionParser.parseVariantQueryFields(query, options, metadataManager); + if (projection == null) { + projection = projectionParser.parseVariantQueryProjection(query, options); + } // Apply the sample pagination. // Remove the sampleLimit and sampleSkip to avoid applying the pagination twice query.remove(SAMPLE_SKIP.key()); query.remove(SAMPLE_LIMIT.key()); - query.put(NUM_TOTAL_SAMPLES.key(), selectVariantElements.getNumTotalSamples()); - query.put(NUM_SAMPLES.key(), selectVariantElements.getNumSamples()); + query.put(NUM_TOTAL_SAMPLES.key(), projection.getNumTotalSamples()); + query.put(NUM_SAMPLES.key(), projection.getNumSamples()); if (!isValidParam(query, INCLUDE_STUDY)) { List includeStudy = new ArrayList<>(); - for (Integer studyId : selectVariantElements.getStudyIds()) { - includeStudy.add(selectVariantElements.getStudy(studyId).getStudyMetadata().getName()); + for (Integer studyId : projection.getStudyIds()) { + includeStudy.add(projection.getStudy(studyId).getStudyMetadata().getName()); } if (includeStudy.isEmpty()) { query.put(INCLUDE_STUDY.key(), NONE); @@ -684,22 +725,17 @@ protected void preProcessStudyParams(Query query, QueryOptions options) { query.put(INCLUDE_STUDY.key(), includeStudy); } } - if (!isValidParam(query, INCLUDE_SAMPLE) || selectVariantElements.getSamplePagination()) { - List includeSample = selectVariantElements.getSamples() - .entrySet() - .stream() - .flatMap(e -> e.getValue() - .stream() - .map(s -> metadataManager.getSampleName(e.getKey(), s))) - .collect(Collectors.toList()); + if (!isValidParam(query, INCLUDE_SAMPLE) || projection.getSamplePagination()) { + List includeSample = projection.getSampleNames().values() + .stream().flatMap(Collection::stream).collect(Collectors.toList()); if (includeSample.isEmpty()) { query.put(INCLUDE_SAMPLE.key(), NONE); } else { query.put(INCLUDE_SAMPLE.key(), includeSample); } } - if (!isValidParam(query, INCLUDE_FILE) || selectVariantElements.getSamplePagination()) { - List includeFile = selectVariantElements.getFiles() + if (!isValidParam(query, INCLUDE_FILE) || projection.getSamplePagination()) { + List includeFile = projection.getFiles() .entrySet() .stream() .flatMap(e -> e.getValue() @@ -851,15 +887,28 @@ public static ParsedVariantQuery.VariantQueryXref parseXrefs(Query query) { if (variant != null) { xrefs.getVariants().add(variant); } else { - if (isVariantAccession(value) || isClinicalAccession(value) || isGeneAccession(value)) { + if (isVariantAccession(value) + || isClinicalAccession(value) + || isGeneAccession(value) + || isHGVS(value) + || isProteinFeatureId(value)) { xrefs.getOtherXrefs().add(value); } else { genes.add(value); } } } + } + if (!xrefs.getVariants().isEmpty()) { + List normalizedVariants = normalizeVariants(xrefs.getVariants()); + for (Variant normalizedVariant : normalizedVariants) { + if (!xrefs.getVariants().contains(normalizedVariant)) { + xrefs.getVariants().add(normalizedVariant); + } + } } + if (isValidParam(query, ANNOT_GENE_ROLE_IN_CANER_GENES)) { List thisGenes = query.getAsStringList(ANNOT_GENE_ROLE_IN_CANER_GENES.key()); if (thisGenes.size() != 1 || !thisGenes.get(0).equals(NONE)) { @@ -886,6 +935,21 @@ public static ParsedVariantQuery.VariantQueryXref parseXrefs(Query query) { return xrefs; } + public static Variant normalizeVariant(Variant variant) { + return normalizeVariants(Collections.singletonList(variant)).get(0); + } + + public static List normalizeVariants(List variants) { + VariantNormalizer variantNormalizer = new VariantNormalizer(); + List normalizedVariants; + try { + normalizedVariants = variantNormalizer.normalize(variants, false); + } catch (NonStandardCompliantSampleField e) { + throw VariantQueryException.internalException(e); + } + return normalizedVariants; + } + public static ParsedQuery> parseFreqFilter(Query query, QueryParam queryParam) { return VariantQueryUtils.splitValue(query, queryParam) .map(VariantQueryUtils::parseKeyOpValue) diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/response/VariantQueryResult.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryResult.java similarity index 64% rename from opencga-core/src/main/java/org/opencb/opencga/core/response/VariantQueryResult.java rename to opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryResult.java index c955b3ca379..ee432deb3b7 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/response/VariantQueryResult.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryResult.java @@ -14,13 +14,17 @@ * limitations under the License. */ -package org.opencb.opencga.core.response; +package org.opencb.opencga.storage.core.variant.query; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.Event; import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.opencga.core.response.OpenCGAResult; +import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -45,25 +49,19 @@ public class VariantQueryResult extends OpenCGAResult { private static final String APPROXIMATE_COUNT = "approximateCount"; private static final String APPROXIMATE_COUNT_SAMPLING_SIZE = "approximateCountSamplingSize"; - public VariantQueryResult() { + protected VariantQueryResult() { } - public VariantQueryResult(long time, int numResults, long numMatches, List events, List result) { - this(time, numResults, numMatches, events, result, null, null, null, null, null); + public VariantQueryResult(long time, int numResults, long numMatches, List events, List result, String source, + ParsedVariantQuery variantQuery) { + this(time, numResults, numMatches, events, result, source, null, null, null, variantQuery); } public VariantQueryResult(long time, int numResults, long numMatches, List events, List result, - Map> samples, String source) { - this(time, numResults, numMatches, events, result, samples, source, null, null, null); - } - - public VariantQueryResult(long time, int numResults, long numMatches, List events, List result, - Map> samples, String source, Boolean approximateCount, - Integer approximateCountSamplingSize, Integer numTotalSamples) { + String source, Boolean approximateCount, Integer approximateCountSamplingSize, Integer numTotalSamples, + ParsedVariantQuery variantQuery) { super((int) time, events, numResults, result, numMatches); - if (samples != null) { - setSamples(samples); - } + if (source != null) { setSource(source); } @@ -73,15 +71,18 @@ public VariantQueryResult(long time, int numResults, long numMatches, List()); + } + if (variantQuery != null) { + addSamplesMetadataIfRequested(variantQuery); + } } - public VariantQueryResult(DataResult dataResult) { + private VariantQueryResult(DataResult dataResult, List results) { super(dataResult.getTime(), dataResult.getEvents(), dataResult.getNumMatches(), @@ -90,27 +91,66 @@ public VariantQueryResult(DataResult dataResult) { dataResult.getNumDeleted(), dataResult.getNumErrors(), dataResult.getAttributes()); - setResults(dataResult.getResults()); - setNumResults(dataResult.getNumResults()); + setResults(results); + setNumResults(results.size()); + if (getEvents() == null) { + setEvents(new ArrayList<>()); + } + } + + public VariantQueryResult(DataResult dataResult, ParsedVariantQuery variantQuery) { + this(dataResult, dataResult.getResults()); + if (variantQuery != null) { + addSamplesMetadataIfRequested(variantQuery); + } + } + + public VariantQueryResult(VariantQueryResult dataResult, List results) { + this((DataResult) dataResult, results); + } + + public VariantQueryResult(DataResult dataResult, String source, ParsedVariantQuery variantQuery) { + this(dataResult, variantQuery); + setSource(source); + } + + /* + * @deprecated Missing ParsedVariantQuery. + * Use {@link #VariantQueryResult(long, int, long, List, List, String, Boolean, Integer, Integer, ParsedVariantQuery)} + */ + @Deprecated + public VariantQueryResult(long time, int numResults, long numMatches, List events, List result, String source) { + this(time, numResults, numMatches, events, result, source, null, null, null, (ParsedVariantQuery) null); } - public VariantQueryResult(DataResult queryResult, Map> samples) { - this(queryResult, samples, null); + /* + * @deprecated Missing ParsedVariantQuery. + * Use {@link #VariantQueryResult(DataResult, ParsedVariantQuery)} + */ + @Deprecated + public VariantQueryResult(DataResult dataResult) { + this(dataResult, (ParsedVariantQuery) null); } - public VariantQueryResult(DataResult dataResult, Map> samples, String source) { - this(dataResult); - setSamples(samples); - if (getNumMatches() >= 0) { - setApproximateCount(false); + private void addSamplesMetadataIfRequested(ParsedVariantQuery query) { + VariantQueryProjection projection = query.getProjection(); + + // Ensure is modifiable + if (getEvents() == null || Collections.emptyList().getClass().equals(getEvents().getClass())) { + setEvents(new ArrayList<>()); } - if (samples != null) { - this.setNumSamples(samples.values().stream().mapToInt(List::size).sum()); - this.setNumTotalSamples(getNumSamples()); + if (!query.getEvents().isEmpty()) { + getEvents().addAll(query.getEvents()); } - if (source != null) { - this.setSource(source); + if (!projection.getEvents().isEmpty()) { + getEvents().addAll(projection.getEvents()); + } + + if (query.getQuery().sampleMetadata()) { + setSamples(query.getProjection().getSampleNames()); } + setNumSamples(projection.getNumSamples()); + setNumTotalSamples(projection.getNumTotalSamples()); } public Map> getSamples() { @@ -173,7 +213,9 @@ public VariantQueryResult setApproximateCount(Boolean approximateCount) { } public Integer getApproximateCountSamplingSize() { - return getAttributes().containsKey(APPROXIMATE_COUNT_SAMPLING_SIZE) ? getAttributes().getInt(APPROXIMATE_COUNT_SAMPLING_SIZE) : null; + return getAttributes().containsKey(APPROXIMATE_COUNT_SAMPLING_SIZE) + ? getAttributes().getInt(APPROXIMATE_COUNT_SAMPLING_SIZE) + : null; } public VariantQueryResult setApproximateCountSamplingSize(Integer approximateCountSamplingSize) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryUtils.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryUtils.java index 114379c77a9..9cf773d001f 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryUtils.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryUtils.java @@ -28,12 +28,13 @@ import org.opencb.biodata.models.variant.VariantBuilder; import org.opencb.biodata.models.variant.annotation.ConsequenceTypeMappings; import org.opencb.biodata.models.variant.avro.*; -import org.opencb.commons.datastore.core.*; +import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.commons.datastore.core.Query; +import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.commons.datastore.core.QueryParam; import org.opencb.commons.utils.ListUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.models.variant.VariantAnnotationConstants; -import org.opencb.opencga.core.response.VariantQueryResult; -import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.utils.CellBaseUtils; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; @@ -466,6 +467,33 @@ public static boolean isVariantAccession(String value) { return value.startsWith("rs") || value.startsWith("VAR_"); } + /** + * Determines if the given value might be a known transcript or not. + * Ensembl transcripts start with `ENST` + * RefSeq transcripts start with `NM_` and `XM_` + * See ... + * + * @param value Value to check + * @return If is a known transcript + */ + public static boolean isTranscript(String value) { + return value.startsWith("ENST") || value.startsWith("NM_") || value.startsWith("XM_"); + } + + /** + * Determines if the given value is a HGVS. + * + * @param value Value to check + * @return If is a known accession + */ + public static boolean isHGVS(String value) { + // Check regex ':[cnpg].' + // HGVC examples : + // - "1:g.65325832G>A" + // - "1:g.65325832_65325833insA" + return value.contains(":c.") || value.contains(":n.") || value.contains(":p.") || value.contains(":g."); + } + /** * Determines if the given value is a known clinical accession or not. *

@@ -494,6 +522,18 @@ public static boolean isGeneAccession(String value) { return isHpo(value) || value.startsWith("OMIM:") || value.startsWith("umls:"); } + /** + * Determines if the given value is a valid protein feature id. + *

+ * Protein feature id starts with 'PRO_', 'VAR_' or 'VSP_' + * + * @param value Value to check + * @return If is a known accession + */ + public static boolean isProteinFeatureId(String value) { + return value.startsWith("PRO_") | value.startsWith("VAR_") | value.startsWith("VSP_"); + } + /** * Determines if the given value is a HPO term or not. *

@@ -552,6 +592,14 @@ public static Variant toVariant(String value) { return variant; } + public static Variant toVariant(String variantStr, boolean normalize) { + Variant variant = toVariant(variantStr); + if (normalize && variant != null) { + return VariantQueryParser.normalizeVariant(variant); + } + return variant; + } + public static String[] splitStudyResource(String value) { int idx = value.lastIndexOf(STUDY_RESOURCE_SEPARATOR); if (idx <= 0 || idx == value.length() - 1) { @@ -583,37 +631,6 @@ public static boolean isOutputMultiStudy(Query query, QueryOptions options, Coll } } - public static VariantQueryResult addSamplesMetadataIfRequested(DataResult result, Query query, QueryOptions options, - VariantStorageMetadataManager variantStorageMetadataManager) { - return addSamplesMetadataIfRequested(new VariantQueryResult<>(result, null), query, options, variantStorageMetadataManager); - } - - public static VariantQueryResult addSamplesMetadataIfRequested(VariantQueryResult result, Query query, QueryOptions options, - VariantStorageMetadataManager variantStorageMetadataManager) { - if (query.getBoolean(SAMPLE_METADATA.key(), false)) { - int numTotalSamples = query.getInt(NUM_TOTAL_SAMPLES.key(), -1); - int numSamples = query.getInt(NUM_SAMPLES.key(), -1); - Map> samplesMetadata = VariantQueryProjectionParser - .getIncludeSampleNames(query, options, variantStorageMetadataManager); - if (numTotalSamples < 0 && numSamples < 0) { - numTotalSamples = samplesMetadata.values().stream().mapToInt(List::size).sum(); - VariantQueryProjectionParser.skipAndLimitSamples(query, samplesMetadata); - numSamples = samplesMetadata.values().stream().mapToInt(List::size).sum(); - } - return result.setNumSamples(numSamples) - .setNumTotalSamples(numTotalSamples) - .setSamples(samplesMetadata); - } else { - int numTotalSamples = query.getInt(NUM_TOTAL_SAMPLES.key(), -1); - int numSamples = query.getInt(NUM_SAMPLES.key(), -1); - if (numTotalSamples >= 0 && numSamples >= 0) { - return result.setNumSamples(numSamples) - .setNumTotalSamples(numTotalSamples); - } - return result; - } - } - /** * Gets a list of elements sample data keys to return. * @@ -1572,6 +1589,13 @@ public static String printQuery(Query query) { query.put(ANNOT_GENE_REGIONS.key(), "numGeneRegions : " + ((Collection) geneRegions).size()); } } + if (isValidParam(query, ID_INTERSECT)) { + query = new Query(query); + Object idIntersect = query.get(ID_INTERSECT.key()); + if (idIntersect instanceof Collection) { + query.put(ID_INTERSECT.key(), "numIdIntersect : " + ((Collection) idIntersect).size()); + } + } try { return QUERY_MAPPER.writeValueAsString(query); } catch (JsonProcessingException e) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/AbstractTwoPhasedVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/AbstractTwoPhasedVariantQueryExecutor.java index a49fe60c3e4..d71311a107d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/AbstractTwoPhasedVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/AbstractTwoPhasedVariantQueryExecutor.java @@ -7,7 +7,7 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.common.TimeUtils; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIteratorWithCounts; @@ -133,7 +133,7 @@ protected boolean shouldGetApproximateCount(QueryOptions options, boolean iterat // } protected int getLimit(QueryOptions options) { - return options.getInt(QueryOptions.LIMIT); + return options.getInt(QueryOptions.LIMIT, -1); } protected int getSkip(QueryOptions options) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java index 0e8c9ead98c..6eb237ea4b3 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java @@ -1,6 +1,5 @@ package org.opencb.opencga.storage.core.variant.query.executors; -import com.google.common.collect.Iterators; import org.apache.commons.lang3.StringUtils; import org.opencb.biodata.models.core.Region; import org.opencb.biodata.models.variant.StudyEntry; @@ -13,14 +12,13 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.QueryParam; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; -import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; -import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.query.filters.VariantFilterBuilder; @@ -33,12 +31,12 @@ public class BreakendVariantQueryExecutor extends VariantQueryExecutor { private final VariantDBAdaptor variantDBAdaptor; private final VariantFilterBuilder filterBuilder; - public BreakendVariantQueryExecutor(VariantStorageMetadataManager metadataManager, String storageEngineId, ObjectMap options, + public BreakendVariantQueryExecutor(String storageEngineId, ObjectMap options, VariantQueryExecutor delegatedQueryExecutor, VariantDBAdaptor variantDBAdaptor) { - super(metadataManager, storageEngineId, options); + super(variantDBAdaptor.getMetadataManager(), storageEngineId, options); this.delegatedQueryExecutor = delegatedQueryExecutor; this.variantDBAdaptor = variantDBAdaptor; - filterBuilder = new VariantFilterBuilder(metadataManager); + filterBuilder = new VariantFilterBuilder(); } @Override @@ -48,35 +46,39 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) throws Stor } @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean getIterator) throws StorageEngineException { - int limit = options.getInt(QueryOptions.LIMIT); - int skip = options.getInt(QueryOptions.SKIP); - boolean count = options.getBoolean(QueryOptions.COUNT); - int approximateCountSamplingSize = options.getInt( - VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), - VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.defaultValue()); - Query baseQuery = baseQuery(query); - Predicate variantLocalFilter = filterBuilder.buildFilter(query, options); - + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean getIterator) throws StorageEngineException { + int limit = variantQuery.getLimitOr(-1); + int skip = variantQuery.getSkip(); + boolean count = variantQuery.getCount() && !getIterator; + int approximateCountSamplingSize = variantQuery.getApproximateCountSamplingSize(); + Query baseQuery = baseQuery(variantQuery.getQuery()); + Predicate variantLocalFilter = filterBuilder.buildFilter(variantQuery); + + // Copy to avoid modifications to input query + ParsedVariantQuery delegatedVariantQuery = new ParsedVariantQuery(variantQuery); + QueryOptions options = new QueryOptions(variantQuery.getInputOptions()); + options.remove(QueryOptions.SKIP); + delegatedVariantQuery.setSkip(0); + if (limit >= 0) { + int tmpLimit = skip + limit * 4; + if (count && tmpLimit < approximateCountSamplingSize) { + tmpLimit = approximateCountSamplingSize; + } + options.put(QueryOptions.LIMIT, tmpLimit); + delegatedVariantQuery.setLimit(tmpLimit); + } + delegatedVariantQuery.setInputOptions(options); if (getIterator) { - VariantDBIterator iterator = delegatedQueryExecutor.iterator(query, options); + VariantDBIterator iterator = delegatedQueryExecutor.iterator(delegatedVariantQuery); iterator = iterator.mapBuffered(l -> getBreakendPairs(0, baseQuery, variantLocalFilter, l), 100); iterator = iterator.localLimitSkip(limit, skip); return iterator; } else { - // Copy to avoid modifications to input options - options = new QueryOptions(options); - options.remove(QueryOptions.SKIP); - int tmpLimit = skip + limit * 2; - if (count && tmpLimit < approximateCountSamplingSize) { - tmpLimit = approximateCountSamplingSize; - } - options.put(QueryOptions.LIMIT, tmpLimit); - VariantQueryResult queryResult = delegatedQueryExecutor.get(query, options); + VariantQueryResult queryResult = delegatedQueryExecutor.get(delegatedVariantQuery); List results = queryResult.getResults(); results = getBreakendPairs(0, baseQuery, variantLocalFilter, results); - if (queryResult.getNumMatches() < tmpLimit) { + if (queryResult.getNumMatches() < delegatedVariantQuery.getLimitOr(-1)) { // Exact count!! queryResult.setApproximateCount(false); queryResult.setNumMatches(results.size()); @@ -100,24 +102,6 @@ protected Object getOrIterator(Query query, QueryOptions options, boolean getIte } } - - protected VariantDBIterator iterator(Query query, QueryOptions options, int batchSize) throws StorageEngineException { - - int limit = options.getInt(QueryOptions.LIMIT); - int skip = options.getInt(QueryOptions.SKIP); - Query baseQuery = baseQuery(query); - Predicate variantLocalFilter = filterBuilder.buildFilter(query, options); - - VariantDBIterator iterator = delegatedQueryExecutor.iterator(query, options); - iterator = iterator.mapBuffered(l -> getBreakendPairs(0, baseQuery, variantLocalFilter, l), batchSize); - Iterators.advance(iterator, skip); - iterator = iterator.localSkip(skip); - if (limit > 0) { - iterator = iterator.localLimit(limit); - } - return iterator; - } - private Query baseQuery(Query query) { return subQuery(query, VariantQueryParam.STUDY, @@ -143,7 +127,6 @@ private List getBreakendPairs(int samplePosition, Query baseQuery, Pred } // Copy query to avoid propagating modifications baseQuery = new Query(baseQuery); -// System.out.println("variants = " + variants); List regions = new ArrayList<>(variants.size()); for (Variant variant : variants) { BreakendMate mate = variant.getSv().getBreakend().getMate(); @@ -190,7 +173,7 @@ private List getBreakendPairs(int samplePosition, Query baseQuery, Pred return variantPairs; } - private void addPair(Predicate filter, List variantPairs, Variant variant, Variant mateVariant) { + private boolean addPair(Predicate filter, List variantPairs, Variant variant, Variant mateVariant) { // Check for duplicated pairs if (VariantDBIterator.VARIANT_COMPARATOR.compare(variant, mateVariant) > 0) { // The mate variant is "before" the main variant @@ -200,10 +183,14 @@ private void addPair(Predicate filter, List variantPairs, Vari // But first the "mate" to respect order variantPairs.add(mateVariant); variantPairs.add(variant); + return true; + } else { + return false; } } else { variantPairs.add(variant); variantPairs.add(mateVariant); + return true; } } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/ChromDensityVariantAggregationExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/ChromDensityVariantAggregationExecutor.java index b7a03ceac34..9606a74a85d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/ChromDensityVariantAggregationExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/ChromDensityVariantAggregationExecutor.java @@ -8,7 +8,7 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.solr.FacetQueryParser; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; import org.opencb.opencga.storage.core.variant.adaptors.VariantIterable; @@ -144,7 +144,7 @@ protected VariantQueryResult aggregation(Query query, QueryOptions o regionBuckets.size(), regionBuckets); return new VariantQueryResult<>((int) stopWatch.getTime(TimeUnit.MILLISECONDS), 1, numMatches, Collections.emptyList(), - Collections.singletonList(field), null, null); + Collections.singletonList(field), null); } private VariantQueryException invalidNestedField(String nestedFieldName) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java index db0815b5135..c6f4b87a5f0 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java @@ -3,21 +3,24 @@ import com.google.common.collect.Iterators; import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.tools.pedigree.ModeOfInheritance; -import org.opencb.opencga.core.models.variant.VariantAnnotationConstants; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.core.models.variant.VariantAnnotationConstants; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; import org.opencb.opencga.storage.core.metadata.models.Trio; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; -import org.opencb.opencga.storage.core.variant.adaptors.*; +import org.opencb.opencga.storage.core.variant.adaptors.VariantField; +import org.opencb.opencga.storage.core.variant.adaptors.VariantIterable; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.UnionMultiVariantKeyIterator; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIteratorWithCounts; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,15 +69,11 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) throws Stor } @Override - public DataResult count(Query query) { - throw new UnsupportedOperationException(); - } - - @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator) { - Trio trio = getCompHetTrio(query); - return getOrIterator(query.getString(VariantQueryParam.STUDY.key()), trio.getChild(), trio.getFather(), trio.getMother(), - query, options, iterator); + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) { + Trio trio = getCompHetTrio(variantQuery.getQuery()); + String study = variantQuery.getStudyQuery().getStudyOrFail(); + return getOrIterator(study, trio.getChild(), trio.getFather(), trio.getMother(), + variantQuery, iterator); } @Override @@ -84,25 +83,25 @@ protected long primaryCount(Query query, QueryOptions options) { .append(QueryOptions.INCLUDE, VariantField.ID.fieldName()))); } - public VariantQueryResult get(String study, String proband, String father, String mother, Query query, QueryOptions options) { - return (VariantQueryResult) getOrIterator(study, proband, father, mother, query, options, false); + public VariantQueryResult get(String study, String proband, String father, String mother, ParsedVariantQuery variantQuery) { + return (VariantQueryResult) getOrIterator(study, proband, father, mother, variantQuery, false); } - public VariantDBIterator iterator(String study, String proband, String father, String mother, Query query, QueryOptions options) { - return (VariantDBIterator) getOrIterator(study, proband, father, mother, query, options, true); + public VariantDBIterator iterator(String study, String proband, String father, String mother, ParsedVariantQuery variantQuery) { + return (VariantDBIterator) getOrIterator(study, proband, father, mother, variantQuery, true); } - private Object getOrIterator(String study, String proband, String father, String mother, Query query, QueryOptions inputOptions, + private Object getOrIterator(String study, String proband, String father, String mother, ParsedVariantQuery variantQuery, boolean iterator) { // Prepare query and options - int skip = getSkip(inputOptions); - int limit = inputOptions.containsKey(QueryOptions.LIMIT) ? getLimit(inputOptions) : (Integer.MAX_VALUE - skip); - int samplingSize = getSamplingSize(inputOptions, DEFAULT_SAMPLING_SIZE, iterator); - QueryOptions options = buildQueryOptions(inputOptions); + int skip = variantQuery.getSkip(); + int limit = variantQuery.getLimit() != null ? variantQuery.getLimit() : (Integer.MAX_VALUE - skip); + int samplingSize = getSamplingSize(variantQuery.getInputOptions(), DEFAULT_SAMPLING_SIZE, iterator); + QueryOptions options = buildQueryOptions(variantQuery.getInputOptions()); // Always sort results for compound heterozygous options.put(QueryOptions.SORT, true); - query = new Query(query); + Query query = new Query(variantQuery.getQuery()); List includeSample = getAndCheckIncludeSample(query, proband, father, mother); Set biotypes; @@ -158,12 +157,12 @@ private Object getOrIterator(String study, String proband, String father, String return VariantDBIterator.wrapper(variantIterator); } else { VariantQueryResult result = VariantDBIterator.wrapper(variantIterator) - .toDataResult(Collections.singletonMap(study, includeSample)); + .toDataResult(variantQuery); if ((limit + skip) < samplingSize && compoundHeterozygous.size() < samplingSize) { result.setApproximateCount(false); result.setNumMatches(compoundHeterozygous.size()); } else { - setNumTotalResults(unfilteredIterator, result, query, inputOptions, + setNumTotalResults(unfilteredIterator, result, query, variantQuery.getInputOptions(), unfilteredIterator.getCount(), compoundHeterozygous.size()); } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java index 08705ccf69e..474cbc3fa9f 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java @@ -2,7 +2,9 @@ import org.opencb.biodata.models.variant.Variant; import org.opencb.commons.datastore.core.*; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.exceptions.StorageEngineException; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; @@ -37,11 +39,11 @@ public DBAdaptorVariantQueryExecutor(VariantDBAdaptor dbAdaptor, String storageE } @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator) { + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) throws StorageEngineException { if (iterator) { - return dbAdaptor.iterator(query, options); + return dbAdaptor.iterator(variantQuery); } else { - VariantQueryResult result = dbAdaptor.get(query, options); + VariantQueryResult result = dbAdaptor.get(variantQuery); if (result.getSource() == null || result.getSource().isEmpty()) { result.setSource(storageEngineId); } @@ -49,11 +51,6 @@ protected Object getOrIterator(Query query, QueryOptions options, boolean iterat } } - @Override - public DataResult count(Query query) { - return dbAdaptor.count(query); - } - @Override public boolean canUseThisExecutor(Query query, QueryOptions options) { for (QueryParam unsupportedParam : UNSUPPORTED_PARAMS) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java index 57b1e64ae58..e286b4a07ce 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java @@ -1,10 +1,8 @@ package org.opencb.opencga.storage.core.variant.query.executors; -import org.opencb.biodata.models.variant.Variant; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.CohortMetadata; @@ -126,14 +124,11 @@ private boolean checkStatsFilter(Query query, String sample, VariantQueryParam p } @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator) throws StorageEngineException { + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) throws StorageEngineException { if (iterator) { return VariantDBIterator.emptyIterator(); } else { - VariantQueryResult result = new VariantQueryResult<>(0, 0, 0, Collections.emptyList(), Collections.emptyList()); - result.setSource(NO_OP); - VariantQueryUtils.addSamplesMetadataIfRequested(result, query, options, metadataManager); - return result; + return new VariantQueryResult<>(0, 0, 0, Collections.emptyList(), Collections.emptyList(), NO_OP, variantQuery); } } } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantAggregationExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantAggregationExecutor.java index a2c7f67d54e..8479dd683f7 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantAggregationExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantAggregationExecutor.java @@ -4,7 +4,7 @@ import org.opencb.commons.datastore.core.FacetField; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java index 8e445cc30e2..26d53e89e5c 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java @@ -1,18 +1,15 @@ package org.opencb.opencga.storage.core.variant.query.executors; import org.opencb.biodata.models.variant.Variant; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; -import org.opencb.opencga.storage.core.variant.adaptors.VariantIterable; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; - -import java.util.Collections; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import static org.opencb.opencga.storage.core.variant.VariantStorageOptions.QUERY_DEFAULT_TIMEOUT; import static org.opencb.opencga.storage.core.variant.VariantStorageOptions.QUERY_MAX_TIMEOUT; @@ -22,7 +19,7 @@ * * @author Jacobo Coll <jacobo167@gmail.com> */ -public abstract class VariantQueryExecutor implements VariantIterable { +public abstract class VariantQueryExecutor { protected final VariantStorageMetadataManager metadataManager; protected final String storageEngineId; @@ -34,19 +31,17 @@ public VariantQueryExecutor(VariantStorageMetadataManager metadataManager, Strin this.options = options; } - public final VariantQueryResult get(Query query, QueryOptions options) { + public final VariantQueryResult get(ParsedVariantQuery query) { try { - return (VariantQueryResult) getOrIterator(query, options, false); + return (VariantQueryResult) getOrIterator(query, false); } catch (StorageEngineException e) { throw VariantQueryException.internalException(e); } } - @Override - public final VariantDBIterator iterator(Query query, QueryOptions options) { + public final VariantDBIterator iterator(ParsedVariantQuery variantQuery) { try { -// query = parser.preProcessQuery(query, options); - return (VariantDBIterator) getOrIterator(query, options, true); + return (VariantDBIterator) getOrIterator(variantQuery, true); } catch (StorageEngineException e) { throw VariantQueryException.internalException(e); } @@ -77,19 +72,7 @@ public static void setDefaultTimeout(QueryOptions queryOptions, ObjectMap config */ public abstract boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException; - @Deprecated - public DataResult count(Query query) { - VariantQueryResult result = get(query, new QueryOptions(QueryOptions.COUNT, true).append(QueryOptions.LIMIT, 0)); - return new DataResult<>( - result.getTime(), - result.getEvents(), - 1, - Collections.singletonList(result.getNumMatches()), - result.getNumMatches(), - result.getAttributes()); - } - - protected abstract Object getOrIterator(Query query, QueryOptions options, boolean iterator) throws StorageEngineException; + protected abstract Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) throws StorageEngineException; protected VariantStorageMetadataManager getMetadataManager() { return metadataManager; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/filters/VariantFilterBuilder.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/filters/VariantFilterBuilder.java index f8705d26eef..1f505864fd1 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/filters/VariantFilterBuilder.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/filters/VariantFilterBuilder.java @@ -5,32 +5,26 @@ import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.ConsequenceType; import org.opencb.biodata.models.variant.avro.SequenceOntologyTerm; -import org.opencb.commons.datastore.core.Query; -import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; -import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; -import org.opencb.opencga.storage.core.variant.query.*; +import org.opencb.opencga.storage.core.variant.query.KeyOpValue; +import org.opencb.opencga.storage.core.variant.query.ParsedQuery; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; -import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.*; - public class VariantFilterBuilder { - private final VariantStorageMetadataManager metadataManager; - - public VariantFilterBuilder(VariantStorageMetadataManager metadataManager) { - this.metadataManager = metadataManager; + public VariantFilterBuilder() { } - public Predicate buildFilter(Query query, QueryOptions queryOptions) { + public Predicate buildFilter(ParsedVariantQuery variantQuery) { List> filters = new LinkedList<>(); - addRegionFilters(query, filters); - addAnnotationFilters(query, filters); + addRegionFilters(variantQuery, filters); + addAnnotationFilters(variantQuery, filters); if (filters.isEmpty()) { return v -> true; @@ -39,18 +33,18 @@ public Predicate buildFilter(Query query, QueryOptions queryOptions) { } } - private void addRegionFilters(Query query, List> filters) { + private void addRegionFilters(ParsedVariantQuery variantQuery, List> filters) { List> regionFilters = new LinkedList<>(); - if (VariantQueryUtils.isValidParam(query, VariantQueryParam.REGION)) { - List regions = Region.parseRegions(query.getString(REGION.key()), true); + List regions = variantQuery.getRegions(); + if (!regions.isEmpty()) { regions = VariantQueryUtils.mergeRegions(regions); for (Region region : regions) { regionFilters.add(variant -> region.contains(variant.getChromosome(), variant.getStart())); } } - ParsedVariantQuery.VariantQueryXref variantQueryXref = VariantQueryParser.parseXrefs(query); - Predicate geneFilter = getGeneFilter(query, variantQueryXref.getGenes()); + ParsedVariantQuery.VariantQueryXref variantQueryXref = variantQuery.getXrefs(); + Predicate geneFilter = getGeneFilter(variantQuery, variantQueryXref.getGenes()); if (!variantQueryXref.getIds().isEmpty()) { Set ids = new HashSet<>(variantQueryXref.getIds()); regionFilters.add(variant -> ids.contains(variant.getAnnotation().getId())); @@ -65,15 +59,14 @@ private void addRegionFilters(Query query, List> filters) { if (!regionFilters.isEmpty()) { Set bts; - if (VariantQueryUtils.isValidParam(query, VariantQueryParam.ANNOT_BIOTYPE)) { - bts = new HashSet<>(query.getAsStringList(VariantQueryParam.ANNOT_BIOTYPE.key())); + if (!variantQuery.getBiotypes().isEmpty()) { + bts = new HashSet<>(variantQuery.getBiotypes()); } else { bts = null; } Set cts; - if (VariantQueryUtils.isValidParam(query, VariantQueryParam.ANNOT_CONSEQUENCE_TYPE)) { - cts = new HashSet<>(VariantQueryUtils - .parseConsequenceTypes(query.getAsStringList(VariantQueryParam.ANNOT_CONSEQUENCE_TYPE.key()))); + if (!variantQuery.getConsequenceTypes().isEmpty()) { + cts = new HashSet<>(variantQuery.getConsequenceTypes()); } else { cts = null; } @@ -99,12 +92,12 @@ private void addRegionFilters(Query query, List> filters) { } } - private Predicate getGeneFilter(Query query, List genes) { + private Predicate getGeneFilter(ParsedVariantQuery variantQuery, List genes) { if (genes.isEmpty()) { return null; } - List geneRegions = Region.parseRegions(query.getString(VariantQueryUtils.ANNOT_GENE_REGIONS.key())); + List geneRegions = variantQuery.getGeneRegions(); Predicate geneRegionFilter; if (CollectionUtils.isEmpty(geneRegions)) { geneRegionFilter = null; @@ -115,15 +108,14 @@ private Predicate getGeneFilter(Query query, List genes) { Predicate geneFilter; Set bts; - if (VariantQueryUtils.isValidParam(query, VariantQueryParam.ANNOT_BIOTYPE)) { - bts = new HashSet<>(query.getAsStringList(VariantQueryParam.ANNOT_BIOTYPE.key())); + if (!variantQuery.getBiotypes().isEmpty()) { + bts = new HashSet<>(variantQuery.getBiotypes()); } else { bts = null; } Set cts; - if (VariantQueryUtils.isValidParam(query, VariantQueryParam.ANNOT_CONSEQUENCE_TYPE)) { - cts = new HashSet<>(VariantQueryUtils - .parseConsequenceTypes(query.getAsStringList(VariantQueryParam.ANNOT_CONSEQUENCE_TYPE.key()))); + if (!variantQuery.getConsequenceTypes().isEmpty()) { + cts = new HashSet<>(variantQuery.getConsequenceTypes()); } else { cts = null; } @@ -151,31 +143,30 @@ private Predicate getGeneFilter(Query query, List genes) { } } - private void addAnnotationFilters(Query query, List> filters) { -// ParsedVariantQuery.VariantQueryXref variantQueryXref = VariantQueryParser.parseXrefs(query); - addClinicalFilters(query, filters); + private void addAnnotationFilters(ParsedVariantQuery variantQuery, List> filters) { +// ParsedVariantQuery.VariantQueryXref variantQueryXref = variantQuery.getXrefs(); + addClinicalFilters(variantQuery, filters); - if (VariantQueryUtils.isValidParam(query, ANNOT_POPULATION_ALTERNATE_FREQUENCY)) { - ParsedQuery> freqQuery - = VariantQueryParser.parseFreqFilter(query, ANNOT_POPULATION_ALTERNATE_FREQUENCY); + ParsedQuery> freqQuery = variantQuery.getPopulationFrequencyAlt(); + if (!freqQuery.isEmpty()) { List freqFilters = freqQuery.mapValues(popFreq -> { String[] split = popFreq.getKey().split(VariantQueryUtils.STUDY_POP_FREQ_SEPARATOR); return new PopulationFrequencyVariantFilter.AltFreqFilter(split[0], split[1], popFreq.getOp(), popFreq.getValue()); }); filters.add(new PopulationFrequencyVariantFilter(freqQuery.getOperation(), freqFilters)); } - if (VariantQueryUtils.isValidParam(query, ANNOT_POPULATION_REFERENCE_FREQUENCY)) { - ParsedQuery> freqQuery - = VariantQueryParser.parseFreqFilter(query, ANNOT_POPULATION_REFERENCE_FREQUENCY); + + freqQuery = variantQuery.getPopulationFrequencyRef(); + if (!freqQuery.isEmpty()) { List freqFilters = freqQuery.mapValues(popFreq -> { String[] split = popFreq.getKey().split(VariantQueryUtils.STUDY_POP_FREQ_SEPARATOR); return new PopulationFrequencyVariantFilter.RefFreqFilter(split[0], split[1], popFreq.getOp(), popFreq.getValue()); }); filters.add(new PopulationFrequencyVariantFilter(freqQuery.getOperation(), freqFilters)); } - if (VariantQueryUtils.isValidParam(query, ANNOT_POPULATION_MINOR_ALLELE_FREQUENCY)) { - ParsedQuery> freqQuery - = VariantQueryParser.parseFreqFilter(query, ANNOT_POPULATION_MINOR_ALLELE_FREQUENCY); + + freqQuery = variantQuery.getPopulationFrequencyMaf(); + if (!freqQuery.isEmpty()) { List freqFilters = freqQuery.mapValues(popFreq -> { String[] split = popFreq.getKey().split(VariantQueryUtils.STUDY_POP_FREQ_SEPARATOR); return new PopulationFrequencyVariantFilter.MafFreqFilter(split[0], split[1], popFreq.getOp(), popFreq.getValue()); @@ -185,8 +176,8 @@ private void addAnnotationFilters(Query query, List> filters) } - private void addClinicalFilters(Query query, List> filters) { - List> clinicalCombinations = VariantQueryParser.parseClinicalCombination(query) + private void addClinicalFilters(ParsedVariantQuery variantQuery, List> filters) { + List> clinicalCombinations = variantQuery.getClinicalCombinations() .stream().map(HashSet::new).collect(Collectors.toList()); if (clinicalCombinations.isEmpty()) { return; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjection.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjection.java index 717967cc038..a31d810fffc 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjection.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjection.java @@ -1,6 +1,7 @@ package org.opencb.opencga.storage.core.variant.query.projection; import com.google.common.collect.Iterables; +import org.opencb.commons.datastore.core.Event; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; @@ -21,6 +22,7 @@ public final class VariantQueryProjection { private final int numSamples; private final int numTotalSamples; + private List events = new ArrayList<>(); public VariantQueryProjection(StudyMetadata studyMetadata, List samples, List files) { this.fields = VariantField.getIncludeFields(null); @@ -77,14 +79,28 @@ public Map> getSamples() { return studies.values().stream().collect(Collectors.toMap(s -> s.studyMetadata.getId(), s -> s.samples)); } + public Map> getSampleNames() { + return studies.values().stream().collect(Collectors.toMap(s -> s.studyMetadata.getName(), s -> s.sampleNames)); + } + @Deprecated public Map> getFiles() { return studies.values().stream().collect(Collectors.toMap(s -> s.studyMetadata.getId(), s -> s.files)); } + public List getEvents() { + return events; + } + + public VariantQueryProjection setEvents(List events) { + this.events = events; + return this; + } + public static class StudyVariantQueryProjection { private StudyMetadata studyMetadata; private List samples = Collections.emptyList(); + private List sampleNames = Collections.emptyList(); private Map> multiFileSampleFiles = Collections.emptyMap(); private Set multiFileSamples = Collections.emptySet(); private List files = Collections.emptyList(); @@ -129,6 +145,15 @@ public StudyVariantQueryProjection setSamples(List samples) { return this; } + public List getSampleNames() { + return sampleNames; + } + + public StudyVariantQueryProjection setSampleNames(List sampleNames) { + this.sampleNames = sampleNames; + return this; + } + public Map> getMultiFileSampleFiles() { return multiFileSampleFiles; } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java index 371903a7626..bdcd501ad88 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java @@ -1,12 +1,14 @@ package org.opencb.opencga.storage.core.variant.query.projection; import org.apache.commons.collections4.CollectionUtils; +import org.opencb.commons.datastore.core.Event; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.CohortMetadata; import org.opencb.opencga.storage.core.metadata.models.FileMetadata; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; +import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; @@ -53,6 +55,7 @@ public static VariantQueryProjection parseVariantQueryFields( public VariantQueryProjection parseVariantQueryProjection(Query query, QueryOptions options) { Set includeFields = VariantField.getIncludeFields(options); + List events = new ArrayList<>(); List includeStudies = getIncludeStudies(query, options, metadataManager, includeFields); Map studies = new HashMap<>(includeStudies.size()); @@ -68,13 +71,20 @@ public VariantQueryProjection parseVariantQueryProjection(Query query, QueryOpti } Map> sampleIdsMap = getIncludeSampleIds(query, options, includeStudies, metadataManager); - for (VariantQueryProjection.StudyVariantQueryProjection study : studies.values()) { - study.setSamples(sampleIdsMap.get(study.getId())); - } int numTotalSamples = sampleIdsMap.values().stream().mapToInt(List::size).sum(); skipAndLimitSamples(query, sampleIdsMap); int numSamples = sampleIdsMap.values().stream().mapToInt(List::size).sum(); + for (VariantQueryProjection.StudyVariantQueryProjection study : studies.values()) { + List sampleIds = sampleIdsMap.get(study.getId()); + study.setSamples(sampleIds); + List sampleNames = new ArrayList<>(sampleIds.size()); + for (Integer sampleId : sampleIds) { + sampleNames.add(metadataManager.getSampleName(study.getId(), sampleId)); + } + study.setSampleNames(sampleNames); + } + Map> fileIdsMap = getIncludeFiles(query, includeStudies, includeFields, metadataManager, sampleIdsMap); for (VariantQueryProjection.StudyVariantQueryProjection study : studies.values()) { @@ -125,22 +135,35 @@ public VariantQueryProjection parseVariantQueryProjection(Query query, QueryOpti for (VariantQueryProjection.StudyVariantQueryProjection study : studies.values()) { int studyId = study.getId(); List cohorts = new LinkedList<>(); - for (CohortMetadata cohort : metadataManager.getCalculatedCohorts(studyId)) { + for (CohortMetadata cohort : metadataManager.getCalculatedOrPartialCohorts(studyId)) { cohorts.add(cohort.getId()); + TaskMetadata.Status status = cohort.getStatsStatus(); + if (status == TaskMetadata.Status.ERROR) { + String message = "Please note that the Cohort Stats for " + + "'" + study.getName() + ":" + cohort.getName() + "' are currently outdated."; + int numSampmles = cohort.getSamples().size(); + int invalidStatsNumSamples = cohort.getAttributes().getInt(CohortMetadata.INVALID_STATS_NUM_SAMPLES, -1); + if (invalidStatsNumSamples > 0) { + message += " The statistics have been calculated with " + invalidStatsNumSamples + " samples, " + + "while the total number of samples in the cohort is " + numSampmles + "."; + } + message += " To display updated statistics, please execute variant-stats-index."; + events.add(new Event(Event.Type.WARNING, message)); + } else if (status == TaskMetadata.Status.RUNNING) { + String message = "Please note that the Cohort Stats for " + + "'" + study.getName() + ":" + cohort.getName() + "' are currently being calculated."; + events.add(new Event(Event.Type.WARNING, message)); + } } -// metadataManager.cohortIterator(studyId).forEachRemaining(cohort -> { -// if (cohort.isReady()/* || cohort.isInvalid()*/) { -// cohorts.add(cohort.getId()); -// } -// }); study.setCohorts(cohorts); } } - return new VariantQueryProjection(includeFields, studies, numTotalSamples != numSamples, numSamples, numTotalSamples); + return new VariantQueryProjection(includeFields, studies, numTotalSamples != numSamples, numSamples, numTotalSamples) + .setEvents(events); } - public static void skipAndLimitSamples(Query query, Map> sampleIds) { + private void skipAndLimitSamples(Query query, Map> sampleIds) { if (VariantQueryUtils.isValidParam(query, VariantQueryParam.SAMPLE_SKIP)) { int skip = query.getInt(VariantQueryParam.SAMPLE_SKIP.key()); if (skip > 0) { @@ -483,6 +506,10 @@ public static boolean isIncludeSamplesDefined(Query query, Set fie return getIncludeSamplePartialStatus(query, fields) != null || getIncludeFilePartialStatus(query, fields) != null; } + /* + * @deprecated use VariantQueryProjection.getSampleNames() + */ + @Deprecated public static Map> getIncludeSampleNames(Query query, QueryOptions options, VariantStorageMetadataManager metadataManager) { if (VariantField.getIncludeFields(options).contains(VariantField.STUDIES)) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java index dc278d1e3db..200b2eb463d 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java @@ -1,7 +1,5 @@ package org.opencb.opencga.storage.core.variant.search; -import org.apache.commons.lang3.time.StopWatch; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; @@ -10,11 +8,10 @@ import org.opencb.opencga.storage.core.exceptions.VariantSearchException; import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; import org.opencb.opencga.storage.core.variant.search.solr.VariantSearchManager; import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeUnit; import static org.opencb.opencga.storage.core.variant.search.VariantSearchUtils.inferSpecificSearchIndexSamplesCollection; @@ -37,19 +34,9 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) throws Stor } @Override - public DataResult count(Query query) { - try { - StopWatch watch = StopWatch.createStarted(); - long count = searchManager.count(dbName, query); - int time = (int) watch.getTime(TimeUnit.MILLISECONDS); - return new DataResult<>(time, Collections.emptyList(), 1, Collections.singletonList(count), 1); - } catch (IOException | VariantSearchException e) { - throw new VariantQueryException("Error querying Solr", e); - } - } - - @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator) throws StorageEngineException { + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) throws StorageEngineException { + Query query = variantQuery.getQuery(); + QueryOptions options = variantQuery.getInputOptions(); String specificSearchIndexSamples = inferSpecificSearchIndexSamplesCollection(query, options, getMetadataManager(), dbName); if (specificSearchIndexSamples == null) { @@ -59,7 +46,7 @@ protected Object getOrIterator(Query query, QueryOptions options, boolean iterat if (iterator) { return searchManager.iterator(specificSearchIndexSamples, query, options); } else { - return searchManager.query(specificSearchIndexSamples, query, options); + return searchManager.query(specificSearchIndexSamples, variantQuery); } } catch (IOException | VariantSearchException e) { throw VariantQueryException.internalException(e); diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantAggregationExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantAggregationExecutor.java index cd466046999..eba762ab0af 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantAggregationExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantAggregationExecutor.java @@ -4,7 +4,7 @@ import org.opencb.commons.datastore.core.FacetField; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.executors.VariantAggregationExecutor; import org.opencb.opencga.storage.core.variant.search.solr.VariantSearchManager; import org.slf4j.Logger; diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java index 137c48cd163..12c86cc4e2b 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java @@ -8,7 +8,6 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.config.storage.StorageConfiguration; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.exceptions.VariantSearchException; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; @@ -16,6 +15,8 @@ import org.opencb.opencga.storage.core.variant.adaptors.VariantField; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.search.solr.SolrNativeIterator; import org.opencb.opencga.storage.core.variant.search.solr.VariantSearchManager; @@ -80,29 +81,16 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) throws Stor } @Override - public DataResult count(Query query) { - try { - StopWatch watch = StopWatch.createStarted(); - long count = searchManager.count(dbName, query); - int time = (int) watch.getTime(TimeUnit.MILLISECONDS); - return new DataResult<>(time, Collections.emptyList(), 1, Collections.singletonList(count), 1); - } catch (IOException | VariantSearchException e) { - throw new VariantQueryException("Error querying Solr", e); - } - } - - @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator) { - if (options == null) { - options = QueryOptions.empty(); - } + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) { + Query query = variantQuery.getQuery(); + QueryOptions options = variantQuery.getInputOptions(); if (doQuerySearchManager(query, options)) { try { if (iterator) { return searchManager.iterator(dbName, query, options); } else { - return searchManager.query(dbName, query, options); + return searchManager.query(dbName, variantQuery); } } catch (IOException | VariantSearchException e) { throw new VariantQueryException("Error querying Solr", e); @@ -134,7 +122,7 @@ protected Object getOrIterator(Query query, QueryOptions options, boolean iterat approxCount = false; } else if (options.getBoolean(APPROXIMATE_COUNT.key()) || options.getBoolean(QueryOptions.COUNT)) { options.put(QueryOptions.COUNT, false); - VariantQueryResult result = approximateCount(query, options); + VariantQueryResult result = approximateCount(variantQuery); numTotalResults = result.first(); approxCount = result.getApproximateCount(); approxCountSamplingSize = result.getApproximateCountSamplingSize(); @@ -176,7 +164,9 @@ protected Object getOrIterator(Query query, QueryOptions options, boolean iterat } } - public VariantQueryResult approximateCount(Query query, QueryOptions options) { + public VariantQueryResult approximateCount(ParsedVariantQuery variantQuery) { + Query query = variantQuery.getQuery(); + QueryOptions options = variantQuery.getInputOptions(); long count; boolean approxCount = true; int sampling = 0; @@ -193,7 +183,7 @@ public VariantQueryResult approximateCount(Query query, QueryOptions optio Query searchEngineQuery = getSearchEngineQuery(query); Query engineQuery = getEngineQuery(query, options, getMetadataManager()); - VariantQueryResult nativeResult = searchManager + DataResult nativeResult = searchManager .nativeQuery(dbName, searchEngineQuery, queryOptions); List variantIds = nativeResult.getResults().stream().map(VariantSearchModel::getId).collect(Collectors.toList()); // Adjust numSamples if the results from SearchManager is smaller than numSamples @@ -202,7 +192,7 @@ public VariantQueryResult approximateCount(Query query, QueryOptions optio approxCount = false; sampling = variantIds.size(); } - long numSearchResults = nativeResult.getNumTotalResults(); + long numSearchResults = nativeResult.getNumMatches(); long numResults; if (variantIds.isEmpty()) { @@ -223,8 +213,8 @@ public VariantQueryResult approximateCount(Query query, QueryOptions optio throw new VariantQueryException("Error querying Solr", e); } int time = (int) watch.getTime(TimeUnit.MILLISECONDS); - return new VariantQueryResult<>(time, 1, 1, Collections.emptyList(), Collections.singletonList(count), null, - SEARCH_ENGINE_ID + '+' + getStorageEngineId(), approxCount, approxCount ? sampling : null, null); + return new VariantQueryResult<>(time, 1, 1, Collections.emptyList(), Collections.singletonList(count), + SEARCH_ENGINE_ID + '+' + getStorageEngineId(), approxCount, approxCount ? sampling : null, null, variantQuery); } /** @@ -308,7 +298,7 @@ protected Iterator variantIdIteratorFromSearch(Query query, int limit, i try { // Do not iterate for small queries if (limit < 10000) { - VariantQueryResult nativeResult = searchManager.nativeQuery(dbName, query, queryOptions); + DataResult nativeResult = searchManager.nativeQuery(dbName, query, queryOptions); if (numTotalResults != null) { numTotalResults.set(nativeResult.getNumMatches()); } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java index a56c3e4c3f1..6f55a892f7c 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java @@ -34,6 +34,7 @@ import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.common.JacksonUtils; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; +import org.opencb.opencga.storage.core.variant.annotation.converters.VariantAnnotationModelUtils; import org.opencb.opencga.storage.core.variant.annotation.converters.VariantTraitAssociationToEvidenceEntryConverter; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.slf4j.Logger; @@ -59,6 +60,8 @@ public class VariantSearchToVariantConverter implements ComplexTypeConverter includeFields; + private final VariantAnnotationModelUtils variantAnnotationModelUtils = new VariantAnnotationModelUtils(); + public VariantSearchToVariantConverter() { this.includeFields = null; } @@ -666,19 +669,6 @@ public VariantSearchModel convertToStorageType(Variant variant) { variantSearchModel.setEnd(variant.getEnd()); variantSearchModel.setType(variant.getType().toString()); - // This field contains all possible IDs: id, dbSNP, names, genes, transcripts, protein, clinvar, hpo, ... - // This will help when searching by variant id. This is added at the end of the method after collecting all IDs - Set xrefs = new HashSet<>(); - xrefs.add(variantSearchModel.getId()); - xrefs.add(variantSearchModel.getVariantId()); - if (variant.getNames() != null && !variant.getNames().isEmpty()) { - variant.getNames().forEach(name -> { - if (name != null) { - xrefs.add(name); - } - }); - } - // convert Study related information convertStudies(variant, variantSearchModel, other); @@ -698,11 +688,6 @@ public VariantSearchModel convertToStorageType(Variant variant) { // Process Variant Annotation VariantAnnotation variantAnnotation = variant.getAnnotation(); if (variantAnnotation != null) { - - if (StringUtils.isNotEmpty(variantAnnotation.getId())) { - xrefs.add(variantAnnotation.getId()); - } - // This object will store all info and descriptions for full-text search Set traits = new HashSet<>(); @@ -721,27 +706,6 @@ public VariantSearchModel convertToStorageType(Variant variant) { } variantSearchModel.setRelease(release); - // Add cytoband names - if (variantAnnotation.getCytoband() != null) { - for (Cytoband cytoband : variantAnnotation.getCytoband()) { - xrefs.add(cytoband.getChromosome() + cytoband.getName()); - } - } - - // Add all XRefs coming from the variant annotation - if (variantAnnotation.getXrefs() != null && !variantAnnotation.getXrefs().isEmpty()) { - variantAnnotation.getXrefs().forEach(xref -> { - if (xref != null) { - xrefs.add(xref.getId()); - } - }); - } - - // Add all HGVS coming from the variant annotation - if (ListUtils.isNotEmpty(variantAnnotation.getHgvs())) { - xrefs.addAll(variantAnnotation.getHgvs()); - } - // Set Genes and Consequence Types List consequenceTypes = variantAnnotation.getConsequenceTypes(); if (consequenceTypes != null) { @@ -782,10 +746,6 @@ public VariantSearchModel convertToStorageType(Variant variant) { } } - xrefs.add(gene); - xrefs.add(conseqType.getGeneId()); - xrefs.add(conseqType.getTranscriptId()); - if (StringUtils.isNotEmpty(conseqType.getBiotype())) { biotypes.add(conseqType.getBiotype()); @@ -860,19 +820,16 @@ public VariantSearchModel convertToStorageType(Variant variant) { trans.append(FIELD_SEP); if (StringUtils.isNotEmpty(protVarAnnotation.getUniprotAccession())) { trans.append(protVarAnnotation.getUniprotAccession()); - xrefs.add(protVarAnnotation.getUniprotAccession()); } trans.append(FIELD_SEP); if (StringUtils.isNotEmpty(protVarAnnotation.getUniprotName())) { trans.append(protVarAnnotation.getUniprotName()); - xrefs.add(protVarAnnotation.getUniprotName()); } trans.append(FIELD_SEP); if (StringUtils.isNotEmpty(protVarAnnotation.getUniprotVariantId())) { trans.append(protVarAnnotation.getUniprotVariantId()); - xrefs.add(protVarAnnotation.getUniprotVariantId()); } trans.append(FIELD_SEP).append(protVarAnnotation.getPosition() == null @@ -913,8 +870,6 @@ public VariantSearchModel convertToStorageType(Variant variant) { if (protVarAnnotation.getFeatures() != null) { for (ProteinFeature proteinFeature : protVarAnnotation.getFeatures()) { if (StringUtils.isNotEmpty(proteinFeature.getId())) { - // We store them in xrefs and traits, the number of these IDs is very small - xrefs.add(proteinFeature.getId()); traits.add("PD" + FIELD_SEP + proteinFeature.getId() + FIELD_SEP + proteinFeature.getDescription()); } @@ -997,9 +952,6 @@ public VariantSearchModel convertToStorageType(Variant variant) { List clinical = VariantQueryUtils.buildClinicalCombinations(variantAnnotation); for (EvidenceEntry ev : variantAnnotation.getTraitAssociation()) { if (ev.getSource() != null && StringUtils.isNotEmpty(ev.getSource().getName())) { - if (StringUtils.isNotEmpty(ev.getId())) { - xrefs.add(ev.getId()); - } if ("clinvar".equalsIgnoreCase(ev.getSource().getName())) { String clinSigSuffix = ""; if (ev.getVariantClassification() != null @@ -1069,6 +1021,18 @@ public VariantSearchModel convertToStorageType(Variant variant) { variantSearchModel.setOther(other); } + // This field contains all possible IDs: id, dbSNP, names, genes, transcripts, protein, clinvar, hpo, ... + // This will help when searching by variant id. This is added at the end of the method after collecting all IDs + Set xrefs = variantAnnotationModelUtils.extractXRefs(variant.getAnnotation()); + xrefs.add(variantSearchModel.getId()); + xrefs.add(variantSearchModel.getVariantId()); + if (variant.getNames() != null && !variant.getNames().isEmpty()) { + variant.getNames().forEach(name -> { + if (name != null) { + xrefs.add(name); + } + }); + } variantSearchModel.setXrefs(new ArrayList<>(xrefs)); return variantSearchModel; } diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/VariantSearchManager.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/VariantSearchManager.java index 74ee51bbe3a..374f5f9ffd6 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/VariantSearchManager.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/VariantSearchManager.java @@ -42,7 +42,8 @@ import org.opencb.commons.utils.ListUtils; import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.storage.StorageConfiguration; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.VariantSearchException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.utils.CellBaseUtils; @@ -278,25 +279,24 @@ public int delete(String collection, VariantDBIterator variantDBIterator, Progre * according a given query. * * @param collection Collection name - * @param query Query - * @param queryOptions Query options + * @param variantQuery Parsed variant query * @return List of Variant objects * @throws VariantSearchException VariantSearchException * @throws IOException IOException */ - public VariantQueryResult query(String collection, Query query, QueryOptions queryOptions) + public VariantQueryResult query(String collection, ParsedVariantQuery variantQuery) throws VariantSearchException, IOException { - SolrQuery solrQuery = solrQueryParser.parse(query, queryOptions); + SolrQuery solrQuery = solrQueryParser.parse(variantQuery.getQuery(), variantQuery.getInputOptions()); SolrCollection solrCollection = solrManager.getCollection(collection); DataResult queryResult; try { queryResult = solrCollection.query(solrQuery, VariantSearchModel.class, - new VariantSearchToVariantConverter(VariantField.getIncludeFields(queryOptions))); + new VariantSearchToVariantConverter(VariantField.getIncludeFields(variantQuery.getInputOptions()))); } catch (SolrServerException e) { throw new VariantSearchException("Error executing variant query", e); } - return new VariantQueryResult<>(queryResult, null, SEARCH_ENGINE_ID); + return new VariantQueryResult<>(queryResult, SEARCH_ENGINE_ID, variantQuery); } /** @@ -310,7 +310,7 @@ public VariantQueryResult query(String collection, Query query, QueryOp * @throws VariantSearchException VariantSearchException * @throws IOException IOException */ - public VariantQueryResult nativeQuery(String collection, Query query, QueryOptions queryOptions) + public DataResult nativeQuery(String collection, Query query, QueryOptions queryOptions) throws VariantSearchException, IOException { SolrQuery solrQuery = solrQueryParser.parse(query, queryOptions); SolrCollection solrCollection = solrManager.getCollection(collection); @@ -321,7 +321,7 @@ public VariantQueryResult nativeQuery(String collection, Que throw new VariantSearchException("Error executing variant query (nativeQuery)", e); } - return new VariantQueryResult<>(queryResult, null, SEARCH_ENGINE_ID); + return queryResult; } /** diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/DefaultVariantStatisticsManager.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/DefaultVariantStatisticsManager.java index c85c63b1505..db6ead71f58 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/DefaultVariantStatisticsManager.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/DefaultVariantStatisticsManager.java @@ -474,6 +474,7 @@ protected VariantStatsDBWriter newVariantStatisticsDBWriter(VariantDBAdaptor dbA // // } + @Deprecated void checkAndUpdateCalculatedCohorts(StudyMetadata studyMetadata, URI uri, boolean updateStats) throws IOException, StorageEngineException { Set cohortNames = readCohortsFromStatsFile(uri); diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManager.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManager.java index fe6765529fa..d4fdc948622 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManager.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManager.java @@ -112,6 +112,7 @@ public void postCalculateStats( } } + @Deprecated public static void checkAndUpdateCalculatedCohorts( VariantStorageMetadataManager metadataManager, StudyMetadata studyMetadata, Collection cohorts, boolean updateStats) throws StorageEngineException { diff --git a/opencga-storage/opencga-storage-core/src/main/resources/storage-configuration.yml b/opencga-storage/opencga-storage-core/src/main/resources/storage-configuration.yml index 226fc106783..4f64a3ad939 100644 --- a/opencga-storage/opencga-storage-core/src/main/resources/storage-configuration.yml +++ b/opencga-storage/opencga-storage-core/src/main/resources/storage-configuration.yml @@ -18,10 +18,8 @@ cellbase: server: rest: port: ${OPENCGA.SERVER.REST.PORT} - logFile: null grpc: port: ${OPENCGA.SERVER.GRPC.PORT} - logFile: null ## Solr Search Configuration search: diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java index 598e1fae586..08dabda7562 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java @@ -1,6 +1,7 @@ package org.opencb.opencga.storage.core.variant; import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -17,6 +18,7 @@ import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.io.VariantWriterFactory; @@ -75,13 +77,13 @@ public void checkAllAnnotated() throws Exception { @Test public void getPairs() throws Exception { - getPairs(new Query()); - getPairs(new Query(VariantQueryParam.REGION.key(), "2")); - getPairs(new Query(VariantQueryParam.REGION.key(), "17")); - getPairs(new Query(VariantQueryParam.REGION.key(), "2,13,5")); - getPairs(new Query(VariantQueryParam.REGION.key(), "2").append(VariantQueryParam.GENE.key(), "VPS53")); - getPairs(new Query(VariantQueryParam.GENE.key(), "VPS53")); - getPairs(new Query(VariantQueryParam.GENE.key(), "BRCA2")); + getPairs(new VariantQuery()); + getPairs(new VariantQuery().region("2")); + getPairs(new VariantQuery().region("17")); + getPairs(new VariantQuery().region("2", "13", "5")); + getPairs(new VariantQuery().region("2").gene("LINC00423")); + getPairs(new VariantQuery().gene("LINC00423")); + getPairs(new VariantQuery().gene("ENSG00000263015")); } public void getPairs(Query inputQuery) throws Exception { @@ -115,8 +117,8 @@ public void getPairs(Query inputQuery) throws Exception { } assertNull(prevMateid); assertNull(prevId); - assertThat(duplicatedVariants, CoreMatchers.not(CoreMatchers.hasItem(CoreMatchers.anything()))); - + MatcherAssert.assertThat(duplicatedVariants, CoreMatchers.not(CoreMatchers.hasItem(CoreMatchers.anything()))); + assertNotEquals(0, variants.size()); // Check pagination testPagination(variantsList, query, 1); testPagination(variantsList, query, 2); @@ -130,17 +132,19 @@ private void testPagination(List variantsList, Query query, int batchSiz List actualVariantsGet = new ArrayList<>(variantsList.size()); List actualVariantsIterator = new ArrayList<>(variantsList.size()); for (int i = 0; i < variantsList.size(); i += batchSize) { + System.out.println(" --- limit = " + batchSize + " skip = " + i); QueryOptions options = new QueryOptions(QueryOptions.LIMIT, batchSize) .append(QueryOptions.SKIP, i); - List results = variantStorageEngine.get(query, options).getResults(); - System.out.println("get = " + options.toJson() + " -> " + results.size() + " " + results); + List results; + results = variantStorageEngine.get(query, options).getResults(); + System.out.println("get = " + options.toJson() + " -> " + results.size() + " result " + results.stream().map(Variant::toString).collect(Collectors.joining("\", \"", "[ \"", "\" ]"))); results.stream().map(Variant::toString).forEach(actualVariantsGet::add); assertTrue(results.size() <= batchSize); results = new ArrayList<>(batchSize); variantStorageEngine.iterator(query, options).forEachRemaining(results::add); - System.out.println("it = " + options.toJson() + " -> " + results.size() + " " + results); - results.stream().map(Variant::toString).forEach(actualVariantsGet::add); + System.out.println("it = " + options.toJson() + " -> " + results.size() + " result " + results.stream().map(Variant::toString).collect(Collectors.joining("\", \"", "[ \"", "\" ]"))); + results.stream().map(Variant::toString).forEach(actualVariantsIterator::add); assertTrue(results.size() <= batchSize); } diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java index fe7cebbd6a4..64fc14c4c6a 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java @@ -16,6 +16,7 @@ import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.io.VariantWriterFactory; @@ -39,11 +40,14 @@ public abstract class VariantStorageEngineSVTest extends VariantStorageBaseTest { protected static StudyMetadata studyMetadata; + protected static StudyMetadata studyMetadata2; protected static boolean loaded = false; protected static StoragePipelineResult pipelineResult1; protected static StoragePipelineResult pipelineResult2; + protected static StoragePipelineResult pipelineResult3; protected static URI input1; protected static URI input2; + protected static URI input3; @Before public void before() throws Exception { @@ -66,12 +70,22 @@ protected void loadFiles() throws Exception { variantStorageEngine.getOptions().append(VariantStorageOptions.ANNOTATOR_CELLBASE_EXCLUDE.key(), "expression,clinical"); pipelineResult1 = runDefaultETL(input1, variantStorageEngine, studyMetadata, new QueryOptions() .append(VariantStorageOptions.ANNOTATE.key(), true) + .append(VariantStorageOptions.STATS_CALCULATE.key(), true) .append(VariantStorageOptions.ASSEMBLY.key(), "grch38") ); input2 = getResourceUri("variant-test-sv_2.vcf"); pipelineResult2 = runDefaultETL(input2, variantStorageEngine, studyMetadata, new QueryOptions() .append(VariantStorageOptions.ANNOTATE.key(), true) + .append(VariantStorageOptions.STATS_CALCULATE.key(), true) .append(VariantStorageOptions.ASSEMBLY.key(), "grch38")); + + input3 = getResourceUri("variant-test-sv-large.vcf"); + studyMetadata2 = new StudyMetadata(2, "s2"); + pipelineResult3 = runDefaultETL(input3, variantStorageEngine, studyMetadata2, new QueryOptions() + .append(VariantStorageOptions.ANNOTATE.key(), true) + .append(VariantStorageOptions.STATS_CALCULATE.key(), true) + .append(VariantStorageOptions.ASSEMBLY.key(), "grch38")); + } @Test @@ -88,7 +102,7 @@ public void checkCount() throws Exception { + 1 + 1 // negative cipos ; - int count = variantStorageEngine.getDBAdaptor().count().first().intValue(); + int count = variantStorageEngine.count(new VariantQuery().study(studyMetadata.getName())).first().intValue(); assertEquals(expected, count); } @@ -146,7 +160,10 @@ protected void checkCorrectness(URI file) throws StorageEngineException, NonStan @Test public void exportVcf() throws Exception { - variantStorageEngine.exportData(null, VariantWriterFactory.VariantOutputFormat.VCF, null, new Query(VariantQueryParam.UNKNOWN_GENOTYPE.key(), "./."), new QueryOptions(QueryOptions.SORT, true)); + variantStorageEngine.exportData(null, VariantWriterFactory.VariantOutputFormat.VCF, null, + new VariantQuery().unknownGenotype("./.").study(studyMetadata.getName()), new QueryOptions(QueryOptions.SORT, true)); + variantStorageEngine.exportData(null, VariantWriterFactory.VariantOutputFormat.VCF, null, + new VariantQuery().unknownGenotype("./.").study(studyMetadata2.getName()), new QueryOptions(QueryOptions.SORT, true)); } protected Map readVariants(URI input) throws StorageEngineException, NonStandardCompliantSampleField { diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageSearchIntersectTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageSearchIntersectTest.java index 5e29cd2b127..b18e535454b 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageSearchIntersectTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageSearchIntersectTest.java @@ -32,7 +32,7 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.api.ParamConstants; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; @@ -293,7 +293,7 @@ public void testCount() throws Exception { .append(ANNOT_CONSERVATION.key(), "gerp>0.1"); long realCount = dbAdaptor.count(query).first(); VariantQueryResult result = variantQueryExecutor - .get(query, new QueryOptions(COUNT, true).append(LIMIT, 0)); + .get(variantStorageEngine.parseQuery(query, new QueryOptions(COUNT, true).append(LIMIT, 0))); assertEquals(0, result.getResults().size()); assertEquals(realCount, result.getNumMatches()); } @@ -304,7 +304,7 @@ public void testApproxCount() throws Exception { .append(ANNOT_CONSERVATION.key(), "gerp>0.1"); long realCount = dbAdaptor.count(query).first(); VariantQueryResult result = variantQueryExecutor - .approximateCount(query, new QueryOptions(VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), realCount * 0.1)); + .approximateCount(variantStorageEngine.getVariantQueryParser().parseQuery(query, new QueryOptions(VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), realCount * 0.1))); long approxCount = result.first(); System.out.println("approxCount = " + approxCount); System.out.println("realCount = " + realCount); @@ -319,7 +319,7 @@ public void testExactApproxCount() throws Exception { .append(ANNOT_CONSERVATION.key(), "gerp>0.1"); long realCount = dbAdaptor.count(query).first(); VariantQueryResult result = variantQueryExecutor - .approximateCount(query, new QueryOptions(VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), allVariants.getNumResults())); + .approximateCount(variantStorageEngine.getVariantQueryParser().parseQuery(query, new QueryOptions(VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), allVariants.getNumResults()))); long approxCount = result.first(); System.out.println("approxCount = " + approxCount); System.out.println("realCount = " + realCount); @@ -333,7 +333,7 @@ public void testExactApproxCountToSearch() throws Exception { Query query = new Query(ANNOT_CONSERVATION.key(), "gerp>0.1"); long realCount = dbAdaptor.count(query).first(); VariantQueryResult result = variantQueryExecutor - .approximateCount(query, new QueryOptions(VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), allVariants.getNumResults())); + .approximateCount(variantStorageEngine.getVariantQueryParser().parseQuery(query, new QueryOptions(VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(), allVariants.getNumResults()))); long approxCount = result.first(); System.out.println("approxCount = " + approxCount); System.out.println("realCount = " + realCount); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileSpecificSamplesCollectionTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileSpecificSamplesCollectionTest.java index 4c267a35d15..58cc058dc4b 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileSpecificSamplesCollectionTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileSpecificSamplesCollectionTest.java @@ -5,7 +5,8 @@ import org.opencb.biodata.models.variant.Variant; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.exceptions.VariantSearchException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; @@ -55,19 +56,19 @@ protected void load() throws Exception { protected VariantQueryResult query(Query query, QueryOptions options) { try { - query = variantStorageEngine.preProcessQuery(query, options); + ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options); VariantStorageMetadataManager scm = dbAdaptor.getMetadataManager(); - String collection = VariantSearchUtils.inferSpecificSearchIndexSamplesCollection(query, options, scm, DB_NAME); + String collection = VariantSearchUtils.inferSpecificSearchIndexSamplesCollection(variantQuery.getQuery(), options, scm, DB_NAME); // Do not execute this test if the query is not covered by the specific search index collection - Assume.assumeThat(query.toJson(), collection, CoreMatchers.notNullValue()); + Assume.assumeThat(variantQuery.getQuery().toJson(), collection, CoreMatchers.notNullValue()); if (options.getInt(QueryOptions.LIMIT, 0) <= 0) { options = new QueryOptions(options); options.put(QueryOptions.LIMIT, 100000); } - return variantStorageEngine.getVariantSearchManager().query(collection, query, options); + return variantStorageEngine.getVariantSearchManager().query(collection, variantQuery); } catch (StorageEngineException | VariantSearchException | IOException e) { e.printStackTrace(); Assert.fail(e.getMessage()); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java index 6291344e0e3..905f64a5373 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java @@ -10,7 +10,8 @@ import org.opencb.biodata.models.variant.avro.SampleEntry; import org.opencb.biodata.models.variant.stats.VariantStats; import org.opencb.commons.datastore.core.*; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.FileMetadata; @@ -111,9 +112,8 @@ protected void load() throws Exception { } protected VariantQueryResult query(Query query, QueryOptions options) { - options = options == null ? QueryOptions.empty() : options; - query = variantStorageEngine.preProcessQuery(query, options); - return dbAdaptor.get(query, options); + ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options); + return dbAdaptor.get(variantQuery); } protected ObjectMap getOptions() { @@ -378,13 +378,13 @@ public void testSampleLimitSkip() throws Exception { System.out.println("samples(ALL) = " + result.getSamples()); for (int i : new int[]{1, 3, 6, 8, 10}) { - result = query(new Query(VariantQueryParam.SAMPLE_SKIP.key(), i).append(VariantQueryParam.INCLUDE_SAMPLE.key(), ALL).append(SAMPLE_METADATA.key(), true), options); + result = query(new VariantQuery().sampleSkip(i).includeSampleAll().sampleMetadata(true), options); // System.out.println("samples(SKIP=" + i + ") = " + result.getSamples()); assertEquals(Math.max(0, 8 - i), result.getSamples().values().stream().mapToInt(List::size).sum()); assertEquals(Math.max(0, 8 - i), result.getNumSamples().intValue()); assertEquals(8, result.getNumTotalSamples().intValue()); - result = query(new Query(VariantQueryParam.SAMPLE_LIMIT.key(), i).append(VariantQueryParam.INCLUDE_SAMPLE.key(), ALL).append(SAMPLE_METADATA.key(), true), options); + result = query(new VariantQuery().sampleLimit(i).includeSampleAll().sampleMetadata(true), options); // System.out.println("samples(LIMIT=" + i + ") = " + result.getSamples()); assertEquals(Math.min(8, i), result.getSamples().values().stream().mapToInt(List::size).sum()); assertEquals(Math.min(8, i), result.getNumSamples().intValue()); @@ -1329,6 +1329,16 @@ public void testSampleData() throws Exception { assertEquals(0, variant.getStudies().get(0).getFiles().size()); } + @Test + public void testSampleDataUnnormalized() throws Exception { + // Check unnormalized queries + Variant variant = variantStorageEngine.getSampleData("1:10352:T:TA", study1, new QueryOptions()).first(); + assertEquals("1:10353:-:A", variant.toString()); + System.out.println("variant = " + variant.toJson()); + assertNotNull(variant.getStudies().get(0).getStats(DEFAULT_COHORT)); + assertEquals(4, variant.getStudies().get(0).getSamples().size()); + assertEquals(4, variant.getStudies().get(0).getFiles().size()); + } @Test public void testCount() throws StorageEngineException { diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorTest.java index 1f305574a64..984cc5bfb83 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorTest.java @@ -30,14 +30,11 @@ import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.VariantFileMetadata; import org.opencb.biodata.models.variant.avro.*; -import org.opencb.biodata.models.variant.exceptions.NonStandardCompliantSampleField; import org.opencb.biodata.models.variant.stats.VariantStats; -import org.opencb.biodata.tools.variant.VariantNormalizer; import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; @@ -47,6 +44,8 @@ import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.annotation.annotators.CellBaseRestVariantAnnotator; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.query.executors.NoOpVariantQueryExecutor; import org.opencb.opencga.storage.core.variant.query.filters.VariantFilterBuilder; @@ -268,8 +267,8 @@ public void multiIterator() throws Exception { VariantDBIterator iterator = dbAdaptor.iterator(variantsToQuery.iterator(), new Query(), new QueryOptions()); - DataResult queryResult = iterator.toDataResult(); - assertEquals(variantsToQuery.size(), queryResult.getResults().size()); + List variants = iterator.toList(); + assertEquals(variantsToQuery.size(), variants.size()); } @Test @@ -531,11 +530,14 @@ public long filterPopulation(DataResult queryResult, Predicate public void testGetAllVariants_variantId() { int i = 0; List variants = new ArrayList<>(); + Map normalizedVariants = new HashMap<>(); for (Variant variant : allVariants.getResults()) { - if (i++ % 10 == 0) { - if (!variant.isSymbolic()) { - variants.add(variant); - } + if ((i++ % 10) == 0) { + variants.add(variant); + } + OriginalCall call = variant.getStudies().get(0).getFiles().get(0).getCall(); + if (call != null) { + normalizedVariants.put(variant.toString(), call.getVariantId()); } } List result = query(new Query(ID.key(), variants), new QueryOptions()).getResults(); @@ -554,32 +556,61 @@ public void testGetAllVariants_variantId() { } } assertEquals(expectedList, actualList); + + normalizedVariants.forEach((key, value) -> { + System.out.println(key + " = " + value); + }); + List resultNormalized = query(new Query(ID.key(), normalizedVariants.values()).append(INCLUDE_FILE.key(), ALL), new QueryOptions()).getResults(); + assertEquals(normalizedVariants.size(), resultNormalized.size()); + assertTrue(!resultNormalized.isEmpty()); + for (Variant variant : resultNormalized) { + String expected = normalizedVariants.get(variant.toString()); + String actual = variant.getStudies().get(0).getFiles().get(0).getCall().getVariantId(); + assertEquals(expected, actual); + } } @Test public void testGetAllVariants_xref() { - Query query = new Query(ANNOT_XREF.key(), "3:108634973:C:A,rs2032582,HP:0001250,VAR_048225,Q9BY64,ENSG00000250026,TMPRSS11B,COSM1421316"); - queryResult = query(query, null); - assertThat(queryResult, everyResult(allVariantsSummary, anyOf( - hasAnnotation(at("3:108634973:C:A")), - with("id", Variant::getId, is("rs2032582")), - hasAnnotation(with("GeneTraitAssociation", VariantAnnotation::getGeneTraitAssociation, - hasItem(with("HPO", GeneTraitAssociation::getHpo, is("HP:0001250"))))), - hasAnnotation(with("ConsequenceType", VariantAnnotation::getConsequenceTypes, - hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, - with("UniprotVariantId", ProteinVariantAnnotation::getUniprotVariantId, is("VAR_048225")))))), - hasAnnotation(with("ConsequenceType", VariantAnnotation::getConsequenceTypes, - hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, - with("UniprotName", ProteinVariantAnnotation::getUniprotAccession, is("Q9BY64")))))), - hasAnnotation(with("ConsequenceType", VariantAnnotation::getConsequenceTypes, - hasItem(with("EnsemblGene", ConsequenceType::getGeneId, is("ENSG00000250026"))))), - hasAnnotation(with("ConsequenceType", VariantAnnotation::getConsequenceTypes, - hasItem(with("GeneName", ConsequenceType::getGeneName, is("TMPRSS11B"))))) -// hasAnnotation(with("VariantTraitAssociation", VariantAnnotation::getVariantTraitAssociation, -// with("Cosmic", VariantTraitAssociation::getCosmic, -// hasItem(with("MutationId", Cosmic::getMutationId, is("COSM1421316")))))) - ))); + Map> matchers = new HashMap<>(); + matchers.put("3:108634973:C:A", hasAnnotation(at("3:108634973:C:A"))); + matchers.put("rs2032582", with("id", Variant::getId, is("rs2032582"))); + matchers.put("HP:0001250", hasAnnotation(with("GeneTraitAssociation", VariantAnnotation::getGeneTraitAssociation, + hasItem(with("HPO", GeneTraitAssociation::getHpo, is("HP:0001250")))))); + matchers.put("VAR_048225", hasAnnotation(with("ConsequenceType", VariantAnnotation::getConsequenceTypes, + hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, + with("UniprotVariantId", ProteinVariantAnnotation::getUniprotVariantId, is("VAR_048225"))))))); + matchers.put("Q9BY64", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, hasItem( + with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, + with("UniprotAccession", ProteinVariantAnnotation::getUniprotAccession, + is("Q9BY64"))))))); + matchers.put("ENSG00000250026", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, hasItem( + with("GeneId", ConsequenceType::getGeneId, + is("ENSG00000250026")))))); + matchers.put("TMPRSS11B", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, hasItem( + with("GeneName", ConsequenceType::getGeneName, + is("TMPRSS11B")))))); + matchers.put("COSM1421316", hasAnnotation(with("TraitAssociation", VariantAnnotation::getTraitAssociation, hasItem( + with("Cosmic", EvidenceEntry::getId, is("COSM1421316")))))); + + // Query one by one + for (Map.Entry> entry : matchers.entrySet()) { + Query query = new Query(ANNOT_XREF.key(), entry.getKey()); + queryResult = query(query, null); + assertThat(entry.getKey(), queryResult, everyResult(allVariantsSummary, allOf(entry.getValue()))); + query = new Query(ID.key(), entry.getKey()); + queryResult = query(query, null); + assertThat(entry.getKey(), queryResult, everyResult(allVariantsSummary, allOf(entry.getValue()))); + } + + // Query by all xrefs + Query query = new Query(ANNOT_XREF.key(), matchers.keySet()); + queryResult = query(query, null); + assertThat(queryResult, everyResult(allVariantsSummary, anyOf(matchers.values()))); } @Test @@ -2420,7 +2451,8 @@ private void testQuery(Query query, QueryOptions options) { } private Matcher withFilter(Query query) { - return VariantMatchers.withFilter(new VariantFilterBuilder(metadataManager).buildFilter(query, null), query.toJson()); + ParsedVariantQuery parsedVariantQuery = variantStorageEngine.parseQuery(query, new QueryOptions()); + return VariantMatchers.withFilter(new VariantFilterBuilder().buildFilter(parsedVariantQuery), query.toJson()); } /* @Test diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantMatchers.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantMatchers.java index 7f790ffe609..11c9a6f8927 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantMatchers.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantMatchers.java @@ -139,6 +139,20 @@ public static Matcher overlaps(Variant variant) { return overlaps(new Region(variant.getChromosome(), variant.getStart(), variant.getEnd()), true); } + public static Matcher samePosition(Variant variant) { + return new TypeSafeDiagnosingMatcher() { + @Override + protected boolean matchesSafely(Variant item, Description mismatchDescription) { + return variant.sameGenomicVariant(item); + } + + @Override + public void describeTo(Description description) { + description.appendText("has same genomic region " + variant); + } + }; + } + public static Matcher overlaps(Region region) { return overlaps(region, true); } diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryTest.java index de17c52a8a0..5cfdde4ca13 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryTest.java @@ -28,6 +28,12 @@ public void test() throws Exception { if (param.type() == QueryParam.Type.BOOLEAN || param.type() == QueryParam.Type.BOOLEAN_ARRAY) { methodSet = getMethodSafe(param.key(), boolean.class); expectedValue = true; + } else if (param.type() == QueryParam.Type.INTEGER || param.type() == QueryParam.Type.INTEGER_ARRAY) { + methodSet = getMethodSafe(param.key(), int.class); + expectedValue = 42; + if (methodSet == null) { + methodSet = getMethodSafe(param.key(), Integer.class); + } } else { expectedValue = RandomStringUtils.random(10); methodSet = getMethodSafe(param.key(), String.class); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryUsingSearchIndexTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryUsingSearchIndexTest.java index 395e41d2675..6194d25c6d8 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryUsingSearchIndexTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantQueryUsingSearchIndexTest.java @@ -6,7 +6,7 @@ import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.search.solr.VariantSearchManager; diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/DummyTestAnnotator.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/DummyTestAnnotator.java index b36e8fa90ad..8491d03b41e 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/DummyTestAnnotator.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/DummyTestAnnotator.java @@ -4,7 +4,9 @@ import org.opencb.biodata.models.variant.avro.AdditionalAttribute; import org.opencb.biodata.models.variant.avro.ConsequenceType; import org.opencb.biodata.models.variant.avro.VariantAnnotation; +import org.opencb.biodata.models.variant.avro.Xref; import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.commons.utils.CryptoUtils; import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.storage.core.metadata.models.ProjectMetadata; import org.opencb.opencga.storage.core.variant.annotation.annotators.VariantAnnotator; @@ -29,6 +31,10 @@ public DummyTestAnnotator(StorageConfiguration configuration, ProjectMetadata pr fail = options.getBoolean(FAIL, false); } + static String getRs(Variant variant) { + return "rs" + variant.toString().hashCode(); + } + @Override public List annotate(List variants) throws VariantAnnotatorException { if (fail) { @@ -48,6 +54,7 @@ public List annotate(List variants) throws VariantAn ct.setExonOverlap(Collections.emptyList()); ct.setTranscriptFlags(Collections.emptyList()); a.setConsequenceTypes(Collections.singletonList(ct)); + a.setXrefs(Collections.singletonList(new Xref(getRs(v), "dbSNP"))); a.setAdditionalAttributes( Collections.singletonMap(GROUP_NAME.key(), new AdditionalAttribute(Collections.singletonMap(VARIANT_ID.key(), v.toString())))); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/VariantAnnotationManagerTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/VariantAnnotationManagerTest.java index dc2721882fa..00544a3d715 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/VariantAnnotationManagerTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/VariantAnnotationManagerTest.java @@ -4,6 +4,7 @@ import org.apache.commons.lang.StringUtils; import org.junit.Assume; import org.junit.Test; +import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.VariantAnnotation; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; @@ -262,6 +263,10 @@ public void testQueries(VariantStorageEngine variantStorageEngine) throws Storag assertThat(annotation.getConsequenceTypes(), VariantMatchers.isEmpty()); } + for (Variant variant : variantStorageEngine) { + Variant thisVariant = variantStorageEngine.getVariant(DummyTestAnnotator.getRs(variant)); + assertThat(thisVariant, VariantMatchers.samePosition(variant)); + } // Get annotations from a deleted snapshot thrown.expectMessage("Variant Annotation snapshot \"v1\" not found!"); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/converters/VariantAnnotationModelUtilsTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/converters/VariantAnnotationModelUtilsTest.java new file mode 100644 index 00000000000..f1167213be9 --- /dev/null +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/converters/VariantAnnotationModelUtilsTest.java @@ -0,0 +1,46 @@ +package org.opencb.opencga.storage.core.variant.annotation.converters; + +import junit.framework.TestCase; +import org.junit.experimental.categories.Category; +import org.opencb.biodata.models.variant.avro.ConsequenceType; +import org.opencb.biodata.models.variant.avro.VariantAnnotation; +import org.opencb.biodata.models.variant.avro.Xref; +import org.opencb.opencga.core.testclassification.duration.ShortTests; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +@Category(ShortTests.class) +public class VariantAnnotationModelUtilsTest extends TestCase { + + public void testXrefsHgvs() throws Exception { + VariantAnnotation variantAnnotation = new VariantAnnotation(); + variantAnnotation.setId("id"); + variantAnnotation.setXrefs(Collections.singletonList(new Xref("xref1", "source"))); + variantAnnotation.setHgvs(Arrays.asList( + "ENST00000680783.1(ENSG00000135744):c.776T>C", + "ENSP00000451720.1:p.Asn134Lys" + )); + ConsequenceType ct = new ConsequenceType(); + ct.setGeneName("GENE"); + ct.setHgvs(variantAnnotation.getHgvs()); + ct.setGeneId(null); + variantAnnotation.setConsequenceTypes(Arrays.asList(ct, new ConsequenceType())); + Set xrefs = new VariantAnnotationModelUtils().extractXRefs(variantAnnotation); + + assertEquals(7, xrefs.size()); + // Default fields + assertTrue(xrefs.contains("id")); + assertTrue(xrefs.contains("xref1")); + assertTrue(xrefs.contains("GENE")); + + // Untouched hgvs, not starting with ENST or NM_ + assertTrue(xrefs.contains("ENSP00000451720.1:p.Asn134Lys")); + + assertTrue(xrefs.contains("ENST00000680783.1(ENSG00000135744):c.776T>C")); + assertTrue(xrefs.contains("ENST00000680783.1:c.776T>C")); + assertTrue(xrefs.contains("GENE:c.776T>C")); + + } +} \ No newline at end of file diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyStudyMetadataDBAdaptor.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyStudyMetadataDBAdaptor.java index f954026b234..3d519030b19 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyStudyMetadataDBAdaptor.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyStudyMetadataDBAdaptor.java @@ -222,7 +222,7 @@ public Iterator taskIterator(int studyId, List get(ParsedVariantQuery query, QueryOptions options) { + public VariantQueryResult get(ParsedVariantQuery query) { List variants = new ArrayList<>(); - iterator(query, options).forEachRemaining(variants::add); + iterator(query).forEachRemaining(variants::add); - return new VariantQueryResult<>(0, variants.size(), variants.size(), Collections.emptyList(), variants, null, - DummyVariantStorageEngine.STORAGE_ENGINE_ID); + return new VariantQueryResult<>(0, variants.size(), variants.size(), Collections.emptyList(), variants, + DummyVariantStorageEngine.STORAGE_ENGINE_ID, query); } @Override @@ -102,7 +102,8 @@ public DataResult distinct(Query query, String field) { } @Override - public VariantDBIterator iterator(ParsedVariantQuery variantQuery, QueryOptions options) { + public VariantDBIterator iterator(ParsedVariantQuery variantQuery) { + QueryOptions options = variantQuery.getInputOptions(); logger.info("Query " + variantQuery.getQuery().toJson()); logger.info("QueryOptions " + options.toJson()); logger.info("dbName " + dbName); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParserTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParserTest.java index 8fae5f12310..2611cfc337a 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParserTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParserTest.java @@ -101,10 +101,17 @@ public static Set set(T... ts) { @Test public void testParseClinicalCombinations() { - assertEquals(Arrays.asList("cosmic"), VariantQueryParser.parseClinicalCombinationsList(new Query(ANNOT_CLINICAL.key(), "cosmic"))); - assertEquals(Arrays.asList("clinvar", "cosmic"), VariantQueryParser.parseClinicalCombinationsList(new Query(ANNOT_CLINICAL.key(), "clinvar,cosmic"))); - assertEquals(Arrays.asList("cosmic_pathogenic"), VariantQueryParser.parseClinicalCombinationsList(new Query(ANNOT_CLINICAL.key(), "cosmic").append(ANNOT_CLINICAL_SIGNIFICANCE.key(), "pathogenic"))); - assertEquals(Arrays.asList("cosmic_confirmed"), VariantQueryParser.parseClinicalCombinationsList(new Query(ANNOT_CLINICAL.key(), "cosmic").append(ANNOT_CLINICAL_CONFIRMED_STATUS.key(), true))); - assertEquals(Arrays.asList("clinvar_pathogenic_confirmed", "clinvar_likely_pathogenic_confirmed", "cosmic_pathogenic_confirmed", "cosmic_likely_pathogenic_confirmed"), VariantQueryParser.parseClinicalCombinationsList(new Query(ANNOT_CLINICAL.key(), "clinvar,cosmic").append(ANNOT_CLINICAL_SIGNIFICANCE.key(), "pathogenic,likely_pathogenic").append(ANNOT_CLINICAL_CONFIRMED_STATUS.key(), true))); + assertEquals(Arrays.asList("cosmic"), variantQueryParser.parseQuery( + new Query(ANNOT_CLINICAL.key(), "cosmic"), new QueryOptions()).getClinicalCombinationsList()); + assertEquals(Arrays.asList("clinvar", "cosmic"), variantQueryParser.parseQuery( + new Query(ANNOT_CLINICAL.key(), "clinvar,cosmic"), new QueryOptions()).getClinicalCombinationsList()); + assertEquals(Arrays.asList("cosmic_pathogenic"), variantQueryParser.parseQuery( + new Query(ANNOT_CLINICAL.key(), "cosmic").append(ANNOT_CLINICAL_SIGNIFICANCE.key(), "pathogenic"), new QueryOptions()).getClinicalCombinationsList()); + assertEquals(Arrays.asList("cosmic_confirmed"), variantQueryParser.parseQuery( + new Query(ANNOT_CLINICAL.key(), "cosmic").append(ANNOT_CLINICAL_CONFIRMED_STATUS.key(), true), new QueryOptions()).getClinicalCombinationsList()); + assertEquals(Arrays.asList("clinvar_pathogenic_confirmed", "clinvar_likely_pathogenic_confirmed", "cosmic_pathogenic_confirmed", "cosmic_likely_pathogenic_confirmed"), variantQueryParser.parseQuery(new Query() + .append(ANNOT_CLINICAL.key(), "clinvar,cosmic") + .append(ANNOT_CLINICAL_SIGNIFICANCE.key(), "pathogenic,likely_pathogenic") + .append(ANNOT_CLINICAL_CONFIRMED_STATUS.key(), true), new QueryOptions()).getClinicalCombinationsList()); } } \ No newline at end of file diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java new file mode 100644 index 00000000000..778257d5999 --- /dev/null +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java @@ -0,0 +1,272 @@ +package org.opencb.opencga.storage.core.variant.query.executors; + +import org.hamcrest.Matcher; +import org.junit.*; +import org.opencb.biodata.models.variant.Variant; +import org.opencb.biodata.models.variant.VariantFileMetadata; +import org.opencb.biodata.models.variant.avro.*; +import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.commons.datastore.core.Query; +import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.storage.core.StoragePipelineResult; +import org.opencb.opencga.storage.core.exceptions.StorageEngineException; +import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; +import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; +import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest; +import org.opencb.opencga.storage.core.variant.VariantStorageOptions; +import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; +import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; +import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; +import org.opencb.opencga.storage.core.variant.solr.VariantSolrExternalResource; +import org.opencb.opencga.storage.core.variant.stats.DefaultVariantStatisticsManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Paths; +import java.util.*; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.opencb.opencga.storage.core.variant.adaptors.VariantMatchers.*; + +@Ignore +public abstract class VariantQueryExecutorTest extends VariantStorageBaseTest { + + private static Logger logger = LoggerFactory.getLogger(VariantQueryExecutorTest.class); + + protected boolean fileIndexed; + private StudyMetadata studyMetadata; + private VariantFileMetadata fileMetadata; + private int numVariants; + private DBAdaptorVariantQueryExecutor dbQueryExecutor; + private List variantQueryExecutors; + + @ClassRule + public static VariantSolrExternalResource solr = new VariantSolrExternalResource(); + + @Before + public void setUp() throws Exception { + + VariantDBAdaptor dbAdaptor = getVariantStorageEngine().getDBAdaptor(); + VariantStorageMetadataManager metadataManager = dbAdaptor.getMetadataManager(); + solr.configure(variantStorageEngine); + if (!fileIndexed) { + studyMetadata = newStudyMetadata(); +// variantSource = new VariantSource(smallInputUri.getPath(), "testAlias", "testStudy", "Study for testing purposes"); + clearDB(DB_NAME); + ObjectMap params = new ObjectMap() + .append(VariantStorageOptions.ASSEMBLY.key(), "GRCH38") + .append(VariantStorageOptions.ANNOTATE.key(), true) + .append(VariantStorageOptions.ANNOTATION_CHECKPOINT_SIZE.key(), 500) + .append(VariantStorageOptions.STATS_CALCULATE.key(), true); + + StoragePipelineResult etlResult = runDefaultETL(smallInputUri, getVariantStorageEngine(), studyMetadata, params); + fileMetadata = variantStorageEngine.getVariantReaderUtils().readVariantFileMetadata(Paths.get(etlResult.getTransformResult().getPath()).toUri()); + numVariants = getExpectedNumLoadedVariants(fileMetadata); + fileIndexed = true; + Integer indexedFileId = metadataManager.getIndexedFiles(studyMetadata.getId()).iterator().next(); + + //Calculate stats + QueryOptions options = new QueryOptions(VariantStorageOptions.STUDY.key(), STUDY_NAME) + .append(VariantStorageOptions.LOAD_BATCH_SIZE.key(), 100) + .append(DefaultVariantStatisticsManager.OUTPUT, outputUri) + .append(DefaultVariantStatisticsManager.OUTPUT_FILE_NAME, "cohort1.cohort2.stats"); + Iterator iterator = metadataManager.getFileMetadata(studyMetadata.getId(), indexedFileId).getSamples().iterator(); + + /** Create cohorts **/ + HashSet cohort1 = new HashSet<>(); + cohort1.add(metadataManager.getSampleName(studyMetadata.getId(), iterator.next())); + cohort1.add(metadataManager.getSampleName(studyMetadata.getId(), iterator.next())); + + HashSet cohort2 = new HashSet<>(); + cohort2.add(metadataManager.getSampleName(studyMetadata.getId(), iterator.next())); + cohort2.add(metadataManager.getSampleName(studyMetadata.getId(), iterator.next())); + + Map> cohorts = new HashMap<>(); + cohorts.put("cohort1", cohort1); + cohorts.put("cohort2", cohort2); + metadataManager.registerCohorts(studyMetadata.getName(), cohorts); + + variantStorageEngine.calculateStats(studyMetadata.getName(), + new ArrayList<>(cohorts.keySet()), options); + + solr.configure(variantStorageEngine); + variantStorageEngine.secondaryIndex(); + Assert.assertTrue(variantStorageEngine.secondaryAnnotationIndexActiveAndAlive()); + + variantQueryExecutors = variantStorageEngine.getVariantQueryExecutors(); + dbQueryExecutor = null; + for (VariantQueryExecutor variantQueryExecutor : variantQueryExecutors) { + if (variantQueryExecutor instanceof DBAdaptorVariantQueryExecutor) { + dbQueryExecutor = (DBAdaptorVariantQueryExecutor) variantQueryExecutor; + break; + } + } + Assert.assertNotNull(dbQueryExecutor); + } + } + + @Test + public void testXRefRs() throws StorageEngineException { + Map> matchers = new LinkedHashMap<>(); + matchers.put("3:108634973:C:A", hasAnnotation(at("3:108634973:C:A"))); +// matchers.put("rs2032582", with("id", Variant::getId, is("rs2032582"))); + matchers.put("HP:0001250", hasAnnotation(with("GeneTraitAssociation", VariantAnnotation::getGeneTraitAssociation, + hasItem(with("HPO", GeneTraitAssociation::getHpo, is("HP:0001250")))))); + matchers.put("VAR_031174", hasAnnotation(with("ConsequenceType", VariantAnnotation::getConsequenceTypes, + hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, + with("UniprotVariantId", ProteinVariantAnnotation::getUniprotVariantId, is("VAR_031174"))))))); +// matchers.put("Q9BY64", hasAnnotation( +// with("ConsequenceType", VariantAnnotation::getConsequenceTypes, hasItem( +// with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, +// with("UniprotAccession", ProteinVariantAnnotation::getUniprotAccession, +// is("Q9BY64"))))))); + matchers.put("ENSG00000170925", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, hasItem( + with("GeneId", ConsequenceType::getGeneId, + is("ENSG00000170925")))))); + matchers.put("TEX13B", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, hasItem( + with("GeneName", ConsequenceType::getGeneName, + is("TEX13B")))))); + matchers.put("COSV60260399", hasAnnotation(with("TraitAssociation", VariantAnnotation::getTraitAssociation, hasItem( + with("Cosmic", EvidenceEntry::getId, is("COSV60260399")))))); + matchers.put("ENST00000341832.11(ENSG00000248333):c.356-1170A>G", hasAnnotation(with("HGVS", VariantAnnotation::getHgvs, hasItem( + is("ENST00000341832.11(ENSG00000248333):c.356-1170A>G"))))); + matchers.put("ENST00000341832.11:c.356-1170A>G", hasAnnotation(with("HGVS", VariantAnnotation::getHgvs, hasItem( + // The variant annotation may not have the "alternate" hgvs + is("ENST00000341832.11(ENSG00000248333):c.356-1170A>G"))))); + matchers.put("ENSG00000248333:c.356-1170A>G", hasAnnotation(with("HGVS", VariantAnnotation::getHgvs, hasItem( + // The variant annotation may not have the "alternate" hgvs + is("ENST00000341832.11(ENSG00000248333):c.356-1170A>G"))))); + matchers.put("VSP_039324", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, + hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, + with("Features", ProteinVariantAnnotation::getFeatures, + hasItem(with("id", ProteinFeature::getId, is("VSP_039324"))))))))); + matchers.put("VAR_081776", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, + hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, + with("Features", ProteinVariantAnnotation::getFeatures, + hasItem(with("id", ProteinFeature::getId, is("VAR_081776"))))))))); + matchers.put("PRO_0000211180", hasAnnotation( + with("ConsequenceType", VariantAnnotation::getConsequenceTypes, + hasItem(with("ProteinVariantAnnotation", ConsequenceType::getProteinVariantAnnotation, + with("Features", ProteinVariantAnnotation::getFeatures, + hasItem(with("id", ProteinFeature::getId, is("PRO_0000211180"))))))))); + + for (Map.Entry> entry : matchers.entrySet()) { + testQuery(new VariantQuery().xref(entry.getKey()), new QueryOptions(), entry.getValue()); + VariantQueryResult result = testQuery(new VariantQuery().xref(entry.getKey()) + .study(studyMetadata.getName()) + .includeSampleId(true) + .includeSampleAll(), new QueryOptions(), entry.getValue()); + if (result.getNumResults() == 1) { + for (SampleEntry sample : result.first().getStudies().get(0).getSamples()) { + if (GenotypeClass.MAIN_ALT.test(sample.getData().get(0))) { + testQuery(new VariantQuery().xref(entry.getKey()) + .study(studyMetadata.getName()) + .includeSampleId(true) + .sample(sample.getSampleId()), + new QueryOptions(), entry.getValue()); + } + } + } else { + List samples = result.first().getStudies().get(0).getOrderedSamplesName(); + testQuery(new VariantQuery().xref(entry.getKey()) + .study(studyMetadata.getName()) + .includeSampleId(true) + .sample(samples), + new QueryOptions(), entry.getValue()); + } + } + } + + public VariantQueryResult testQuery(Query query, QueryOptions options, Matcher matcher) throws StorageEngineException { + logger.info(""); + logger.info(""); + logger.info("####################################################"); + logger.info("########## TEST QUERY :" + query.toJson()); + logger.info("####################################################"); + logger.info("## Allowed VariantQueryExecutors:"); + for (VariantQueryExecutor variantQueryExecutor : variantQueryExecutors) { + if (variantQueryExecutor.canUseThisExecutor(query, options)) { + logger.info("## - " + variantQueryExecutor.getClass().getSimpleName()); + } + } + logger.info("## Using DBAdaptorVariantQueryExecutor for expected results"); + Assert.assertTrue(dbQueryExecutor.canUseThisExecutor(query, options)); + + ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options); + VariantQueryResult expected = dbQueryExecutor.get(variantQuery); + + VariantQueryResult unfilteredResult = null; + VariantQueryResult result = null; + if (matcher != null) { + logger.info("## Unfiltered query for comparison"); + Query emptyQuery = new Query(); + List fileNames = new LinkedList<>(); + List sampleNames = new LinkedList<>(); + List studyNames = new LinkedList<>(); + emptyQuery.put(VariantQueryParam.INCLUDE_FILE.key(), VariantQueryUtils.NONE); + emptyQuery.put(VariantQueryParam.INCLUDE_SAMPLE.key(), VariantQueryUtils.NONE); + emptyQuery.put(VariantQueryParam.INCLUDE_STUDY.key(), VariantQueryUtils.NONE); + emptyQuery.putIfNotNull(VariantQueryParam.INCLUDE_SAMPLE_ID.key(), query.get(VariantQueryParam.INCLUDE_SAMPLE_ID.key())); + emptyQuery.putIfNotNull(VariantQueryParam.INCLUDE_GENOTYPE.key(), query.get(VariantQueryParam.INCLUDE_GENOTYPE.key())); + emptyQuery.putIfNotNull(VariantQueryParam.INCLUDE_SAMPLE_DATA.key(), query.get(VariantQueryParam.INCLUDE_SAMPLE_DATA.key())); + for (VariantQueryProjection.StudyVariantQueryProjection study : variantQuery.getProjection().getStudies().values()) { + studyNames.add(study.getStudyMetadata().getName()); + for (Integer file : study.getFiles()) { + String fileName = metadataManager.getFileName(study.getStudyMetadata().getId(), file); + fileNames.add(fileName); + } + for (Integer sample : study.getSamples()) { + String sampleName = metadataManager.getSampleName(study.getStudyMetadata().getId(), sample); + sampleNames.add(sampleName); + } + } + if (!studyNames.isEmpty()) { + emptyQuery.put(VariantQueryParam.INCLUDE_STUDY.key(), studyNames); + } + if (!fileNames.isEmpty()) { + emptyQuery.put(VariantQueryParam.INCLUDE_FILE.key(), fileNames); + } + if (!sampleNames.isEmpty()) { + emptyQuery.put(VariantQueryParam.INCLUDE_SAMPLE.key(), sampleNames); + } + QueryOptions emptyOptions = new QueryOptions(); + emptyOptions.putIfNotEmpty(QueryOptions.INCLUDE, options.getString(QueryOptions.INCLUDE)); + emptyOptions.putIfNotEmpty(QueryOptions.EXCLUDE, options.getString(QueryOptions.EXCLUDE)); + unfilteredResult = dbQueryExecutor.get(variantStorageEngine.parseQuery(emptyQuery, emptyOptions)); + } + + for (VariantQueryExecutor variantQueryExecutor : variantQueryExecutors) { + if (variantQueryExecutor.canUseThisExecutor(query, options)) { + logger.info(""); + logger.info("###################"); + logger.info("### Testing " + variantQueryExecutor.getClass().getSimpleName()); + result = variantQueryExecutor.get(variantQuery); + logger.info("### Num results : " + result.getNumResults()); + logger.info("###################"); + expected.getResults().sort(Comparator.comparing(Variant::toString)); + result.getResults().sort(Comparator.comparing(Variant::toString)); + Assert.assertEquals(expected.getResults(), result.getResults()); + + assertThat(result, numResults(gt(0))); + + if (matcher != null) { + assertThat(result, everyResult(unfilteredResult, matcher)); + } + } + } + // Return any result. + return result; + } + +} diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/SearchIndexSamplesTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/SearchIndexSamplesTest.java index bd25023f6dc..12cb35f8714 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/SearchIndexSamplesTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/SearchIndexSamplesTest.java @@ -229,7 +229,8 @@ protected void checkLoadedData(String collection, List samples) Query query = new Query(SAMPLE.key(), samples); int expectedCount = variantStorageEngine.getDBAdaptor().count(query).first().intValue(); - assertEquals(expectedCount, variantSearchManager.query(collection, new Query(), new QueryOptions()).getNumTotalResults()); + assertEquals(expectedCount, variantSearchManager.query(collection, variantStorageEngine.parseQuery(new Query(), new QueryOptions())) + .getNumTotalResults()); SolrVariantDBIterator solrIterator = variantSearchManager.iterator(collection, new Query(), new QueryOptions(QueryOptions.SORT, true)); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchIndexTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchIndexTest.java index b83d1a9338e..4d5d8da6716 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchIndexTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchIndexTest.java @@ -215,7 +215,7 @@ public void checkVariantSearchIndex(VariantDBAdaptor dbAdaptor) throws IOExcepti TreeSet variantsFromSearch = new TreeSet<>(Comparator.comparing(Variant::toString)); TreeSet variantsFromDB = new TreeSet<>(Comparator.comparing(Variant::toString)); - variantsFromSearch.addAll(variantStorageEngine.getVariantSearchManager().query(DB_NAME, query, queryOptions).getResults()); + variantsFromSearch.addAll(variantStorageEngine.getVariantSearchManager().query(DB_NAME, variantStorageEngine.parseQuery(query, queryOptions)).getResults()); variantsFromDB.addAll(dbAdaptor.get(query, queryOptions).getResults()); assertEquals(variantsFromDB.size(), variantsFromSearch.size()); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchTest.java index a8fe6d9790a..56588d25863 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchTest.java @@ -21,7 +21,7 @@ import org.opencb.commons.datastore.solr.FacetQueryParser; import org.opencb.commons.utils.ListUtils; import org.opencb.opencga.core.common.JacksonUtils; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; @@ -67,8 +67,8 @@ public void testTranscriptInfo() throws Exception { variantSearchManager.insert(collection, annotatedVariants); - VariantQueryResult results = variantSearchManager.query(collection, new Query(), - new QueryOptions(QueryOptions.LIMIT, limit)); + VariantQueryResult results = variantSearchManager.query(collection, variantStorageEngine.parseQuery(new Query(), + new QueryOptions(QueryOptions.LIMIT, limit))); for (int i = 0; i < limit; i++) { Variant expectedVariant = annotatedVariants.get(i); @@ -213,8 +213,8 @@ public void testSpecialCharacter() throws Exception { query.put(VariantQueryParam.FILE.key(), fileId); query.put(VariantQueryParam.FILTER.key(), "PASS"); query.put(VariantQueryParam.ANNOT_CLINICAL_SIGNIFICANCE.key(), "benign"); - VariantQueryResult results = variantSearchManager.query(collection, query, - new QueryOptions(QueryOptions.LIMIT, limit)); + VariantQueryResult results = variantSearchManager.query(collection, variantStorageEngine.parseQuery(query, + new QueryOptions(QueryOptions.LIMIT, limit))); if (results.getResults().size() > 0) { System.out.println(results.getResults().get(0).toJson()); diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/solr/VariantSolrExternalResource.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/solr/VariantSolrExternalResource.java index 611265b0a0e..dc5a019baa1 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/solr/VariantSolrExternalResource.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/solr/VariantSolrExternalResource.java @@ -118,6 +118,7 @@ public VariantSearchManager configure(VariantStorageEngine variantStorageEngine) VariantSearchManager variantSearchManager = variantStorageEngine.getVariantSearchManager(); variantSearchManager.setSolrManager(new SolrManager(solrClient, "localhost", "core", variantStorageEngine.getConfiguration().getSearch().getTimeout())); + variantSearchManager.setSolrClient(solrClient); return variantSearchManager; } diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManagerTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManagerTest.java index 5fb147d6d9e..e90b49cff4d 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManagerTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/stats/VariantStatisticsManagerTest.java @@ -18,6 +18,7 @@ import org.junit.*; import org.junit.rules.ExpectedException; +import org.mockito.Mockito; import org.opencb.biodata.models.variant.Genotype; import org.opencb.biodata.models.variant.StudyEntry; import org.opencb.biodata.models.variant.Variant; @@ -27,18 +28,21 @@ import org.opencb.biodata.tools.variant.VariantNormalizer; import org.opencb.biodata.tools.variant.stats.VariantStatsCalculator; import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.models.CohortMetadata; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; +import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest; +import org.opencb.opencga.storage.core.variant.VariantStorageEngine; import org.opencb.opencga.storage.core.variant.VariantStorageEngineTest; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor; import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import java.io.IOException; import java.net.URI; @@ -48,8 +52,7 @@ import java.util.*; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Created by hpccoll1 on 01/06/15. @@ -112,6 +115,88 @@ public void calculateStatsMultiCohortsTest() throws Exception { checkCohorts(dbAdaptor, studyMetadata); } + @Test + public void queryInvalidStats() throws Exception { + //Calculate stats for 2 cohorts at one time + checkCohorts(dbAdaptor, studyMetadata); + + QueryOptions options = new QueryOptions(); + options.put(VariantStorageOptions.LOAD_BATCH_SIZE.key(), 100); + Iterator iterator = metadataManager.sampleMetadataIterator(studyMetadata.getId()); + + /** Create cohorts **/ + HashSet cohort1Samples = new HashSet<>(); + cohort1Samples.add(iterator.next().getName()); + cohort1Samples.add(iterator.next().getName()); + + HashSet cohort2Samples = new HashSet<>(); + cohort2Samples.add(iterator.next().getName()); + cohort2Samples.add(iterator.next().getName()); + + Map> cohorts = new HashMap<>(); + cohorts.put("cohort1", cohort1Samples); + cohorts.put("cohort2", cohort2Samples); + + // Just cohort ALL is expected + VariantQueryResult result = variantStorageEngine.get(new Query(), new QueryOptions(QueryOptions.LIMIT, 1)); + assertEquals(1, result.first().getStudies().get(0).getStats().size()); + assertEquals(0, result.getEvents().size()); + + metadataManager.registerCohort(studyMetadata.getName(), "cohort1", cohort1Samples); + + // Still just cohort ALL is expected, as cohort1 is not ready nor partial + result = variantStorageEngine.get(new Query(), new QueryOptions(QueryOptions.LIMIT, 1)); + assertEquals(1, result.first().getStudies().get(0).getStats().size()); + assertEquals(0, result.getEvents().size()); + + //Calculate stats + stats(options, studyMetadata.getName(), cohorts, outputUri.resolve("cohort1.cohort2.stats")); + + checkCohorts(dbAdaptor, studyMetadata); + + // All 3 cohorts are ready and expected + result = variantStorageEngine.get(new Query(), new QueryOptions(QueryOptions.LIMIT, 1)); + assertEquals(3, result.first().getStudies().get(0).getStats().size()); + assertEquals(0, result.getEvents().size()); + + List cohort1SampleIds = metadataManager.getCohortMetadata(studyMetadata.getId(), "cohort1").getSamples(); + CohortMetadata cohort2 = metadataManager.addSamplesToCohort(studyMetadata.getId(), "cohort2", cohort1SampleIds); + assertTrue(cohort2.isInvalid()); + + // Cohort2 is invalid, but still all cohorts are expected, but with a warning event + result = variantStorageEngine.get(new Query(), new QueryOptions(QueryOptions.LIMIT, 1)); + assertEquals(3, result.first().getStudies().get(0).getStats().size()); + assertEquals(1, result.getEvents().size()); + assertEquals("Please note that the Cohort Stats for '1000g:cohort2' are currently outdated." + + " The statistics have been calculated with 2 samples, while the total number of samples in the cohort is 4." + + " To display updated statistics, please execute variant-stats-index.", result.getEvents().get(0).getMessage()); + + VariantStorageEngine engineMock = Mockito.spy(variantStorageEngine); + VariantStatisticsManager statsManagerMock = Mockito.spy(variantStorageEngine.newVariantStatisticsManager()); + Mockito.doReturn(statsManagerMock).when(engineMock).newVariantStatisticsManager(); + Mockito.doAnswer(invocation -> { + invocation.callRealMethod(); + throw new StorageEngineException("Mock error calculating stats"); + }).when(statsManagerMock).preCalculateStats(Mockito.any(), Mockito.any(), Mockito.anyList(), Mockito.anyBoolean(), Mockito.any()); + + options.put(DefaultVariantStatisticsManager.OUTPUT, outputUri.resolve("stats_mock_fail").toString()); + try { + engineMock.calculateStats(studyMetadata.getName(), Collections.singletonList(cohort2.getName()), options); + fail("Expected to fail mock"); + } catch (Exception e) { + assertEquals("Mock error calculating stats", e.getMessage()); + } + + cohort2 = metadataManager.getCohortMetadata(studyMetadata.getId(), cohort2.getName()); + assertEquals(TaskMetadata.Status.RUNNING, cohort2.getStatsStatus()); + + result = variantStorageEngine.get(new Query(), new QueryOptions(QueryOptions.LIMIT, 1)); + assertEquals(3, result.first().getStudies().get(0).getStats().size()); + assertEquals(1, result.getEvents().size()); + assertEquals("Please note that the Cohort Stats for '1000g:cohort2' are currently being calculated.", + result.getEvents().get(0).getMessage()); + } + @Test public void calculateStatsSeparatedCohortsTest() throws Exception { //Calculate stats for 2 cohorts separately diff --git a/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv-large.vcf b/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv-large.vcf new file mode 100644 index 00000000000..5e1de5845e2 --- /dev/null +++ b/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv-large.vcf @@ -0,0 +1,52 @@ +##fileformat=VCFv4.2 +##FILTER= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +##ALT= +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE_3 SAMPLE_4 +20 500000 . C CCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG . PASS . GT 0/1 0/1 +20 600000 . CCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG G . PASS SVTYPE=DEL GT 0/1 0/1 +20 700010 . T C . PASS . GT 0/1 0/1 +20 800000 . A . PASS SVTYPE=INS;END=800000;SVLEN=6027;CIPOS=-16,22;RIGHT_SVINSSEQ=CCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG;LEFT_SVINSSEQ=ACCACACCCACACAACACACA GT 0/1 0/1 +20 850000 . A . PASS SVTYPE=INS;END=850000;SVINSSEQ=CCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG GT 0/1 0/1 +20 860000 . A . PASS SVTYPE=INS;END=860000;SVLEN=1000 GT 0/1 0/1 +20 900000 . C CACCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG . PASS SVTYPE=INS GT 0/1 0/1 +20 900000 . C CCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG . PASS SVTYPE=INS GT 0/1 0/1 +20 900000 . C CGCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG . PASS SVTYPE=INS GT 0/1 0/1 +20 900000 . C CTCCCGTTTATGAACTCAGCGCCGAACAGAAAAAATAGCCCCACTTTAAGTCCGCTTTAGCGACTCTAGGGTCCGAACGCGCTGTTTCGTACATGGCACGTCGGTAGGCAAGAACTCCCGTCCTCAGATGAAGATGCGTAATATCCTTACGTATTTTGGAACTCAGGCTGCCGAGCATCTTATTGGGAGACTCTTACCACTTTGCCCGTAAGCAGGGAAACGACATTTGATAAAGGATGGCAGGGAAGCTTTTATGCCCCTTTTCCTACTACAACGTCGAATGTTGACTTCCTGGTTAGCGTTGTGGCCTTGTACTGACCCTACTAGGGTTTCAGCTGCCTAGAGGACATTCGACCGACCCACGACCGCAGCTCGCGGTTCATACACGAGAAGCTACAATCCGCTTAAGTATTTGTTCTTTTCGTTCTAGGGCCCGCGGCACCAAGGAGGCTTCAGAAAGAGAATAATTACTCGATCGTCCCGTAAGTAGTGTATCGCTAACAGGGCCTGCGTCGTCCATACTAAGGCAAGGTGCTCCAGCAGGGCATAGGAATTGACCGGCGGGTAACATTGGAGACAACATGTGATTTGTTGCTTAATCTCGGTTAACCCGCCCCGCTGTAAAGGCGAGACGGCAATCATGAATTCTAGCACGCACCCCGCCCGTCTCTGTCTTCAAAATGTATTTTTGGCAACGAAGGATCAGCCTGTCCCGAATCGACACTGTGTTCCTATGGCGTAAAGAATTCTGTTCCTGAATCGCGGGCGCACCGTAATTCTTACTTTCACATCGAATTGTTAGATGCTGACGAGCAGAGCGCAGTGCGGCCGGGCGGAGATTGCAGGGCTGGCAGAGCCATTGGCCGTTGATGGTGTTTAGACGCTAAGCTAACCCTATGCCTCATGGATACTTGCTACACAACAGTGTCTGCGTAGGCTGAAATGGGGACGGCATGAGACCTCAGTGTCAACACAATTTGATGCACTGGGTTTTCTGCAACTACTTTACTACGGCTTTGTCCTTAACGCGTTAAGGGACTCCATCTCATCCGTATCATAAACTCCGTAGTGTATTGGGGCCAAATCTAGAATACGTACCGTCGACAATGCTCACGAGGGTTACTCAAAACTCGGCGGGGTGCCTCAATCCGCGTGCCGTGAAATGCCCGTATTCACGACGACTAAGCACTTAAGTCTCGGGAGCTCTGTTGCGTCTCGTCTAAGGGCAGTCTTGCTTCCTGTGTCTGCAAGTTCCTCTCTAGTGTTTAGGGTGCCTAATATACTCCACGTGTGTCTATGGACTCCATATGGTAAGTAATGGTGTTTGATAAACCCCTGGCCCTTTAGGTTCTACCTAGCCTAACTTCTTCCTTTGACTTTTACCCCCATTGATCCATGTTCGTCGAAGTGGCCGATTGAGGCTGCCGCATGGCCAAACCGCTAACCCGATCGAGAAGGTTGCAAGGGCGCATCCGCAAATAAACCATGGTTGCTAATTGGGGTGCAGGCGTAGAATTTGTCGGTTCAAAAGGCCCTCGACCTGCACAATGACTTGCGCTCGTAACTTATGATAGGGCCGGCATGGGATTATTCGAGGGACTCCTCACACTCAGAAGTTTACTCCCGGAGCACGACTTTACGAGGTGTGTTGCTTTATGATGGCATATAAAAAAGATGCACCAATCACCAAAACCCAACTATCCTTGACACGATCAGTGCGCGACCTACGACTACTCTAGTGGTACGACTATCGCGATGGAGGGGATATTCGTATATTTGAAGTTCATGTATTGATTCGGTTATGGCGTCTCCCTTAGTGTTTATGGACTTCATGCGTGCCCCTTATCCGCCCGCAGGCACCAATCACATTGTGGAGTTAGTTGGAGCGAAATTGGGTGGCTGTACGGTCAAACTAGAAGCATACCTTCACAAGGGCGTGGCGCTATGGGAAGTGGACCTGAGGGACACGATGGGTGAGATTCGGACACTCTGTCTACAAAATTAGGAGGTACCTACTGCTAGCGAGCTTCAAAATTCGGGAAGAGATCCTCGTACCACGTATTAATGAAGAGCCAGTGGAGCGAAGAAATTATCGCAAGACCGACCCTGTGCCGGCAGATGGACGTGTTAAACATGGTACATTGAGCAGTCCGACTCGTTGTATAGTTACCACGTGTGAGAAGACTTAGCTCGCAGCTAGTGGACAAGAACTCGGCCGAAGTTTCGTTCCTGTATTGCGCGTGTCCAGGATGTGAACAGTCCAACTTCATTATTTTATTCTGACTTGACACTGGACGTAAGTCAGCTGCTCAGAAACCGGAGTTCCCTGCCTTGGCCTGGGGGCCCCATTTGCGGATGAGCCCACAGGTCTCGTAGACTTAGGTGACGCCGGGATCTGCTGGCCTTATGTCCCTAGAAGCTTGGATGACCGGCCCACGGCAATGCGTAGTCAGGGAGGTATCCATGACGTGCGTGAGTCGGGCGAAGAGGGCCCCACATGCACCTAGAATAGCTATGTGTGTTTCTCGGGCGGAGCCCGACGTCGTCCCGCTCATATCCAAGTGCTTTTTCCGGGGGTTAAGCGGTGGCTTGGGCACAAACGGTCGCCTCCGCCCGCTTGGTCTCTCAGGTATTTCCGGGGGCACTCATAAACTACGAACGCGATCTCCGATAGCCTTCAGTTGTGGGGATGCGGCTAAATCCAAGCAATTGCGACGCTTTGGGTGTGGTGGGGGTTACTGATCCGGACTGCAAATTACTAACTCCGTCACCTATCAACACGTCTCCCTAATCTGTTGGAACCTCGCTCTATGACATCAGAATGCGAGCGCGGACATGGGACATGTCGGGACAATCTATTCTGCCTCTCCGGTACTGGGTACGATAAGCGCGACACAAGATTGTGCTCTTACTTCGCCGTGTTACCGGGTAAATCGCCGGGATGCTACTCTTATATGTGCCTTCTCACCTGAAGCCTTGCGACCTCGAATGATTTGGGTGTGAGTAAGCACCCGGGATCGAAGCTGGTAAGCACGTCACCATGTTAGAGATACTTGACCGACGATGCCCTTCTACGGCTCGGTTCACGGTGCCCAGCGGGATCCTCTTATCCCAGCCGGTGCCTAGCCGATAAAAGCTCACAAGCTTAAGGCTCTGTCAGGGGAGATGTGGGAGAGGCTCGATGAGAAGTCCTAACGACACCAGAACCCCAATTAGCACACATGGCTCTTCCGGGGACTTGGCTCTCGTAACTGCCTGCAATGTACTACGTCGCATGCTCATGTACCAGACATTATGTCCGTCCTTAACAGTCGTATTGGAGCAAGTTGTGAGTACAAACAGTACACAAGCTGTAGGTCACTCCCCCCACGTCTACCCTATATACTATAAGTCCTTGTAGCCAGGATGAGTAGGCCCCGTGTACCTTATTTGAATGCGCACCCGTGACTTCTGGTGGAGAACTTGGTCACGTCGGGTGGACCGTGTACGATACCCCACAGTGCCCCAGGAGGTATGGAACGATGCTGCCCTGAAAGGTGGAAGGCCGGAGTTTTTGTAGTTACAAACAGTACTCAGCGCTATCAGCAAGTGGACACGGAGAAGTAATTGAATGACCGCCTAGCAGTGCTCCGCCTCCGGGCGGCAGCCCGCGTGCCTCAGTCAGAGATCTCGAGCACGCACCCTTGCTGCTTCCGACTAATGGGAAGAAGTGGAGATGGCGACCCCCACGCGTTATAGTCCCAAGGTTGCTTCCAGGAGCTATACCCGAACCTTTAGGTCTAGACTTGTCGAGTTTGCGACCGTACTCGCTTTGTATGTCGACTTTTTTGAAAAGACAACCGTGGCCCGGTCACTGTGTCTCCAGTTTGGCGAGCCGTAGTCAGTGATCGCGCTGCTCGGGATCAATTATCTCCAACGGTCAGCAGATCTACGGAGTCGGTGGCCGGTATAATGCTGGAGGCCGGTGTATGCAGGAACCTAATAGTCCCCGAGTGTCGATCTGTACGGGAGTCACTAAGATAAGGCAGAACTGACTATCTCTCCACCGCTACGTAGTCCAGTGCCAAGGTGTGGTCATGGCCCACATAAATCAAGGATTTGTATGCTAGAATTACGCGATCGAGTGACACTCAGAGTGCACTGGCTAAGGACTCTAGCTTTGTAAAGACAGCATTACATCAACACTCGTTGCATTCCTAGACTGCGGTTCCTTCGGGATCTCCCCTTGGTACTTCCTATATTTCCTGAGATGTGGGCGCAAACAAATGCCAGATCCCGCCTGTAGGCATTCGGGACGGGTACTACGCGATGACCATATCGAGATTGAATCTTACGTATCTCCCTACAAGTGTGGCTTAAGGGGTTAGCACCGACAACTCATATAAAAACCAAGTAAAGAGAGTAAACACAGTGCGGAGTAGCCGTACTTGCGAAGCGCCATTTGGGCGCGACCTTCCGAATTTGCATCCGACGTCGGTCGTATAGCAAAGAGCGATTCTGAGCCTCCAGTCGCGGCCACGTCGCCGCTTGGCCAATGTATACCAGTTGTGAATAGGTCGCGGAACGCTAATGGAAGTAACCCCTCCTATTACCAACACGCCTCAATCGTACCACCGGTACCCACGGCATATAGAGACTATACGGCGTCGCGGCGGGCCTCCTCATGCTCGTATGGACTACACACCATGTCCTAATAACCTTGGTAACATTACGCCTTTGAGGGGTTCTTTATCTCCGATTTCGCGGTAGATAAGGCCACATGACATTTTCCGTTCGAGCGGACACGTGTCGCCTCAGTAGCCTATTGCCGTCGCCCAACCTACGCCACTGGCTCATGCCATGCCATCTCATACAATTAACTCCCGACGTTCGTACCAGGTGTACCAGGGGGTCCATACTAAGAGAGTGTGACCTCCAGATTCTAAAATACTATGTGAGGAAGTCATGATTTCCTCTAGTCATTGTGGCACCGCTCGTACCGTGAATCATATCAGATAAGAACGATCAGTTTGCGACTGTTATTCCGAGGGTACAGTCTTTAAGACACCATTCGGAAACGAAAAGGGACTAATGGATGCCGCGAAAAACCTAAAAAGGAAACAAGCTATCGATCTGTGGCGCCAAGGAGGGCGGGGCCCCGCATGACTCGTCAACTCCCATAGGCACCGCTCCGGGCCGGTAGCGACCGGAAGGCAATTGCTACCCTATCATATAAATGATCCTGGTCATTAGTTGTGCCCCGTATACTTGAGTCAAGTATTCCGTCGCAGAGACTACTGCTCATCCGAGACAACTTCTTCTCTAAGCGACCCGGGCGGACAAATTCACACGCAGATGGGAATTACACTAGGGGTATACTCACCCGGGCCTAAGGAAGGCACATGAGTGGGGCGGGCTGATTAGCAATTGTATCAAACTAGGGTCAGGCACACATAATGAAGGAGCAAATACATTGCACGGATATAGCCTAGTCGAGTTCGACGCGGAAGAGGTAGGCGAAATAAACTGTGAAAATGGGCAACACCTCACGAATGGTCATGAGTCAATGTAAAATCTTCGCGGCTTACTCGGCCTATTGCTATCGTGACTCCCAGGCCCTCAGTCCACCTCTCGTCAGATACTAGTTCGGGAGGCTCGAGATAATTGACCCTCCCTAATTCAAACTCAAACGAACTCACGTACGGGTAGGGCACCTAATTCGAGATGAAACATGCCTAAACGCCTCGAGAATATGTCTGTTTTGTACATAGTCTGTCGCTTATTCCACCACTTCAATCTGAAAATAGCGGACTAGTCCCGAGGGCCCCGATTTGACAGGTGGTTGAGAAGATCAAGTTGCACGCTAGCACCAGACCCTAACTCGCAACTGCTCTGCCTGATAAAAAACACATGAATCTGCGAACCATAACCTTCTTTGCCATTCTTTGTCTGCGGTTAGCGATGGGGGGTGTAAAACCACCTGCAGCGCGTCCCGATGATAAGCTATATGGGTTCACGTGCAATCCCAGAATCCTTTGGGGGGTGGATCAAATTGAAGCGAACGCAGCGTTGTATCGACGGTGAGCCCGCACATTTGAACTCGTTCCTCGGGTTCTTCGATGAATGTGGACTACGCTAGTTGACAATGCCTGCAAGTCTCCTCTCTCCCCCACGTGTAATATTGTACATAGTCAAGAAAGCTACATGCACGCCGACGTATCTTCTGTGCTTATGGATCGTTATAAAATTTTGTACTTACAGTGAAAATATGAAGGATGAAGCCTTAGAACTCGCTGCGTGAGAGAGAAACCCACCTATAACAAGACGCACGTGGCGCACATCCGTCCCCAGTGTTCAGCGACAGTTTGTCTAGCCAGTGAATCGGATCAGGCGGTCCTAGAGGTCTAGGCGACTTATGATCTTAGTCAGTAACCCCAGTAATTACCGTGATTACCATTTACAACGGTTAATTGGGTTGGTCTATGGTAACGCACAAATTACTCGTCATACCTGCTTAACATGCCTTCGGGAGTTAACGCTGTCGGGCGGGGTCAGCTTACATTACAGTAATCCGGCAAAAGACAAACCAATGCATCTCAAGAACCAACATAATAAAAAGCCTTATCAAATCTGGATCACAACTAAACGTATCACAGTAATATGTATCGCGAGTCTAGGAACTTGAGCACCCAATCATCATCCGTGGCGTCTCCGCGGGCTCTGAACGAGAAAACTGGGCACTCCCGGCTACTTTGAAATCAGGAAAAGGTCATCTGTTAAATGTCTGTTCACGATAATAGGCTATTCCACTTCAATACATGAATGACTTAGGATAGATATTCCCCTGGGCACGATGTTCTTATGGAGTGCGGGATTTAGTGGATATTCACGCTTGTATTCAGTCCCCTGTCCCATCACCTTTTTATTTGTCCTCCGCCCGGATTCGTTTCGTGCACGGCCAACGAGGAGCTGTTCATCAAATCTACCGACATTAATAAGTCTGGCCTATATGTCGTCCAACTAGGGACCCCTTTCAGAGTAGGTGGAGCTATCGTAGTCATCAAGTCACAAAATGAGTTCCTTTAAGGTGGCGATGTGTTGGGTGCAGACAGTATAGATAACACTCGGCATGGCTTTAGTCCCTCTATCTCGGATGCGCGCATCACAGATACCGCACAGGGTCCCACCTTACATGTTCGGTTGGGACTGAGTGGAAAAGTTGGACCCTAATGGTTGCTTCCGCGAGAACAGAGACTTGAAGCAGTCAGTCAGGGATTCAGTCCGTCAGATAGGGGTGGAAGTATGGCCGGCGGCCATTTGAAGTCGGCTAGCAGCAAATAAATTGCGTAAATATGTTGGTGTTTGGGACAAGGAGGCACGGATCCAGACGTTACTGAGTCCCCGAAAGCGTTACTCACGGATACACGCCATACGCTCTACTAAGAAGAACCATGCACGGACCATTCGATACAGTCTTTCAAACGCCAATGAACCAGGAGCCGGGGGGATGAAGGTGGGTTGGAGGCATCGGAGTGGTTGGGAACTGGTAAACGAGAAGGCCTAAGAGTACTCCCAACGCGGTGGGTAGTATGTAGGTTGGTTTAAGTCGCGTTATCTGGAGCCCGATACAGAAGACCATCAGCTAAGAGTGGTCGTAAGACAACAGATAATCCAGGCATACTAACACCGTTTTATTAACACCTAACCCGTGCGCGGGCAATGATCTGTGATTTCGGCGGAACCCTTTAGCAATCACTCGAATGCTCCCCTCAAATTTGAAAGGCATGACCTCCGTTGTACGCCCTGGATTTCTTAGGGATCGATTAAATTGGAGGCGAATCGTGTGTCTGATCTATTGGCCAATTTGATAAATCGGGACAGAAAACCCTGGGCAGCTGTATCCCGGCCAACTTGACTTTCACCTTTATGCTATCAGAAAAATCGGTGAAACTGAAAGTCTCTGAAGTTCTCTGCAGTCATGCGGGTACACGAATGTGAATATGAGCTGTCGCGAGTTAATAGGACAGGCAGAAGCGCAGGTTGCGTCCTAACAGGAATACTCTTAAAGTAGGTGCGAATCGAGCAACGTCATCGCGTCAAGCGCGACCGCTTCAGTTGGTTGGAGGCTGTCGGCAGGTTCATCTTGCCAAATCGAACTAGCAGTGGCTTCGTTACGGGCCAATACCACCTCGAGGAACCCGGGTAATGAGGTACAGCTTACACAGGCAACCTGACAGAATGGATGTTTGACCTTCTATGCTGTACGTGTTAGCCCAACACAATTAGCAAGATTGCACCGGCGCTAACAATGTTAATGAGGTGTACTAAAGAGGTCGTACAGACAGGTCATGGGGGTAGGCGTATTTTCGCGCAGAACACGGGTCTATCCCATGAACGTACAGGCAAAATATTCGTTCTAGGAGCTGTGGGTCGGTTCCGTGGAGAAAAGTCGCAAGGCTATGTAAGCATTCATGCCTAATCCTCGCAAAAAGTGTGTGGGATTAGAAGCGTGTCTGAGCAGATGTGGTGCGGGATCGAAATAAGTAGGTAACTACCACAGCACACTATTCCAAGCAATAGTTCGAAAAAGGAAGACAAACCGATTCGGTTAAACTTGTGGCGGTCCTTTATTGCGCTAGCGTGCACATACTTATAACCGTTCCTTGCTAGATGCCAATACACGATATTGGCACATCAGGTGCTTTCACCCGAGACAAACGGCCTCTGCTAGCAATCCCGGGTCGGTAAAAATAGAAGACGAGAGGACGGCGATCATATCGTGCGAATAGTATCAGGCTTCATATGGGCAGTTCGCCTCGAGGACTACCCTCGTCGAAAAGCTTTACCGGAAGATGCCAAACCCGGGCAGCGGCACCAGGCTGTTACCCGTACTCCTAATAGCTCGGCTAAAACACGGAGAATGAAATACCTGTACCAAACGCACGTTGAGCTTCATCCACCTCTGGCGTGTGTACTATCCTATAGCGGATTTCGCCGGAGTTGCCTTTTCTCCTTGAGTGTTGATGCTGCCTTCTAACAAGCGCCTTTGCACCCAACATCGACATTTAAACTTTATTATGGGGTTGGTCCTGCTCCGTGCAACTGTCCTCTCTGTCACCACTATATTAAACATAGTCTGACCTAATGCAAGTTAGTAAGGAACAAGAGCAGCCTCATAAAGACTTGGCGACGCGTTGAGACACATATTGGGTACTTTAGGGATAATAAGTGTGAGGTACGAGGTGCGCGGACATTATCTTGATGCCCTTCTTGGAAATGTCGTATTTCAGGCCCCTGGTGCGGAAGAACTTTGGCCGGGTTTACATAGTTTAGATAGTCCTAGGTACATCAAGACGCCAGTATGAGTCCGCGAGCCGGTATGAGTCATATACAAGCCAAAGGGCCGCCTCCAACTTCTGTATCCGTAATTTTCTCCATAGGGCTCTGACCAGTATTGACCCCAAAATGTGGGCTGCAGATCCAAAGAGTTAACGGTACTACCCGAGGGATCAGTCATTCACTACGGTGCGCCTTGGATGGGATCCTACCGTTGAATTCGTAGCCTCCTGCGCTCCGCTAACCCTGACAATATGTGCACTTCGCCTGGACGTATTGACACCTGAAACGAGCACTGAGCGTAAAGCTAGTTGTTATACGTCACTGCACCATGTGGCGGGTTATAGCATCCTCCCATTTAGGTAATGCGAATCTTCACAGACATGACGGGGCATGTGGACTTACCGGTTACTTCGGGAAAACTGGCAGACAAAGGTTGGTTATAACCTACCAGCAAGTCATAATCTGTTTCAGGAATTACGCAGTTCTGCCGCTCATCCTAAAAATCCACCGACCCTCGCACGTGCCGAGTTAATCTACGGAACGCATAAGAGAACGGAAGCCCAACTGGAAGTTGGACAATGCCATGAGCGCCCTCGGACCACCCTCAGAGAGGTAATTTAGGGGAGCTAGGTAATGATCACGCTTTTCAGTGGTCCTGTATCGTGCATAGGGAAACCACTCTGGATAGTGCTTCTAGCGAATCGTCTAAGTACCCCATCCTTATAGCCCTGCCAAAGTGCTGGTAGTAGCTATCCGGATCACCACGTTCGGGGGACGGGGCGTGGCTCGTCCGTATGCTGGCTTCGCCGTAGCGCAGTCCGAAAACTGACAGACATAGTGCATTAGCATTTAAACCACCTCGAATAAGATTTGGGCAGATAATAGCGCCATCGTGTGCACTTGTTGCTCGGTGCCACCTAACCCCGAAGCTCCTAAGGCGCGGGCCTATAATCGTTATCCCCTACGAAATGCAAGTGCGTCCTTGCTAGAATCATTTGACTCTCCAGGGCCGGGAGTGATAGACCTTTTCAGATGTGTACCCAACGATGGGTTCCTACTCACGTAGATACCAGTTCAACCGCATAACCGTGAATTTTGGTCCGTGTCGTGGACGCCCGGAATGATAAGCCTCAACAGATTGCCTGTTTAGCTGGTCGATTGTGCCGATCAAGTTGACGTGCTAGGCCGGCGCTAAGCGTAGAGGAGAAATCTCGCAGCTTCGATACACGCGAGAACTTGGGCGTCACTTACATAGTCGGAGGTGTCATGGTTGAGCCGGTATCTCCGATTGATCACTCGGGACCAAACTCTATCTTCCTTCTAGAATCATCACGCCGACTTGAACACTGTCGTCGTTTATCACTGATAACGCTTATCAGGTTACGTGGAATCGAAGGTTCAAGACGGTCATGGAGCTGTTTTCTATTATTTCACTGTTAGTTCTTGGATACTGGCGAAGGACGCGCCACTTAGACTGTGCAGTCCCCAATTGGCTCGAGATTCCATCGGCGCGTCTACCTGACTCAATGCGTTACAGTCTTGGCCTGGCGCTCCTACCCCTGCGGCCTATAACGAGGTGGCTGTCCCGCGTGCAACACTCGCGAGCAACATGCCAAGACTTAGGTATAATAGCTTAAAGGTGTTCGCTTCTTGGTAGATAATTCTGGATCCGCGTGCCCACCTCCCTGTGCTACGAAGTTCCGTGAACAATGTCTAGATGTATAGCAGTGTGCAAGCCACCAGGTTCTATGGTGAGCAGCAATACAAGCCGTAGGTGACGTTCGGAATGACCCGCAATTTGTCGCGCCCCAGAGTAAAGCGAAGGCTTGACACTCCCGACTAGTAATCCCATGTGTAGGCCCCAGCACTAAAACATCTCGTTCCTCCTCACTTCCTGTGCTCAAGCCGCCTGGGGTCGAGGCGTAAGTACGGTCTTGATTGGTTGATTTTTGGACAATATGATGTCTCTACCAGCAACGCCGCCACCGGTTATGTCAGTCTCTGAGTTATAGCTACAATCTCAAGAATGTCGTGCGGCTCCCGTGGATGGCAGACGAGTCAAGACCAATAACAGACGTGTGGCACCTCTGGTCGTATCGTCACCCCCAATTGATGAAAATGTGGCTAGAGTTCCGAGGAGGACTTGCAAAACGAGTATCATTGATCCAGCCCATTACTCCAGTGTCCAAGCCCGAAACTGTAAGAGGGCCATTGAAATAATACCGGCTAAGGAATGTCTCGGGCGTGCGGGAATCATAAGGATCCCTCAGTTGACGATTGGGCGCCTCTTTTGCGAGCGGCATCCGCTGCATCGAGGTAAATATGTGTTGGGGACACAAGCCGGATTGGCATTGCCGGAACTTATGTGCGGCAATTGCAAAAGTCGAGTACTAGAAGGATGATCAATTGCCGATGCGCCAATTGAAGATGATCGCCTAAATTGGCTTGTTCATCCTTCGCTCCTGTACGAAGAACTCTATCCAACAGTTGATGGTTGGCAGTAGTTCCGCACGAACAACAACGGGATGGAATTCACGGACATGTTGGTGCCTAAACTAGCGCCGGTTTACCGGACGTGTCCGTACTCATTCCCGCCGCGCCAGCCAGAAAGGGTCGATAATTGCAACCTCTCTCGCAGTATCCTTTGGTCGGCAGAACGCCATGGAGCGATCTCCTATCTCTCAAGGTCAGTTTAAGAATTATGAAGTACTTCTCGACGTGTTGAACTTCCGGTCTCGGCCGTTAGGCTCGCTCGATTGAAGTACAACAGAATTCAGTTTAACCTACTACTCGCCCCAGAGTACTTCCTATGAATGGCTGAACTCTGACACCGAGTAGACAAGTGGTGGATATACTCTTTACGGGAGTCACGTATATCGTACTATTATGTTTCAACGGTGCTTGCCATCCACCACAACACCGGTAAGATTATCAACGCTTACTCTCAATATTGCCCGCGAGGAGGTCTTATCCAAGGTGTAACAATAGTAGTCTGTAGCGTTGAGATTAGTCTTAGAAACGGTGAAGGGCGTTCTCGTGCGCTCGATACCGCTCCCCCCCAATCGTGTTCGGCATCACTAGGACATACAGCGGTCGCCAAAGCCTTTCACCGGTATCGCTTCTCGGGACTGATGCTGCAATACACGCGTAGTCGAAGCAAATTTATGGTGCCCAGCATCTGAAGCAACTCCATAACTTTTACCGTCGATAACAGGATTTGGTAGTCCGCTCGGTACTAATGCCGCAAAATGAAGCCCTATAATGAGATATCCTAAGGACGTAGGGCGCCATTGTTGGTTAATTCCCCCTACGGATTGACGGGCCGCGCCGTCGGGCTGGGGTTCGTATGAACCTAGCTCCACGCCAAGACCTGTCCCTTGTCATAGCCAACAGGACACCTTTCATTATGGCGCTACGCATATGCGACTGTTCCCACAGTACAATGATGTGCTTAACCATCGTCGCTCGGCGATAGGGTATCTTTTAACTATCTTAGACTGATAACGTGTGGGCCGATAGTATCTAAGAGCTGTGAGGCTTGTGGACCACTTAAGCTATCGACTCCCATTGGGGCAAGTGATTACCCGGGTGCTATCCACAAGACCGCACTTGTGTCCTTGCGTCGAATTAAGAAAGCTATAGGGACCAGCTGATATTAGGTGCTGAGGAGTGCTACCGGCCTGTTTCACATATATCAGAACGCGTGCCCTGAACTAAGACTCAGCCAATCAATACTGCGTTGTGGGTGAGGCAAGTATTCTGTCCGGGGATGCTGTGGGGGACAGTATTGATCTATCATACTAACCATAGTCGACATCTCAGGTTTTCGAGCGCTGACGGTACAGACAAATTTAGGGCGCTACGAGAACAGCATGTCGCTTGAACCGACTCAGGCGCCGTAATTAAAGGGAGTGTGCCATGAACTCGAACGCACTCCGTATCAACGCAGAAACCGCCTTACAACACTGAGCGGCGTAAGAATACTCACTCGATATTGATTACCGTAAGGCTATATTTGTATGGGAAGCAAACGAATGACATTTCCTGGATATACAACAGTTAGCTGAAGGCATATGTGATCTGCCAGATTCCAGAAGCTGATGGGGGATCAGGATGAACCTTGTCAAGGATTCGTATCTGCGCTATACAGGGAGAACTGTGCGATTGTTGAGGCTACGCCGCGTAGCCGAGCCGTTGGAATAAGCAGCCCCGTATGTGGACATTGGTAGGAGCATATTCAACGGGCCACTAAAGGTCGCAATTGCCTACATCAGATGAAAGGTTGTTACCGGGCTAATATCATATAGAGCCCTCTAATATCCACCTGTTGTATGAATACTGTCGCAGTTTCATCACGGTTGCCCCTAAAACACTGACAATTAGGTAGCGGTATCGTTTCTCATCTACTCAAGGCGTATCTTGGGTCGCAGGGGCACGCCGTGGTTGCTCGCCAGTTACTTACTGACGGTACCTCTCATTCTTTGCAATGCGGGTACTTGTGTCGTTGAAAGTAGTGGTACTCGCACCAACTCAATCTATCGCCGCTGCGAGACAGTCCCCGAAAGCCTCCGCAGGAGCAGCGCTACCTCATATCCTGCACGATTAGAATTAACTGCCTCTCTAGAAACGAGTCTTACTAGTAGTTGACCTTGATTCTACGGCCACTCGCATCGCGATGATTGAGAGAGTTCCCCCACTCACGAATTAGGAATCATGTTGTAACAGCGGTCCCACTTGAAGGTGAGTCTCCATACAACGAGTACATACTTGAGCAAGCAACTAGATTATCAAGAACACGCATTTGTCCTAGCCTGAACCACTTTTGGTCTCACATAAGACAGCGGGAGAAGCGATTCGGGCGACGAGGCTGTTAAGTCGCACGGGTTGCTATTCATATAACACCTCGCCGTCGTGTTAATGATGTTTATGAGCCCTGGCGGTATGCAGAGCTAATTGGTTTGCATGGGTGAGGTAACTTGATTCCGTGTTTGGTAACGAGCGGCGGTCTACACCTAGTCTCGGCCATTGGAGACTGTTCGCGGGGGTAAAAAATCAATTATACAGACACTCTAGCCTGATATTGCCCAATTTGAAAGAGTTCAACCTTTCGCAGTTTAGTGAAAACTGAGCGGTTTGTGAATATTACCGGCCTTATATATCAAATGGTCCGGTACCAAGCCTAGACATGTTCGGACTTTGTTCCGTTAAAACCGAAGATTACGCATTTCACGACTCTAATTTTTAGAGACTGCTAGACAGGATATCAGCCACGGTGCGTCCTTCCTGTATCGCTACGTACTTGCTCCTCGCCTTCTATATAACGGTACTGGCGAATTCGACAATACCCTAATAGGAGCTGCGACTCTCTAGACGACCAGCATGCAGCTTATTTAAGTCACACAGATCGTAAGAGAGCCTCGCAATGCTCAGGATATCGGCTGCCCTAGAGGATTAGCGGGAGTTGGCTGGCGCTATTGAATGTGTCAGCAATGGCAGCTCCAGGATCGTCCAAGGAGCCGCTGGCCCGTCCCATATGGTCTTCGTATCAGTACTGATCAAAAGGCGCAGCGTGAATACGTTTTCCTCCATAACATCCAGTCAGTAGCTGATAACGTTCTCACCCACTGACTTCCAACAGGGGCGATGGACCTCCAGGCGAGCCATGGAGTTCGACCCCCTTGAGGTATAGTGTAGGAACGGGATAGGGATTAAAAGCGTTGTGGTTTACTCTAAGCAGGCTAAGAGGCATAATGTCAGCCCATCAGCGCGCGGTTAGACCTAGCCGATTGGCGCGTACCAGGAAGCCGTCCGTCAATTTACAATTGGTCGTAGTCGGGATACCATCTTGAAATGCGTAGTCCGTCGCGCTTCGGGGCGGGCTTGAAGTGATCATGTGCACCCTTTAAAGCCATAGCCATACAACGTACCTCGGCTGAGTAGGTTCATTCTCTTGGAAAAATACCGCTAAGATAGAGACACCTAAAGTAGGCTTCTTAATAGGGCATGTGTTTCCGCCTAGCGTTGACAGCATGTGATTAACCACTTGACCATCTGTCACGCGTCATGCTGAAGCTTACCCTTGATACACGGGCCGTTATCCAGACGCCTGGAAACTCGAATTCATCAAAATTCCTCCATTTCTCTGGCCCGACAATCCACTGTAGAGTGGGATCGGTGTAATACCCTGAGTTTTCTAAACGGCGACACCGATGCTATTTAGTATGCCACGCGCTTCGTACTGTTGAAAAGCAATTGGAACTGGATGTCTATCCCGTACTTTTAAGTCGTAGATCCGCATCATCCATGCTAATCGTCGTCATTAACCAATCACAGTCTTGCAAAACTCACACTGGCTGATTCTGTCCAGCGTTACCGCGGTCCTGTCCGATTAAGAGCTCTCTCTGCCTATTTTCAGTTCCCTCCAGGCGTAAGGGACTTGATGTACACGCTCGACGAGTGCGGCTCTCTGTGACTAGAAGTCACTCGGGCTAAAGACGACTAGAGGTTTTTTCTGCGATCTAATATTAACCATACCCTACACAAACGCCCACCGGTTTTTATCACAACCACAGTAGCGGAGACGTAAATCGTTGTGCACAGAAAGTTAGGTAAGTTATCAAAGGGCAAAAGAATTACAGGCCATATTCTTTCAGTGTGACCTTCGAGTGCGGATTAAGTCCGCGGCAAAAGCCTAGGGACGACATCTTAGTAGGCTATCTCCACCCAGTATCTGGCGCGACGTCAGGTGTCAGTACCATGCTCTTATGCCCCCCTTTAGGTCCACCTAAACCTCGAGGTCAGCGATTGACCCATGTAGATACTGGTAGACGTCGGTGCCACTATGATCGGTTTTGTCGTAATTCACGAGCATTTGTCATACTGTTAAAAACAAACTTTGACGACAGTTTGGTGCTAATTTGAGAACCCACGTGGGGCCTGCCGCCGGATGTACGGTATGATCTCACCGGGTCTCCATGATACCCCCATCAGCCTCATGTTCTGGCCCGTCCTCAGAAGCGAGCCCTCAGTCCTTTCTTAATCTCCAGTTACTTATCAGGGAGTCCAGAAGCTCCGGGTCTTCCAAATCCTCCGCGATGTCTTAGTCGACCCTAGGGACACCTAGAGGATAGTCGCATTTGTAGTGTAGCTGTCCGAGAGCAGAACGCTGCGGCAGGCGGACATTGAAGTTACGGCTATGTTATCAGTCATCCTCGGCTTATAAGAGGCTTTGGGAAAGTTGGGTTAGTGGCCCAAACGATGATATGCACCAACCAAACTCTTTCACAGCAATCCCGCAGAATAATTGCGGCGCGGACGGGCGATGGGCCAGCTGCTAGGGTAGCGACAAAAGGACCACCAGCTTCTGTCTGAAGACAGGGGCCACCAAACCACCCTTGTCCAACTCGTATGCCCGAACAAGAATCGAGATCGAATTGTCACTTATCATCCCGTACACAGTAGGTCCCGTCTACGTATTCCATGCGTTGCCGCAGGATTTACACCAAGTTTGGTCGCCGGTCCTCACTCGCTAACTACAGCTGTGTAAAGCGCGGGCGAGGGCGATGGATACGTACCCGAGGTTATCAGGCTGAATTGCTGGTGAACACTTAGGTACGACTCATACGCGGGAACCCGGCAGAACTGTACATTCGCGGTTTTTGTCCTCTAGCTATTTCCAAGAGCTGATCACGAGAAGTCTCTCCACGCACATCGCTGAAGCGAGACCCTCGCCAAGTGTTACTTATAGACTTCTATGCGGCGCCGCAGTTTATACAAGTCCTCGCTCATTTTAGTTCGTGCCTACACTGTCACTCCTACGGTACGCCACGGCGCGTGGTGATGAAAAGGTTTCAAGTTCGTATGCGAAAATACAGGCCATTTAACACGGACTCCGCGTCCGGGCGGGGCCGTAACGTTGCCCGTACCCGGACAGTTCCATTGTGAAGCACTACAGCTTCAATGTTACCAGTGTAACGCCAGCATCAAGAGTTGCAATGGTCTAAGCGTCACCTGAATTTGCTATATCCTGACGAGACAGGCGTGCCCCATCGGAATGGCAGAGGTAATAGGCAGCATCCCCGCGGATCTTCATCTGCCGTCAACAGCGCCGGATTTGTAGGCGGGGCGATAAGCACTCGGGGGTCATCGTTTAACCACCAAATGGGCAGTACAGGTGTTCTCCTGTCGGGCCGTGTAGTTAGTCCGACAGGTGAAACAATCTGTCAACGAATTGGCGCCTACTTGGTCCGCCAAGTGAGATCTTGAAATTACAAGTCTTTCCGGCGCAATCAGGCCACGATACTGGGGCAACACCCCGTTGGGACGACTGGTAGAAGCTGGACTCCGGGACGGGGCACAAGGCGGTGGCGGCATTGAACAAGCACGCACGCTCCTCCCTATACCTACGACAACTTAATCTTGTACCAGACAACCTAGGAACCCTCACTTGACTGCTCAAGCCGAAGGGAACAACCCCCTCCTTGGACAAAGCTTACTGCCAAGGCTTGGTACGTTCGCGTGTCCGTTCTGTATCGGGACCTGAATTGCGTGTGCAAGCGTCATTCGCCATGGCCACTACGGCCTGGTCGGGCACCCAGTGTGATAGTCTTTCATGCGAATGGCCCAGTGCTATCTTGTTCGCAACGTTCTGTAGATAGAAGGCCGTGGAAACTGCGACTATCCGCCGCACAACCTTCACGGGCCCCGCCTCTCTATGGAATACGTGTCTCATGTGCGATATGAAGGTTGCCCTCCATAAAGCAGATAGTGCTGATGTGCACTACTTGGTACTTTGAAACGTATTAGTCTCACCTGCTGATGATTCCTTTAAAGCGGTGCAAACAGCCTTCGGCAACCGGCCACTTAGGCTCGACGGACACCATGCCTTTCTGACTTACAAAATGACCACGCCCCTACGACAGCTGTCGTTACCAGGCATGCCTTAGCTGTCAATGGCCTCATCCTGCCTCCTAAGGGGGTATGTTCTCCTAGTAGCCATGTCATAAAACCCCTGCGCGAACCGGGTTATCTACGAACAGTACGAACCCGGCGCGTTTTGGCTAAAGTGGGGTTTATTCAGGGAGCCCGACATACCACAACAGTAACAATAACACACTAGCCGTTAGCAGGGTCGACGTGCTCCCGTTCGCGGAATTCTGGCTGAACGTAAATACGCACCTGAGCGAATTTTCCACGAACACTTTTACATTTAACCTCGCAAGAACCCTCCCCATTAACGAGCTGAAACGCTTATAAGAGAGACGTAATGGATCAGAGACCATCTCCTCCGAGAAAAGTCTGGGTGAATCTACGATGGGTATCTGGAACCATGCTCCCTCTTCCGGAGGACAAGCCCTTAATATGGAATCCAGACTAAAGCCATAGCGATAGCAGCTTAAAACTAAGGCACGTCGTCCAGCCAATTCGGCCGATTCCGTTTGGAACAAATAGCTGTCTGTCGCAAACACGACCCTGGGAAATGATCCAGTTCTACCGCACCAGAAGCGACCCACCCCGTATTCGCGTGGTGTTCCTTCAAAAACGCCCCCTTGGGACTCAAGACGTCGAACTTCTTTACCCCAACCGTTGCGGGGTGTGTGAGAAACCGCAGACTTGAGAGCCCGAGTATCTAATGCAATCTTCTCGGGTAACAAGTGTGTGTACATCGTGACAAGTCCTTGGGGTCTGCAACAGTATGTCCGACCAATTGACCGACCGCAGCATAGTCACAGTCGGAACTTTCGGAGTAAATCCAGGAGGGACGCCTATAAACTGTTCATAGGTGTCAAAACGGGCAAGGAACCAGAGAATGATTCAGTGCATTCGAAGAGTGGACGCGGTAACGACGTATAAATTTCGCGCTTCCAAGTAACACCGGACCGCGATGTGTGTCGCATACCGAGTTCTATGACGATGCTCAGAACCTTTGTCCACTAGCTTGCAGTTGCTCGACCCTAAGCGTTGATCCGATCTGTGTACAGAGAATCCACTTCTTATCTCCGTTGGCCATTAAAGCGTTTGTGTCCAGATCGGACTTGGTGTGTATGTAAGGCCTGATACCGCACAATAGGAAATATATCGGCTAAATTAAATTGGCTCGGGATAGCCGATCTCGAATACGTCCATTACTGAAGCAATTGGTTACGCGCGAGCCCCCGATATTGACTTTGATAGTATGTCCAAACGAGTACAAACTCGTTGCACTTAAGTTCGACTTGCTATGAAACATAGCCGTAGGAGGATTTAACCGAGCCACAGGCGGCTCCCTCTATCTTAGCGGATCGAAGTAATATTCTGTTGTCATGGCTTTAATTGGGAAATGGCTGCCGAAATTTGGCGAAAGATAACAAGTTAATAAGGACAATTCCTACTTACCAAGGGTACTGTTTCGCAGCGGCCGGTACCCCTTCGAGACTCTGTTGATTTTCCGTGTACACAGGCAAAATGCATTGTATATCCCGGATTGCTTGCTGCTCATTAGGACTTTACGACATTAAAAGCAGACCAATTTGACCCTGCTAACCATTAATCAAGGTAAGCTGCTCTTTCACGGAGAAAGCGCGTGTCTGACTGAGTATATCTAATAGAGTTAGCGTACAGTAGTGATTGGCATGCCATCGAGAGGCGACGTCCGTAGTCAACCTACACCGTTAGAGAACCCTTGTCCGTTGCGCTTCTTAATCTTTTGTCGAAGAGGAAGTACATGAAACAATCAGATTAGTCATTATGATACACATCGGTTGGGACACATAATCTTTGCGCTGCAATACCCTACAGTGGCTGAAAGAGTGGCGTTGCACGCGGACTAGGATTACAGCGTATGCCCAGGTTCTTCGTACGTTCCTATGGGGCTGTAACTTAGTATCAGACAAGCTTGCAGACCCGCGCCCGGGCACCGTGCTATCTGAGTGCATGCCGTCGGCGTAAATCGTGCAGTGATTCTCCTCCGCCAACGGCCCTGAACAGGGTTCGGTGCTACTACTGTGGCAGGGGGGCGGGGTGTATGTTACCACAGAAACTCCTCACGCGGCAGGGTTGACGGCCAGTATACGCTATGAAAGCCAACAATTGGAGTATGAATCCCTCACGAGGCTTAGCGATGCAGAAGCTCGCCTGTAGACTAGTCTTACTAAGTGAAGAAACGGCGGACGAAATCGAACGTGGGTTATGCTTCGATAGTCAACCATGAGTGCATTCAGTAGAAGGTCGGAGAATCCACTAGTGTGCGGAATTATGGGTTCCATCTAAGGCGAGAAGGGTCCGGGTTAAGTCCAGCTTCTACACTCAGCAGGCAAACATACGTGGCCAGGCTCCTTTTAATCCCCGGAACAACCCACTCCGGGGGGTGATCCCGAAAGATAGATTTATGTTGAACAGGCAGTAGCGATTAAGGCAGCGGGAGTGGACATATATAGCAGCAAATTGTCTCCCTCGGGCCTTAGCTCTAGCTCCGGATAACATAACATACCTGTGCATTTGGCAGCAATGATGATCTTGTGCTAGGTTGACACTGGTTGGCGGACAACGCCCAGTAATGGGACTAGCCTGTGGCAGTTTGAGTTTATGCGTGAGCACAGAAGCGCGCCCGTCTTAGGTTGCTTCACACGGCCGATGTCACGGCAAATTGGTTTCCAGGGAGAGATTGACGTGTCTTGAGTCTACGCTCCGCGTTAAATACACCCCCCCTGACCACGTTATTGTGCGTTTAGACCTATTCAGTTCAAGGCGTATGGGAAGACAGCCCGGTAACGTCGCGGTCGATGTCGAGATCCGCTGCGCCCGTTCATAGACGACAGCATGAGAATCGACGGTCTTGAACGTTAACAGATCTCAAAGTCGTTAGCATAAAGCTCCTAGACCAGTCCGTATTGCCGAATGTCCTTAGACTGATAGGCTATGGCTGCAGACTCACGCATCAGCCCGGCGGGGCTTCAGCGATCTAAAGCTCGCTCCGAAGAAGCTCGGGATACTCATGAAGGGAACTTGAAGAGTTATATCGAGCACGGAGAGACCACACATAGCCCTCTATAGATTTGTAAAATTAATGGGATTGTCATACATGCCAATGGTATGGGCTAGAGGCGCCTGTCAGTCAAGGAGATAATTCGTACTCCCTTACTTCAGGGTAGATCATGACAGTTCAGGTGAGGTCATCTAAGGAAGGTCGCTCGGCTGTAGGGGTCTCGTACTATTAGCCCACCTCAGCCCATCCGCCTTCACTCACATATTGTCCCCCTGGCCTAATGGAATGGAAGGTGATCACTCTGGTCGGGTACTACCACCATATAAACACTGTCGCTGTAATTGTGTTCACTAAATACGCAGTCGAACGTCCCCAGTCCACGTCCGGGTGATGCCGCGCTCCCAAGTGGAAGGTATCGATTTAGGAACCATCGATGCTAAAGACTGTATCCTCGTACGCAAGCTACCGGGAGACACCTCCGGGAGCAGTGTCGGTACGCTTGTATAACATCCTGACAAACGCGTTAGTTGAACATTAGGCGTGGTTCGCAAGGTCGGCACTCGCTGAGACAGTGTAAAGTTTGGCGGCTCGTTACCATGAGGCCTTTTCTTCTTGGGAGGTGTTAACCCAGGGCGTGTGCGTATACGCCATAGGACTTACTAAGTTGTGGACACCCGCCCGCCTTTTTAGTTTTAATAATGCCCGGCACCTTTGCTTATGCGATTGCAACATCGCTGCAATAAGCTCGCTGGACGTATCAGGCCGGAGAGATGCTATCGCGAAAGGATTCTTCCCTCGCAGCGCGTTCGCATTCTCATTTCGATAGAGCTCGATTAAATCACGGAACGCTATGGAACAAGTGTGCGGGACCGAGAGCAAAACGCACGTGTTAGCACCTTGACCTAAGAATTGGGGATAAGGATAGCCGAATGAATGCACTTAGATGCGTTTCGCCGCCTGCTACCACATGAGCTGAGACAATTAAGCGCAGAGATCATTGGTGCTGTCAGTGCAATACCTATGTGGGCCGCCTTACACTAATTAGACATCCGCAGCCGCCCATTTGCGGCACGAAGGGAAATCCGTTAAACACGGGCCGTCCGAAGTCCACAAGCACAGGTCCGTGACCCGTGATGGTTCTGTTTTAAGCCATCTTTTGGCGTTCTGTCGATGAAATGCTGAGGTTAAGTGCGCGAAGTTGCTTTCCGACGAGACTGCTCCCTGCCGGCGGGTACGCGTACAGACCCTGCGGTCCGCGGGCGTGATCTAGATTACAATCGACTTCCACGGCTATCATCCGCTCGTTTGCCATGGAGTCGTGACAACCAGTCTAATTGGGCTATATCCCCTGCGCGAGTACTCCGCGGCCATAGGTATGCGTTACAGCGTTCCGGAGCAAAGCGTGCACTAGTATCTTCATCCATGAGGTAGACGATAGGGGTGGGATCGACTCCGATGCTAATGTCTAAAGTCCCCACGCATCTATGTCCCATAACACATGACCTACAGACCTTCTGTTGTTAAGATGGTGTTTGTCCGATGAATCGTAAAGACTAGCTGACTGTACGCAAAACTAGGTAGTTTGCTATCTGACCATGGGGTTTGTCTCTGTCGCTTTCGAGGGAATAATTAAAATCCGGGATGCGGGTTTCAGGCCCACGTAAGGCATCCGACTATCTACCAGCATACACGAAATTCTAATGAGTTCAGATGCCGTCTAAACAGCTGATAGTGACTGCCATCCTAGAGGACTCGCTCTCTTCTTACCCATGAGGTACTGCGTCGGAGTTTACTGACCACCCCCGATCCAGCTAGAGCGATGAAAATTAAGTTTTACGGACCATAGATGGGGTGTATACATTTACAGCAAACAATCGCCCTTGCTCCTAGAGTTACATACACGCCTCTACGACCTGCCAGCCAGATCTCGTTATCGCCCAAGGCAAAACTTACCGCAACGCGAGGCGCCTTTGGGACGGCTGCCTCATATAGCCGCCATACACATGACTACCCAGCGACGTTTGTAGAAGCAATGGGCGAAGAAGGACGATTCGAACACTATCAGGGGTGCTTCAGTTTACCTGGAAACGACTTTCTCTGCGATCCTATTCACGTCCTAAAGCATCTCCGCTAACTCGACGTTGAGATGAGCCATTGGGCTGGAACAACAGTCAGCCCCTAATGAGGCTACCTCGACCAGTTTTGGTTGCAAAAATCCGCCCCCGGCTCTCCTTACAATTTGCGGTCGCTTAGAGACATCATTATTCACGGTCGCCAGGATACTATCCGTACGAGACCTATGCGTAGATTTAAGACCCGGCATTCGTTTTTCCGGTGTATAATGATTCAGTTATTACCATCCACATCTAACATCAAGAAAACCAGCAGGCGTGATCATGCTTTAGGTGGGTCCAGAGGATGACGACAGGCTCTTGCTTATATCCGTTCACTACTGAGGTCCCGGGGACCCACATAAGGAGCATTGGGCAAAATACGATTGCAGAAGGCCGCAGCTGTGCTACCAATTCTTGCCTTCCGTGTTTCCCTATCACTAAGACACCGCAAAGTCCATCAGGTCGAGTCATCCCCAGCCGGCAAGCAGATTCAGGTAACTACACAACCTCACTGAGCAGCGACGGCTTATAAAAGTGCGTTTGTTAACTTAGGTCTTTAGCTATAACACGTGGCTGATCACACTCATTCATGTATCATCGGCGCTAATGCGGCAAGTGGGAAGACATCTAACCGAGGGTAAGAGTTCTATCTTAGATCCGGTATCGTCAATACTGACGCGAAGTTGCGGACCATTGGAGTGCGCGCCCTCTGGCTGTTTGGGGTATTTCATCTCTGAACATTTAAACATGTAAGAGGTTGAACGATCATGACTCCAACCACCTTCGCCCAATTCGAACAAGTTGACGGCTGTGTGAGACCCGCTCATAATACCAATAAAGAAGGTCACAAAGCTTAGTGCACGTTTGATGTGAGCCCAGCCCGCTAGAGGCCTGCTAAAACCTGCAGCTGACAGGGGCGCCAAGACGCGAATCTGTTGTATTACAAATCAAAGAAACCGACACATTTTAGACTACGAGCCAATTACGATATCGGGCCCCCTTCCCGCCGAAGAGTTTGTCAGTCACATAATGATCGTATGTCACCGATACCCCTGGAGGGTCCAAATATAAAGAACAGTCATTTGTAATCTGGGAAGTCACAACATTTATGACAGGAGATAAGGTTATACAGCCTTGTAAGACTCGGATGATAATATTCGCTCTATTGACTGGCAAGTATTGCACTATTTGCGAATTAAGAAACACCCGCGTCCCAGCTCGACATTCCCGAGTGTTCATTCCTGGTCCCATCTTGACCGGGTATGGACTGGGTCCCTCGATGAGCACCTGTTCGCTATCATGTGGGCCGATACATCACACACCCTGATCAGGGGAGTGCCGAATTTGCACCGTCCCTGTGGCATGAAGTCTGGTGCACCGGTTACAGGGATAGCCATTGAATTCAGGGCCTCAATGTAACCAGTAAGAGCAGTGGGAAAATGCTGATGAGTTCGGATGTGGTTGTAGTCGACAGAGTACCGCGCATACTGCTTCTTGAAACGTCCGCGAGGGGATATTCGGCTTGTCGGATTATGGAATGGGATCTCATAAATCTTGAACAACGAAACACTACATGCGTCCCGACCGTGCCAAGTCATCTATTTAGAGTGCGCTTAATATATCGTAAGTAAGTCTGCAACTGTTCCGGCCGTACTTCTTTGTGGTTCCTCACAAGTATGAACCTTGTGCTCTATTCCACCGGAAGCTGGTGACTGCAATCAGAAATCGACCTGTTCTCGTTGACTGCACCGGCTGACGCTTTCATACTCTTATACGGACGAATGAATTTCTAGTTAAAGCCGCGACTTTTTAGGAACAGAGGCACCGGTTGTATCTTCGCCCGGAAATATTGGCAGCTTTTGTTGCGTAGTTATGGCCATACTAATACCGCCTCATTAGTCTATATTGTTTGGGCACATATATCATCACGCGTACCCGCCCAGTACTAACCATCAATGGCCTCTACGAGACAGTAACATTTTAAGACCCTTAGGCGACTCAAGAACTTCTCAGCAGGGTTCTCGCGAGATCATAATCTATTGCGGTTTCGTTAGCCATAGCAGCATCTTGTATTGTTATCATGTCAGGTATGCGCTCTTGTGAGTCTGACGAGTGGTCTTGTTAGTCGTTTCCACCGCTCGATGCGAGTGAGTGCAGACCGCTTCATGTTCTTCGGTTGCTCGGAATGGAATGGGGCACGCAGGCGACAGCCTACCGTCCTGCCTGGCGAGGGAAGATTTCAAAGTCGCTACCCGATCTGGGTTGTTTCTTAGTCGGCATATTGGAGGCGACTCATAGAACGTTACTTCAAACATGGGATTTCATAGTTGTTTTAGAGCCGCAAGTCTTACCACACGATTGCATAACTTGATCGTATTGCGAAGTTTGAGTCCGGGAAAAACAAACCTCTAATGATAACACATGTCTGCCGGACCGCCTTGCAGTTATCACCGATCCGGTTCATAGGGTAGCCAGCTTCGTATGACCTTAGCCGATTACTGTAGAGGCAATTGCAAAGTCAAAGCGAAATTCAGGCGTACCTCAGACATAAACGGGAACCTCGTCAGCAAGATCGGTAGACCTAGGCTACTCACGCAACGCGGTATATGCCCGGTTACTACGCGACCACGTGATGTAACCTGCTGTGTTTTACCTGGATTGCCATCGCTGGCAGATAAGTATACTAGTGATCGTGTATTCAGCATTAGGTTATTTAGAGAGAACCATCCACGTCAGGTTATTGTTGTTAACATCGTGTCGGGCTCCATCAGAGATATCCGATCTACCCTGTCAAACAACGCGAACACGAGACAACTTTTTGCGGCGCCATTTGGCCGCCAGCCGCAGGCCCCCAACGTGGTCTTACCATGAAGGGGAAGCACTTGGACGTGAAAACTGAGGTCGATTTAAGTTTGACATACAGGACTCTACGATGACCGTCGGGTGACCAATAAGGATCTACATCTCTATTCTTCGTAAGCACTGATGGTAGTATTCCAGTAATGGGAAGCTCGCAGCCACTTTGATGCTCGTTTGATAAACCCATATCGACCAAGAGCCGGCGGAGTGGCGGTTTGCTTATCAAATTCATACAGCCTGAATTTGGTTATCAGATGCCTAGCCCCTGAAACTGCAGCTCAAGCCACTGACTACAGAACTGCCCGTTTCGCCGACATCAACCCAATCGAAAGCACGATGAATCTATCGGTAAACTCATCTGTGGATCCAAACAGCATCGTTGGCCAGTACGATTTTTGCATCCCCTAGGACAGGCGGGTCCCTAGTCCGCTTGGGGCCACAGCTTGAAGGATTAGAACAAGTCGTGAGGATAGTGTTCTGTACTTTACTATAAGTATTTCGAAACTATCCATCCGTTGAGTTGACTCACGTTCGCCATGGGTTGAAGATGGACCTTCAATGGTCCAGTGTGCGCAAACAACCAGTAAACGTATTATCGTATCGAGGCAAGCGACTCATCACGAGCGCCTCGTTGGGATCGCAGAAGGGACGTAGCTGGTCACCCCAGCTATTAGGCCTGCATTGACTGCCCGCGTCATAATTCGTGCGACAGTCTATCCCGGTACTCAGCTCTACGTTATGTCTACTAGATACTCGGCTCTTGAACACCTTCCGGGCAGGACCGGAATCCCTGTTCCAGTGCACGATAAGAAGAGTTTTCGCTTCACTGCAATAAGCCGTCGATTACACAGTGAATGTAAAAAGAATGCTGGCCATGAAAGCATTGTACAACCATGCCCAGGAGGGTCTCGGACTCTCTTTCGGACAACCGCACTGGCCTTTAGAGCTGGAGCGCGGGTAAATTTCGAGGGGGTCGACTTTTTCGGGTGATTCCATATAGGCGATGTGAGACACGGAACATAATCTGTCACGATGGATGTGCCCCATGACGACCGTTGACGTACGACAACTCGAATTCAAGGAGAGTAACGAAGCGTCCGATATAAAGTCCCAACGTCTTCCGAGATCAGACCCAACTAGAGGATAATATTGTGCCAATAAAGACAGGCAAGCACTGTGGGGTCGTCAGTTTTGAGCCTAGCGTATCGGCAGAAGCTCGCAAATAAAGTCACTGACGCACATCGGGTAACGTGGTGTCCCACTGGATTTAGCATTGCAAACGGCTCAGAGCGCGGGATATCGGTGTTCTCGACTCCGAATGTATTGAGTGCGCCGAGTCGCCTCGTTATTCCGGCTTGAGTCGCTCTCTTCTGGGATATGAGAGAATGCTTTCAGCCTCTGACATCCGCCGCGCATGAGAGAGAGATGGAGATAATAGGGTGTTGGCATTCGGGAAGGGCGCTCAATGCCTACCTCGTTCCCCTCATGGTTCCCGCCTTAACTTGAGGAAAGATGGCCAGTGTACTCGGACCCCCATGGAAGATTGGTTTCCATAGATTCCAGCAGACCTAAACGCTATCTGTTCTAGCACGTAGGTCGGAAAAATAACGGCCGTAATTGGTTTTTTTCCCAATATTACAGCCCTTCCAGAATTACATATCGTCACCAACTAGGAATACAAAATCGAGCCAGATTCCCAAGCGCTCCCATATCTTTATGGTGCATTGATATAGCCAAACCCTGGTAGAGAAAGGGTGGGCGTACATCGTCAACTCTTCTGAGTAAAGTCTCAATTGAAGTGTCTTACAAAACGATCCGATATCGACTAGCTGTCTCTGTTCCTTCGGTGCCCAATAGACATTGACAAGCTGTGAAATGTTGGTTGCACTAACTTTGGGAAGCTGCTACAAATGGCATAACGATTACGACCGACGGGCTCTATTCCCTTTTGCCCCATCCTACTCTGTTATCTCGCAAATCCCAGATGTGCGACCTCCTAAACAGACAGAGTGGTGTTCCCGCGATCTGCTAGAAGCAGAGTGGTCTGGGTACGACGTACCTTCCTCGCGGAAAGTTAACGGGAGGGTTACCCTCCTGATTAAGCTCCGCGCCCTCCATCACGACTCCGATGGCCCGTTCAAACGACCCACTCGATATAACAGGACTAAGCCACCGCGCAGGGACCGCTTGACCTATCGCCAGCCCGTTGCTGGGGGAGCCTTGCTTTGCAAAATTAAGCCCCGAACCGGACATTTGCGGTCATTGCAAGGGCAGATCTACTCGTAAAGTTTCCCAGTATCGAATAGCTACGAGTAAACGGAAGCATAAACGCATCAGTTATTCCGGGAAGCTCTCTTTAACGTTGCTATCTCGGTATTAAACTCATTTTTGCCTCCGTCACTTGACCCCACCGGAGACAAAAGGAAGCGCGCTCTAGGCGAGGTATCTACTCGTAACCGCATCCACCCGAGCGTGGGTATTTGGCCTTGGTAAGGAATCTATCGATCATCTGACACGCTACTCCGGCTCTAAATAGCTTCGTTACGGGGACTATTCACAAATCACTGGAACCCATCTTTGTAAAATTGGGGGGCTGGGGCCATACTCAGTAACTAGGCGGTTTCGTTATCCACAGTAAGCTAGCTTGCCCCTTCAGTACAAGATTCAGCACTCTATGTCTCATTGCGGGTGCGGTCCTGAATGACTGTATTTCTCCAAAAGTCCTCTGAAGCGTCATCATCGTCAAGCTCCTTATCCTCTCTAATTGTCAATATTCAGATGTTGCGTCCATCGGAGCTCGGTATGGCGTGATAATACCAGAAACGTGTTAAATGAATGCTGACGGAAGCCGTTCGACCATTCCCCGAGAGTGCATGGTCGTGTGGCGACAGATCCTTCATTTACGCTACACTTTTGGCGGTTAGACCTCACCTTCCAGGTGTGTCGTGCGCATCATTCGGCGCAAATGACAGGTTTTGCCGACTTGACGCCCTATCCGTGGCACCCCCCTACCTTCGTGAGCGTTGGCCCTGCGGCACTTCCCCAAACCCTGTACATCGTGGGAGATCAGAGACACTCATAAGTACTAGCGTTGGAAGAACCGGTGTTGGCGGGTGTCAGCTCTCTCTGTATCACATACTCTGAAGTCCTACCAAGAGGGACGCTGCCTACGCTACGCCCAGGTAAAGGCATTGGACTGCTTGTTTTGTTCGGCGTCGCCCATTCACTACATCGTACCCAACGGTCTAAATTGTTGGACCAATTTGTTCCACGGATGGGCGAGCTGCATCACCTCACAGCGGAACCCCTTCATAATTCGCGACCTTCCCGCAAGGTGAGGTATAAGGAAAAAACGGCATCCCGTGCAGTCGCGGACCGCCACTGGACAGGTTCTGAGTACTAGATGGGTGTGGCCGAGAAGATCCGGACTAAAAGTCGCCTCAATCATCCGTTACCAGTTTCTAAGTGTATACGTGAGCGACACATTAGCTCTGGGTTTCACCACCAGTCGAATGCGTCAATTCAAAATTGGCGTCCTCGAACACGCTTTACGAGCGATGCTCATCGCGACACTCCAGTCACTGTTAAAGGTGTTCCGTTAAGGCAAGAGCAGCTCCACATATAACGTCCATGACATGTTGCAAGCCTGCACCATACGTCCTTTTGGAGCCGTAACCTTTTCCGGAAAGAGGATTCAGGTCAGCATTTTAGGTCTCTATTAGTGACATTGCGGATTCGCTCCGTTAACTCAAGGCCATCATTTTGGGCACTCCTCGGAGGGACCTAATTTTACTCCTACTTGCGCGATATCGATTGACAAAAGGAATTGCGTCGTATTTTCCATTGATATAAAGTGTACTTACGGCCCTGACATATCCTCACGGACACGCCAACACCACCCCGCCGTTCACGTGCCCCCCCTCGGGCTACCAACTGCACAAGGGCACCACAGCTTGACATCCATCAGCATGTTTTTTCGACTATTCGGCCGGCACCAAGTTGACACCGCCGACTGACGCGTGGTCAGCCGATCAGAGGCACCAAAACGGGGTCGCATGCCTTAGACGAATAAGGGTGCCATCGATGTTGGGTATTATTACCGAAACATTCGGATTAATAGTTGAAATAAACCTCCTATTCCAGAGTACTTACCCTAGACCTCAAATAAGACCTGCGAGTGGATGGTCTTAACAATCGGGTGGTCGTGGTCCCGGTTTGGGATAGCGAAGGATAGGGCAAGAGGACGTCCAAGTCCAGTAAACTCCAAACAGCACGCTCACTCTAACAGGGCGGTGATAATGGGGTAGTTAGACGAGCACTCATCGAGCAGTACTTGCAACTGTCTTTCTCCAAGCGACGCTTGTCCCAATGGCATCCGTAGACGACGATGTGTCGGTCCGCCGTCAGGGGAATTATTCGTATGATGCCTCGAGTCGGTCTCGGGAACTTTTCTCGGTTTCCGGTTCCTAGGGTTGCATCCCTAGGTCCAATAATCATCTGTCGTGAAGGGGCGAGTCCTTCGGGGAGATGCTAATTTCTATTGGCCCCAACCAATTTTATGAAGTGTCGGGCGGCGATGTAGTAAAATTTATTTCTATACCATGAAGAGTGCTCAAAGACTGATCCAGGTTCTGTCAAGCTTTTTCTACTATCTATGAGACCCTAGCCCACTATGCACTGGATACGATAACGATGCTAAGGACTACGATGATGCGTGCGGGTATTTACGCTTTGTTGGTTACCATAACACCCACAACGGATCTCTTATGGTTCTTTGATACTTTAAGATCCTTACAATATATCAGACATGTCTACAAGCCCATTCGGTGAATTCTTTTCTCTCTGAAGAGGGTTTTGGCGTTCAACCGGGTATGCTGAAAAGCGACTAAAGTTAGCGCGAGAAACATTATAACACAAGCTCGCTGTCTTAGCAGGTCGGGCTATGCCCAGGAGGGGAACGATGATGGACACGTGTACTTGTGCGACCGGTCATGGACATATCTCTCCGTTGGAGCGTCCGTTCCCAAATGGAGAGAGACTGTGACAGTTATCTACAACTGCCGGTAGCCGTGCCCACTCCTACGGTACCGCTAGTCACAGGGATAGCAGGAAGTTAGTCCCAGTTAGCCATCACGCGGAAGTTATTGACCGTCTGAGTTATTGTTCCCATTATGAGCCTAGCTGAGATGAGTCTCAGCGCGGCTCCGCCTGTTGATTAAAATGTTTCCAGATTAGGTACTTCCATGAACTGATTTGCTCATACATTGACGGCGGGCGAGATGACTACGCTTGCCGACTACGTGGGCTCGGCTCACAAGCTGCGCGGAGTGATCGAAATCAAGTCAGTTGCACATAGCCTCACCCAGCACCCTTGACCGGAGCAAAAGTGCTGAATGACTGCCCGCGCAACAGCTCATGTCTAACTATAGGTCCAAGGAGACAACTTGGAGAACGTTCCTGCGCAATGCTCCCAAGGTAGCCATGTGCCAGGTAAACGCCTGCTAATCTAGTTAAGGTTACACACTAGAGGGGTCCCATTATTGCTCACGTGGGCCACGTGCTACACTTCGCCCATAGCGTACGGTCTTTCACTAGTTCCGGGTACCCACATTACGTACGTTCGTTCACTACTCGCTCAGTAGCTAAGATCGGGCTCTGGGGAGTTCCAATAGAGCCAGGTCCGAGCCATCAATTGTCTGACATATTTTAACTCTAGAACTAAAGCAGCCCAGGTGGGAAGGCCACAAAGGAGCAGCCGGAGACTATCAGATAAATACATACGCACCACTAGTCGTCATAAATAAAGGAGTTGTCCCCATGCTACTTAGGATTCAACGGCTGGTAACGGGACGACAAATAGGATTACGGTTCTGTCTTAGTAAGGCTTATTCTATGGAATGGGGACGTTGGGCCTTCAAGAACGTAAGGGAATGTCAAGTCCGGCTTGGTTTTTTCCTGATAGGCGTGATACGCGAGCTTTTGAGTGTAATAGCGGGAGTGTCTGTTGTTAGATTACTTTTTCCGTAGTATCTCACTCAAACTAAATTAACACCAGTAGGTATTATACGCGGAATCTTCCGCTTTTGACGTAGAGCATCCCGTGTCCAAACCGAATTGTCCTTTTTGGATCGCATGACATAAGGTTAAGAATTTACCACCACTCGTAGGGAAAGACCAAAGCGGGACAGACAACTGCCAGCGGGGCATAGCCTACTTCCTGTTATATCAAGCTCCAGCTGACTCAGAACCAGAGTCAGTAACGCCTATCTCTGACCTTTGGGTACTCCCACGCGGTATCATTGGCGACCAGCTTGTGGAGGATCCATTTAGCCACTCAACTTGTTTCTAGTAGAATTGAATAGACACTGGAGAGATGGCCAGCGACTGATCTTGTCATACACTTGTAGGTACTGTACCTAAGGTGGTTCAATCCTGGCTACGGGTAACAGTTGGTGAGGTGGGCCCTTCCTTGCGTTTGATGGGGGCAGCCTCGTTGGGACCGACTACCTAACCAGGTATGGTTTCCTCGCAAAGCATGGGCCGCCAGTATCAACTTGAATTCCCGGATTACGTAGCAGATTACTCCTGTAGTTCTTACACGCCCTCCTCTAGAGAGGAGCCGCCACATAGGGTACGCTCGTCCTGGGGATATTCACTATACGACTGTGTACTCCCTGGCACTGCGCAATAACCGGAAATAGGAACATGATAGCAAATCACAGGCATTGACCCCAGTGAACAATACCAACCTCAGAAAATGGGGGAACACCCTGCACCTCCGTGCTGCCTATAATACCTCATATCGTCGGCTCTCCATATGAGGGATAAAGATTCTTGTGCTTCGAATTTCAGACAGTCGACCAGTAGAGCAGAATAATAATCGTCGACCTGGTCAGTAAGGGGGCCGGCTAACGTAGACGTTCCCTCACGACCGCTCAACGTGTCTAGACAAGCACACAGCATATTCCGTCCGGATCCACCAGTGTATATTGGTAAGTTGCTCCCAACTGGTCAGGATGATCCTCGAAATTATTTTGGATAAATAGATACAATGCCTATCCACCCAGGTAACACCACTGGTACGCTATTTAACGCCTTCTCCCGGGTCGCTTAACTAAGTATGCTACACCCACATGCTTCAAATATGGTCGTTTCACCCTGTCGGTAGACTCGTCAGACCTTGTCTCATACCCAGTGATTTCAACCGACCAGTGGTGATATAGTAGACCCTGGCGGTAACGATGTATCCTTATTGACTCACCTCAACCCCCTGTTCACACACATTACGCCCCGTCCGGGGCGAGTAGTGCTGCCAGGATTTTGGGGATACAAAAGGTCTCTTCCTTAGCGGTGTAGGGGCGGATTTACCTGTTTCTCAGGTTAGAGTCACATAAGCTCTGAGATAGATATGAGGGCGTCATAGGTTCGCACCGGACATACCTCGCATGTCCCCCTGGCGTAGCCACAAGGTGACTAGAGCCCACCCTGTCCGCGACCTTATGGCCCACATCTCGCTACTCACACCATTGATGTAATAGGGGAGTTATCCTTCGTTCAAGTCCGTTACCAGGTTCATCAAACAAGCTTTACGGATTGAAGCATCCCGGTAAAGACAGTAGCATGACTCCAAGGGCATTTTATAGCCTTAAAGGGCGTCCATGCGGGCCGGCAAGCCACTAAACCTTCATCTCGGACTGTTGGTCCTCTTTGCAAATTCATGAATGCTTTATGCTGGGAGACTAAGAACTTTTGAGGTTTCTATAGTTCAGCGGTGCGACGAAGTGGTCAGGCGCTGTAAATGAATGGAATACTCCTAGCGGGTTACCCCAGGCTTGAGGTTTTCCTAATAAACCCACAGCGTGGATCTCACCCAAGGCGCTAAGCCATAAATCAAGTCCCTAAATGTCCTTTTTAGAGCAAATGATCAGATCTCTGCGCGAAATTTGATCAATGTAGGACCGCAAAACCGCGAAGTCCCGCTGCAATCAAAAGGCGTTATACCGCCACCATTCCCGTGTGCAAATATATAGGCGACACCGCTGCAAAGCTCGGCTCATGCGATCATAACCCCACGCATAGCTTCCTCAATGTTATTTGCACTTCCCCCATCACACTGATATGCCCGGATGAACACCATTCGGGGTTTAATAGCCAGAAGATCCGCCTGCCTAAGATAGATTGTGGTTTCACCGAAGTAATGCCAAGCCAGTAGGTGACAAGACTGTTATCCATTCACGGGTGTAATATTTGGCGGTTCTCCTACAGGGTCGTTCCATGTGCAATGGGCCCTCTTACGACCCCGAGCAGCCTGAAGTCTGTCGAATTAATCTTATTCCTCAGCCCGCGGTCAGGAGGGCCGTAGGTCATACAATCAAGTGAACTCTGGCAGCGTGACGGCAGAAATGCGTAAGAACAGGGCTGTAACGATCCATGCCGGGTCAAGAGAAGGCAAACGGGGCTCTAACGTCCGATCTCGACGAAAATCGGAGGAACCGTCGCTAAATCGCTGTGCGCATTATTTACTCGGCTCTCTCTTGCCCATAAGTTTCTAGGTACGTACGCACCAATAGACAGGGGTATGTACTTTCGGGTAAGCACTGATCGTGGTGTTGTCAATCGGCTTCACTAGTGCTAGTGCTGAGAGTTCACTGTCCTTCTTCCGTGCTAGTTAATGAACCGCTTTCTATCCGGAGCGGTCTTCTTTCGCTCACTTGTAACATGCGCTAGTGGCACTACCGACAAGCAAAGCTAAGGTGCCTCCTCATCGACCGGAGGTCCCTCCGAGTTTAGACGAGCTTTGTTCACTCAAAACGAACACGCTGCGCATAGAGCAGGAATCAGTAAAGGGAACAACCTAACTGCAAACACGTGGCGGCTTGTCGTGTCTGACTACCGGGCAGTTCGGGTCCTAGGCGGTTAGTGAGCGGAACGCGTCGCCGGGCGTATCCGTAAGGATTGAAATAATTCTCTAAACGCCCCGCGTCGAATCTATGTCCCTTAGGCTGTGCCCGTCATTTCCGAAGCGCCCACAGGTAAGAAAAGATGGGTTTTGCAAGGCAAGGTTGCCGATTGGCGTTCGCAGCTGTTTAACAGCACATGCCGCGTGCTATACGGCAAGGAGAGCCTCTACTCATGACCGTCATCACACGCCATATGCCGTGAACCCCCCCGAGGAGTAAAGCGATGTTCTGCTGTACTTACTTCACAATTGTTAGCCGTGGAATTCGCATTCATTCCAACCGTTTCAATGATTCGAGCAGGCGAGGCTCCTGGGTGTTTCGTAGCAGGGCCCAACCACGAACATCTCCTTAAGCATCCACCCACGTGTAGTATGCACACCATAGATGGCATATGATTGTTCGAATGCTACCGTGATGCGCCTGTCTGACCAAATACCCTGAAGTTGCGGGCGCTTGTCCAAAATATGTAGGCGGGACACAGGGCCAACGTATTCCCTACGTCCGTGTACCTAGCTCAGGGCAAGTTTCTTCAGATTCCATTGGGACCCTGTAATAAGCAGCTTTTAAATTACGCTCCCTTCAACGAGACGGAAGCGATTCCGAAGCACCGAACCCTCAGAAATGGACATAGCTGGTGTTGTGGTGAAGCCTGAAATCCGACTCGGTACTATTTGTCATGGGGCTCCGGATATTTGTTGATCTTCGCTGCATTTGCGTCAAACTACGACACAAGTAGGATGGGCGTACCGCGAATTCATGACATGCGCCCCGACTATCATGAGCCAGAATGTAGAATCCAGGCAGTACCATTGGGAATGATTCCGTTGATTTGCAGACGGCGCATTCACTCTAAAGACACATATTGCAAGATTAACCTTCACTTTAACTCATGTCTCATACGGGTTGTACCAGCTGCTAACAACTTGACGTGGATGGCCGGGAAAAGACAGTAGTGGGTAAGAGGCATCTATCAGCGATACCACTTTGAATATGAATTATCCTATTAGAACCGTTCGCGTCGGCTTCTTAAAGTTAAAGGTCAAGAAGCGCCGGCCATCGTTAGGGCACATAGTGGCTATGAGTTGCGAACGTCATAAAACTCGTATTTAGCGAGGTCTCGGGACAGGATAGGGTTGCTGCGTAATTGTACGCGGCAGACAGTACAAACGTGCGCGCGGCGACTCCTATCTCTCCCGCTTAAGCTTTATATCAGCCTGCGCCGGGTGTGCGCGCGAAGCGAGTCTGAGTCCCGGTCGCTAGTGATAAGAAGGCACGTGCTCCCGAATTGCCCCCCTCTAAGGTGTGGTCCTGACGAGACTTTGGATCACGCCTCTGCACAACCAGGGGTTAGCAAGAAAAGCCCAACCTCCTTGACGGCCGTTCGCCCAATTGTAATGTCCGATATTAGGCGAGCTCAAAGTTATCGGGTGCTAAAGCTTCCACTTATCTTTAGAGGGCGCACAGTTGAGGTACGGGCCCGAGAATCGGTTCTGCCACGTAGACTCTTATGTGGTGTCGTATGTGGCGTAGTTCAACCGCGTCACGACGAATCGTCATAAAGGGTAAACTATCCCTAAGGGTTTTGGAATCTTGCGCAATAGTCGCCTTTGAACGGAAGAAGCCTTACCCCTCTGCGTTCACAGGGTAGCACAAGCTGCGACGAGAGTTACTGGTTGTTTGAGGGGTGCCCTAGTAGGACCTCCGCCGTATTATGTACTATCATCCGAGTGGGCCACCGCGCCCGCCTTAAACACATGCTACCAGTCTGTGCGAGTTGCAGGTGGATCGGCAAGGGGGGGTCCGCTACGCATGTAACCATAGGGGAGGGGTAGCAAAGGTGCGAGGACCACATGCCGGCGTAAAAACGAACTCTCACATGTCAGGCTTTGCATTCTGCGCCCCATAATCATCTAGGCTTTGCCCCACCGTAGCGAAACAGTTAATGCTTTGTCGCTCGTGGCTTAACTTCCTACGCGTTCTAGTTGCTACCCGAAGCGGCGTGTGGAGTCTGCGTTTTTAAGACCGGTGTAGTTCGCATGCAGACGTGAGACCCGAGTTGTGCTTGTGCCTTGTGGTACAACTCCACATATAGTCGCAGTGCGCACGCAAGGCGCACTTCGTCGCTGGCCGGGTCGGGTCGTACGTGCGTTACGAAGCAAAGAGTTGTTTATTAGAACGCTGTGATCCGCACGTAAGACCCGTTACTAGCTGCTCCTCTGTTCTTGCAAGGAGTGGTCAGCACGTATACCAGAACAATATCCTGTTAGAACTGTTAGACATACGACACTTCTCCACCTGGGATACGTAGGAAGGTGCCCCCTCACAACTGATGTCTTCCGCATCGCAAGACAACGTTGCAAAATATTACCCAGCGCCGCGCGTAGTGTGCACGTGCTGGTCCCTCGCAGTACCGACGGTAGATTTAACTCCTAAGTCGTTAAAAAAATTGTGATTCTACGAGGAGAGGCCTACGGGTTCTTTCACCAATTCGATAACTTTCCGACCGTCATATTCCCAATGAATGGAATTGAACTGGACAACATATTACTGACATAGAATCATGCGAGTGATCAAGAGTTCAGGAGTAACAGGTGATAACCTCGGTCGTGGGCCCGCGATTAAACAGACCTTAATGCACTGGATCAACGTTATAACTTCTCTGTAATTGGACATATCTTGTATCCTACCCAGACACTGAGGCACGCGGCTGGCAAGCCCCCAGGACAAGTTCATGCTGCGATGGGCTTTATTCGGACTGCCGGGCCTGACCAAGGCCGATCTATGAGTAGGGCGCGGTAAGAGCGAGCCCCGTTGTCCCAGCCGTGCTGAGGGCCATCTGCCTGGTAAGATACACAAGAAATACGCCTAAATGTTCGAAGAAGGCCTTAGATGATGAAACGTTCTAAACATGCGCGCTAACAGGCCACACTGTATGACATTGCCAGTCTCAATGGGGGATACACTTGAATTAGCCAATGTCTTGCCTCGCCTGACACCACTCGACTTCGCTGCACGGAGCAGCGCTTTTCCTTCAGTAAGCCTATAAGGGGATCTCTCTAGTCACGAACTGCGGTTTAACCCGAGAAGACGTTCCTAGTGAGAGCGCGAGCTCCGAGTACGTTCGGCTAGACTCCGTGAATGGGAGACTTAGGAACTAAGCAGCGCGGGGTTTCGTTTATTGGTACTTGTGCTTGAGCGGACGGGAGCACTTCCTGAGGCACATGCCGGGCGGGGTCGCAGGTAGGCTTAGCAGAGACCAGTAGCTCGTCCGCCTTTCCTCCCGAGCGTGAAAGCGGTAGCCATACACGGGGCTAATAGGGAGCGCACCATGCCCACAGTAGGCCAGCCCGGGCCTCACTCCCCCCATTTTCAGGCTACCTGCAGAGCCATTCGGGTTCCGTGGCGTTTCAGAGAGACCTGACTATCGGTTTGTGTCAGCCGATAGTCTTGCGATGTAACACACGAAGGGATAAAAAAGCGGCCGCAGTGTAGTACCAGAGGTATTTAAACGTTAGACCGAAATTGAGTTTTCGAGCCTATCGAATAATTTCGCTAGGGCTAGGTGATCCATTGGTTGAATTTGCACGCTTGCAAATACAGGCAGTGTGTCATCCAGCTCGCCATAAATGTTCAGGCGACTGACAAGAATTCGAAACTCACACGCACTAGTATTAAGCTTGGGTCACCCTGAGGCACGGGCGATTGTTCCGGGATTGATATCGGACGCCGCTTTTTAAACGATGTACAATTATCATATTTAGCCCATAGGCTCGATCATTCAGCCTTATGCGAACCTCGAAGGCAGGCGGAGTTAGTCGCAGTCCTCAAGGTGCCTCGTAACAAATAATGGGCAGCGCCTGACAAGGGTTGATGTACACTCTCACAACACTCTTAATTCAAAGGAGCCCGCTTATGGATAAAACTAATCCCAGCATTATCTCTCTAGGCTTTTTTTGGGAAGCTTAATCTCACACCTTAATTCCTCGTTCGCGGCCCCGTTGACAAACCTCGACGCCCAGCCTTATAGGAACATTAGATACTCGAAATATGGCAGCGAGAAAGTCCGAAGTGGCTACAAAATGACAATTGTCTACATCGGGAGTATTGCCTCGGTCTTTGGTTGATTGGAACGGGGTTGCATAAAGTACGCGTACATATCGTTGCCGGATAGATAATCTCAGTATTATTCTATTTCATGTGTGTGTAACGGCTACTCCGAAGTCCTGTCAATCAAGACGGGGGCAAGCACAACAGTTCACTCACGGATAGCAGATTATTTACGGCTGAGTTCAATATCGGCAGGAGGAATTAATTTGAACGAAAATACACGCCCCCGGGCTACTGTTTAGCAAGACTATCATACGCAAATACTATGCCATTGCTCGGTGGAATTTCGTTTCCGGACCATGATAAACCGTGTTGTTATTCCAAAAGCGAAAATTTTGATTTAGATCGGACCCTAGCATCCAATGCAGGATCTAAAATAATAATGCGGATAAAATGATAAATTCCCTGCGATCGACGGACGCTAAGTATTGAAGAGCATATAAGAATTGAACGGGTGGCTCCTTCGGTTATGTGGCACTGTTTGCTTGCCTGGCAGAGTATATCAACAGTGATCAACGCGGGAAACTCGGCCGCGGTCCGATGCTGTCTGCTTGGTTACCTCCGGTTTACCATCTGGTTCTACGTCCACTTCGTGCATTTTTGTTGGGTAGGCGCACCTGATGAATTGAGCTGCCCCCGATTTCGCACGTCTGACATCCGAATTTTTTGTCTCAACTTGTCAATTCCGGATCGTCCTAGCTCAGAACCCACGGATCAGCTCGGATAATTTGTTTGGTTGTTTTTACACGTTTATCATACGAGCACAATGGATGGGGCTCATACCCCGGCCCTGCCGGCGTGTGACGAGTGGAGATATTCCTTCTCTAGCTGTTCCTTGTGTAGGAGCACGCTTGGTCCTAACCGTAGCCACTTCGCGATATTGAATGGTTATGTTGCAGCTACAACCAGAACTGGTTGAGGGAGCCCCGAGCCTCGATCGTCTTCACGTCCATACGCATTCGCCACCCTAGCTGGGCTTAGACGTTAAACTGCGACCGGGTAATTACCTAATTCAGTCAAGCGTCCCGTTCCACATTAATATTCCAGATTTGCTTAATGGCCCAGTTAAATCTATCATTACGTAATAGCTGTTACTCCCTAAAGCGCCAAACTTTTGACCCCTTGTTACTTACAAATAAACGGGTTGCGTCTTTCTTTCCACGGACCTCCATGCTCGAATATTGTCTCTTTCCGTATTGTACTGTACAACTCGGTGACAGTTCGTAACTACCTTCAATAGCAACCAACCCCTGTTACACCCGTCGGGCTACTAGTTTCTCTTTACAGCCAGCGTCTGCAGGTAGCTCAGCCCTCAGATTCAGACACAATATGAGGCTCAACTTTGCACTGAGTCCGTGGCCGGAGGGCTCTGGCTAACGGTTACGTTAGCATGAAGTACTGGCTTCATAGAGCCGGGCCTAGTCACACTGGTTGGACACTACCCGGATAGAACGCAAGTTATAAGATATAGGGCAGGTTCTGGGGTGTGCCTGGATATTTTTTGTCCCAGGTTCCTTGCGGCATTGCGTTCAAGGTCTCTCTTACCCAGTCGTCATATACAAGATAATACTATCGACAATCTTGACAACGTCTGTTGAGAGCTCGCGTCATAGGGACGGCAGCGCTAGGGAGGTATGCCCAACCGGGAATGTGGGGTTCTCCACACCGTATGTCCGATGGGCACAACTAGACCGTGTCATTATAAGAAGCTACAACCGAGATGCCGCACCTCTTTAATGTTGATCCTGTATTGTGAACTATCACTCAGCTAATTCGGGGCCATAGGATATAGGCTTCTAAGTCGACTTTTCCGATGAAAGATCACTATCCAGCGTGTCAAGGGTAAATGACATCAGGACGCTGACAATTTGGCTGAATCCTTGGCGACGCGGCAAGAATAAAGGCATAGCACGGGCGTTTGAGGGGACGCTGATTGGCTCGGGTCCAGGTCTGACCTCCACGGAGTCCGGATTCGAACTCGGTCAGTCACACGGGAGGCTTAGGAGCCTTCGAACGGACGATAATATCCCGTTACTCGCCACTGTGCCTGCCTATAATGGAAAAGACAACTTTTTGGAAGGAGCTCGCATTGTCCGCGGCCGAGCGATTTCATGTGGCTTTGTGAAAGAACTCGAGTTGGACATAATGTCTGTATATGTATCGCCGACGCGACATATCCTGTACGTGTTGTCGATGATATGAAATTGACCTACTTACATGATACCAAAAGGAGGCGGCCCCCGGAAGGTTGGCCCGAAACACGAGCTCCTCGCAAATGATGTCATGACGATGGCAGAAGCCGAGCTCACGTCTCATTCGGTCGCAAATCCTTGATTCGTGCCGTAAGGAACTTGGGAGGGTAATAGCAGGTGCCACGTTCTCCTATTGTCGGACGCAGTGCTTATGTAACTGATTCCCTGAAACGTAAGCTATTTCGTCCGGAGATAGAACATTATGTCCACAAGGCAAGATATGTTTCCTATGAGAAGAAAACGACGCTTAATGACTTGGTGTTCTTTCTCCAGGCAAGATACGCGAGCGCCTACGGAACAAATGACGTCGTCTACGACGTCCAGATCCCAGCTTTTCGTTCGCTGCGGCCCCCAACTTTGCCAGTAGTGCCGATTGTGCGTTGCGGCAGGGGTCCTTCATACTAGTGCTTCATGCATTTGCCAGGTACCATGGATCTGCACTACTATCAACCGGGGACCGCGCCATCGCTTATGGGGTAGATGATCGCGCCACATCGTTAAGCGCGGATTATCGGAAGCAATCGTCCCACGTATCCTAGGTATAAGATATAAGTTGCAACCGCCACTAATCCCTCACACTTTCCACGGTGGAATCAGATGTCAAGCACTACAAACTTGGTCCACAGATGGGCAAATATATGTGCTCATCTCATAAAAGGTGTGACCGCTCGTTCCGGAACCTCAACTTTTGCTCGTAGCGGGTATTCCTGACGCGATTCACCAATCGGGGCCAACGGCTGACTTAGACCCGGCAAATAACACAGTCCAGCGTAGCTTGCGCATTTACGGCTTTTTGAGCTGGCGTTCCCGCCAGATTTGCACAATAACATCGATGAACCTGTGCTGCGGCTAAATGATAACGGAATGGACAGTTCGCAGTGCTTCTGATTGAGTTCGTGACCAAAGTCATCCGTAGGCAGGGCACTCTATACCTCACGTATGTAGCCTGTGTATCCTTGCCCAAAGGCGCGCGCAGCTCGAGGAGGCGGGATTGACCGGGACACATGTATTCGCCTCCGAGCGAACGCAGTAAATATTCATACGCAACGAAAGAGCTTCTTACGGTATTAACAATTTCATCCGGGGGTTATCGAGGATCCGCGCCACGGGTCTGCGCGAGACTCACGTGGCGGCGGATGAACCCGGCAACAGTCGGTGCCCCTGCTCAGCAGAGCGTTATTGCGAAGTCCATGAAAACCTTATTCACTAACCCGGAACAGCCCCTTTAATACCAAGTGTGAGTAGAATATTCACGCATGGGCAGGGTGTGGCGAAGAGGCGGCCACACATCGACCGGAAATCTCTGTCCTGCGTTCCATACCCCGACGAATTTCATTTCCCCACGGACTAACTTTCAGGGGGAATAAGCGAGGTACCCCTGGCAGAAAAGGCGCAATGTCTGATGAAATTTCTCGTGATGCTCGTGTGGACGATGGCAAAGCTCATGGAATTAGCTAGCAGCAGTGAAGTAAAAGCCGCTTCGGCGGCGACGGAGTGTATCACATCGGGTCGGATGTGGTCGCGGAGTGACGAGATCGAGGATGTGAGAGATAAGGCCCGTAGATCGGCAGGTGTGTGCCCTACGGCTCACTCGTGGTCAGCCAACTGAACTCGTGACCCTCGTTATGGATCTTAAGCTCTAGTAGGACAGCCCAACTACCACGTCTTAGAAGCCAACGATAAGCCCATAGGGCAATGTTTTTAAAGGAGCCCCCGTTCGTTTCGGCCACAATTAAGAATTGAGTTGATCATGAGGACTATGTGATTTAAAAAGTACACGAAAGGCGCTCAACGGAGTGTACTAGGCATTAATCATCCGAGTACATGCTATAGCTTGTCCAACACTGTGCGATGCGGCGAGCGCGGGTCTGAAACGCACCCGATATTGGTTATGTGTTGGGGTGCCCATCGCGCTGTGTCCCCGGTGGGAAAGTGATGTGCCATCATACAAACGATCGTAAATCTGCTGATAAGGACAGACGGAAGGCTAAAATGGAAAGATTGAGAATTGGGCGACTTTCTTTATTCTCAGAAGCAGGAATAGTCATCCGTAGTGCCGTCAGTTAGTAACAAATAGTTGCACGCTGCGGGGAAATCCCCCCACTCCCGGAGTACCCAGCAAGTTAGGAGGGTGAATCCCGGTTGGGCTCAGCGGACACATGGTGCCGACCAGTATTTCCGCATTTTACGGGACCCATACAACGCGTAACGCTTGACACTGGAAACAATAATAGATGTGTCATGAACTTCTAAAGGTCAGTTCTGTCGTCGATCAGTGCTCTCCGCGATACCCGAATGCGATTCTAAGAGTGCAGCTTTCCCTAATTTGCTACGCGACGTATAAATTGGCCTAGATTGATGGGTGGCGTAATGAAAAGTTATGCTGTTGGTTCGTATCCTGTGTATTTCGGTTCTTCAATTCGCACAGTGATTAACGGCCCATGACTGATGGACGTGGGCGTGTAGTATGGGTATACCCCCCTCAATCACCCCGAGTGCACCTTCGTGCGAGAAGCACATCGCCTACATGATTGGGTACCCCCTAGCTAATGACACCTCCGGCCGGTGCGGCGGAGTCGTAGAATGGGCCAATGATTCATCCCCTTGTTTCGTCAGTTCAGGTCCTGTGATGCACCGTACCTGCCTTATCATTCTCGAAGCCTGTTTAAGTTTCGTTTGCGAATTCTGCGGCCTGCTCTGATCCGGGGAGACCTTCGAACATGTGGTACCGAGACTGAGGTGAGTAGTTCAACCATTTAACTGGAACAGCTAAATGTCGCTTTCGCCCCTTACTTCTTGTATCTTTCGTGGCGTTACCATGAGGAGGGCATTTCTGACTGTCGCTTGGATGGAGGAGTAGAATAACCAGTACCCTATTCTGGCCCTGTCAGCGGCACCGGAAACCGGGCCGCACGTGTGTTCGCAGTCATTTGTTGACGAAGCATGCGTCCAGTGAAACGTATCAGGCATAACTTGCAGGGTAGGAAAATAGGCACTCGTAGCCTAGGTGCAAGGCGTTTTATGGCGCTAGAACTTAGAAACGAAAGATCCGGAGCTGTCCGATAACTAGTTTAAAGAATCACCATCGGGGGATTGCCAAACGATACTACCATTCCTTACACCAGTGAGGGGGCGCTTAGTGGGCATGGAGTTGACGCGGGATGCGCGGGCCAACAGATTCCAAAAAGTGACGCTATTTAAAGAGGTGGGCTATGGAATCGGCATGCTTACAGCTGCTCTTCCTCCTGTAGTAAGTATATGCGACCGATGGAGGTCAAAAGGTCTTGATTCGTCGGTGGCCGATGTTATGCGGTGAGATACCTCCCAACGGCGTTGATAAAGGAGGAGGCTTACCATTACGCCCATGTTCGAGCCCCCAGCCAGACATCGGTACCAAGAAGGGTAGTTGGATACACCCCGTTGCCACCAACGAGTGTCTTGTGATTGGCCGATTTCACCAAGGGCAGATCGACCTGGCGTGGAATTCCCTTATGCAACAGAGTCGACAGTTTGGGGTGTAACAATTGTGCGTAGGCCCAGCCGTCGAGTGGTCCAGGGCTGGTACTATACAGCTAGCCGTACAGAATACTCGAACATTGATCACCATGTGACACATCCGAATAAAGGCTAGCTACTTCCGCACAAGCAGATATATTGCCCCGAGTGGATTCCCTGGGGTCTCTGAGGTATGGGGATAACATACTTTTTAGGACTACAGATAAAAGTCTTACATGGAAACCAGCTACGCGTGGTAAACGGATAACGCCACCGGATGTTAATTGGCCTCGCGAGAAGTTTCATATGCACTTAGGGGTCTTAAGCCGTAGCGCTCGGTTTATGCGGCATTGCGAAGTCTGACGAGGCCATTCGACAAGTCCAACAACTTATCACACTTGGTTTGTGCTGGGCTGTGAAGTCTGCAATTGCAGGTAACTTAGCACTCCGTGATCTACACCCAACCGGCCGTACCTCTTGGGTAAAGAGACTGACCCGGGTGTGATATCTGTGGTAGCCAGGTGTTCATTCAAATAAAAATTGTCCTAGCATTCCGACTCTAATTTTCAAACCGTTGCAAAATTCATCGTTCTAAAAGCAATAGCTTGAGAGGTCCTTTTTTACGGGTCACTGCATCAATATTCTTTCCTCTACCACTCAGTTGATCTTAGTCACGGGTATAAAATATCCCTACTGTACTGTTTGCCGGGCGCGACGGTATATTACCCGTGGGGCGGCGCCATCCGATAGACTCGTATATATCAATCACTTCCGCGGAAATTTTCAGCGTGAGCCAATCGATTGACACAGAGTCCGGCACCGGGGTGACGCATTGAGGTCCATGACACTCGAAACTTGGGCTACGACAGGCGCGGGACGGCCGGCTCGTTACTGAACTGTCAATGGGGATCGCGTGCTTACGGTGCGCGAAGGATTTATTTGAACGAAGGCATCTTCTTAATTGTCTTCATTAAAATGGCCTGGTATATAGTTCGCTAGGCGTTTCTACAGTACGGGTAACTTTCTCTCAAGCAGGAACCGCAAACGTCGATGCTGCAGTGGCATTACGGGGTGATCTTAACATCAAGGAATATCTCCGGTGATTCGACACGTCTCCCCACTTAGCGCGTGACCTCTGACAGGTACCGACTGTAAAAGATCTACGTGTTATATTCAACATTATCCAGCAAGTCGTGGAGTGCGTACATGTTATTGTTTGTGTACTAGAACACTACTATGACGGGTGGAGCCATCATGGTTGACACGGAAGGATAAGGATTGCGCACCCATCAGGAACATGTACTAGACACTGCGCGTTACCACCATAAGCCAGTGCCAAGGATATTGGAACGCTAGGCTTACAGTAAGGCCACAAAGATGAGACTCCTAGATCGTCTGCCGTAATATAACAACTGTACTGCTACAACGACCGATATGCGCATGGCATGGATGCGTAGTTTTCAGGGCATTCGGTATACCGGAGCTGTGCCGAAATCACCGTTTCGGCCACGACGCTCACACGATGGTCTGTGAGACAGGGGGGCTCGCAATCTTCCCCATGAACATGGCGTGATTCAAGAGGAAGCGGTGGCGTGTTCGTAGGCGGGGCGCTTTTCATTTCACGAGAGTAAGACAATAGCAAAGTGACGGCCACGATAAGGGAACTAGAGAAACGAAATTATATTACACGTGCAGTCCGGATGGCCTTGCTGATGTAGCTACATGTCCCACTATTCGCGTTGCTACAGGGGGTCTAGGCGTCTGTCGGAGATTCTATTCAGAACATATGTACAACAATCGGCTAGGACCCACTGGCTAAACGCCTGTGGCAGAGTCTCGCTTTCTGTCAAGTCTACACTAGTAATAATGGTCGCGACTGTGTGCTTAAGCGATCCCGGGCGTATCCGATATGAGAGGTAAAGGTATATTGCTCGTCCCGCCCCCATCTAGGTGCGCGTTTTTCTAGATGATTCAGACCCTCTAAGCACCGACGATACTAGTACAAGAACTCTCCATGGTACCCTCATGCAGCTGTGACGTACCGTGTAAGTATCACTGTATAACTACGATTGGGTAATGCTCGTGTACCTGGTCTATAGTTTACGTATGGCACGTGCCAACCGCGCACCTTTTAGGATTCGAAAAGGCTGCGATCGCGGATGTGCTGGCTCGCTTCACAAAGAATAGGGCGCCGTATTAGCTCGCGGCCGGGTTGCGTTCCCGCTAAGTTACTCTATGCACGCTGGTATAAACTCTAGAGATATAAAATCATGAGCCCAGGGCGGGTTGTAAATATCAGCAACGTAACAATTGTGTGCGTTCCTTGGTATCTGCGCAATCGCGATTCTGGGTTACCGCGAGAGCCCCAGGTTCCGGTTTTACAAGTACAGGGCACAAGAGAGATACAGATGAGTGCACGCAAGATGCATTATCGCCATAACTGAAATGGAAGTTCCTTCCGCTAAAGTTGTACCGCACCGAGAGAAACCATTACTCCAAGGACGCTACAACGGGCTCATCTTCCCGTCAGCGGGTATGCAGTATACTTTACGCGGCCGTCCAACATCCCAGTCTATACCTAACCTTAGACATCGTCTTACAACAGCCGGTATCCCTGTGACCGCCCTACGTACGTTGTGGGTGGAAATCAAAAGACTAAATCAATGGATTTCGATGTCGGAGTGACTTTACTCCATGTGAATAAGACCGGACGAACGCCAACTCGAATTGTACCCGGCTCTCCTTTATCGACAGATGCGTCAATACACAGCGAGATCTATGCCTCGAGCAAATCCGGACACTTGACGTCTGCGATCGTAGAAGGACGGTTTTGTTAATCGTACAAACTACCTCCAATATAGCCCCTCGTACTGTTGACAGCGATACGGTGCTATGGCACTAATCTTCACTGGCGTTTATGGGTATGACAGCTTGGATCATTTACCCCTGACCACTCTTACATACGGAAGACAAATTCCAACCAAGCGCTGGATCTTCGCCAGTGACACCAGAATAGTAACCGGGACCACACATTCCTCAGCTGAAGGAAAAGGTAGCAAGTCACTGGAAGGCCTGGGCACAGGAACGATTGCCAAAATTCTTAAACGAGGCACGAGTAGTGATCCTAAGGCACGGAAACAGCGCGTGGCCGATCTGTCGTGCACCTTGTGGTACTTTTCTGGACGAAGAGCAACGTGTATTAACTCTTAACGCTCTTCTGACATGGCTCACAACGCAACAACGTACCGTTTAAGACTCACGGACACCTAACGCTTCCAACCCCCAAGTATTCACGATGCTCCCTCCGTCGTATACCACTCGTTAACTTGATAGGCTTGCTGAAAACAGTGCTCAGGCCCGGTCGGTCGCCTGTGGTAGCTACCTTACTTTACAGCGAATCTAGCGATAAGTTACGTTCAAACAAGACTGACTTTAACGCCGACGGAGAGTTAACCAATGGACCTTCCAGCGGCGCTGACTGTTAGGTGTGCAACTCCCCATGCCAATTAAAACCAATGGGCCTTACAAAATATGATCTGAATGTCCACATTAGTCTGGGAAGAGTCATATTGTGTCCATAGACCAACTTATCAATAGCCCCAGATACTCCCTTGCGGTATGGAGCTAGCATAGTTGCTTTAGTTATTTGAGAACGTGAACTTCCTATTTGTTGTTTACAATTCTAATCGTCTCCTATGCGAACACATAGAGTCCTGACCTAATTCGTTACGTGGCTTGGAAGGTTGCACAGGTCTATAAAGCCGTAGACAGTGGTCCAGATCGTCTTTGCGTTAATTCGGGACAAATCCCTTCCAGTTAACGTTACCACACGGGAGCTCCTACACGAAAATGCTCTTGGGTGTCCTATAACCTGTACATCTACCCAGGCTCGTCGCACCCCCAGGGAAAGGCCACATCTGCGAGGGCTCTGGCGGACCGTGAGTCTTTATTCTTACTCCAACTCCCCCGAAGTAGGCACTGAAGGCCGTTCGTGTGACAGTTCGACCCAGCAGCTCCGCCGTAAAATGCTACAGAATGAGTGACCCCCCCAGTACGGACACAATAGATTGTCCCTCGTCCACCGATATGCGACGCAATTATGCTCGAGCACAGTTTGCATACGATGCCTACAGATGGGTACCACTTGGGCCGGGGCGACTTTCGCCGGGATTGCCGAATATACGTAATGCCGTCATCGGGCCATTTAAAACGGGGAGACTGGGTGGGGAACAGTGAACCCTGAGGCTAGGCGCAAACATTGAGGGGATCGGACCGTTCAAGCCACGAATCCCACGGCGAAGATATCTAGGAACATGTAATCTGACGGATCTCTTACGAAAATCAGAGAACTGGTGCATCAGTGTCATGCCTTTTTAGACATCATATACATGTCTTAGCACATGGCAATTGGGTGCACAACATCCTTGAGTCGAATTCCCTACCGGTTAAGGGGACGTACAGAACACAACATAGGAAGTCTAGTTCGATGACGATCGAAAGCTCGTGCAACATTTACATTTCACGACGGACTCATTAACGTTGCCCCGGCGTGATATACGTCTGCCACTCGGCCCGTCGTACTAAGGTAATCCACTTGAAAGAGCAAGGTCTCTTGCAGGTGACAACACGTGCCTTTTCCGGTACTCAGGTGAATATATCTCGTCCAATGGGCCTCGCCGCAACTATCACCCTCGTTATCGACGCTAGGACGGTTGGGGATTTCTGCATTGGCTCGTAACTACCATATAAACTTTGCAAACCGGATTCCTTGCGTAGGGACCTTCATTGATATGGAAAGCTGCTCATCGTAGGGTATTTGCGCCGCGCAGTTTAAATGTCTTAATGCTGCGCCTCTAACGCTGTCATAAAACGTTTTTGTGAATAATAGAGACTAACTTCCGCTATTTCAAGTGAGTCTTGCTGGTCAGAAACAG . PASS SVTYPE=INS GT 0/1 0/1 \ No newline at end of file diff --git a/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv.vcf b/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv.vcf index bce755b9486..9fc4fe9c325 100644 --- a/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv.vcf +++ b/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv.vcf @@ -46,7 +46,7 @@ 1 700000 . C . PASS SVTYPE=DEL;END=700297;SVLEN=-297;CIPOS=-22,18;CIEND=-12,32 GT 0/1 0/1 1 800000 . A . PASS SVTYPE=INS;END=800000;SVLEN=6027;CIPOS=-16,22;RIGHT_SVINSSEQ=ACCACACCCACACAACACACA;LEFT_SVINSSEQ=TGTGGTGTGTGTGGTGTG GT 0/1 0/1 1 850000 . A . PASS SVTYPE=INS;END=850000;SVINSSEQ=ACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACAACCACACCCACACAACACACA GT 0/1 0/1 -1 860000 . A . PASS SVTYPE=INS;END=860000;SVLEN=1000 GT 0/1 0/1 +1 860000 . A . PASS SVTYPE=INVERSION;END=870000 GT 0/1 0/1 1 900000 . G . PASS SVTYPE=INS;END=900000;SVLEN=6027;CIPOS=-16,22 GT 0/1 0/1 1 1000000 . A . PASS SVTYPE=DUP;END=1021100;SVLEN=21100;CIPOS=-500,500;CIEND=-500,500 GT 0/1 0/1 1 1100000 . T . PASS SVTYPE=DUP;END=1100076;SVLEN=76;CIPOS=-10,10;CIEND=-10,10 GT 0/1 0/1 diff --git a/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv_2.vcf b/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv_2.vcf index c6e2b9176e4..32ad6f4c4f0 100644 --- a/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv_2.vcf +++ b/opencga-storage/opencga-storage-core/src/test/resources/variant-test-sv_2.vcf @@ -46,7 +46,7 @@ 1 700010 . T C . PASS . GT 0/1 0/1 1 800000 . A . PASS SVTYPE=INS;END=800000;SVLEN=6027;CIPOS=-16,22;RIGHT_SVINSSEQ=TGTGGTGTGTGTGGTGTG;LEFT_SVINSSEQ=ACCACACCCACACAACACACA GT 0/1 0/1 1 850000 . A . PASS SVTYPE=INS;END=850000;SVINSSEQ=TGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTGTGTGGTGTG GT 0/1 0/1 -1 860000 . A . PASS SVTYPE=INS;END=860000;SVLEN=1000 GT 0/1 0/1 +1 860000 . A . PASS SVTYPE=INVERSION;END=870000 GT 0/1 0/1 1 900000 . G . PASS SVTYPE=INS;END=900000;SVLEN=6027;CIPOS=-16,22 GT 0/1 0/1 1 1000000 . A . PASS SVTYPE=DUP;END=1021100;SVLEN=21100;CIPOS=-500,500;CIEND=-500,500 GT 0/1 0/1 1 1100000 . T . PASS SVTYPE=DUP;END=1100076;SVLEN=76;CIPOS=-10,10;CIEND=-10,10 GT 0/1 0/1 diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/pom.xml b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/pom.xml index 066630724a2..38607eb3a72 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/pom.xml +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/pom.xml @@ -259,10 +259,6 @@ org.apache.parquet parquet-hadoop - - - - org.apache.ant ant @@ -428,6 +424,9 @@ org.apache.logging.log4j:log4j-api + + commons-lang:commons-lang + org.opencb.opencga:opencga-storage-hadoop-compat-api @@ -439,6 +438,9 @@ org.apache.zookeeper:zookeeper + + commons-lang:commons-lang + diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopLocalLoadVariantStoragePipeline.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopLocalLoadVariantStoragePipeline.java index 462aa6858b0..ada36f36d49 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopLocalLoadVariantStoragePipeline.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopLocalLoadVariantStoragePipeline.java @@ -70,6 +70,7 @@ import static org.opencb.opencga.storage.core.metadata.models.TaskMetadata.Type; import static org.opencb.opencga.storage.core.variant.VariantStorageOptions.*; import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageEngine.TARGET_VARIANT_TYPE_SET; +import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageEngine.UNSUPPORTED_VARIANT_TYPE_SET; import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageOptions.*; /** @@ -80,7 +81,7 @@ public class HadoopLocalLoadVariantStoragePipeline extends HadoopVariantStoragePipeline { private final Logger logger = LoggerFactory.getLogger(HadoopLocalLoadVariantStoragePipeline.class); - private static final String OPERATION_NAME = "Load"; + public static final String OPERATION_NAME = "Load"; private int taskId; private HashSet loadedGenotypes; private int sampleIndexVersion; @@ -182,19 +183,33 @@ protected void securePreLoad(StudyMetadata studyMetadata, VariantFileMetadata fi final AtomicInteger ongoingLoads = new AtomicInteger(1); // this boolean resume = options.getBoolean(VariantStorageOptions.RESUME.key(), VariantStorageOptions.RESUME.defaultValue()); - List fileIds = Collections.singletonList(getFileId()); - taskId = getMetadataManager() - .addRunningTask(studyId, OPERATION_NAME, fileIds, resume, Type.LOAD, + VariantStorageMetadataManager metadataManager = getMetadataManager(); + LinkedHashSet sampleIdsFromFileId = metadataManager.getSampleIdsFromFileId(studyId, getFileId()); + VariantStorageEngine.SplitData splitData = VariantStorageEngine.SplitData.from(options); + + taskId = metadataManager + .addRunningTask(studyId, OPERATION_NAME, Collections.singletonList(getFileId()), resume, Type.LOAD, operation -> { if (operation.getName().equals(OPERATION_NAME)) { - if (operation.currentStatus().equals(Status.ERROR)) { + if (operation.currentStatus() == Status.ERROR) { Integer fileId = operation.getFileIds().get(0); - String fileName = getMetadataManager().getFileName(studyMetadata.getId(), fileId); + String fileName = metadataManager.getFileName(studyMetadata.getId(), fileId); logger.warn("Pending load operation for file " + fileName + " (" + fileId + ')'); } else { ongoingLoads.incrementAndGet(); } + if (splitData != VariantStorageEngine.SplitData.CHROMOSOME && splitData != VariantStorageEngine.SplitData.REGION) { + // Do not allow any concurrent load operation on files sharing samples + for (Integer fileId : operation.getFileIds()) { + Set samples = metadataManager.getSampleIdsFromFileId(studyId, fileId); + for (Integer sample : samples) { + if (sampleIdsFromFileId.contains(sample)) { + return false; + } + } + } + } return true; } else { return false; @@ -490,7 +505,7 @@ private void logLoadResults(VariantFileMetadata variantFileMetadata, int duplica if (skipped > 0) { logger.info("There were " + skipped + " skipped variants"); for (VariantType type : VariantType.values()) { - if (!TARGET_VARIANT_TYPE_SET.contains(type)) { + if (UNSUPPORTED_VARIANT_TYPE_SET.contains(type)) { Long countByType = variantFileMetadata.getStats().getTypeCount().get(type.toString()); if (countByType != null && countByType > 0) { logger.info(" * Of which " + countByType + " are " + type.toString() + " variants."); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java new file mode 100644 index 00000000000..6aa472ee735 --- /dev/null +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java @@ -0,0 +1,37 @@ +package org.opencb.opencga.storage.hadoop.variant; + +import org.opencb.commons.datastore.core.Query; +import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; +import org.opencb.opencga.storage.core.utils.CellBaseUtils; +import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; +import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; + +import java.util.List; + +import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.STUDY; +import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.*; + +public class HadoopVariantQueryParser extends VariantQueryParser { + public HadoopVariantQueryParser(CellBaseUtils cellBaseUtils, VariantStorageMetadataManager metadataManager) { + super(cellBaseUtils, metadataManager); + } + + @Override + protected Query preProcessQuery(Query originalQuery, QueryOptions options, VariantQueryProjection projection) { + Query query = super.preProcessQuery(originalQuery, options, projection); + List studyNames = metadataManager.getStudyNames(); + + if (isValidParam(query, STUDY) && studyNames.size() == 1) { + String study = query.getString(STUDY.key()); + if (!isNegated(study)) { + // Check that study exists + metadataManager.getStudyId(study); + query.remove(STUDY.key()); + } + } + + convertGenesToRegionsQuery(query, cellBaseUtils); + return query; + } +} diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngine.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngine.java index ff93b19fe54..e0cbbfb3963 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngine.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngine.java @@ -26,7 +26,6 @@ import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.util.StopWatch; -import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.VariantType; import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; @@ -47,7 +46,6 @@ import org.opencb.opencga.storage.core.metadata.VariantMetadataFactory; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.*; -import org.opencb.opencga.storage.core.utils.CellBaseUtils; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.VariantStoragePipeline; @@ -55,10 +53,12 @@ import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; +import org.opencb.opencga.storage.core.variant.adaptors.sample.VariantSampleDataManager; import org.opencb.opencga.storage.core.variant.annotation.VariantAnnotationManager; import org.opencb.opencga.storage.core.variant.annotation.annotators.VariantAnnotator; import org.opencb.opencga.storage.core.variant.io.VariantExporter; import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; import org.opencb.opencga.storage.core.variant.query.executors.*; import org.opencb.opencga.storage.core.variant.score.VariantScoreFormatDescriptor; import org.opencb.opencga.storage.core.variant.search.SamplesSearchIndexVariantQueryExecutor; @@ -115,25 +115,16 @@ import static org.opencb.opencga.storage.core.variant.VariantStorageOptions.*; import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.REGION; -import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.STUDY; -import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.*; import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageOptions.*; import static org.opencb.opencga.storage.hadoop.variant.gaps.FillGapsDriver.*; public class HadoopVariantStorageEngine extends VariantStorageEngine implements Configurable { public static final String STORAGE_ENGINE_ID = "hadoop"; - public static final EnumSet TARGET_VARIANT_TYPE_SET = EnumSet.of( - VariantType.SNV, VariantType.SNP, - VariantType.INDEL, - VariantType.MNV, VariantType.MNP, - VariantType.INSERTION, VariantType.DELETION, - VariantType.CNV, - VariantType.COPY_NUMBER, VariantType.COPY_NUMBER_LOSS, VariantType.COPY_NUMBER_GAIN, - VariantType.DUPLICATION, VariantType.TANDEM_DUPLICATION, VariantType.TRANSLOCATION, - VariantType.BREAKEND, - VariantType.SV, VariantType.SYMBOLIC + public static final EnumSet UNSUPPORTED_VARIANT_TYPE_SET = EnumSet.of( + VariantType.NO_VARIATION, VariantType.MIXED ); + public static final EnumSet TARGET_VARIANT_TYPE_SET = EnumSet.complementOf(UNSUPPORTED_VARIANT_TYPE_SET); public static final String FILE_ID = "fileId"; public static final String STUDY_ID = "studyId"; @@ -698,6 +689,11 @@ public void removeSamples(String study, List samples, URI outdir) throws remove(study, fullyDeletedFiles, samples, outdir); } + @Override + protected TaskMetadata preRemove(String study, List files, List samples) throws StorageEngineException { + return super.preRemove(study, files, samples); + } + @Override public void removeFiles(String study, List files, URI outdir) throws StorageEngineException { remove(study, files, Collections.emptyList(), outdir); @@ -1031,46 +1027,48 @@ private synchronized HBaseManager getHBaseManager(Configuration configuration) { @Override public ParsedVariantQuery parseQuery(Query originalQuery, QueryOptions options) { try { - Query query = preProcessQuery(originalQuery, options); - ParsedVariantQuery parsedVariantQuery = getVariantQueryParser().parseQuery(query, options, true); - parsedVariantQuery.setInputQuery(originalQuery); - return parsedVariantQuery; + return getVariantQueryParser().parseQuery(originalQuery, options); } catch (StorageEngineException e) { throw VariantQueryException.internalException(e).setQuery(originalQuery); } } @Override - public Query preProcessQuery(Query originalQuery, QueryOptions options) { - Query query = super.preProcessQuery(originalQuery, options); - - VariantStorageMetadataManager metadataManager; - CellBaseUtils cellBaseUtils; - try { - metadataManager = getMetadataManager(); - cellBaseUtils = getCellBaseUtils(); - } catch (StorageEngineException e) { - throw VariantQueryException.internalException(e); - } - List studyNames = metadataManager.getStudyNames(); - - if (isValidParam(query, STUDY) && studyNames.size() == 1) { - String study = query.getString(STUDY.key()); - if (!isNegated(study)) { - try { - // Check that study exists - getMetadataManager().getStudyId(study); - } catch (StorageEngineException e) { - throw VariantQueryException.internalException(e); - } - query.remove(STUDY.key()); - } - } - - convertGenesToRegionsQuery(query, cellBaseUtils); - return query; + protected VariantQueryParser getVariantQueryParser() throws StorageEngineException { + return new HadoopVariantQueryParser(getCellBaseUtils(), getMetadataManager()); } +// @Override +// public Query preProcessQuery(Query originalQuery, QueryOptions options) { +// Query query = super.preProcessQuery(originalQuery, options); +// +// VariantStorageMetadataManager metadataManager; +// CellBaseUtils cellBaseUtils; +// try { +// metadataManager = getMetadataManager(); +// cellBaseUtils = getCellBaseUtils(); +// } catch (StorageEngineException e) { +// throw VariantQueryException.internalException(e); +// } +// List studyNames = metadataManager.getStudyNames(); +// +// if (isValidParam(query, STUDY) && studyNames.size() == 1) { +// String study = query.getString(STUDY.key()); +// if (!isNegated(study)) { +// try { +// // Check that study exists +// getMetadataManager().getStudyId(study); +// } catch (StorageEngineException e) { +// throw VariantQueryException.internalException(e); +// } +// query.remove(STUDY.key()); +// } +// } +// +// convertGenesToRegionsQuery(query, cellBaseUtils); +// return query; +// } + @Override protected List initVariantAggregationExecutors() { List executors = new ArrayList<>(3); @@ -1164,8 +1162,8 @@ public VariantStorageMetadataManager getMetadataManager() throws StorageEngineEx } @Override - public DataResult getSampleData(String variant, String study, QueryOptions options) throws StorageEngineException { - return new HBaseVariantSampleDataManager(getDBAdaptor(), getCellBaseUtils()).getSampleData(variant, study, options); + protected VariantSampleDataManager getVariantSampleDataManager() throws StorageEngineException { + return new HBaseVariantSampleDataManager(getDBAdaptor()); } @Override @@ -1177,7 +1175,7 @@ protected List initVariantQueryExecutors() throws StorageE executors.add(new SampleIndexCompoundHeterozygousQueryExecutor( getMetadataManager(), getStorageEngineId(), getOptions(), this, getSampleIndexDBAdaptor(), getDBAdaptor())); executors.add(new BreakendVariantQueryExecutor( - getMetadataManager(), getStorageEngineId(), getOptions(), new SampleIndexVariantQueryExecutor( + getStorageEngineId(), getOptions(), new SampleIndexVariantQueryExecutor( getDBAdaptor(), getSampleIndexDBAdaptor(), getStorageEngineId(), getOptions()), getDBAdaptor())); executors.add(new SamplesSearchIndexVariantQueryExecutor( getDBAdaptor(), getVariantSearchManager(), getStorageEngineId(), dbName, getConfiguration(), getOptions())); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java index 40d45a70bee..c672e94fb58 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java @@ -2,14 +2,14 @@ import com.google.common.collect.Iterators; import org.opencb.biodata.models.variant.Variant; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor; import org.opencb.opencga.storage.core.variant.query.ParsedQuery; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.query.executors.VariantQueryExecutor; import org.slf4j.Logger; @@ -76,23 +76,19 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) { return false; } - @Override - public DataResult count(Query query) { - throw new UnsupportedOperationException("Count not implemented in " + getClass()); - } - /** * Intersect result of column hbase scan and full phoenix query. * Use {@link org.opencb.opencga.storage.core.variant.adaptors.iterators.MultiVariantDBIterator}. * - * @param query Query - * @param options Options + * @param variantQuery Parsed query * @param iterator Shall the resulting object be an iterator instead of a DataResult * @return DataResult or Iterator with the variants that matches the query */ @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator) { + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) { logger.info("HBase column intersect"); + Query query = variantQuery.getQuery(); + QueryOptions options = variantQuery.getInputOptions(); // Build the query with only one query filter -> Single HBase column filter // diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHBaseQueryParser.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHBaseQueryParser.java index 1de83e96382..9c838765146 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHBaseQueryParser.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHBaseQueryParser.java @@ -577,9 +577,12 @@ public Scan parseQuery(VariantQueryProjection selectElements, Query query, Query //// filters.addFilter(keyOnlyFilter); // scan.addColumn(genomeHelper.getColumnFamily(), VariantPhoenixHelper.VariantColumn.TYPE.bytes()); // } - if (selectElements.getFields().contains(VariantField.TYPE) || !scan.hasFamilies()) { - scan.addColumn(family, VariantColumn.TYPE.bytes()); - } + + // Alleles must always be included. + scan.addColumn(family, VariantColumn.ALLELES.bytes()); + // Because alleles column may be empty, we must still ensure that we get, at least, one result per row. + // Include "type" column, which is never empty. + scan.addColumn(family, VariantColumn.TYPE.bytes()); // if (!columnPrefixes.isEmpty()) { // MultipleColumnPrefixFilter columnPrefixFilter = new MultipleColumnPrefixFilter( diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHadoopDBAdaptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHadoopDBAdaptor.java index 3d6fe440da1..d3c608c8b42 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHadoopDBAdaptor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/VariantHadoopDBAdaptor.java @@ -33,7 +33,6 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.*; import org.opencb.opencga.core.config.storage.StorageConfiguration; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.ProjectMetadata; @@ -45,6 +44,7 @@ import org.opencb.opencga.storage.core.variant.annotation.VariantAnnotationManager; import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjectionParser; @@ -52,6 +52,7 @@ import org.opencb.opencga.storage.hadoop.auth.HBaseCredentials; import org.opencb.opencga.storage.hadoop.utils.HBaseManager; import org.opencb.opencga.storage.hadoop.variant.GenomeHelper; +import org.opencb.opencga.storage.hadoop.variant.HadoopVariantQueryParser; import org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageEngine; import org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageOptions; import org.opencb.opencga.storage.hadoop.variant.adaptors.iterators.VariantHBaseResultSetIterator; @@ -221,33 +222,35 @@ public void close() throws IOException { } @Override - public VariantQueryResult get(ParsedVariantQuery query, QueryOptions options) { + public VariantQueryResult get(ParsedVariantQuery query) { List variants = new LinkedList<>(); - VariantDBIterator iterator = iterator(query, options); + VariantDBIterator iterator = iterator(query); + QueryOptions options = new QueryOptions(query.getInputOptions()); iterator.forEachRemaining(variants::add); long numTotalResults; - if (options == null) { - numTotalResults = variants.size(); - } else { - if (options.getInt(QueryOptions.LIMIT, -1) >= 0) { - if (options.getBoolean(QueryOptions.COUNT, false)) { - numTotalResults = count(query).first(); - } else { - numTotalResults = -1; - } + if (options.getInt(QueryOptions.LIMIT, -1) >= 0) { + if (options.getBoolean(QueryOptions.COUNT, false)) { + numTotalResults = count(query).first(); } else { - // There are no limit. Do not count. - numTotalResults = variants.size(); + numTotalResults = -1; } + } else { + // There are no limit. Do not count. + numTotalResults = variants.size(); } - VariantQueryResult result = new VariantQueryResult<>(iterator.getTime(TimeUnit.MILLISECONDS), variants.size(), - numTotalResults, null, variants, null, HadoopVariantStorageEngine.STORAGE_ENGINE_ID) + return new VariantQueryResult<>(iterator.getTime(TimeUnit.MILLISECONDS), variants.size(), + numTotalResults, null, variants, HadoopVariantStorageEngine.STORAGE_ENGINE_ID, query) .setFetchTime(iterator.getTimeFetching(TimeUnit.MILLISECONDS)) .setConvertTime(iterator.getTimeConverting(TimeUnit.MILLISECONDS)); - return addSamplesMetadataIfRequested(result, query.getQuery(), options, getMetadataManager()); + } + + @Override + @Deprecated + public VariantQueryResult get(Query query, QueryOptions options) { + return get(new HadoopVariantQueryParser(null, getMetadataManager()).parseQuery(query, options)); } @Override @@ -334,7 +337,8 @@ public DataResult distinct(Query query, String field) { } @Override - public VariantDBIterator iterator(ParsedVariantQuery variantQuery, QueryOptions options) { + public VariantDBIterator iterator(ParsedVariantQuery variantQuery) { + QueryOptions options = variantQuery.getInputOptions(); if (options == null) { options = new QueryOptions(); } else { @@ -385,7 +389,7 @@ public VariantDBIterator iterator(ParsedVariantQuery variantQuery, QueryOptions private VariantHBaseResultSetIterator phoenixIterator(ParsedVariantQuery variantQuery, QueryOptions options, HBaseVariantConverterConfiguration converterConfiguration) { VariantStorageMetadataManager metadataManager = getMetadataManager(); - new VariantQueryParser(null, metadataManager).optimize(variantQuery); + new HadoopVariantQueryParser(null, metadataManager).optimize(variantQuery); logger.debug("Table name = " + variantTable); logger.info("Query : " + VariantQueryUtils.printQuery(variantQuery.getQuery())); @@ -679,7 +683,7 @@ private class VariantQueryIteratorCustomSplit extends MultiVariantDBIterator.Var VariantQueryIteratorCustomSplit(Iterator variants, Query query, int batchSize, QueryOptions options) { super(variants, query, batchSize); - parser = new VariantQueryParser(null, getMetadataManager()); + parser = new HadoopVariantQueryParser(null, getMetadataManager()); variantQuery = parser.parseQuery(query, options); cts = sizeOrOne(variantQuery.getConsequenceTypes()); bts = sizeOrOne(variantQuery.getBiotypes()); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactory.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactory.java index eedeeb3c9ed..9e06ebc4b51 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactory.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactory.java @@ -19,6 +19,8 @@ import htsjdk.variant.variantcontext.Allele; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; import org.apache.phoenix.query.QueryConstants; @@ -28,6 +30,7 @@ import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.VariantBuilder; import org.opencb.biodata.models.variant.avro.*; +import org.opencb.opencga.storage.hadoop.variant.GenomeHelper; import java.sql.ResultSet; import java.sql.SQLException; @@ -43,12 +46,15 @@ */ public class VariantPhoenixKeyFactory { + public static final Integer UINT_SIZE = PUnsignedInt.INSTANCE.getByteSize(); protected static final String SV_ALTERNATE_SEPARATOR = "|"; protected static final String SV_ALTERNATE_SEPARATOR_SPLIT = "\\" + SV_ALTERNATE_SEPARATOR; public static final Comparator HBASE_KEY_CHROMOSOME_COMPARATOR = (c1, c2) -> Bytes.compareTo( VariantPhoenixKeyFactory.generateSimpleVariantRowKey(c1, 1, "N", "N"), VariantPhoenixKeyFactory.generateSimpleVariantRowKey(c2, 1, "N", "N")); + public static final String HASH_PREFIX = "#"; + public static final byte[] HASH_PREFIX_BYTES = Bytes.toBytes(HASH_PREFIX); public static byte[] generateVariantRowKey(String chrom, int position) { return generateSimpleVariantRowKey(chrom, position, "", ""); @@ -59,6 +65,26 @@ public static byte[] generateVariantRowKey(Variant var) { var.getSv()); } + public static byte[] generateVariantRowKey(ResultSet resultSet) { + String chromosome = null; + Integer start = null; + String reference = null; + String alternate = null; + try { + chromosome = resultSet.getString(VariantPhoenixSchema.VariantColumn.CHROMOSOME.column()); + start = resultSet.getInt(VariantPhoenixSchema.VariantColumn.POSITION.column()); + reference = resultSet.getString(VariantPhoenixSchema.VariantColumn.REFERENCE.column()); + alternate = resultSet.getString(VariantPhoenixSchema.VariantColumn.ALTERNATE.column()); + + return generateVariantRowKey(chromosome, start, null, reference, alternate, null); + } catch (RuntimeException | SQLException e) { + throw new IllegalStateException("Fail to generate row key from Phoenix result set: " + chromosome + + ':' + start + + ':' + (reference == null ? "-" : reference) + + ':' + (alternate == null ? "-" : alternate), e); + } + } + public static byte[] generateVariantRowKey(VariantAnnotation variantAnnotation) { byte[] bytesRowKey = null; if (variantAnnotation.getAdditionalAttributes() != null) { @@ -97,6 +123,12 @@ public static byte[] generateSimpleVariantRowKey(String chrom, int position, Str return generateVariantRowKey(chrom, position, null, ref, alt, null); } + public static boolean mightHashAlleles(Variant variant) { + int size = getSize(variant); + return size > HConstants.MAX_ROW_LENGTH; + } + + /** * Generates a Row key based on Chromosome, start, end (optional), ref and alt.
*

    @@ -114,16 +146,16 @@ public static byte[] generateSimpleVariantRowKey(String chrom, int position, Str */ public static byte[] generateVariantRowKey(String chrom, int start, Integer end, String ref, String alt, StructuralVariation sv) { chrom = Region.normalizeChromosome(chrom); - int size = PVarchar.INSTANCE.estimateByteSizeFromLength(chrom.length()) - + QueryConstants.SEPARATOR_BYTE_ARRAY.length - + PUnsignedInt.INSTANCE.getByteSize() - + PVarchar.INSTANCE.estimateByteSizeFromLength(ref.length()); alt = buildSymbolicAlternate(ref, alt, end, sv); - if (!alt.isEmpty()) { - size += QueryConstants.SEPARATOR_BYTE_ARRAY.length - + PVarchar.INSTANCE.estimateByteSizeFromLength(alt.length()); + int size = getSize(chrom, ref, alt); + + if (size > HConstants.MAX_ROW_LENGTH) { + // This is a problem. The row key is too long. + // Use hashCode for reference/alternate/SV fields + ref = hashAllele(ref); + alt = hashAllele(alt); + size = getSize(chrom, ref, alt); } - byte[] rk = new byte[size]; int offset = 0; @@ -143,6 +175,31 @@ public static byte[] generateVariantRowKey(String chrom, int start, Integer end, return rk; } + private static int getSize(Variant variant) { + String symbolicAlternate = buildSymbolicAlternate(variant); + return getSize(variant.getChromosome(), variant.getReference(), symbolicAlternate); + } + + private static int getSize(String chrom, String ref, String alt) { + int size = PVarchar.INSTANCE.estimateByteSizeFromLength(chrom.length()) + + QueryConstants.SEPARATOR_BYTE_ARRAY.length + + PUnsignedInt.INSTANCE.getByteSize() + + PVarchar.INSTANCE.estimateByteSizeFromLength(ref.length()); + if (!alt.isEmpty()) { + size += QueryConstants.SEPARATOR_BYTE_ARRAY.length + + PVarchar.INSTANCE.estimateByteSizeFromLength(alt.length()); + } + return size; + } + + public static String hashAllele(String ref) { + return HASH_PREFIX + Integer.toString(ref.hashCode()); + } + + public static String buildAlleles(Variant v) { + return v.getReference() + SV_ALTERNATE_SEPARATOR + buildSymbolicAlternate(v); + } + public static String buildSymbolicAlternate(Variant v) { return buildSymbolicAlternate(v.getReference(), v.getAlternate(), v.getEnd(), v.getSv()); } @@ -215,6 +272,26 @@ public static String extractChrFromVariantRowKey(byte[] variantRowKey, int offse return (String) PVarchar.INSTANCE.toObject(variantRowKey, offset, chrPosSeparator, PVarchar.INSTANCE); } + public static Variant extractVariantFromResult(Result result) { + byte[] variantRowKey = result.getRow(); + + int chrPosSeparator = ArrayUtils.indexOf(variantRowKey, (byte) 0); + int referenceOffset = chrPosSeparator + 1 + UINT_SIZE; + if (variantRowKey.length > (referenceOffset + HASH_PREFIX_BYTES.length) + && Bytes.equals(variantRowKey, referenceOffset, HASH_PREFIX_BYTES.length, + HASH_PREFIX_BYTES, 0, HASH_PREFIX_BYTES.length)) { + // The reference and alternate are hashed. + // The type and alleles are stored in the result + byte[] type = result.getValue(GenomeHelper.COLUMN_FAMILY_BYTES, + VariantPhoenixSchema.VariantColumn.TYPE.bytes()); + byte[] alleles = result.getValue(GenomeHelper.COLUMN_FAMILY_BYTES, + VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + return extractVariantFromVariantRowKey(variantRowKey, type, alleles); + } else { + return extractVariantFromVariantRowKey(variantRowKey, null, null); + } + } + public static Variant extractVariantFromResultSet(ResultSet resultSet) { String chromosome = null; Integer start = null; @@ -226,9 +303,10 @@ public static Variant extractVariantFromResultSet(ResultSet resultSet) { reference = resultSet.getString(VariantPhoenixSchema.VariantColumn.REFERENCE.column()); alternate = resultSet.getString(VariantPhoenixSchema.VariantColumn.ALTERNATE.column()); + String alleles = resultSet.getString(VariantPhoenixSchema.VariantColumn.ALLELES.column()); String type = resultSet.getString(VariantPhoenixSchema.VariantColumn.TYPE.column()); - return buildVariant(chromosome, start, reference, alternate, type); + return buildVariant(chromosome, start, reference, alternate, type, alleles); } catch (RuntimeException | SQLException e) { throw new IllegalStateException("Fail to parse variant: " + chromosome + ':' + start @@ -237,13 +315,12 @@ public static Variant extractVariantFromResultSet(ResultSet resultSet) { } } - public static Variant extractVariantFromVariantRowKey(byte[] variantRowKey) { + public static Variant extractVariantFromVariantRowKey(byte[] variantRowKey, byte[] type, byte[] alleles) { int chrPosSeparator = ArrayUtils.indexOf(variantRowKey, (byte) 0); String chromosome = (String) PVarchar.INSTANCE.toObject(variantRowKey, 0, chrPosSeparator, PVarchar.INSTANCE); - Integer intSize = PUnsignedInt.INSTANCE.getByteSize(); - int position = (Integer) PUnsignedInt.INSTANCE.toObject(variantRowKey, chrPosSeparator + 1, intSize, PUnsignedInt.INSTANCE); - int referenceOffset = chrPosSeparator + 1 + intSize; + int position = (Integer) PUnsignedInt.INSTANCE.toObject(variantRowKey, chrPosSeparator + 1, UINT_SIZE, PUnsignedInt.INSTANCE); + int referenceOffset = chrPosSeparator + 1 + UINT_SIZE; int refAltSeparator = ArrayUtils.indexOf(variantRowKey, (byte) 0, referenceOffset); String reference; String alternate; @@ -257,8 +334,16 @@ public static Variant extractVariantFromVariantRowKey(byte[] variantRowKey) { alternate = (String) PVarchar.INSTANCE.toObject(variantRowKey, refAltSeparator + 1, variantRowKey.length - (refAltSeparator + 1), PVarchar.INSTANCE); } + String typeStr = null; + String alleleStr = null; + if (type != null) { + typeStr = (String) PVarchar.INSTANCE.toObject(type); + } + if (alleles != null) { + alleleStr = (String) PVarchar.INSTANCE.toObject(alleles); + } try { - return buildVariant(chromosome, position, reference, alternate, null); + return buildVariant(chromosome, position, reference, alternate, typeStr, alleleStr); } catch (RuntimeException e) { throw new IllegalStateException("Fail to parse variant: " + chromosome + ':' + position @@ -268,7 +353,17 @@ public static Variant extractVariantFromVariantRowKey(byte[] variantRowKey) { } } - public static Variant buildVariant(String chromosome, int start, String reference, String alternate, String type) { + public static Variant buildVariant(String chromosome, int start, String reference, String alternate, String type, String alleles) { + if ((reference != null && reference.startsWith(HASH_PREFIX)) || (alternate != null && alternate.startsWith(HASH_PREFIX))) { + if (StringUtils.isNotEmpty(alleles)) { + int i1 = alleles.indexOf(SV_ALTERNATE_SEPARATOR); + reference = alleles.substring(0, i1); + alternate = alleles.substring(i1 + SV_ALTERNATE_SEPARATOR.length()); + } else { + throw new IllegalStateException("Reference and alternate are hashed, but alleles is empty!" + + " '" + chromosome + "' '" + start + "' '" + reference + "' '" + alternate + "'"); + } + } if (alternate != null && alternate.length() > 5 && alternate.contains(SV_ALTERNATE_SEPARATOR)) { Integer end = null; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchema.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchema.java index 1d408da09b4..eb627dcaeda 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchema.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchema.java @@ -105,6 +105,7 @@ public enum VariantColumn implements Column { CI_END_R("CI_END_R", PUnsignedInt.INSTANCE), TYPE("TYPE", PVarchar.INSTANCE), + ALLELES("ALLELES", PVarchar.INSTANCE), ANNOTATION_ID(ANNOTATION_PREFIX + "ID", PInteger.INSTANCE), diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchemaManager.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchemaManager.java index ae8ce82ee84..2cb6bbe5403 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchemaManager.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixSchemaManager.java @@ -79,7 +79,7 @@ public void registerStudyColumns(int studyId) throws StorageEngineException { registerNewFiles(studyId, new ArrayList<>(metadataManager.getIndexedFiles(studyId))); List cohortIds = new LinkedList<>(); - for (CohortMetadata cohort : metadataManager.getCalculatedCohorts(studyId)) { + for (CohortMetadata cohort : metadataManager.getCalculatedOrPartialCohorts(studyId)) { cohortIds.add(cohort.getId()); } registerNewCohorts(studyId, cohortIds); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantSqlQueryParser.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantSqlQueryParser.java index bb9737144d2..6a7d9fe5518 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantSqlQueryParser.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantSqlQueryParser.java @@ -44,6 +44,7 @@ import org.opencb.opencga.storage.core.variant.query.*; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjectionParser; +import org.opencb.opencga.storage.hadoop.variant.HadoopVariantQueryParser; import org.opencb.opencga.storage.hadoop.variant.converters.HBaseToVariantConverter; import org.opencb.opencga.storage.hadoop.variant.converters.annotation.VariantAnnotationToPhoenixConverter; import org.opencb.opencga.storage.hadoop.variant.converters.study.HBaseToStudyEntryConverter; @@ -116,7 +117,7 @@ public VariantSqlQueryParser(String variantTable, @Deprecated public String parse(Query query, QueryOptions options) { - VariantQueryParser parser = new VariantQueryParser(null, metadataManager); + VariantQueryParser parser = new HadoopVariantQueryParser(null, metadataManager); ParsedVariantQuery variantQuery = parser.parseQuery(query, options, true); return parse(variantQuery, options); } @@ -124,8 +125,6 @@ public String parse(Query query, QueryOptions options) { public String parse(ParsedVariantQuery variantQuery, QueryOptions options) { Query query = variantQuery.getQuery(); - StringBuilder sb = new StringBuilder("SELECT "); - try { Set dynamicColumns = new HashSet<>(); @@ -144,21 +143,22 @@ public String parse(ParsedVariantQuery variantQuery, QueryOptions options) { hints.add(HintNode.Hint.valueOf(hint)); } } + + StringBuilder sb = new StringBuilder("SELECT "); if (!hints.isEmpty()) { sb.append("/*+ ").append(hints.stream().map(Object::toString).collect(Collectors.joining(","))).append(" */ "); } - appendProjectedColumns(sb, variantQuery.getProjection(), options); appendFromStatement(sb, dynamicColumns); appendWhereStatement(sb, regionFilters, filters); appendOrderby(options, sb); appendLimitSkip(options, sb); + return sb.toString(); } catch (VariantQueryException e) { e.setQuery(query); throw e; } - return sb.toString(); } private void appendOrderby(QueryOptions options, StringBuilder sb) { @@ -216,13 +216,13 @@ protected StringBuilder appendProjectedColumns(StringBuilder sb, VariantQueryPro return sb.append(" COUNT(*) "); } else { Set returnedFields = projection.getFields(); - Collection studyIds = projection.getStudyIds(); sb.append(VariantColumn.CHROMOSOME).append(',') .append(VariantColumn.POSITION).append(',') .append(VariantColumn.REFERENCE).append(',') .append(VariantColumn.ALTERNATE).append(',') - .append(VariantColumn.TYPE); + .append(VariantColumn.TYPE).append(',') + .append(VariantColumn.ALLELES); for (VariantQueryProjection.StudyVariantQueryProjection study : projection.getStudies().values()) { int studyId = study.getId(); @@ -600,10 +600,17 @@ private String getVariantFilter(List variants) { Iterator iterator = variants.iterator(); while (iterator.hasNext()) { Variant variant = iterator.next(); + + String reference = variant.getReference(); + String alternate = VariantPhoenixKeyFactory.buildSymbolicAlternate(variant); + if (VariantPhoenixKeyFactory.mightHashAlleles(variant)) { + reference = VariantPhoenixKeyFactory.hashAllele(reference); + alternate = VariantPhoenixKeyFactory.hashAllele(alternate); + } sb.append("('").append(checkStringValue(variant.getChromosome())).append("', ") .append(variant.getStart()).append(", ") - .append('\'').append(checkStringValue(variant.getReference())).append("', ") - .append('\'').append(checkStringValue(VariantPhoenixKeyFactory.buildSymbolicAlternate(variant))).append("') "); + .append('\'').append(checkStringValue(reference)).append("', ") + .append('\'').append(checkStringValue(alternate)).append("') "); if (iterator.hasNext()) { sb.append(','); } @@ -710,9 +717,9 @@ protected List getOtherFilters(ParsedVariantQuery variantQuery, QueryOpt protected void addVariantFilters(ParsedVariantQuery variantQuery, QueryOptions options, List filters) { Query query = variantQuery.getQuery(); - addQueryFilter(query, REFERENCE, VariantColumn.REFERENCE, filters); + addQueryFilter(query, REFERENCE, VariantColumn.REFERENCE, filters, VariantSqlQueryParser::hashAlleles); - addQueryFilter(query, ALTERNATE, VariantColumn.ALTERNATE, filters); + addQueryFilter(query, ALTERNATE, VariantColumn.ALTERNATE, filters, VariantSqlQueryParser::hashAlleles); addQueryFilter(query, TYPE, VariantColumn.TYPE, filters); @@ -966,36 +973,39 @@ protected void addVariantFilters(ParsedVariantQuery variantQuery, QueryOptions o } boolean multiFileSample = VariantStorageEngine.SplitData.MULTI.equals(sampleMetadata.getSplitData()); List sampleFiles = new ArrayList<>(); + // First file does not have the fileID in the column name + Integer firstSampleFile = sampleMetadata.getFiles().get(0); + if (multiFileSample) { if (fileIds.isEmpty()) { - sampleFiles.add(null); // First file does not have the fileID in the column name List fileIdsFromSampleId = sampleMetadata.getFiles(); - sampleFiles.addAll(fileIdsFromSampleId.subList(1, fileIdsFromSampleId.size())); + sampleFiles.addAll(fileIdsFromSampleId); } else { for (Pair fileIdPair : fileIds) { - if (fileIdPair.getKey().equals(studyId)) { - Integer fileId = fileIdPair.getValue(); - int idx = sampleMetadata.getFiles().indexOf(fileId); - if (idx == 0) { - sampleFiles.add(null); // First file does not have the fileID in the column name - } else if (idx > 0) { - sampleFiles.add(fileId); // First file does not have the fileID in the column name - } + if (fileIdPair.getKey().equals(studyId) + && sampleMetadata.getFiles().contains(fileIdPair.getValue())) { + sampleFiles.add(fileIdPair.getValue()); } } } } else { - sampleFiles.add(null); // First file does not have the fileID in the column name + // Non multi file sample + sampleFiles.add(firstSampleFile); } for (Integer sampleFile : sampleFiles) { + if (!metadataManager.isFileIndexed(studyId, sampleFile)) { + // Skip non indexed files + continue; + } List sampleFileGtFilters = new ArrayList<>(genotypes.size()); for (String genotype : genotypes) { if (negated) { genotype = removeNegation(genotype); } String key; - if (sampleFile == null) { + if (Objects.equals(sampleFile, firstSampleFile)) { + // Special scenario for the first file. Column name does not contain the fileId key = buildSampleColumnKey(studyId, sampleId, new StringBuilder()).toString(); } else { key = buildSampleColumnKey(studyId, sampleId, sampleFile, new StringBuilder()).toString(); @@ -1079,6 +1089,16 @@ protected void addVariantFilters(ParsedVariantQuery variantQuery, QueryOptions o } } + private static List hashAlleles(String r) { + String[] split = r.split(","); + List list = new ArrayList<>(split.length * 2); + for (String s : split) { + list.add(s); + list.add(VariantPhoenixKeyFactory.hashAllele(s)); + } + return list; + } + private void addFileFilterFieldFilter(QueryOperation filtersOperation, List filterValues, StringBuilder sb, Pair fileIdPair) { sb.append(" ( "); @@ -1230,27 +1250,24 @@ private void addSampleDataFilter(ParsedVariantQuery query, List filters, SampleMetadata sampleMetadata = sampleDataFilter.getKey(); boolean multiFileSample = VariantStorageEngine.SplitData.MULTI.equals(sampleMetadata.getSplitData()); + // First file does not have the fileID in the column name + Integer firstSampleFile = sampleMetadata.getFiles().get(0); List sampleFiles = new ArrayList<>(); if (multiFileSample) { if (fileIds.isEmpty()) { - sampleFiles.add(null); // First file does not have the fileID in the column name List fileIdsFromSampleId = sampleMetadata.getFiles(); - sampleFiles.addAll(fileIdsFromSampleId.subList(1, fileIdsFromSampleId.size())); + sampleFiles.addAll(fileIdsFromSampleId); } else { for (Pair fileIdPair : fileIds) { - if (fileIdPair.getKey().equals(sampleMetadata.getStudyId())) { - Integer fileId = fileIdPair.getValue(); - int idx = sampleMetadata.getFiles().indexOf(fileId); - if (idx == 0) { - sampleFiles.add(null); // First file does not have the fileID in the column name - } else if (idx > 0) { - sampleFiles.add(fileId); // First file does not have the fileID in the column name - } + if (fileIdPair.getKey().equals(sampleMetadata.getStudyId()) + && sampleMetadata.getFiles().contains(fileIdPair.getValue())) { + sampleFiles.add(fileIdPair.getValue()); } } } } else { - sampleFiles.add(null); // First file does not have the fileID in the column name + // Non multi file sample + sampleFiles.add(firstSampleFile); } for (Integer sampleFile : sampleFiles) { List sampleFileFilters = new LinkedList<>(); @@ -1270,10 +1287,11 @@ private void addSampleDataFilter(ParsedVariantQuery query, List filters, sb.append("TO_NUMBER("); } sb.append('"'); - if (sampleFile != null) { - buildSampleColumnKey(sampleMetadata.getStudyId(), sampleMetadata.getId(), sampleFile, sb); - } else { + if (Objects.equals(sampleFile, firstSampleFile)) { + // Special scenario for the first file. Column name does not contain the fileId buildSampleColumnKey(sampleMetadata.getStudyId(), sampleMetadata.getId(), sb); + } else { + buildSampleColumnKey(sampleMetadata.getStudyId(), sampleMetadata.getId(), sampleFile, sb); } sb.append('"'); @@ -1287,10 +1305,11 @@ private void addSampleDataFilter(ParsedVariantQuery query, List filters, if (op.startsWith(">>") || op.startsWith("<<")) { sb.append(" OR \""); - if (sampleFile != null) { - buildSampleColumnKey(sampleMetadata.getStudyId(), sampleMetadata.getId(), sampleFile, sb); - } else { + if (Objects.equals(sampleFile, firstSampleFile)) { + // Special scenario for the first file. Column name does not contain the fileId buildSampleColumnKey(sampleMetadata.getStudyId(), sampleMetadata.getId(), sb); + } else { + buildSampleColumnKey(sampleMetadata.getStudyId(), sampleMetadata.getId(), sampleFile, sb); } sb.append('"'); @@ -1307,8 +1326,8 @@ private void addSampleDataFilter(ParsedVariantQuery query, List filters, if (multiFileSample) { // The first file is null. Get the actual fileId Integer actualFileId; - if (sampleFile == null) { - actualFileId = sampleMetadata.getFiles().get(0); + if (Objects.equals(sampleFile, firstSampleFile)) { + actualFileId = firstSampleFile; } else { actualFileId = sampleFile; } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/sample/HBaseVariantSampleDataManager.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/sample/HBaseVariantSampleDataManager.java index 588833cfb6e..31850f1f486 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/sample/HBaseVariantSampleDataManager.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/sample/HBaseVariantSampleDataManager.java @@ -15,7 +15,6 @@ import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; -import org.opencb.opencga.storage.core.utils.CellBaseUtils; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; @@ -26,8 +25,8 @@ import org.opencb.opencga.storage.hadoop.variant.GenomeHelper; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHadoopDBAdaptor; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.PhoenixHelper; -import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixKeyFactory; +import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema; import org.opencb.opencga.storage.hadoop.variant.converters.HBaseVariantConverterConfiguration; import org.opencb.opencga.storage.hadoop.variant.converters.VariantRow; import org.opencb.opencga.storage.hadoop.variant.converters.annotation.HBaseToVariantAnnotationConverter; @@ -45,31 +44,20 @@ public class HBaseVariantSampleDataManager extends VariantSampleDataManager { private final VariantHadoopDBAdaptor dbAdaptor; private final VariantStorageMetadataManager metadataManager; - private final CellBaseUtils cellBaseUtils; - - public HBaseVariantSampleDataManager(VariantHadoopDBAdaptor dbAdaptor, CellBaseUtils cellBaseUtils) { + public HBaseVariantSampleDataManager(VariantHadoopDBAdaptor dbAdaptor) { super(dbAdaptor); this.dbAdaptor = dbAdaptor; metadataManager = dbAdaptor.getMetadataManager(); - this.cellBaseUtils = cellBaseUtils; } @Override - protected DataResult getSampleData(String variantStr, String study, QueryOptions options, + protected DataResult getSampleData(Variant inputVariant, String study, QueryOptions options, List includeSamples, Set genotypes, int sampleLimit) { StopWatch stopWatch = StopWatch.createStarted(); Set includeFields = VariantField.getIncludeFields(options); - final Variant variant; - if (VariantQueryUtils.isVariantId(variantStr)) { - variant = new Variant(variantStr); - } else { - variant = cellBaseUtils.getVariant(variantStr); - } - variant.setId(variant.toString()); - int studyId = metadataManager.getStudyId(study); boolean includeNoneSamples = VariantQueryUtils.isNoneOrEmpty(includeSamples); @@ -90,6 +78,7 @@ protected DataResult getSampleData(String variantStr, String study, Que int skip = Math.max(0, options.getInt(QueryOptions.SKIP, 0)); int limit = Math.max(0, options.getInt(QueryOptions.LIMIT, 10)); + byte[] rowKey = VariantPhoenixKeyFactory.generateVariantRowKey(inputVariant); try { List samples = new ArrayList<>(limit); @@ -97,7 +86,7 @@ protected DataResult getSampleData(String variantStr, String study, Que dbAdaptor.getHBaseManager().act(dbAdaptor.getVariantTable(), table -> { // Create one GET for samples - Get get = new Get(VariantPhoenixKeyFactory.generateVariantRowKey(variant)); + Get get = new Get(rowKey); LinkedList filters = new LinkedList<>(); filters.add(new QualifierFilter(CompareFilter.CompareOp.EQUAL, @@ -139,7 +128,7 @@ protected DataResult getSampleData(String variantStr, String study, Que samples.add(sampleColumn.getSampleId()); sampleDataMap.add(sampleColumn); } - }).walk(); + }).walk(inputVariant); } }); @@ -148,8 +137,10 @@ protected DataResult getSampleData(String variantStr, String study, Que Set fileIdsFromSampleIds = metadataManager.getFileIdsFromSampleIds(studyId, samples); HBaseToVariantStatsConverter statsConverter = new HBaseToVariantStatsConverter(); List stats = new LinkedList<>(); + Variant variantResult = new Variant(inputVariant.toString()); dbAdaptor.getHBaseManager().act(dbAdaptor.getVariantTable(), table -> { - Get get = new Get(VariantPhoenixKeyFactory.generateVariantRowKey(variant)); + Get get = new Get(rowKey); + // Add file columns for (Integer fileId : fileIdsFromSampleIds) { get.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.buildFileColumnKey(studyId, fileId)); @@ -172,7 +163,7 @@ protected DataResult getSampleData(String variantStr, String study, Que Result result = table.get(get); if (result == null || result.isEmpty()) { - throw VariantQueryException.variantNotFound(variantStr); + throw VariantQueryException.variantNotFound(variantResult.toString()); } // Walk row VariantRow.walker(result) @@ -186,9 +177,9 @@ protected DataResult getSampleData(String variantStr, String study, Que }) .onVariantAnnotation(column -> { ImmutableBytesWritable b = column.toBytesWritable(); - variant.setAnnotation(new HBaseToVariantAnnotationConverter().convert(b.get(), b.getOffset(), b.getLength())); + variantResult.setAnnotation(new HBaseToVariantAnnotationConverter().convert(b)); }) - .walk(); + .walk(variantResult); }); // Convert to VariantSampleData @@ -204,13 +195,14 @@ protected DataResult getSampleData(String variantStr, String study, Que new ArrayList<>(fileIdsFromSampleIds))) .build()); - StudyEntry studyEntry = converter.convert(sampleDataMap, filesMap, variant, studyId); + variantResult.setId(inputVariant.toString()); + StudyEntry studyEntry = converter.convert(sampleDataMap, filesMap, variantResult, studyId); - variant.addStudyEntry(studyEntry); + variantResult.addStudyEntry(studyEntry); studyEntry.setStats(stats); -// String msg = "Queries : " + queries + " , readSamples : " + readSamples; + return new DataResult<>((int) stopWatch.getTime(TimeUnit.MILLISECONDS), Collections.emptyList(), 1, - Collections.singletonList(variant), 1); + Collections.singletonList(variantResult), 1); } catch (IOException e) { throw VariantQueryException.internalException(e); } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/analysis/julie/JulieToolDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/analysis/julie/JulieToolDriver.java index 2321e25f6e1..5192e7356f9 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/analysis/julie/JulieToolDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/analysis/julie/JulieToolDriver.java @@ -68,6 +68,7 @@ protected void parseAndValidateParameters() throws IOException { for (Integer studyId : metadataManager.getStudies().values()) { List studyCohorts = new LinkedList<>(); cohorts.put(studyId, studyCohorts); + // Only READY cohort stats are used for JulieTool for (CohortMetadata c : metadataManager.getCalculatedCohorts(studyId)) { studyCohorts.add(c.getId()); } @@ -78,6 +79,8 @@ protected void parseAndValidateParameters() throws IOException { @Override protected Job setupJob(Job job, String archiveTable, String variantTable) throws IOException { Scan scan = new Scan(); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.FULL_ANNOTATION.bytes()); for (Map.Entry> entry : cohorts.entrySet()) { Integer studyId = entry.getKey(); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/mr/VariantAnnotationRebuilderDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/mr/VariantAnnotationRebuilderDriver.java index 5690b374981..eb4bdc5b4eb 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/mr/VariantAnnotationRebuilderDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/mr/VariantAnnotationRebuilderDriver.java @@ -68,6 +68,8 @@ protected Job setupJob(Job job, String archiveTable, String variantTable) throws logger.info("Regenerate annotations for region " + region); VariantHBaseQueryParser.addRegionFilter(scan, new Region(region)); } + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.FULL_ANNOTATION.bytes()); VariantMapReduceUtil.configureMapReduceScan(scan, getConf()); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/pending/AnnotationPendingVariantsDescriptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/pending/AnnotationPendingVariantsDescriptor.java index c5ba3d73e83..c606a42043f 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/pending/AnnotationPendingVariantsDescriptor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/annotation/pending/AnnotationPendingVariantsDescriptor.java @@ -1,6 +1,7 @@ package org.opencb.opencga.storage.hadoop.variant.annotation.pending; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.io.compress.Compression; import org.apache.hadoop.hbase.util.Bytes; @@ -16,8 +17,7 @@ import java.io.IOException; import java.util.function.Function; -import static org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema.VariantColumn.SO; -import static org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema.VariantColumn.TYPE; +import static org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema.VariantColumn.*; public class AnnotationPendingVariantsDescriptor implements PendingVariantsDescriptor { @@ -50,6 +50,7 @@ public boolean createTableIfNeeded(String tableName, HBaseManager hBaseManager) public Scan configureScan(Scan scan, VariantStorageMetadataManager metadataManager) { scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, ALLELES.bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, SO.bytes()); return scan; } @@ -57,9 +58,22 @@ public Scan configureScan(Scan scan, VariantStorageMetadataManager metadataManag public Function getPendingEvaluatorMapper(VariantStorageMetadataManager metadataManager, boolean overwrite) { return value -> { + byte[] alleles = null; if (overwrite || isPending(value)) { + for (Cell cell : value.rawCells()) { + if (cell.getValueLength() > 0) { + if (Bytes.equals( + cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), + ALLELES.bytes(), 0, ALLELES.bytes().length)) { + alleles = CellUtil.cloneValue(cell); + } + } + } Put put = new Put(value.getRow()); put.addColumn(FAMILY, COLUMN, VALUE); + if (alleles != null) { + put.addColumn(FAMILY, ALLELES.bytes(), alleles); + } return put; } else { return new Delete(value.getRow()); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/HBaseToVariantConverter.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/HBaseToVariantConverter.java index 026a6a5e122..4bfcb0011e1 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/HBaseToVariantConverter.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/HBaseToVariantConverter.java @@ -48,8 +48,7 @@ import java.util.stream.Collectors; import static org.opencb.opencga.storage.core.variant.VariantStorageOptions.SEARCH_INDEX_LAST_TIMESTAMP; -import static org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixKeyFactory.extractVariantFromResultSet; -import static org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixKeyFactory.extractVariantFromVariantRowKey; +import static org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixKeyFactory.*; /** * Created on 20/11/15. @@ -210,7 +209,7 @@ private static class ResultToVariantConverter extends HBaseToVariantConverter 0) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/VariantRow.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/VariantRow.java index 4b0b6ef9584..6f4ee0ad344 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/VariantRow.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/VariantRow.java @@ -64,9 +64,7 @@ public static VariantRowWalkerBuilder walker(ResultSet resultSet) { public Variant getVariant() { if (variant == null) { if (result != null) { - byte[] row = result.getRow(); - Objects.requireNonNull(row, "Empty result. Missing variant rowkey."); - variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(row); + variant = VariantPhoenixKeyFactory.extractVariantFromResult(result); } else { variant = VariantPhoenixKeyFactory.extractVariantFromResultSet(resultSet); } @@ -104,11 +102,27 @@ public VariantRowWalkerBuilder walker() { } public void walk(VariantRowWalker walker) { - walk(walker, true, true, true, true, true); + walk(walker, true, true, true, true, true, null); } - protected void walk(VariantRowWalker walker, boolean file, boolean sample, boolean cohort, boolean score, boolean annotation) { - walker.variant(getVariant()); + protected void walk(VariantRowWalker walker, boolean file, boolean sample, boolean cohort, boolean score, boolean annotation, + Variant variant) { + if (variant == null) { + variant = getVariant(); + } else { + byte[] expectedRow = VariantPhoenixKeyFactory.generateVariantRowKey(variant); + byte[] actualRow; + if (result != null) { + actualRow = result.getRow(); + } else { + actualRow = VariantPhoenixKeyFactory.generateVariantRowKey(resultSet); + } + if (!Bytes.equals(expectedRow, actualRow)) { + throw new IllegalStateException("Expected row " + + Bytes.toStringBinary(expectedRow) + " but got " + Bytes.toStringBinary(actualRow)); + } + } + walker.variant(variant); if (resultSet != null) { try { ResultSetMetaData metaData = resultSet.getMetaData(); @@ -136,6 +150,10 @@ protected void walk(VariantRowWalker walker, boolean file, boolean sample, boole walker.fillMissing(studyId, resultSet.getInt(i)); } else if (annotation && columnName.equals(VariantColumn.FULL_ANNOTATION.column())) { walker.variantAnnotation(new BytesVariantAnnotationColumn(bytes)); + } else if (columnName.equals(VariantColumn.TYPE.column())) { + walker.type(VariantType.valueOf(Bytes.toString(bytes))); + } else if (columnName.equals(VariantColumn.ALLELES.column())) { + walker.alleles(Bytes.toString(bytes)); } } } catch (SQLException e) { @@ -164,6 +182,8 @@ protected void walk(VariantRowWalker walker, boolean file, boolean sample, boole walker.variantAnnotation(new BytesVariantAnnotationColumn(cell)); } else if (columnName.equals(VariantColumn.TYPE.column())) { walker.type(VariantType.valueOf(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()))); + } else if (columnName.equals(VariantColumn.ALLELES.column())) { + walker.alleles(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())); } } } @@ -177,6 +197,9 @@ protected void variant(Variant variant) { protected void type(VariantType type) { } + protected void alleles(String alleles) { + } + protected void study(int studyId) { } @@ -202,6 +225,7 @@ protected void variantAnnotation(VariantAnnotationColumn column) { public class VariantRowWalkerBuilder extends VariantRowWalker { private IntConsumer studyConsumer = r -> { }; + private Consumer allelesConsumer = r -> { }; private Consumer fileConsumer = r -> { }; private boolean hasFileConsumer = false; private Consumer sampleConsumer = r -> { }; @@ -229,6 +253,11 @@ protected void study(int studyId) { studyConsumer.accept(studyId); } + @Override + protected void alleles(String alleles) { + allelesConsumer.accept(alleles); + } + @Override protected void file(FileColumn fileColumn) { fileConsumer.accept(fileColumn); @@ -265,15 +294,29 @@ public Variant walk() { hasSampleConsumer, hasStatsConsumer, hasVariantScoreConsumer, - hasVariantAnnotationConsummer); + hasVariantAnnotationConsummer, null); return getVariant(); } + public void walk(Variant variant) { + VariantRow.this.walk(this, + hasFileConsumer, + hasSampleConsumer, + hasStatsConsumer, + hasVariantScoreConsumer, + hasVariantAnnotationConsummer, variant); + } + public VariantRowWalkerBuilder onStudy(IntConsumer consumer) { studyConsumer = consumer; return this; } + public VariantRowWalkerBuilder onAlleles(Consumer consumer) { + allelesConsumer = consumer; + return this; + } + public VariantRowWalkerBuilder onFile(Consumer consumer) { fileConsumer = consumer; hasFileConsumer = true; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/annotation/VariantAnnotationToPhoenixConverter.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/annotation/VariantAnnotationToPhoenixConverter.java index c9bb8646c04..d991c8a3c30 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/annotation/VariantAnnotationToPhoenixConverter.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/annotation/VariantAnnotationToPhoenixConverter.java @@ -16,7 +16,6 @@ package org.opencb.opencga.storage.hadoop.variant.converters.annotation; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.util.Bytes; @@ -24,6 +23,7 @@ import org.opencb.biodata.models.variant.avro.*; import org.opencb.biodata.tools.commons.Converter; import org.opencb.commons.utils.CompressionUtils; +import org.opencb.opencga.storage.core.variant.annotation.converters.VariantAnnotationModelUtils; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.PhoenixHelper; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema; @@ -62,6 +62,7 @@ public class VariantAnnotationToPhoenixConverter extends AbstractPhoenixConverte } private int annotationId; + private VariantAnnotationModelUtils variantAnnotationModelUtils = new VariantAnnotationModelUtils(); @Deprecated public VariantAnnotationToPhoenixConverter(byte[] columnFamily) { @@ -114,10 +115,6 @@ public VariantAnnotationToPhoenixConverter(byte[] columnFamily, int annotationId // Set hpo = new HashSet<>(); Set drugs = new HashSet<>(); Set proteinKeywords = new HashSet<>(); - // Contains all the xrefs, and the id, the geneNames and transcripts - Set xrefs = new HashSet<>(); - - addNotNull(xrefs, variantAnnotation.getId()); List consequenceTypes = variantAnnotation.getConsequenceTypes() == null ? Collections.emptyList() @@ -125,7 +122,9 @@ public VariantAnnotationToPhoenixConverter(byte[] columnFamily, int annotationId for (ConsequenceType consequenceType : consequenceTypes) { addNotNull(genes, consequenceType.getGeneName()); addNotNull(genes, consequenceType.getGeneId()); + addNotNull(genes, consequenceType.getEnsemblGeneId()); addNotNull(transcripts, consequenceType.getTranscriptId()); + addNotNull(transcripts, consequenceType.getEnsemblTranscriptId()); addNotNull(biotype, consequenceType.getBiotype()); addAllNotNull(flags, consequenceType.getTranscriptFlags()); @@ -192,32 +191,14 @@ public VariantAnnotationToPhoenixConverter(byte[] columnFamily, int annotationId if (proteinVariantAnnotation.getKeywords() != null) { proteinKeywords.addAll(proteinVariantAnnotation.getKeywords()); } - addNotNull(xrefs, proteinVariantAnnotation.getUniprotName()); - addNotNull(xrefs, proteinVariantAnnotation.getUniprotAccession()); - addNotNull(xrefs, proteinVariantAnnotation.getUniprotVariantId()); } } - if (CollectionUtils.isNotEmpty(variantAnnotation.getTraitAssociation())) { - for (EvidenceEntry evidenceEntry : variantAnnotation.getTraitAssociation()) { - addNotNull(xrefs, evidenceEntry.getId()); - } - } - - xrefs.addAll(genes); - xrefs.addAll(transcripts); - if (variantAnnotation.getXrefs() != null) { - for (Xref xref : variantAnnotation.getXrefs()) { - addNotNull(xrefs, xref.getId()); - } - } if (variantAnnotation.getGeneTraitAssociation() != null) { for (GeneTraitAssociation geneTrait : variantAnnotation.getGeneTraitAssociation()) { addNotNull(geneTraitName, geneTrait.getName()); addNotNull(geneTraitId, geneTrait.getId()); - addNotNull(xrefs, geneTrait.getHpo()); - addNotNull(xrefs, geneTrait.getId()); } } @@ -235,6 +216,8 @@ public VariantAnnotationToPhoenixConverter(byte[] columnFamily, int annotationId geneSoFlag.remove(null); soFlag.remove(null); + Set xrefs = variantAnnotationModelUtils.extractXRefs(variantAnnotation); + map.put(CHROMOSOME, variantAnnotation.getChromosome()); map.put(POSITION, variantAnnotation.getStart()); map.put(REFERENCE, variantAnnotation.getReference()); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/study/StudyEntryToHBaseConverter.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/study/StudyEntryToHBaseConverter.java index e3188e15716..4a4bafa1be8 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/study/StudyEntryToHBaseConverter.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/converters/study/StudyEntryToHBaseConverter.java @@ -119,6 +119,7 @@ public Put convert(Variant variant) { add(put, VariantPhoenixSchema.VariantColumn.CI_END_R, Math.max(0, variant.getSv().getCiEndRight())); } } + add(put, VariantPhoenixSchema.VariantColumn.ALLELES, VariantPhoenixKeyFactory.buildAlleles(variant)); add(put, studyColumn, 0); if (releaseColumn != null) { add(put, releaseColumn, true); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/AbstractFillFromArchiveTask.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/AbstractFillFromArchiveTask.java index d218cf0a470..1e0a01f89b4 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/AbstractFillFromArchiveTask.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/AbstractFillFromArchiveTask.java @@ -32,7 +32,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageEngine.TARGET_VARIANT_TYPE_SET; +import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageEngine.UNSUPPORTED_VARIANT_TYPE_SET; /** * Created on 31/10/17. @@ -348,7 +348,7 @@ protected static Scan buildScan(String regionStr, int fileId, Configuration conf protected static boolean isVariantAlreadyLoaded(VcfSliceProtos.VcfSlice slice, VcfSliceProtos.VcfRecord vcfRecord) { VariantType variantType = VcfRecordProtoToVariantConverter.getVariantType(vcfRecord.getType()); // The variant is not loaded if is a NO_VARIATION (fast check first) - if (!TARGET_VARIANT_TYPE_SET.contains(variantType)) { + if (UNSUPPORTED_VARIANT_TYPE_SET.contains(variantType)) { return false; } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingDriver.java index aea25583571..d760ab4543a 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingDriver.java @@ -62,6 +62,8 @@ protected Job setupJob(Job job, String archiveTableName, String variantTableName scan.setCacheBlocks(false); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.getStudyColumn(getStudyId()).bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.getFillMissingColumn(getStudyId()).bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); LOG.info(scan.toString()); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingMapper.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingMapper.java index 080a1a66a7a..10feadf2cc8 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingMapper.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/gaps/PrepareFillMissingMapper.java @@ -57,7 +57,7 @@ protected void setup(Context context) throws IOException, InterruptedException { @Override protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException { - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(value.getRow()); + Variant variant = VariantPhoenixKeyFactory.extractVariantFromResult(value); byte[] column = FillMissingFromArchiveTask.getArchiveVariantColumn(variant); long sliceId = rowKeyFactory.getSliceId(variant.getStart()); String chromosome = variant.getChromosome(); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexCompoundHeterozygousQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexCompoundHeterozygousQueryExecutor.java index cb2019c2f6d..c7599e7e288 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexCompoundHeterozygousQueryExecutor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexCompoundHeterozygousQueryExecutor.java @@ -5,7 +5,7 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.Trio; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java index 68eed2db889..4dd50c9eacf 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java @@ -15,6 +15,7 @@ import org.opencb.opencga.storage.core.metadata.models.Trio; import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHadoopDBAdaptor; import org.opencb.opencga.storage.hadoop.variant.index.query.SampleIndexQuery; @@ -43,7 +44,7 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) { } @Override - protected Object getOrIterator(Query query, QueryOptions options, boolean iterator, SampleIndexQuery sampleIndexQuery) { + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator, SampleIndexQuery sampleIndexQuery) { List trios = new ArrayList<>(sampleIndexQuery.getMendelianErrorSet().size()); int studyId = metadataManager.getStudyId(sampleIndexQuery.getStudy()); @@ -65,7 +66,7 @@ protected Object getOrIterator(Query query, QueryOptions options, boolean iterat trios.add(new Trio(null, father, mother, sampleMetadata.getName())); } - Object object = super.getOrIterator(query, options, iterator, sampleIndexQuery); + Object object = super.getOrIterator(variantQuery, iterator, sampleIndexQuery); if (iterator) { VariantDBIterator variantIterator = (VariantDBIterator) object; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java index 510085420dd..371d57ac4da 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java @@ -9,7 +9,6 @@ import org.opencb.biodata.models.variant.avro.FileEntry; import org.opencb.biodata.models.variant.avro.SampleEntry; import org.opencb.biodata.tools.commons.Converter; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; @@ -17,7 +16,6 @@ import org.opencb.opencga.core.common.BatchUtils; import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.storage.IndexFieldConfiguration; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.io.bit.BitBuffer; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; @@ -26,7 +24,7 @@ import org.opencb.opencga.storage.core.variant.adaptors.*; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; -import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.query.executors.VariantQueryExecutor; import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection; @@ -50,7 +48,6 @@ import java.util.stream.Collectors; import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.NONE; -import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.addSamplesMetadataIfRequested; import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageOptions.SAMPLE_INDEX_QUERY_SAMPLE_INDEX_ONLY_PD_BATCH; import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageOptions.SAMPLE_INDEX_QUERY_SAMPLE_INDEX_ONLY_PD_BUFFER; import static org.opencb.opencga.storage.hadoop.variant.index.SampleIndexVariantQueryExecutor.SAMPLE_INDEX_TABLE_SOURCE; @@ -64,7 +61,6 @@ public class SampleIndexOnlyVariantQueryExecutor extends VariantQueryExecutor { private final SampleIndexDBAdaptor sampleIndexDBAdaptor; private final VariantHadoopDBAdaptor dbAdaptor; - private final VariantQueryParser variantQueryParser; private final VariantQueryProjectionParser variantQueryProjectionParser; private static Logger logger = LoggerFactory.getLogger(SampleIndexOnlyVariantQueryExecutor.class); @@ -82,7 +78,6 @@ public SampleIndexOnlyVariantQueryExecutor(VariantHadoopDBAdaptor dbAdaptor, Sam super(dbAdaptor.getMetadataManager(), storageEngineId, options); this.sampleIndexDBAdaptor = sampleIndexDBAdaptor; this.dbAdaptor = dbAdaptor; - variantQueryParser = new VariantQueryParser(null, getMetadataManager()); variantQueryProjectionParser = new VariantQueryProjectionParser(getMetadataManager()); partialDataBufferSize = options.getInt(SAMPLE_INDEX_QUERY_SAMPLE_INDEX_ONLY_PD_BUFFER.key(), SAMPLE_INDEX_QUERY_SAMPLE_INDEX_ONLY_PD_BUFFER.defaultValue()); @@ -101,26 +96,16 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) { return false; } - @Override - public DataResult count(Query query) { - StopWatch stopWatch = StopWatch.createStarted(); - SampleIndexQuery sampleIndexQuery = sampleIndexDBAdaptor.parseSampleIndexQuery(query); - long count = sampleIndexDBAdaptor.count(sampleIndexQuery); - return new DataResult<>(((int) stopWatch.getTime(TimeUnit.MILLISECONDS)), Collections.emptyList(), 1, - Collections.singletonList(count), count); - } - /** * Fetch results exclusively from SampleSecondaryIndex. * - * @param inputQuery Query - * @param options Options + * @param variantQuery Query * @param iterator Shall the resulting object be an iterator instead of a DataResult * @return DataResult or Iterator with the variants that matches the query */ @Override - protected Object getOrIterator(Query inputQuery, QueryOptions options, boolean iterator) { - Query query = new Query(inputQuery); + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) { + Query query = new Query(variantQuery.getQuery()); query.put(SampleIndexQueryParser.INCLUDE_PARENTS_COLUMN, true); SampleIndexQuery sampleIndexQuery = sampleIndexDBAdaptor.parseSampleIndexQuery(query); @@ -128,7 +113,7 @@ protected Object getOrIterator(Query inputQuery, QueryOptions options, boolean i boolean count; Future asyncCountFuture; - if (shouldGetCount(options, iterator)) { + if (shouldGetCount(variantQuery.getInputOptions(), iterator)) { count = true; asyncCountFuture = THREAD_POOL.submit(() -> { StopWatch stopWatch = StopWatch.createStarted(); @@ -141,13 +126,12 @@ protected Object getOrIterator(Query inputQuery, QueryOptions options, boolean i asyncCountFuture = null; } - VariantDBIterator variantIterator = getVariantDBIterator(sampleIndexQuery, inputQuery, options); + VariantDBIterator variantIterator = getVariantDBIterator(sampleIndexQuery, variantQuery); if (iterator) { return variantIterator; } else { - VariantQueryResult result = - addSamplesMetadataIfRequested(variantIterator.toDataResult(), inputQuery, options, getMetadataManager()); + VariantQueryResult result = variantIterator.toDataResult(variantQuery); if (count) { result.setApproximateCount(false); try { @@ -161,8 +145,8 @@ protected Object getOrIterator(Query inputQuery, QueryOptions options, boolean i } } - private VariantDBIterator getVariantDBIterator(SampleIndexQuery sampleIndexQuery, Query inputQuery, QueryOptions options) { - ParsedVariantQuery parsedQuery = variantQueryParser.parseQuery(inputQuery, options, true); + private VariantDBIterator getVariantDBIterator(SampleIndexQuery sampleIndexQuery, ParsedVariantQuery parsedQuery) { + QueryOptions options = parsedQuery.getInputOptions(); VariantDBIterator variantIterator; if (parsedQuery.getProjection().getStudyIds().isEmpty()) { logger.info("Using sample index iterator Iterator"); @@ -174,9 +158,9 @@ private VariantDBIterator getVariantDBIterator(SampleIndexQuery sampleIndexQuery try { rawIterator = sampleIndexDBAdaptor.rawIterator(sampleIndexQuery, options); } catch (IOException e) { - throw VariantQueryException.internalException(e).setQuery(inputQuery); + throw VariantQueryException.internalException(e).setQuery(parsedQuery.getInputQuery()); } - boolean includeAll = inputQuery.getBoolean("includeAllFromSampleIndex", false); + boolean includeAll = parsedQuery.getInputQuery().getBoolean("includeAllFromSampleIndex", false); SampleVariantIndexEntryToVariantConverter converter = new SampleVariantIndexEntryToVariantConverter( parsedQuery, sampleIndexQuery, dbAdaptor.getMetadataManager(), includeAll); variantIterator = VariantDBIterator.wrapper(Iterators.transform(rawIterator, converter::convert)); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantAggregationExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantAggregationExecutor.java index f193dcad3e2..973a922780b 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantAggregationExecutor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantAggregationExecutor.java @@ -9,7 +9,7 @@ import org.opencb.commons.datastore.core.FacetField; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.io.bit.BitBuffer; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.utils.iterators.CloseableIterator; @@ -142,7 +142,7 @@ protected VariantQueryResult aggregation(Query query, QueryOptions o } return new VariantQueryResult<>((int) stopWatch.getTime(TimeUnit.MILLISECONDS), 1, numMatches, Collections.emptyList(), - fields, null, SampleIndexVariantQueryExecutor.SAMPLE_INDEX_TABLE_SOURCE); + fields, SampleIndexVariantQueryExecutor.SAMPLE_INDEX_TABLE_SOURCE); } } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java index 64cf8669153..6175128018d 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java @@ -3,17 +3,17 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.time.StopWatch; import org.opencb.biodata.models.variant.Variant; -import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.common.TimeUtils; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.MultiVariantDBIterator; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIteratorWithCounts; +import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.core.variant.query.executors.AbstractTwoPhasedVariantQueryExecutor; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHadoopDBAdaptor; @@ -31,7 +31,6 @@ import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.ID; import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.REGION; -import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.addSamplesMetadataIfRequested; /** * Created on 01/04/19. @@ -66,11 +65,6 @@ public boolean canUseThisExecutor(Query query, QueryOptions options) { return false; } - @Override - public DataResult count(Query query) { - throw new UnsupportedOperationException("Count not implemented in " + getClass()); - } - @Override protected long primaryCount(Query query, QueryOptions options) { SampleIndexQuery sampleIndexQuery = sampleIndexDBAdaptor.parseSampleIndexQuery(query); @@ -81,24 +75,25 @@ protected long primaryCount(Query query, QueryOptions options) { * Intersect result of SampleIndexTable and full phoenix query. * Use {@link org.opencb.opencga.storage.core.variant.adaptors.iterators.MultiVariantDBIterator}. * - * @param inputQuery Query - * @param options Options + * @param variantQuery Parsed query * @param iterator Shall the resulting object be an iterator instead of a DataResult * @return DataResult or Iterator with the variants that matches the query */ @Override - protected Object getOrIterator(Query inputQuery, QueryOptions options, boolean iterator) { - Query query = new Query(inputQuery); + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) { + Query query = new Query(variantQuery.getQuery()); SampleIndexQuery sampleIndexQuery = sampleIndexDBAdaptor.parseSampleIndexQuery(query); - return getOrIterator(query, options, iterator, sampleIndexQuery); + return getOrIterator(variantQuery, iterator, sampleIndexQuery); } - protected Object getOrIterator(Query query, QueryOptions inputOptions, boolean iterator, SampleIndexQuery sampleIndexQuery) { + protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator, SampleIndexQuery sampleIndexQuery) { logger.info("HBase SampleIndex intersect"); + QueryOptions inputOptions = variantQuery.getInputOptions(); + Query uncoveredQuery = new Query(sampleIndexQuery.getUncoveredQuery()); Future asyncCountFuture; boolean asyncCount; - if (shouldGetApproximateCount(inputOptions, iterator) && queryFiltersCovered(query)) { + if (shouldGetApproximateCount(inputOptions, iterator) && queryFiltersCovered(uncoveredQuery)) { asyncCount = true; asyncCountFuture = THREAD_POOL.submit(() -> { StopWatch stopWatch = StopWatch.createStarted(); @@ -120,10 +115,10 @@ protected Object getOrIterator(Query query, QueryOptions inputOptions, boolean i int batchSize = inputOptions.getInt("multiIteratorBatchSize", 200); if (iterator) { // SampleIndex iterator will be closed when closing the variants iterator - return dbAdaptor.iterator(variants, query, inputOptions, batchSize); + return dbAdaptor.iterator(variants, uncoveredQuery, inputOptions, batchSize); } else { - int skip = getSkip(inputOptions); - int limit = getLimit(inputOptions); + int skip = variantQuery.getSkip(); + int limit = variantQuery.getLimitOr(-1); int samplingSize = asyncCount ? 0 : getSamplingSize(inputOptions, DEFAULT_SAMPLING_SIZE, iterator); int tmpLimit = Math.max(limit, samplingSize); @@ -131,7 +126,9 @@ protected Object getOrIterator(Query query, QueryOptions inputOptions, boolean i // Ensure results are sorted and it's not counting from variants dbAdaptor options.put(QueryOptions.SORT, true); options.put(QueryOptions.COUNT, false); - options.put(QueryOptions.LIMIT, tmpLimit); + if (limit > 0) { + options.put(QueryOptions.LIMIT, tmpLimit); + } MultiVariantDBIterator variantDBIterator = dbAdaptor.iterator( new org.opencb.opencga.storage.core.variant.adaptors.iterators.DelegatedVariantDBIterator(variants) { @@ -139,9 +136,8 @@ protected Object getOrIterator(Query query, QueryOptions inputOptions, boolean i public void close() throws Exception { // Do not close this iterator! We'll need to keep iterating to get the approximate count } - }, query, options, batchSize); - VariantQueryResult result = - addSamplesMetadataIfRequested(variantDBIterator.toDataResult(), query, options, getMetadataManager()); + }, uncoveredQuery, options, batchSize); + VariantQueryResult result = variantDBIterator.toDataResult(variantQuery); if (result.getNumResults() < tmpLimit) { // Not an approximate count! @@ -162,11 +158,11 @@ public void close() throws Exception { inputOptions.get(QueryOptions.COUNT)); numTotalResultsOptions.put(VariantStorageOptions.APPROXIMATE_COUNT.key(), inputOptions.get(VariantStorageOptions.APPROXIMATE_COUNT.key())); - setNumTotalResults(variantDBIterator, variants, result, sampleIndexQuery, query, numTotalResultsOptions); + setNumTotalResults(variantDBIterator, variants, result, sampleIndexQuery, uncoveredQuery, numTotalResultsOptions); } // Ensure limit - if (result.getNumResults() > limit) { + if (limit > 0 && result.getNumResults() > limit) { result.setResults(result.getResults().subList(0, limit)); result.setNumResults(limit); } @@ -195,7 +191,7 @@ private boolean queryFiltersCovered(Query query) { // Check if the query is fully covered Set params = VariantQueryUtils.validParams(query, true); params.remove(VariantQueryParam.STUDY); - + logger.info("Uncovered filters : " + params); return params.isEmpty(); } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/SampleIndexAnnotationLoaderDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/SampleIndexAnnotationLoaderDriver.java index 41826fd4abe..9f593f928c6 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/SampleIndexAnnotationLoaderDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/SampleIndexAnnotationLoaderDriver.java @@ -138,6 +138,8 @@ protected Job setupJob(Job job, String archiveTable, String variantTable) throws } } scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.FULL_ANNOTATION.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); SampleIndexAnnotationLoaderMapper.setHasGenotype(job, hasGenotype); SampleIndexAnnotationLoaderMapper.setMultiFileSamples(job, multiFileSamples); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/VariantTableSampleIndexOrderMapper.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/VariantTableSampleIndexOrderMapper.java index 0d1e45f40be..ab9fc2a1d82 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/VariantTableSampleIndexOrderMapper.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/annotation/mr/VariantTableSampleIndexOrderMapper.java @@ -51,11 +51,7 @@ public void run(Context context) throws IOException, InterruptedException { // Current result does not start in the previous position. // Sort buffer if (buffer.size() > 1) { - buffer.sort((o1, o2) -> { - Variant v1 = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(o1.getSecond().getRow()); - Variant v2 = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(o2.getSecond().getRow()); - return SampleIndexSchema.INTRA_CHROMOSOME_VARIANT_COMPARATOR.compare(v1, v2); - }); + sortBuffer(buffer); } if (!buffer.isEmpty()) { @@ -82,6 +78,9 @@ public void run(Context context) throws IOException, InterruptedException { } } countBufferSize(context, buffer.size(), chromosome, position); + if (buffer.size() > 1) { + sortBuffer(buffer); + } for (Pair pair : buffer) { this.map(pair.getFirst(), pair.getSecond(), context); } @@ -92,6 +91,14 @@ public void run(Context context) throws IOException, InterruptedException { } } + private static void sortBuffer(List> buffer) { + buffer.sort((o1, o2) -> { + Variant v1 = VariantPhoenixKeyFactory.extractVariantFromResult(o1.getSecond()); + Variant v2 = VariantPhoenixKeyFactory.extractVariantFromResult(o2.getSecond()); + return SampleIndexSchema.INTRA_CHROMOSOME_VARIANT_COMPARATOR.compare(v1, v2); + }); + } + private void countBufferSize(Context context, int size, String chromosome, int position) { String counterName; if (size < 5) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java index ada52270419..af632d69436 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java @@ -226,7 +226,8 @@ protected Job setupJob(Job job, String archiveTable, String variantTable) throws } } } -// scan.addColumn(getHelper().getColumnFamily(), VariantPhoenixHelper.VariantColumn.FULL_ANNOTATION.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); VariantMapReduceUtil.configureMapReduceScan(scan, getConf()); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SampleIndexQuery.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SampleIndexQuery.java index 664ace07747..4a4803ff6a4 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SampleIndexQuery.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SampleIndexQuery.java @@ -4,6 +4,7 @@ import org.opencb.biodata.models.core.Region; import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.VariantType; +import org.opencb.commons.datastore.core.Query; import org.opencb.opencga.storage.core.variant.query.Values; import org.opencb.opencga.storage.hadoop.variant.index.family.GenotypeCodec; import org.opencb.opencga.storage.hadoop.variant.index.sample.SampleIndexSchema; @@ -49,6 +50,8 @@ public class SampleIndexQuery { private final Set mendelianErrorSet; private final MendelianErrorType mendelianErrorType; private final boolean includeParentColumns; + + private final Query uncoveredQuery; private final QueryOperation queryOperation; public enum MendelianErrorType { @@ -73,6 +76,7 @@ public SampleIndexQuery(Collection locusQueries, SampleIndexQuery qu this.mendelianErrorSet = query.mendelianErrorSet; this.mendelianErrorType = query.mendelianErrorType; this.includeParentColumns = query.includeParentColumns; + this.uncoveredQuery = query.uncoveredQuery; this.queryOperation = query.queryOperation; } @@ -83,7 +87,7 @@ public SampleIndexQuery(SampleIndexSchema schema, Collection locusQu Map> fileFilterMap, SampleAnnotationIndexQuery annotationIndexQuery, Set mendelianErrorSet, MendelianErrorType mendelianErrorType, boolean includeParentColumns, - QueryOperation queryOperation) { + QueryOperation queryOperation, Query uncoveredQuery) { this.schema = schema; this.locusQueries = locusQueries; this.extendedFilteringRegion = extendedFilteringRegion; @@ -100,6 +104,7 @@ public SampleIndexQuery(SampleIndexSchema schema, Collection locusQu this.mendelianErrorType = mendelianErrorType; this.includeParentColumns = includeParentColumns; this.queryOperation = queryOperation; + this.uncoveredQuery = uncoveredQuery; } public SampleIndexSchema getSchema() { @@ -227,6 +232,10 @@ public boolean isIncludeParentColumns() { return includeParentColumns; } + public Query getUncoveredQuery() { + return uncoveredQuery; + } + public QueryOperation getQueryOperation() { return queryOperation; } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SingleSampleIndexQuery.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SingleSampleIndexQuery.java index 35a9e67e718..51f44824872 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SingleSampleIndexQuery.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/query/SingleSampleIndexQuery.java @@ -36,7 +36,8 @@ protected SingleSampleIndexQuery(SampleIndexQuery query, String sample, List scans; if (multiScan) { @@ -220,6 +221,8 @@ protected Job setupJob(Job job, String archiveTable, String table) throws IOExce if (StringUtils.isNotEmpty(region)) { VariantHBaseQueryParser.addRegionFilter(scan, Region.parseRegion(region)); } + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); scan.setFilter(filter); scans.add(scan); for (int sample : samplesSubSet) { @@ -247,6 +250,8 @@ protected Job setupJob(Job job, String archiveTable, String table) throws IOExce VariantHBaseQueryParser.addRegionFilter(scan, Region.parseRegion(region)); } scan.setFilter(filter); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); int approxExpectedNumColumns = sampleIds.size() diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java index 673061dec30..027d241282c 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java @@ -274,7 +274,7 @@ public SampleIndexQuery parse(Query query) { //} else if (isValidParam(query, FILE)) { // Add FILEs filter ? } else { - throw new IllegalStateException("Unable to query SamplesIndex"); + throw new IllegalStateException("Unable to query SamplesIndex. Missing sample filter! Query: " + query.toJson()); } boolean requireFamilyIndex = !mendelianErrorSet.isEmpty(); SampleIndexSchema schema = schemaFactory.getSchema(studyId, sampleGenotypeQuery.keySet(), false, requireFamilyIndex); @@ -551,7 +551,7 @@ public SampleIndexQuery parse(Query query) { return new SampleIndexQuery(schema, regionGroups, extendedFilteringRegion, variantTypes, study, sampleGenotypeQuery, multiFileSamples, negatedSamples, fatherFilterMap, motherFilterMap, - fileIndexMap, annotationIndexQuery, mendelianErrorSet, mendelianErrorType, includeParentsField, queryOperation); + fileIndexMap, annotationIndexQuery, mendelianErrorSet, mendelianErrorType, includeParentsField, queryOperation, query); } private Set findParents(Set childrenSet, Map> parentsMap) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexVariantBiConverter.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexVariantBiConverter.java index 1004dfa36c2..f1b167ae9c1 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexVariantBiConverter.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexVariantBiConverter.java @@ -661,7 +661,7 @@ private Variant toVariantEncodedAlleles(String chromosome, int batchStart, byte[ String[] refAlt = AlleleCodec.decode(bytes[offset]); int start = batchStart + (read24bitInteger(bytes, offset) & 0x0F_FF_FF); - return VariantPhoenixKeyFactory.buildVariant(chromosome, start, refAlt[0], refAlt[1], null); + return VariantPhoenixKeyFactory.buildVariant(chromosome, start, refAlt[0], refAlt[1], null, null); } private Variant toVariant(String chromosome, int batchStart, byte[] bytes, int offset, int referenceLength, int alternateLength) { @@ -671,7 +671,7 @@ private Variant toVariant(String chromosome, int batchStart, byte[] bytes, int o offset += referenceLength + SEPARATOR_LENGTH; // add reference, and separator String alternate = readString(bytes, offset, alternateLength); - return VariantPhoenixKeyFactory.buildVariant(chromosome, start, reference, alternate, null); + return VariantPhoenixKeyFactory.buildVariant(chromosome, start, reference, alternate, null, null); } private int readNextSeparator(byte[] bytes, int offset) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/io/VariantExporterDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/io/VariantExporterDriver.java index 196ab0e1689..aa342c10a92 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/io/VariantExporterDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/io/VariantExporterDriver.java @@ -33,6 +33,7 @@ import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery; import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; import org.opencb.opencga.storage.hadoop.variant.AbstractVariantsTableDriver; +import org.opencb.opencga.storage.hadoop.variant.HadoopVariantQueryParser; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHBaseQueryParser; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantSqlQueryParser; import org.opencb.opencga.storage.hadoop.variant.index.sample.SampleIndexDBAdaptor; @@ -188,7 +189,7 @@ protected Job setupJob(Job job, String archiveTable, String variantTable) throws VariantMapReduceUtil.setNoneReduce(job); } - VariantQueryParser variantQueryParser = new VariantQueryParser(null, getMetadataManager()); + VariantQueryParser variantQueryParser = new HadoopVariantQueryParser(null, getMetadataManager()); ParsedVariantQuery variantQuery = variantQueryParser.parseQuery(query, options); Query query = variantQuery.getQuery(); if (VariantHBaseQueryParser.isSupportedQuery(query)) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseTaskMetadataDBAdaptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseTaskMetadataDBAdaptor.java index 290f937631b..4fe8d236cc1 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseTaskMetadataDBAdaptor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseTaskMetadataDBAdaptor.java @@ -28,7 +28,7 @@ public TaskMetadata getTask(int studyId, int taskId, Long timeStamp) { @Override public Iterator taskIterator(int studyId, List statusFilter, boolean reversed) { - if (statusFilter == null) { + if (statusFilter == null || statusFilter.isEmpty()) { return iterator(getTaskRowKeyPrefix(studyId), TaskMetadata.class, reversed); } else if (statusFilter.contains(TaskMetadata.Status.READY)) { EnumSet set = EnumSet.copyOf(statusFilter); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/SampleIndexTableRecordReader.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/SampleIndexTableRecordReader.java index 2efd1115846..7e747813b0e 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/SampleIndexTableRecordReader.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/SampleIndexTableRecordReader.java @@ -7,6 +7,7 @@ import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableRecordReader; +import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.mapreduce.InputSplit; import org.apache.hadoop.mapreduce.TaskAttemptContext; import org.opencb.biodata.models.core.Region; @@ -186,9 +187,9 @@ public void initialize(InputSplit inputsplit, TaskAttemptContext context) throws startChr = null; start = 0; } else { - Variant startVariant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(firstRow); - startChr = startVariant.getChromosome(); - start = startVariant.getStart(); + Pair startLocus = VariantPhoenixKeyFactory.extractChrPosFromVariantRowKey(firstRow); + startChr = startLocus.getFirst(); + start = startLocus.getSecond(); } String stopChr; Integer end; @@ -197,9 +198,9 @@ public void initialize(InputSplit inputsplit, TaskAttemptContext context) throws stopChr = null; end = Integer.MAX_VALUE; } else { - Variant stopVariant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(lastRow); - stopChr = stopVariant.getChromosome(); - end = stopVariant.getStart(); + Pair stopLocus = VariantPhoenixKeyFactory.extractChrPosFromVariantRowKey(lastRow); + stopChr = stopLocus.getFirst(); + end = stopLocus.getSecond(); } List regions = new ArrayList<>(); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/VariantMapReduceUtil.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/VariantMapReduceUtil.java index 623deec75da..367196742db 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/VariantMapReduceUtil.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/mr/VariantMapReduceUtil.java @@ -29,11 +29,11 @@ import org.opencb.opencga.core.config.storage.SampleIndexConfiguration; import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; -import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; import org.opencb.opencga.storage.hadoop.utils.AbstractHBaseDriver; import org.opencb.opencga.storage.hadoop.variant.AbstractVariantsTableDriver; import org.opencb.opencga.storage.hadoop.variant.GenomeHelper; +import org.opencb.opencga.storage.hadoop.variant.HadoopVariantQueryParser; import org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageOptions; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHBaseQueryParser; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHadoopDBAdaptor; @@ -153,7 +153,7 @@ public static void initTableMapperJobFromPhoenix(Job job, String variantTable, S public static void initVariantMapperJob(Job job, Class mapperClass, String variantTable, VariantStorageMetadataManager metadataManager, Query query, QueryOptions queryOptions, boolean skipSampleIndex) throws IOException { - query = new VariantQueryParser(null, metadataManager).preProcessQuery(query, queryOptions); + query = new HadoopVariantQueryParser(null, metadataManager).preProcessQuery(query, queryOptions); setQuery(job, query); setQueryOptions(job, queryOptions); @@ -266,7 +266,7 @@ public static void initVariantRowMapperJob(Job job, Class mapperClass, String variantTable, VariantStorageMetadataManager metadataManager, Query query, QueryOptions queryOptions, boolean skipSampleIndex) throws IOException { - query = new VariantQueryParser(null, metadataManager).preProcessQuery(query, queryOptions); + query = new HadoopVariantQueryParser(null, metadataManager).preProcessQuery(query, queryOptions); setQuery(job, query); setQueryOptions(job, queryOptions); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsDescriptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsDescriptor.java index 152c0a86c3c..972f1aeecfe 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsDescriptor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsDescriptor.java @@ -35,6 +35,13 @@ default boolean createTableIfNeeded(HBaseVariantTableNameGenerator generator, HB boolean createTableIfNeeded(String tableName, HBaseManager hBaseManager) throws IOException; + /** + * Configure the scan to read from the variants table. + * + * @param scan Scan to configure + * @param metadataManager Metadata manager + * @return The same scan object + */ Scan configureScan(Scan scan, VariantStorageMetadataManager metadataManager); Function getPendingEvaluatorMapper(VariantStorageMetadataManager metadataManager, boolean overwrite); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsReader.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsReader.java index 3a58ebdb1bd..54d93c5c6ba 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsReader.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/pending/PendingVariantsReader.java @@ -52,7 +52,7 @@ public boolean pre() { protected List convert(List results) { List variants = new ArrayList<>(results.size()); for (Result result : results) { - variants.add(VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(result.getRow())); + variants.add(VariantPhoenixKeyFactory.extractVariantFromResult(result)); } return variants; } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneDriver.java index a2ff5f08b4a..fddaa7c189e 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneDriver.java @@ -23,7 +23,6 @@ import org.opencb.opencga.storage.core.metadata.models.VariantScoreMetadata; import org.opencb.opencga.storage.hadoop.variant.AbstractVariantsTableDriver; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.PhoenixHelper; -import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixKeyFactory; import org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix.VariantPhoenixSchema; import org.opencb.opencga.storage.hadoop.variant.converters.VariantRow; import org.opencb.opencga.storage.hadoop.variant.mr.VariantMapReduceUtil; @@ -37,6 +36,7 @@ import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static org.opencb.biodata.models.variant.StudyEntry.DEFAULT_COHORT; @@ -45,6 +45,7 @@ public class VariantPruneDriver extends AbstractVariantsTableDriver { private Logger logger = LoggerFactory.getLogger(VariantPruneManager.class); + public static final String ATTRIBUTE_DELETION_VARIANT = "d_variant"; public static final String ATTRIBUTE_DELETION_STUDIES = "d_studies"; public static final String ATTRIBUTE_DELETION_TYPE = "d_type"; public static final byte[] ATTRIBUTE_DELETION_TYPE_FULL = Bytes.toBytes(VariantPruneReportRecord.Type.FULL.toString()); @@ -95,6 +96,8 @@ protected Job setupJob(Job job, String archiveTable, String variantTable) throws FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE); scan.setFilter(filterList); + scan.addColumn(COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + scan.addColumn(COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); scan.addColumn(COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.INDEX_NOT_SYNC.bytes()); scan.addColumn(COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.INDEX_UNKNOWN.bytes()); scan.addColumn(COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.INDEX_STUDIES.bytes()); @@ -210,7 +213,7 @@ protected void map(ImmutableBytesWritable key, Result value, Context context) List emptyStudies = new ArrayList<>(); List studies = new ArrayList<>(); List studiesWithStats = new ArrayList<>(); - + AtomicReference alleles = new AtomicReference<>(); Variant variant = variantRow.walker() .onStudy(studies::add) .onCohortStats(c -> { @@ -220,6 +223,7 @@ protected void map(ImmutableBytesWritable key, Result value, Context context) emptyStudies.add(c.getStudyId()); } }) + .onAlleles(alleles::set) .walk(); for (Integer studyWithStats : studiesWithStats) { @@ -253,13 +257,18 @@ protected void map(ImmutableBytesWritable key, Result value, Context context) // Drop variant && add to deleted variants list context.getCounter(COUNTER_GROUP_NAME, "variants_deleted").increment(1); - context.write(pendingDeletionVariantsTable, - new Put(value.getRow()).addColumn(COLUMN_FAMILY_BYTES, COLUMN, VALUE)); + Put put = new Put(value.getRow()) + .addColumn(COLUMN_FAMILY_BYTES, COLUMN, VALUE); + if (alleles.get() != null) { + put.addColumn(COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes(), Bytes.toBytes(alleles.get())); + } + context.write(pendingDeletionVariantsTable, put); context.write(pendingAnnotationVariantsTable, new Delete(value.getRow())); Delete delete = new Delete(value.getRow()); delete.addFamily(COLUMN_FAMILY_BYTES); + delete.setAttribute(ATTRIBUTE_DELETION_VARIANT, Bytes.toBytes(variant.toString())); delete.setAttribute(ATTRIBUTE_DELETION_TYPE, ATTRIBUTE_DELETION_TYPE_FULL); delete.setAttribute(ATTRIBUTE_DELETION_STUDIES, Bytes.toBytes(emptyStudies.stream() @@ -285,12 +294,16 @@ protected void map(ImmutableBytesWritable key, Result value, Context context) // This block is here to prevent accidental "full row" deletes. throw new IllegalStateException("Unexpected empty delete at partial variant prune in variant " + variant); } + delete.setAttribute(ATTRIBUTE_DELETION_VARIANT, Bytes.toBytes(variant.toString())); delete.setAttribute(ATTRIBUTE_DELETION_TYPE, ATTRIBUTE_DELETION_TYPE_PARTIAL); delete.setAttribute(ATTRIBUTE_DELETION_STUDIES, Bytes.toBytes(emptyStudies.stream().map(Object::toString).collect(Collectors.joining(",")))); Put updateSecondaryIndexColumns = new Put(value.getRow()); - + if (alleles.get() != null) { + updateSecondaryIndexColumns.addColumn(COLUMN_FAMILY_BYTES, + VariantPhoenixSchema.VariantColumn.ALLELES.bytes(), Bytes.toBytes(alleles.get())); + } HadoopVariantSearchIndexUtils.addNotSyncStatus(updateSecondaryIndexColumns); context.write(variantsTable, delete); @@ -360,8 +373,7 @@ public ReportRecordWriter(DataOutputStream out, ImmutableBytesWritable variantsT public synchronized void write(ImmutableBytesWritable key, Mutation mutation) throws IOException { if (mutation instanceof Delete && key.equals(variantsTable)) { - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(mutation.getRow()); - out.write(variant.toString().getBytes(StandardCharsets.UTF_8)); + out.write(mutation.getAttribute(ATTRIBUTE_DELETION_VARIANT)); out.write(SEPARATOR); out.write(mutation.getAttribute(ATTRIBUTE_DELETION_TYPE)); out.write(SEPARATOR); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneManager.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneManager.java index 93d6801d448..43299e6e503 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneManager.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/prune/VariantPruneManager.java @@ -205,7 +205,7 @@ private void checkReportedVariants(Path report, long count) throws IOException, Result[] get = table.get(gets); for (int i = 0; i < get.length; i++) { Result result = get[i]; - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(result.getRow()); + Variant variant = VariantPhoenixKeyFactory.extractVariantFromResult(result); VariantPruneReportRecord record = batch.get(i); if (!variant.sameGenomicVariant(record.getVariant())) { throw new IllegalStateException("Error checking report! Expected " diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/SecondaryIndexPendingVariantsDescriptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/SecondaryIndexPendingVariantsDescriptor.java index 59c55cf8dda..cc6323b2c0a 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/SecondaryIndexPendingVariantsDescriptor.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/SecondaryIndexPendingVariantsDescriptor.java @@ -54,6 +54,7 @@ public boolean createTableIfNeeded(String tableName, HBaseManager hBaseManager) @Override public Scan configureScan(Scan scan, VariantStorageMetadataManager metadataManager) { scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, ALLELES.bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, FULL_ANNOTATION.bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, ANNOTATION_ID.bytes()); scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, INDEX_NOT_SYNC.bytes()); @@ -61,7 +62,7 @@ public Scan configureScan(Scan scan, VariantStorageMetadataManager metadataManag scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, INDEX_STUDIES.bytes()); for (Integer studyId : metadataManager.getStudyIds()) { scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.getStudyColumn(studyId).bytes()); - for (CohortMetadata cohort : metadataManager.getCalculatedCohorts(studyId)) { + for (CohortMetadata cohort : metadataManager.getCalculatedOrPartialCohorts(studyId)) { scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.getStatsColumn(studyId, cohort.getId()).bytes()); } } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/CheckVariantStatsDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/CheckVariantStatsDriver.java index 091a93eef54..c39ade7a754 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/CheckVariantStatsDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/CheckVariantStatsDriver.java @@ -369,7 +369,7 @@ protected void setup(Context context) throws IOException, InterruptedException { @Override protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException { - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(value.getRow()); + Variant variant = VariantPhoenixKeyFactory.extractVariantFromResult(value); VariantType type = variant.getType(); int chromosomeIdx = FileStatsWritable.getChromosomeIdx(variant.getChromosome()); for (Cell cell : value.rawCells()) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/SaturationStatsDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/SaturationStatsDriver.java index 100f1f7f73d..0c509a0d7e7 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/SaturationStatsDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/SaturationStatsDriver.java @@ -106,6 +106,7 @@ protected Job setupJob(Job job, String archiveTable, String variantTable) throws maxSampleId += maxSampleIdInStudy; } scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); // scan.setFilter(new KeyOnlyFilter()); scan.setFilter( diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantHistogramDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantHistogramDriver.java index 306a59faf7b..7c4db8f70d3 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantHistogramDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantHistogramDriver.java @@ -76,7 +76,8 @@ protected void parseAndValidateParameters() throws IOException { protected Job setupJob(Job job, String archiveTable, String variantTable) throws IOException { Scan scan = new Scan(); - + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); for (Integer studyId : getMetadataManager().getStudyIds()) { scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.getStudyColumn(studyId).bytes()); } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsDriver.java index eb76771b159..c1dc34e2e21 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsDriver.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsDriver.java @@ -178,6 +178,7 @@ protected Job setupJob(Job job, String archiveTableName, String variantTableName // See #1600 // Add TYPE column to force scan ALL rows to avoid unlikely but possible timeouts fetching new variants scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.TYPE.bytes()); + scan.addColumn(GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes()); // Remove STUDY filter scan.setFilter(null); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsFromResultMapper.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsFromResultMapper.java index 28b0115a326..7bc90517688 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsFromResultMapper.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/stats/VariantStatsFromResultMapper.java @@ -165,7 +165,7 @@ public void run(Context context) throws IOException, InterruptedException { // } protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException { - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(value.getRow()); + Variant variant = VariantPhoenixKeyFactory.extractVariantFromResult(value); VariantStatsWrapper wrapper = new VariantStatsWrapper(variant, new ArrayList<>(calculators.size())); calculators.forEach((cohort, calculator) -> { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java index b1bd9d84879..fbcbd773477 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java @@ -4,23 +4,22 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.opencb.biodata.models.variant.StudyEntry; import org.opencb.biodata.models.variant.Variant; -import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.api.ParamConstants; -import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.LongTests; import org.opencb.opencga.storage.core.variant.VariantStorageEngineSVTest; import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass; import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; -import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHadoopDBAdaptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import static org.junit.Assert.assertEquals; +import static org.opencb.opencga.core.api.ParamConstants.OVERWRITE; /** * Created on 26/06/18. @@ -32,6 +31,7 @@ public class HadoopVariantStorageEngineSVTest extends VariantStorageEngineSVTest @ClassRule public static HadoopExternalResource externalResource = new HadoopExternalResource(); + private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void loadFiles() throws Exception { @@ -39,6 +39,12 @@ protected void loadFiles() throws Exception { VariantHbaseTestUtils.printVariants(getVariantStorageEngine().getDBAdaptor(), newOutputUri(getTestName().getMethodName())); } + @Test + public void testRecreateSampleIndex() throws Exception { + variantStorageEngine.sampleIndex(studyMetadata.getName(), Collections.singletonList("all"), new QueryOptions(OVERWRITE, true)); + variantStorageEngine.sampleIndex(studyMetadata2.getName(), Collections.singletonList("all"), new QueryOptions(OVERWRITE, true)); + } + @Test public void checkSampleIndex() throws Exception { for (Variant variant : variantStorageEngine.iterable(new VariantQuery() @@ -46,21 +52,34 @@ public void checkSampleIndex() throws Exception { .includeSampleId(true) , new QueryOptions())) { Set samplesInVariant = new HashSet<>(); - for (String sample : metadataManager.getIndexedSamplesMap(studyMetadata.getId()).keySet()) { - QueryOptions options = new QueryOptions(VariantHadoopDBAdaptor.NATIVE, false); - VariantQueryResult result = variantStorageEngine.get(new Query() - .append(VariantQueryParam.SAMPLE.key(), sample) - .append(VariantQueryParam.ID.key(), variant.toString()), options); - String genotype = variant.getStudies().get(0).getSample(sample).getData().get(0); - if (GenotypeClass.MAIN_ALT.test(genotype)) { - Assert.assertNotNull(result.first()); - samplesInVariant.add(sample); - } else { - Assert.assertNull("Sample=" + sample + " with GT=" + genotype + " in variant=" + variant, result.first()); + for (Map.Entry entry : metadataManager.getStudies().entrySet()) { + String studyName = entry.getKey(); + Integer studyId = entry.getValue(); + StudyEntry studyEntry = variant.getStudy(studyName); + if (studyEntry != null) { + for (String sample : metadataManager.getIndexedSamplesMap(studyId).keySet()) { + QueryOptions options = new QueryOptions(VariantHadoopDBAdaptor.NATIVE, false); + VariantQueryResult result = variantStorageEngine.get(new VariantQuery() + .study(studyName) + .id(variant.toString()) + .sample(sample), options); + String genotype = studyEntry.getSample(sample).getData().get(0); + String message = "Study=" + studyName + " Sample=" + sample + " with GT=" + genotype + " in variant=" + variant; + if (GenotypeClass.MAIN_ALT.test(genotype)) { + Assert.assertNotNull(message, result.first()); + samplesInVariant.add(sample); + } else { + Assert.assertNull(message, result.first()); + } + } + logger.info("Variant " + variant + " with samples " + samplesInVariant); + logger.info("Query variant " + variant + " in study " + studyName + " from sampleData"); + Variant sampleDataVariant = variantStorageEngine.getSampleData(variant.toString(), studyName, new QueryOptions()).first(); + List actualSampleNames = sampleDataVariant.getSampleNames(studyName); + logger.info("Variant " + variant + " with actual samples " + actualSampleNames); + Assert.assertEquals(samplesInVariant, new HashSet<>(actualSampleNames)); } } - List actualSampleNames = variantStorageEngine.getSampleData(variant.toString(), studyMetadata.getName(), new QueryOptions()).first().getSampleNames(studyMetadata.getName()); - Assert.assertEquals(samplesInVariant, new HashSet<>(actualSampleNames)); } } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSplitDataTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSplitDataTest.java index 7ef850e2063..25b1dfaca2f 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSplitDataTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSplitDataTest.java @@ -2,8 +2,11 @@ import htsjdk.variant.vcf.VCFConstants; import org.apache.commons.lang3.StringUtils; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; import org.junit.*; import org.junit.experimental.categories.Category; +import org.mockito.Mockito; import org.opencb.biodata.models.variant.StudyEntry; import org.opencb.biodata.models.variant.Variant; import org.opencb.biodata.models.variant.avro.FileEntry; @@ -13,6 +16,7 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.common.UriUtils; import org.opencb.opencga.core.testclassification.duration.LongTests; import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; @@ -24,8 +28,10 @@ import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; +import org.opencb.opencga.storage.core.variant.VariantStoragePipeline; import org.opencb.opencga.storage.core.variant.adaptors.VariantField; import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.annotation.annotators.CellBaseRestVariantAnnotator; @@ -50,6 +56,7 @@ public class HadoopVariantStorageEngineSplitDataTest extends VariantStorageBaseTest implements HadoopVariantStorageTest { public static final List SAMPLES = Arrays.asList("NA19600", "NA19660", "NA19661", "NA19685"); + public static final String MOCKED_EXCEPTION = "Mocked exception"; @ClassRule public static HadoopExternalResource externalResource = new HadoopExternalResource(); @@ -63,6 +70,91 @@ public void tearDown() throws Exception { VariantHbaseTestUtils.printVariants(getVariantStorageEngine().getDBAdaptor(), newOutputUri(getTestName().getMethodName())); } + @Test + public void testMultiChromosomeSplitDataConcurrentFail() throws Exception { + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + failAtLoadingFile("by_chr/", "chr20.variant-test-file.vcf.gz", outputUri); + // Will fail if LOAD_SPLIT_DATA is not set + thrown.expect(StoragePipelineException.class); + variantStorageEngine.index(Collections.singletonList(getResourceUri("by_chr/chr21.variant-test-file.vcf.gz")), outputUri); + } + + @Test + public void testMultiChromosomeSplitDataConcurrentFailOneIndexOther() throws Exception { + // Test goal: + // - Index chr20 and chr21 concurrently with shared samples and LOAD_SPLIT_DATA=CHROMOSOME + // Test steps: + // - Fail at loading chr20 (left the file in status "RUNNING") + // - Index chr21 correctly with LOAD_SPLIT_DATA=CHROMOSOME + + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + failAtLoadingFile("by_chr/", "chr20.variant-test-file.vcf.gz", outputUri); + + // Won't fail if LOAD_SPLIT_DATA is set correctly + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_SPLIT_DATA.key(), VariantStorageEngine.SplitData.CHROMOSOME); + variantStorageEngine.index(Collections.singletonList(getResourceUri("by_chr/chr21.variant-test-file.vcf.gz")), outputUri); + } + + @Test + public void testMultiChromosomeSplitDataConcurrentFail3() throws Exception { + // Test goal: + // - Ensure file can be loaded after deleting a file with shared samples without any LOAD_SPLIT_DATA. + // Test steps: + // - Fail at loading chr20 (left the file in status "RUNNING") + // - Force remove chr20 + // - Index chr21 correctly + + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + failAtLoadingFile("by_chr/", "chr20.variant-test-file.vcf.gz", outputUri); + + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), true); + variantStorageEngine.removeFile(STUDY_NAME, "chr20.variant-test-file.vcf.gz", outputUri); + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), false); + + variantStorageEngine.index(Collections.singletonList(getResourceUri("by_chr/chr21.variant-test-file.vcf.gz")), outputUri); + } + + @Test + public void testMultiChromosomeSplitDataConcurrentFailDelete() throws Exception { + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + failAtLoadingFile("by_chr/", "chr20.variant-test-file.vcf.gz", outputUri); + + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_SPLIT_DATA.key(), VariantStorageEngine.SplitData.CHROMOSOME); + variantStorageEngine.index(Collections.singletonList(getResourceUri("by_chr/chr21.variant-test-file.vcf.gz")), outputUri); + + try { + // FORCE=true is needed, as the file is not correctly indexed + // Set FORCE=false to assert exception + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), false); + variantStorageEngine.removeFile(STUDY_NAME, "chr20.variant-test-file.vcf.gz", outputUri); + fail(); + } catch (StorageEngineException e) { + assertEquals("Unable to remove non indexed file: chr20.variant-test-file.vcf.gz", e.getMessage()); + } + + // FORCE=true is needed, as the file is not correctly indexed + failAtDeletingFile("chr20.variant-test-file.vcf.gz", outputUri, 1, new ObjectMap(VariantStorageOptions.FORCE.key(), true)); + + try { + // FORCE=true is needed, as the file is not correctly indexed + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), true); + // RESUME=true is needed, as the delete task is in status "RUNNING" from the previous failAtDeletingFile + // Set RESUME=false to assert exception + variantStorageEngine.getOptions().put(VariantStorageOptions.RESUME.key(), false); + variantStorageEngine.removeFile(STUDY_NAME, "chr20.variant-test-file.vcf.gz", outputUri); + fail(); + } catch (StorageEngineException e) { + assertEquals("Operation \"remove\" for files [\"chr20.variant-test-file.vcf.gz\" (id=1)] in status \"RUNNING\". Relaunch with resume=true to finish the operation.", e.getMessage()); + } + + + // FORCE=true is needed, as the file is not correctly indexed + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), true); + // RESUME=true is needed, as the delete task is in status "RUNNING" + variantStorageEngine.getOptions().put(VariantStorageOptions.RESUME.key(), true); + variantStorageEngine.removeFile(STUDY_NAME, "chr20.variant-test-file.vcf.gz", outputUri); + } + @Test public void testMultiChromosomeSplitData() throws Exception { variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); @@ -131,6 +223,57 @@ public void testMultiChromosomeSplitData() throws Exception { }, QueryOptions.empty()); } + private void failAtLoadingFile(String x, String file1, URI outputUri) throws Exception { + try { + VariantStorageEngine engine = getMockedStorageEngine(new ObjectMap(VariantStorageOptions.STUDY.key(), STUDY_NAME)); + engine.index(Collections.singletonList(getResourceUri(x + file1)), outputUri); + fail("Should have thrown an exception"); + } catch (StoragePipelineException e) { + try { + assertEquals(MOCKED_EXCEPTION, e.getCause().getMessage()); + int studyId = metadataManager.getStudyId(STUDY_NAME); + FileMetadata fileMetadata = metadataManager.getFileMetadata(studyId, file1); + assertEquals(TaskMetadata.Status.NONE, fileMetadata.getIndexStatus()); + List runningTasks = new ArrayList<>(); + metadataManager.getRunningTasks(studyId).forEach(runningTasks::add); + assertEquals(1, runningTasks.size()); + assertEquals(TaskMetadata.Type.LOAD, runningTasks.get(0).getType()); + assertEquals(TaskMetadata.Status.RUNNING, runningTasks.get(0).currentStatus()); + assertEquals(Arrays.asList(fileMetadata.getId()), runningTasks.get(0).getFileIds()); + } catch (AssertionError error) { + e.printStackTrace(); + throw error; + } + } + } + + private void failAtDeletingFile(String file, URI outputUri, int expectedRunningTasks, ObjectMap options) throws Exception { + int studyId = metadataManager.getStudyId(STUDY_NAME); + FileMetadata fileMetadata = metadataManager.getFileMetadata(studyId, file); + try { + getMockedStorageEngine(options).removeFile(STUDY_NAME, file, outputUri); + fail("Should have thrown an exception"); + } catch (StorageEngineException e) { + try { + assertEquals(MOCKED_EXCEPTION, e.getMessage()); + assertEquals(TaskMetadata.Status.NONE, fileMetadata.getIndexStatus()); + List runningTasks = new ArrayList<>(); + metadataManager.getRunningTasks(studyId).forEach(runningTasks::add); + assertEquals(expectedRunningTasks, runningTasks.size()); + Optional optional = runningTasks.stream() + .filter(t -> t.getType() == TaskMetadata.Type.REMOVE && Arrays.asList(fileMetadata.getId()).equals(t.getFileIds())) + .findFirst(); + assertTrue(optional.isPresent()); + assertEquals(TaskMetadata.Type.REMOVE, optional.get().getType()); + assertEquals(TaskMetadata.Status.RUNNING, optional.get().currentStatus()); + assertEquals(Arrays.asList(fileMetadata.getId()), optional.get().getFileIds()); + } catch (AssertionError error) { + e.printStackTrace(); + throw error; + } + } + } + @Test public void testMultiChromosomeSplitDataVirtualFile() throws Exception { variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); @@ -407,6 +550,226 @@ private void checkIssueEntries_22_44681612_A_G(Variant v) { } + @Test + public void testLoadMultiFileDataConcurrency() throws Exception { + + URI outDir = newOutputUri(); + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_MULTI_FILE_DATA.key(), false); + variantStorageEngine.getOptions().put(VariantStorageOptions.FAMILY.key(), true); + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + + String resourceDir = "by_chr/"; + String file1 = "chr22.variant-test-file.vcf.gz"; + String file2 = "chr22_1-2-DUP.variant-test-file.vcf.gz"; + + failAtLoadingFile(resourceDir, file1, outDir); + + try { + variantStorageEngine.index(Collections.singletonList(getResourceUri(resourceDir + file2)), outDir); + } catch (StoragePipelineException e) { + MatcherAssert.assertThat(e.getCause().getMessage(), startsWith("Can not \"Load\" files")); + MatcherAssert.assertThat(e.getCause().getMessage(), CoreMatchers.containsString(file2)); + MatcherAssert.assertThat(e.getCause().getMessage(), CoreMatchers.containsString(file1)); + } + + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), true); + variantStorageEngine.removeFile(STUDY_NAME, file1, outDir); + variantStorageEngine.index(Collections.singletonList(getResourceUri(resourceDir + file2)), outDir); + + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_MULTI_FILE_DATA.key(), true); +// variantStorageEngine.getOptions().put(VariantStorageOptions.RESUME.key(), true); + variantStorageEngine.index(Collections.singletonList(getResourceUri(resourceDir + file1)), outDir); + } + + + @Test + public void testLoadMultiFileDataConcurrencyDeleteMany() throws Exception { + + URI outDir = newOutputUri(); + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_MULTI_FILE_DATA.key(), false); + variantStorageEngine.getOptions().put(VariantStorageOptions.FAMILY.key(), true); + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + + String resourceDir = "platinum/"; + String file1 = "1K.end.platinum-genomes-vcf-NA12877_S1.vcf.gz"; + String file2 = "1K.end.platinum-genomes-vcf-NA12878_S1.vcf.gz"; + + failAtLoadingFile(resourceDir, file1, outDir); + failAtLoadingFile(resourceDir, file2, outDir); +// try { +// getMockedStorageEngine().index(Collections.singletonList(getResourceUri(resourceDir + file1)), outDir); +// fail("Should have thrown an exception"); +// } catch (StoragePipelineException e) { +// assertEquals(MOCKED_EXCEPTION, e.getCause().getMessage()); +// } +// try { +// getMockedStorageEngine().index(Collections.singletonList(getResourceUri(resourceDir + file2)), outDir); +// fail("Should have thrown an exception"); +// } catch (StoragePipelineException e) { +// assertEquals(MOCKED_EXCEPTION, e.getCause().getMessage()); +// } + + + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), true); + try { + variantStorageEngine.removeFile(STUDY_NAME, file1, outDir); + fail(); + } catch (StorageEngineException e) { + MatcherAssert.assertThat(e.getMessage(), startsWith("Can not \"remove\" files")); + MatcherAssert.assertThat(e.getMessage(), CoreMatchers.containsString(file1)); + MatcherAssert.assertThat(e.getMessage(), CoreMatchers.containsString(file2)); + } + try { + variantStorageEngine.removeFile(STUDY_NAME, file2, outDir); + fail(); + } catch (StorageEngineException e) { + MatcherAssert.assertThat(e.getMessage(), startsWith("Can not \"remove\" files")); + MatcherAssert.assertThat(e.getMessage(), CoreMatchers.containsString(file1)); + MatcherAssert.assertThat(e.getMessage(), CoreMatchers.containsString(file2)); + } + variantStorageEngine.removeFiles(STUDY_NAME, Arrays.asList(file1, file2), outDir); + + variantStorageEngine.index(Collections.singletonList(getResourceUri(resourceDir + file2)), outDir); + variantStorageEngine.index(Collections.singletonList(getResourceUri(resourceDir + file1)), outDir); + } + + @Test + public void testLoadMultiFileDataConcurrencyFail() throws Exception { + + URI outDir = newOutputUri(); + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_MULTI_FILE_DATA.key(), false); + variantStorageEngine.getOptions().put(VariantStorageOptions.FAMILY.key(), true); + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + + String resourceDir = "by_chr/"; + String file1 = "chr22.variant-test-file.vcf.gz"; + String file2 = "chr22_1-2-DUP.variant-test-file.vcf.gz"; + + failAtLoadingFile(resourceDir, file1, outDir); + + try { + variantStorageEngine.index(Collections.singletonList(getResourceUri(resourceDir + file2)), outDir); + } catch (StoragePipelineException e) { + MatcherAssert.assertThat(e.getCause().getMessage(), startsWith("Can not \"Load\" files")); + MatcherAssert.assertThat(e.getCause().getMessage(), CoreMatchers.containsString(file2)); + MatcherAssert.assertThat(e.getCause().getMessage(), CoreMatchers.containsString(file1)); + } + + variantStorageEngine.getOptions().put(VariantStorageOptions.LOAD_MULTI_FILE_DATA.key(), true); + variantStorageEngine.getOptions().put(VariantStorageOptions.RESUME.key(), true); + variantStorageEngine.index(Collections.singletonList(getResourceUri(file1)), outDir); + + } + + private VariantStorageEngine getMockedStorageEngine() throws Exception { + return getMockedStorageEngine(new ObjectMap()); + } + + private VariantStorageEngine getMockedStorageEngine(ObjectMap options) throws Exception { + HadoopVariantStorageEngine mockedStorageEngine = Mockito.spy(getVariantStorageEngine()); + mockedStorageEngine.getOptions().putAll(options); + mockedStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + VariantStoragePipeline mockedPipeline = Mockito.spy(variantStorageEngine.newStoragePipeline(true)); + + Mockito.doReturn(mockedPipeline).when(mockedStorageEngine).newStoragePipeline(Mockito.anyBoolean()); +// Mockito.doThrow(new StoragePipelineException(MOCKED_EXCEPTION, Collections.emptyList())).when(mockedPipeline).load(Mockito.any(), Mockito.any()); + Mockito.doAnswer(invocation -> { + // Throw StorageEngineException when calling load + System.out.printf("MOCKED load(%s, %s)%n", invocation.getArgument(0), invocation.getArgument(1)); + System.out.println("MOCKED load throw StorageEngineException"); + throw new StoragePipelineException(MOCKED_EXCEPTION, Collections.emptyList()); + }).when(mockedPipeline).load(Mockito.any(), Mockito.any()); + + Mockito.doAnswer(invocation -> { + // Call real method when calling preRemove, then throw StorageEngineException + System.out.printf("MOCKED preRemove(%s, %s, %s)%n", invocation.getArgument(0), invocation.getArgument(1), invocation.getArgument(2)); + System.out.println("MOCKED preRemove callRealMethod"); + invocation.callRealMethod(); + System.out.println("MOCKED preRemove callRealMethod DONE"); + System.out.println("MOCKED preRemove throw StorageEngineException"); + throw new StorageEngineException(MOCKED_EXCEPTION); + }).when(mockedStorageEngine).preRemove(Mockito.any(), Mockito.any(), Mockito.any()); + return mockedStorageEngine; + } + + @Test + public void testDeleteErrorFiles() throws Exception { + URI outDir = newOutputUri(); + + VariantStorageMetadataManager mm = variantStorageEngine.getMetadataManager(); + + variantStorageEngine.getOptions().put(VariantStorageOptions.STUDY.key(), STUDY_NAME); + URI file = variantStorageEngine.index(Collections.singletonList(getPlatinumFile(1)), outDir).get(0).getInput(); + String fileName = UriUtils.fileName(file); + + int studyId = mm.getStudyId(STUDY_NAME); + int fileId = mm.getFileId(studyId, fileName); + FileMetadata fileMetadata = mm.updateFileMetadata(studyId, fileId, fm -> { + fm.setIndexStatus(TaskMetadata.Status.INVALID); + }); + assertFalse(mm.isFileIndexed(studyId, fileId)); + assertFalse(fileMetadata.isIndexed()); + LinkedHashSet samples = fileMetadata.getSamples(); + + for (Integer sample : samples) { + mm.updateSampleMetadata(studyId, sample, sampleMetadata -> { + sampleMetadata.setIndexStatus(TaskMetadata.Status.INVALID); + }); + } + + try { + variantStorageEngine.get(new VariantQuery().file(fileName), new QueryOptions()); + fail(); + } catch (VariantQueryException e) { + String expected = VariantQueryException.fileNotIndexed(fileName, STUDY_NAME).getMessage(); + assertEquals(expected, e.getMessage()); + } + + try { + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), true); + variantStorageEngine.index(Collections.singletonList(getPlatinumFile(1)), outDir); + fail(); + } catch (StorageEngineException e) { + try { + String expected = StorageEngineException.invalidFileStatus(fileId, fileName).getMessage(); + assertEquals(expected, e.getCause().getMessage()); + } catch (AssertionError error) { + e.printStackTrace(); + throw error; + } + } + + try { + variantStorageEngine.getOptions().put(VariantStorageOptions.FORCE.key(), false); + variantStorageEngine.index(Collections.singletonList(getPlatinumFile(1)), outDir); + fail(); + } catch (StorageEngineException e) { + try { + String expected = StorageEngineException.invalidFileStatus(fileId, fileName).getMessage(); + assertEquals(expected, e.getCause().getMessage()); + } catch (AssertionError error) { + e.printStackTrace(); + throw error; + } + } + + variantStorageEngine.removeFile(STUDY_NAME, fileName, outDir); + + fileMetadata = mm.getFileMetadata(studyId, fileId); + assertEquals(TaskMetadata.Status.NONE, fileMetadata.getIndexStatus()); + for (Integer sample : samples) { + assertEquals(TaskMetadata.Status.NONE, mm.getSampleMetadata(studyId, sample).getIndexStatus()); + } + + variantStorageEngine.index(Collections.singletonList(getPlatinumFile(1)), outDir); + + fileMetadata = mm.getFileMetadata(studyId, fileId); + assertEquals(TaskMetadata.Status.READY, fileMetadata.getIndexStatus()); + for (Integer sample : samples) { + assertEquals(TaskMetadata.Status.READY, mm.getSampleMetadata(studyId, sample).getIndexStatus()); + } + } + @Test public void testLoadByRegion() throws Exception { URI outDir = newOutputUri(); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHadoopStoragePipelineTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHadoopStoragePipelineTest.java index 7abd5c71449..773f017a67c 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHadoopStoragePipelineTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHadoopStoragePipelineTest.java @@ -207,7 +207,7 @@ public void checkVariantTable() throws IOException { int num = 0; ResultScanner resultScanner = table.getScanner(GenomeHelper.COLUMN_FAMILY_BYTES); for (Result result : resultScanner) { - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(result.getRow()); + Variant variant = VariantPhoenixKeyFactory.extractVariantFromResult(result); System.out.println("Variant = " + variant); num++; } diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHbaseTestUtils.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHbaseTestUtils.java index 3557c5e55ff..8da9d0911f0 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHbaseTestUtils.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/VariantHbaseTestUtils.java @@ -46,6 +46,7 @@ import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; +import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator; import org.opencb.opencga.storage.core.variant.io.VariantWriterFactory; @@ -64,6 +65,7 @@ import java.io.*; import java.net.URI; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; @@ -72,7 +74,6 @@ import java.util.zip.DataFormatException; import static org.opencb.opencga.storage.core.variant.VariantStorageBaseTest.getTmpRootDir; -import static org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageTest.configuration; /** * Utility class for VariantStorage hadoop tests @@ -159,7 +160,7 @@ public static void printVariantsFromVariantsTable(VariantHadoopDBAdaptor dbAdapt for (Result result : resultScanner) { Variant variant; try { - variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(result.getRow()); + variant = VariantPhoenixKeyFactory.extractVariantFromResult(result); } catch (RuntimeException e) { os.println(Arrays.toString(result.getRow())); os.println("--------------------"); @@ -236,8 +237,11 @@ private static void printVariantsFromDBAdaptor(VariantHadoopDBAdaptor dbAdaptor, } private static void printVariantsFromDBAdaptor(VariantHadoopDBAdaptor dbAdaptor, PrintStream out) { - VariantDBIterator iterator = dbAdaptor.iterator(new Query(VariantQueryParam.INCLUDE_SAMPLE_DATA.key(), "all,SAMPLE_ID") - .append(VariantQueryParam.INCLUDE_SAMPLE.key(), ParamConstants.ALL), + if (dbAdaptor.getMetadataManager().getStudyIds().isEmpty()) { + out.println("No studies found!"); + return; + } + VariantDBIterator iterator = dbAdaptor.iterator(new VariantQuery().includeSampleId(true).includeSampleAll(), new QueryOptions("simpleGenotypes", true)); ObjectMapper mapper = new ObjectMapper().configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, true); while (iterator.hasNext()) { @@ -406,7 +410,7 @@ public static void printVariants(Collection studies, VariantHadoo } private static void printVcf(StudyMetadata studyMetadata, VariantHadoopDBAdaptor dbAdaptor, Path outDir) throws IOException { - try (OutputStream os = new FileOutputStream(outDir.resolve("variant." + studyMetadata.getName() + ".vcf").toFile())) { + try (OutputStream os = Files.newOutputStream(outDir.resolve("variant." + studyMetadata.getName() + ".vcf"))) { Query query = new Query(VariantQueryParam.STUDY.key(), studyMetadata.getName()) .append(VariantQueryParam.INCLUDE_SAMPLE.key(), ParamConstants.ALL) .append(VariantQueryParam.UNKNOWN_GENOTYPE.key(), "."); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorMultiFileTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorMultiFileTest.java index be47dd08890..bebcb5ce4da 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorMultiFileTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorMultiFileTest.java @@ -10,8 +10,7 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.api.ParamConstants; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; import org.opencb.opencga.storage.core.variant.VariantStorageEngine; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorNativeTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorNativeTest.java index 7d6b6f19ba5..e03253739d3 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorNativeTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorNativeTest.java @@ -9,7 +9,7 @@ import org.opencb.biodata.models.variant.Variant; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.LongTests; import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactoryTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactoryTest.java index 18798990bf1..05b6d81a5b3 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactoryTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/phoenix/VariantPhoenixKeyFactoryTest.java @@ -1,5 +1,9 @@ package org.opencb.opencga.storage.hadoop.variant.adaptors.phoenix; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.util.ByteStringer; import org.apache.hadoop.hbase.util.Bytes; @@ -11,7 +15,9 @@ import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.opencb.biodata.models.variant.Variant; +import org.opencb.biodata.models.variant.avro.StructuralVariation; import org.opencb.opencga.core.testclassification.duration.ShortTests; +import org.opencb.opencga.storage.hadoop.variant.GenomeHelper; import org.opencb.opencga.storage.hadoop.HBaseCompat; import java.sql.SQLException; @@ -36,6 +42,7 @@ public void testVariantRowKey() throws Exception { checkVariantRowKeyGeneration(new Variant("5", 21648, "A", "T")); checkVariantRowKeyGeneration(new Variant("5", 21648, "AAAAAA", "T")); checkVariantRowKeyGeneration(new Variant("5", 21648, "A", "")); + checkVariantRowKeyGeneration(new Variant("5", 21648, "", "T")); checkVariantRowKeyGeneration(new Variant("5", 21648, "AAT", "TTT")); checkVariantRowKeyGeneration(new Variant("X", 21648, "", "TTT")); checkVariantRowKeyGeneration(new Variant("MT", 21648, "", "")); @@ -54,6 +61,16 @@ public void testStructuralVariantRowKey() throws Exception { checkVariantRowKeyGeneration(new Variant("5:100:A:A]:chr5:234]")); } + @Test + public void testExtraLargeVariantRowKey() throws Exception { + String allele1 = RandomStringUtils.random(50000, "ACGT"); + checkVariantRowKeyGeneration(new Variant("5:1000:-:" + allele1)); + StructuralVariation sv = new StructuralVariation(); + sv.setLeftSvInsSeq(allele1); + sv.setRightSvInsSeq(allele1); + checkVariantRowKeyGeneration(new Variant("5:1000:A:").setSv(sv)); + } + @Test public void testExtractChrPosFromVariantRowKeyPartial() { byte[] phoenixRowKey = VariantPhoenixKeyFactory.generateVariantRowKey("1", 20 << 16); @@ -73,11 +90,18 @@ public void checkVariantRowKeyGeneration(Variant variant) { // System.out.println("expected = " + Bytes.toStringBinary(phoenixRowKey)); byte[] variantRowkey = VariantPhoenixKeyFactory.generateVariantRowKey(variant); + byte[] alleles = Bytes.toBytes(VariantPhoenixKeyFactory.buildAlleles(variant)); // System.out.println("actual = " + Bytes.toStringBinary(variantRowkey)); - Variant generatedVariant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(variantRowkey); + Variant generatedVariant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(variantRowkey, null, alleles); + + Result result = Result.create(Collections.singletonList(new KeyValue(phoenixRowKey, GenomeHelper.COLUMN_FAMILY_BYTES, VariantPhoenixSchema.VariantColumn.ALLELES.bytes(), + alleles))); + + Variant generatedVariant2 = VariantPhoenixKeyFactory.extractVariantFromResult(result); assertArrayEquals(variant.toString(), phoenixRowKey, variantRowkey); assertEquals(variant, generatedVariant); + assertEquals(variant, generatedVariant2); } public byte[] generateVariantRowKeyPhoenix(Variant variant) { @@ -104,12 +128,23 @@ public byte[] generateVariantRowKeyPhoenix(Variant variant) { } ImmutableBytesWritable key = new ImmutableBytesWritable(); + String reference = variant.getReference(); + String alternate = VariantPhoenixKeyFactory.buildSymbolicAlternate(reference, variant.getAlternate(), variant.getEnd(), variant.getSv()); table.newKey(key, new byte[][]{ Bytes.toBytes(variant.getChromosome()), Bytes.toBytes(variant.getStart()), - Bytes.toBytes(variant.getReference()), - Bytes.toBytes(VariantPhoenixKeyFactory.buildSymbolicAlternate(variant.getReference(), variant.getAlternate(), variant.getEnd(), variant.getSv())), + Bytes.toBytes(reference), + Bytes.toBytes(alternate), }); + if (key.getLength() > HConstants.MAX_ROW_LENGTH) { + key = new ImmutableBytesWritable(); + table.newKey(key, new byte[][]{ + Bytes.toBytes(variant.getChromosome()), + Bytes.toBytes(variant.getStart()), + Bytes.toBytes(VariantPhoenixKeyFactory.hashAllele(reference)), + Bytes.toBytes(VariantPhoenixKeyFactory.hashAllele(alternate)), + }); + } if (key.getLength() == key.get().length) { return key.get(); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTest.java index 3ae2a36bfbc..fdb5d42f1b9 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTest.java @@ -18,7 +18,7 @@ import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.models.operations.variant.VariantAggregateFamilyParams; import org.opencb.opencga.core.models.operations.variant.VariantAggregateParams; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.LongTests; import org.opencb.opencga.storage.core.StoragePipelineResult; import org.opencb.opencga.storage.core.metadata.models.StudyMetadata; @@ -356,8 +356,7 @@ private void checkInputValuesAreUnmodified(String aggregatedStudy, String refere dbAdaptor.getHBaseManager().act(dbAdaptor.getVariantTable(), table -> { table.getScanner(new Scan()).iterator().forEachRemaining(r -> { - byte[] row = r.getRow(); - Variant variant = VariantPhoenixKeyFactory.extractVariantFromVariantRowKey(row); + Variant variant = VariantPhoenixKeyFactory.extractVariantFromResult(r); NavigableMap cells = r.getFamilyMap(GenomeHelper.COLUMN_FAMILY_BYTES); for (Map.Entry entry : cells.entrySet()) { diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java index ccafd14be63..f67d5734d7c 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java @@ -16,7 +16,7 @@ import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.models.operations.variant.VariantAggregateFamilyParams; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; import org.opencb.opencga.storage.core.metadata.models.Trio; import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest; diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDBAdaptorTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDBAdaptorTest.java index 27f8a56f317..dc87e7d6d11 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDBAdaptorTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDBAdaptorTest.java @@ -46,7 +46,7 @@ public void testSampleIdFF() throws Exception { SampleIndexQuery query = new SampleIndexQuery(SampleIndexSchema.defaultSampleIndexSchema(), Collections.emptyList(), 0, null, "ST", Collections.singletonMap(sampleName, Collections.singletonList("0/1")), Collections.emptySet(), null, Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), new SampleAnnotationIndexQuery(SampleIndexSchema.defaultSampleIndexSchema()), - Collections.emptySet(), null, false, VariantQueryUtils.QueryOperation.AND); + Collections.emptySet(), null, false, VariantQueryUtils.QueryOperation.AND, null); new SampleIndexDBAdaptor(new HBaseManager(new Configuration()), new HBaseVariantTableNameGenerator("default", "my_dbname"), metadataManager) .parse(query.forSample(sampleName), null); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDuplicatedVariantsTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDuplicatedVariantsTest.java index 6a9c35eb0da..be83e365bed 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDuplicatedVariantsTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexDuplicatedVariantsTest.java @@ -63,7 +63,7 @@ public void test2FilesSampleIndex() throws Exception { SampleIndexOnlyVariantQueryExecutor queryExecutor = new SampleIndexOnlyVariantQueryExecutor(dbAdaptor, sampleIndexDBAdaptor, "", new ObjectMap()); List expectedVariants = new ArrayList<>(); - queryExecutor.iterator(new VariantQuery().sample("s1"), new QueryOptions()).forEachRemaining(expectedVariants::add); + queryExecutor.iterator(variantStorageEngine.parseQuery(new VariantQuery().sample("s1"), new QueryOptions())).forEachRemaining(expectedVariants::add); int studyId = engine.getMetadataManager().getStudyId(STUDY_NAME); String actualSampleIndexTableName = sampleIndexDBAdaptor.getSampleIndexTableNameLatest(studyId); @@ -81,7 +81,7 @@ public void test2FilesSampleIndex() throws Exception { VariantHbaseTestUtils.printVariants(dbAdaptor, newOutputUri()); List actualVariants = new ArrayList<>(); - queryExecutor.iterator(new VariantQuery().sample("s1"), new QueryOptions()).forEachRemaining(actualVariants::add); + queryExecutor.iterator(variantStorageEngine.parseQuery(new VariantQuery().sample("s1"), new QueryOptions())).forEachRemaining(actualVariants::add); Assert.assertEquals(expectedVariants, actualVariants); @@ -105,7 +105,7 @@ public void test3FilesSampleIndex() throws Exception { SampleIndexOnlyVariantQueryExecutor queryExecutor = new SampleIndexOnlyVariantQueryExecutor(dbAdaptor, sampleIndexDBAdaptor, "", new ObjectMap()); List expectedVariants = new ArrayList<>(); - queryExecutor.iterator(new VariantQuery().sample("s1"), new QueryOptions()).forEachRemaining(expectedVariants::add); + queryExecutor.iterator(variantStorageEngine.parseQuery(new VariantQuery().sample("s1"), new QueryOptions())).forEachRemaining(expectedVariants::add); int studyId = engine.getMetadataManager().getStudyId(STUDY_NAME); String actualSampleIndexTableName = sampleIndexDBAdaptor.getSampleIndexTableNameLatest(studyId); @@ -123,7 +123,7 @@ public void test3FilesSampleIndex() throws Exception { VariantHbaseTestUtils.printVariants(dbAdaptor, newOutputUri()); List actualVariants = new ArrayList<>(); - queryExecutor.iterator(new VariantQuery().sample("s1"), new QueryOptions()).forEachRemaining(actualVariants::add); + queryExecutor.iterator(variantStorageEngine.parseQuery(new VariantQuery().sample("s1"), new QueryOptions())).forEachRemaining(actualVariants::add); Assert.assertEquals(expectedVariants, actualVariants); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexEntryFilterTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexEntryFilterTest.java index 7933889d93f..a631c07e099 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexEntryFilterTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexEntryFilterTest.java @@ -280,7 +280,7 @@ private SingleSampleIndexQuery getSingleSampleIndexQuery(SampleAnnotationIndexQu private SingleSampleIndexQuery getSingleSampleIndexQuery(SampleAnnotationIndexQuery annotationIndexQuery, Map> fileFilterMap) { return new SampleIndexQuery( - schema, Collections.emptyList(), 0, null, "study", Collections.singletonMap("S1", Arrays.asList("0/1", "1/1")), Collections.emptySet(), null, Collections.emptyMap(), Collections.emptyMap(), fileFilterMap, annotationIndexQuery, Collections.emptySet(), null, false, VariantQueryUtils.QueryOperation.AND) + schema, Collections.emptyList(), 0, null, "study", Collections.singletonMap("S1", Arrays.asList("0/1", "1/1")), Collections.emptySet(), null, Collections.emptyMap(), Collections.emptyMap(), fileFilterMap, annotationIndexQuery, Collections.emptySet(), null, false, VariantQueryUtils.QueryOperation.AND, null) .forSample("S1"); } } \ No newline at end of file diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java index fa1cc7ffe8e..99cf76e71cc 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java @@ -26,8 +26,8 @@ import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.dummy.DummyVariantStorageMetadataDBAdaptorFactory; import org.opencb.opencga.storage.core.variant.query.Values; -import org.opencb.opencga.storage.core.variant.query.VariantQueryParser; import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils; +import org.opencb.opencga.storage.hadoop.variant.HadoopVariantQueryParser; import org.opencb.opencga.storage.hadoop.variant.index.annotation.AnnotationIndexConverter; import org.opencb.opencga.storage.hadoop.variant.index.core.IndexField; import org.opencb.opencga.storage.hadoop.variant.index.core.RangeIndexField; @@ -127,7 +127,7 @@ public void setUp() throws Exception { } private SampleIndexQuery parse(final Query query) { - Query newQuery = new VariantQueryParser(null, mm).preProcessQuery(query, new QueryOptions()); + Query newQuery = new HadoopVariantQueryParser(null, mm).preProcessQuery(query, new QueryOptions()); query.clear(); query.putAll(newQuery); return sampleIndexQueryParser.parse(query); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java index 6267f8d8e23..55d9d98a0f8 100644 --- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java @@ -26,7 +26,7 @@ import org.opencb.opencga.core.config.storage.IndexFieldConfiguration; import org.opencb.opencga.core.config.storage.SampleIndexConfiguration; import org.opencb.opencga.core.models.variant.VariantAnnotationConstants; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.core.testclassification.duration.LongTests; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; @@ -369,8 +369,8 @@ public void regenerateSampleIndex() throws Exception { } public VariantQueryResult dbAdaptorQuery(Query query, QueryOptions options) { - query = variantStorageEngine.preProcessQuery(query, options); - return dbAdaptor.get(query, options); + ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options); + return dbAdaptor.get(variantQuery); } @Test @@ -463,26 +463,26 @@ public void testLocusQueryOverlap() throws Exception { VariantQuery query = new VariantQuery().study(STUDY_NAME_5).sample("NA19600"); // System.out.println("query = " + query.toJson()); List variants = sampleIndexDBAdaptor.iterator(new Query(query), new QueryOptions()) - .toDataResult().getResults(); + .toList(); assertEquals(2, variants.size()); query.region("1:2000200-5500000"); // System.out.println("query = " + query.toJson()); variants = sampleIndexDBAdaptor.iterator(new Query(query), new QueryOptions()) - .toDataResult().getResults(); + .toList(); assertEquals(2, variants.size()); query.region("1:200-2500000"); // System.out.println("query = " + query.toJson()); variants = sampleIndexDBAdaptor.iterator(new Query(query), new QueryOptions()) - .toDataResult().getResults(); + .toList(); assertEquals(1, variants.size()); assertEquals("1:1000001-4000000:-:", variants.get(0).toString()); query.region("1:2000200-2500000"); // System.out.println("query = " + query.toJson()); variants = sampleIndexDBAdaptor.iterator(new Query(query), new QueryOptions()) - .toDataResult().getResults(); + .toList(); assertEquals(1, variants.size()); assertEquals("1:1000001-4000000:-:", variants.get(0).toString()); } @@ -668,10 +668,10 @@ public SampleIndexQuery testQueryIndex(Query testQuery, Query query, boolean sam SampleIndexQuery indexQuery = sampleIndexDBAdaptor.parseSampleIndexQuery(sampleIndexVariantQuery); // int onlyIndex = (int) ((HadoopVariantStorageEngine) variantStorageEngine).getSampleIndexDBAdaptor() // .count(indexQuery, "NA19600"); - DataResult result = ((HadoopVariantStorageEngine) variantStorageEngine).getSampleIndexDBAdaptor() - .iterator(indexQuery).toDataResult(); + List result = ((HadoopVariantStorageEngine) variantStorageEngine).getSampleIndexDBAdaptor() + .iterator(indexQuery).toList(); // System.out.println("result.getResults() = " + result.getResults()); - List onlyIndex = result.getResults().stream().map(Variant::toString).sorted().collect(toList()); + List onlyIndex = result.stream().map(Variant::toString).sorted().collect(toList()); // Query SampleIndex+DBAdaptor System.out.println("#Query SampleIndex+DBAdaptor"); @@ -815,7 +815,7 @@ public void testCount() throws StorageEngineException { System.out.println("Count = " + actualCount); stopWatch = StopWatch.createStarted(); - long actualCountIterator = sampleIndexDBAdaptor.iterator(sampleIndexDBAdaptor.parseSampleIndexQuery(new Query(query))).toDataResult().getNumResults(); + long actualCountIterator = sampleIndexDBAdaptor.iterator(sampleIndexDBAdaptor.parseSampleIndexQuery(new Query(query))).toList().size(); System.out.println("---"); System.out.println("Count indexTable iterator " + stopWatch.getTime(TimeUnit.MILLISECONDS) / 1000.0); System.out.println("Count = " + actualCountIterator); @@ -1200,24 +1200,24 @@ private void testSampleIndexOnlyVariantQueryExecutor(VariantQuery query, QueryOp private void testSampleIndexOnlyVariantQueryExecutor(VariantQuery query, QueryOptions options, Class expected, Function mapper) { - VariantQueryExecutor variantQueryExecutor = variantStorageEngine.getVariantQueryExecutor( - query, - options); + ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options); + + VariantQueryExecutor variantQueryExecutor = variantStorageEngine.getVariantQueryExecutor(variantQuery); assertEquals(expected, variantQueryExecutor.getClass()); - ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options); List expectedVariants = new ArrayList<>(1000); - dbAdaptor.iterator(variantQuery, new QueryOptions(options)) + dbAdaptor.iterator(variantQuery) .forEachRemaining(expectedVariants::add); List actualVariants = new ArrayList<>(1000); - variantQueryExecutor.iterator(variantQuery.getQuery(), options) + variantQueryExecutor.iterator(variantStorageEngine.parseQuery(variantQuery.getQuery(), options)) .forEachRemaining(actualVariants::add); - VariantQueryResult result = variantQueryExecutor.get(variantQuery.getQuery(), new QueryOptions(options) + ParsedVariantQuery limitedQuery = variantStorageEngine.parseQuery(variantQuery.getQuery(), new QueryOptions(options) .append(QueryOptions.LIMIT, 10) .append(QueryOptions.COUNT, true)); + VariantQueryResult result = variantQueryExecutor.get(limitedQuery); assertEquals(10, result.getNumResults()); assertEquals(10, result.getResults().size()); long count = result.getNumMatches(); diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/query/executors/HadoopVariantQueryExecutorTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/query/executors/HadoopVariantQueryExecutorTest.java new file mode 100644 index 00000000000..7cc495bbe63 --- /dev/null +++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/query/executors/HadoopVariantQueryExecutorTest.java @@ -0,0 +1,30 @@ +package org.opencb.opencga.storage.hadoop.variant.query.executors; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExternalResource; +import org.opencb.opencga.core.testclassification.duration.LongTests; +import org.opencb.opencga.storage.core.variant.query.executors.VariantQueryExecutorTest; +import org.opencb.opencga.storage.hadoop.variant.HadoopVariantStorageTest; +import org.opencb.opencga.storage.hadoop.variant.VariantHbaseTestUtils; + +import static org.opencb.opencga.storage.hadoop.variant.VariantHbaseTestUtils.printVariants; + + +@Category(LongTests.class) +public class HadoopVariantQueryExecutorTest extends VariantQueryExecutorTest implements HadoopVariantStorageTest { + + @ClassRule + public static ExternalResource externalResource = new HadoopExternalResource(); + + @Override + @Before + public void setUp() throws Exception { + boolean firstRun = !super.fileIndexed; + super.setUp(); + if (firstRun) { + VariantHbaseTestUtils.printVariants(getVariantStorageEngine().getDBAdaptor(), newOutputUri()); + } + } +} diff --git a/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/RestStorageServer.java b/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/RestStorageServer.java index 226c8ab46ce..5343a17eb1d 100644 --- a/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/RestStorageServer.java +++ b/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/RestStorageServer.java @@ -16,11 +16,15 @@ package org.opencb.opencga.storage.server.rest; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; +import org.opencb.opencga.core.config.RestServerConfiguration; import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.storage.server.common.AbstractStorageServer; import org.slf4j.LoggerFactory; @@ -60,7 +64,14 @@ public void start() throws Exception { ServletHolder sh = new ServletHolder("opencga", sc); logger.info("Server in port : {}", port); - server = new Server(port); + server = new Server(); + + HttpConfiguration httpConfig = getHttpConfiguration(); + + ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); + httpConnector.setPort(port); + + server.addConnector(httpConnector); ServletContextHandler context = new ServletContextHandler(server, null, ServletContextHandler.SESSIONS); context.addServlet(sh, "/opencga/webservices/rest/*"); @@ -103,6 +114,27 @@ public void run() { AdminRestWebService.setServer(this); } + private HttpConfiguration getHttpConfiguration() { + HttpConfiguration httpConfig = new HttpConfiguration(); + RestServerConfiguration.HttpConfiguration restHttpConf = storageConfiguration.getServer().getRest().getHttpConfiguration(); + if (restHttpConf.getOutputBufferSize() > 0) { + httpConfig.setOutputBufferSize(restHttpConf.getOutputBufferSize()); + } + if (restHttpConf.getOutputAggregationSize() > 0) { + httpConfig.setOutputAggregationSize(restHttpConf.getOutputAggregationSize()); + } + if (restHttpConf.getRequestHeaderSize() > 0) { + httpConfig.setRequestHeaderSize(restHttpConf.getRequestHeaderSize()); + } + if (restHttpConf.getResponseHeaderSize() > 0) { + httpConfig.setResponseHeaderSize(restHttpConf.getResponseHeaderSize()); + } + if (restHttpConf.getHeaderCacheSize() > 0) { + httpConfig.setHeaderCacheSize(restHttpConf.getHeaderCacheSize()); + } + return httpConfig; + } + @Override public void stop() throws Exception { // By setting exit to true the monitor thread will close the Jetty server diff --git a/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/VariantRestWebService.java b/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/VariantRestWebService.java index 00832a76dd9..c6292d4f2f1 100644 --- a/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/VariantRestWebService.java +++ b/opencga-storage/opencga-storage-server/src/main/java/org/opencb/opencga/storage/server/rest/VariantRestWebService.java @@ -20,7 +20,7 @@ import org.opencb.commons.datastore.core.DataResult; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.opencga.core.response.VariantQueryResult; +import org.opencb.opencga.storage.core.variant.query.VariantQueryResult; import org.opencb.opencga.storage.core.StorageEngineFactory; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor;