diff --git a/.gitmodules b/.gitmodules index e0f4529..f075173 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "vendor/googletest"] path = vendor/googletest url = https://github.com/google/googletest +[submodule "vendor/rampler"] + path = vendor/rampler + url = https://github.com/rvaser/rampler diff --git a/CMakeLists.txt b/CMakeLists.txt index d17a8d9..590a7c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) option(racon_build_tests "Build racon unit tests" OFF) +option(racon_build_wrapper "Build racon wrapper" OFF) add_executable(racon src/main.cpp @@ -46,3 +47,16 @@ if (racon_build_tests) target_link_libraries(racon_test bioparser spoa thread_pool pthread edlib_static gtest_main) endif(racon_build_tests) + +if (racon_build_wrapper) + set(racon_path ${PROJECT_BINARY_DIR}/bin/racon) + set(rampler_path ${PROJECT_BINARY_DIR}/vendor/rampler/bin/rampler) + configure_file(${PROJECT_SOURCE_DIR}/scripts/racon_wrapper.py + ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/racon_wrapper) + file(COPY ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/racon_wrapper + DESTINATION ${PROJECT_BINARY_DIR}/bin + FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + + add_subdirectory(vendor/rampler) +endif(racon_build_wrapper) diff --git a/README.md b/README.md index 794e0b2..283a133 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,13 @@ Consensus module for raw de novo DNA assembly of long uncorrected reads. ## Description Racon is intended as a standalone consensus module to correct raw contigs generated by rapid assembly methods which do not include a consensus step. The goal of Racon is to generate genomic consensus which is of similar or better quality compared to the output generated by assembly methods which employ both error correction and consensus steps, while providing a speedup of several times compared to those methods. It supports data produced by both Pacific Biosciences and Oxford Nanopore Technologies. -Racon can be used as a polishing tool after the assembly with either Illumina data or data produced by third generation of sequencing. The type of data inputed is automatically detected. +Racon can be used as a polishing tool after the assembly with **either Illumina data or data produced by third generation of sequencing**. The type of data inputed is automatically detected. -Racon takes as input only three files: contigs in FASTA/FASTQ format, reads in FASTA/FASTQ format and overlaps/alignments between the reads and the contigs in MHAP/PAF/SAM format. Output is a set of polished contigs in FASTA format printed to stdout. All input files can be compressed with gzip. +Racon takes as input only three files: contigs in FASTA/FASTQ format, reads in FASTA/FASTQ format and overlaps/alignments between the reads and the contigs in MHAP/PAF/SAM format. Output is a set of polished contigs in FASTA format printed to stdout. All input files **can be compressed with gzip**. -Racon can also be used as a read error-correction tool. In this scenario, the MHAP/PAF/SAM file needs to contain pairwise overlaps between reads without any dual or self overlaps. +Racon can also be used as a read error-correction tool. In this scenario, the MHAP/PAF/SAM file needs to contain pairwise overlaps between reads **with dual overlaps**. + +A **wrapper script** is also available to enable easier usage to the end-user for large datasets. It has the same interface as racon but adds two additional features from the outside. Sequences can be **subsampled** to decrease the total execution time (accuracy might be lower) while target sequences can be **split** into smaller chunks and run sequentially to decrease memory consumption. Both features can be run at the same time as well. ## Dependencies 1. gcc 4.8+ or clang 3.4+ @@ -37,8 +39,12 @@ Optionally, you can run `sudo make install` to install racon executable to your ***Note***: if you omitted `--recursive` from `git clone`, run `git submodule update --init --recursive` before proceeding with compilation. +To build unit tests add `-Dracon_build_tests=ON` while running `cmake`. After installation, an executable named `racon_test` will be created in `build/bin`. + +To build the wrapper script add `-Dracon_build_wrapper=ON` while running `cmake`. After installation, an executable named `racon_wrapper` (python script) will be created in `build/bin`. + ## Usage -Usage of racon is as following: +Usage of `racon` is as following: racon [options ...] @@ -86,6 +92,19 @@ Usage of racon is as following: -h, --help prints the usage +`racon_test` is run without any parameters. + +Usage of `racon_wrapper` equals the one of `racon` with two additional parameters: + + ... + options: + --split + split target sequences into chunks of desired size in bytes + --subsample + subsample sequences to desired coverage (2nd argument) given the + reference length (1st argument) + ... + ## Contact information For additional information, help and bug reports please send an email to one of the following: ivan.sovic@irb.hr, robert.vaser@fer.hr, mile.sikic@fer.hr, nagarajann@gis.a-star.edu.sg diff --git a/scripts/racon_wrapper.py b/scripts/racon_wrapper.py new file mode 100644 index 0000000..4d47221 --- /dev/null +++ b/scripts/racon_wrapper.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python + +from __future__ import print_function +import os, sys, time, shutil, argparse, subprocess + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + +#******************************************************************************* + +class RaconWrapper: + + __racon = '@racon_path@' + __rampler = '@rampler_path@' + + def __init__(self, sequences, overlaps, target_sequences, split, subsample, + include_unpolished, fragment_correction, window_length, quality_threshold, + error_threshold, match, mismatch, gap, threads): + + self.sequences = os.path.abspath(sequences) + self.subsampled_sequences = None + self.overlaps = os.path.abspath(overlaps) + self.target_sequences = os.path.abspath(target_sequences) + self.split_target_sequences = [] + self.chunk_size = split + self.reference_length, self.coverage = subsample if subsample is not None\ + else (None, None) + self.include_unpolished = include_unpolished + self.fragment_correction = fragment_correction + self.window_length = window_length + self.quality_threshold = quality_threshold + self.error_threshold = error_threshold + self.match = match + self.mismatch = mismatch + self.gap = gap + self.threads = threads + self.work_directory = os.path.dirname(os.path.realpath(__file__)) +\ + '/racon_work_directory_' + str(time.time()) + + try: + os.makedirs(self.work_directory) + except OSError: + if (not os.path.isdir(self.work_directory)): + eprint('[RaconWrapper::run] error: unable to create work directory!') + sys.exit(1) + + def __del__(self): + try: + shutil.rmtree(self.work_directory) + except OSError: + eprint('[RaconWrapper::run] warning: unable to clean work directory!') + + def run(self): + # run preprocess + eprint('[RaconWrapper::run] preparing data with rampler') + if (self.reference_length is not None and self.coverage is not None): + try: + p = subprocess.Popen([RaconWrapper.__rampler, '-o', self.work_directory, + 'subsample', self.sequences, self.reference_length, self.coverage]) + except OSError: + eprint('[RaconWrapper::run] error: unable to run rampler!') + sys.exit(1) + p.communicate() + if (p.returncode != 0): + sys.exit(1) + + base_name = os.path.basename(self.sequences).split('.')[0] + extension = '.fasta' if (self.sequences.endswith('.fasta') or\ + self.sequences.endswith('.fasta.gz') or\ + self.sequences.endswith('.fa') or\ + self.sequences.endswith('.fa.gz')) else\ + '.fastq' + self.subsampled_sequences = os.path.join(self.work_directory, base_name) +\ + '_' + self.coverage + 'x' + extension + if (not os.path.isfile(self.subsampled_sequences)): + eprint('[RaconWrapper::run] error: unable to find subsampled sequences!') + sys.exit(1) + else: + self.subsampled_sequences = self.sequences + + if (self.chunk_size is not None): + try: + p = subprocess.Popen([RaconWrapper.__rampler, '-o', self.work_directory, + 'split', self.target_sequences, self.chunk_size]) + except OSError: + eprint('[RaconWrapper::run] error: unable to run rampler!') + sys.exit(1) + p.communicate() + if (p.returncode != 0): + sys.exit(1) + + base_name = os.path.basename(self.target_sequences).split('.')[0] + extension = '.fasta' if (self.target_sequences.endswith('.fasta') or\ + self.target_sequences.endswith('.fasta.gz') or\ + self.target_sequences.endswith('.fa') or\ + self.target_sequences.endswith('.fa.gz')) else\ + '.fastq' + + i = 0 + while (True): + target_sequences_part = os.path.join(self.work_directory, base_name) +\ + '_' + str(i) + extension + if (not os.path.isfile(target_sequences_part)): + break + self.split_target_sequences.append(target_sequences_part) + i += 1 + if (len(self.split_target_sequences) == 0): + eprint('[RaconWrapper::run] error: unable to find split target sequences!') + sys.exit(1) + else: + self.split_target_sequences.append(self.target_sequences) + + racon_params = [RaconWrapper.__racon] + if (self.include_unpolished == True): racon_params.append('-u') + if (self.fragment_correction == True): racon_params.append('-f') + racon_params.extend(['-w', str(self.window_length), + '-q', str(self.quality_threshold), + '-e', str(self.error_threshold), + '-m', str(self.match), + '-x', str(self.mismatch), + '-g', str(self.gap), + '-t', str(self.threads), + self.subsampled_sequences, self.overlaps, ""]) + + for target_sequences_part in self.split_target_sequences: + eprint('[RaconWrapper::run] processing data with racon') + racon_params[-1] = target_sequences_part + try: + p = subprocess.Popen(racon_params) + except OSError: + eprint('[RaconWrapper::run] error: unable to run racon!') + sys.exit(1) + p.communicate() + if (p.returncode != 0): + sys.exit(1) + + self.subsampled_sequences = None + self.split_target_sequences = [] + +#******************************************************************************* + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description='''Racon_wrapper encapsulates + racon and adds two additional features from the outside to enable easier + usage to the end-user. Sequences can now be subsampled to decrease the + total execution time (accuracy might be lower) while target + sequences can be split into smaller chunks and run sequentially to + decrease memory consumption. Both features can be run at the same time + as well! The usage equals the one of racon.''', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('sequences', help='''input file in FASTA/FASTQ format + (can be compressed with gzip) containing sequences used for correction''') + parser.add_argument('overlaps', help='''input file in MHAP/PAF/SAM format + (can be compressed with gzip) containing overlaps between sequences and + target sequences''') + parser.add_argument('target_sequences', help='''input file in FASTA/FASTQ + format (can be compressed with gzip) containing sequences which will be + corrected''') + parser.add_argument('--split', help='''split target sequences into chunks of + desired size in bytes''') + parser.add_argument('--subsample', nargs=2, help='''subsample sequences to + desired coverage (2nd argument) given the reference length (1st argument)''') + parser.add_argument('-u', '--include-unpolished', action='store_true', + help='''output unpolished target sequences''') + parser.add_argument('-f', '--fragment-correction', action='store_true', + help='''perform fragment correction instead of contig polishing + (overlaps file should not contain dual/self overlaps!)''') + parser.add_argument('-w', '--window-length', default=500, help='''size of + window on which POA is performed''') + parser.add_argument('-q', '--quality-threshold', default=10.0, + help='''threshold for average base quality of windows used in poa''') + parser.add_argument('-e', '--error-threshold', default=0.3, help='''maximum + allowed error rate used for filtering overlaps''') + parser.add_argument('-m', '--match', default=5, help='''score for matching + bases''') + parser.add_argument('-x', '--mismatch', default=-4, help='''score for + mismatching bases''') + parser.add_argument('-g', '--gap', default=-8, help='''gap penalty (must be + negative)''') + parser.add_argument('-t', '--threads', default=1, help='''number of threads''') + + args = parser.parse_args() + + racon = RaconWrapper(args.sequences, args.overlaps, args.target_sequences, + args.split, args.subsample, args.include_unpolished, + args.fragment_correction, args.window_length, args.quality_threshold, + args.error_threshold, args.match, args.mismatch, args.gap, args.threads) + racon.run() diff --git a/src/main.cpp b/src/main.cpp index c60379d..3ea2522 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ #include "sequence.hpp" #include "polisher.hpp" -static const char* version = "v1.1.1"; +static const char* version = "v1.2.0"; static struct option options[] = { {"include-unpolished", no_argument, 0, 'u'}, @@ -128,9 +128,7 @@ void help() { "\n" " options:\n" " -u, --include-unpolished\n" - " output unpolished target sequences (each sequence contains\n" - " a tag in its header (C:) which represents the\n" - " percentage of polished windows)\n" + " output unpolished target sequences\n" " -f, --fragment-correction\n" " perform fragment correction instead of contig polishing\n" " (overlaps file should not contain dual/self overlaps!)\n" diff --git a/src/overlap.cpp b/src/overlap.cpp index dc02d48..5a95230 100644 --- a/src/overlap.cpp +++ b/src/overlap.cpp @@ -19,7 +19,7 @@ Overlap::Overlap(uint64_t a_id, uint64_t b_id, double, uint32_t, q_length_(a_length), t_name_(), t_id_(b_id - 1), t_begin_(b_begin), t_end_(b_end), t_length_(b_length), strand_(a_rc ^ b_rc), length_(), error_(), cigar_(), is_valid_(true), is_transmuted_(false), - breaking_points_(), dual_breaking_points_() { + breaking_points_() { length_ = std::max(q_end_ - q_begin_, t_end_ - t_begin_); error_ = 1 - std::min(q_end_ - q_begin_, t_end_ - t_begin_) / @@ -34,8 +34,7 @@ Overlap::Overlap(const char* q_name, uint32_t q_name_length, uint32_t q_length, q_end_(q_end), q_length_(q_length), t_name_(t_name, t_name_length), t_id_(), t_begin_(t_begin), t_end_(t_end), t_length_(t_length), strand_(orientation == '-'), length_(), error_(), cigar_(), - is_valid_(true), is_transmuted_(false), breaking_points_(), - dual_breaking_points_() { + is_valid_(true), is_transmuted_(false), breaking_points_() { length_ = std::max(q_end_ - q_begin_, t_end_ - t_begin_); error_ = 1 - std::min(q_end_ - q_begin_, t_end_ - t_begin_) / @@ -51,7 +50,7 @@ Overlap::Overlap(const char* q_name, uint32_t q_name_length, uint32_t flag, q_length_(0), t_name_(t_name, t_name_length), t_id_(), t_begin_(t_begin - 1), t_end_(), t_length_(0), strand_(flag & 0x10), length_(), error_(), cigar_(cigar, cigar_length), is_valid_(!(flag & 0x4)), - is_transmuted_(false), breaking_points_(), dual_breaking_points_() { + is_transmuted_(false), breaking_points_() { if (cigar_.size() < 2 && is_valid_) { fprintf(stderr, "[Racon::Overlap::Overlap] error: " @@ -223,27 +222,12 @@ void Overlap::find_breaking_points(const std::vector>& } window_ends.emplace_back(t_end_ - 1); - std::vector dual_window_ends; - if (strand_) { - dual_window_ends.emplace_back(q_length_ - q_begin_ - 1); - } - for (uint32_t i = 0; i < q_end_; i += window_length) { - if (i > q_begin_) { - dual_window_ends.emplace_back(strand_ ? q_length_ - i - 1 : i - 1); - } - } - if (strand_) { - std::reverse(dual_window_ends.begin(), dual_window_ends.end()); - } else { - dual_window_ends.emplace_back(q_end_ - 1); - } + uint32_t w = 0; + bool found_first_match = false; + std::pair first_match = {0, 0}, last_match = {0, 0}; - uint32_t w = 0, d_w = 0; - bool found_first_match = false, found_first_dual_match = false; - std::pair first_match = {0, 0}, last_match = {0, 0}, - first_dual_match = {0, 0}, last_dual_match = {0, 0}; - int32_t q_ptr = (strand_ ? (q_length_ - q_end_) : q_begin_) - 1, - t_ptr = t_begin_ - 1; + int32_t q_ptr = (strand_ ? (q_length_ - q_end_) : q_begin_) - 1; + int32_t t_ptr = t_begin_ - 1; for (uint32_t i = 0, j = 0; i < cigar_.size(); ++i) { if (cigar_[i] == 'M' || cigar_[i] == '=' || cigar_[i] == 'X') { @@ -269,38 +253,11 @@ void Overlap::find_breaking_points(const std::vector>& ++w; } - if (!found_first_dual_match) { - found_first_dual_match = true; - first_dual_match.first = strand_ ? q_length_ - q_ptr : q_ptr; - first_dual_match.second = strand_ ? t_length_ - t_ptr : t_ptr; - } - last_dual_match.first = strand_ ? q_length_ - q_ptr - 1 : q_ptr + 1; - last_dual_match.second = strand_ ? t_length_ - t_ptr - 1 : t_ptr + 1; - if (q_ptr == dual_window_ends[d_w]) { - if (found_first_dual_match) { - dual_breaking_points_.emplace_back(first_dual_match); - dual_breaking_points_.emplace_back(last_dual_match); - } - found_first_dual_match = false; - ++d_w; - } ++k; } } else if (cigar_[i] == 'I') { - uint32_t k = 0, num_bases = atoi(&cigar_[j]); + q_ptr += atoi(&cigar_[j]); j = i + 1; - while (k < num_bases) { - ++q_ptr; - if (q_ptr == dual_window_ends[d_w]) { - if (found_first_dual_match) { - dual_breaking_points_.emplace_back(first_dual_match); - dual_breaking_points_.emplace_back(last_dual_match); - } - found_first_dual_match = false; - ++d_w; - } - ++k; - } } else if (cigar_[i] == 'D' || cigar_[i] == 'N') { uint32_t k = 0, num_bases = atoi(&cigar_[j]); j = i + 1; @@ -320,33 +277,8 @@ void Overlap::find_breaking_points(const std::vector>& j = i + 1; } } - if (strand_) { - std::reverse(dual_breaking_points_.begin(), dual_breaking_points_.end()); - } std::string().swap(cigar_); } -std::unique_ptr Overlap::dual_overlap() { - - if (!is_transmuted_) { - fprintf(stderr, "[racon::Overlap::dual_overlap] error: " - "overlap is not transmuted!\n"); - exit(1); - } - if (dual_breaking_points_.empty()) { - fprintf(stderr, "[racon::Overlap::dual_overlap] error: " - "dual breaking points unavailable!\n"); - exit(1); - } - - auto other = std::unique_ptr(new Overlap()); - other->q_id_ = t_id_; - other->t_id_ = q_id_; - other->strand_ = strand_; - other->breaking_points_.swap(dual_breaking_points_); - - return std::move(other); -} - } diff --git a/src/overlap.hpp b/src/overlap.hpp index 6490841..351289c 100644 --- a/src/overlap.hpp +++ b/src/overlap.hpp @@ -65,15 +65,9 @@ class Overlap { return breaking_points_; } - const std::vector>& dual_breaking_points() const { - return dual_breaking_points_; - } - void find_breaking_points(const std::vector>& sequences, uint32_t window_length); - std::unique_ptr dual_overlap(); - friend bioparser::MhapParser; friend bioparser::PafParser; friend bioparser::SamParser; diff --git a/src/polisher.cpp b/src/polisher.cpp index ec06735..5e0ccbd 100644 --- a/src/polisher.cpp +++ b/src/polisher.cpp @@ -188,7 +188,6 @@ void Polisher::initialize() { fprintf(stderr, "[racon::Polisher::initialize] loaded target sequences\n"); - std::unordered_set duplicate_sequences; uint64_t sequences_size = 0, total_sequences_length = 0; sparser_->reset(); @@ -214,7 +213,6 @@ void Polisher::initialize() { name_to_id[sequences_[i]->name() + "q"] = it->second; id_to_id[sequences_size << 1 | 0] = it->second; - duplicate_sequences.insert(it->second); sequences_[i].reset(); ++n; } else { @@ -252,7 +250,8 @@ void Polisher::initialize() { if (overlaps[i] == nullptr) { continue; } - if (overlaps[i]->error() > error_threshold_) { + if (overlaps[i]->error() > error_threshold_ || + overlaps[i]->q_id() == overlaps[i]->t_id()) { overlaps[i].reset(); continue; } @@ -306,12 +305,6 @@ void Polisher::initialize() { if (overlaps[i]->strand()) { has_reverse_data[overlaps[i]->q_id()] = true; - if (type_ == PolisherType::kF && - duplicate_sequences.find(overlaps[i]->q_id()) != duplicate_sequences.end() && - duplicate_sequences.find(overlaps[i]->t_id()) != duplicate_sequences.end()) { - - has_reverse_data[overlaps[i]->t_id()] = true; - } } else { has_data[overlaps[i]->q_id()] = true; } @@ -360,22 +353,6 @@ void Polisher::initialize() { } fprintf(stderr, "\n"); - if (type_ == PolisherType::kF && !duplicate_sequences.empty()) { - uint64_t num_overlaps = overlaps.size(); - for (uint64_t i = 0; i < num_overlaps; ++i) { - if (duplicate_sequences.find(overlaps[i]->q_id()) != duplicate_sequences.end() && - duplicate_sequences.find(overlaps[i]->t_id()) != duplicate_sequences.end()) { - - overlaps.emplace_back(overlaps[i]->dual_overlap()); - } - } - - std::sort(overlaps.begin(), overlaps.end(), - [](const std::unique_ptr& lhs, const std::unique_ptr& rhs) { - return lhs->q_id() < rhs->q_id(); - }); - } - std::vector id_to_first_window_id(targets_size + 1, 0); for (uint64_t i = 0; i < targets_size; ++i) { uint32_t k = 0; @@ -393,8 +370,12 @@ void Polisher::initialize() { id_to_first_window_id[i + 1] = id_to_first_window_id[i] + k; } + targets_coverages_.resize(targets_size, 0); + for (uint64_t i = 0; i < overlaps.size(); ++i) { + ++targets_coverages_[overlaps[i]->t_id()]; + const auto& sequence = sequences_[overlaps[i]->q_id()]; const auto& breaking_points = overlaps[i]->breaking_points(); @@ -482,9 +463,12 @@ void Polisher::polish(std::vector>& dst, static_cast(windows_[i]->rank() + 1); if (!drop_unpolished_sequences || polished_ratio > 0) { - std::string ratio_str = "_C:" + std::to_string(polished_ratio); + std::string tags = type_ == PolisherType::kF ? "r" : ""; + tags += " LN:i:" + std::to_string(polished_data.size()); + tags += " RC:i:" + std::to_string(targets_coverages_[windows_[i]->id()]); + tags += " XC:f:" + std::to_string(polished_ratio); dst.emplace_back(createSequence(sequences_[windows_[i]->id()]->name() + - ratio_str, polished_data)); + tags, polished_data)); } num_polished_windows = 0; diff --git a/src/polisher.hpp b/src/polisher.hpp index 0e526d1..0afc50b 100644 --- a/src/polisher.hpp +++ b/src/polisher.hpp @@ -77,6 +77,7 @@ class Polisher { std::vector> alignment_engines_; std::vector> sequences_; + std::vector targets_coverages_; std::string dummy_quality_; uint32_t window_length_; diff --git a/test/data/sample_ava_overlaps.mhap.gz b/test/data/sample_ava_overlaps.mhap.gz index c4d9c9c..2a29557 100644 Binary files a/test/data/sample_ava_overlaps.mhap.gz and b/test/data/sample_ava_overlaps.mhap.gz differ diff --git a/test/data/sample_ava_overlaps.paf.gz b/test/data/sample_ava_overlaps.paf.gz index 52532eb..def1fad 100644 Binary files a/test/data/sample_ava_overlaps.paf.gz and b/test/data/sample_ava_overlaps.paf.gz differ diff --git a/test/racon_test.cpp b/test/racon_test.cpp index 5921caa..935aabe 100644 --- a/test/racon_test.cpp +++ b/test/racon_test.cpp @@ -225,13 +225,13 @@ TEST_F(RaconPolishingTest, FragmentCorrectionWithQualities) { std::vector> polished_sequences; polish(polished_sequences, true); - EXPECT_EQ(polished_sequences.size(), 34); + EXPECT_EQ(polished_sequences.size(), 39); uint32_t total_length = 0; for (const auto& it: polished_sequences) { total_length += it->data().size(); } - EXPECT_EQ(total_length, 334619); + EXPECT_EQ(total_length, 389394); } TEST_F(RaconPolishingTest, FragmentCorrectionWithQualitiesFull) { @@ -249,7 +249,7 @@ TEST_F(RaconPolishingTest, FragmentCorrectionWithQualitiesFull) { for (const auto& it: polished_sequences) { total_length += it->data().size(); } - EXPECT_EQ(total_length, 1658339); + EXPECT_EQ(total_length, 1658216); } TEST_F(RaconPolishingTest, FragmentCorrectionWithoutQualitiesFull) { @@ -267,7 +267,7 @@ TEST_F(RaconPolishingTest, FragmentCorrectionWithoutQualitiesFull) { for (const auto& it: polished_sequences) { total_length += it->data().size(); } - EXPECT_EQ(total_length, 1664087); + EXPECT_EQ(total_length, 1663982); } TEST_F(RaconPolishingTest, FragmentCorrectionWithQualitiesFullMhap) { @@ -285,5 +285,5 @@ TEST_F(RaconPolishingTest, FragmentCorrectionWithQualitiesFullMhap) { for (const auto& it: polished_sequences) { total_length += it->data().size(); } - EXPECT_EQ(total_length, 1658339); + EXPECT_EQ(total_length, 1658216); } diff --git a/vendor/rampler b/vendor/rampler new file mode 160000 index 0000000..00dae65 --- /dev/null +++ b/vendor/rampler @@ -0,0 +1 @@ +Subproject commit 00dae65e6229006be7c4e3e4a8b22b3f2420eab2