From 034bb1427673eda5b611dffdba37f464e1bb2835 Mon Sep 17 00:00:00 2001 From: Martin Raden Date: Fri, 1 Mar 2019 13:35:39 +0100 Subject: [PATCH] v2.4.0 (#144) * explSeedLink corrected * typo corrected * outfile links extended * windowOverlap default value = 150 * fix for outfile docu on multi-input * bump easylogging++ version to v9.96.7 * fix docu mistakes (thanks to Sebastian Will) * * detailed output reordered * Pu now below respective ED * Pu now with ':' prefix instead of '+' (thanks to Sebastian Will) + E(hybrid) information (thanks to Sebastian Will) * explicit handling of duplicated base pairs (might happen due to explicit seeds containing only a single base pair) (thanks to Sebastian Will) * update * check viennarna 2.4.9 * vienna version extended for latest build * travis update * + lim-helix-length prediction * lim-helix-length commandline parsing * * now subclass of SeedHandler + use of index-offset-energy object * * rename '--pred' to '--model' * cleanup helix-based prediction * Started working on documnentation * Added explanation for new interaction model * general model introduction and structral context discussion * moved figure to doc/figures * fig link correct * interaction pattern examples * changed figure for helix documentation * interaction pattern * correction * API changes incorporated * * renaming helix-based predictors * log renaming of helix-based predictors * model docu extended * publications cited * docu updated * figure link corrected * labels corrected * centering cols of context table * docu typo * bugfix: log output reenabled - easylogging version bump was causing log output to be sent to /dev/null (default) if no --default-log-file specified * bugfix: IL-size check of helix-based predictors * IL-size check corrected * updateOptima extended * checks more readable * adaptation to predictor IL-size check change * bugfix: wrong assert removed * publication updated * test corrected * class docu extended * v2.4.0 --- .gitignore | 1 + ChangeLog | 95 ++ README.md | 497 +++++--- configure.ac | 2 +- doc/figures/helixbased.svg | 948 +++++++++++++++ doc/figures/icon-no.png | Bin 0 -> 1662 bytes doc/figures/icon-no.svg | 103 ++ doc/figures/icon-yes.png | Bin 0 -> 1711 bytes doc/figures/icon-yes.svg | 121 ++ doc/figures/interaction-context.svg | 752 ++++++++++++ perl/IntaRNA-fuse.pl | 3 +- src/IntaRNA/HelixConstraint.h | 269 +++++ src/IntaRNA/HelixHandler.cpp | 18 + src/IntaRNA/HelixHandler.h | 203 ++++ src/IntaRNA/HelixHandlerIdxOffset.h | 484 ++++++++ src/IntaRNA/HelixHandlerStackingOnly.cpp | 196 ++++ src/IntaRNA/HelixHandlerStackingOnly.h | 541 +++++++++ src/IntaRNA/HelixHandlerStackingOnlySeed.cpp | 239 ++++ src/IntaRNA/HelixHandlerUnpaired.cpp | 278 +++++ src/IntaRNA/HelixHandlerUnpaired.h | 638 ++++++++++ src/IntaRNA/HelixHandlerUnpairedSeed.cpp | 316 +++++ src/IntaRNA/Makefile.am | 13 + src/IntaRNA/OutputHandlerText.cpp | 7 +- src/IntaRNA/PredictorMfe2dHelixHeuristic.cpp | 425 +++++++ src/IntaRNA/PredictorMfe2dHelixHeuristic.h | 122 ++ .../PredictorMfe2dHelixHeuristicSeed.cpp | 546 +++++++++ .../PredictorMfe2dHelixHeuristicSeed.h | 163 +++ src/IntaRNA/PredictorMfe2dSeed.cpp | 2 - src/IntaRNA/SeedHandlerIdxOffset.h | 49 +- src/bin/CommandLineParsing.cpp | 155 ++- src/bin/CommandLineParsing.h | 107 +- src/bin/IntaRNA.cpp | 10 +- src/easylogging++.LICENCE.txt | 7 +- src/easylogging++.cc | 287 +++-- src/easylogging++.h | 288 +++-- tests/HelixConstraint_test.cpp | 31 + ...HelixHandlerStackingOnlyIdxOffset_test.cpp | 564 +++++++++ ...xHandlerStackingOnlySeedIdxOffset_test.cpp | 451 +++++++ tests/HelixHandlerStackingOnlySeed_test.cpp | 315 +++++ tests/HelixHandlerStackingOnly_test.cpp | 636 ++++++++++ tests/HelixHandlerUnpairedIdxOffset_test.cpp | 531 +++++++++ ...HelixHandlerUnpairedSeedIdxOffset_test.cpp | 1034 +++++++++++++++++ tests/HelixHandlerUnpairedSeed_test.cpp | 896 ++++++++++++++ tests/HelixHandlerUnpaired_test.cpp | 876 ++++++++++++++ tests/Makefile.am | 13 + .../PredictorMfe2dHelixHeuristicSeed_test.cpp | 96 ++ tests/PredictorMfe2dHelixHeuristic_test.cpp | 253 ++++ tests/SeedHandlerIdxOffset_test.cpp | 43 + tests/SeedHandlerMfe_test.cpp | 39 + 49 files changed, 13193 insertions(+), 470 deletions(-) create mode 100644 doc/figures/helixbased.svg create mode 100644 doc/figures/icon-no.png create mode 100644 doc/figures/icon-no.svg create mode 100644 doc/figures/icon-yes.png create mode 100644 doc/figures/icon-yes.svg create mode 100644 doc/figures/interaction-context.svg create mode 100644 src/IntaRNA/HelixConstraint.h create mode 100644 src/IntaRNA/HelixHandler.cpp create mode 100644 src/IntaRNA/HelixHandler.h create mode 100644 src/IntaRNA/HelixHandlerIdxOffset.h create mode 100644 src/IntaRNA/HelixHandlerStackingOnly.cpp create mode 100644 src/IntaRNA/HelixHandlerStackingOnly.h create mode 100644 src/IntaRNA/HelixHandlerStackingOnlySeed.cpp create mode 100644 src/IntaRNA/HelixHandlerUnpaired.cpp create mode 100644 src/IntaRNA/HelixHandlerUnpaired.h create mode 100644 src/IntaRNA/HelixHandlerUnpairedSeed.cpp create mode 100644 src/IntaRNA/PredictorMfe2dHelixHeuristic.cpp create mode 100644 src/IntaRNA/PredictorMfe2dHelixHeuristic.h create mode 100644 src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.cpp create mode 100644 src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.h create mode 100644 tests/HelixConstraint_test.cpp create mode 100644 tests/HelixHandlerStackingOnlyIdxOffset_test.cpp create mode 100644 tests/HelixHandlerStackingOnlySeedIdxOffset_test.cpp create mode 100644 tests/HelixHandlerStackingOnlySeed_test.cpp create mode 100644 tests/HelixHandlerStackingOnly_test.cpp create mode 100644 tests/HelixHandlerUnpairedIdxOffset_test.cpp create mode 100644 tests/HelixHandlerUnpairedSeedIdxOffset_test.cpp create mode 100644 tests/HelixHandlerUnpairedSeed_test.cpp create mode 100644 tests/HelixHandlerUnpaired_test.cpp create mode 100644 tests/PredictorMfe2dHelixHeuristicSeed_test.cpp create mode 100644 tests/PredictorMfe2dHelixHeuristic_test.cpp create mode 100644 tests/SeedHandlerIdxOffset_test.cpp create mode 100644 tests/SeedHandlerMfe_test.cpp diff --git a/.gitignore b/.gitignore index befb9c4f..8adcf9d5 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ compile /intaRNA-*.tar.gz doxygen-doc doxygen-doc/* +conf3108* # temp dir diff --git a/ChangeLog b/ChangeLog index 217ce339..288e7111 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,98 @@ +################################################################################ +# +# Change log of IntaRNA available at +# +# https://github.com/BackofenLab/IntaRNA +# +################################################################################ + +development version + +version 2.4.0 + +190228 Martin Raden : + * easylogging++ : + * kDefaultLogFileParam now part of header to be accessible + * bin/IntaRNA : + * bugfix: check whether or not --default-log-file specified was always true + such that all log output was going to the default "/dev/null" if not + specified by the user + * IntaRNA/PredictorMfe2dHelixHeuristic* : + * bugfix: check for too small interior loops was based on individual gap + length instead of sum of unpaired positions + * IntaRNA/PredictorMfe2dHelixHeuristicSeed : + * updateOptima() : + + update superclass mfe information to enable early abortion + * predict() : + + check if non-seed mfe prediction is within allowed range; otherwise abort + * IntaRNA/PredictorMfe2dSeed : + * fillHybridE_seed() : + - bugfix: wrong assert removed + * tests updated + +190224 Martin Raden : + * renaming PredictorMfe2dLimStack* to PredictorMfe2dHelix* + * README.md : + + general interaction discussion + + model parameter + + publication links + + helix-based prediction + parameters + +190223 Martin Raden / Rick Gelhausen : + + integration of helix-based RNA-RNA interaction prediction + + IntaRNA/HelixConstraint : constraints for considered inter-molecular helices + + IntaRNA/HelixHandler : general interface to enumerate inter-molecular helices + + IntaRNA/HelixHandlerIdxOffset : allows index shifting for localized access + + IntaRNA/HelixHandlerStackingOnly : enumerates only canonical helices + + IntaRNA/HelixHandlerStackingOnlySeed : canonical helices that contain a seed + + IntaRNA/HelixHandlerUnpaired : enumerate helices that can contain bulges + + IntaRNA/HelixHandlerUnpairedSeed : (bulged) helices that contain a seed + + IntaRNA/PredictorMfe2dLimStackHeuristic : helix-based interaction prediction + + IntaRNA/PredictorMfe2dLimStackHeuristicSeed : helix-based interaction + prediction that enforces the seed constraint + * IntaRNA/SeedHandlerIdxOffset : + * now subclass of SeedHandler + + energy access via InteractionEnergyIdxOffset member + * tests : + + HelixConstraint + + HelixHandlerStackingOnly + + HelixHandlerIdxOffset + HelixHandlerStackingOnly + + HelixHandlerStackingOnlySeed + + HelixHandlerIdxOffset + HelixHandlerStackingOnlySeed + + HelixHandlerUnpaired + + HelixHandlerIdxOffset + HelixHandlerUnpaired + + HelixHandlerUnpairedSeed + + HelixHandlerIdxOffset + HelixHandlerUnpairedSeed + + PredictorMfe2dLimStackHeuristic + + PredictorMfe2dLimStackHeuristicSeed + + SeedHandlerIdxOffset + + SeedHandlerMfe + * bin/CommandLineParsing : + * replace 'pred' with 'model' = defines underlying interaction model + + new model 'H' = helix-based prediction + + new 'Helix' parameter group + +190207 Martin Raden : + * IntaRNA/OutputHandlerText : + * detailed output reordered and extended + * Pu now below respective ED + * Pu now with ':' prefix instead of '+' (thanks to Sebastian Will) + + E(hybrid) information (thanks to Sebastian Will) + * NOTE: this causes changes in the detailed ASCII output! + * README.md : + * minor fixes + * IntaRNA/OutputHandlerText : + * add() : + * explicit handling of duplicated base pairs (might happen due to explicit + seeds containing only a single base pair) (thanks to Sebastian Will) + +190128 Martin Raden : + * bump easylogging++ version to v9.96.7 + +190110 Martin Raden : + * CommandLineParsing : + * windowOverlap : default changed to 150 (was 0) to avoid the need to set for + default q|tAccW values version 2.3.1 diff --git a/README.md b/README.md index 09434697..9b0b388c 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,28 @@ # IntaRNA [![releases](https://img.shields.io/github/tag/BackofenLab/IntaRNA.svg)](https://github.com/BackofenLab/IntaRNA/releases) [![Bioconda](https://anaconda.org/bioconda/intarna/badges/version.svg)](https://anaconda.org/bioconda/intarna) [![Docker Repository on Quay](https://quay.io/repository/biocontainers/intarna/status "Docker Repository on Quay")](https://quay.io/repository/biocontainers/intarna) [![Build Status](https://travis-ci.org/BackofenLab/IntaRNA.svg?branch=master)](https://travis-ci.org/BackofenLab/IntaRNA) -**Efficient RNA-RNA interaction prediction incorporating accessibility and +**Efficient RNA-RNA interaction prediction incorporating accessibility and seeding of interaction sites** -During the last few years, several new small regulatory RNAs -(sRNAs) have been discovered in bacteria. Most of them act as post-transcriptional -regulators by base pairing to a target mRNA, causing translational repression -or activation, or mRNA degradation. Numerous sRNAs have already been identified, -but the number of experimentally verified targets is considerably lower. -Consequently, computational target prediction is in great demand. Many existing -target prediction programs neglect the accessibility of target sites and the -existence of a seed, while other approaches are either specialized to certain +During the last few years, several new small regulatory RNAs +(sRNAs) have been discovered in bacteria. Most of them act as post-transcriptional +regulators by base pairing to a target mRNA, causing translational repressionex +or activation, or mRNA degradation. Numerous sRNAs have already been identified, +but the number of experimentally verified targets is considerably lower. +Consequently, computational target prediction is in great demand. Many existing +target prediction programs neglect the accessibility of target sites and the +existence of a seed, while other approaches are either specialized to certain types of RNAs or too slow for genome-wide searches. -IntaRNA, developed by [Prof. Backofen's bioinformatics group at Freiburg University](http://www.bioinf.uni-freiburg.de), -is a general and fast approach to the -prediction of RNA-RNA interactions incorporating both the accessibility of -interacting sites -as well as the existence of a user-definable seed interaction. We successfully applied -IntaRNA to the prediction of bacterial sRNA targets and determined the exact -locations of the interactions with a higher accuracy than competing programs. +is a general and fast approach to the +prediction of RNA-RNA interactions incorporating both the accessibility of +interacting sites +as well as the existence of a user-definable seed interaction. We successfully applied +IntaRNA to the prediction of bacterial sRNA targets and determined the exact +locations of the interactions with a higher accuracy than competing programs. +IntaRNA, developed by For testing or ad hoc use of IntaRNA, you can use its webinterface at the **==> [Freiburg RNA tools IntaRNA webserver](http://rna.informatik.uni-freiburg.de/IntaRNA/) <==** @@ -30,23 +30,31 @@ For testing or ad hoc use of IntaRNA, you can use its webinterface at the ## Contribution -Feel free to contribute to this project by writing -[Issues](https://github.com/BackofenLab/IntaRNA/issues) +Feel free to contribute to this project by writing +[Issues](https://github.com/BackofenLab/IntaRNA/issues) with feature requests, bug reports, or just contact messages. ## Citation -If you use IntaRNA, please cite our articles - -- [IntaRNA 2.0: enhanced and customizable prediction of RNA–RNA interactions](http://dx.doi.org/10.1093/nar/gkx279) - Martin Mann, Patrick R. Wright, and Rolf Backofen, - Nucleic Acids Research, 45 (W1), W435–W439, 2017, DOI(10.1093/nar/gkx279). -- [CopraRNA and IntaRNA: predicting small RNA targets, networks and interaction domains](http://dx.doi.org/10.1093/nar/gku359) +If you use IntaRNA, please cite our respective articles + +#### Method +- [IntaRNA 2.0: enhanced and customizable prediction of RNA-RNA interactions](https://doi.org/10.1093/nar/gkx279) + Martin Mann, Patrick R. Wright, and Rolf Backofen, + Nucleic Acids Research, 45 (W1), W435–W439, 2017, DOI:[10.1093/nar/gkx279](https://doi.org/10.1093/nar/gkx279). +- [IntaRNA: efficient prediction of bacterial sRNA targets incorporating target site accessibility and seed regions](https://doi.org/10.1093/bioinformatics/btn544) + Anke Busch, Andreas S. Richter, and Rolf Backofen, + Bioinformatics, 24 no. 24 pp. 2849-56, 2008, DOI:[10.1093/bioinformatics/btn544](https://doi.org/10.1093/bioinformatics/btn544). +#### Features and Application +- [Integration of accessibility data from structure probing into RNA–RNA interaction prediction](https://doi.org/10.1093/bioinformatics/bty1029) + Milad Miladi, Soheila Montaseri, Rolf Backofen, Martin Raden, + Bioinformatics, 2019, DOI:[10.1093/bioinformatics/bty1029](https://doi.org/10.1093/bioinformatics/bty1029). +- [Constraint maximal inter-molecular helix lengths within RNA-RNA interaction prediction improves bacterial sRNA target prediction.](http://insticc.org/node/TechnicalProgram/biostec/presentationDetails/76897) + Rick Gelhausen, Sebastian Will, Ivo L. Hofacker, Rolf Backofen, and Martin Raden, + In Proc. of the 10th International Conference on Bioinformatics Models, Methods and Algorithms. INSTICC, SciTePress, 2019 +- [CopraRNA and IntaRNA: predicting small RNA targets, networks and interaction domains](https://doi.org/10.1093/nar/gku359) Patrick R. Wright, Jens Georg, Martin Mann, Dragos A. Sorescu, Andreas S. Richter, Steffen Lott, Robert Kleinkauf, Wolfgang R. Hess, and Rolf Backofen, - Nucleic Acids Research, 42 (W1), W119-W123, 2014, DOI(10.1093/nar/gku359). -- [IntaRNA: efficient prediction of bacterial sRNA targets incorporating target site accessibility and seed regions](http://dx.doi.org/10.1093/bioinformatics/btn544) - Anke Busch, Andreas S. Richter, and Rolf Backofen, - Bioinformatics, 24 no. 24 pp. 2849-56, 2008, DOI(10.1093/bioinformatics/btn544). + Nucleic Acids Research, 42 (W1), W119-W123, 2014, DOI:[10.1093/nar/gku359](https://doi.org/10.1093/nar/gku359). @@ -69,12 +77,16 @@ The following topics are covered by this documentation: - [OS X installation with homebrew](#instosx) - [Usage and Parameters](#usage) - [Just run ...](#defaultRun) + - [Interaction Model](#interactionModel) + - [Single-site, unconstraint RNA-RNA interaction](#interactionModel-ssUnconstraintMfe) + - [Single-site, helix-based RNA-RNA interaction](#interactionModel-ssHelixMfe) - [Prediction modes](#predModes) - [Emulating other RNA-RNA interaction prediction tools](#predEmulateTools) - [Limiting memory consumption - window-based prediction](#predWindowBased) - [Interaction restrictions](#interConstr) - [Seed constraints](#seed) - [Explicit seed input](#seedExplicit) + - [Helix constraints](#helix) - [SHAPE reactivity data to enhance accessibility computation](#shape) - [Output modes](#outmodes) - [Suboptimal RNA-RNA interaction prediction and output restrictions](#subopts) @@ -92,7 +104,6 @@ The following topics are covered by this documentation: - [Library for integration in external tools](#lib) -



@@ -103,15 +114,15 @@ The following topics are covered by this documentation: ## IntaRNA via conda (bioconda channel) -The most easy way to locally install IntaRNA is via conda using the -[bioconda](https://bioconda.github.io/) +The most easy way to locally install IntaRNA is via conda using the +[bioconda](https://bioconda.github.io/) channel (linux only). This way, you will install a pre-built IntaRNA binary along with all dependencies. Follow [![install with bioconda](https://img.shields.io/badge/install%20with-bioconda-brightgreen.svg?style=flat-square)](http://bioconda.github.io/recipes/intarna/README.html) to get detailed information or run ```bash -conda install -c bioconda intarna +conda install -c conda-forge -c bioconda intarna ``` if you are using bioconda already. @@ -120,8 +131,8 @@ if you are using bioconda already. ## IntaRNA docker container (via QUAY) -An [IntaRNA docker container](https://quay.io/repository/biocontainers/intarna) -([?](https://www.docker.com/)) is provided from the bioconda package via +An [IntaRNA docker container](https://quay.io/repository/biocontainers/intarna) +([?](https://www.docker.com/)) is provided from the bioconda package via [Quay.io](https://quay.io/). This provides you with an encapsulated IntaRNA installation. @@ -135,7 +146,7 @@ If you are going to compile IntaRNA from source, ensure you meet the following dependencies: - compiler supporting C++11 standard and OpenMP -- [boost C++ library](http://www.boost.org/) version >= 1.50.0 +- [boost C++ library](http://www.boost.org/) version >= 1.50.0 (ensure the following libraries are installed for development (not just runtime libraries!); or install all e.g. in Ubuntu via package `libboost-all-dev`) - libboost_regex - libboost_program_options @@ -162,15 +173,15 @@ The data provided within the github repository (or within the `Source code` archives provided at the [IntaRNA release page](https://github.com/BackofenLab/IntaRNA/releases)) is no complete distribution and -lacks all system specifically generated files. Thus, in order to get started with -a fresh clone of the IntaRNA source code repository you have to run the GNU autotools +lacks all system specifically generated files. Thus, in order to get started with +a fresh clone of the IntaRNA source code repository you have to run the GNU autotools to generate all needed files for a proper `configure` and `make`. To this end, we provide the helper script `autotools-init.sh` that can be run as shown in the following. ```bash # call aclocal, automake, autoconf bash ./autotools-init.sh ``` -Afterwards, you can continue as if you would have downloaded an +Afterwards, you can continue as if you would have downloaded an [IntaRNA package distribution](#instsource).

@@ -178,8 +189,8 @@ Afterwards, you can continue as if you would have downloaded an ## IntaRNA package distribution (e.g. `intaRNA-2.0.0.tar.gz`) -When downloading an IntaRNA package distribution (e.g. `intaRNA-2.0.0.tar.gz`) from the -[IntaRNA release page](https://github.com/BackofenLab/IntaRNA/releases), you should +When downloading an IntaRNA package distribution (e.g. `intaRNA-2.0.0.tar.gz`) from the +[IntaRNA release page](https://github.com/BackofenLab/IntaRNA/releases), you should first ensure, that you have all [dependencies](#deps) installed. If so, you can simply run the following (assuming `bash` shell). ```bash @@ -195,7 +206,7 @@ make install make install prefix=XYZ ``` -If you installed one of the dependencies in a non-standard directory, you have +If you installed one of the dependencies in a non-standard directory, you have to use the according `configure` options: - `--with-vrna` : the prefix where the Vienna RNA package is installed - `--with-boost` : the prefix where the boost library is installed @@ -206,9 +217,9 @@ error message as follows: checking whether the Boost::System library is available... yes configure: error: Could not find a version of the library! ``` -In that case your boost libraries are most likely installed to a non-standard -directory that you have to specify either using `--with-boost` or just the -library directory via `--with-boost-libdir`. +In that case your boost libraries are most likely installed to a non-standard +directory that you have to specify either using `--with-boost` or just the +library directory via `--with-boost-libdir`.

@@ -232,18 +243,18 @@ Cygwin with the following packages: - *Perl*: - perl -and follow either [install from github](#instgithub) or +and follow either [install from github](#instgithub) or [install from package](#instsource). ### ... using pre-compiled binaries For some releases, we also provide precompiled binary packages for Microsoft Windows at the -[IntaRNA release page](https://github.com/BackofenLab/IntaRNA/releases) +[IntaRNA release page](https://github.com/BackofenLab/IntaRNA/releases) that enable 'out-of-the-box' usage. If you want to use them: - [download](https://github.com/BackofenLab/IntaRNA/releases) the according ZIP archive and extract - open a [Windows command prompt](https://www.lifewire.com/how-to-open-command-prompt-2618089) -- [run IntaRNA](#usage) +- [run IntaRNA](#usage) *Note*, these binaries come without any waranties, support or what-so-ever! They are just an offer due to according user requests. @@ -260,8 +271,8 @@ directory to your [`Path` System variable](http://www.computerhope.com/issues/ch ## OS X installation with homebrew (thanks to Lars Barquist) -If you do not want to or can use the pre-compiled binaries for OS X available from -[bioconda](https://anaconda.org/bioconda/intarna), you can compile `IntaRNA` +If you do not want to or can use the pre-compiled binaries for OS X available from +[bioconda](https://anaconda.org/bioconda/intarna), you can compile `IntaRNA` locally. The following wraps up how to build `IntaRNA-2.0.2` under OS X (Sierra 10.12.4) using homebrew. @@ -272,18 +283,18 @@ First, install homebrew! :) brew install gcc --without-multilib ``` -`--without-multilib` is necessary for OpenMP multithreading support -- note -OS X default `gcc`/`clang` doesn't support OpenMP, so we need to install standard +`--without-multilib` is necessary for OpenMP multithreading support -- note +OS X default `gcc`/`clang` doesn't support OpenMP, so we need to install standard `gcc`/`g++` ```[bash] brew install boost --cc=gcc-6 ``` -`--cc=gcc-6` is necessary to build `boost` with standard `gcc`, rather than the -default bottle which appears to have been built with the system `clang`. -Brew installs `gcc`/`g++` as `/usr/local/bin/gcc-VERSION` by default to avoid -clashing with the system's `gcc`/`clang`. `6` is the current version as of +`--cc=gcc-6` is necessary to build `boost` with standard `gcc`, rather than the +default bottle which appears to have been built with the system `clang`. +Brew installs `gcc`/`g++` as `/usr/local/bin/gcc-VERSION` by default to avoid +clashing with the system's `gcc`/`clang`. `6` is the current version as of writing, but may change. ```[bash] @@ -297,8 +308,8 @@ Download and extract the IntaRNA source code package (e.g. `intaRNA-2.0.2.tar.gz ./configure CC=gcc-6 CXX=g++-6 ``` -This sets up makefiles to use standard `gcc`/`g++` from brew, which will -need an update to the appropriate compiler version if not still `6`. +This sets up makefiles to use standard `gcc`/`g++` from brew, which will +need an update to the appropriate compiler version if not still `6`. You might also want to set `--prefix=INSTALLPATH` if you dont want to install IntaRNA globally. @@ -319,19 +330,19 @@ make install IntaRNA comes with a vast variety of ways to tune or enhance *YOUR* RNA-RNA prediction. To this end, different [prediction modes](#predModes) are implemented that allow -to balance predication quality and runtime requirement. Furthermore, it is -possible to define +to balance predication quality and runtime requirement. Furthermore, it is +possible to define [interaction restrictions](#interConstr), -[seed constraints](#seed), -[explicit seed information](#seedExplicit), -[SHAPE reactivity constraints](#shape), +[seed constraints](#seed), +[explicit seed information](#seedExplicit), +[SHAPE reactivity constraints](#shape), [output modes](#outmodes), -[suboptimal enumeration](#subopts), +[suboptimal enumeration](#subopts), [energy parameters, temperature](#energy), and the [accessibility](#accessibility) handling. If you are doing high-throughput computations, you might also want to consider [multi-threading support](#multithreading). -For ad hoc usage you can use the +For ad hoc usage you can use the [Freiburg RNA tools IntaRNA webserver](http://rna.informatik.uni-freiburg.de/IntaRNA/) (with limited parameterization). @@ -342,10 +353,10 @@ For ad hoc usage you can use the ## Just run ... -If you just want to start and are fine with the default parameters set, -you only have to provide two RNA sequences, +If you just want to start and are fine with the default parameters set, +you only have to provide two RNA sequences, a (long) target RNA (using `-t` or `--target`) and a (short) query RNA -(via `-q` or `--query`), in +(via `-q` or `--query`), in [IUPAC RNA encoding](#https://en.wikipedia.org/wiki/Nucleic_acid_notation). You can either directly input the sequences ```bash @@ -371,7 +382,7 @@ interaction energy = -10.7116 kcal/mol or provide (multiple) sequence(s) in [FASTA-format](#https://en.wikipedia.org/wiki/FASTA_format). It is possible to provide either file input or to read the FASTA input from the STDIN stream. - + ```bash # running IntaRNA with FASTA files IntaRNA -t myTargets.fasta -q myQueries.fasta @@ -379,24 +390,129 @@ IntaRNA -t myTargets.fasta -q myQueries.fasta cat myQueries.fasta | IntaRNA -q STDIN -t myTargets.fasta ``` -If you are working with large FASTA input files, e.g. covering a whole -transcriptome, you can restrict the prediction to a subset of the input +If you are working with large FASTA input files, e.g. covering a whole +transcriptome, you can restrict the prediction to a subset of the input sequences using the `--qSet` or `--tSet` parameter as shown in the following. ```bash -# restrict prediction to the second load of 100 target sequences +# restrict prediction to the second load of 100 target sequences IntaRNA -t myTranscriptome.fasta --tSet=101-200 -q myQuery.fasta ``` Nucleotide encodings different from `ACGUT` are rewritten as `N` and the respective -positions are not considered to form base pairs (and this ignored). -Thymine `T` encodings are replaced by uracil `U`, since a `ACGU`-only -energy model is used. +positions are not considered to form base pairs (and thus ignored). +Thymine `T` encodings are replaced by uracil `U`, since we apply an RNA-only +energy model. For a list of general program argument run `-h` or `--help`. For a complete list covering also more sophisticated options, run `--fullhelp`. +

+
+ +## RNA-RNA interaction models + +IntaRNA supports various models how RNA-RNA interactions are represented. +The model selection has direct consequences for the interaction patterns that +can be predicted by IntaRNA. +Before elaborating the supported models, first terms needed for understanding +and representation: + +We denote with a **single site** an interaction pattern of two respective RNA +subsequences Qi..Qk and Tj..Tl that + +- form a *base pair on each end*, i.e. (Qi,Tl) and (Qk,Tj) are pairing, and +- there are *no intra-molecular base pairs* within the two subsequences, i.e. +the subsequences form only inter-molecular base pairs. + +Given that we can classify single-site RNA-RNA interactions based on the +structural context of the respective subsequences, which are + +- *exterior* - not enclosed by any base pair +- *hairpin loop* - directly enclosed by a base pair +- *non-hairpin loop* - subsequence enclosed by two loops forming a bulge, interior or multi-loop + +The following figure shows an RNA structure depiction with context annotations +(abbreviated by resp. first letter) of unpaired regions that can form RNA-RNA interactions. + +![depiction of RNA-RNA interaction pattern](doc/figures/interaction-context.svg) + +IntaRNA can predict single-site interactions within any structural context of the respective subsequences. + +| context | exterior | hairpin loop | non-hairpin loop | +| :-------------: | :------: | :----------: | :--------------: | +| **exterior** | ![yes](doc/figures/icon-yes.png) | ![yes](doc/figures/icon-yes.png) | ![yes](doc/figures/icon-yes.png) | +| **hairpin** | ![yes](doc/figures/icon-yes.png) | ![yes](doc/figures/icon-yes.png) | ![yes](doc/figures/icon-yes.png) | +| **non-hairpin loop** | ![yes](doc/figures/icon-yes.png) | ![yes](doc/figures/icon-yes.png) | ![yes](doc/figures/icon-yes.png) | + +Note, *concatenation-based approaches* as implemented in UNAfold or RNAcofold can +*only predict exterior-exterior context* combinations (shown by (b) in the figure below) +and are thus not capable +to investigate e.g. common loop-exterior or kissing-hairpin-loop +interaction patterns that are depicted by (c) and (d) in the figure from above, respectively! + +A detailed discussion about different prediction approaches and predictable +interaction pattern is available in our publications + +- [Interactive implementations of thermodynamics-based RNA structure and RNA-RNA interaction prediction approaches for example-driven teaching.](https://doi.org/10.1371/journal.pcbi.1006341) and +- [Structure and interaction prediction in prokaryotic RNA biology.](https://doi.org/10.1128/microbiolspec.RWR-0001-2017) + + + +

+
+ +### Unconstraint single-site RNA-RNA interaction with minimal free energy + +This *default model* of IntaRNA predicts the single-site interaction `I` with +minimal free energy. That is, it minimizes +``` + arg min ( E_hybrid(I) + ED1(I) + ED2(I) ) + I +``` +where `E_hybrid` represents all energy terms of intermolecular base pairs and +`ED` corresponds to the energy needed to make the respective subsequences +accessible for inter-molecular base pairing, i.e. removing any possible intra-molecular +base pairs. + +The model considers inter-molecular base pair patterns that correspond to +(helical) stackings, bulges or interior loops, which are depicted in figure (b) from above. +Since intra-molecular base pairs are not explicitely represented, any structural +context of single-site interactions is considered/possible within IntaRNA +predictions. + + + +

+
+ +### Helix-based single-site RNA-RNA interaction with minimal free energy + +The formation of multiple base pair stackings, i.e. helix formation, requires +a 'winding' of the respective subsequences. +Depending on the structural context, such winding might be sterically and kinetically +hindered by the necessary unwinding of intra-molecular structural elements. + +This model aims to incorporate such effects into the predictions of IntaRNA. +This is done by restricting the maximum length of inter-molecular helices to a +specified number of (stacked) base pairs. +That way, 'wound up' subhelices are interspaced by flexible interior loops that +will allow for a more flexible 3D arrangement of the overall helix. + +The following figure depicts the effect of the maximum helix length constraints +that only allows for helices up to a specified length. +That way, long interactions (left) are avoided and replaced by a more flexible +model composed of short inter-molecular helices (right). +The blue boxes represent the lenth-bound helices and while the red boxes depict +the interspacing unpaired regions (interior loops). + +![helixbased](./doc/figures/helixbased.svg) + +For further details, please refer to our respective publication + +- [Constraint maximal inter-molecular helix lengths within RNA-RNA interaction prediction improves bacterial sRNA target prediction.](http://www.bioinf.uni-freiburg.de/Subpages/publications.html?de#Gelhausen-helixLength-2019.abstract) + @@ -425,7 +541,7 @@ of equal length *n*. Note, due to the low run-time requirement of the heuristic prediction mode (`--mode=H`), heuristic IntaRNA interaction predictions are widely used to screen for interaction in a genome-wide scale. If you are more interested in specific -details of an interaction site or of two relatively short RNA molecules, you +details of an interaction site or of two relatively short RNA molecules, you should investigate the exact prediction mode (`--mode=M`, or `--mode=E` if non-overlapping suboptimal prediction is required). Note further, the exact mode `E` should provide the same results as mode `M` but uses dramatically more @@ -440,22 +556,22 @@ memory for computations. Given these features, we can emulate and extend a couple of RNA-RNA interaction tools using IntaRNA. -**TargetScan** and **RNAhybrid** are approaches that predict the interaction hybrid with -minimal interaction energy without consideration whether or not the interacting +**TargetScan** and **RNAhybrid** are approaches that predict the interaction hybrid with +minimal interaction energy without consideration whether or not the interacting subsequences are probably involved involved in intramolecular base pairings. Furthermore, no seed constraint is taken into account. -This prediction result can be emulated (depending on the used prediction mode) +This prediction result can be emulated (depending on the used prediction mode) by running IntaRNA when disabling both the seed constraint as well as the accessibility integration using ```bash # prediction results similar to TargetScan/RNAhybrid IntaRNA [..] --noSeed --qAcc=N --tAcc=N ``` -We *add seed-constraint support to TargetScan/RNAhybrid-like computations* by removing the +We *add seed-constraint support to TargetScan/RNAhybrid-like computations* by removing the `--noSeed` flag from the above call. -**RNAup** was one of the first RNA-RNA interaction prediction approaches that took the -accessibility of the interacting subsequences into account while not considering the seed feature. +**RNAup** was one of the first RNA-RNA interaction prediction approaches that took the +accessibility of the interacting subsequences into account while not considering the seed feature. IntaRNA's exact prediction mode is eventually an alternative implementation when disabling seed constraint incorporation. Furthermore, the unpaired probabilities used by RNAup to score the accessibility of subregions are covering the respective overall structural ensemble for each @@ -465,7 +581,7 @@ using # prediction results similar to RNAup IntaRNA --mode=M --noSeed --qAccW=0 --qAccL=0 --tAccW=0 --tAccL=0 ``` -We *add seed-constraint support to RNAup-like computations* by removing the +We *add seed-constraint support to RNAup-like computations* by removing the `--noSeed` flag from the above call. @@ -475,11 +591,11 @@ We *add seed-constraint support to RNAup-like computations* by removing the ### Limiting memory consumption - window-based prediction The memory requirement of IntaRNA grows quadratically with lengths of the input -sequences. Thus, for very long input RNAs, the requested memory can exceed the +sequences. Thus, for very long input RNAs, the requested memory can exceed the available RAM of smaller computers. This can be circumvented by using a window-based prediction where the input -sequences are decomposed in overlapping subsequences (windows) that are +sequences are decomposed in overlapping subsequences (windows) that are processed individually. That way, the maximal memory consumption is defined by the (shorter) window length rather the length of the input sequence, resulting in a user guided memory/RAM consumption. @@ -487,13 +603,13 @@ in a user guided memory/RAM consumption. The window-based computation is enabled by setting the following parameters - `--windowWidth` : length of the windows/subsequences (value of 0 disables window-based computations) -- `--windowOverlap` : overlap of the windows that has to be cover the maximal interaction length (see [`--q|tIntLenMax`](#interConstr)) +- `--windowOverlap` : overlap of the windows, which has to be larger than the maximal interaction length (see [`--q|tIntLenMax`](#interConstr)) -Note, window-based computation produces a computational overhead due to +Note, window-based computation produces a computational overhead due to redundant consideration of the overlapping subsequences. Thus, the runtime is increased proportionally to the ratio of window overlap and length. -If only one query and target are given, window-based computation can be +If only one query and target are given, window-based computation can be [parallelized](#multithreading), which typically remedies the computational overhead. @@ -504,28 +620,28 @@ If only one query and target are given, window-based computation can be ## Interaction restrictions The predicted RNA-RNA interactions can be enhanced if additional -knowledge is available. To this end, IntaRNA provides different options to +knowledge is available. To this end, IntaRNA provides different options to restrict predicted interactions. -A general most general restriction is the maximal energy (inversely related to +A general most general restriction is the maximal energy (inversely related to stability) an RNA-RNA interaction is allowed to have. Per default, a reported interaction should have a negative energy (<0) to be energetically favorable. This report barrier can be altered using `--outMaxE`. For suboptimal interaction restriction, please refer to [suboptimal interaction prediction](#subopts) section. -If you are only interested in predictions for highly accessible regions, i.e. +If you are only interested in predictions for highly accessible regions, i.e. with a high probability to be unpaired, you can use the `--outMinPu` parameter. If given, each individual position of the interacting subsequences has to have an unpaired probability reaching at least the given value. This significantly increases prediction time but will exclude predictions where the formation of -the interaction (intermolecular base pairing) replaces intramolecular base -pairing (where the latter will cause low unpaired probabilities for the +the interaction (intermolecular base pairing) replaces intramolecular base +pairing (where the latter will cause low unpaired probabilities for the respective positions). Furthermore, the region where interactions are supposed to occur can be restricted -for target and query independently. To this end, a list of according +for target and query independently. To this end, a list of according subregion-defining index pairs -can be provided using `--qRegion` and `--tRegion`, respectively. The indexing +can be provided using `--qRegion` and `--tRegion`, respectively. The indexing starts with 1 and should be in the format `from1-end1,from2-end2,..` using integers. Note, if you want to have predictions individually for each region combination (rather than just the best for each query-target combination) you @@ -534,13 +650,13 @@ want to add `--outPerRegion` to the call. If you are dealing with very long sequences it might be useful to use the *automatic identification of accessible regions*, which dramatically reduces runtime and memory consumption of IntaRNA since predictions are only done for -individual regions and not for the whole sequence. Here, we use a +individual regions and not for the whole sequence. Here, we use a heuristic approach that finds and ignores subregions that are unlikely to form an interaction, resulting in a decomposition of the full sequence range into -intervals of accessible regions. It can be enabled by providing the maximal +intervals of accessible regions. It can be enabled by providing the maximal length of the resulting intervals via the parameters `--qRegionLenMax` and `--tRegionLenMax`.
-More specifically, starting from the full +More specifically, starting from the full sequence's index range, the algorithm iteratively identifies in every too-long range the window with highest ED value (penalty for non-accessibility). To this end, it uses windows of length `--seedBP` to find subsequences where it is @@ -572,15 +688,15 @@ first formed parts of the full RNA-RNA interaction, and thus considered as the Based on this observation, RNA-RNA interaction predictors were enhanced by incorporating such seed constraints into their prediction pipeline, i.e. a -reported interaction has to feature at least one seed. Typically, -a seed is defined as a short subinteraction of 5-8 consecutive base pairs that +reported interaction has to feature at least one seed. Typically, +a seed is defined as a short subinteraction of 5-8 consecutive base pairs that are not enclosing any unpaired nucleotides (or if so only very few). IntaRNA supports the definition of such seed constraints and adds further -options to even more constrain the seed selection. The list of options is given -by +options to even more constrain the seed selection. The list of options is given +by -- `--seedBP` : the number of base pairs the seed has to show +- `--seedBP` : the number of base pairs within the seed - `--seedMaxUP` : the maximal overall number of unpaired bases within the seed - `--seedQMaxUP` : the maximal number of unpaired bases within the query's seed region - `--seedTMaxUP` : the maximal number of unpaired bases within the target's seed region @@ -589,15 +705,14 @@ by - `--seedQRange` : a list of index intervals where a seed in the query is allowed - `--seedTRange` : a list of index intervals where a seed in the target is allowed -Alternatively, you can set +Alternatively, you can set -- `--seedTQ` : to specify [explicit seed interactions](seedExplicit) +- `--seedTQ` : to specify [explicit seed interactions](#seedExplicit) Seed constraint usage can be globally disabled using the `--noSeed` flag. -

@@ -607,12 +722,12 @@ Some experiments provide hints or explicit knowledge about the seed or even provide details about some intermolecular base pairs formed between two RNAs. This information can be incorporated into IntaRNA predictions by providing *explicit seed information*. To this end, the `--seedTQ` parameter can be used. -It takes a comma-separated list of seed string encodings in the format +It takes a comma-separated list of seed string encodings in the format `startTbpsT&startQbpsQ`, which is in the same format as the IntaRNA `hybridDB` -output (see [below](#outModeCsv)), i.e. e.g. `--seedTQ='4|||.|&7||.||'` -(ensure you quote the seed encoding to avoid a shell interpretation of the pipe symbol '|') -to encode a seed interaction like -the following +output (see [below](#outModeCsv)), i.e. e.g. `--seedTQ='4|||.|&7||.||'` +(ensure you quote the seed encoding to avoid a shell interpretation of the pipe symbol '|') +to encode a seed interaction like +the following ```bash target 4 8 @@ -626,10 +741,15 @@ target 11 7 query ``` -If several or alternative seeds are known, you can provide all as a +If several or alternative seeds are known, you can provide all as a comma-separated list and IntaRNA will consider all interactions that cover at least one of them. +

+
+ +## Helix constraints +

@@ -640,20 +760,24 @@ least one of them. For some RNA sequences, experimental reactivity data is available that can be used to guide/help the structure and thus accessibility prediction for the RNA molecule. IntaRNA supports such data by interfacing the Vienna RNA package -capabilities for SHAPE reactivity data incorporation, see +capabilities for SHAPE reactivity data incorporation, see Lorenz et al. ([2015](https://doi.org/10.1093/bioinformatics/btv523), [2016](https://dx.doi.org/10.1186%2Fs13015-016-0070-z)) or the [RNAfold manpage](https://www.tbi.univie.ac.at/RNA/RNAfold.1.html). The SHAPE reactivity data can be provided via file using `--qShape` or -`--tShape` for query or target sequence, respectively. +`--tShape` for query or target sequence, respectively. Independently for each, it is possible to define the methods to be used to convert the data into pseudo energies and pairing probabilities. The respective IntaRNA arguments are `--qShapeMethod`|`--tShapeMethod` -and `--qShapeConversion`|`--tShapeConversion`, which mimics the according -tool arguments in the Vienna RNA package (see e.g. the +and `--qShapeConversion`|`--tShapeConversion`, which mimics the according +tool arguments in the Vienna RNA package (see e.g. the [RNAfold manpage](https://www.tbi.univie.ac.at/RNA/RNAfold.1.html)). +For further details, please refer to our respective publication + +- [Integration of accessibility data from structure probing into RNA-RNA interaction prediction.](https://doi.org/10.1093/bioinformatics/bty1029) + @@ -666,7 +790,7 @@ The RNA-RNA interactions predicted by IntaRNA can be provided in different formats. The style is set via the argument `--outMode` and the different modes will be discussed below. -Furthermore, it is possible to define *where to output*, i.e. using `--out` +Furthermore, it is possible to define *where to output*, i.e. using `--out` you can either name a file or one of the stream names `STDOUT`|`STDERR`. Note, any string not matching one of the two stream names is considered a file name. The file will be overwritten by IntaRNA! @@ -674,8 +798,8 @@ The file will be overwritten by IntaRNA! Besides interaction output, you can set the verbosity of computation information using the `-v` or `--verbose` arguments. To reduce the output to a minimum, you can redirect all logging output of user information, warnings or verbose output -to a specific file using `--default-log-file=LOGFILENAME`. -If you are not interested in any logging output, redirect it to nirvana via +to a specific file using `--default-log-file=LOGFILENAME`. +If you are not interested in any logging output, redirect it to nirvana via `--default-log-file=/dev/null`. Note, error output is not redirected and always given on standard output streams. @@ -684,8 +808,8 @@ given on standard output streams. ### Standard RNA-RNA interaction output with ASCII chart -The standard output mode `--outMode=D` provides a detailed ASCII chart of the -interaction together with its overall interaction energy. +The standard output mode `--outMode=D` provides a detailed ASCII chart of the +interaction together with its overall interaction energy. For an example see the [Just run ...](#defaultRun) section. @@ -734,7 +858,7 @@ seed ED2 = 2.66437 kcal/mol seed Pu1 = 0.0132596 seed Pu2 = 0.0132596 ``` -Position annotations start indexing with 1 at the 5'-end of each RNA. +Position annotations start indexing with 1 at the 5'-end of each RNA. `ED` values are the energy penalties for reduced [accessibility](#accessibility) and `Pu` denote unpaired probabilities of the respective interacting subsequences. @@ -742,7 +866,7 @@ and `Pu` denote unpaired probabilities of the respective interacting subsequence ### Customizable CSV RNA-RNA interaction output -IntaRNA provides via `--outMode=C` a flexible interface to generate RNA-RNA +IntaRNA provides via `--outMode=C` a flexible interface to generate RNA-RNA interaction output in CSV format (using `;` as separator). Note, target sequence information is listed with index `1` while query sequence information is given by index `2`. @@ -756,8 +880,8 @@ target;1;14;query;4;18;AAACACCCCCGGUG&CACCCCCGGUGGUUU;(((((((...((((&))))...)))) ``` For each prediction, a row in the CSV is generated. -Using the argument `--outCsvCols`, the user can specify what columns are -printed to the output using a comma-separated list of colIds. Available colIds +Using the argument `--outCsvCols`, the user can specify what columns are +printed to the output using a comma-separated list of colIds. Available colIds are - `id1` : id of first sequence (target) @@ -776,7 +900,7 @@ are - `hybridDPfull` : hybrid in VRNA dot-bracket notation (full sequence length) - `hybridDB` : hybrid in dot-bar notation (interactin sites only) - `hybridDBfull` : hybrid in dot-bar notation (full sequence length) -- `E` : overall hybridization energy +- `E` : overall interaction energy - `ED1` : ED value of seq1 - `ED2` : ED value of seq2 - `Pu1` : probability to be accessible for seq1 @@ -806,16 +930,16 @@ Energies are provided in unit *kcal/mol*, probabilities in the interval [0,1]. Position annotations start indexing with 1. The `hybridDP` format is a dot-bracket notation as e.g. generated by **RNAup**. -Here, for each target sequence position within the interaction, +Here, for each target sequence position within the interaction, a '.' represents a position not involved in the interaction while a '(' marks an interacting position. For the query sequence this is done analogously but using a ')' for interacting positions. Both resulting strings are concatenated by a separator '&' to yield a single string encoding of the interaction's base pairing details. -The `hybridDB` format is similar to the `hybridDP` but also provides site information. -Here, a bar '|' is used in both base pairing encodings (which makes it a 'dot-bar encoding'). -Furthermore, each interaction string is prefixed +The `hybridDB` format is similar to the `hybridDP` but also provides site information. +Here, a bar '|' is used in both base pairing encodings (which makes it a 'dot-bar encoding'). +Furthermore, each interaction string is prefixed with the start position of the respective interaction site. In the following, an altered CSV output for the example from above is generated. @@ -845,7 +969,7 @@ Note, for for IntaRNA v1.* output, currently *no multi-threading computation* is ## Suboptimal RNA-RNA interaction prediction and output restrictions -Besides the identification of the optimal (e.g. minimum-free-energy) RNA-RNA +Besides the identification of the optimal (e.g. minimum-free-energy) RNA-RNA interaction, IntaRNA enables the enumeration of suboptimal interactions. To this end, the argument `-n N` or `--outNumber=N` can be used to generate up to `N` interactions for each query-target pair (including the optimal one). Note, the @@ -866,8 +990,8 @@ Furthermore, it is possible to *restrict (sub)optimal enumeration* using is allowed (Note, IntaRNA v1.* used implicitly the 'T' mode): - 'N' : no overlap neither in target nor query allowed for reported interactions - 'B' : overlap allowed for interacting subsequences for both target and query - - 'T' : overlap allowed for interacting subsequences in target only - - 'Q' : overlap allowed for interacting subsequences in query only + - 'T' : overlap allowed for interacting subsequences in target only + - 'Q' : overlap allowed for interacting subsequences in query only @@ -879,10 +1003,10 @@ Furthermore, it is possible to *restrict (sub)optimal enumeration* using ## Energy parameters and temperatures The selection of the correct temperature and energy parameters is cruicial for -a correct RNA-RNA interaction prediction. To this end, various settings are +a correct RNA-RNA interaction prediction. To this end, various settings are supported by IntaRNA. -The temperature can be set via `--temperature=C`to set a temperature `C` in +The temperature can be set via `--temperature=C`to set a temperature `C` in degree Celsius. Note, this is important especially for predictions within plants etc., since the default temperature is 37°C. @@ -890,10 +1014,10 @@ The energy model used can be specified using the `--energy` parameters using - 'B' for base pair maximization similar to the Nussinov intramolecular structure prediction. Here, each base pair contributes an energy term of `-1` independently of its - structural or sequence context. This mode is mainly useful for study or teaching + structural or sequence context. This mode is mainly useful for study or teaching purpose. - 'V' enables *Nearest Neighbor Model* energy computation similar to the Zuker - intramolecular structure prediction using the Vienna RNA package routines. + intramolecular structure prediction using the Vienna RNA package routines. Within this model, the energy contribution of a base pair depends on its directly enclosed (neighbored) basepair and the subsequence(s) involved. Different energy parameter sets have been experimentally derived @@ -906,7 +1030,7 @@ The energy model used can be specified using the `--energy` parameters using If Vienna RNA package is used for energy computation (`--energy=V`), per default the default parameter set of the linked Vienna RNA package is used (e.g. the set `Turner04` for VRNA 2.3.0). If you want to use a different parameter set, you -can provide an according parameter file via `--energVRNA=MyParamFile`. The +can provide an according parameter file via `--energVRNA=MyParamFile`. The following example exemplifies the use of the old `Turner99` parameter set as used by IntaRNA v1.*. ```bash @@ -936,17 +1060,16 @@ definition of the `--out` argument in combination with one of the following argument prefixes (case insensitive) that have to be colon-separated to the targeted file/stream name: -- `qMinE:`|`tMinE:` the query/target's minimal interaction energy profile (CSV format), respectively -- `pMinE:` the minimal interaction energy for all pairs of query-target index pairs (CSV format) +- `qSpotProb:`|`tSpotProb:` [query/target's spot probability profile](#profileSpotProb) (CSV format), respectively +- `spotProb:` [all spot probabilities](#spotProb) (CSV format) +- `qMinE:`|`tMinE:` [the query/target's minimal interaction energy profile](profileMinE) (CSV format), respectively +- `pMinE:` [minimal interaction energy for all query-target index pairs](pairMinE) (CSV format) - `qAcc:`|`tAcc:` the [query/target's ED accessibility values](#accessibility) (RNAplfold-like format), respectively - `qPu:`|`tPu:` the [query/target's unpaired probabilities](#accessibility) (RNAplfold format), respectively Note, for *multiple sequences* in FASTA input, the provided file names -are suffixed with with `-s` and the according sequence's number (where indexing -starts with 1) within the FASTA input to generate an individual file for each sequence. -For `pMinE` output, if multiple query-target combinations are to be reported, the -file name is suffixed with `-q#t#` (where `#` denotes the according sequence number -within the input. +are suffixed with with `-q#t#` (where `#` denotes the according sequence number +within the input where indexing starts with 1).
@@ -988,7 +1111,7 @@ holding for each index pair the minimal energy of any interaction covering this index combination or `NA` if no covers it at all. This information can be visualized with your preferred program. In the following, -the provided R call is used to generate a heatmap visualization of the +the provided R call is used to generate a heatmap visualization of the RNA-RNA interaction possibilities. ```R @@ -1004,7 +1127,7 @@ box(); The following plot (for the [minimal energy profile](#profileMinE) example from above) reveals, that the alternative stable (*E*<0) interactions all involve the -mfe-site in the second sequence and are thus less likely to occure. +mfe-site in the second sequence and are thus less likely to occure. ![Minimal interaction energy index pair information](/doc/figures/pair-minE.png?raw=true "Minimal interaction energy index pair information") @@ -1019,13 +1142,13 @@ Similarly to [minimal energy profiles](#profileMinE), it is also possible to compute position-wise probabilities how likely a position is covered by an interaction, i.e. its *spot probability*. To the end, we compute for each position *i* the partition function *Zi* of all interactions covering *i*. -Given the overall partition function *Z* including all possible interactions, +Given the overall partition function *Z* including all possible interactions, the position-speficit spot probability for *i* is given by *Zi/Z*. -Such profiles can be generated using `--out=qSpotProb:MYPROFILEFILE.csv` or +Such profiles can be generated using `--out=qSpotProb:MYPROFILEFILE.csv` or `--out=tSpotProb:...` for the query/target sequence respectively and independently. -Note, instead of a file you can also write the profile to stream using `STDOUT` +Note, instead of a file you can also write the profile to stream using `STDOUT` or `STDERR` instead of a file name. @@ -1038,13 +1161,13 @@ or `STDERR` instead of a file name. For some research questions, putative regions of interactions are known from other sources and it is of interest to study the effect of competitive binding or other scenarios that might influence the accessibility of the interacting -RNAs (e.g. refer to [SHAPE data](#shape) or +RNAs (e.g. refer to [SHAPE data](#shape) or [structure/accessibility constraints](#accConstraints)). To this end, one can specify the spots of interest by intermolecular index pairs, e.g. using `5&67` to encode the fifth target RNA position (first number of the encoding) and the 67th query RNA -position (second number of the encoding). Note, indexing starts with 1. +position (second number of the encoding). Note, indexing starts with 1. Multiple spots can be provided as comma-separated list. The list in concert with an output stream/file name (colon-separated) can be passed via the `--out` argument using the `spotProb:` prefix, e.g. @@ -1054,20 +1177,20 @@ IntaRNA ... --out=spotProb:5&67,33&12:mySpotProbFile.csv ``` The reported probability is the ratio of according partition functions. That is, -for each interaction `I` that respects all input constraints and has an energy -below 0 (or set `--outMaxE` value) the respective Boltzmann weight `bw(I)` -is computed by `bw(I) = exp( - E(I) / RT )`. This weight is added to the +for each interaction `I` that respects all input constraints and has an energy +below 0 (or set `--outMaxE` value) the respective Boltzmann weight `bw(I)` +is computed by `bw(I) = exp( - E(I) / RT )`. This weight is added to the `overallZ` partition function. Furthermore, we add the weight to a respective spot associated partition function `spotZ`, if the interaction `I` spans the spot, ie. the spot's indices are within the interaction subsequences of `I`. If none of the spots if spanned by `I`, the `noSpotZ` partition function is increased by `bw(I)`. The final probability of a spot is than given by `spotZ/overallZ` and -the probability of interactions not covering any of the tracked spots is +the probability of interactions not covering any of the tracked spots is computed by `noSpotZ/overallZ` and reported for the pseudo-spot encoding `0&0` (since indexing starts with 1). *NOTE* and be aware that the probabilities are *only estimates* since IntaRNA -is not considering (in default prediction mode) all possible interactions due +is not considering (in default prediction mode) all possible interactions due to its heuristic (see [discussion about suboptimal interactions](#subopts)). Nevertheless, since the Boltzmann probabilities are dominated by the low(est) energy interactions, we consider the probability estimates as meaningful! @@ -1119,16 +1242,16 @@ base pairing. It can be expressed in terms of the probability of the subsequence to be unpaired (its *unpaired probability* *Pu*). A limited accessibility, i.e. a low unpaired probability, can be incorporated into -the RNA-RNA interaction prediction by adding according energy penalties. +the RNA-RNA interaction prediction by adding according energy penalties. These so called *ED* values are transformed unpaired probabilities, i.e. the -penalty for a subsequence partaking in an interaction is given by *ED=-RT log(Pu)*, -where *Pu* denotes the unpaired probability of the subsequence. Within the +penalty for a subsequence partaking in an interaction is given by *ED=-RT log(Pu)*, +where *Pu* denotes the unpaired probability of the subsequence. Within the IntaRNA energy model, *ED* values for both interacting subsequences are considered. Accessibility incorporation can be disabled for query or target sequences using `--qAcc=N` or `--tAcc=N`, respectively. -A setup of `--qAcc=C` or `--tAcc=C` (default) enables accessibility computation +A setup of `--qAcc=C` or `--tAcc=C` (default) enables accessibility computation using the Vienna RNA package routines for query or target sequences, respectively. @@ -1144,7 +1267,7 @@ Since global probability computation is (a) computationally demanding and (b) no reasonable for long sequences, local RNA folding was suggested, which also enables according *local unpaired probability* computation, as e.g. done by **RNAplfold**. Here, a folding window of a defined length 'screens' along the RNA and computes -unpaired probabilities within the window (while only intramolecular base pairs +unpaired probabilities within the window (while only intramolecular base pairs within the window are considered). IntaRNA enables both global as well as local unpaired probability computation. @@ -1153,7 +1276,7 @@ local folding. ##### Use case examples global/local unpaired probability computation -The use of global or local accessibilities can be defined independently +The use of global or local accessibilities can be defined independently for query and target sequences using `--qAccW|L` and `--tAccW|L`, respectively. Here, `--?AccW` defines the sliding window length (0 sets it to the whole sequence length) and `--?AccL` defines the maximal length of considered intramolecular base pairs, @@ -1174,13 +1297,13 @@ IntaRNA [..] --qAccW=0 --qAccL=0 --tAccW=150 --qAccL=100 For some RNAs additional accessibility information is available. For instance, it might be known from experiments that some subsequence is unpaired or already -bound by some other factor. The first case (unpaired) makes such regions +bound by some other factor. The first case (unpaired) makes such regions especially interesting for interaction prediction and should result in no ED -penalties for these regions. In the second case (blocked) the region should be +penalties for these regions. In the second case (blocked) the region should be excluded from interaction prediction. To incorporate such information, IntaRNA provides the possibility to constrain -the accessibility computation using the `--qAccConstr` and `--tAccConstr` +the accessibility computation using the `--qAccConstr` and `--tAccConstr` parameters. Both take a string encoding for each sequence position whether it is - `.` unconstrained @@ -1199,8 +1322,8 @@ IntaRNA [..] --query="GGGGGGGCCCCCCC" \ It is also possible to provide a more compact index-range-based encoding of the constraints, which is especially useful for longer sequences or if you have only -a few constrained regions. To this end, one can provide a comma-separated list -of index ranges that are prefixed with the according constraint letter from +a few constrained regions. To this end, one can provide a comma-separated list +of index ranges that are prefixed with the according constraint letter from above and a colon. Best check the following examples, which should give a good idea how to use. Note, indexing is supposed to be based on a minimal index of 1 and all positions not covered by the encoding are assumed to be unconstrained @@ -1229,20 +1352,20 @@ formats | RNAplfold unpaired probabilities | `RNAplfold -u` or `IntaRNA --out=*Pu:` | | RNAplfold-styled ED values | `IntaRNA --out=*Acc:` | -The **RNAplfold** format is a table encoding of a banded upper triangular matrix +The **RNAplfold** format is a table encoding of a banded upper triangular matrix with band width l. First row contains a header comment on the data starting with `#`. Second line encodes the column headers, i.e. the window width per column. Every successive line starts with the index (starting from 1) of the window end followed by a tabulator separated list for each windows value in increasing -window length order. That is, column 2 holds values for window length 1, column -3 for length 2, ... . The following provides a short output/input +window length order. That is, column 2 holds values for window length 1, column +3 for length 2, ... . The following provides a short output/input example for a sequence of length 5 with a maximal window length of 3. ``` #unpaired probabilities - #i$ l=1 2 3 -1 0.9949492 NA NA -2 0.9949079 0.9941056 NA + #i$ l=1 2 3 +1 0.9949492 NA NA +2 0.9949079 0.9941056 NA 3 0.9554214 0.9518663 0.9511048 4 0.9165814 0.9122866 0.9090283 5 0.998999 0.915609 0.9117766 @@ -1263,8 +1386,8 @@ IntaRNA [..] --qAcc=P --qAccFile=plfold_lunp cat plfold_lunp | IntaRNA [..] --qAcc=P --qAccFile=STDIN ``` -Another option is to store the accessibility data computed by IntaRNA for -successive calls using +Another option is to store the accessibility data computed by IntaRNA for +successive calls using ```bash # storing and reusing (target) accessibility (Pu) data for successive IntaRNA calls @@ -1279,10 +1402,10 @@ Note, for *multiple sequences* in FASTA input, one can also load the accessibilities (for *all* sequencces) from file. To this end, the file names have to be prefixed with with `s` and the according sequence's number (where indexing starts with 1) within the FASTA input using a common suffix after the index. -This suffix is to be provided to the according `--?AccFile` argument. +This suffix is to be provided to the according `--?AccFile` argument. The files generated by `--out=?Acc:...` are already conform to this requirement, such that you can use the use case examples from above also for multi-sequence -FASTA input. +FASTA input. Note, this is not supported for a piped setup (e.g. via `--out=tAcc:STDOUT` as shown above), since this does not produce the according output files! @@ -1294,26 +1417,26 @@ as shown above), since this does not produce the according output files! ## Multi-threading and parallelized computation -IntaRNA supports the parallelization of the target-query-combination processing. +IntaRNA supports the parallelization of the target-query-combination processing. The maximal number of threads to be used can be specified using the `--threads` parameter. If `--threads=k != 1`, than *k* predictions are processed in parallel. A value of `0` requests the maximally available number of threads for this machine. When using parallelization, you should have the following in mind: -- The memory consumption will be multiplied by the number of threads, +- The memory consumption will be multiplied by the number of threads, since each thread runs an independent prediction (with according memory consumption). Thus, ensure you have enough - RAM available when using many threads of memory-demanding + RAM available when using many threads of memory-demanding [prediction modes](#predModes). You might consider [window-based prediction](#predWindowBased) to limit the required RAM. - + - Parallelization is enabled hierarchically, ie. only one of the following input - sets is processed in parallel: + sets is processed in parallel: - target sequences (if more than one) - if only one target: query sequences (if more than one) - if only one target and query: [window combinations](#predWindowBased) (if enabled) - + The support for multi-threading can be completely disabled before compilation using `configure --disable-multithreading`. @@ -1338,7 +1461,7 @@ information is generated and installed too. ## Mandatory `Easylogging++` initalization ! -Since IntaRNA makes heavy use of the `Easylogging++` library, you have to add (and adapt) +Since IntaRNA makes heavy use of the `Easylogging++` library, you have to add (and adapt) the following code to your central code that includes the `main()` function: ```[c++] // get central IntaRNA-lib definitions and includes @@ -1367,10 +1490,8 @@ int main(int argc, char **argv){ } ``` -Note further, to get the library correctly working the following compiler +Note further, to get the library correctly working the following compiler flags are used within the IntaRNA configuration: ```[bash] CXXFLAGS=" -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE " -``` - - +``` diff --git a/configure.ac b/configure.ac index d597d599..8adea131 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.65]) # 5 argument version only available with aclocal >= 2.64 -AC_INIT([IntaRNA], [2.3.1], [], [intaRNA], [http://www.bioinf.uni-freiburg.de] ) +AC_INIT([IntaRNA], [2.4.0], [], [intaRNA], [http://www.bioinf.uni-freiburg.de] ) # minimal required version of the boost library BOOST_REQUIRED_VERSION=1.50.0 diff --git a/doc/figures/helixbased.svg b/doc/figures/helixbased.svg new file mode 100644 index 00000000..f88325de --- /dev/null +++ b/doc/figures/helixbased.svg @@ -0,0 +1,948 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IL + IL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + versus + + + + diff --git a/doc/figures/icon-no.png b/doc/figures/icon-no.png new file mode 100644 index 0000000000000000000000000000000000000000..ad7139f6b725921e05db1d865079eb7f12216a29 GIT binary patch literal 1662 zcmV-^27&pBP)`W%l?^<%|)bK>wl7f&TvZHeLGL)VV<`+8PFSBC5GQ)Y(D-4d;z)oiQgM zqFx4S3xHWgyHc_-a3a{6Pi05swwZIPbfrE7Zk+_&*!U?avZmp9u*5d$05xw-UYb+D_mA2#4r|*?fOwj^C9__OW(zI(VrB#Is0K6^_w)G z;{Ou}Fx7NqB7_kLc6VwnwhZ0trF8D~m0dfph7L{RV9W+;y0oo+e=(`W zf@LJ5QF^}Jmb1(XY6k?^yX6~*#R$d|1V`dlP1kMp`-`>Ia?5!&)Aima;^A;!P$%Hf zK$z`sy@N*tj|km~B;D~uuByelUb0wU>AT`-ft#F=@xKS?|79=bO9JR#@3?Z^>!o~2 zfN0Md7QFkWE%2559-{Y1FoX1nFsu|hLa8=xrXG;itE;ynG&mvC;gls(qlGQ-^npX8 zfNp^z5!@p9b)75qEU03eX0E97j>w_I{I=o|%dRgJ=rMum&jZTfZ^Ov?J}NXQ44HvG z{HQfF7jS7~&J23Y47x>dNov6`s0_}l475UHg41B+;30l{_)!u=L$>uUoF7U7+Yj1i z&@JEw+~((~Qbbehim4i@P1D3esnpA%jvMmIpi97QzITg|01`^YG@xf9zVrj+Dk-t8 zH|6pYp8M)Es%q!umBBPnC-oi?w}vVl>G*_?iQ+OJ>$fy>O>IpUPO`E#I)iDTE`o*( zXsLbiYrOjF8ytZDg_%K7ug0L?DX0n`Bvby@@2Ia$D=aD)%mKg)19lC#`f=A*IY zWBl`L95a{#f*`x)yXn)5UHWBxI82R~T|0|gzW&@2I2;+|)uoM`+}}?1!Ua6Nr5SJO zLyRExB z3S+jkJ1Vc+gUx2_U|AW{mp{PS?qjrVYekPIY=PP1V3Ef|)m?S?X3iw^`Ip51>a_#{ z=juYi%yeDwzWa~Z2*Hu~7@QqY@)>5bn^1-(cLa|8`D6ORs1;_z@nDx2#&RI%G8y2; zA`kP6Jb7oZKp>*j3)#SNnM_AMXF(nD1Sb + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/doc/figures/icon-yes.png b/doc/figures/icon-yes.png new file mode 100644 index 0000000000000000000000000000000000000000..c452ee79d0629b0f5080a328cf8063f4f5edb6c5 GIT binary patch literal 1711 zcmV;g22lBlP)kP<6jyVTFk)2(&}JAGbb5>R9(6arr03Z77Dr%u(oOiP1IPMxTQ)3 zE7VI{+Vkv>bMK|Sz2|aoeAOyClN zs|+{_^eOZi9eH>v3Bin*u0qOB)W zt&ET>yMFo;Rkjk0vwW4KA}pW!Bt!{{g(`m4)ErYW(G!%Bp#Io94t(7B}}gDth`B)o%x0ngj>J&5YdZ& zmb~YyPtBgYu*H4j>gmrD>!^wUQBDpZm#sDpD_qBq3LH zEn0F`c9JsyE}8Lr7L-+|eSS&FclhYTF~01NgDP`ZbS+wvgfz&qd;%IVES<4}`DIn1 zb+&hJ$62AbcZ{!&1)vEoS>5@o!eNc|liOh-XTZ6oi$d$Pe9%fmTOG~=&jahh<(FHE zE;6wKDV*+xiB;zh(DLgUHTM?@YlK6+WEU>CR|iXzi<2E4ue zk&J+^9@@#;J%7YHe}ci_w_rWNIpx%-&T;V*^`zpN0&e;4gUl(t0fXhkuMe{8zZ>~- z=(CJ~RvfpNKf(`7t`Du#{N4`M?_G=aG_An^5}X0CTxbY^oL5%Kys}E1co;E!zxX=t zoxP6f=Y@;6;#fBG4;cYp>279y`#P+r84Y?y5f8+0o*`tPo}Xqto931ByAV(-jyq+o{QKzu%*6qn;Jm<~OBl2ui7s}6GlDgOCvwynBy_s=4jyL9N41RjL!m}z zPMaSJ*!0%l*|7iVsT#xz7}0*CqAj6OfZoAFG=3Zo8dm<+(ZpZ(ZwTz=HU>`v0*dS~ z1r)Xd88Zu?o_H@O&==l(k!KHVz(q8$Hx+|`h;aTXx6Z7JHpfKBXfqek#skl@`QXOD zpf~Pva(WDUinEIGz&Z&oE0X|?@V`~pzIMz2_-Io$h7zEs{Nc3EpF7aVmX1vURAfA0 z;e>2Z_Y))g4+zkdz4jmZuA5KCl zs-jwx4J1I%iEbJ{UYP{l_@8Hav9mF)!31DV1_4Cs_AcBz3MsF&wVjpwa>@CWPn(As zn8gbRn@+ERDS)0QSWg2e$7y>u98lm`UB&L?c0J*hRx$VF^4{HZ;#~%gzk?SE0MZ$> zp5hFcEDb8C8eKX*&V*c0eMMW32up#{Y;6{o2v^Q_c#+8lUEE-nV=!(}AFN-nr+s{k zvz7}Q{6_iwi_u_9OH2|I2aKc%# zvzEVDykvSuMwM?y ze5@0UAvn>bo5f=KP^qq~XgQpzy}aejM}1}3*R%!&8S*5ues%QKtk&kdetqy2#?S + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/doc/figures/interaction-context.svg b/doc/figures/interaction-context.svg new file mode 100644 index 00000000..ad951342 --- /dev/null +++ b/doc/figures/interaction-context.svg @@ -0,0 +1,752 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + c) + d) + + + + + + + + + + + + + + + + + + + + + + + + + + + + e + n + n + n + n + h + h + + + + + + + + + + + + + + + + + + + + e + e + + e + b) + a) + h + + h + h + + + diff --git a/perl/IntaRNA-fuse.pl b/perl/IntaRNA-fuse.pl index e2de0c05..577e75c4 100644 --- a/perl/IntaRNA-fuse.pl +++ b/perl/IntaRNA-fuse.pl @@ -152,7 +152,8 @@ my $overlapQ = min($dataMfe[$h2i{'end2'}],$dataMfeConstr[$h2i{'end2'}])-max($dataMfe[$h2i{'start2'}],$dataMfeConstr[$h2i{'start2'}]); my $overlap = min($overlapT,$overlapQ) / min($dataMfe[$h2i{'end1'}]-$dataMfe[$h2i{'start1'}],$dataMfe[$h2i{'end2'}]-$dataMfe[$h2i{'start2'}]); # check if overlapping at all - if ($overlap > 0) { + if ($overlapT > 0 and $overlapQ > 0) { +# if ($overlap > 0) { # print "\n# compatible interactions with mfe-overlap = $overlap :\n".$subopts[$s]."\n".$constraintOut[1]."\n"; ######################################## # compute overall energy diff --git a/src/IntaRNA/HelixConstraint.h b/src/IntaRNA/HelixConstraint.h new file mode 100644 index 00000000..5fc75c68 --- /dev/null +++ b/src/IntaRNA/HelixConstraint.h @@ -0,0 +1,269 @@ + +#ifndef INTARNA_HELIXCONSTRAINT_H +#define INTARNA_HELIXCONSTRAINT_H + +#include "IntaRNA/general.h" +#include "IntaRNA/IndexRangeList.h" + +#include +#include + +namespace IntaRNA { + +/** + * Encodes helix constraints to be used for interaction prediction. + * + * @author Rick Gelhausen + * + */ +class HelixConstraint { + +public: + + /** + * Constructor + * + * @param minBP minimal number of base pairs a helix is allowed to have (>= 2) + * @param maxBP maximal number of base pairs a helix is allowed to have (>= bpMin) + * @param maxIL maximal size for each internal loop during helix computation + * @param maxED maximal allowed energy for ED-values + * @param maxE maximal allowed energy during helix computation + * @parem noED whether or not ED values are added in the computation of helices + */ + HelixConstraint( const size_t minBP + , const size_t maxBP + , const size_t maxIL + , const E_type maxED + , const E_type maxE + , const bool noED + ); + + virtual ~HelixConstraint(); + + /** + * Provides the minimum number of base pairs allowed within an helix (>=2) + * + * @return the minimum number of base pairs an helix is allowed to have (>=2) + */ + size_t + getMinBasePairs() const; + + /** + * Provides the maximum number of base pairs allowed within an helix (>=minBP) + * + * @return the maximum number of base pairs an helix is allowed to have (>=maxBP) + */ + size_t + getMaxBasePairs() const; + + /** + * Provides the maximally allowed size of each internal loop considered in an helix + * @return the maximally allowed size of each internal loop considered in an helix + */ + size_t + getMaxIL() const; + + /** + * Provides the maximally allowed ED value (per sequence) for a helix to be considered + * @return the maximally allowed ED value (per sequence) for a helix + */ + E_type + getMaxED() const; + + /** + * Provides the maximally allowed energy for a helix to be considered + * @return the maximally allowed energy for a helix + */ + E_type + getMaxE() const; + + /** + * Whether or not ED- values are used in the helix computation + * @return true / false + */ + bool + useNoED() const; + + /** + * Provides the minimum interior loop length depending on the maximum allowed number of unpaired bases + * @return minimum interior loop length + */ + size_t + getMinInternalLoopSize() const; + + /** + * Provides the maximal length of the helix in seq1 + * @return the maximal length of the helix in seq1 + */ + size_t + getMaxLength1() const; + + /** + * Provides the maximal length of the helix in seq2 + * @return the maximal length of the helix in seq2 + */ + size_t + getMaxLength2() const; + + /** + * Prints the helix constraint details to stream + * @param out the ostream to write to + * @param c the object to add + * @return the altered stream out + */ + friend std::ostream& operator<<(std::ostream& out, const HelixConstraint& c); + +protected: + + //! the minimal number of base pairs allowed in the helix (>=2) + size_t minBP; + + //! the maximal number of base pairs allowed in the helix (>=maxBP) + size_t maxBP; + + //! the maximally allowed size of internal loops in the helix + size_t maxIL; + + //! the maximal ED value (per sequence) allowed for a helix + E_type maxED; + + //! the maximal energie allowed for a helix + E_type maxE; + + //! decision variable, whether ED-values are used in the computation of helix energies or not + bool noED; +}; + + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +inline +HelixConstraint::HelixConstraint( + const size_t minBP_ + , const size_t maxBP_ + , const size_t maxIL_ + , const E_type maxED_ + , const E_type maxE_ + , const bool noED_) + : + minBP(minBP_) + , maxBP(maxBP_) + , maxIL(maxIL_) + , maxED(maxED_) + , maxE(maxE_) + , noED(noED_) +{ + if (minBP < 2) throw std::runtime_error("HelixConstraint() : minimal base pair number ("+toString(minBP)+") < 2"); +} + +///////////////////////////////////////////////////////////////////////////// + +inline +HelixConstraint::~HelixConstraint() { +} + +///////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixConstraint:: +getMinBasePairs() const { + return minBP; +} + +///////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixConstraint:: +getMaxBasePairs() const { + return maxBP; +} + +///////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixConstraint:: +getMaxIL() const { + return maxIL; +} + +///////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixConstraint:: +getMaxED() const { + return maxED; +} + +///////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixConstraint:: +getMaxE() const { + return maxE; +} + +///////////////////////////////////////////////////////////////////////////// + +inline +bool +HelixConstraint:: +useNoED() const { + return noED; +} + +///////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixConstraint:: +getMaxLength1() const { + return getMaxBasePairs() + (getMaxBasePairs()-1) * getMaxIL(); +} + +///////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixConstraint:: +getMaxLength2() const { + return getMaxBasePairs() + (getMaxBasePairs()-1) * getMaxIL(); +} + +///////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixConstraint:: +getMinInternalLoopSize() const { + return getMaxIL(); +} + +///////////////////////////////////////////////////////////////////////////// + +inline +std::ostream& +operator<<(std::ostream& out, const HelixConstraint& c) +{ + out <<"HelixConstraint( minBP="< + +#include + +namespace IntaRNA { + +/** + * Wrapper around a HelixHandler object where indices are shifted by + * a given positive offset (shifted towards infinity). + * This is useful for local interaction computations. + * + * @author Rick Gelhausen + */ +class HelixHandlerIdxOffset : public HelixHandler +{ + +public: + + /** + * Construction + * + */ + HelixHandlerIdxOffset( HelixHandler * helixHandler ); + + /** + * destruction + */ + virtual ~HelixHandlerIdxOffset(); + + /** + * Access to the underlying interaction energy function + * @return the used energy function + */ + virtual + const InteractionEnergy& + getInteractionEnergy() const; + + /** + * Access to the currently used index offset for sequence 1 + * @return the index offset for sequence 1 used + */ + size_t getOffset1() const; + + /** + * Sets the index offset to be used for sequence 1 + * @param offset1 the index offset for sequence 1 to be used + */ + void setOffset1(size_t offset1); + + /** + * Access to the currently used index offset for sequence 2 + * @return the index offset for sequence 2 used + */ + size_t getOffset2() const; + + /** + * Sets the index offset to be used for sequence 2 + * @param offset2 the index offset for sequence 2 to be used + */ + void setOffset2(size_t offset2); + + /////////////// OVERWRITTEN MEMBERS USING OFFSET ///////////////// + + /** + * Access to the underlying helix constraint + * @return the used helix constraint + */ + virtual + const HelixConstraint& + getConstraint() const; + + /** + * Computes the helix matrix for the given interval boundaries + * + * Note, the indices are shifted by an offset for computation. + * + * @param i1 the first index of seq1 that might interact + * @param j1 the last index of seq1 that might interact + * @param i2 the first index of seq2 that might interact + * @param j2 the last index of seq2 that might interact + * + * @return number of possible helix interactions + */ + virtual + size_t + fillHelix(const size_t i1, const size_t j1, const size_t i2, const size_t j2); + + /** + * Computes the helix matrix for the given interval boundaries for helices containing a seed + * + * Note, the indices are shifted by an offset for computation. + * + * @param i1 the first index of seq1 that might interact + * @param j1 the last index of seq1 that might interact + * @param i2 the first index of seq2 that might interact + * @param j2 the last index of seq2 that might interact + * @return + */ + virtual + size_t + fillHelixSeed(const size_t i1, const size_t j1, const size_t i2, const size_t j2); + + /** + * Identifies the base pairs of the mfe helix interaction starting at i1,i2 + * and writes them to the provided container + * + * NOTE: the right most base pair is excluded! + * + * Note, the indices are shifted by an offset for computation. + * + * @param interaction the container to add the base pairs too + * @param i1 the start of the helix in seq1 + * @param i2 the start of the helix in seq2 + */ + virtual + void + traceBackHelix( Interaction & interaction, const size_t i1, const size_t i2); + + /** + * Identifies the base pairs of the mfe helix interaction, containing a seed, starting at i1,i2 + * and writes them to the provided container + * + * Note, the indices are shifted by an offset for computation. + * + * @param interaction the container to add the base pairs too + * @param i1 the start of the helix in seq1 + * @param i2 the start of the helix in seq2 + */ + virtual + void + traceBackHelixSeed( Interaction & interaction, const size_t i1, const size_t i2); + + /** + * Access to the mfe of any helix with left-most base pair (i1,i2) + * + * Note, the indices are shifted by an offset for computation + * + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the mfe of any helix starting at (i1,i2) or E_INF if none possible + */ + virtual + E_type + getHelixE( const size_t i1, const size_t i2 ) const; + + /** + * Access to the mfe of any helix with left-most base pair (i1,i2) and containing a seed + * + * Note, the indices are shifted by an offset for computation + * + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the mfe of any helix starting at (i1,i2) or E_INF if none possible + */ + virtual + E_type + getHelixSeedE( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq1 of the mfe helix with left-most base pair (i1,i2) + * + * Note, the indices are shifted by an offset for computation. + * + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength1( const size_t i1, const size_t i2 ) const; + + + /** + * Access to the length in seq2 of the mfe helix with left-most base pair (i1,i2) + * + * Note, the indices are shifted by an offset for computation. + * + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength2( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq1 of the mfe helix with left-most base pair (i1,i2), containing a seed + * + * Note, the indices are shifted by an offset for computation. + * + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixSeedLength1( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq2 of the mfe helix with left-most base pair (i1,i2), containing a seed + * + * Note, the indices are shifted by an offset for computation. + * + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixSeedLength2( const size_t i1, const size_t i2 ) const; + + /** + * Check whether the given seedHandler is of type SeedHandlerIdxOffset + * Set the seedHandler of helixHandlerOriginal + * + * @param seedHandler the seedHandler with offset + */ + virtual + void + setSeedHandler( SeedHandler & seedHandler); + +protected: + + //! the index shifted helixHandler + HelixHandler * helixHandlerOriginal; + + //! the index shifted helix constraint + HelixConstraint helixConstraintOffset; + + //! the seedHandler with offset + //! Note: (might be needed later) + SeedHandlerIdxOffset * seedHandlerIdxOffset; + + //! offset for indices in seq1 + size_t idxOffset1; + + //! offset for indices in seq2 + size_t idxOffset2; + +}; + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// + + +inline +HelixHandlerIdxOffset:: +HelixHandlerIdxOffset( HelixHandler * helixHandlerInstance ) + : + helixHandlerOriginal( helixHandlerInstance ) + , helixConstraintOffset( helixHandlerOriginal->getConstraint() ) + , idxOffset1(0) + , idxOffset2(0) + , seedHandlerIdxOffset(NULL) +{ + +} + +//////////////////////////////////////////////////////////////////////////// + +inline +HelixHandlerIdxOffset::~HelixHandlerIdxOffset() +{ + if (helixHandlerOriginal != NULL) { + delete helixHandlerOriginal; + helixHandlerOriginal = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////// + +inline +const InteractionEnergy& +HelixHandlerIdxOffset:: +getInteractionEnergy() const +{ + return helixHandlerOriginal->getInteractionEnergy(); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +const HelixConstraint& +HelixHandlerIdxOffset:: +getConstraint() const +{ + return helixConstraintOffset; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +fillHelix(const size_t i1min, const size_t i1max, const size_t i2min, const size_t i2max) +{ + return helixHandlerOriginal->fillHelix( i1min+idxOffset1, i1max+idxOffset1, i2min+idxOffset2, i2max+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +fillHelixSeed(const size_t i1min, const size_t i1max, const size_t i2min, const size_t i2max) +{ + return helixHandlerOriginal->fillHelixSeed( i1min+idxOffset1, i1max+idxOffset1, i2min+idxOffset2, i2max+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerIdxOffset:: +traceBackHelix(Interaction &interaction + , const size_t i1 + , const size_t i2 + ) +{ + helixHandlerOriginal->traceBackHelix( interaction, i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerIdxOffset:: +traceBackHelixSeed(Interaction &interaction + , const size_t i1 + , const size_t i2 +) +{ + helixHandlerOriginal->traceBackHelixSeed( interaction, i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerIdxOffset:: +getHelixE(const size_t i1, const size_t i2) const +{ + return helixHandlerOriginal->getHelixE( i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerIdxOffset:: +getHelixSeedE(const size_t i1, const size_t i2) const +{ + return helixHandlerOriginal->getHelixSeedE( i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +getHelixLength1(const size_t i1, const size_t i2) const +{ + return helixHandlerOriginal->getHelixLength1( i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +getHelixLength2(const size_t i1, const size_t i2) const +{ + return helixHandlerOriginal->getHelixLength2( i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +getHelixSeedLength1(const size_t i1, const size_t i2) const +{ + return helixHandlerOriginal->getHelixSeedLength1( i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +getHelixSeedLength2(const size_t i1, const size_t i2) const +{ + return helixHandlerOriginal->getHelixSeedLength2( i1+idxOffset1, i2+idxOffset2 ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +getOffset1() const +{ + return idxOffset1; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerIdxOffset:: +getOffset2() const +{ + return idxOffset2; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerIdxOffset:: +setOffset1( const size_t offset ) +{ +#if INTARNA_IN_DEBUG_MODE + if (offset >= helixHandlerOriginal->getInteractionEnergy().size1()) { + throw std::runtime_error("HelixHandlerIdxOffset.setOffset1("+toString(offset) + +") offset > seq1.length "+toString(helixHandlerOriginal->getInteractionEnergy().size1())); + } +#endif + // set idx offset + this->idxOffset1 = offset; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerIdxOffset:: +setOffset2( const size_t offset ) +{ +#if INTARNA_IN_DEBUG_MODE + if (offset >= helixHandlerOriginal->getInteractionEnergy().size2()) { + throw std::runtime_error("HelixHandlerIdxOffset.setOffset1("+toString(offset) + +") offset > seq1.length "+toString(helixHandlerOriginal->getInteractionEnergy().size2())); + } +#endif + // set idx offset + this->idxOffset2 = offset; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerIdxOffset:: +setSeedHandler(SeedHandler & seedHandler) +{ + // cast input + SeedHandlerIdxOffset * shOffset = dynamic_cast(&seedHandler); + +#if INTARNA_IN_DEBUG_MODE + // sanity check + if (shOffset == NULL) { + throw std::runtime_error("HelixHandlerIdxOffset.setSeedHandler(). Given seedHandler is not of type SeedHandlerIdxOffset."); + } +#endif + + // store locally (might be useful later) + seedHandlerIdxOffset = shOffset; + + // forward seedHandler to helixHandler + helixHandlerOriginal->setSeedHandler( shOffset->getOriginalSeedHandler()); +} + +} // namespace +#endif /* HELIXHANDLERIDXOFFSET_H_ */ diff --git a/src/IntaRNA/HelixHandlerStackingOnly.cpp b/src/IntaRNA/HelixHandlerStackingOnly.cpp new file mode 100644 index 00000000..2cb5ce9b --- /dev/null +++ b/src/IntaRNA/HelixHandlerStackingOnly.cpp @@ -0,0 +1,196 @@ +#include "IntaRNA/HelixHandlerStackingOnly.h" + +namespace IntaRNA { + +///////////////////////////////////////////////////////////////////////////// + +size_t +HelixHandlerStackingOnly:: +fillHelix(const size_t i1min, const size_t i1max, const size_t i2min, const size_t i2max) +{ + +#if INTARNA_IN_DEBUG_MODE + if ( i1min > i1max ) throw std::runtime_error("HelixHandlerStackingOnly::fillHelix: i1min("+toString(i1min)+") > i1max("+toString(i1max)+")"); + if ( i2min > i2max ) throw std::runtime_error("HelixHandlerStackingOnly::fillHelix: i2min("+toString(i2min)+") > i2max("+toString(i2max)+")"); + if ( i1max > energy.size1() ) throw std::runtime_error("HelixHandlerStackingOnly::fillHelix: i1max("+toString(i1max)+") > energy.size1("+toString(energy.size1())+")"); + if ( i2max > energy.size2() ) throw std::runtime_error("HelixHandlerStackingOnly::fillHelix: i2max("+toString(i2max)+") > energy.size2("+toString(energy.size2())+")"); + if ( helixConstraint.getMinBasePairs() > helixConstraint.getMaxBasePairs() ) + throw std::runtime_error("HelixHandlerStackingOnly::fillHelix: bpMin("+toString(helixConstraint.getMinBasePairs()) +") > bpMax("+toString(helixConstraint.getMaxBasePairs())+")"); +#endif + + helix.resize( i1max-i1min+1, i2max-i2min+1 ); + helixE_rec.resize( HelixIndex({{(HelixRecMatrix::index)(helix.size1()) + , (HelixRecMatrix::index)(helix.size2()) + , (HelixRecMatrix::index)(getConstraint().getMaxBasePairs()+1)}})); + + // Initialize helixE_rec with E_INF in order to ensure that skipped values are not considered. + std::fill( helixE_rec.origin(), helixE_rec.origin() + helixE_rec.num_elements(), E_INF); + + size_t minBP; + E_type maxE; + // TODO: Fix until better solution is found + // if no seedHandler is given use given values, else use default values to ensure they dont compromise the results of the helixSeed method + if (seedHandler == NULL) { + minBP = helixConstraint.getMinBasePairs(); + maxE = helixConstraint.getMaxE(); + } else { + minBP = 2; + maxE = 999; + } + + // store index offset due to restricted matrix size generation + offset1 = i1min; + offset2 = i2min; + + // temporary variables + size_t i1, i2, curBP, j1, j2, k1, k2, bestBP; + E_type curE, bestE; + + size_t helixCountNotInf = 0, helixCount = 0; + + // fill for all start indices + // in decreasing index order + for (i1=i1max+1; i1-- > i1min;) { + for (i2=i2max+1; i2-- > i2min;) { + + // count possible helices + helixCount++; + + // init according to no helix interaction + helix(i1-offset1,i2-offset2) = HelixMatrix::value_type( E_INF, 0 ); + + // skip non-complementary left helix boundaries + if (!energy.areComplementary(i1,i2)) { + continue; // go to next helixE index + } + + // screen over all possible base pair combinations (starting at 2) + for (curBP=2; curBP < helixConstraint.getMaxBasePairs()+1 && (i1+curBP-1-offset1 skip + } + + // update mfe for split at k1,k2 + curE = energy.getE_interLeft(i1,k1,i2,k2) + getHelixE(k1-offset1, k2-offset2, curBP-1); + } + } else { + break; + } + + // store helix energy + setHelixE(i1-offset1, i2-offset2, curBP, curE); + + } // curBP + + // find best combination in helix for i1, i2, bp + bestBP = 0; + bestE = E_INF; + + // Calculate energy for all different number of base pairs + // Ensure minimum number of base pairs + for (curBP = minBP; curBP < helixConstraint.getMaxBasePairs() + 1 + && (i1+curBP-1-offset1) < helix.size1() + && (i2+curBP-1-offset2) < helix.size2(); curBP++) { + // right helix boundaries + j1 = i1 + curBP-1; + j2 = i2 + curBP-1; + + if (E_isINF(getHelixE(i1-offset1, i2-offset2, curBP))) { + continue; + } + + // ensure that ED-values are within the boundaries (default 999) + if (energy.getED1(i1, j1) > helixConstraint.getMaxED() + || energy.getED2(i2, j2) > helixConstraint.getMaxED()) + { + continue; + } + + // calculate energy with ED values + curE = energy.getE(i1,j1,i2,j2,getHelixE(i1-offset1, i2-offset2, curBP)) + energy.getE_init(); + + // if noED option is set, ED-values are skipped, remove them. + if (helixConstraint.useNoED()) + curE -= (energy.getED1(i1, j1) + energy.getED2(i2, j2)); + + // check if better than what is know so far + if (curE < bestE) { + bestE = curE; + bestBP = curBP; + } + } // curBP + + // reduce bestE to hybridization energy only (init+loops) + if (E_isNotINF( bestE )) { + // overwrite all helices with too high energy -> infeasible start interactions + if (bestE > maxE) { + bestE = E_INF; + } else { + // get helix hybridization loop energies only + bestE = getHelixE(i1 - offset1, i2 - offset2, bestBP); + // count true helix + helixCountNotInf++; + } + } + + // store best (mfe) helix for left boundary i1, i2 + helix(i1-offset1, i2-offset2) = HelixMatrix::value_type(bestE, E_isINF(bestE) ? 0: encodeHelixLength(bestBP, bestBP)); + } // i2 + } // i1 + +#if INTARNA_MULITHREADING +#pragma omp critical(intarna_omp_logOutput) +#endif + { VLOG(2) << "valid helices = " << helixCountNotInf << " (" << (helixCountNotInf/helixCount) << "% of start index combinations)"; } + + return helixCountNotInf; +} + +////////////////////////////////////////////////////////////////////////// + +void +HelixHandlerStackingOnly:: +traceBackHelix( Interaction & interaction + , const size_t i1_ + , const size_t i2_ + , const size_t bp) +{ + + // get boundaries + size_t i1 = i1_ + , i2 = i2_; + + // trace helices + // trace each helix base pair (excluding right most) + for (size_t curBP = 0; curBP < bp-1; curBP++) { + if (i1 != i1_) { + interaction.basePairs.push_back(energy.getBasePair(i1+offset1, i2+offset2)); + } + i1++; + i2++; + } + +} + +} // namespace \ No newline at end of file diff --git a/src/IntaRNA/HelixHandlerStackingOnly.h b/src/IntaRNA/HelixHandlerStackingOnly.h new file mode 100644 index 00000000..0c2a990f --- /dev/null +++ b/src/IntaRNA/HelixHandlerStackingOnly.h @@ -0,0 +1,541 @@ + +#ifndef INTARNA_HELIXHANDLERSTACKINGONLY_H_ +#define INTARNA_HELIXHANDLERSTACKINGONLY_H_ + +#include "IntaRNA/InteractionEnergy.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandler.h" + +#include + +namespace IntaRNA { + +/** + * Handler to provide helix interactions that are based on stacked base pairs + * only + * + * @author Rick Gelhausen + */ +class HelixHandlerStackingOnly : public HelixHandler { + +public: + + //! 3D matrix type to hold the mfe energies for helix interactions + //! of the ranges i1..(i1+bp-1) and i2..(i2+bp-1) with + //! i1,i2 = start indices of the helix in seq1/2 respectively + //! bp = the number of base pairs within this helix + typedef boost::multi_array HelixRecMatrix; + + //! defines the helix data (( i1, i2, bp )) to access elements of the HelixRecMatrix + typedef boost::array HelixIndex; + + //! matrix to store the helix information for each helix left side (i1, i2) + //! it holds both the energy (first) as well as the length of the helix using + //! the length combination of encodeHelixLength() + typedef boost::numeric::ublas::matrix< std::pair > HelixMatrix; + +public: + + /** + * Constructor + * @param energy the energy function to be used + */ + HelixHandlerStackingOnly( + const InteractionEnergy & energy + , const HelixConstraint & helixConstraint + , SeedHandler * const seedHandler = NULL + ); + + /** + * destructor + */ + virtual ~HelixHandlerStackingOnly(); + + /** + * Access to the underlying helix constraint + * @return the used helix constraint + */ + virtual + const HelixConstraint& + getConstraint() const; + + /** + * Access to the underlying interaction energy function + * @return the used energy function + */ + virtual + const InteractionEnergy& + getInteractionEnergy() const; + + + /** + * Compute the helix matrix for the given interval boundaries + * @param i1 the first index of seq1 that might interact + * @param j1 the last index of seq1 that might interact + * @param i2 the first index of seq2 that might interact + * @param j2 the last index of seq2 that might interact + * @return number of valid helices + */ + virtual + size_t + fillHelix( const size_t i1, const size_t j1, const size_t i2, const size_t j2 ); + + /** + * Compute the helix matrix, containing a seed, for the given interval boundaries + * @param i1 the first index of seq1 that might interact + * @param j1 the last index of seq1 that might interact + * @param i2 the first index of seq2 that might interact + * @param j2 the last index of seq2 that might interact + * @return + */ + virtual + size_t + fillHelixSeed( const size_t i1, const size_t j1, const size_t i2, const size_t j2 ); + + /** + * Identifies the base pairs of the mfe helix interaction starting at i1,i2 + * and writes them to the provided container + * + * Note: the right most base pair is excluded! + * + * @param interaction the container to add the base pairs too + * @param i1 the start of the helix in seq1 + * @param i2 the start of the helix in seq2 + */ + virtual + void + traceBackHelix( Interaction & interaction, const size_t i1, const size_t i2); + + /** + * Identifies the base pairs of the mfe helix interaction, containing a seed, starting at i1,i2 + * and writes them to the provided container + * + * @param interaction the container to add the base pairs too + * @param i1 the start of the helix in seq1 + * @param i2 the start of the helix in seq2 + */ + virtual + void + traceBackHelixSeed( Interaction & interaction, const size_t i1, size_t i2); + + /** + * Access to the mfe of any helix with left-most base pair (i1, i2) + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the mfe of any helix starting at (i1,i2) or E_INF if none possible + * or return 0 if bp is 0 or 1 (used in helixHandlerSeed computation to avoid too many conditions) + */ + virtual + E_type getHelixE( const size_t i1, const size_t i2 ) const; + + /** + * Access to the mfe of any helix, containing a seed, with left-most base pair (i1, i2) + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the mfe of any helix starting at (i1,i2) or E_INF if none possible + */ + virtual + E_type getHelixSeedE( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq1 of the mfe helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength1( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq2 of the mfe helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength2( const size_t i1, const size_t i2 ) const; + + + /** + * Access to the length in seq1 of the mfe helix, containing a seed, with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixSeedLength1( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq2 of the mfe helix, containing a seed, with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixSeedLength2( const size_t i1, const size_t i2 ) const; + + /** + * Set the seedHandler in order to compute helixSeed + * @param seedHandler seedHandler to be used in the helix computation + */ + void setSeedHandler(SeedHandler & seedHandler); + +protected: + /** + * Provides the helix energy during the recursion + * + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @param bp the number of base pairs + * + * @return the energy of the according helix + */ + E_type + getHelixE(const size_t i1, const size_t i2, const size_t bp); + + /** + * Fills the helix energy during the recursion + * + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @param bp the number of base pairs + * @param E the energy value to be set + */ + void + setHelixE( const size_t i1, const size_t i2, const size_t bp, const E_type E ); + + /** + * Encodes the seed lengths into one number + * @param l1 the length of the seed in seq1 + * @param l2 the length of the seed in seq2 + * @return the combined encoding = (l1 + l2*(max_l1+1)) + */ + size_t + encodeHelixLength( const size_t l1, const size_t l2 ) const; + + /** + * Decodes the length of the seed within sequence 1 from an encoding + * generated with encodeSeedLength() + * @param code the lengths encoding + * @return the length of the seed in seq1 + */ + size_t + decodeHelixLength1( const size_t code ) const; + + /** + * Decodes the length of the seed within sequence 2 from an encoding + * generated with encodeSeedLength() + * @param code the lengths encoding + * @return the length of the seed in seq2 + */ + size_t + decodeHelixLength2( const size_t code ) const; + + /** + * Encodes the seed lengths into one number + * @param l1 the length of the seed in seq1 + * @param l2 the length of the seed in seq2 + * @return the combined encoding = (l1 + l2*(max_l1+1)) + */ + size_t + encodeHelixSeedLength( const size_t l1, const size_t l2 ) const; + + /** + * Decodes the length of the seed within sequence 1 from an encoding + * generated with encodeSeedLength() + * @param code the lengths encoding + * @return the length of the seed in seq1 + */ + size_t + decodeHelixSeedLength1( const size_t code ) const; + + /** + * Decodes the length of the seed within sequence 2 from an encoding + * generated with encodeSeedLength() + * @param code the lengths encoding + * @return the length of the seed in seq2 + */ + size_t + decodeHelixSeedLength2( const size_t code ) const; + + /** + * Fills a given interaction with the according + * hybridizing base pairs of the provided helix interaction + * + * @param interaction IN/OUT the interaction to fill + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @param bp the number of base pairs + */ + void + traceBackHelix( Interaction & interaction + , const size_t i1, const size_t i2, const size_t bp); + +protected: + + //! the used energy function + const InteractionEnergy& energy; + + //! the helix constraint to be applied + const HelixConstraint & helixConstraint; + + //! the recurstion data for the cimputation of a helix interaction + //! bp = the number of base pairs + //! i1..(i1+bp-1) and i2..(i2+bp-1) + //! using the indexing [i1][i2][bp] + HelixRecMatrix helixE_rec; + + //! the helix mfe information for helix starting at (i1, i2) + HelixMatrix helix; + + //! the helix mfe information for helix with seed starting at (i1, i2) + HelixMatrix helixSeed; + + //! offset for seq1 indices for the current matrices + size_t offset1; + + //! offset for seq2 indices for the current matrices + size_t offset2; + + // seedHandler used in helixSeed computation + SeedHandler * seedHandler; +}; + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// + +inline +HelixHandlerStackingOnly::HelixHandlerStackingOnly( + const InteractionEnergy & energy + , const HelixConstraint & helixConstraint + , SeedHandler * const seedHandler +) + : + energy(energy) + , helixConstraint(helixConstraint) + , helix() + , helixSeed() + , offset1(0) + , offset2(0) +{ + if (seedHandler != NULL) { + setSeedHandler( *seedHandler ); + } +} + +//////////////////////////////////////////////////////////////////////////// + +inline +HelixHandlerStackingOnly::~HelixHandlerStackingOnly() +{ +} + +//////////////////////////////////////////////////////////////////////////// + +inline +const InteractionEnergy& +HelixHandlerStackingOnly:: +getInteractionEnergy() const +{ + return energy; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +const HelixConstraint& +HelixHandlerStackingOnly:: +getConstraint() const +{ + return helixConstraint; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerStackingOnly:: +traceBackHelix(Interaction &interaction + , const size_t i1 + , const size_t i2 +) +{ +#if INTARNA_IN_DEBUG_MODE + if ( i1 < offset1 ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i1="+toString(i1)+") is out of range (>"+toString(offset1)+")"); + if ( i1-offset1 >= helix.size1() ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i1="+toString(i1)+") is out of range (<"+toString(helix.size1()+offset1)+")"); + if ( i2 < offset2 ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i2="+toString(i2)+") is out of range (>"+toString(offset2)+")"); + if ( i2-offset2 >= helix.size2() ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i2="+toString(i2)+") is out of range (<"+toString(helix.size2()+offset2)+")"); + if ( E_isINF( getHelixE(i1,i2) ) ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i1="+toString(i1)+",i2="+toString(i2)+") no helix known (E_INF)"); + if ( i1+getHelixLength1(i1,i2)-1-offset1 >= helix.size1() ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i1="+toString(i1)+") helix length ("+toString(getHelixLength1(i1,i2))+") exceeds of range (<"+toString(helix.size1()+offset1)+")"); + if ( i2+getHelixLength2(i1,i2)-1-offset2 >= helix.size2() ) throw std::runtime_error("HelixHandlerStackingOnly::traceBackHelix(i2="+toString(i2)+") helix length ("+toString(getHelixLength2(i1,i2))+") exceeds of range (<"+toString(helix.size2()+offset2)+")"); +#endif + // get number of base pairs for best helix + const size_t bestBP = getHelixLength1(i1,i2); + + // trace back the according helix + traceBackHelix( interaction, i1-offset1, i2-offset2, bestBP); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerStackingOnly:: +getHelixE(const size_t i1, const size_t i2) const +{ + return helix(i1-offset1, i2-offset2).first; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerStackingOnly:: +getHelixSeedE(const size_t i1, const size_t i2) const +{ + return helixSeed(i1-offset1, i2-offset2).first; +} + +//////////////////////////////////////////////////////////////////////////// +inline +E_type +HelixHandlerStackingOnly:: +getHelixE(const size_t i1, const size_t i2, const size_t bp) +{ + // if no base pair is given return 0 in order to simplify helixHandlerSeed computation. + if (bp <= 1) { + return 0; + } else { + return helixE_rec( + HelixIndex({{(HelixRecMatrix::index) i1, (HelixRecMatrix::index) i2, (HelixRecMatrix::index) bp}})); + } +} + +//////////////////////////////////////////////////////////////////////////// +inline +void +HelixHandlerStackingOnly:: +setHelixE(const size_t i1, const size_t i2, const size_t bp, const E_type E) +{ + helixE_rec(HelixIndex({{ (HelixRecMatrix::index) i1 + , (HelixRecMatrix::index) i2 + , (HelixRecMatrix::index) bp}})) = E; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +getHelixLength1(const size_t i1, const size_t i2) const +{ + return decodeHelixLength1(helix(i1-offset1, i2-offset2).second); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +getHelixLength2(const size_t i1, const size_t i2) const +{ + return decodeHelixLength2(helix(i1-offset1, i2-offset2).second); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +getHelixSeedLength1(const size_t i1, const size_t i2) const +{ + return decodeHelixSeedLength1(helixSeed(i1-offset1, i2-offset2).second); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +getHelixSeedLength2(const size_t i1, const size_t i2) const +{ + return decodeHelixSeedLength2(helixSeed(i1-offset1, i2-offset2).second); +} + +/////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +encodeHelixLength( const size_t l1, const size_t l2 ) const +{ + return l1 + l2*(helixConstraint.getMaxLength1()+1); +} + +////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +decodeHelixLength1( const size_t code ) const +{ + return code % (helixConstraint.getMaxLength1()+1); +} + +////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +decodeHelixLength2( const size_t code ) const +{ + return code / (helixConstraint.getMaxLength1()+1); +} + +/////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +encodeHelixSeedLength( const size_t l1, const size_t l2 ) const +{ + return l1 + l2*(helixConstraint.getMaxLength1() + seedHandler->getConstraint().getMaxLength1()+1); +} + +////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +decodeHelixSeedLength1( const size_t code ) const +{ + return code % (helixConstraint.getMaxLength1() + seedHandler->getConstraint().getMaxLength1()+1); +} + +////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerStackingOnly:: +decodeHelixSeedLength2( const size_t code ) const +{ + return code / (helixConstraint.getMaxLength1() + seedHandler->getConstraint().getMaxLength1()+1); +} + +////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerStackingOnly::setSeedHandler(SeedHandler & seedHandler) { + this->seedHandler = &seedHandler; +} + +////////////////////////////////////////////////////////////////////////// + +} // namespace +#endif /* HELIXHANDLERSTACKINGONLY_H_ */ diff --git a/src/IntaRNA/HelixHandlerStackingOnlySeed.cpp b/src/IntaRNA/HelixHandlerStackingOnlySeed.cpp new file mode 100644 index 00000000..338bf5e9 --- /dev/null +++ b/src/IntaRNA/HelixHandlerStackingOnlySeed.cpp @@ -0,0 +1,239 @@ +#include "IntaRNA/HelixHandlerStackingOnly.h" + +namespace IntaRNA { + +///////////////////////////////////////////////////////////////////////////// + +size_t +HelixHandlerStackingOnly:: +fillHelixSeed(const size_t i1min, const size_t i1max, const size_t i2min, const size_t i2max) +{ + helixSeed.resize( i1max-i1min+1, i2max-i2min+1 ); + + // store index offset due to restricted matrix size generation + offset1 = i1min; + offset2 = i2min; + + // temporary variables + size_t i1, i2,j1,j2, seedStart1, seedStart2, seedEnd1, seedEnd2, bestL1, bestL2, possibleBasePairs; + size_t helixCountNotInf = 0, helixCount = 0; + + E_type bestE, curE, bestE_withED, curE_withED; + + // fill for all start indices + // in decreasing index order + for (i1=i1max+1; i1-- > i1min;) { + for (i2=i2max+1; i2-- > i2min;) { + + // count possible helices + helixCount++; + + // init according to no helix interaction + helixSeed(i1 - offset1, i2 - offset2) = HelixMatrix::value_type(E_INF, 0); + + // skip non-complementary left helix boundaries + if (!energy.areComplementary(i1, i2)) { + continue; // go to next helixSeedE index + } + + // TODO: THIS might make the boundary conditions useless + // Check if a seed can fit given the left boundaries + // Note: If seedHandler allows unpaired positions this check is not enough, check happens in loop + if (std::min(helixSeed.size1()+offset1-i1, helixSeed.size2()+offset2-i2) < seedHandler->getConstraint().getBasePairs()) { + continue; + } else { + // Seed fits, check how many bases are possible around + possibleBasePairs = std::min(std::min(helixSeed.size1()+offset1-i1, helixSeed.size2()+offset2-i2), helixConstraint.getMaxBasePairs())-seedHandler->getConstraint().getBasePairs(); + } + + // Initialuze variables + curE_withED = E_INF; + curE = E_INF; + bestE_withED = E_INF; + bestE = E_INF; + bestL1 = 0; + bestL2 = 0; + + // screen over all possible leading and trailing base pair combinations + for (size_t leadingBP=0; leadingBP <= possibleBasePairs + && (i1+leadingBP-offset1) < helixSeed.size1() + && (i2+leadingBP-offset2) < helixSeed.size2(); leadingBP++) { + // check if leading based pairs are possible, otherwise stop computation + if (!energy.areComplementary(i1+leadingBP,i2+leadingBP)) + { + break; + } + + // the start positions for the seed + seedStart1 = i1+leadingBP; + seedStart2 = i2+leadingBP; + + // If no seed is possible here, skip to next leading base pair number + if (E_isINF(seedHandler->getSeedE(seedStart1, seedStart2))) { + continue; + } + + // the end positions of the seed + seedEnd1 = seedStart1+seedHandler->getSeedLength1(seedStart1, seedStart2)-1; + seedEnd2 = seedStart2+seedHandler->getSeedLength2(seedStart1, seedStart2)-1; + + // Run over all trailing base pairs + for (size_t trailingBP=0; trailingBP <= possibleBasePairs - leadingBP + && (seedEnd1+trailingBP-offset1) < helixSeed.size1() + && (seedEnd2+trailingBP-offset2) < helixSeed.size2(); trailingBP++) { + + // If trailing base pairs exist and helixE = E_INF -> skip to the next leadingBP + if (trailingBP != 0 && E_isINF(getHelixE(seedEnd1-offset1,seedEnd2-offset2,trailingBP+1))) { + break; + } + + j1 = seedEnd1+trailingBP; + j2 = seedEnd2+trailingBP; + + // ensure that ED-values are within the boundaries (default 999) + if (energy.getED1(i1, j1) > helixConstraint.getMaxED() + || energy.getED2(i2, j2) > helixConstraint.getMaxED()) { + continue; + } + + // energy value without dangling ends and E_init + curE = getHelixE(i1 - offset1, i2 - offset2, leadingBP + 1) + + seedHandler->getSeedE(seedStart1, seedStart2) + + getHelixE(seedEnd1 - offset1, seedEnd2 - offset2, trailingBP + 1); + + // energy value + curE_withED = energy.getE(i1, j1, i2, j2, curE) + energy.getE_init(); + + if (helixConstraint.useNoED()) + curE_withED -= (energy.getED1(i1, j1) + energy.getED2(i2, j2)); + + // Check whether new combination is better and fullfils the minBP constraints + if (curE_withED < bestE_withED && !E_equal(curE_withED, bestE_withED) + && leadingBP + trailingBP+ seedHandler->getConstraint().getBasePairs() >= getConstraint().getMinBasePairs()) { + bestE_withED = curE_withED; + bestE = curE; + bestL1 = leadingBP + seedHandler->getSeedLength1(seedStart1,seedStart2) + trailingBP; + bestL2 = leadingBP + seedHandler->getSeedLength2(seedStart1,seedStart2) + trailingBP; + } + + } // trailingBP + } // leadingBP + + // reduce bestE to hybridization energy only (init+loops) + if (E_isNotINF( bestE_withED )) { + // overwrite all helices with too high energy -> infeasible start interactions + if (bestE_withED > helixConstraint.getMaxE()) { + bestE = E_INF; + } else { + // count true helix + helixCountNotInf++; + } + } + + // store best (mfe) helix for left boundary i1, i2 + helixSeed(i1-offset1, i2-offset2) = HelixMatrix::value_type(bestE, E_isINF(bestE) ? 0: encodeHelixSeedLength(bestL1,bestL2)); + + } // i2 + } // i1 + + return helixCountNotInf; +} + +////////////////////////////////////////////////////////////////////////// + +void +HelixHandlerStackingOnly:: +traceBackHelixSeed( Interaction & interaction + , const size_t i1_ + , const size_t i2_) +{ + size_t i1 = i1_ + , i2 = i2_ + , seedStart1, seedEnd1 + , seedStart2, seedEnd2; + + bool traceNotFound = true; + + E_type curE = getHelixSeedE(i1_,i2_); + + size_t bestL1 = getHelixSeedLength1(i1_,i2_); + size_t bestL2 = getHelixSeedLength2(i1_,i2_); + + // No traceback possible for current boundary + if (E_isINF(curE)) { + return; + } + + // Calculate how many base pairs are possible allongside the seed. + // Note: If seedHandler allows unpaired positions this check is not enough, check happens in loop + size_t possibleBasePairs = std::min(std::min(helixSeed.size1()+offset1-i1, helixSeed.size2()+offset2-i2), helixConstraint.getMaxBasePairs())-seedHandler->getConstraint().getBasePairs(); + + // screen over all possible leading and trailing base pair combinations + for (size_t leadingBP=0; traceNotFound + && leadingBP <= possibleBasePairs + && i1 + leadingBP-offset1 < helixSeed.size1() + && i2 + leadingBP-offset2 < helixSeed.size2(); leadingBP++) { + + // check if leading based pairs are possible, otherwise stop computation + if (!energy.areComplementary(i1+leadingBP,i2+leadingBP)) + { + break; + } + + // start positions of the seed + seedStart1 = i1 + leadingBP; + seedStart2 = i2 + leadingBP; + + // Check whether seed is possible for this starting position + if (E_isINF(seedHandler->getSeedE(seedStart1, seedStart2))) { + continue; + } + + // end positions of the seed + seedEnd1 = seedStart1+seedHandler->getSeedLength1(seedStart1,seedStart2)-1; + seedEnd2 = seedStart2+seedHandler->getSeedLength2(seedStart1,seedStart2)-1; + + // Trailing base pairs + for (size_t trailingBP = 0; traceNotFound + && trailingBP <= possibleBasePairs - leadingBP + && seedEnd1+trailingBP-offset1 < helixSeed.size1() + && seedEnd2+trailingBP-offset2 < helixSeed.size2(); trailingBP++) { + + // If trailing base pairs exist and helixE = E_INF -> skip to the next leadingBP + if (trailingBP != 0 && E_isINF(getHelixE(seedEnd1-offset1,seedEnd2-offset2,trailingBP+1))) { + break; + } + + if (E_equal(curE,getHelixE(i1-offset1, i2-offset2, leadingBP+1) + + seedHandler->getSeedE(seedStart1, seedStart2) + + getHelixE(seedEnd1-offset1, seedEnd2-offset2, trailingBP+1))) + { + // length check + if (bestL1 == (leadingBP + seedHandler->getSeedLength1(seedStart1,seedStart2) + trailingBP) + && bestL2 == (leadingBP + seedHandler->getSeedLength2(seedStart1,seedStart2) + trailingBP)) { + + // Trace the first part if existing + if (leadingBP != 0) { + traceBackHelix(interaction, i1-offset1, i2-offset2, leadingBP+1); + interaction.basePairs.push_back(energy.getBasePair(seedStart1, seedStart2)); + } + // Trace the seed + seedHandler->traceBackSeed(interaction,seedStart1,seedStart2); + + // Trace the last part if existing + if (trailingBP != 0) { + interaction.basePairs.push_back(energy.getBasePair(seedEnd1, seedEnd2)); + traceBackHelix(interaction, seedEnd1-offset1, seedEnd2-offset2, trailingBP+1); + } + traceNotFound = false; + } + + } + + } // trailing + } // leading + assert(!traceNotFound); + +} // traceback + +} // namespace diff --git a/src/IntaRNA/HelixHandlerUnpaired.cpp b/src/IntaRNA/HelixHandlerUnpaired.cpp new file mode 100644 index 00000000..a8524cf8 --- /dev/null +++ b/src/IntaRNA/HelixHandlerUnpaired.cpp @@ -0,0 +1,278 @@ +#include "IntaRNA/HelixHandlerUnpaired.h" + +namespace IntaRNA { + +///////////////////////////////////////////////////////////////////////////// + +size_t +HelixHandlerUnpaired:: +fillHelix(const size_t i1min, const size_t i1max, const size_t i2min, const size_t i2max) +{ +#if INTARNA_IN_DEBUG_MODE + if ( i1min > i1max ) throw std::runtime_error("HelixHandlerUnpaired::fillHelix: i1min("+toString(i1min)+") > i1max("+toString(i1max)+")"); + if ( i2min > i2max ) throw std::runtime_error("HelixHandlerUnpaired::fillHelix: i2min("+toString(i2min)+") > i2max("+toString(i2max)+")"); + if ( i1max > energy.size1() ) throw std::runtime_error("HelixHandlerUnpaired::fillHelix: i1max("+toString(i1max)+") > energy.size1("+toString(energy.size1())+")"); + if ( i2max > energy.size2() ) throw std::runtime_error("HelixHandlerUnpaired::fillHelix: i2max("+toString(i2max)+") > energy.size2("+toString(energy.size2())+")"); + if ( helixConstraint.getMinBasePairs() > helixConstraint.getMaxBasePairs() ) + throw std::runtime_error("HelixHandlerUnpaired::fillHelix: bpMin("+toString(helixConstraint.getMinBasePairs()) +") > bpMax("+toString(helixConstraint.getMaxBasePairs())+")"); +#endif + + + helix.resize( i1max-i1min+1, i2max-i2min+1 ); + helixE_rec.resize( HelixIndex({{ + (HelixRecMatrix::index)(helix.size1()) + , (HelixRecMatrix::index)(helix.size2()) + , (HelixRecMatrix::index)(getConstraint().getMaxBasePairs()+1)}}) ); + std::fill( helixE_rec.origin(), helixE_rec.origin() + helixE_rec.num_elements(), std::make_pair(E_INF,0)); + + size_t minBP; + E_type maxE; + // TODO: Fix until better solution is found + // if no seedHandler is given use given values, else use default values to ensure they dont compromise the results of the helixSeed method + if (seedHandler == NULL) { + minBP = helixConstraint.getMinBasePairs(); + maxE = helixConstraint.getMaxE(); + } else { + minBP = 2; + maxE = 999; + } + + // store index offset due to restricted matrix size generation + offset1 = i1min; + offset2 = i2min; + + // temporary variables + size_t i1, i2, curBP, bestBP, u1, u2, j1, j2, hL1, hL2, k1, k2, u1best, u2best, bestL1, bestL2; + E_type curE, bestE; + + size_t helixCountNotInf = 0, helixCount = 0; + + // fill for all start indeices + // in decreasing index order + for (i1=i1max+1; i1-- > i1min;) { + for (i2=i2max+1; i2-- > i2min;) { + // count possible helices + helixCount++; + // init according to no helix interaction + helix(i1-offset1,i2-offset2) = HelixMatrix::value_type( E_INF, 0, 0 ); + + // skip non-complementary left helix boundaries + if (!energy.areComplementary(i1,i2)) { + continue; // go to next helixE index + } + + // Calculate energy for all different numbers of base pairs (bpMin to bpMax) + for (curBP=2; curBP < getConstraint().getMaxBasePairs()+1 + && (i1+curBP-1-offset1) < helix.size1() + && (i2+curBP-1-offset2) < helix.size2(); curBP++) { + + // init current helix energy + curE = E_INF; + bestE = E_INF; + bestL1 = 0; + bestL2 = 0; + + // for feasible unpaired bases + for (u1 = 0; u1 < getConstraint().getMaxIL()+1 && (i1+u1+1 -offset1)< helix.size1(); u1++) { + for (u2 = 0; u2 < getConstraint().getMaxIL()+1 - u1 && (i2+u2+1 -offset2) < helix.size2(); u2++) { + + // get split base pair (right boundaries when curBP = 2) + k1 = i1 + u1 +1; + k2 = i2 + u2 +1; + + // check if split base pair is complementary + if (energy.areComplementary(k1, k2)) { + + // base case: only left and right base pair present + if (curBP == 2) { + // energy for stacking/bulge/interior depending on u1/u2 + curE = energy.getE_interLeft(i1, k1, i2, k2); + // save the best energy among all u1/u2 combinations + if (curE < bestE) { + bestE = curE; + bestL1 = curBP +u1; + bestL2 = curBP +u2; + } + } else { + // check if recursed entry is < E_INF + if (E_isINF(getHelixE(k1 - offset1, k2 - offset2, curBP - 1))) { + continue; // invalid entry -> skip + } + + // check right boundaries + if (i1 + u1 + getHelixLength1(k1-offset1, k2-offset2, curBP-1) -offset1 >= helix.size1() + || i2 + u2 + getHelixLength2(k1-offset1, k2-offset2, curBP-1)-offset2 >= helix.size2()) { + continue; // not within the boundaries -> skip + } + + // update mfe for split at k1,k2 + curE = energy.getE_interLeft(i1, k1, i2, k2) + getHelixE(k1 - offset1, k2 - offset2, curBP - 1); + + // store best energy only + if (curE < bestE) { + bestE = curE; + bestL1 = u1 + getHelixLength1(k1 - offset1, k2 - offset2, curBP - 1)+1; + bestL2 = u2 + getHelixLength2(k1 - offset1, k2 - offset2, curBP - 1)+1; + + } + } // more than two base pairs + } // (j1, j2) complementary + + + } // u2 + } // u1 + // store helix energy and length + setHelixPair(i1 - offset1, i2 - offset2, curBP, bestE, encodeHelixLength(bestL1, bestL2)); + + } // curBP + + // TODO: Possible runtime improvement by checking this while calculating + // find best unpaired combination in helx for i1,i2,bp + bestBP = 0; + bestE = E_INF; + + // Calculate energy for all different numbers of base pairs + // Ensuring minimum number of base pairs here + for (curBP = minBP; curBP < helixConstraint.getMaxBasePairs() + 1 + && (i1 + curBP - 1 - offset1) < helix.size1() + && (i2 + curBP - 1 - offset2) < helix.size2(); curBP++) { + + if (E_isINF(getHelixE(i1-offset1, i2-offset2, curBP))) { + continue; + } + // get right helix boundaries + j1 = i1 + getHelixLength1(i1-offset1, i2-offset2, curBP)-1; + j2 = i2 + getHelixLength2(i1-offset1, i2-offset2, curBP)-1; + + if (energy.getED1(i1, j1) > helixConstraint.getMaxED() + || energy.getED2(i2, j2) > helixConstraint.getMaxED()) { + continue; + } + + // get overall interaction energy + curE = energy.getE(i1, j1, i2, j2, getHelixE(i1 - offset1, i2 - offset2, curBP)) + energy.getE_init(); + + // skip if ED boundary exceeded and ED value computation is disabled + if (helixConstraint.useNoED()) + { + curE -= (energy.getED1(i1,j1) + energy.getED2(i2,j2)); + } + + // check if better than what is known so far + if (curE < bestE && !E_equal(curE, bestE)) { + bestE = curE; + bestBP = curBP; + } + } // curBP + + // reduce bestE to hybridization energy only (init+loops) + if (E_isNotINF(bestE)) { + // overwrite all helices with too high energy -> infeasible start interactions + if (bestE > maxE) { + bestE = E_INF; + } else { + // get helix hybridization loop energies only + bestE = getHelixE(i1 - offset1, i2 - offset2, bestBP); + // count true helix + helixCountNotInf++; + } + } + + + // store best (mfe) helix for all u1/u2 + helix(i1 - offset1, i2 - offset2) = HelixMatrix::value_type(bestE, + E_isINF(bestE) ? 0 : encodeHelixLength( + getHelixLength1(i1-offset1, i2-offset2, bestBP), + getHelixLength2(i1-offset1, i2-offset2, bestBP)), + E_isINF(bestE) ? 0: bestBP); + + + } // i2 + } // i1 + +#if INTARNA_MULITHREADING + #pragma omp critical(intarna_omp_logOutput) +#endif + { VLOG(2) << "valid helices = " << helixCountNotInf << " (" << (helixCountNotInf/helixCount) << "% of start index combinations)"; } + + return helixCountNotInf; +} + +////////////////////////////////////////////////////////////////////////// + +void +HelixHandlerUnpaired:: +traceBackHelix( Interaction & interaction + , const size_t i1_ + , const size_t i2_ + , const size_t bp) +{ + // get boundaries + size_t i1 = i1_ + , i2 = i2_ + , u1, u2 + , k1, k2 + ; + + // get energy of provided seed + E_type curE = getHelixE(i1_,i2_,bp); + // trace helices + // trace each helix base pair (excluding right most) + for ( size_t curBP=1+bp; curBP-- > 2; ) { + // base case: only left and right base pair present + if (curBP==2) { + // add left base pair if not left helix boundary + if (i1 != i1_) { + interaction.basePairs.push_back( energy.getBasePair( i1+offset1, i2+offset2 ) ); + } + + } else { + // split helix recursively into all possible leading interior loops + // i1 .. i1+u1+1 .. j1 + // i2 .. i2+u2+1 .. j2 + bool traceNotFound = true; + for (u1=0; u1 < getConstraint().getMaxIL()+1 && traceNotFound; u1++) { + for (u2=0; u2 < getConstraint().getMaxIL()+1-u1 && traceNotFound; u2++) { + k1 = i1+u1+1; + k2 = i2+u2+1; + + // check right boundary + if ( k1 >= helix.size1() || k2 >= helix.size2()) { + continue; + } + + // check if valid trace + if ( E_isNotINF( getHelixE( k1, k2, curBP-1) ) ) { + + // check if correct trace + if ( E_equal( curE, energy.getE_interLeft(i1+offset1, k1+offset1, i2+offset2, k2+offset2) + + getHelixE( k1, k2, curBP-1 )) ) + { + // store left base pair if not left helix boundary + if (i1 != i1_) { + interaction.basePairs.push_back( energy.getBasePair(i1+offset1, i2+offset2) ); + } + + // store next energy value in trace + curE = getHelixE( k1, k2, curBP-1 ); + // reset for next trace step + i1 = k1; + i2 = k2; + + // mark trace step done + traceNotFound = false; + } + } + + } // u2 + } // u1 + assert( !traceNotFound ); // sanity check + } // more than two base pairs + + } // bp +} + + +} // namespace + + diff --git a/src/IntaRNA/HelixHandlerUnpaired.h b/src/IntaRNA/HelixHandlerUnpaired.h new file mode 100644 index 00000000..aec2f879 --- /dev/null +++ b/src/IntaRNA/HelixHandlerUnpaired.h @@ -0,0 +1,638 @@ + +#ifndef INTARNA_HELIXHANDLERUNPAIRED_H_ +#define INTARNA_HELIXHANDLERUNPAIRED_H_ + +#include "IntaRNA/InteractionEnergy.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandler.h" + +#include + +#include + +namespace IntaRNA { + +/** + * Handler to provide helix interaction information that allows for minor + * interior loops with a constraint maximal number of unpaired bases (per loop) + * + * @author Rick Gelhausen + */ +class HelixHandlerUnpaired : public HelixHandler { + +public: + + //! 3D matrix type to hold the mfe energies for helix interactions + //! of the ranges i1..(i1+bpMax-1) and i2..(i2+bpMax-1), with + //! i1,i2 = the start index of the helix in seq1/2 + //! bp = the maximal number of base pairs within the helix (>=bpMin) + //! using the index [i1][i2][bp] or a HelixIndex object + typedef boost::multi_array,3> HelixRecMatrix; + + //! defines the helix data {{ i1, i2, bp }} to acces elements of + //! the HelixRecMatrix + typedef boost::array HelixIndex; + + //! matrix to store the helix information for each helix left side (i1, i2) + //! it holds both the energy (first) as well as the length of the helix using + //! the length combination of encodeHelixLength() + //! The third entry is the bestBP, i.e. the optimal number of bases for this left boundary + typedef boost::numeric::ublas::matrix< std::tuple > HelixMatrix; + typedef boost::numeric::ublas::matrix< std::pair > HelixSeedMatrix; + + +public: + + /** + * Constructor + * @param energy the energy function to be used + */ + HelixHandlerUnpaired( + const InteractionEnergy & energy + , const HelixConstraint & helixConstraint + , SeedHandler * const seedHandler = NULL + ); + + /** + * destructor + */ + virtual ~HelixHandlerUnpaired(); + + /** + * Access to the underlying helix constraint + * @return the used helix constraint + */ + virtual + const HelixConstraint& + getConstraint() const; + + /** + * Access to the underlying interaction energy function + * @return the used energy function + */ + virtual + const InteractionEnergy& + getInteractionEnergy() const; + + /** + * Compute the helix matrix for the given interval boundaries + * @param i1 the first index of seq1 that might interact + * @param j1 the last index of seq1 that might interact + * @param i2 the first index of seq2 that might interact + * @param j2 the last index of seq2 that might interact + * @return + */ + virtual + size_t + fillHelix( const size_t i1, const size_t j1, const size_t i2, const size_t j2 ); + + /** + * Computes the helix information for the given interval boundaries for helices containing a seed + * @param i1 the first index of seq1 that might interact + * @param j1 the last index of seq1 that might interact + * @param i2 the first index of seq2 that might interact + * @param j2 the last index of seq2 that might interact + * @return the number of potential helix interactions + */ + virtual + size_t + fillHelixSeed(const size_t i1, const size_t j1, const size_t i2, const size_t j2); + + /** + * Identifies the base pairs of the mfe helix interaction starting at i1,i2 + * and writes them to the provided container + * + * Note: the right most base pair is excluded! + * + * @param interaction the container to add the base pairs too + * @param i1 the start of the helix in seq1 + * @param i2 the start of the helix in seq2 + */ + virtual + void + traceBackHelix( Interaction & interaction, const size_t i1, const size_t i2); + + /** + * Identifies the base pairs of the mfe helix interaction starting at i1,i2 + * and writes them to the provided container + * + * Note: the right most base pair is excluded! + * + * @param interaction the container to add the base pairs too + * @param i1 the start of the helix in seq1 + * @param i2 the start of the helix in seq2 + */ + virtual + void + traceBackHelixSeed( Interaction & interaction, const size_t i1, const size_t i2); + + /** + * Access to the mfe of any helix with left-most base pair (i1, i2) + * @param i1 the left most interaction base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the mfe of any helix starting at (i1,i2) or E_INF if none possible + */ + virtual + E_type + getHelixE( const size_t i1, const size_t i2 ) const; + + /** + * Access to the mfe of any helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interaction base of seq2 + * @return the mfe of any heli starting at (i1,i2) or E_INF if none possible + */ + virtual + E_type + getHelixSeedE( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq1 of the mfe helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength1( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq2 of the mfe helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength2( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq1 of the mfe helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @param bp the number of base pairs allowed + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength1( const size_t i1, const size_t i2, const size_t bp ) const; + + /** + * Access to the length in seq2 of the mfe helix with left-most base pair (i1,i2) + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @param bp the number of base pairs allowed + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixLength2( const size_t i1, const size_t i2, const size_t bp ) const; + + /** + * Access to the length in seq1 of the mfe helix with left-most base pair (i1,i2) containing a seed + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq1 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixSeedLength1( const size_t i1, const size_t i2 ) const; + + /** + * Access to the length in seq2 of the mfe helix with left-most base pair (i1,i2) containing a seed + * @param i1 the left most interacting base of seq1 + * @param i2 the left most interacting base of seq2 + * @return the length in seq2 of the mfe helix starting at (i1,i2) or 0 if none possible + */ + virtual + size_t + getHelixSeedLength2( const size_t i1, const size_t i2 ) const; + + /** + * Set the seedHandler in order to compute helixSeed + * @param seedHandler seedHandler to be used in the helix computation + */ + void setSeedHandler(SeedHandler & seedHandler); + +protected: + + /** + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @return get optimal number of bases for this left boundary + */ + size_t + getBestBP( const size_t i1, const size_t i2) const; + + /** + * Provides the helix energy during recursion + * + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @param bp the number of base pairs + * @param u1 the number of unpaired bases within seq 1 + * @param u2 the number of unpaired bases within seq 2 + * + * @return the energy of the according helix + */ + E_type + getHelixE( const size_t i1, const size_t i2, const size_t bp); + + + /** + * Fills the helix energy during recursion + * + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @param bp the number of base pairs + * @param u1 the number of unpaired bases within seq 1 + * @param u2 the number of unpaired bases within seq 2 + * @param E the energy value to be set + */ + void + setHelixPair( const size_t i1, const size_t i2, const size_t bp + , const E_type E, const size_t length ); + + + /** + * Encodes the helix lengths into one number + * @param l1 the length of the helix in seq1 + * @param l2 the length of the helix in seq2 + * @return the combined encoding = (l1 + l2*(max_l1+1)) + */ + size_t + encodeHelixLength( const size_t l1, const size_t l2 ) const; + + /** + * Decodes the length of the helix within sequence 1 from an encoding + * generated with encodeHelixLength() + * @param code the lengths encoding + * @return the length of the helix in seq1 + */ + size_t + decodeHelixLength1( const size_t code ) const; + + + /** + * Decodes the length of the helix within sequence 2 from an encoding + * generated with encodeHelixLength() + * @param code the lengths encoding + * @return the length of the helix in seq2 + */ + size_t + decodeHelixLength2( const size_t code ) const; + + /** + * Encodes the seed lengths into one number + * @param l1 the length of the seed in seq1 + * @param l2 the length of the seed in seq2 + * @return the combined encoding = (l1 + l2*(max_l1+1)) + */ + size_t + encodeHelixSeedLength( const size_t l1, const size_t l2 ) const; + + /** + * Decodes the length of the seed within sequence 1 from an encoding + * generated with encodeHelixSeedLength() + * @param code the lengths encoding + * @return the length of the seed in seq1 + */ + size_t + decodeHelixSeedLength1( const size_t code ) const; + + /** + * Decodes the length of the seed within sequence 2 from an encoding + * generated with encodeHelixSeedLength() + * @param code the lengths encoding + * @return the length of the seed in seq2 + */ + size_t + decodeHelixSeedLength2( const size_t code ) const; + + /** + * Fills a given interaction with the according + * hybridizing base pairs of the provided helix interaction + * + * @param interaction IN/OUT the interaction to fill + * @param i1 the helix left end in seq 1 (index including offset) + * @param i2 the helix left end in seq 2 (index including offset) + * @param bp the number of base pairs (bestBP) + * @param u1 the number of unpaired bases within seq 1 + * @param u2 the number of unpaired bases within seq 2 + */ + void + traceBackHelix( Interaction & interaction + , const size_t i1, const size_t i2, const size_t bp); + +protected: + + //! the used energy function + const InteractionEnergy & energy; + + //! the helix constraint to be applied + const HelixConstraint & helixConstraint; + + //! the recursion data for the computation of a helix interaction + //! bp: the number of bases + //! i1..(i1+bp-1) and i2..(i2+bp-1) + //! using the indexing [i1][i2][bp] + HelixRecMatrix helixE_rec; + + //! the helix mfe information for helix starting at (i1, i2) + HelixMatrix helix; + + //! the helix mfe information for helix with seed starting at (i1, i2) + HelixSeedMatrix helixSeed; + + //! offset for seq1 indices for the current matrices + size_t offset1; + + //! offset for seq2 indices for the current matrices + size_t offset2; + + //! used seedHandler + SeedHandler * seedHandler; +}; + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// + +inline +HelixHandlerUnpaired::HelixHandlerUnpaired( + const InteractionEnergy & energy + , const HelixConstraint & helixConstraint + , SeedHandler * const seedHandler +) + : + energy(energy) + , helixConstraint(helixConstraint) + , seedHandler(seedHandler) + , helixE_rec( HelixIndex({{ 0, 0, 0 }})) + , helix() + , helixSeed() + , offset1(0) + , offset2(0) +{ + if (seedHandler != NULL) { + setSeedHandler(*seedHandler); + } +} + +//////////////////////////////////////////////////////////////////////////// + +inline +HelixHandlerUnpaired::~HelixHandlerUnpaired() +{ +} + +//////////////////////////////////////////////////////////////////////////// + +inline +const InteractionEnergy& +HelixHandlerUnpaired:: +getInteractionEnergy() const +{ + return energy; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +const HelixConstraint& +HelixHandlerUnpaired:: +getConstraint() const +{ + return helixConstraint; +} +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getBestBP(const size_t i1, const size_t i2) const +{ + return std::get<2>(helix(i1-offset1, i2-offset2)); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerUnpaired::traceBackHelix(Interaction &interaction + , const size_t i1 + , const size_t i2 +) +{ +#if INTARNA_IN_DEBUG_MODE + if ( i1 < offset1 ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i1="+toString(i1)+") is out of range (>"+toString(offset1)+")"); + if ( i1-offset1 >= helix.size1() ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i1="+toString(i1)+") is out of range (<"+toString(helix.size1()+offset1)+")"); + if ( i2 < offset2 ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i2="+toString(i2)+") is out of range (>"+toString(offset2)+")"); + if ( i2-offset2 >= helix.size2() ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i2="+toString(i2)+") is out of range (<"+toString(helix.size2()+offset2)+")"); + if ( E_isINF( getHelixE(i1,i2) ) ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i1="+toString(i1)+",i2="+toString(i2)+") no helix known (E_INF)"); + if ( i1+getHelixLength1(i1,i2)-1-offset1 >= helix.size1() ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i1="+toString(i1)+") helix length ("+toString(getHelixLength1(i1,i2))+") exceeds of range (<"+toString(helix.size1()+offset1)+")"); + if ( i2+getHelixLength2(i1,i2)-1-offset2 >= helix.size2() ) throw std::runtime_error("HelixHandlerUnpaired::traceBackHelix(i2="+toString(i2)+") helix length ("+toString(getHelixLength2(i1,i2))+") exceeds of range (<"+toString(helix.size2()+offset2)+")"); +#endif + // get number of base pairs allowed within the helix + const size_t bestBP = getBestBP(i1,i2); + + // trace back the according helix + traceBackHelix( interaction, i1-offset1, i2-offset2, bestBP); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerUnpaired:: +getHelixE(const size_t i1, const size_t i2) const +{ + return std::get<0>(helix(i1-offset1, i2-offset2)); +} + + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerUnpaired:: +getHelixSeedE(const size_t i1, const size_t i2) const +{ + return helixSeed(i1-offset1, i2-offset2).first; +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getHelixLength1(const size_t i1, const size_t i2) const +{ + return decodeHelixLength1(std::get<1>(helix(i1-offset1, i2-offset2))); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getHelixLength2(const size_t i1, const size_t i2) const +{ + return decodeHelixLength2(std::get<1>(helix(i1-offset1, i2-offset2))); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getHelixLength1(const size_t i1, const size_t i2, const size_t bp) const { +// if no base pair is given return 0 in order to simplify helixHandlerSeed computation. + if (bp <= 1) { + return 0; + } else { + return decodeHelixLength1(helixE_rec(HelixIndex( + {{(HelixRecMatrix::index) i1, (HelixRecMatrix::index) i2, (HelixRecMatrix::index) bp}})).second); + } +} +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getHelixLength2(const size_t i1, const size_t i2, const size_t bp) const +{ + // if no base pair is given return 0 in order to simplify helixHandlerSeed computation. + if (bp <= 1) { + return 0; + } else { + return decodeHelixLength2(helixE_rec(HelixIndex( + {{(HelixRecMatrix::index) i1, (HelixRecMatrix::index) i2, (HelixRecMatrix::index) bp}})).second); + } +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getHelixSeedLength1(const size_t i1, const size_t i2) const +{ + return decodeHelixSeedLength1(helixSeed(i1-offset1, i2-offset2).second); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +getHelixSeedLength2(const size_t i1, const size_t i2) const +{ + return decodeHelixSeedLength2(helixSeed(i1-offset1, i2-offset2).second); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +E_type +HelixHandlerUnpaired:: +getHelixE(const size_t i1, const size_t i2, const size_t bp) +{ + // if no base pair is given return 0 in order to simplify helixHandlerSeed computation. + if (bp <= 1) { + return 0; + } else { + return helixE_rec(HelixIndex({{(HelixRecMatrix::index) i1, (HelixRecMatrix::index) i2, (HelixRecMatrix::index) bp}})).first; + } +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerUnpaired:: +setHelixPair(const size_t i1, const size_t i2, const size_t bp, const E_type E, const size_t length) +{ + helixE_rec( HelixIndex({{ + (HelixRecMatrix::index) i1 + , (HelixRecMatrix::index) i2 + , (HelixRecMatrix::index) bp}}) ) = std::make_pair(E, E_isINF(E) ? 0 : length); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +encodeHelixLength(const size_t l1, const size_t l2) const +{ + return l1 + l2 * (helixConstraint.getMaxLength1()+1); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +decodeHelixLength1(const size_t code) const +{ + return code % (helixConstraint.getMaxLength1()+1); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +decodeHelixLength2(const size_t code) const +{ + return code / (helixConstraint.getMaxLength1()+1); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +encodeHelixSeedLength(const size_t l1, const size_t l2) const +{ + return l1 + l2 * (helixConstraint.getMaxLength1()+ seedHandler->getConstraint().getMaxLength1()+1); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +decodeHelixSeedLength1(const size_t code) const +{ + return code % (helixConstraint.getMaxLength1()+ seedHandler->getConstraint().getMaxLength1()+1); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +size_t +HelixHandlerUnpaired:: +decodeHelixSeedLength2(const size_t code) const +{ + return code / (helixConstraint.getMaxLength1()+ seedHandler->getConstraint().getMaxLength1()+1); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void +HelixHandlerUnpaired:: +setSeedHandler(SeedHandler & seedHandler) { + this->seedHandler = &seedHandler; +} + + + +/////////////////////////////////////////////////////////////////////////// + +} // namespace + +#endif //INTARNA_HELIXHANDLERUNPAIRED_H_ diff --git a/src/IntaRNA/HelixHandlerUnpairedSeed.cpp b/src/IntaRNA/HelixHandlerUnpairedSeed.cpp new file mode 100644 index 00000000..1c83e8b1 --- /dev/null +++ b/src/IntaRNA/HelixHandlerUnpairedSeed.cpp @@ -0,0 +1,316 @@ +#include "IntaRNA/HelixHandlerUnpaired.h" + +namespace IntaRNA { + +///////////////////////////////////////////////////////////////////////////// + +size_t +HelixHandlerUnpaired:: +fillHelixSeed(const size_t i1min, const size_t i1max, const size_t i2min, const size_t i2max) +{ + + helixSeed.resize( i1max-i1min+1, i2max-i2min+1 ); + + // store index offset due to restricted matrix size generation + offset1 = i1min; + offset2 = i2min; + + // temporary variables + size_t i1, i2, seedStart1, seedStart2, seedEnd1, seedEnd2, j1, j2, bestL1, bestL2, possibleBasePairs; + size_t helixCountNotInf = 0, helixCount = 0; + + E_type curE_withED, curE, bestE_withED, bestE; + + // fill for all start indices + // in decreasing index order + for (i1=i1max+1; i1-- > i1min;) { + for (i2=i2max+1; i2-- > i2min;) { + + // count possible helices + helixCount++; + + // init according to no helix interaction + helixSeed(i1 - offset1, i2 - offset2) = HelixSeedMatrix::value_type(E_INF, 0); + + // skip non-complementary left helix boundaries + if (!energy.areComplementary(i1, i2)) { + continue; // go to next helixSeedE index + } + + // Check if a seed can fit and calculate how many base pairs are theoretically possible + // Note: If seedHandler allows unpaired positions this check is not enough, check happens in loop + if (std::min(helixSeed.size1()-i1+offset1, helixSeed.size2()-i2+offset2) < seedHandler->getConstraint().getBasePairs()) { + continue; + } else { + // Seed fits, check how many bases are possible around + possibleBasePairs = std::min(std::min(helixSeed.size1()-i1+offset1, helixSeed.size2()-i2+offset2), helixConstraint.getMaxBasePairs())-seedHandler->getConstraint().getBasePairs(); + } + + // Initialuze variables + curE = E_INF; + curE_withED = E_INF; + bestE = E_INF; + bestE_withED = E_INF; + bestL1 = 0; + bestL2 = 0; + + // screen over all possible leading and trailing base pair combinations + for (size_t leadingBP=possibleBasePairs+1; leadingBP-- > 0;) { + + // check if boundaries are met + if ((i1+leadingBP-offset1) >= helixSeed.size1() && (i2+leadingBP-offset2) >= helixSeed.size2()) { + continue; + } + + // If leading base pairs exist and helixE = E_INF -> skip to the next leadingBP + if (leadingBP != 0 && E_isINF(getHelixE(i1-offset1,i2-offset2,leadingBP+1))) { + continue; + } + + // the start positions for the seed + if (leadingBP != 0) { + seedStart1 = i1 + getHelixLength1(i1 - offset1, i2 - offset2, leadingBP + 1) - 1; + seedStart2 = i2 + getHelixLength2(i1 - offset1, i2 - offset2, leadingBP + 1) - 1; + } else { + seedStart1 = i1; + seedStart2 = i2; + } + + // check whether the right boundaries are broken + if (seedStart1-offset1 >= helixSeed.size1() || seedStart2-offset2 >= helixSeed.size2()) { + continue; + } + + // If no seed is possible here, skip to next leading base pair number + if (E_isINF(seedHandler->getSeedE(seedStart1, seedStart2))) { + continue; + } + + // the end positions of the seed + seedEnd1 = seedStart1+seedHandler->getSeedLength1(seedStart1, seedStart2)-1; + seedEnd2 = seedStart2+seedHandler->getSeedLength2(seedStart1, seedStart2)-1; + + // Run over all trailing base pairs + for (size_t trailingBP=possibleBasePairs+1 - leadingBP; trailingBP-- > 0;) { + + // check if boundaries are met + if ((seedEnd1+trailingBP-offset1) >= helixSeed.size1() && (seedEnd2+trailingBP-offset2) >= helixSeed.size2()) { + continue; + } + // If trailing base pairs exist and helixE = E_INF -> skip to the next leadingBP + if (trailingBP != 0 && E_isINF(getHelixE(seedEnd1 - offset1, seedEnd2 - offset2, trailingBP + 1))) { + continue; + } + + // right boundary + if (trailingBP != 0) { + j1 = seedEnd1+getHelixLength1(seedEnd1-offset1,seedEnd2-offset2,trailingBP+1)-1; + j2 = seedEnd2+getHelixLength2(seedEnd1-offset1,seedEnd2-offset2,trailingBP+1)-1; + } else { + j1 = seedEnd1; + j2 = seedEnd2; + } + + // check whether the right boundaries are broken + if (j1-offset1 >= helixSeed.size1() + || j2-offset2 >= helixSeed.size2()) { + continue; + } + + // ensure that ED-values are within the boundaries (default 999) + if (energy.getED1(i1, j1) > helixConstraint.getMaxED() + || energy.getED2(i2, j2) > helixConstraint.getMaxED()) { + continue; + } + + + // energy without contributions + curE = getHelixE(i1 - offset1, i2 - offset2, leadingBP + 1) + + seedHandler->getSeedE(seedStart1, seedStart2) + + getHelixE(seedEnd1 - offset1, seedEnd2 - offset2, trailingBP + 1); + // energy value + curE_withED = energy.getE(i1,j1,i2,j2, curE) + energy.getE_init(); + + // If no ED-values are wanted, remove them + if (helixConstraint.useNoED()) + curE_withED -= (energy.getED1(i1,j1) + energy.getED2(i2, j2)); + + if ((curE_withED < bestE_withED || E_equal(curE_withED, bestE_withED)) + && leadingBP + trailingBP+ seedHandler->getConstraint().getBasePairs() >= getConstraint().getMinBasePairs()) { + + bestE_withED = curE_withED; + bestE = curE; + // current lengths + bestL1 = seedHandler->getSeedLength1(seedStart1, seedStart2); + bestL2 = seedHandler->getSeedLength2(seedStart1, seedStart2); + + // Add leadingBP length contribution + if (leadingBP != 0) { + bestL1 += getHelixLength1(i1-offset1, i2-offset2, leadingBP+1)-1; + bestL2 += getHelixLength2(i1-offset1, i2-offset2, leadingBP+1)-1; + } + // Add trailingBP length contribution + if (trailingBP != 0) { + bestL1 += getHelixLength1(seedEnd1-offset1, seedEnd2-offset2, trailingBP+1)-1; + bestL2 += getHelixLength2(seedEnd1-offset1, seedEnd2-offset2, trailingBP+1)-1; + } + + } + } // trailingBP + } // leadingBP + + // Ensures that the helixCount is only increased for the mfe helix. + if (E_isNotINF(bestE_withED)) { + // overwrite all helices with too high energy -> infeasible start interactions + if (bestE_withED > helixConstraint.getMaxE()) { + bestE = E_INF; + } else { + helixCountNotInf++; + } + } + + // set best energy and according lengths, or (INF, 0) if not valid + helixSeed(i1-offset1, i2-offset2) = HelixSeedMatrix::value_type(bestE, E_isINF(bestE) ? 0: encodeHelixSeedLength(bestL1,bestL2)); + + + } // i2 + } // i1 + + return helixCountNotInf; +} + +////////////////////////////////////////////////////////////////////////// + +void +HelixHandlerUnpaired:: +traceBackHelixSeed( Interaction & interaction + , const size_t i1_ + , const size_t i2_) +{ + + size_t i1 = i1_ + , i2 = i2_ + , seedStart1, seedEnd1 + , seedStart2, seedEnd2 + , j1, j2 + , curL1, curL2; + + bool traceNotFound = true; + + E_type curE = getHelixSeedE(i1_,i2_); + + size_t bestL1 = getHelixSeedLength1(i1_, i2_); + size_t bestL2 = getHelixSeedLength2(i1_, i2_); + // No traceback possible for current boundary + if (E_isINF(curE)) { + return; + } + + // Calculate how many base pairs are possible allongside the seed. + // Note: If seedHandler allows unpaired positions this check is not enough, check happens in loop + size_t possibleBasePairs = std::min(std::min(helixSeed.size1()-i1 +offset1, helixSeed.size2()-i2+offset2), helixConstraint.getMaxBasePairs())-seedHandler->getConstraint().getBasePairs(); + + // screen over all possible leading and trailing base pair combinations + for (size_t leadingBP=0; traceNotFound + && leadingBP <= possibleBasePairs + && i1 + leadingBP-offset1 < helixSeed.size1() + && i2 + leadingBP-offset2 < helixSeed.size2(); leadingBP++) { + + // If leading base pairs exist and helixE = E_INF -> skip to the next leadingBP + if (leadingBP != 0 && E_isINF(getHelixE(i1-offset1,i2-offset2,leadingBP+1))) { + continue; + } + + // the start positions for the seed + if (leadingBP != 0) { + seedStart1 = i1 + getHelixLength1(i1 - offset1, i2 - offset2, leadingBP + 1) - 1; + seedStart2 = i2 + getHelixLength2(i1 - offset1, i2 - offset2, leadingBP + 1) - 1; + } else { + seedStart1 = i1; + seedStart2 = i2; + } + // check whether the right boundaries are broken + if (seedStart1-offset1 >= helixSeed.size1() || seedStart2-offset2 >= helixSeed.size2()) { + continue; + } + + // Check whether seed is possible for this starting position + if (E_isINF(seedHandler->getSeedE(seedStart1, seedStart2))) { + continue; + } + + // end positions of the seed + seedEnd1 = seedStart1 + seedHandler->getSeedLength1(seedStart1, seedStart2) - 1; + seedEnd2 = seedStart2 + seedHandler->getSeedLength2(seedStart1, seedStart2) - 1; + + // Trailing base pairs + for (size_t trailingBP = 0; traceNotFound + && trailingBP <= possibleBasePairs - leadingBP + && (seedEnd1 + trailingBP - offset1) < helixSeed.size1() + && (seedEnd2 + trailingBP - offset2) < helixSeed.size2(); trailingBP++) { + + + // If trailing base pairs exist and helixE = E_INF -> skip to the next leadingBP + if (trailingBP != 0 && E_isINF(getHelixE(seedEnd1 - offset1, seedEnd2 - offset2, trailingBP + 1))) { + continue; + } + + // right boundary + if (trailingBP != 0) { + j1 = seedEnd1+getHelixLength1(seedEnd1-offset1,seedEnd2-offset2,trailingBP+1)-1; + j2 = seedEnd2+getHelixLength2(seedEnd1-offset1,seedEnd2-offset2,trailingBP+1)-1; + } else { + j1 = seedEnd1; + j2 = seedEnd2; + } + + // check whether the right boundaries are broken + if (j1-offset1 >= helixSeed.size1() + || j2-offset2 >= helixSeed.size2()) { + continue; + } + + // check whether trace is found + if (E_equal(curE, getHelixE(i1 - offset1, i2 - offset2, leadingBP + 1) + + seedHandler->getSeedE(seedStart1, seedStart2) + + getHelixE(seedEnd1 - offset1, seedEnd2 - offset2, trailingBP + 1))) { + + // calculate the current length + curL1 = seedHandler->getSeedLength1(seedStart1, seedStart2); + curL2 = seedHandler->getSeedLength2(seedStart1, seedStart2); + + // Add leadingBP length contribution + if (leadingBP != 0) { + curL1 += getHelixLength1(i1-offset1, i2-offset2, leadingBP+1)-1; + curL2 += getHelixLength2(i1-offset1, i2-offset2, leadingBP+1)-1; + } + // Add trailingBP length contribution + if (trailingBP != 0) { + curL1 += getHelixLength1(seedEnd1-offset1, seedEnd2-offset2, trailingBP+1)-1; + curL2 += getHelixLength2(seedEnd1-offset1, seedEnd2-offset2, trailingBP+1)-1; + } + + // ensure that the lengths are correct + if ( curL1 == bestL1 && curL2 == bestL2 ) { + // Trace the leading part if existing + if (leadingBP != 0) { + traceBackHelix(interaction, i1 - offset1, i2 - offset2, leadingBP + 1); + interaction.basePairs.push_back(energy.getBasePair(seedStart1, seedStart2)); + } + // Trace the seed + seedHandler->traceBackSeed(interaction, seedStart1, seedStart2); + // Trace the trailing part if existing + if (trailingBP != 0) { + interaction.basePairs.push_back(energy.getBasePair(seedEnd1, seedEnd2)); + traceBackHelix(interaction, seedEnd1 - offset1, seedEnd2 - offset2, trailingBP + 1); + } + traceNotFound = false; + } + } + } // trailing + } // leading + assert(!traceNotFound); + +} // traceback + +} // namespace \ No newline at end of file diff --git a/src/IntaRNA/Makefile.am b/src/IntaRNA/Makefile.am index 97273467..23a3e8ef 100644 --- a/src/IntaRNA/Makefile.am +++ b/src/IntaRNA/Makefile.am @@ -29,6 +29,10 @@ libIntaRNA_a_HEADERS = \ AccessibilityFromStream.h \ AccessibilityVrna.h \ AccessibilityBasePair.h \ + HelixHandler.h \ + HelixHandlerIdxOffset.h \ + HelixHandlerStackingOnly.h \ + HelixHandlerUnpaired.h \ IndexRange.h \ IndexRangeList.h \ Interaction.h \ @@ -60,6 +64,8 @@ libIntaRNA_a_HEADERS = \ PredictorMfe2dSeed.h \ PredictorMfe2dHeuristic.h \ PredictorMfe2dHeuristicSeed.h \ + PredictorMfe2dHelixHeuristic.h \ + PredictorMfe2dHelixHeuristicSeed.h \ PredictorMfe4d.h \ PredictorMfe4dSeed.h \ ReverseAccessibility.h \ @@ -81,6 +87,11 @@ libIntaRNA_a_SOURCES = \ AccessibilityFromStream.cpp \ AccessibilityVrna.cpp \ AccessibilityBasePair.cpp \ + HelixHandler.cpp \ + HelixHandlerStackingOnly.cpp \ + HelixHandlerStackingOnlySeed.cpp \ + HelixHandlerUnpaired.cpp \ + HelixHandlerUnpairedSeed.cpp \ IndexRange.cpp \ IndexRangeList.cpp \ Interaction.cpp \ @@ -108,6 +119,8 @@ libIntaRNA_a_SOURCES = \ PredictorMfe2dSeed.cpp \ PredictorMfe2dHeuristic.cpp \ PredictorMfe2dHeuristicSeed.cpp \ + PredictorMfe2dHelixHeuristic.cpp \ + PredictorMfe2dHelixHeuristicSeed.cpp \ PredictorMfe4d.cpp \ PredictorMfe4dSeed.cpp \ ReverseAccessibility.cpp \ diff --git a/src/IntaRNA/OutputHandlerText.cpp b/src/IntaRNA/OutputHandlerText.cpp index 2a81ed99..7074dd9a 100644 --- a/src/IntaRNA/OutputHandlerText.cpp +++ b/src/IntaRNA/OutputHandlerText.cpp @@ -151,6 +151,8 @@ add( const Interaction & i ) Interaction::PairingVec::const_iterator curBP = i.basePairs.begin(); size_t loop1=0, loop2=0, loop=0, interactionLength = 1; for (++curBP; curBP != i.basePairs.end(); ++curBP, ++leftBP) { + // handle duplicated BPs (might happen due to explicit seeds containing only a single bp) + if (*curBP == *leftBP) {continue;} // handle internal loop region // get specific loop lengths loop1 = curBP->first - leftBP->first -1; @@ -308,10 +310,11 @@ add( const Interaction & i ) <<" + E(dangleRight) = "< 0;) { + for (i2=hybridE.size2(); i2-- > 0;) { +// LOG(DEBUG) << "i1, i2: " << i1 << " " << i2; + // direct cell access + curCell = &(hybridE(i1,i2)); + + // check if left side can pair + if (E_isINF(curCell->E)) { + continue; + } + + // E_init initialization + curCellEtotal = energy.getE(i1,curCell->j1,i2,curCell->j2,curCell->E); + + // check if helix is possible for this left boundary + if ( E_isNotINF( helixHandler.getHelixE(i1,i2) ) ) { + // get right extension + h1 = helixHandler.getHelixLength1(i1,i2)-1; assert(i1+h1 < hybridE.size1()); + h2 = helixHandler.getHelixLength2(i1,i2)-1; assert(i2+h2 < hybridE.size2()); + +// LOG(DEBUG) << "h1, h2: " << h1 << " " << h2; + curE = helixHandler.getHelixE(i1,i2) + energy.getE_init(); + +// LOG(DEBUG) << "Case 1: curE " << curE; + // check if this combination yields better energy + curEtotal = energy.getE(i1, i1+h1, i2, i2+h2, curE); + if ( curEtotal < curCellEtotal ) + { + // TODO: Check this "right" boundary + // update current best for this left boundary + // set right boundary + curCell->j1 = i1+h1; + curCell->j2 = i2+h2; + // set new energy + curCell->E = curE; + // store total energy to avoid recomputation + curCellEtotal = curEtotal; + } + + /////////// extend stacking with bulge and further interaction + + // iterate over all loop sizes w1 (seq1) and w2 (seq2) (minus 1) + for (w1=1; w1-1 <= energy.getMaxInternalLoopSize1() && i1+h1+w1E)) { + continue; + } + + // check if interaction length is within boundary + if ( (rightExt->j1 +1 -i1) > energy.getAccessibility1().getMaxLength() + || (rightExt->j2 +1 -i2) > energy.getAccessibility2().getMaxLength() ) + { + continue; + } + + // compute energy for this loop sizes + curE = helixHandler.getHelixE(i1,i2) + energy.getE_interLeft(i1+h1,i1+h1+w1,i2+h2,i2+h2+w2) + rightExt->E; +// LOG(DEBUG) << "Case 2: curE " << curE; + // check if this combination yields better energy + curEtotal = energy.getE(i1,rightExt->j1,i2,rightExt->j2,curE); + if ( !E_equal(curEtotal, curCellEtotal) && curEtotal < curCellEtotal ) + { + // update current best for this left boundary + // copy right boundary + *curCell = *rightExt; + // set new energy + curCell->E = curE; + // store total energy to avoid recomputation + curCellEtotal = curEtotal; + } + + } // w2 + } // w1 + +// LOG(DEBUG) << "NEW VALUE!!!: i1, i2: " << i1 << " " << i2 << " " << curCellEtotal; + // update mfe if needed + updateOptima( i1,curCell->j1, i2,curCell->j2, curCellEtotal, false ); + } // helix + } // i2 + } // i1 +} + +//////////////////////////////////////////////////////////////////////////// + +void +PredictorMfe2dHelixHeuristic:: +traceBack( Interaction & interaction, const OutputConstraint & outConstraint ) +{ + + // check if something to trace + if (interaction.basePairs.size() < 2) { + return; + } + +#if INTARNA_IN_DEBUG_MODE + // sanity checks + if ( ! interaction.isValid() ) { + throw std::runtime_error("PredictorMfe2dHelixHeuristic::traceBack() : given interaction not valid"); + } + if ( interaction.basePairs.size() != 2 ) { + throw std::runtime_error("PredictorMfe2dHelixHeuristic::traceBack() : given interaction does not contain boundaries only"); + } +#endif + + // check for single interaction + if (interaction.basePairs.at(0).first == interaction.basePairs.at(1).first) { + // delete second boundary (identical to first) + interaction.basePairs.resize(1); + // update done + return; + } + + // ensure sorting + interaction.sort(); + // get indices in hybridE for boundary base pairs + size_t i1 = energy.getIndex1(interaction.basePairs.at(0)), + i2 = energy.getIndex2(interaction.basePairs.at(0)); + const size_t j1 = energy.getIndex1(interaction.basePairs.at(1)); + const size_t j2 = energy.getIndex2(interaction.basePairs.at(1)); + + +// LOG(DEBUG) << "TRACEBACK: " << i1 << " " << i2; + // the currently traced value for i1-j1, i2-j2 + E_type curE = hybridE(i1,i2).E; +// LOG(DEBUG) << "curE: " << curE; + assert( hybridE(i1,i2).j1 == j1 ); + assert( hybridE(i1,i2).j2 == j2 ); + assert( i1 <= j1 ); + assert( i2 <= j2 ); + assert( j1 < hybridE.size1() ); + assert( j2 < hybridE.size2() ); + + // trace back + // temp variables + size_t h1,h2,k1,k2; + // do until only right boundary is left over + while( (j1-i1) > 1 ) { + const BestInteraction * curCell = NULL; + bool traceNotFound = true; + + assert(E_isNotINF(helixHandler.getHelixE(i1,i2))); + h1 = helixHandler.getHelixLength1(i1,i2)-1; assert(h1 < hybridE.size1()); + h2 = helixHandler.getHelixLength2(i1,i2)-1; assert(h2 < hybridE.size2()); + + // check all combinations of decompositions into (i1,i2)..(k1,k2)-(j1,j2) + for (size_t w1=1; traceNotFound && w1-1 <= energy.getMaxInternalLoopSize1() && i1+h1+w1j1 == j1 && curCell->j2 == j2 && + // and energy is the source of curE + E_equal( curE, (helixHandler.getHelixE(i1,i2) + energy.getE_interLeft(i1+h1,k1,i2+h2,k2) + curCell->E ) ) ) + { + // stop searching + traceNotFound = false; + + // store helix base pairs + helixHandler.traceBackHelix( interaction, i1, i2 ); + + // Right most base pair of helix + interaction.basePairs.push_back( energy.getBasePair(i1+h1,i2+h2) ); + // Left most base pair of next case (if not last of interaction) + if (k1 < j1) { + interaction.basePairs.push_back(energy.getBasePair(k1, k2)); + } + // trace right part of split + i1=k1; + i2=k2; + curE = curCell->E; + + } +// LOG(DEBUG) << curE + } // w1 + } // w2 + + // init case + if ( traceNotFound && E_equal(curE, helixHandler.getHelixE(i1,i2) + energy.getE_init()) ) { + // stop searching + traceNotFound = false; + // traceback helix base pairs ( excluding right most = (k1,k2)) + helixHandler.traceBackHelix(interaction, i1, i2); + + // trace right part of split + i1=i1+h1; + i2=i2+h2; + curE=0.0; + } + + assert( !traceNotFound ); + } + + // sort final interaction (to make valid) (faster than calling sort()) + if (interaction.basePairs.size() > 2) { + Interaction::PairingVec & bps = interaction.basePairs; + // shift all added base pairs to the front + for (size_t i=2; i 0;) { + // ensure interaction site start is not covered + if (reportedInteractions.first.covers(i1)) { + continue; + } + for (i2=hybridE.size2(); i2-- > 0;) { + // ensure interaction site start is not covered + if (reportedInteractions.second.covers(i2)) { + continue; + } + // direct cell access + curCell = &(hybridE(i1,i2)); + // check if left side can pair + if (E_isINF(curCell->E)) + { + continue; + } + // get overall energy of the interaction + curCellE = energy.getE(i1,curCell->j1,i2,curCell->j2,curCell->E); + // or energy is too low to be considered + // or energy is higher than current best found so far + if (curCellE < curBestE || curCellE >= curBestCellE ) + { + continue; + } + // ensure site is not overlapping + r1.from = i1; + r1.to = curCell->j1; + if ( reportedInteractions.first.overlaps( r1 )) { + continue; + } + r2.from = i2; + r2.to = curCell->j2; + if ( reportedInteractions.second.overlaps( r2 )) { + continue; + } + //// FOUND THE NEXT BETTER SOLUTION + // overwrite current best found so far + curBestCell = curCell; + curBestCellE = curCellE; + curBestCellStart.first = i1; + curBestCellStart.second = i2; + + } // i2 + } // i1 + + // overwrite curBest + curBest.energy = curBestCellE; + curBest.basePairs.resize(2); + if (E_isNotINF(curBestCellE)) { + curBest.basePairs[0] = energy.getBasePair( curBestCellStart.first, curBestCellStart.second ); + curBest.basePairs[1] = energy.getBasePair( curBestCell->j1, curBestCell->j2 ); + } +} +//////////////////////////////////////////////////////////////////////////// + +} // namespace + diff --git a/src/IntaRNA/PredictorMfe2dHelixHeuristic.h b/src/IntaRNA/PredictorMfe2dHelixHeuristic.h new file mode 100644 index 00000000..41d7b01c --- /dev/null +++ b/src/IntaRNA/PredictorMfe2dHelixHeuristic.h @@ -0,0 +1,122 @@ + +#ifndef INTARNA_PREDICTORMFE2DHELIXHEURISTIC_H_ +#define INTARNA_PREDICTORMFE2DHELIXHEURISTIC_H_ + +#include "IntaRNA/PredictorMfe2dHeuristic.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/HelixHandlerIdxOffset.h" + +#include + +namespace IntaRNA { + +/** + * Memory efficient interaction predictor that uses a heuristic to + * find the mfe or a close-to-mfe interaction. + * + * To this end, for each interaction start i1,i2 only the optimal right side + * interaction with boundaries j1,j2 is considered in the recursion instead of + * all possible interaction ranges. + * + * Helices (continues stackings of base pairs) are restricted to a given length. + * + * This yields a quadratic time and space complexity. + * + * @author Rick Gelhausen + * + */ +class PredictorMfe2dHelixHeuristic: public PredictorMfe2dHeuristic { + +protected: + + //! matrix type to hold the mfe energies and boundaries for interaction site starts + typedef PredictorMfe2dHeuristic::E2dMatrix E2dMatrix; + +public: + + /** + * Constructs a predictor and stores the energy and output handler + * + * @param energy the interaction energy handler + * @param output the output handler to report mfe interactions to + * @param predTracker the prediction tracker to be used or NULL if no + * tracking is to be done; if non-NULL, the tracker gets deleted + * on this->destruction. + */ + PredictorMfe2dHelixHeuristic( const InteractionEnergy & energy + , OutputHandler & output + , PredictionTracker * predTracker + , const HelixConstraint & helixConstraint); + + virtual ~PredictorMfe2dHelixHeuristic(); + + /** + * Computes the mfe for the given sequence ranges (i1-j1) in the first + * sequence and (i2-j2) in the second sequence and reports it to the output + * handler. + * + * @param r1 the index range of the first sequence interacting with r2 + * @param r2 the index range of the second sequence interacting with r1 + * @param outConstraint constrains the interactions reported to the output handler + * + */ + virtual + void + predict( const IndexRange & r1 = IndexRange(0,RnaSequence::lastPos) + , const IndexRange & r2 = IndexRange(0,RnaSequence::lastPos) + , const OutputConstraint & outConstraint = OutputConstraint() ); + +protected: + + //! access to the interaction energy handler of the super class + using PredictorMfe2dHeuristic::energy; + + //! access to the output handler of the super class + using PredictorMfe2dHeuristic::output; + + //! access to the list of reported interaction ranges of the super class + using PredictorMfe2dHeuristic::reportedInteractions; + + //! energy of all interaction hybrids starting in i1,i2 + using PredictorMfe2dHeuristic::hybridE; + + //! helixHandler used only for creating HelixHandlerOffset + //HelixHandler helixHandler; + HelixHandlerIdxOffset helixHandler; + +protected: + + /** + * Computes all entries of the hybridE matrix + */ + virtual + void + fillHybridE(); + + /** + * Fills a given interaction (boundaries given) with the according + * hybridizing base pairs. + * @param interaction IN/OUT the interaction to fill + */ + virtual + void + traceBack( Interaction & interaction, const OutputConstraint & outConstraint ); + + /** + * Identifies the next best interaction with an energy equal to or higher + * than the given interaction. The new interaction will not overlap any + * index range stored in reportedInteractions. + * + * @param curBest IN/OUT the current best interaction to be replaced with one + * of equal or higher energy not overlapping with any reported + * interaction so far; an interaction with energy E_INF is set, if + * there is no better interaction left + */ + virtual + void + getNextBest( Interaction & curBest ); +}; + +} // namespace + +#endif /* INTARNA_PREDICTORMFE2DHELIXHEURISTIC_H_ */ diff --git a/src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.cpp b/src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.cpp new file mode 100644 index 00000000..9732c78c --- /dev/null +++ b/src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.cpp @@ -0,0 +1,546 @@ + +#include "IntaRNA/PredictorMfe2dHelixHeuristicSeed.h" + +#include + +namespace IntaRNA { + +//////////////////////////////////////////////////////////////////////////// + +PredictorMfe2dHelixHeuristicSeed:: +PredictorMfe2dHelixHeuristicSeed( const InteractionEnergy & energy + , OutputHandler & output + , PredictionTracker * predTracker + , const HelixConstraint & helixConstraint + , SeedHandler * seedHandlerInstance ) + + : PredictorMfe2dHelixHeuristic(energy,output,predTracker,helixConstraint) + , seedHandler(seedHandlerInstance) + +{ + helixHandler.setSeedHandler(seedHandler); +} + + +//////////////////////////////////////////////////////////////////////////// + +PredictorMfe2dHelixHeuristicSeed:: +~PredictorMfe2dHelixHeuristicSeed() +{ + // clean up +} + + +//////////////////////////////////////////////////////////////////////////// + +void +PredictorMfe2dHelixHeuristicSeed:: +predict( const IndexRange & r1 + , const IndexRange & r2 + , const OutputConstraint & outConstraint ) +{ +#if INTARNA_MULITHREADING +#pragma omp critical(intarna_omp_logOutput) +#endif + { VLOG(2) <<"predicting mfe interactions with seed based on helices heuristically in O(n^2) space and time..."; } + // measure timing + TIMED_FUNC_IF(timerObj,VLOG_IS_ON(9)); + +#if INTARNA_IN_DEBUG_MODE + // check indices + if (!(r1.isAscending() && r2.isAscending()) ) + throw std::runtime_error("PredictorMfe2dHelixHeuristicSeed::predict("+toString(r1)+","+toString(r2)+") is not sane"); +#endif + + // set index offset + energy.setOffset1(r1.from); + energy.setOffset2(r2.from); + helixHandler.setOffset1(r1.from); + helixHandler.setOffset2(r2.from); + seedHandler.setOffset1(r1.from); + seedHandler.setOffset2(r2.from); + + const size_t hybridEsize1 = std::min( energy.size1() + , (r1.to==RnaSequence::lastPos?energy.size1()-1:r1.to)-r1.from+1 ); + const size_t hybridEsize2 = std::min( energy.size2() + , (r2.to==RnaSequence::lastPos?energy.size2()-1:r2.to)-r2.from+1 ); + + // resize matrix + hybridE.resize( hybridEsize1, hybridEsize2 ); + hybridE_seed.resize( hybridE.size1(), hybridE.size2() ); + + // Fill seed / helix and helixSeed Matrices, if one is empty trigger empty interaction reporting + if ((seedHandler.fillSeed(0, hybridEsize1-1, 0, hybridEsize2-1) == 0) + || (helixHandler.fillHelix( 0, hybridEsize1-1, 0, hybridEsize2-1) == 0) + || (helixHandler.fillHelixSeed( 0, hybridEsize1-1, 0, hybridEsize2-1) == 0)) { + // trigger empty interaction reporting + initOptima(outConstraint); + reportOptima(outConstraint); + // stop computation + return; + } + + // temp vars + size_t i1,i2,h1,h2,w1,w2; + + // init hybridE matrix + bool isValidCell = true; + for (i1=0; i1 no hybrid update since updateOptima overwritten + PredictorMfe2dHelixHeuristic::fillHybridE(); + + // check result of predictions without seed if any interaction possible + // if not no seed-containing interaction is possible neither + if (this->mfeInteractions.begin()->energy >= tmpOutConstraint.maxE) { + // stop computation since no favorable interaction found + reportOptima(tmpOutConstraint); + return; + } + + // init mfe for later updates + initOptima( outConstraint ); + + // compute entries + // current minimal value + E_type curE = E_INF, curEtotal = E_INF, curCellEtotal = E_INF; + BestInteraction * curCell = NULL; + const BestInteraction * rightExt = NULL; + + // iterate (decreasingly) over all left interaction starts + for (i1=hybridE_seed.size1(); i1-- > 0;) { + for (i2=hybridE_seed.size2(); i2-- > 0;) { + + // check if left side can pair + if (E_isINF(hybridE(i1,i2).E)) { + continue; + } + // direct cell access + curCell = &(hybridE_seed(i1,i2)); + // reset temporary variables + curEtotal = E_INF; + curCellEtotal = E_INF; + + // check if helix containing a seed is possible for this left boundary + if ( E_isNotINF( helixHandler.getHelixSeedE(i1,i2) ) ) { + // helixHandlerSeed Lengths + h1 = helixHandler.getHelixSeedLength1(i1,i2)-1; assert(i1+h1 < hybridE_seed.size1()); + h2 = helixHandler.getHelixSeedLength2(i1,i2)-1; assert(i2+h2 < hybridE_seed.size2()); + + /////////////////////////////////////////////////////////////////// + // Case: Helix_seed + E_init + /////////////////////////////////////////////////////////////////// + + curE = helixHandler.getHelixSeedE(i1,i2) + energy.getE_init(); + // check if this combination yields better energy + curEtotal = energy.getE(i1,i1+h1, i2, i2+h2, curE); + if ( curEtotal < curCellEtotal ) + { + + // update current best for this left boundary + // set right boundary + curCell->j1 = i1+h1; + curCell->j2 = i2+h2; + // set new energy + curCell->E = curE; + // store total energy to avoid recomputation + curCellEtotal = curEtotal; + } + + /////////////////////////////////////////////////////////////////// + // Case: Helix_seed + interior loop + hybridE + /////////////////////////////////////////////////////////////////// + + for (w1=1; w1-1 <= energy.getMaxInternalLoopSize1() && i1+h1+w1E)) { + continue; + } + // check if interaction length is within boundary + if ( (rightExt->j1+1-i1) > energy.getAccessibility1().getMaxLength() + || (rightExt->j2+1-i2) > energy.getAccessibility2().getMaxLength() ) + { + continue; + } + // compute energy for this loop sizes + curE = helixHandler.getHelixSeedE(i1,i2) + energy.getE_interLeft(i1+h1,i1+h1+w1,i2+h2,i2+h2+w2) + rightExt->E; + + // check if this combination yields better energy + curEtotal = energy.getE(i1,rightExt->j1,i2,rightExt->j2,curE); + if ( !E_equal(curEtotal, curCellEtotal) && curEtotal < curCellEtotal ) + { + // update current best for this left boundary + // copy right boundary + *curCell = *rightExt; + // set new energy + curCell->E = curE; + // store total energy to avoid recomputation + curCellEtotal = curEtotal; + } + + } // w2 + } // w1 + } // helixSeed + + + /////////////////////////////////////////////////////////////////// + // Case: Helix + interior loop + hybridE_seed + /////////////////////////////////////////////////////////////////// + + // check if helix containing no seed is possible for this left boundary + if ( E_isNotINF(helixHandler.getHelixE(i1,i2) ) ) { + // helixHandler lengths + h1 = helixHandler.getHelixLength1(i1,i2)-1; assert(i1+h1E)) { + continue; + } + // check if interaction length is within boundary + if ( (rightExt->j1 +1 -i1) > energy.getAccessibility1().getMaxLength() + || (rightExt->j2 +1 -i2) > energy.getAccessibility2().getMaxLength() ) + { + continue; + } + // compute energy for this loop sizes + curE = helixHandler.getHelixE(i1,i2) + energy.getE_interLeft(i1+h1,i1+h1+w1,i2+h2,i2+h2+w2) + rightExt->E; + + // check if this combination yields better energy + curEtotal = energy.getE(i1,rightExt->j1,i2,rightExt->j2,curE); + if ( curEtotal < curCellEtotal ) + { + // update current best for this left boundary + // copy right boundary + *curCell = *rightExt; + // set new energy + curCell->E = curE; + // store total energy to avoid recomputation + curCellEtotal = curEtotal; + } + + } // w2 + } // w1 + + } // helix + + // update mfe if needed (call superclass update routine) + PredictorMfe2dHelixHeuristic::updateOptima( i1,curCell->j1, i2,curCell->j2, curCellEtotal, false ); + + } // i2 + } // i1 + + + // report mfe interaction + reportOptima( outConstraint ); + +} + + +//////////////////////////////////////////////////////////////////////////// + +void +PredictorMfe2dHelixHeuristicSeed:: +traceBack( Interaction & interaction, const OutputConstraint & outConstraint ) +{ + // check if something to trace + if (interaction.basePairs.size() < 2) { + return; + } + +#if INTARNA_IN_DEBUG_MODE + // sanity checks + if ( ! interaction.isValid() ) { + throw std::runtime_error("PredictorMfe2dHelixHeuristicSeed::traceBack() : given interaction not valid"); + } + if ( interaction.basePairs.size() != 2 ) { + throw std::runtime_error("PredictorMfe2dHelixHeuristicSeed::traceBack() : given interaction does not contain boundaries only"); + } +#endif + + // check for single interaction + if (interaction.basePairs.at(0).first == interaction.basePairs.at(1).first) { + // delete second boundary (identical to first) + interaction.basePairs.resize(1); + // update done + return; + } + + // ensure sorting + interaction.sort(); + // get indices in hybridE for boundary base pairs + size_t i1 = energy.getIndex1(interaction.basePairs.at(0)), + i2 = energy.getIndex2(interaction.basePairs.at(0)); + const size_t j1 = energy.getIndex1(interaction.basePairs.at(1)); + const size_t j2 = energy.getIndex2(interaction.basePairs.at(1)); + + // the currently traced value for i1-j1, i2-j2 + E_type curE = hybridE_seed(i1,i2).E; + assert( hybridE_seed(i1,i2).j1 == j1 ); + assert( hybridE_seed(i1,i2).j2 == j2 ); + assert( i1 <= j1 ); + assert( i2 <= j2 ); + assert( j1 < hybridE_seed.size1() ); + assert( j2 < hybridE_seed.size2() ); + + // trace back + // temp variables + size_t h1,h2,k1,k2; + // do until only right boundary is left over + while( (j1-i1) > 1 ) { + const BestInteraction * curCell = NULL; + bool traceNotFound = true; + + // Assure that atleast one case is possible + assert(E_isNotINF(helixHandler.getHelixE(i1,i2)) || E_isNotINF(helixHandler.getHelixSeedE(i1,i2))); + + // helix + il + hybridE_seed + if (E_isNotINF(helixHandler.getHelixE(i1,i2))) { + h1 = helixHandler.getHelixLength1(i1,i2)-1; assert(i1+h1j1 == j1 && curCell->j2 == j2 && + // and energy is the source of curE + E_equal( curE, (helixHandler.getHelixE(i1,i2) + energy.getE_interLeft(i1+h1,k1,i2+h2,k2) + curCell->E ) ) ) + { + // stop searching + traceNotFound = false; + // store helix base pairs + helixHandler.traceBackHelix( interaction, i1, i2 ); + + // stor last base pair of helix + interaction.basePairs.push_back( energy.getBasePair(i1+h1,i2+h2)); + // store splitting base pair + interaction.basePairs.push_back( energy.getBasePair(k1,k2) ); + + // trace right part of split + i1=k1; + i2=k2; + curE = curCell->E; + } + } // w1 + } // w2 + + } + + // seed + il + hybridE + if (E_isNotINF(helixHandler.getHelixSeedE(i1,i2))) { + h1 = helixHandler.getHelixSeedLength1(i1,i2)-1; assert(i1+h1j1 == j1 && curCell->j2 == j2 && + // and energy is the source of curE + E_equal( curE, (helixHandler.getHelixSeedE(i1,i2) + energy.getE_interLeft(i1+h1,k1,i2+h2,k2) + curCell->E ) ) ) + { + // store helix base pairs + helixHandler.traceBackHelixSeed( interaction, i1, i2 ); + interaction.basePairs.push_back(energy.getBasePair(i1+h1, i2+h2)); + i1 = k1; + i2 = k2; + + // traceback remaining right interaction via hybridE + if (i1 < j1) { + Interaction bpsRight(*(interaction.s1), *(interaction.s2)); + bpsRight.basePairs.push_back(energy.getBasePair(i1, i2)); + bpsRight.basePairs.push_back(energy.getBasePair(j1, j2)); + PredictorMfe2dHelixHeuristic::traceBack(bpsRight, outConstraint); + // copy remaining base pairs + Interaction::PairingVec &bps = bpsRight.basePairs; + // copy all base pairs excluding the right most + for (size_t i = 0; i + 1 < bps.size(); i++) { + interaction.basePairs.push_back(bps.at(i)); + } + } + // stop search since all trace back done + traceNotFound = false; + i1=j1; + i2=j2; + } + } // w1 + } // w2 + + // seed + E_init() + if ( traceNotFound && E_equal(curE, helixHandler.getHelixSeedE(i1,i2) + energy.getE_init()) ) { + // stop searching + traceNotFound = false; + // traceback helix base pairs ( excluding right most = (k1,k2)) + helixHandler.traceBackHelixSeed(interaction, i1, i2); + // trace right part of split + i1=i1+h1; + i2=i2+h2; + curE=0.0; + } + } + assert(!traceNotFound); + + } + // sort final interaction (to make valid) (faster than calling sort()) + if (interaction.basePairs.size() > 2) { + Interaction::PairingVec & bps = interaction.basePairs; + // shift all added base pairs to the front + for (size_t i=2; i 0;) { + // ensure interaction site start is not covered + if (reportedInteractions.first.covers(i1)) { + continue; + } + for (i2=hybridE_seed.size2(); i2-- > 0;) { + // ensure interaction site start is not covered + if (reportedInteractions.second.covers(i2)) { + continue; + } + // direct cell access + curCell = &(hybridE_seed(i1,i2)); + // check if left side can pair + if (E_isINF(curCell->E)) + { + continue; + } + // get overall energy of the interaction + curCellE = energy.getE(i1,curCell->j1,i2,curCell->j2,curCell->E); + // or energy is too low to be considered + // or energy is higher than current best found so far + if (curCellE < curBestE || curCellE >= curBestCellE ) + { + continue; + } + // ensure site is not overlapping + r1.from = i1; + r1.to = curCell->j1; + if ( reportedInteractions.first.overlaps( r1 )) { + continue; + } + r2.from = i2; + r2.to = curCell->j2; + if ( reportedInteractions.second.overlaps( r2 )) { + continue; + } + //// FOUND THE NEXT BETTER SOLUTION + // overwrite current best found so far + curBestCell = curCell; + curBestCellE = curCellE; + curBestCellStart.first = i1; + curBestCellStart.second = i2; + + } // i2 + } // i1 + + // overwrite curBest + curBest.basePairs.resize(2); + curBest.energy = curBestCellE; + if (E_isNotINF(curBestCellE)) { + curBest.basePairs[0] = energy.getBasePair( curBestCellStart.first, curBestCellStart.second ); + curBest.basePairs[1] = energy.getBasePair( curBestCell->j1, curBestCell->j2 ); + } + +} + +//////////////////////////////////////////////////////////////////////////// + +} // namespace + diff --git a/src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.h b/src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.h new file mode 100644 index 00000000..7fa3b5e6 --- /dev/null +++ b/src/IntaRNA/PredictorMfe2dHelixHeuristicSeed.h @@ -0,0 +1,163 @@ + +#ifndef INTARNA_PREDICTORMFE2DHELIXHEURISTICSEED_H +#define INTARNA_PREDICTORMFE2DHELIXHEURISTICSEED_H + +#include "IntaRNA/PredictorMfe2dHelixHeuristic.h" +#include "IntaRNA/SeedHandlerIdxOffset.h" + +namespace IntaRNA { + + +/** + * Memory efficient interaction predictor that uses both qualitative heuristics + * (interactions have to have a seed interaction) and performance heuristics + * (not all possible interactions considered) + * + * To this end, for each interaction start i1,i2 only the optimal right side + * interaction with boundaries j1,j2 is considered in the recursion instead of + * all possible interaction ranges. + * + * This yields a quadratic time and space complexity. + * + * @author Rick Gelhausen + * + */ +class PredictorMfe2dHelixHeuristicSeed: public PredictorMfe2dHelixHeuristic { + + + //! matrix type to hold the mfe energies and boundaries for interaction site starts + typedef PredictorMfe2dHeuristic::E2dMatrix E2dMatrix; + +public: + + /** + * Constructs a predictor and stores the energy and output handler + * + * @param energy the interaction energy handler + * @param output the output handler to report mfe interactions to + * @param predTracker the prediction tracker to be used or NULL if no + * tracking is to be done; if non-NULL, the tracker gets deleted + * on this->destruction. + * @param seedConstraint the seed constraint to be applied + */ + PredictorMfe2dHelixHeuristicSeed( const InteractionEnergy & energy + , OutputHandler & output + , PredictionTracker * predTracker + , const HelixConstraint & helixConstraint + , SeedHandler * seedHandlerInstance + ); + + virtual ~PredictorMfe2dHelixHeuristicSeed(); + + /** + * Computes the mfe for the given sequence ranges (i1-j1) in the first + * sequence and (i2-j2) in the second sequence and reports it to the output + * handler. + * + * @param r1 the index range of the first sequence interacting with r2 + * @param r2 the index range of the second sequence interacting with r1 + * @param outConstraint constrains the interactions reported to the output handler + * + */ + virtual + void + predict( const IndexRange & r1 = IndexRange(0,RnaSequence::lastPos) + , const IndexRange & r2 = IndexRange(0,RnaSequence::lastPos) + , const OutputConstraint & outConstraint = OutputConstraint()); + +protected: + + //! access to the interaction energy handler of the super class + using PredictorMfe2dHelixHeuristic::energy; + + //! access to the output handler of the super class + using PredictorMfe2dHelixHeuristic::output; + + //! access to the list of reported interaction ranges of the super class + using PredictorMfe2dHelixHeuristic::reportedInteractions; + + //! energy of all interaction hybrids that end in position p (seq1) and + //! q (seq2) + using PredictorMfe2dHelixHeuristic::hybridE; + + //! handler to generate and access helix information with idx offset + using PredictorMfe2dHelixHeuristic::helixHandler; + + //! the best hybridization energy including a seed for start i1,i2 + E2dMatrix hybridE_seed; + + // + SeedHandlerIdxOffset seedHandler; + +protected: + + /** + * Fills a given interaction (boundaries given) with the according + * hybridizing base pairs. + * @param interaction IN/OUT the interaction to fill + */ + virtual + void + traceBack( Interaction & interaction, const OutputConstraint & outConstraint ); + + + /** + * Identifies the next best interaction (containing a seed) + * with an energy equal to or higher + * than the given interaction. The new interaction will not overlap any + * index range stored in reportedInteractions. + * + * @param curBest IN/OUT the current best interaction to be replaced with one + * of equal or higher energy not overlapping with any reported + * interaction so far; an interaction with energy E_INF is set, if + * there is no better interaction left + */ + virtual + void + getNextBest( Interaction & curBest ); + + /** + * does nothing but to ignore the calls from fillHybridE() + * + * @param i1 the index of the first sequence interacting with i2 + * @param j1 the index of the first sequence interacting with j2 + * @param i2 the index of the second sequence interacting with i1 + * @param j2 the index of the second sequence interacting with j1 + * @param energy ignored + * @param isHybridE ignored + */ + virtual + void + updateOptima( const size_t i1, const size_t j1 + , const size_t i2, const size_t j2 + , const E_type energy + , const bool isHybridE ); + + +}; + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +inline +void +PredictorMfe2dHelixHeuristicSeed:: +updateOptima( const size_t i1, const size_t j1 + , const size_t i2, const size_t j2 + , const E_type energy + , const bool isHybridE ) +{ + // temporarily disable tracker + PredictionTracker * curPredTracker = this->predTracker; + this->predTracker = NULL; + // update optimum information, such that we might skip this sequence pair + PredictorMfe2dHeuristic::updateOptima(i1,j1,i2,j2,energy,isHybridE); + // reenable tracker + this->predTracker = curPredTracker; +} + +////////////////////////////////////////////////////////////////////////// + +} // namespace +#endif //INTARNA_PREDICTORMFE2DHELIXHEURISTICSEED_H diff --git a/src/IntaRNA/PredictorMfe2dSeed.cpp b/src/IntaRNA/PredictorMfe2dSeed.cpp index 032f8cf1..4a8f715d 100644 --- a/src/IntaRNA/PredictorMfe2dSeed.cpp +++ b/src/IntaRNA/PredictorMfe2dSeed.cpp @@ -117,8 +117,6 @@ fillHybridE_seed( const size_t j1, const size_t j2, const size_t i1min, const si assert(i1min <= j1); assert(i2min <= j2); - assert(hybridErange.r1.from <= i1min); - assert(hybridErange.r2.from <= i2min); assert(j1==hybridErange.r1.to); assert(j2==hybridErange.r2.to); assert(j1 @@ -19,7 +20,7 @@ namespace IntaRNA { * @author Martin Mann * */ -class SeedHandlerIdxOffset +class SeedHandlerIdxOffset : public SeedHandler { public: @@ -36,6 +37,14 @@ class SeedHandlerIdxOffset */ virtual ~SeedHandlerIdxOffset(); + /** + * Access to the underlying interaction energy function + * @return the used energy function + */ + virtual + const InteractionEnergy& + getInteractionEnergy() const; + /** * Access to the currently used index offset for sequence 1 * @return the index offset for sequence 1 used @@ -146,6 +155,15 @@ class SeedHandlerIdxOffset getSeedLength2( const size_t i1, const size_t i2 ) const; + /** + * Access to the wrapped SeedHandler instance. + * + * @return the internally used SeedHandler without offset + */ + virtual + SeedHandler& + getOriginalSeedHandler(); + protected: @@ -161,6 +179,9 @@ class SeedHandlerIdxOffset //! offset for indices in seq2 size_t idxOffset2; + //! dedicated energy object to avoid + InteractionEnergyIdxOffset energyIdxOffset; + }; @@ -175,10 +196,12 @@ inline SeedHandlerIdxOffset:: SeedHandlerIdxOffset( SeedHandler * seedHandlerInstance ) : - seedHandlerOriginal( seedHandlerInstance ) + SeedHandler(seedHandlerInstance->getInteractionEnergy(), seedHandlerInstance->getConstraint() ) + , seedHandlerOriginal( seedHandlerInstance ) , seedConstraintOffset( seedHandlerOriginal->getConstraint() ) , idxOffset1(0) , idxOffset2(0) + , energyIdxOffset(seedHandlerInstance->getInteractionEnergy(),idxOffset1,idxOffset2) { } @@ -203,6 +226,16 @@ getConstraint() const return seedConstraintOffset; } +//////////////////////////////////////////////////////////////////////////// + +inline +const InteractionEnergy& +SeedHandlerIdxOffset:: +getInteractionEnergy() const +{ + return this->energyIdxOffset; +} + ////////////////////////////////////////////////////////////////////////// inline @@ -291,6 +324,7 @@ setOffset1( const size_t offset ) #endif // set idx offset this->idxOffset1 = offset; + this->energyIdxOffset.setOffset1(idxOffset1); // update ranges of seed constraint seedConstraintOffset.getRanges1() = seedHandlerOriginal->getConstraint().getRanges1().shift( -(int)offset, seedHandlerOriginal->getInteractionEnergy().size1()-1-offset ); } @@ -310,12 +344,23 @@ setOffset2( const size_t offset ) #endif // set idx offset this->idxOffset2 = offset; + this->energyIdxOffset.setOffset2(idxOffset2); // update ranges of seed constraint seedConstraintOffset.getRanges2() = seedHandlerOriginal->getConstraint().getRanges2().shift( -(int)offset, seedHandlerOriginal->getInteractionEnergy().size2()-1-offset ); } //////////////////////////////////////////////////////////////////////////// +inline +SeedHandler& +SeedHandlerIdxOffset:: +getOriginalSeedHandler() +{ + return *seedHandlerOriginal; +} + +//////////////////////////////////////////////////////////////////////////// + } // namespace #endif /* SEEDHANDLERIDXOFFSET_H_ */ diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index b2d30f44..4df13f48 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -31,15 +31,19 @@ extern "C" { #include "IntaRNA/AccessibilityVrna.h" #include "IntaRNA/AccessibilityBasePair.h" +#include "IntaRNA/HelixHandler.h" + #include "IntaRNA/InteractionEnergyBasePair.h" #include "IntaRNA/InteractionEnergyVrna.h" #include "IntaRNA/PredictorMfe2dHeuristic.h" +#include "IntaRNA/PredictorMfe2dHelixHeuristic.h" #include "IntaRNA/PredictorMfe2d.h" #include "IntaRNA/PredictorMfe4d.h" #include "IntaRNA/PredictorMaxProb.h" #include "IntaRNA/PredictorMfe2dHeuristicSeed.h" +#include "IntaRNA/PredictorMfe2dHelixHeuristicSeed.h" #include "IntaRNA/PredictorMfe2dSeed.h" #include "IntaRNA/PredictorMfe4dSeed.h" @@ -73,6 +77,7 @@ CommandLineParsing::CommandLineParsing() stdinUsed(false), opts_query("Query"), opts_target("Target"), + opts_helix("Helix (only if --model=H)"), opts_seed("Seed"), opts_shape("SHAPE"), opts_inter("Interaction"), @@ -119,6 +124,15 @@ CommandLineParsing::CommandLineParsing() tShapeMethod("Zb0.89"), tShapeConversion("Os1.6i-2.29"), + // Helix Constraints + helixMinBP(2,4,2), + helixMaxBP(2,20,10), + helixMaxIL(0,2,0), + helixMaxED(-999,+999, 999), + helixMaxE(-999,+999,0), + helixNoED(false), + helixConstraint(NULL), + noSeedRequired(false), seedTQ(""), seedBP(2,20,7), @@ -133,13 +147,13 @@ CommandLineParsing::CommandLineParsing() temperature(0,100,37), - pred( "SP", 'S'), + model( "SPH", 'S'), predMode( "HME", 'H'), #if INTARNA_MULITHREADING threads( 0, omp_get_max_threads(), 1), #endif windowWidth(0,99999,0), - windowOverlap(0,99999,0), + windowOverlap(0,99999,150), energy("BV",'V'), energyFile(""), @@ -363,6 +377,49 @@ CommandLineParsing::CommandLineParsing() ).c_str()) ; + + //// HELIX OPTIONS /////////////////////////////////// + + opts_helix.add_options() + ("helixMinBP" + , value(&(helixMinBP.val)) + ->default_value(helixMinBP.def) + ->notifier(boost::bind(&CommandLineParsing::validate_helixMinBP, this,_1)) + , std::string("minimal number of base pairs inside a helix" + " (arg in range ["+toString(helixMinBP.min)+","+toString(helixMinBP.max)+"])").c_str()) + + ("helixMaxBP" + , value(&(helixMaxBP.val)) + ->default_value(helixMaxBP.def) + ->notifier(boost::bind(&CommandLineParsing::validate_helixMaxBP, this,_1)) + , std::string("maximal number of base pairs inside a helix" + " (arg in range ["+toString(helixMaxBP.min)+","+toString(helixMaxBP.max)+"])").c_str()) + + ("helixMaxIL" + , value(&(helixMaxIL.val)) + ->default_value(helixMaxIL.def) + ->notifier(boost::bind(&CommandLineParsing::validate_helixMaxIL, this,_1)) + , std::string("maximal size for each internal loop size in a helix" + " (arg in range ["+toString(helixMaxIL.min)+","+toString(helixMaxIL.max)+"]).").c_str()) + + ("helixMaxED" + , value(&(helixMaxED.val)) + ->default_value(helixMaxED.def) + ->notifier(boost::bind(&CommandLineParsing::validate_helixMaxED, this,_1)) + , std::string("maximal ED-value allowed (per sequence) during helix computation" + " (arg in range ["+toString(helixMaxED.min)+","+toString(helixMaxED.max)+"]).").c_str()) + + ("helixMaxE" + , value(&(helixMaxE.val)) + ->default_value(helixMaxE.def) + ->notifier(boost::bind(&CommandLineParsing::validate_helixMaxE, this,_1)) + , std::string("maximal energy considered during helix computation" + " (arg in range ["+toString(helixMaxE.min)+","+toString(helixMaxE.max)+"]).").c_str()) + + ("helixWithED", "if present, ED-values will be used within the energy evaluation of a helix") + ; + opts_cmdline_short.add(opts_helix); + //// SEED OPTIONS //////////////////////////////////// @@ -494,12 +551,13 @@ CommandLineParsing::CommandLineParsing() ; opts_cmdline_short.add(opts_inter); opts_inter.add_options() - ("pred" - , value(&(pred.val)) - ->default_value(pred.def) - ->notifier(boost::bind(&CommandLineParsing::validate_pred,this,_1)) - , std::string("prediction target : " - "\n 'S' = single-site minimum-free-energy interaction (interior loops only), " + ("model" + , value(&(model.val)) + ->default_value(model.def) + ->notifier(boost::bind(&CommandLineParsing::validate_model,this,_1)) + , std::string("interaction model : " + "\n 'S' = single-site, minimum-free-energy interaction (interior loops only), " + "\n 'H' = single-site, helix-based, minimum-free-energy interaction (helices and interior loops only), " "\n 'P' = single-site maximum-probability interaction (interior loops only)" ).c_str()) ("energy,e" @@ -642,7 +700,7 @@ CommandLineParsing::CommandLineParsing() //// GENERAL OPTIONS //////////////////////////////////// - opts_cmdline_all.add(opts_query).add(opts_target).add(opts_seed).add(opts_shape).add(opts_inter).add(opts_output).add(opts_general); + opts_cmdline_all.add(opts_query).add(opts_target).add(opts_seed).add(opts_shape).add(opts_inter).add(opts_helix).add(opts_output).add(opts_general); } @@ -651,6 +709,7 @@ CommandLineParsing::CommandLineParsing() CommandLineParsing::~CommandLineParsing() { + INTARNA_CLEANUP(helixConstraint); INTARNA_CLEANUP(seedConstraint); // reset output stream @@ -765,6 +824,28 @@ parse(int argc, char** argv) validate_qAccFile( qAccFile ); validate_tAccFile( tAccFile ); + // check helix setup + // check for minimal sequence length + for(size_t i=0; i helixMaxBP.val) { + throw error("the minimum number of base pairs (" +toString(helixMinBP.val)+") is higher than the maximum number of base pairs (" +toString(helixMaxBP.val)+")"); + } + + // check for helixWithED + helixNoED = vm.count("helixWithED") == 0; + // check seed setup noSeedRequired = vm.count("noSeed") > 0; if (noSeedRequired) { @@ -796,6 +877,10 @@ parse(int argc, char** argv) } } + // check if helixMaxBP >= seedBP + if (helixMaxBP.val < seedBP.val) { + throw error("maximum number of allowed seed base pairs ("+toString(seedBP.val)+") exceeds the maximal allowed number of helix base pairs ("+toString(helixMaxBP.val)+")"); + } // check for minimal sequence length (>=seedBP) for( size_t i=0; i 1) { // warn if >= 4D space prediction enabled - if (pred.val != 'S' || predMode.val == 'E') { + if (model.val != 'S' || predMode.val == 'E') { LOG(WARNING) <<"Multi-threading enabled in high-mem-prediction mode : ensure you have enough memory available!"; } if (outMode.val == '1' || outMode.val == 'O') { @@ -1461,7 +1546,7 @@ getEnergyHandler( const Accessibility& accTarget, const ReverseAccessibility& ac checkIfParsed(); // check whether to compute ES values (for multi-site predictions) - const bool initES = std::string("M").find(pred.val) != std::string::npos; + const bool initES = std::string("M").find(model.val) != std::string::npos; switch( energy.val ) { case 'B' : return new InteractionEnergyBasePair( accTarget, accQuery, tIntLoopMax.val, qIntLoopMax.val, initES ); @@ -1795,34 +1880,46 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output ) const if (noSeedRequired) { // predictors without seed constraint - switch( pred.val ) { + switch( model.val ) { + case 'H': { + switch ( predMode.val ) { + case 'H' : return new PredictorMfe2dHelixHeuristic( energy, output, predTracker, getHelixConstraint(energy)); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)); + } + } break; // single-site mfe interactions (contain only interior loops) case 'S' : { switch ( predMode.val ) { case 'H' : return new PredictorMfe2dHeuristic( energy, output, predTracker ); case 'M' : return new PredictorMfe2d( energy, output, predTracker ); case 'E' : return new PredictorMfe4d( energy, output, predTracker ); - default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(pred.val)); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)); } } break; // single-site max-prob interactions (contain only interior loops) case 'P' : { switch ( predMode.val ) { case 'E' : return new PredictorMaxProb( energy, output, predTracker ); - default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(pred.val)+" : try --mode=E"); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)+" : try --mode=E"); } } break; // multi-site mfe interactions (contain interior and multi-loops loops) case 'M' : { switch ( predMode.val ) { - default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(pred.val)); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)); } } break; default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented"); } } else { // seed-constrained predictors - switch( pred.val ) { + switch( model.val ) { + case 'H' : { + switch ( predMode.val ) { + case 'H' : return new PredictorMfe2dHelixHeuristicSeed(energy, output, predTracker, getHelixConstraint(energy), getSeedHandler(energy)); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)); + } + } break; // single-site mfe interactions (contain only interior loops) case 'S' : { switch ( predMode.val ) { @@ -1835,13 +1932,13 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output ) const case 'P' : { switch ( predMode.val ) { case 'E' : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for seed constraint (try --noSeed)"); return NULL; - default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(pred.val)); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)); } } break; // multi-site mfe interactions (contain interior and multi-loops loops) case 'M' : { switch ( predMode.val ) { - default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(pred.val)); + default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented for prediction target "+toString(model.val)); } } break; default : INTARNA_NOT_IMPLEMENTED("mode "+toString(predMode.val)+" not implemented"); @@ -1931,6 +2028,26 @@ updateParsingCode( const ReturnCode currentParsingCode ) //////////////////////////////////////////////////////////////////////////// +const HelixConstraint & +CommandLineParsing:: +getHelixConstraint(const InteractionEnergy &energy) const +{ + if (helixConstraint == NULL) { + // setup according to user data + helixConstraint = new HelixConstraint( + helixMinBP.val + , helixMaxBP.val + , helixMaxIL.val + , helixMaxED.val + , helixMaxE.val + , helixNoED + ); + } + return *helixConstraint; +} + +//////////////////////////////////////////////////////////////////////////// + const SeedConstraint & CommandLineParsing:: getSeedConstraint( const InteractionEnergy & energy ) const diff --git a/src/bin/CommandLineParsing.h b/src/bin/CommandLineParsing.h index 707f1c84..bd1cf157 100644 --- a/src/bin/CommandLineParsing.h +++ b/src/bin/CommandLineParsing.h @@ -15,6 +15,8 @@ #include "IntaRNA/Accessibility.h" #include "IntaRNA/InteractionEnergy.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandler.h" #include "IntaRNA/OutputHandler.h" #include "IntaRNA/Predictor.h" #include "IntaRNA/SeedConstraint.h" @@ -169,6 +171,12 @@ class CommandLineParsing { Predictor* getPredictor( const InteractionEnergy & energy , OutputHandler & output ) const; + /** + * Provides the seed constraint according to the user settings + * @param energy the interaction energy handler to be used + * @return the user defined seed constraints + */ + const HelixConstraint & getHelixConstraint( const InteractionEnergy & energy ) const; /** * Provides the seed constraint according to the user settings @@ -367,6 +375,8 @@ class CommandLineParsing { boost::program_options::options_description opts_query; //! target specific options boost::program_options::options_description opts_target; + //! helix specific options + boost::program_options::options_description opts_helix; //! seed specific options boost::program_options::options_description opts_seed; //! SHAPE reactivity data specific options @@ -465,6 +475,22 @@ class CommandLineParsing { //! probabilities for according accessibility prediction std::string tShapeConversion; + + //! the minimal number of base pairs allowed in the helix (>2) + NumberParameter helixMinBP; + //! the maximal number of base pairs allowed in the helix (>helixMinBP) + NumberParameter helixMaxBP; + //! maximal internal loop size in the helix computation (0-2) + NumberParameter helixMaxIL; + //! maximal ED-value allowed (per sequence) of a helix to be considered + NumberParameter helixMaxED; + //! maximal energy of a helix to be considered + NumberParameter helixMaxE; + //! when set, ED values are excluded from a helix's energy evaluation + bool helixNoED; + //! the final helix constraint to be used + mutable HelixConstraint * helixConstraint; + //! whether or not a seed is to be required for an interaction or not bool noSeedRequired; //! explicit seed encodings (optional) @@ -491,8 +517,8 @@ class CommandLineParsing { //! the temperature to be used for energy computations NumberParameter temperature; - //! the prediction target (mfe-single-site, max-prob-site, ..) - CharParameter pred; + //! the interaction model to predict in (mfe-single-site, helix-based-single-site, max-prob-site, ..) + CharParameter model; //! the prediction mode (heuristic, space-efficient, exact) CharParameter predMode; #if INTARNA_MULITHREADING @@ -695,6 +721,37 @@ class CommandLineParsing { */ void validate_tIntLoopMax(const int & value); + + /** + * Validates the helixMinBP argument. + * @param value the argument value to validate + */ + void validate_helixMinBP(const int & value); + + /** + * Validates the helixMaxBP argument. + * @param value the argument value to validate + */ + void validate_helixMaxBP(const int & value); + + /** + * Validates the helixMaxIL argument. + * @param value the argument value to validate + */ + void validate_helixMaxIL(const int & value); + + /** + * Validates the helixMaxED argument. + * @param value the argument value to validate + */ + void validate_helixMaxED(const E_type & value); + + /** + * Validates the helixMaxE argument. + * @param value the argument value to validate + */ + void validate_helixMaxE(const E_type & value); + /** * Validates the target's region argument. * @param value the argument value to validate @@ -790,7 +847,7 @@ class CommandLineParsing { * Validates the prediction target argument. * @param value the argument value to validate */ - void validate_pred(const char & value); + void validate_model(const char & value); /** * Validates the prediction mode argument. @@ -1445,6 +1502,46 @@ void CommandLineParsing::validate_tShapeConversion( const std::string & value ) //////////////////////////////////////////////////////////////////////////// +inline +void CommandLineParsing::validate_helixMinBP(const int &value) { + // forward check to general method + validate_numberArgument("helixMinBP", helixMinBP, value); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void CommandLineParsing::validate_helixMaxBP(const int &value) { + // forward check to general method + validate_numberArgument("helixMaxBP", helixMaxBP, value); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void CommandLineParsing::validate_helixMaxIL(const int & value) { + // forward check to general method + validate_numberArgument("helixMaxIL", helixMaxIL, value); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void CommandLineParsing::validate_helixMaxED(const E_type & value) { + // forward check to general method + validate_numberArgument("helixMaxED", helixMaxED, value); +} + +//////////////////////////////////////////////////////////////////////////// + +inline +void CommandLineParsing::validate_helixMaxE(const E_type & value) { + // forward check to general method + validate_numberArgument("helixMaxE", helixMaxE, value); +} + +//////////////////////////////////////////////////////////////////////////// + inline void CommandLineParsing::validate_seedTQ(const std::string & value) { if (!value.empty()) { @@ -1559,10 +1656,10 @@ void CommandLineParsing::validate_temperature(const T_type & value) { //////////////////////////////////////////////////////////////////////////// inline -void CommandLineParsing::validate_pred(const char & value) +void CommandLineParsing::validate_model(const char & value) { // forward check to general method - validate_charArgument("mode", pred, value); + validate_charArgument("model", model, value); } //////////////////////////////////////////////////////////////////////////// diff --git a/src/bin/IntaRNA.cpp b/src/bin/IntaRNA.cpp index 8d6d9bbd..5de35dbd 100644 --- a/src/bin/IntaRNA.cpp +++ b/src/bin/IntaRNA.cpp @@ -1,6 +1,9 @@ #include "IntaRNA/general.h" +// initialize logging for binary +INITIALIZE_EASYLOGGINGPP + #include #include @@ -20,9 +23,6 @@ #include "IntaRNA/OutputHandlerIntaRNA1.h" #include "IntaRNA/OutputHandlerInteractionList.h" -// initialize logging for binary -INITIALIZE_EASYLOGGINGPP - using namespace IntaRNA; ///////////////////////////////////////////////////////////////////// @@ -52,9 +52,9 @@ int main(int argc, char **argv){ // setup logging with given parameters START_EASYLOGGINGPP(argc, argv); + // check if log file set and update all loggers before going on - el::Logger* logger = el::Loggers::getLogger("default"); - if (logger != NULL && logger->configurations() != NULL && logger->configurations()->hasConfiguration(el::ConfigurationType::Filename)) + if (el::Helpers::commandLineArgs() != NULL && el::Helpers::commandLineArgs()->hasParamWithValue(el::base::consts::kDefaultLogFileParam)) { // default all to file el::Loggers::reconfigureAllLoggers(el::ConfigurationType::ToStandardOutput, std::string("false")); diff --git a/src/easylogging++.LICENCE.txt b/src/easylogging++.LICENCE.txt index 2c8998dd..302737da 100644 --- a/src/easylogging++.LICENCE.txt +++ b/src/easylogging++.LICENCE.txt @@ -1,9 +1,10 @@ The MIT License (MIT) -Copyright (c) 2017 muflihun.com +Copyright (c) 2012-2018 Zuhd Web Services +Copyright (c) 2012-2018 @abumusamq -https://github.com/muflihun/ -https://muflihun.github.io +https://github.com/zuhd-org/ +https://zuhd.org https://muflihun.com Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/src/easylogging++.cc b/src/easylogging++.cc index 5faa9a37..59f92950 100644 --- a/src/easylogging++.cc +++ b/src/easylogging++.cc @@ -1,16 +1,16 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.95.0 +// Easylogging++ v9.96.7 // Cross-platform logging library for C++ applications // -// Copyright (c) 2017 muflihun.com +// Copyright (c) 2012-2018 Zuhd Web Services +// Copyright (c) 2012-2018 @abumusamq // // This library is released under the MIT Licence. -// http://labs.muflihun.com/easyloggingpp/licence.php +// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE // -// https://github.com/muflihun/easyloggingpp -// https://muflihun.github.io/easyloggingpp +// https://zuhd.org // http://muflihun.com // @@ -22,8 +22,92 @@ INITIALIZE_EASYLOGGINGPP namespace el { -// el::base::utils +// el::base namespace base { +// el::base::consts +namespace consts { + +// Level log values - These are values that are replaced in place of %level format specifier +// Extra spaces after format specifiers are only for readability purposes in log files +static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO"); +static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); +static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING"); +static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); +static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); +static const base::type::char_t* kVerboseLevelLogValue = + ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level +static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); +static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); +static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); +static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); +static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); +static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); +static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); +static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); +// Format specifiers - These are used to define log format +static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); +static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); +static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); +static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); +static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); +static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); +static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); +static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); +static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); +static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); +static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); +static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); +static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); +static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); +static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); +static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; +// Date/time +static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", + "September", "October", "November", "December" + }; +static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; +static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; +static const int kYearBase = 1900; +static const char* kAm = "AM"; +static const char* kPm = "PM"; +// Miscellaneous constants + +static const char* kNullPointer = "nullptr"; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +static const base::type::VerboseLevel kMaxVerboseLevel = 9; +static const char* kUnknownUser = "user"; +static const char* kUnknownHost = "unknown-host"; + + +//---------------- DEFAULT LOG FILE ----------------------- + +#if defined(ELPP_NO_DEFAULT_LOG_FILE) +# if ELPP_OS_UNIX +static const char* kDefaultLogFile = "/dev/null"; +# elif ELPP_OS_WINDOWS +static const char* kDefaultLogFile = "nul"; +# endif // ELPP_OS_UNIX +#elif defined(ELPP_DEFAULT_LOG_FILE) +static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +static const char* kDefaultLogFile = "myeasylog.log"; +#endif // defined(ELPP_NO_DEFAULT_LOG_FILE) + + +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kValidLoggerIdSymbols = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; +static const char* kConfigurationComment = "##"; +static const char* kConfigurationLevel = "*"; +static const char* kConfigurationLoggerId = "--"; +} +// el::base::utils namespace utils { /// @brief Aborts application due with user-defined status @@ -276,11 +360,7 @@ void Configurations::set(Configuration* conf) { void Configurations::setToDefault(void) { setGlobally(ConfigurationType::Enabled, std::string("true"), true); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); -#else - ELPP_UNUSED(base::consts::kDefaultLogFile); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) #if defined(ELPP_NO_LOG_TO_FILE) setGlobally(ConfigurationType::ToFile, std::string("false"), true); #else @@ -309,9 +389,7 @@ void Configurations::setRemainingToDefault(void) { #else unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); #endif // defined(ELPP_NO_LOG_TO_FILE) -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); @@ -569,7 +647,6 @@ void Logger::configure(const Configurations& configurations) { if (m_typedConfigurations != nullptr) { Configurations* c = const_cast(m_typedConfigurations->configurations()); if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { - // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE flush(); } } @@ -613,10 +690,11 @@ void Logger::flush(Level level, base::type::fstream_t* fs) { } if (fs != nullptr) { fs->flush(); - std::map::iterator iter = m_unflushedCount.find(level); + std::unordered_map::iterator iter = m_unflushedCount.find(level); if (iter != m_unflushedCount.end()) { iter->second = 0; } + Helpers::validateFileRolling(this, level); } } @@ -675,10 +753,9 @@ std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { if (fs == nullptr) { return 0; } - std::streampos currPos = fs->tellg(); - fs->seekg(0, fs->end); + // Since the file stream is appended to or truncated, the current + // offset is the file size. std::size_t size = static_cast(fs->tellg()); - fs->seekg(currPos); return size; } @@ -850,7 +927,7 @@ void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::st std::size_t foundAt = base::type::string_t::npos; while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { - str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + str.erase(foundAt - 1, 1); ++foundAt; } else { str.replace(foundAt, replaceWhat.length(), replaceWith); @@ -997,8 +1074,9 @@ const std::string OS::getBashOutput(const char* command) { char hBuff[4096]; if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { pclose(proc); - if (hBuff[strlen(hBuff) - 1] == '\n') { - hBuff[strlen(hBuff) - 1] = '\0'; + const std::size_t buffLen = strlen(hBuff); + if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { + hBuff[buffLen - 1] = '\0'; } return std::string(hBuff); } else { @@ -1272,7 +1350,7 @@ bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { } const char* CommandLineArgs::getParamValue(const char* paramKey) const { - std::map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); + std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; } @@ -1418,7 +1496,7 @@ void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { if (hasFlag(flag)) { // If we already have flag we remove the escape chars so that '%%' is turned to '%' // even after specifier resolution - this is because we only replaceFirst specifier - formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + formatCopy.erase(foundAt - 1, 1); ++foundAt; } } else { @@ -1621,10 +1699,11 @@ void TypedConfigurations::build(Configurations* configurations) { } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { - setValue(conf->level(), static_cast(getULong(conf->value())), &m_maxLogFileSizeMap); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - withFileSizeLimit.push_back(conf); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + auto v = getULong(conf->value()); + setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap); + if (v != 0) { + withFileSizeLimit.push_back(conf); + } } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); } @@ -1698,12 +1777,6 @@ std::string TypedConfigurations::resolveFilename(const std::string& filename) { } void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { -#if defined(ELPP_NO_LOG_TO_FILE) - setValue(level, false, &m_toFileMap); - ELPP_UNUSED(fullFilename); - m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(nullptr))); - return; -#endif std::string resolvedFilename = resolveFilename(fullFilename); if (resolvedFilename.empty()) { std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" @@ -1839,8 +1912,10 @@ bool RegisteredLoggers::remove(const std::string& id) { if (id == base::consts::kDefaultLoggerId) { return false; } + // get has internal lock Logger* logger = base::utils::Registry::get(id); if (logger != nullptr) { + // unregister has internal lock unregister(logger); } return true; @@ -1948,7 +2023,7 @@ bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { } else { char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; base::utils::File::buildBaseFilename(file, baseFilename); - std::map::iterator it = m_modules.begin(); + std::unordered_map::iterator it = m_modules.begin(); for (; it != m_modules.end(); ++it) { if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { return vlevel <= it->second; @@ -1989,20 +2064,26 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), m_flags(ELPP_DEFAULT_LOGGING_FLAGS), m_vRegistry(new base::VRegistry(0, &m_flags)), + #if ELPP_ASYNC_LOGGING m_asyncLogQueue(new base::AsyncLogQueue()), m_asyncDispatchWorker(asyncDispatchWorker), #endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { // Register default logger m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); // We register default logger anyway (worse case it's not going to register) just in case m_registeredLoggers->get("default"); + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) // Register performance logger and reconfigure format Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); m_registeredLoggers->get("performance"); performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); performanceLogger->reconfigure(); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + #if defined(ELPP_SYSLOG) // Register syslog logger and reconfigure format Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); @@ -2045,7 +2126,7 @@ Storage::~Storage(void) { } bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), formatSpecifier) != m_customFormatSpecifiers.end(); } @@ -2054,12 +2135,12 @@ void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFo if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { return; } - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); m_customFormatSpecifiers.push_back(customFormatSpecifier); } bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), formatSpecifier); if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { @@ -2097,9 +2178,33 @@ void Storage::setApplicationArguments(int argc, char** argv) { #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) } +} // namespace base + +// LogDispatchCallback +void LogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) + base::threading::ScopedLock scopedLock(m_fileLocksMapLock); + std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()); + auto lock = m_fileLocks.find(filename); + if (lock == m_fileLocks.end()) { + m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr(new base::threading::Mutex))); + } +#endif +} + +base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) { + auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level())); + return *(it->second.get()); +} + +namespace base { // DefaultLogDispatchCallback void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) + LogDispatchCallback::handle(data); + base::threading::ScopedLock scopedLock(fileHandle(data)); +#endif m_data = data; dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), m_data->dispatchAction() == base::DispatchAction::NormalLog)); @@ -2348,6 +2453,8 @@ base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); } #if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); + ELPP_UNUSED(lock_); for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); it != ELPP->customFormatSpecifiers()->end(); ++it) { std::string fs(it->formatSpecifier()); @@ -2368,10 +2475,15 @@ void LogDispatcher::dispatch(void) { if (!m_proceed) { return; } +#ifndef ELPP_NO_GLOBAL_LOCK + // see https://github.com/muflihun/easyloggingpp/issues/580 + // global lock is turned off by default unless + // ELPP_NO_GLOBAL_LOCK is defined base::threading::ScopedLock scopedLock(ELPP->lock()); - base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; +#endif + base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations; if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { - tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback()); } LogDispatchCallback* callback = nullptr; LogDispatchData data; @@ -2379,7 +2491,7 @@ void LogDispatcher::dispatch(void) { : ELPP->m_logDispatchCallbacks) { callback = h.second.get(); if (callback != nullptr && callback->enabled()) { - data.setLogMessage(&m_logMessage); + data.setLogMessage(m_logMessage); data.setDispatchAction(m_dispatchAction); callback->handle(&data); } @@ -2426,6 +2538,7 @@ Writer& Writer::construct(int count, const char* loggerIds, ...) { va_list loggersList; va_start(loggersList, loggerIds); const char* id = loggerIds; + m_loggerIds.reserve(count); for (int i = 0; i < count; ++i) { m_loggerIds.push_back(std::string(id)); id = va_arg(loggersList, const char*); @@ -2444,12 +2557,12 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); } if (m_logger == nullptr) { - ELPP->acquireLock(); - if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { - // Somehow default logger has been unregistered. Not good! Register again - ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + { + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } } - ELPP->releaseLock(); // Need to unlock it for next writer Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) << "Logger [" << loggerId << "] is not registered yet!"; m_proceed = false; @@ -2510,8 +2623,13 @@ void Writer::processDispatch() { void Writer::triggerDispatch(void) { if (m_proceed) { - base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, - m_logger), m_dispatchAction).dispatch(); + if (m_msg == nullptr) { + LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger); + base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch(); + } else { + base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch(); + } } if (m_logger != nullptr) { m_logger->stream().str(ELPP_LITERAL("")); @@ -2524,7 +2642,7 @@ void Writer::triggerDispatch(void) { std::stringstream reasonStream; reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" << " If you wish to disable 'abort on fatal log' please use " - << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; base::utils::abort(1, reasonStream.str()); } m_proceed = false; @@ -2640,18 +2758,19 @@ namespace debug { // StackTrace -StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, - const char* addr) { - m_index = index; - m_location = std::string(loc); - m_demangled = std::string(demang); - m_hex = std::string(hex); - m_addr = std::string(addr); +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, + const std::string& hex, + const std::string& addr) : + m_index(index), + m_location(loc), + m_demangled(demang), + m_hex(hex), + m_addr(addr) { } std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { - ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled - << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr << + (si.m_demangled.empty() ? "" : ":") << si.m_demangled; return ss; } @@ -2671,44 +2790,40 @@ void StackTrace::generateNew(void) { char** strings = backtrace_symbols(stack, size); if (size > kStackStart) { // Skip StackTrace c'tor and generateNew for (std::size_t i = kStackStart; i < size; ++i) { - char* mangName = nullptr; - char* hex = nullptr; - char* addr = nullptr; - for (char* c = strings[i]; *c; ++c) { - switch (*c) { - case '(': - mangName = c; - break; - case '+': - hex = c; - break; - case ')': - addr = c; - break; - default: - break; - } + std::string mangName; + std::string location; + std::string hex; + std::string addr; + + // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21 + const std::string line(strings[i]); + auto p = line.find("_"); + if (p != std::string::npos) { + mangName = line.substr(p); + mangName = mangName.substr(0, mangName.find(" +")); + } + p = line.find("0x"); + if (p != std::string::npos) { + addr = line.substr(p); + addr = addr.substr(0, addr.find("_")); } // Perform demangling if parsed properly - if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { - *mangName++ = '\0'; - *hex++ = '\0'; - *addr++ = '\0'; + if (!mangName.empty()) { int status = 0; - char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status); // if demangling is successful, output the demangled function name if (status == 0) { // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) - StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + StackTraceEntry entry(i - 1, location, demangName, hex, addr); m_stack.push_back(entry); } else { // Not successful - we will use mangled name - StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + StackTraceEntry entry(i - 1, location, mangName, hex, addr); m_stack.push_back(entry); } free(demangName); } else { - StackTraceEntry entry(i - 1, strings[i]); + StackTraceEntry entry(i - 1, line); m_stack.push_back(entry); } } @@ -2742,6 +2857,9 @@ static std::string crashReason(int sig) { } /// @brief Logs reason of crash from sig static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) { + return; + } std::stringstream ss; ss << "CRASH HANDLED; "; ss << crashReason(sig); @@ -2820,7 +2938,6 @@ void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, c // Loggers Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { - base::threading::ScopedLock scopedLock(ELPP->lock()); return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); } @@ -2829,12 +2946,10 @@ void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { } bool Loggers::unregisterLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); return ELPP->registeredLoggers()->remove(identity); } bool Loggers::hasLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); return ELPP->registeredLoggers()->has(identity); } @@ -2984,11 +3099,11 @@ void Loggers::clearVModules(void) { // VersionInfo const std::string VersionInfo::version(void) { - return std::string("9.95.0"); + return std::string("9.96.7"); } /// @brief Release date of current version const std::string VersionInfo::releaseDate(void) { - return std::string("02-08-2017 2312hrs"); + return std::string("24-11-2018 0728hrs"); } } // namespace el diff --git a/src/easylogging++.h b/src/easylogging++.h index 7e4a6831..d5842f8f 100644 --- a/src/easylogging++.h +++ b/src/easylogging++.h @@ -1,18 +1,20 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.95.0 +// Easylogging++ v9.96.7 ==> extended by Martin Raden (kDefaultLogFileParam now part of header to be accessible) +// // Single-header only, cross-platform logging library for C++ applications // -// Copyright (c) 2017 muflihun.com +// Copyright (c) 2012-2018 Zuhd Web Services +// Copyright (c) 2012-2018 @abumusamq // // This library is released under the MIT Licence. -// http://labs.muflihun.com/easyloggingpp/licence.php +// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE // -// https://github.com/muflihun/easyloggingpp -// https://muflihun.github.io/easyloggingpp +// https://zuhd.org // http://muflihun.com // + #ifndef EASYLOGGINGPP_H #define EASYLOGGINGPP_H // Compilers and C++0x/C++11 Evaluation @@ -93,7 +95,7 @@ #else # define ELPP_OS_MAC 0 #endif -#if (defined(__FreeBSD__)) +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) # define ELPP_OS_FREEBSD 1 #else # define ELPP_OS_FREEBSD 0 @@ -103,13 +105,23 @@ #else # define ELPP_OS_SOLARIS 0 #endif +#if (defined(_AIX)) +# define ELPP_OS_AIX 1 +#else +# define ELPP_OS_AIX 0 +#endif #if (defined(__NetBSD__)) # define ELPP_OS_NETBSD 1 #else # define ELPP_OS_NETBSD 0 #endif +#if defined(__EMSCRIPTEN__) +# define ELPP_OS_EMSCRIPTEN 1 +#else +# define ELPP_OS_EMSCRIPTEN 0 +#endif // Unix -#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN) && (!ELPP_OS_WINDOWS)) # define ELPP_OS_UNIX 1 #else # define ELPP_OS_UNIX 0 @@ -194,7 +206,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # define ELPP_INTERNAL_INFO(lvl, msg) #endif // (defined(ELPP_DEBUG_INFO)) #if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) -# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN) # define ELPP_STACKTRACE 1 # else # if ELPP_COMPILER_MSVC @@ -373,6 +385,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #include #include #include +#include #include #include #include @@ -410,9 +423,6 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # if defined(ELPP_LOG_STD_ARRAY) # include # endif // defined(ELPP_LOG_STD_ARRAY) -# if defined(ELPP_LOG_UNORDERED_MAP) -# include -# endif // defined(ELPP_LOG_UNORDERED_MAP) # if defined(ELPP_LOG_UNORDERED_SET) # include # endif // defined(ELPP_UNORDERED_SET) @@ -580,6 +590,16 @@ enum class Level : base::type::EnumType { /// @brief Represents unknown level Unknown = 1010 }; +} // namespace el +namespace std { +template<> struct hash { + public: + std::size_t operator()(const el::Level& l) const { + return hash {}(static_cast(l)); + } +}; +} +namespace el { /// @brief Static class that contains helper functions for el::Level class LevelHelper : base::StaticClass { public: @@ -702,115 +722,47 @@ enum class LoggingFlag : base::type::EnumType { /// @brief Adds spaces b/w logs that separated by left-shift operator AutoSpacing = 8192, /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) - FixedTimeFormat = 16384 + FixedTimeFormat = 16384, + // @brief Ignore SIGINT or crash + IgnoreSigInt = 32768, }; namespace base { /// @brief Namespace containing constants used internally. namespace consts { -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" -#endif -// Level log values - These are values that are replaced in place of %level format specifier -// Extra spaces after format specifiers are only for readability purposes in log files -static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO"); -static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); -static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING"); -static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); -static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); -static const base::type::char_t* kVerboseLevelLogValue = - ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level -static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); -static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); -static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); -static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); -static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); -static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); -static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); -static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); -// Format specifiers - These are used to define log format -static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); -static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); -static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); -static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); -static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); -static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); -static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); -static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); -static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); -static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); -static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); -static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); -static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); -static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); -static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); -static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; -// Date/time -static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; -static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", - "September", "October", "November", "December" - }; -static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; -static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; -static const int kYearBase = 1900; -static const char* kAm = "AM"; -static const char* kPm = "PM"; -// Miscellaneous constants +static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierChar = '%'; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kDefaultSubsecondPrecision = 3; + #ifdef ELPP_DEFAULT_LOGGER static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; #else static const char* kDefaultLoggerId = "default"; #endif + +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) #ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; #else static const char* kPerformanceLoggerId = "performance"; +#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER #endif + #if defined(ELPP_SYSLOG) static const char* kSysLogLoggerId = "syslog"; #endif // defined(ELPP_SYSLOG) -static const char* kNullPointer = "nullptr"; -static const char kFormatSpecifierChar = '%'; -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED -static const char kFormatSpecifierCharValue = 'v'; -#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED -static const unsigned int kMaxLogPerContainer = 100; -static const unsigned int kMaxLogPerCounter = 100000; -static const unsigned int kDefaultSubsecondPrecision = 3; -static const base::type::VerboseLevel kMaxVerboseLevel = 9; -static const char* kUnknownUser = "user"; -static const char* kUnknownHost = "unknown-host"; -#if defined(ELPP_DEFAULT_LOG_FILE) -static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; -#else -# if ELPP_OS_UNIX -# if ELPP_OS_ANDROID -static const char* kDefaultLogFile = "logs/myeasylog.log"; -# else -static const char* kDefaultLogFile = "logs/myeasylog.log"; -# endif // ELPP_OS_ANDROID -# elif ELPP_OS_WINDOWS -static const char* kDefaultLogFile = "logs\\myeasylog.log"; -# endif // ELPP_OS_UNIX -#endif // defined(ELPP_DEFAULT_LOG_FILE) -#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -static const char* kDefaultLogFileParam = "--default-log-file"; -#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) -static const char* kLoggingFlagsParam = "--logging-flags"; -#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) + #if ELPP_OS_WINDOWS static const char* kFilePathSeperator = "\\"; #else static const char* kFilePathSeperator = "/"; #endif // ELPP_OS_WINDOWS -static const char* kValidLoggerIdSymbols = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; -static const char* kConfigurationComment = "##"; -static const char* kConfigurationLevel = "*"; -static const char* kConfigurationLoggerId = "--"; + static const std::size_t kSourceFilenameMaxLength = 100; static const std::size_t kSourceLineMaxLength = 10; static const Level kPerformanceTrackerDefaultLevel = Level::Info; @@ -855,9 +807,6 @@ const struct { }, }; static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif } // namespace consts } // namespace base typedef std::function PreRollOutCallback; @@ -1257,8 +1206,8 @@ class DateTime : base::StaticClass { base::TimestampUnit timestampUnit); - private: static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + private: static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, std::size_t msec, const base::SubsecondPrecision* ssPrec); }; @@ -1297,7 +1246,7 @@ class CommandLineArgs { private: int m_argc; char** m_argv; - std::map m_paramsWithValue; + std::unordered_map m_paramsWithValue; std::vector m_params; }; /// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. @@ -1422,7 +1371,7 @@ class AbstractRegistry : public base::threading::ThreadSafe { /// of AbstractRegistry. Any implementation of this class should be /// explicitly (by using lock functions) template -class Registry : public AbstractRegistry> { +class Registry : public AbstractRegistry> { public: typedef typename Registry::iterator iterator; typedef typename Registry::const_iterator const_iterator; @@ -1486,7 +1435,7 @@ class Registry : public AbstractRegistry> { } private: - virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { registerNew(it->first, new T_Ptr(*it->second)); } @@ -1586,7 +1535,7 @@ class RegistryWithPred : public AbstractRegistry> { class Utils { public: template - static bool installCallback(const std::string& id, std::map* mapT) { + static bool installCallback(const std::string& id, std::unordered_map* mapT) { if (mapT->find(id) == mapT->end()) { mapT->insert(std::make_pair(id, TPtr(new T()))); return true; @@ -1595,15 +1544,15 @@ class Utils { } template - static void uninstallCallback(const std::string& id, std::map* mapT) { + static void uninstallCallback(const std::string& id, std::unordered_map* mapT) { if (mapT->find(id) != mapT->end()) { mapT->erase(id); } } template - static T* callback(const std::string& id, std::map* mapT) { - typename std::map::iterator iter = mapT->find(id); + static T* callback(const std::string& id, std::unordered_map* mapT) { + typename std::unordered_map::iterator iter = mapT->find(id); if (iter != mapT->end()) { return static_cast(iter->second.get()); } @@ -1948,7 +1897,7 @@ class Configurations : public base::utils::RegistryWithPred FileStreamPtr; -typedef std::map LogStreamsReferenceMap; +typedef std::unordered_map LogStreamsReferenceMap; /// @brief Configurations with data types. /// /// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. @@ -1985,16 +1934,16 @@ class TypedConfigurations : public base::threading::ThreadSafe { private: Configurations* m_configurations; - std::map m_enabledMap; - std::map m_toFileMap; - std::map m_filenameMap; - std::map m_toStandardOutputMap; - std::map m_logFormatMap; - std::map m_subsecondPrecisionMap; - std::map m_performanceTrackingMap; - std::map m_fileStreamMap; - std::map m_maxLogFileSizeMap; - std::map m_logFlushThresholdMap; + std::unordered_map m_enabledMap; + std::unordered_map m_toFileMap; + std::unordered_map m_filenameMap; + std::unordered_map m_toStandardOutputMap; + std::unordered_map m_logFormatMap; + std::unordered_map m_subsecondPrecisionMap; + std::unordered_map m_performanceTrackingMap; + std::unordered_map m_fileStreamMap; + std::unordered_map m_maxLogFileSizeMap; + std::unordered_map m_logFlushThresholdMap; base::LogStreamsReferenceMap* m_logStreamsReference; friend class el::Helpers; @@ -2004,21 +1953,21 @@ class TypedConfigurations : public base::threading::ThreadSafe { friend class el::base::LogDispatcher; template - inline Conf_T getConfigByVal(Level level, const std::map* confMap, const char* confName) { + inline Conf_T getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { base::threading::ScopedLock scopedLock(lock()); return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope } template - inline Conf_T& getConfigByRef(Level level, std::map* confMap, const char* confName) { + inline Conf_T& getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { base::threading::ScopedLock scopedLock(lock()); return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope } template - Conf_T unsafeGetConfigByVal(Level level, const std::map* confMap, const char* confName) { + Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { ELPP_UNUSED(confName); - typename std::map::const_iterator it = confMap->find(level); + typename std::unordered_map::const_iterator it = confMap->find(level); if (it == confMap->end()) { try { return confMap->at(Level::Global); @@ -2033,9 +1982,9 @@ class TypedConfigurations : public base::threading::ThreadSafe { } template - Conf_T& unsafeGetConfigByRef(Level level, std::map* confMap, const char* confName) { + Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { ELPP_UNUSED(confName); - typename std::map::iterator it = confMap->find(level); + typename std::unordered_map::iterator it = confMap->find(level); if (it == confMap->end()) { try { return confMap->at(Level::Global); @@ -2049,14 +1998,15 @@ class TypedConfigurations : public base::threading::ThreadSafe { } template - void setValue(Level level, const Conf_T& value, std::map* confMap, bool includeGlobalLevel = true) { + void setValue(Level level, const Conf_T& value, std::unordered_map* confMap, + bool includeGlobalLevel = true) { // If map is empty and we are allowed to add into generic level (Level::Global), do it! if (confMap->empty() && includeGlobalLevel) { confMap->insert(std::make_pair(Level::Global, value)); return; } // If same value exist in generic level already, dont add it to explicit level - typename std::map::iterator it = confMap->find(Level::Global); + typename std::unordered_map::iterator it = confMap->find(Level::Global); if (it != confMap->end() && it->second == value) { return; } @@ -2218,21 +2168,26 @@ class LogDispatchData { inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } - private: - LogMessage* m_logMessage; - base::DispatchAction m_dispatchAction; - friend class base::LogDispatcher; - inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + }; class LogDispatchCallback : public Callback { + protected: + virtual void handle(const LogDispatchData* data); + base::threading::Mutex& fileHandle(const LogDispatchData* data); private: friend class base::LogDispatcher; + std::unordered_map> m_fileLocks; + base::threading::Mutex m_fileLocksMapLock; }; class PerformanceTrackingCallback : public Callback { private: @@ -2350,7 +2305,7 @@ inline void FUNCTION_NAME(const T&); std::string m_parentApplicationName; bool m_isConfigured; Configurations m_configurations; - std::map m_unflushedCount; + std::unordered_map m_unflushedCount; base::LogStreamsReferenceMap* m_logStreamsReference; LogBuilderPtr m_logBuilder; @@ -2456,7 +2411,7 @@ class RegisteredLoggers : public base::utils::Registry { LogBuilderPtr m_defaultLogBuilder; Configurations m_defaultConfigurations; base::LogStreamsReferenceMap m_logStreamsReference; - std::map m_loggerRegistrationCallbacks; + std::unordered_map m_loggerRegistrationCallbacks; friend class el::base::Storage; void unsafeFlushAll(void); @@ -2482,7 +2437,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { bool allowed(base::type::VerboseLevel vlevel, const char* file); - inline const std::map& modules(void) const { + inline const std::unordered_map& modules(void) const { return m_modules; } @@ -2496,7 +2451,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { private: base::type::VerboseLevel m_level; base::type::EnumType* m_pFlags; - std::map m_modules; + std::unordered_map m_modules; }; } // namespace base class LogMessage { @@ -2680,6 +2635,10 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { return &m_customFormatSpecifiers; } + base::threading::Mutex& customFormatSpecifiersLock() { + return m_customFormatSpecifiersLock; + } + inline void setLoggingLevel(Level level) { m_loggingLevel = level; } @@ -2720,12 +2679,13 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { /// @brief Sets thread name for current thread. Requires std::thread inline void setThreadName(const std::string& name) { if (name.empty()) return; - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(m_threadNamesLock); m_threadNames[base::threading::getCurrentThreadId()] = name; } inline std::string getThreadName(const std::string& threadId) { - std::map::const_iterator it = m_threadNames.find(threadId); + base::threading::ScopedLock scopedLock(m_threadNamesLock); + std::unordered_map::const_iterator it = m_threadNames.find(threadId); if (it == m_threadNames.end()) { return threadId; } @@ -2742,10 +2702,12 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { #endif // ELPP_ASYNC_LOGGING base::utils::CommandLineArgs m_commandLineArgs; PreRollOutCallback m_preRollOutCallback; - std::map m_logDispatchCallbacks; - std::map m_performanceTrackingCallbacks; - std::map m_threadNames; + std::unordered_map m_logDispatchCallbacks; + std::unordered_map m_performanceTrackingCallbacks; + std::unordered_map m_threadNames; std::vector m_customFormatSpecifiers; + base::threading::Mutex m_customFormatSpecifiersLock; + base::threading::Mutex m_threadNamesLock; Level m_loggingLevel; friend class el::Helpers; @@ -2788,7 +2750,7 @@ class AsyncDispatchWorker : public base::IWorker, public base::threading::Thread void run(void); void setContinueRunning(bool value) { - base::threading::ScopedLock scopedLock(m_continueRunningMutex); + base::threading::ScopedLock scopedLock(m_continueRunningLock); m_continueRunning = value; } @@ -2798,7 +2760,7 @@ class AsyncDispatchWorker : public base::IWorker, public base::threading::Thread private: std::condition_variable cv; bool m_continueRunning; - base::threading::Mutex m_continueRunningMutex; + base::threading::Mutex m_continueRunningLock; }; #endif // ELPP_ASYNC_LOGGING } // namespace base @@ -2810,9 +2772,9 @@ class DefaultLogBuilder : public LogBuilder { /// @brief Dispatches log messages class LogDispatcher : base::NoCopy { public: - LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) : m_proceed(proceed), - m_logMessage(std::move(logMessage)), + m_logMessage(logMessage), m_dispatchAction(std::move(dispatchAction)) { } @@ -2820,7 +2782,7 @@ class LogDispatcher : base::NoCopy { private: bool m_proceed; - LogMessage m_logMessage; + LogMessage* m_logMessage; base::DispatchAction m_dispatchAction; }; #if defined(ELPP_STL_LOGGING) @@ -3233,10 +3195,15 @@ class Writer : base::NoCopy { Writer(Level level, const char* file, base::type::LineNumber line, const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, base::type::VerboseLevel verboseLevel = 0) : - m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { } + Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) : + m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown), + m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + virtual ~Writer(void) { processDispatch(); } @@ -3267,6 +3234,7 @@ class Writer : base::NoCopy { Writer& construct(Logger* logger, bool needLock = true); Writer& construct(int count, const char* loggerIds, ...); protected: + LogMessage* m_msg; Level m_level; const char* m_file; const base::type::LineNumber m_line; @@ -3325,6 +3293,7 @@ void Logger::log_(Level level, int vlevel, const T& log) { base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; } else { stream().str(ELPP_LITERAL("")); + releaseLock(); } } else { base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; @@ -3332,23 +3301,23 @@ void Logger::log_(Level level, int vlevel, const T& log) { } template inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(level, 0, s, value, args...); } template inline void Logger::log(Level level, const T& log) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(level, 0, log); } # if ELPP_VERBOSE_LOG template inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(el::Level::Verbose, vlevel, s, value, args...); } template inline void Logger::verbose(int vlevel, const T& log) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(el::Level::Verbose, vlevel, log); } # else @@ -3603,8 +3572,9 @@ class StackTrace : base::NoCopy { static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() class StackTraceEntry { public: - StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr); - StackTraceEntry(std::size_t index, char* loc) : + StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, + const std::string& addr); + StackTraceEntry(std::size_t index, const std::string& loc) : m_index(index), m_location(loc) { } @@ -3789,6 +3759,11 @@ class Helpers : base::StaticClass { static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { return ELPP->commandLineArgs(); } + /// @brief Reserve space for custom format specifiers for performance + /// @see std::vector::reserve + static inline void reserveCustomFormatSpecifiers(std::size_t size) { + ELPP->m_customFormatSpecifiers.reserve(size); + } /// @brief Installs user defined format specifier and handler static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { ELPP->installCustomFormatSpecifier(customFormatSpecifier); @@ -3802,7 +3777,7 @@ class Helpers : base::StaticClass { return ELPP->hasCustomFormatSpecifier(formatSpecifier); } static inline void validateFileRolling(Logger* logger, Level level) { - if (logger == nullptr) return; + if (ELPP == nullptr || logger == nullptr) return; logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); } }; @@ -3948,7 +3923,8 @@ class VersionInfo : base::StaticClass { /// @see el::base::PerformanceTracker /// @see el::base::PerformanceTracker::checkpoint // Note: Do not surround this definition with null macro because of obj instance -#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) #define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) #define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) diff --git a/tests/HelixConstraint_test.cpp b/tests/HelixConstraint_test.cpp new file mode 100644 index 00000000..ce959b7d --- /dev/null +++ b/tests/HelixConstraint_test.cpp @@ -0,0 +1,31 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/HelixConstraint.h" + +using namespace IntaRNA; + +TEST_CASE( "HelixConstraint", "[HelixConstraint]" ) { + + SECTION( "getter", "[HelixConstraint]" ) { + + size_t minBP= 2, maxBP = 10, maxIL=2, maxED=0, maxE=0; + bool noED=false; + + HelixConstraint hC( minBP, maxBP, maxIL, maxED, maxE, noED); + + // check data access + REQUIRE( hC.getMinBasePairs() == 2 ); + REQUIRE( hC.getMaxBasePairs() == 10 ); + REQUIRE( hC.getMaxIL() == 2 ); + REQUIRE( hC.getMaxED() == 0 ); + REQUIRE( hC.getMaxE() == 0 ); + REQUIRE( !hC.useNoED() ); + REQUIRE( hC.getMaxLength1() == 28 ); + REQUIRE( hC.getMaxLength2() == 28 ); + } + + +} diff --git a/tests/HelixHandlerStackingOnlyIdxOffset_test.cpp b/tests/HelixHandlerStackingOnlyIdxOffset_test.cpp new file mode 100644 index 00000000..043ceb05 --- /dev/null +++ b/tests/HelixHandlerStackingOnlyIdxOffset_test.cpp @@ -0,0 +1,564 @@ + +#include "catch.hpp" + +#undef NDEBUG +#define protected public + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerStackingOnly.h" +#include "IntaRNA/HelixHandlerIdxOffset.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/ReverseAccessibility.h" + + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerIdxOffset for StackingOnly", "[HelixHandlerIdxOffset]") { + + SECTION("getter", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGGA"); + RnaSequence r2("r2", "ACCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 10, 0, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerStackingOnly(energy, hC)); + + // Initial offset of 0 + REQUIRE(hhIO.getOffset1() == 0); + REQUIRE(hhIO.getOffset2() == 0); + + // Set offset + hhIO.setOffset1(1); + hhIO.setOffset2(4); + REQUIRE(hhIO.getOffset1() == 1); + REQUIRE(hhIO.getOffset2() == 4); + + // get Constraints + REQUIRE(hhIO.getConstraint().getMinBasePairs() == 2); + REQUIRE(hhIO.getConstraint().getMaxBasePairs() == 10); + + } + + SECTION("Helix with Offset: Case1 - offset 1", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGAAGG"); + RnaSequence r2("r2", "CCAACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2,4, 0, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerStackingOnly(energy, hC)); + + // Set the offsets + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////// FILLHELIXOFFSET ///////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // possible helices + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1()-1, 0, energy.size2()-hhIO.getOffset2()-1)==4); + + // All Working + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -1); + REQUIRE(hhIO.getHelixLength1(0, 0) == 2); + REQUIRE(hhIO.getHelixLength2(0, 0) == hhIO.getHelixLength1(0, 0)); + + // (0,4) + REQUIRE(hhIO.getHelixE(0, 4) == -1); + REQUIRE(hhIO.getHelixLength1(0, 4) == 2); + REQUIRE(hhIO.getHelixLength2(0, 4) == hhIO.getHelixLength1(0, 4)); + + // (0,4) + REQUIRE(hhIO.getHelixE(4, 0) == -1); + REQUIRE(hhIO.getHelixLength1(4, 0) == 2); + REQUIRE(hhIO.getHelixLength2(4, 0) == hhIO.getHelixLength1(4, 0)); + + // (0,4) + REQUIRE(hhIO.getHelixE(4, 4) == -1); + REQUIRE(hhIO.getHelixLength1(4, 4) == 2); + REQUIRE(hhIO.getHelixLength2(4, 4) == hhIO.getHelixLength1(4, 4)); + + // Not Working + // (3,4) + REQUIRE(hhIO.getHelixE(3, 4) == E_INF); + REQUIRE(hhIO.getHelixLength1(3, 4) == 0); + REQUIRE(hhIO.getHelixLength2(3, 4) == hhIO.getHelixLength1(3, 4)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == E_INF); + REQUIRE(hhIO.getHelixLength1(1, 1) == 0); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1,r2); + hhIO.traceBackHelix(interaction,0,0); + + REQUIRE(interaction.basePairs.size() == 0); + + } + + SECTION("Helix with Offset: Case 2 - all complementary", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGGGG"); + RnaSequence r2("r2", "CCCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2,4, 0, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerStackingOnly(energy, hC)); + + // Set the offsets + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////// FILLHELIXOFFSET ///////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // possible helices + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1()-1, 0, energy.size2()-hhIO.getOffset2()-1)==16); + + // All optimal combinations + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -3); + REQUIRE(hhIO.getHelixLength1(0, 0) == 4); + REQUIRE(hhIO.getHelixLength2(0, 0) == hhIO.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhIO.getHelixE(0, 1) == -3); + REQUIRE(hhIO.getHelixLength1(0, 1) == 4); + REQUIRE(hhIO.getHelixLength2(0, 1) == hhIO.getHelixLength1(0, 1)); + + // (0,2) + REQUIRE(hhIO.getHelixE(0, 2) == -2); + REQUIRE(hhIO.getHelixLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixLength2(0, 2) == hhIO.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhIO.getHelixE(0, 3) == -1); + REQUIRE(hhIO.getHelixLength1(0, 3) == 2); + REQUIRE(hhIO.getHelixLength2(0, 3) == hhIO.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhIO.getHelixE(1, 0) == -3); + REQUIRE(hhIO.getHelixLength1(1, 0) == 4); + REQUIRE(hhIO.getHelixLength2(1, 0) == hhIO.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == -3); + REQUIRE(hhIO.getHelixLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + // (1,2) + REQUIRE(hhIO.getHelixE(1, 2) == -2); + REQUIRE(hhIO.getHelixLength1(1, 2) == 3); + REQUIRE(hhIO.getHelixLength2(1, 2) == hhIO.getHelixLength1(1, 2)); + + // (1,3) + REQUIRE(hhIO.getHelixE(1, 3) == -1); + REQUIRE(hhIO.getHelixLength1(1, 3) == 2); + REQUIRE(hhIO.getHelixLength2(1, 3) == hhIO.getHelixLength1(1, 3)); + + // (2,0) + REQUIRE(hhIO.getHelixE(2, 0) == -2); + REQUIRE(hhIO.getHelixLength1(2, 0) == 3); + REQUIRE(hhIO.getHelixLength2(2, 0) == hhIO.getHelixLength1(2, 0)); + + // (2,1) + REQUIRE(hhIO.getHelixE(2, 1) == -2); + REQUIRE(hhIO.getHelixLength1(2, 1) == 3); + REQUIRE(hhIO.getHelixLength2(2, 1) == hhIO.getHelixLength1(2, 1)); + + // (2,2) + REQUIRE(hhIO.getHelixE(2, 2) == -2); + REQUIRE(hhIO.getHelixLength1(2, 2) == 3); + REQUIRE(hhIO.getHelixLength2(2, 2) == hhIO.getHelixLength1(2, 2)); + + // (2,3) + REQUIRE(hhIO.getHelixE(2, 3) == -1); + REQUIRE(hhIO.getHelixLength1(2, 3) == 2); + REQUIRE(hhIO.getHelixLength2(2, 3) == hhIO.getHelixLength1(2, 3)); + + // (3,0) + REQUIRE(hhIO.getHelixE(3, 0) == -1); + REQUIRE(hhIO.getHelixLength1(3, 0) == 2); + REQUIRE(hhIO.getHelixLength2(3, 0) == hhIO.getHelixLength1(3, 0)); + + // (3,1) + REQUIRE(hhIO.getHelixE(3, 1) == -1); + REQUIRE(hhIO.getHelixLength1(3, 1) == 2); + REQUIRE(hhIO.getHelixLength2(3, 1) == hhIO.getHelixLength1(3, 1)); + + // (3,2) + REQUIRE(hhIO.getHelixE(3, 2) == -1); + REQUIRE(hhIO.getHelixLength1(3, 2) == 2); + REQUIRE(hhIO.getHelixLength2(3, 2) == hhIO.getHelixLength1(3, 2)); + + // (3,3) + REQUIRE(hhIO.getHelixE(3, 3) == -1); + REQUIRE(hhIO.getHelixLength1(3, 3) == 2); + REQUIRE(hhIO.getHelixLength2(3, 3) == hhIO.getHelixLength1(3, 3)); + + + // Several cases that do not allow a helix + + // (4,4) + REQUIRE(hhIO.getHelixE(4, 4) == E_INF); + REQUIRE(hhIO.getHelixLength1(4, 4) == 0); + REQUIRE(hhIO.getHelixLength2(4, 4) == hhIO.getHelixLength1(4, 4)); + + // (4,3) + REQUIRE(hhIO.getHelixE(0, 4) == E_INF); + REQUIRE(hhIO.getHelixLength1(0, 4) == 0); + REQUIRE(hhIO.getHelixLength2(0, 4) == hhIO.getHelixLength1(0, 4)); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhIO.traceBackHelix(interaction, 0, 0); + + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (2,1) + ////////////////////// + interaction.clear(); + + hhIO.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + } + + SECTION("Helix with Offset: Case 3 - no interaction possible", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AAAAAAA"); + RnaSequence r2("r2", "AAAAAAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2,4, 0, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerStackingOnly(energy, hC)); + + // Set the offsets + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////// FILLHELIXOFFSET ///////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // possible helices + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1()-1, 0, energy.size2()-hhIO.getOffset2()-1)==0); + + // Not Working + + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == E_INF); + REQUIRE(hhIO.getHelixLength1(0, 0) == 0); + REQUIRE(hhIO.getHelixLength2(0, 0) == hhIO.getHelixLength1(0, 0)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == E_INF); + REQUIRE(hhIO.getHelixLength1(1, 1) == 0); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + // (3,4) + REQUIRE(hhIO.getHelixE(3, 4) == E_INF); + REQUIRE(hhIO.getHelixLength1(3, 4) == 0); + REQUIRE(hhIO.getHelixLength2(3, 4) == hhIO.getHelixLength1(3, 4)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == E_INF); + REQUIRE(hhIO.getHelixLength1(1, 1) == 0); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if INTARNA_IN_DEBUG_MODE + + // Case (0,0) + Interaction interaction(r1,r2); + REQUIRE_THROWS_WITH(hhIO.traceBackHelix(interaction, 0, 0), "HelixHandlerStackingOnly::traceBackHelix(i1=1,i2=1) no helix known (E_INF)"); + + + // Case (2,1) + interaction.clear(); + REQUIRE_THROWS_WITH(hhIO.traceBackHelix(interaction, 2, 1), "HelixHandlerStackingOnly::traceBackHelix(i1=3,i2=2) no helix known (E_INF)"); + +#endif + + } + + SECTION("Helix with Offset: Case 3 - A 'wall' of A's disrupts the possible helices", "[HelixHandlerIdxOffset]") { + // Case 2 - sequence containing an "A"-wall to disrupt perfect stacking + RnaSequence r1("r1", "GGGGGAAGG"); + RnaSequence r2("r2", "CCAACCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2,4, 0, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerStackingOnly(energy, hC)); + + // Set the offsets + hhIO.setOffset1(2); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // possible helices + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1()-1, 0, energy.size2()-hhIO.getOffset2()-1)==9); + + REQUIRE_FALSE(energy.areComplementary(5,4)); + // All optimal combinations + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -2); + REQUIRE(hhIO.getHelixLength1(0, 0) == 3); + REQUIRE(hhIO.getHelixLength2(0, 0) == hhIO.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhIO.getHelixE(0, 1) == -1); + REQUIRE(hhIO.getHelixLength1(0, 1) == 2); + REQUIRE(hhIO.getHelixLength2(0, 1) == hhIO.getHelixLength1(0, 1)); + + // (0,5) + REQUIRE(hhIO.getHelixE(0, 5) == -1); + REQUIRE(hhIO.getHelixLength1(0, 5) == 2); + REQUIRE(hhIO.getHelixLength2(0, 5) == hhIO.getHelixLength1(0, 5)); + + // (1,0) + REQUIRE(hhIO.getHelixE(1, 0) == -1); + REQUIRE(hhIO.getHelixLength1(1, 0) == 2); + REQUIRE(hhIO.getHelixLength2(1, 0) == hhIO.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == -1); + REQUIRE(hhIO.getHelixLength1(1, 1) == 2); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + // (1,5) + REQUIRE(hhIO.getHelixE(1, 5) == -1); + REQUIRE(hhIO.getHelixLength1(1, 5) == 2); + REQUIRE(hhIO.getHelixLength2(1, 5) == hhIO.getHelixLength1(1, 5)); + + // (5,0) + REQUIRE(hhIO.getHelixE(5, 0) == -1); + REQUIRE(hhIO.getHelixLength1(5, 0) == 2); + REQUIRE(hhIO.getHelixLength2(5, 0) == hhIO.getHelixLength1(5, 0)); + + // (5,1) + REQUIRE(hhIO.getHelixE(5, 1) == -1); + REQUIRE(hhIO.getHelixLength1(5, 1) == 2); + REQUIRE(hhIO.getHelixLength2(5, 1) == hhIO.getHelixLength1(5, 1)); + + // (5,5) + REQUIRE(hhIO.getHelixE(5, 5) == -1); + REQUIRE(hhIO.getHelixLength1(5, 5) == 2); + REQUIRE(hhIO.getHelixLength2(5, 5) == hhIO.getHelixLength1(5, 5)); + + + + // Not viable cases + // (3,0) Not complementary + REQUIRE(hhIO.getHelixE(3,0) == E_INF); + REQUIRE(hhIO.getHelixLength1(3,0) == 0); + REQUIRE(hhIO.getHelixLength2(3,0) == hhIO.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhIO.getHelixE(2,2) == E_INF); + REQUIRE(hhIO.getHelixLength1(2,2) == 0); + REQUIRE(hhIO.getHelixLength2(2,2) == hhIO.getHelixLength1(2,2)); + + // (5,3) no helix possible + REQUIRE(hhIO.getHelixE(5,3) == E_INF); + REQUIRE(hhIO.getHelixLength1(5,3) == 0); + REQUIRE(hhIO.getHelixLength2(5,3) == hhIO.getHelixLength1(5,3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhIO.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 5); + + // Case (5,5) - Possible but only 2 base pairs long (e.g no bp needs to be reported) + ////////////////////// + + interaction.clear(); + hhIO.traceBackHelix(interaction, 5, 5); + + REQUIRE(interaction.basePairs.size() == 0); + + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + + // Case (2,1) - Not Possible + ////////////////////// + + interaction.clear(); + + REQUIRE_THROWS_WITH(hhIO.traceBackHelix(interaction, 2, 1), "HelixHandlerStackingOnly::traceBackHelix(i1=4,i2=3) no helix known (E_INF)"); + +#endif + } + + SECTION("Helix with Offset: Case 4 - uneven offset", "[HelixHandlerIdxOffset]") { + // Case 2 - sequence containing an "A"-wall to disrupt perfect stacking + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2,4, 0, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerStackingOnly(energy, hC)); + + // Set the offsets + hhIO.setOffset1(1); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // possible helices + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1()-1, 0, energy.size2()-hhIO.getOffset2()-1)==6); + + // All optimal combinations + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -2); + REQUIRE(hhIO.getHelixLength1(0, 0) == 3); + REQUIRE(hhIO.getHelixLength2(0, 0) == hhIO.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhIO.getHelixE(0, 1) == -1); + REQUIRE(hhIO.getHelixLength1(0, 1) == 2); + REQUIRE(hhIO.getHelixLength2(0, 1) == hhIO.getHelixLength1(0, 1)); + + // (1,0) + REQUIRE(hhIO.getHelixE(1, 0) == -2); + REQUIRE(hhIO.getHelixLength1(1, 0) == 3); + REQUIRE(hhIO.getHelixLength2(1, 0) == hhIO.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == -1); + REQUIRE(hhIO.getHelixLength1(1, 1) == 2); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + // (2,0) + REQUIRE(hhIO.getHelixE(2, 0) == -1); + REQUIRE(hhIO.getHelixLength1(2, 0) == 2); + REQUIRE(hhIO.getHelixLength2(2, 0) == hhIO.getHelixLength1(2, 0)); + + // (5,1) + REQUIRE(hhIO.getHelixE(2, 1) == -1); + REQUIRE(hhIO.getHelixLength1(2, 1) == 2); + REQUIRE(hhIO.getHelixLength2(2, 1) == hhIO.getHelixLength1(2, 1)); + + // Not viable cases + // (3,0) Not complementary + REQUIRE(hhIO.getHelixE(3,0) == E_INF); + REQUIRE(hhIO.getHelixLength1(3,0) == 0); + REQUIRE(hhIO.getHelixLength2(3,0) == hhIO.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhIO.getHelixE(2,2) == E_INF); + REQUIRE(hhIO.getHelixLength1(2,2) == 0); + REQUIRE(hhIO.getHelixLength2(2,2) == hhIO.getHelixLength1(2,2)); + + // (5,3) no helix possible + REQUIRE(hhIO.getHelixE(0,2) == E_INF); + REQUIRE(hhIO.getHelixLength1(0,2) == 0); + REQUIRE(hhIO.getHelixLength2(0,2) == hhIO.getHelixLength1(0,2)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhIO.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (5,5) - Possible but only 2 base pairs long (e.g no bp needs to be reported) + ////////////////////// + + interaction.clear(); + hhIO.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 0); + + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + // Case (3,2) - Not Possible + ////////////////////// + + interaction.clear(); + REQUIRE_THROWS_WITH(hhIO.traceBackHelix(interaction, 3, 2), "HelixHandlerStackingOnly::traceBackHelix(i1=4,i2=4) no helix known (E_INF)"); + +#endif + } +} diff --git a/tests/HelixHandlerStackingOnlySeedIdxOffset_test.cpp b/tests/HelixHandlerStackingOnlySeedIdxOffset_test.cpp new file mode 100644 index 00000000..dc54d8cd --- /dev/null +++ b/tests/HelixHandlerStackingOnlySeedIdxOffset_test.cpp @@ -0,0 +1,451 @@ + +#include "catch.hpp" + +#undef NDEBUG +#define protected public + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerStackingOnly.h" +#include "IntaRNA/HelixHandlerIdxOffset.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/SeedHandlerMfe.h" + + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerIdxOffset for StackingOnlySeed", "[HelixHandlerIdxOffset]") { + + SECTION("HelixSeed with Offset: Case 1 - offset 1", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGGGG"); + RnaSequence r2("r2", "CCCCCG"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhS = new HelixHandlerStackingOnly(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhS->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhS); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1) == + 9); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 9); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(0, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(0, 2) == 3); + + // (0,3) + REQUIRE(hhIO.getHelixSeedE(0, 3) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 3) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 3) == 0); + + // (1,1) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 4); + + // (2,2) + REQUIRE(hhIO.getHelixSeedE(2, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(2, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(2, 2) == 3); + + // (4,4) + REQUIRE(hhIO.getHelixSeedE(4, 4) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(4, 4) == 0); + REQUIRE(hhIO.getHelixSeedLength2(4, 4) == 0); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helixSeed + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (4,4) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 4, 4); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed with Offset: Case 2 - 'A' disrupting complementarity", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCACCG"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhS = new HelixHandlerStackingOnly(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhS->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhS); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1) == + 0); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 0); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(0, 2) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 2) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 2) == 0); + + // (1,3) + REQUIRE(hhIO.getHelixSeedE(1, 3) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(1, 3) == 0); + REQUIRE(hhIO.getHelixSeedLength2(1, 3) == 0); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed with Offset: Case 3 - only seed possible", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AAGGGA"); + RnaSequence r2("r2", "ACCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhS = new HelixHandlerStackingOnly(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhS->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhS); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, + energy.size2() - sHIO.getOffset2() - 1) == 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 1); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(1, 0) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(1, 0) == 0); + REQUIRE(hhIO.getHelixSeedLength2(1, 0) == 0); + + // (1,3) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -2); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 3); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("HelixSeed: Case 4 - unpaired allowed in seed", "[HelixHandlerStackingOnly]") { + + RnaSequence r1("r1", "AGGAGG"); + RnaSequence r2("r2", "CCACCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 2, 1, 1, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhS = new HelixHandlerStackingOnly(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhS->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhS); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, + energy.size2() - sHIO.getOffset2() - 1) == 4); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 4); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 5); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 5); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(1, 0) == -2); + REQUIRE(hhIO.getHelixSeedLength1(1, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 0) == 4); + + // (1,3) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -2); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (0,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("HelixSeed with Offset: Case 5 - uneven offset", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGGGGG"); + RnaSequence r2("r2", "CCCCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhS = new HelixHandlerStackingOnly(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhS->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhS); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(2); + hhIO.setOffset1(1); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1) == + 9); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 9); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(0, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(0, 2) == 3); + + // (0,3) + REQUIRE(hhIO.getHelixSeedE(0, 3) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 3) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 3) == 0); + + // (1,1) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 4); + + // (2,2) + REQUIRE(hhIO.getHelixSeedE(2, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(2, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(2, 2) == 3); + + // (4,4) + REQUIRE(hhIO.getHelixSeedE(4, 4) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(4, 4) == 0); + REQUIRE(hhIO.getHelixSeedLength2(4, 4) == 0); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helixSeed + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (4,4) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 4, 4); + + REQUIRE(interaction.basePairs.size() == 0); + } +} diff --git a/tests/HelixHandlerStackingOnlySeed_test.cpp b/tests/HelixHandlerStackingOnlySeed_test.cpp new file mode 100644 index 00000000..4d96cc74 --- /dev/null +++ b/tests/HelixHandlerStackingOnlySeed_test.cpp @@ -0,0 +1,315 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerStackingOnly.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/SeedHandlerMfe.h" + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerStackingOnlySeed", "[HelixHandlerStackingOnly]" ) { + + SECTION("HelixSeed: Case 1 - all complementary", "[HelixHandlerStackingOnly]") { + + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2,4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3,0,0,0,0 + , AccessibilityDisabled::ED_UPPER_BOUND + , IndexRangeList("") + , IndexRangeList("") + , ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerStackingOnly hhS(energy, hC); + + sH.fillSeed(0, energy.size1()-1, 0,energy.size2()-1); + hhS.setSeedHandler(sH); + + hhS.fillHelix(0, energy.size1()-1, 0, energy.size2()-1); + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelixSeed( 0,energy.size1()-1, 0,energy.size2()-1 ) == 9); + + // (0,0) + REQUIRE(hhS.getHelixSeedE(0,0) == -3); + REQUIRE(hhS.getHelixSeedLength1(0,0) == 4); + REQUIRE(hhS.getHelixSeedLength2(0,0) == 4); + + // (0,1) + REQUIRE(hhS.getHelixSeedE(0,1) == -3); + REQUIRE(hhS.getHelixSeedLength1(0,1) == 4); + REQUIRE(hhS.getHelixSeedLength2(0,1) == 4); + + // (0,2) + REQUIRE(hhS.getHelixSeedE(0,2) == -2); + REQUIRE(hhS.getHelixSeedLength1(0,2) == 3); + REQUIRE(hhS.getHelixSeedLength2(0,2) == 3); + + // (0,3) + REQUIRE(hhS.getHelixSeedE(0,3) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(0,3) == 0); + REQUIRE(hhS.getHelixSeedLength2(0,3) == 0); + + // (1,1) + REQUIRE(hhS.getHelixSeedE(1,1) == -3); + REQUIRE(hhS.getHelixSeedLength1(1,1) == 4); + REQUIRE(hhS.getHelixSeedLength2(1,1) == 4); + + // (2,2) + REQUIRE(hhS.getHelixSeedE(2,2) == -2); + REQUIRE(hhS.getHelixSeedLength1(2,2) == 3); + REQUIRE(hhS.getHelixSeedLength2(2,2) == 3); + + // (4,4) + REQUIRE(hhS.getHelixSeedE(4,4) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(4,4) == 0); + REQUIRE(hhS.getHelixSeedLength2(4,4) == 0); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1,r2); + hhS.traceBackHelixSeed(interaction,0,0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helixSeed + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (4,4) + interaction.clear(); + hhS.traceBackHelixSeed(interaction,4,4); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 2 - 'A' disrupting complementarity", "[HelixHandlerStackingOnly]") { + + RnaSequence r1("r1", "GGAGG"); + RnaSequence r2("r2", "CCACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerStackingOnly hhS(energy, hC); + + sH.fillSeed(0, energy.size1()-1, 0,energy.size2()-1); + hhS.setSeedHandler(sH); + hhS.fillHelix(0, energy.size1()-1, 0, energy.size2()-1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 0); + + // (0,0) + REQUIRE(hhS.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhS.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhS.getHelixSeedE(0, 2) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(0, 2) == 0); + REQUIRE(hhS.getHelixSeedLength2(0, 2) == 0); + + // (1,3) + REQUIRE(hhS.getHelixSeedE(1, 3) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(1, 3) == 0); + REQUIRE(hhS.getHelixSeedLength2(1, 3) == 0); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhS.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 3 - only seed possible", "[HelixHandlerStackingOnly]") { + + RnaSequence r1("r1", "AGGGA"); + RnaSequence r2("r2", "ACCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerStackingOnly hhS(energy, hC); + + sH.fillSeed(0, energy.size1()-1, 0,energy.size2()-1); + hhS.setSeedHandler(sH); + + hhS.fillHelix(0, energy.size1()-1, 0, energy.size2()-1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 1); + + // (0,0) + REQUIRE(hhS.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhS.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhS.getHelixSeedE(1, 0) == E_INF); + REQUIRE(hhS.getHelixSeedLength1(1, 0) == 0); + REQUIRE(hhS.getHelixSeedLength2(1, 0) == 0); + + // (1,3) + REQUIRE(hhS.getHelixSeedE(1, 1) == -2); + REQUIRE(hhS.getHelixSeedLength1(1, 1) == 3); + REQUIRE(hhS.getHelixSeedLength2(1, 1) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhS.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + // Case (1,1) + interaction.clear(); + hhS.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("HelixSeed: Case4 - unpaired allowed in seed", "[HelixHandlerStackingOnly]") { + + RnaSequence r1("r1", "GGAGG"); + RnaSequence r2("r2", "CCACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 2, 1, 1, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerStackingOnly hhS(energy, hC); + + sH.fillSeed(0, energy.size1()-1, 0,energy.size2()-1); + hhS.setSeedHandler(sH); + hhS.fillHelix(0, energy.size1()-1, 0, energy.size2()-1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 4); + + // (0,0) + REQUIRE(hhS.getHelixSeedE(0, 0) == -3); + REQUIRE(hhS.getHelixSeedLength1(0, 0) == 5); + REQUIRE(hhS.getHelixSeedLength2(0, 0) == 5); + + // (0,2) + REQUIRE(hhS.getHelixSeedE(1, 0) == -2); + REQUIRE(hhS.getHelixSeedLength1(1, 0) == 4); + REQUIRE(hhS.getHelixSeedLength2(1, 0) == 4); + + // (1,3) + REQUIRE(hhS.getHelixSeedE(1, 1) == -2); + REQUIRE(hhS.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhS.getHelixSeedLength2(1, 1) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhS.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhS.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (0,1) + interaction.clear(); + hhS.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 1); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } +} diff --git a/tests/HelixHandlerStackingOnly_test.cpp b/tests/HelixHandlerStackingOnly_test.cpp new file mode 100644 index 00000000..47683f55 --- /dev/null +++ b/tests/HelixHandlerStackingOnly_test.cpp @@ -0,0 +1,636 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerStackingOnly.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/ReverseAccessibility.h" + + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerStackingOnly", "[HelixHandlerStackingOnly]") { + + + SECTION("getter", "[HelixHandlerStackingOnly]") { + + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 10, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + REQUIRE(&hhS.getInteractionEnergy() == &energy); + REQUIRE(&hhS.getConstraint() == &hC); + + } + + SECTION("Helix: Case 1 - Everything is complementary", "[HelixHandlerStackingOnly]") { + + // Case 1 Perfect sequence + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // When counting all non-inf values for helices -> 29 + // When only counting the optimal helices that are non-inf -> 16 + // (0,0) -> 4 ; (1,0) -> 4 ; (2,0) -> 3 ; (3,0) -> 2 + // (0,1) -> 4 ; (1,1) -> 4 ; (2,1) -> 3 ; (3,1) -> 2 + // (0,2) -> 3 ; (1,2) -> 3 ; (2,2) -> 3 ; (3,2) -> 2 + // (0,3) -> 2 ; (1,3) -> 2 ; (2,3) -> 2 ; (3,3) -> 2 + REQUIRE(hhS.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 16); + + + // All optimal combinations + // (0,0) + REQUIRE(hhS.getHelixE(0, 0) == -3); + REQUIRE(hhS.getHelixLength1(0, 0) == 4); + REQUIRE(hhS.getHelixLength2(0, 0) == hhS.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhS.getHelixE(0, 1) == -3); + REQUIRE(hhS.getHelixLength1(0, 1) == 4); + REQUIRE(hhS.getHelixLength2(0, 1) == hhS.getHelixLength1(0, 1)); + + // (0,2) + REQUIRE(hhS.getHelixE(0, 2) == -2); + REQUIRE(hhS.getHelixLength1(0, 2) == 3); + REQUIRE(hhS.getHelixLength2(0, 2) == hhS.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhS.getHelixE(0, 3) == -1); + REQUIRE(hhS.getHelixLength1(0, 3) == 2); + REQUIRE(hhS.getHelixLength2(0, 3) == hhS.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhS.getHelixE(1, 0) == -3); + REQUIRE(hhS.getHelixLength1(1, 0) == 4); + REQUIRE(hhS.getHelixLength2(1, 0) == hhS.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhS.getHelixE(1, 1) == -3); + REQUIRE(hhS.getHelixLength1(1, 1) == 4); + REQUIRE(hhS.getHelixLength2(1, 1) == hhS.getHelixLength1(1, 1)); + + // (1,2) + REQUIRE(hhS.getHelixE(1, 2) == -2); + REQUIRE(hhS.getHelixLength1(1, 2) == 3); + REQUIRE(hhS.getHelixLength2(1, 2) == hhS.getHelixLength1(1, 2)); + + // (1,3) + REQUIRE(hhS.getHelixE(1, 3) == -1); + REQUIRE(hhS.getHelixLength1(1, 3) == 2); + REQUIRE(hhS.getHelixLength2(1, 3) == hhS.getHelixLength1(1, 3)); + + // (2,0) + REQUIRE(hhS.getHelixE(2, 0) == -2); + REQUIRE(hhS.getHelixLength1(2, 0) == 3); + REQUIRE(hhS.getHelixLength2(2, 0) == hhS.getHelixLength1(2, 0)); + + // (2,1) + REQUIRE(hhS.getHelixE(2, 1) == -2); + REQUIRE(hhS.getHelixLength1(2, 1) == 3); + REQUIRE(hhS.getHelixLength2(2, 1) == hhS.getHelixLength1(2, 1)); + + // (2,2) + REQUIRE(hhS.getHelixE(2, 2) == -2); + REQUIRE(hhS.getHelixLength1(2, 2) == 3); + REQUIRE(hhS.getHelixLength2(2, 2) == hhS.getHelixLength1(2, 2)); + + // (2,3) + REQUIRE(hhS.getHelixE(2, 3) == -1); + REQUIRE(hhS.getHelixLength1(2, 3) == 2); + REQUIRE(hhS.getHelixLength2(2, 3) == hhS.getHelixLength1(2, 3)); + + // (3,0) + REQUIRE(hhS.getHelixE(3, 0) == -1); + REQUIRE(hhS.getHelixLength1(3, 0) == 2); + REQUIRE(hhS.getHelixLength2(3, 0) == hhS.getHelixLength1(3, 0)); + + // (3,1) + REQUIRE(hhS.getHelixE(3, 1) == -1); + REQUIRE(hhS.getHelixLength1(3, 1) == 2); + REQUIRE(hhS.getHelixLength2(3, 1) == hhS.getHelixLength1(3, 1)); + + // (3,2) + REQUIRE(hhS.getHelixE(3, 2) == -1); + REQUIRE(hhS.getHelixLength1(3, 2) == 2); + REQUIRE(hhS.getHelixLength2(3, 2) == hhS.getHelixLength1(3, 2)); + + // (3,3) + REQUIRE(hhS.getHelixE(3, 3) == -1); + REQUIRE(hhS.getHelixLength1(3, 3) == 2); + REQUIRE(hhS.getHelixLength2(3, 3) == hhS.getHelixLength1(3, 3)); + + + // Several cases that do not allow a helix + + // (4,4) + REQUIRE(hhS.getHelixE(4, 4) == E_INF); + REQUIRE(hhS.getHelixLength1(4, 4) == 0); + REQUIRE(hhS.getHelixLength2(4, 4) == hhS.getHelixLength1(4, 4)); + + // (4,3) + REQUIRE(hhS.getHelixE(0, 4) == E_INF); + REQUIRE(hhS.getHelixLength1(0, 4) == 0); + REQUIRE(hhS.getHelixLength2(0, 4) == hhS.getHelixLength1(0, 4)); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhS.traceBackHelix(interaction, 0, 0); + + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + + + // Case (2,1) + ////////////////////// + interaction.clear(); + + hhS.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + + SECTION("Helix: Case 2 - Sequence 1 contains an A", "[HelixHandlerStackingOnly]") { + // Case 2 - sequence containing an "A" to disrupt perfect stacking + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 12); + + + // All optimal combinations + // (0,0) + REQUIRE(hhS.getHelixE(0, 0) == -2); + REQUIRE(hhS.getHelixLength1(0, 0) == 3); + REQUIRE(hhS.getHelixLength2(0, 0) == hhS.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhS.getHelixE(0, 1) == -2); + REQUIRE(hhS.getHelixLength1(0, 1) == 3); + REQUIRE(hhS.getHelixLength2(0, 1) == hhS.getHelixLength1(0, 1)); + + // (0,2) + REQUIRE(hhS.getHelixE(0, 2) == -2); + REQUIRE(hhS.getHelixLength1(0, 2) == 3); + REQUIRE(hhS.getHelixLength2(0, 2) == hhS.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhS.getHelixE(0, 3) == -1); + REQUIRE(hhS.getHelixLength1(0, 3) == 2); + REQUIRE(hhS.getHelixLength2(0, 3) == hhS.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhS.getHelixE(1, 0) == -1); + REQUIRE(hhS.getHelixLength1(1, 0) == 2); + REQUIRE(hhS.getHelixLength2(1, 0) == hhS.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhS.getHelixE(1, 1) == -1); + REQUIRE(hhS.getHelixLength1(1, 1) == 2); + REQUIRE(hhS.getHelixLength2(1, 1) == hhS.getHelixLength1(1, 1)); + + // (1,2) + REQUIRE(hhS.getHelixE(1, 2) == -1); + REQUIRE(hhS.getHelixLength1(1, 2) == 2); + REQUIRE(hhS.getHelixLength2(1, 2) == hhS.getHelixLength1(1, 2)); + + // (1,3) + REQUIRE(hhS.getHelixE(1, 3) == -1); + REQUIRE(hhS.getHelixLength1(1, 3) == 2); + REQUIRE(hhS.getHelixLength2(1, 3) == hhS.getHelixLength1(1, 3)); + + // (4,0) + REQUIRE(hhS.getHelixE(4, 0) == -1); + REQUIRE(hhS.getHelixLength1(4, 0) == 2); + REQUIRE(hhS.getHelixLength2(4, 0) == hhS.getHelixLength1(4, 0)); + + // (4,1) + REQUIRE(hhS.getHelixE(4, 1) == -1); + REQUIRE(hhS.getHelixLength1(4, 1) == 2); + REQUIRE(hhS.getHelixLength2(4, 1) == hhS.getHelixLength1(4, 1)); + + // (4,2) + REQUIRE(hhS.getHelixE(4, 2) == -1); + REQUIRE(hhS.getHelixLength1(4, 2) == 2); + REQUIRE(hhS.getHelixLength2(4, 2) == hhS.getHelixLength1(4, 2)); + + // (4,3) + REQUIRE(hhS.getHelixE(4, 3) == -1); + REQUIRE(hhS.getHelixLength1(4, 3) == 2); + REQUIRE(hhS.getHelixLength2(4, 3) == hhS.getHelixLength1(4, 3)); + + + // Not viable cases + // (3,0) Not complementary + REQUIRE(hhS.getHelixE(3,0) == E_INF); + REQUIRE(hhS.getHelixLength1(3,0) == 0); + REQUIRE(hhS.getHelixLength2(3,0) == hhS.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhS.getHelixE(5,4) == E_INF); + REQUIRE(hhS.getHelixLength1(5,4) == 0); + REQUIRE(hhS.getHelixLength2(5,4) == hhS.getHelixLength1(5,4)); + + // (5,3) no helix possible + REQUIRE(hhS.getHelixE(5,3) == E_INF); + REQUIRE(hhS.getHelixLength1(5,3) == 0); + REQUIRE(hhS.getHelixLength2(5,3) == hhS.getHelixLength1(5,3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhS.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 1); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + + // Case (2,1) + ////////////////////// + interaction.clear(); + + REQUIRE_THROWS_WITH(hhS.traceBackHelix(interaction, 2, 1), "HelixHandlerStackingOnly::traceBackHelix(i1=2,i2=1) no helix known (E_INF)"); + +#endif + } + + SECTION("Helix: Case 3 - A 'wall' of A's disrupts the possible helices", "[HelixHandlerStackingOnly]") { + // Case 2 - sequence containing an "A"-wall to disrupt perfect stacking + RnaSequence r1("r1", "GGGAAGG"); + RnaSequence r2("r2", "CCAACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 9); + + REQUIRE_FALSE(energy.areComplementary(5,4)); + // All optimal combinations + // (0,0) + REQUIRE(hhS.getHelixE(0, 0) == -2); + REQUIRE(hhS.getHelixLength1(0, 0) == 3); + REQUIRE(hhS.getHelixLength2(0, 0) == hhS.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhS.getHelixE(0, 1) == -1); + REQUIRE(hhS.getHelixLength1(0, 1) == 2); + REQUIRE(hhS.getHelixLength2(0, 1) == hhS.getHelixLength1(0, 1)); + + // (0,5) + REQUIRE(hhS.getHelixE(0, 5) == -1); + REQUIRE(hhS.getHelixLength1(0, 5) == 2); + REQUIRE(hhS.getHelixLength2(0, 5) == hhS.getHelixLength1(0, 5)); + + // (1,0) + REQUIRE(hhS.getHelixE(1, 0) == -1); + REQUIRE(hhS.getHelixLength1(1, 0) == 2); + REQUIRE(hhS.getHelixLength2(1, 0) == hhS.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhS.getHelixE(1, 1) == -1); + REQUIRE(hhS.getHelixLength1(1, 1) == 2); + REQUIRE(hhS.getHelixLength2(1, 1) == hhS.getHelixLength1(1, 1)); + + // (1,5) + REQUIRE(hhS.getHelixE(1, 5) == -1); + REQUIRE(hhS.getHelixLength1(1, 5) == 2); + REQUIRE(hhS.getHelixLength2(1, 5) == hhS.getHelixLength1(1, 5)); + + // (5,0) + REQUIRE(hhS.getHelixE(5, 0) == -1); + REQUIRE(hhS.getHelixLength1(5, 0) == 2); + REQUIRE(hhS.getHelixLength2(5, 0) == hhS.getHelixLength1(5, 0)); + + // (5,1) + REQUIRE(hhS.getHelixE(5, 1) == -1); + REQUIRE(hhS.getHelixLength1(5, 1) == 2); + REQUIRE(hhS.getHelixLength2(5, 1) == hhS.getHelixLength1(5, 1)); + + // (5,5) + REQUIRE(hhS.getHelixE(5, 5) == -1); + REQUIRE(hhS.getHelixLength1(5, 5) == 2); + REQUIRE(hhS.getHelixLength2(5, 5) == hhS.getHelixLength1(5, 5)); + + + + // Not viable cases + // (3,0) Not complementary + REQUIRE(hhS.getHelixE(3,0) == E_INF); + REQUIRE(hhS.getHelixLength1(3,0) == 0); + REQUIRE(hhS.getHelixLength2(3,0) == hhS.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhS.getHelixE(2,2) == E_INF); + REQUIRE(hhS.getHelixLength1(2,2) == 0); + REQUIRE(hhS.getHelixLength2(2,2) == hhS.getHelixLength1(2,2)); + + // (5,3) no helix possible + REQUIRE(hhS.getHelixE(5,3) == E_INF); + REQUIRE(hhS.getHelixLength1(5,3) == 0); + REQUIRE(hhS.getHelixLength2(5,3) == hhS.getHelixLength1(5,3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhS.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 1); + REQUIRE(interaction.basePairs.rbegin()->second == 5); + + // Case (5,5) - Possible but only 2 base pairs long (e.g no bp needs to be reported) + ////////////////////// + + interaction.clear(); + hhS.traceBackHelix(interaction, 5, 5); + + REQUIRE(interaction.basePairs.size() == 0); + + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + + // Case (2,1) - Not Possible + ////////////////////// + + interaction.clear(); + REQUIRE_THROWS_WITH(hhS.traceBackHelix(interaction, 2, 1), "HelixHandlerStackingOnly::traceBackHelix(i1=2,i2=1) no helix known (E_INF)"); + +#endif + } + + SECTION("Helix: Case 4 - No interaction possible", "[HelixHandlerStackingOnly]") { + // Case 4 -NO HELIX POSSIBLE + RnaSequence r1("r1", "AAAAAAA"); + RnaSequence r2("r2", "AAAAAAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 0); + + // NO POSSIBLE HELICES + // (2,2) + REQUIRE(hhS.getHelixE(2, 2) == E_INF); + REQUIRE(hhS.getHelixLength1(2, 2) == 0); + REQUIRE(hhS.getHelixLength2(2, 2) == hhS.getHelixLength1(2, 2)); + + // (0,3) + REQUIRE(hhS.getHelixE(0, 3) == E_INF); + REQUIRE(hhS.getHelixLength1(0, 3) == 0); + REQUIRE(hhS.getHelixLength2(0, 3) == hhS.getHelixLength1(0, 3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + REQUIRE_THROWS_WITH(hhS.traceBackHelix(interaction, 0, 0), "HelixHandlerStackingOnly::traceBackHelix(i1=0,i2=0) no helix known (E_INF)"); + + + // Case (1,3) + ////////////////////// + interaction.clear(); + + REQUIRE_THROWS_WITH(hhS.traceBackHelix(interaction, 1, 3), "HelixHandlerStackingOnly::traceBackHelix(i1=1,i2=3) no helix known (E_INF)"); + +#endif + } + + SECTION("Helix: Case 5 - Example from LimStackHeuristic test", "[HelixHandlerStackingOnly]") { + // Case 5 (3 length helix at the end) + RnaSequence r1("r1", "gggaaggg"); + RnaSequence r2("r2", "cccaaccc"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 16); + + // Possible helices + // (5,5) + REQUIRE(hhS.getHelixE(5, 5) == -2); + REQUIRE(hhS.getHelixLength1(5, 5) == 3); + REQUIRE(hhS.getHelixLength2(5, 5) == hhS.getHelixLength1(5, 5)); + + // (0,0) + REQUIRE(hhS.getHelixE(0, 0) == -2); + REQUIRE(hhS.getHelixLength1(0, 0) == 3); + REQUIRE(hhS.getHelixLength2(0, 0) == hhS.getHelixLength1(0, 0)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhS.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 6); + + REQUIRE(interaction.basePairs.rbegin()->first == 1); + REQUIRE(interaction.basePairs.rbegin()->second == 6); + + // Case (5, 5) + ////////////////////// + interaction.clear(); + + hhS.traceBackHelix(interaction, 5, 5); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 6); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 6); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + } + + SECTION("Helix: Case 6 - Special case Helix+E_init() == Helix+IL+H", "[HelixHandlerStackingOnly]") { + // Case 6 + RnaSequence r1("r1", "GGUUGAAUUACGACAG"); + RnaSequence r2("r2", "cugaaaaacauaacc"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 10, 0, 999, 0, false); + HelixHandlerStackingOnly hhS(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhS.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 26); + + // (0,0) + REQUIRE(hhS.getHelixE(0,0) == -4); + REQUIRE(hhS.getHelixLength1(0,0) == 5); + REQUIRE(hhS.getHelixLength2(0,0) == hhS.getHelixLength1(0,0)); + + // (3,5) + REQUIRE(hhS.getHelixE(3,5) == -1); + REQUIRE(hhS.getHelixLength1(3,5) == 2); + REQUIRE(hhS.getHelixLength2(3,5) == hhS.getHelixLength1(3,5)); + + // (7,11) + REQUIRE(hhS.getHelixE(7,11) == -2); + REQUIRE(hhS.getHelixLength1(7,11) == 3); + REQUIRE(hhS.getHelixLength2(7,11) == hhS.getHelixLength1(7,11)); + + // (13,12) + REQUIRE(hhS.getHelixE(13,12) == -2); + REQUIRE(hhS.getHelixLength1(13,12) == 3); + REQUIRE(hhS.getHelixLength2(13,12) == hhS.getHelixLength1(13,12)); + + + // Not working + + // (7,11) + REQUIRE(hhS.getHelixE(7,12) == E_INF); + REQUIRE(hhS.getHelixLength1(7,12) == 0); + REQUIRE(hhS.getHelixLength2(7,12) == hhS.getHelixLength1(7,12)); + + // (0,1) + REQUIRE(hhS.getHelixE(0,1) == E_INF); + REQUIRE(hhS.getHelixLength1(0,1) == 0); + REQUIRE(hhS.getHelixLength2(0,1) == hhS.getHelixLength1(0,1)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhS.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 13); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 11); + + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + + // Case (5,5) + ////////////////////// + interaction.clear(); + + REQUIRE_THROWS_WITH(hhS.traceBackHelix(interaction, 5, 5), "HelixHandlerStackingOnly::traceBackHelix(i1=5,i2=5) no helix known (E_INF)"); + +#endif + } +} diff --git a/tests/HelixHandlerUnpairedIdxOffset_test.cpp b/tests/HelixHandlerUnpairedIdxOffset_test.cpp new file mode 100644 index 00000000..938a77dc --- /dev/null +++ b/tests/HelixHandlerUnpairedIdxOffset_test.cpp @@ -0,0 +1,531 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerUnpaired.h" +#include "IntaRNA/HelixHandlerIdxOffset.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/ReverseAccessibility.h" + + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerIdxOffset for Unpaired", "[HelixHandlerIdxOffset]") { + + + SECTION("getter", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 10, 2, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerUnpaired(energy, hC)); + + // Initial offset of 0 + REQUIRE(hhIO.getOffset1() == 0); + REQUIRE(hhIO.getOffset2() == 0); + + // Set offset + hhIO.setOffset1(1); + hhIO.setOffset2(4); + REQUIRE(hhIO.getOffset1() == 1); + REQUIRE(hhIO.getOffset2() == 4); + + // get Constraints + REQUIRE(hhIO.getConstraint().getMinBasePairs() == 2); + REQUIRE(hhIO.getConstraint().getMaxBasePairs() == 10); + + } + + SECTION("Helix with Offset: Case 1 - Everything is complementary", "[HelixHandlerIdxOffset]") { + + // Case 1 Perfect sequence + RnaSequence r1("r1", "AGGGGG"); + RnaSequence r2("r2", "CCCCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerUnpaired(energy, hC)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // When counting all non-inf values for helices -> 29 + // When only counting the optimal helices that are non-inf -> 16 + // (0,0) -> 4 ; (1,0) -> 4 ; (2,0) -> 3 ; (3,0) -> 2 + // (0,1) -> 4 ; (1,1) -> 4 ; (2,1) -> 3 ; (3,1) -> 2 + // (0,2) -> 3 ; (1,2) -> 3 ; (2,2) -> 3 ; (3,2) -> 2 + // (0,3) -> 2 ; (1,3) -> 2 ; (2,3) -> 2 ; (3,3) -> 2 + REQUIRE(hhIO.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 16); + + // Set the offsets + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + // All optimal combinations + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -3); + REQUIRE(hhIO.getHelixLength1(0, 0) == 4); + REQUIRE(hhIO.getHelixLength2(0, 0) == hhIO.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhIO.getHelixE(0, 1) == -3); + REQUIRE(hhIO.getHelixLength1(0, 1) == 4); + REQUIRE(hhIO.getHelixLength2(0, 1) == hhIO.getHelixLength1(0, 1)); + + // (0,2) + REQUIRE(hhIO.getHelixE(0, 2) == -2); + REQUIRE(hhIO.getHelixLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixLength2(0, 2) == hhIO.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhIO.getHelixE(0, 3) == -1); + REQUIRE(hhIO.getHelixLength1(0, 3) == 2); + REQUIRE(hhIO.getHelixLength2(0, 3) == hhIO.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhIO.getHelixE(1, 0) == -3); + REQUIRE(hhIO.getHelixLength1(1, 0) == 4); + REQUIRE(hhIO.getHelixLength2(1, 0) == hhIO.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == -3); + REQUIRE(hhIO.getHelixLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixLength2(1, 1) == hhIO.getHelixLength1(1, 1)); + + // (1,2) + REQUIRE(hhIO.getHelixE(1, 2) == -2); + REQUIRE(hhIO.getHelixLength1(1, 2) == 3); + REQUIRE(hhIO.getHelixLength2(1, 2) == hhIO.getHelixLength1(1, 2)); + + // (1,3) + REQUIRE(hhIO.getHelixE(1, 3) == -1); + REQUIRE(hhIO.getHelixLength1(1, 3) == 2); + REQUIRE(hhIO.getHelixLength2(1, 3) == hhIO.getHelixLength1(1, 3)); + + // (2,0) + REQUIRE(hhIO.getHelixE(2, 0) == -2); + REQUIRE(hhIO.getHelixLength1(2, 0) == 3); + REQUIRE(hhIO.getHelixLength2(2, 0) == hhIO.getHelixLength1(2, 0)); + + // (2,1) + REQUIRE(hhIO.getHelixE(2, 1) == -2); + REQUIRE(hhIO.getHelixLength1(2, 1) == 3); + REQUIRE(hhIO.getHelixLength2(2, 1) == hhIO.getHelixLength1(2, 1)); + + // (2,2) + REQUIRE(hhIO.getHelixE(2, 2) == -2); + REQUIRE(hhIO.getHelixLength1(2, 2) == 3); + REQUIRE(hhIO.getHelixLength2(2, 2) == hhIO.getHelixLength1(2, 2)); + + // (2,3) + REQUIRE(hhIO.getHelixE(2, 3) == -1); + REQUIRE(hhIO.getHelixLength1(2, 3) == 2); + REQUIRE(hhIO.getHelixLength2(2, 3) == hhIO.getHelixLength1(2, 3)); + + // (3,0) + REQUIRE(hhIO.getHelixE(3, 0) == -1); + REQUIRE(hhIO.getHelixLength1(3, 0) == 2); + REQUIRE(hhIO.getHelixLength2(3, 0) == hhIO.getHelixLength1(3, 0)); + + // (3,1) + REQUIRE(hhIO.getHelixE(3, 1) == -1); + REQUIRE(hhIO.getHelixLength1(3, 1) == 2); + REQUIRE(hhIO.getHelixLength2(3, 1) == hhIO.getHelixLength1(3, 1)); + + // (3,2) + REQUIRE(hhIO.getHelixE(3, 2) == -1); + REQUIRE(hhIO.getHelixLength1(3, 2) == 2); + REQUIRE(hhIO.getHelixLength2(3, 2) == hhIO.getHelixLength1(3, 2)); + + // (3,3) + REQUIRE(hhIO.getHelixE(3, 3) == -1); + REQUIRE(hhIO.getHelixLength1(3, 3) == 2); + REQUIRE(hhIO.getHelixLength2(3, 3) == hhIO.getHelixLength1(3, 3)); + + + // Several cases that do not allow a helix + + // (4,4) + REQUIRE(hhIO.getHelixE(4, 4) == E_INF); + REQUIRE(hhIO.getHelixLength1(4, 4) == 0); + REQUIRE(hhIO.getHelixLength2(4, 4) == hhIO.getHelixLength1(4, 4)); + + // (4,3) + REQUIRE(hhIO.getHelixE(0, 4) == E_INF); + REQUIRE(hhIO.getHelixLength1(0, 4) == 0); + REQUIRE(hhIO.getHelixLength2(0, 4) == hhIO.getHelixLength1(0, 4)); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1, r2); + + hhIO.traceBackHelix(interaction, 0, 0); + + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + + + // Case (2,1) + ////////////////////// + interaction.clear(); + + hhIO.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("Helix with offset: Case 2 - Sequence 1 contains an A", "[HelixHandlerIdxOffset]") { + // Case 2 - sequence containing an "A" to disrupt perfect stacking + RnaSequence r1("r1", "AGGGAGG"); + RnaSequence r2("r2", "CCCCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerUnpaired(energy, hC)); + + // set offsets + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1() - 1, 0, energy.size2()-hhIO.getOffset2() - 1) == 16); + + + // All optimal combinations + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -3); + REQUIRE(hhIO.getHelixLength1(0, 0) == 5); + REQUIRE(hhIO.getHelixLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhIO.getHelixE(0, 1) == -3); + REQUIRE(hhIO.getHelixLength1(0, 1) == 5); + REQUIRE(hhIO.getHelixLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhIO.getHelixE(0, 2) == -2); + REQUIRE(hhIO.getHelixLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixLength2(0, 2) == hhIO.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhIO.getHelixE(0, 3) == -1); + REQUIRE(hhIO.getHelixLength1(0, 3) == 2); + REQUIRE(hhIO.getHelixLength2(0, 3) == hhIO.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhIO.getHelixE(1, 0) == -3); + REQUIRE(hhIO.getHelixLength1(1, 0) == 5); + REQUIRE(hhIO.getHelixLength2(1, 0) == 4); + + // (1,1) + REQUIRE(hhIO.getHelixE(1, 1) == -3); + REQUIRE(hhIO.getHelixLength1(1, 1) == 5); + REQUIRE(hhIO.getHelixLength2(1, 1) == 4); + + // (1,2) + REQUIRE(hhIO.getHelixE(1, 2) == -2); + REQUIRE(hhIO.getHelixLength1(1, 2) == 4); + REQUIRE(hhIO.getHelixLength2(1, 2) == 3); + + // (1,3) + REQUIRE(hhIO.getHelixE(1, 3) == -1); + REQUIRE(hhIO.getHelixLength1(1, 3) == 2); + REQUIRE(hhIO.getHelixLength2(1, 3) == hhIO.getHelixLength1(1, 3)); + + // (2,0) + REQUIRE(hhIO.getHelixE(2, 0) == -2); + REQUIRE(hhIO.getHelixLength1(2, 0) == 4); + REQUIRE(hhIO.getHelixLength2(2, 0) == 3); + + // (2,1) + REQUIRE(hhIO.getHelixE(2, 1) == -2); + REQUIRE(hhIO.getHelixLength1(2, 1) == 4); + REQUIRE(hhIO.getHelixLength2(2, 1) == 3); + + // (2,2) + REQUIRE(hhIO.getHelixE(2, 2) == -2); + REQUIRE(hhIO.getHelixLength1(2, 2) == 4); + REQUIRE(hhIO.getHelixLength2(2, 2) == 3); + + // (2,3) + REQUIRE(hhIO.getHelixE(2, 3) == -1); + REQUIRE(hhIO.getHelixLength1(2, 3) == 3); + REQUIRE(hhIO.getHelixLength2(2, 3) == 2); + + // (4,0) + REQUIRE(hhIO.getHelixE(4, 0) == -1); + REQUIRE(hhIO.getHelixLength1(4, 0) == 2); + REQUIRE(hhIO.getHelixLength2(4, 0) == hhIO.getHelixLength1(4, 0)); + + // (4,1) + REQUIRE(hhIO.getHelixE(4, 1) == -1); + REQUIRE(hhIO.getHelixLength1(4, 1) == 2); + REQUIRE(hhIO.getHelixLength2(4, 1) == hhIO.getHelixLength1(4, 1)); + + // (4,2) + REQUIRE(hhIO.getHelixE(4, 2) == -1); + REQUIRE(hhIO.getHelixLength1(4, 2) == 2); + REQUIRE(hhIO.getHelixLength2(4, 2) == hhIO.getHelixLength1(4, 2)); + + // (4,3) + REQUIRE(hhIO.getHelixE(4, 3) == -1); + REQUIRE(hhIO.getHelixLength1(4, 3) == 2); + REQUIRE(hhIO.getHelixLength2(4, 3) == hhIO.getHelixLength1(4, 3)); + + + // Not viable cases + // (3,0) Not complementary + REQUIRE(hhIO.getHelixE(3,0) == E_INF); + REQUIRE(hhIO.getHelixLength1(3,0) == 0); + REQUIRE(hhIO.getHelixLength2(3,0) == hhIO.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhIO.getHelixE(5,4) == E_INF); + REQUIRE(hhIO.getHelixLength1(5,4) == 0); + REQUIRE(hhIO.getHelixLength2(5,4) == hhIO.getHelixLength1(5,4)); + + // (5,3) no helix possible + REQUIRE(hhIO.getHelixE(5,3) == E_INF); + REQUIRE(hhIO.getHelixLength1(5,3) == 0); + REQUIRE(hhIO.getHelixLength2(5,3) == hhIO.getHelixLength1(5,3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhIO.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (2,1) + ////////////////////// + interaction.clear(); + + hhIO.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 5); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + } + + SECTION("Helix with Offset: Case 3 - unpaired bases in sequence 1 ", "[HelixHandlerUnpaired]") { + // Case 4 -NO HELIX POSSIBLE + RnaSequence r1("r1", "AGGAAGG"); + RnaSequence r2("r2", "CCCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerUnpaired(energy, hC)); + + // set offsets + hhIO.setOffset1(1); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1() - 1, 0, energy.size2()-hhIO.getOffset2() - 1) == 9); + + + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -3); + REQUIRE(hhIO.getHelixLength1(0, 0) == 6); + REQUIRE(hhIO.getHelixLength2(0, 0) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhIO.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("Helix: Case 8 - unpaired bases, many unpaired bases", "[HelixHandlerUnpaired]") { + RnaSequence r1("r1", "AGAAGAGG"); + RnaSequence r2("r2", "CAACACCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerUnpaired(energy, hC)); + + // set offsets + hhIO.setOffset1(1); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1() - 1, 0, energy.size2()-hhIO.getOffset2() - 1) == 6); + + // (0,0) + REQUIRE(hhIO.getHelixE(0, 0) == -3); + REQUIRE(hhIO.getHelixLength1(0, 0) == 7); + REQUIRE(hhIO.getHelixLength2(0, 0) == 7); + + // (3,0) + REQUIRE(hhIO.getHelixE(3, 0) == -2); + REQUIRE(hhIO.getHelixLength1(3, 0) == 4); + REQUIRE(hhIO.getHelixLength2(3, 0) == 4); + + // (3,1) + REQUIRE(hhIO.getHelixE(3, 1) == -2); + REQUIRE(hhIO.getHelixLength1(3, 1) == 4); + REQUIRE(hhIO.getHelixLength2(3, 1) == 6); + + // (5,0) + REQUIRE(hhIO.getHelixE(5, 0) == -1); + REQUIRE(hhIO.getHelixLength1(5, 0) == 2); + REQUIRE(hhIO.getHelixLength2(5, 0) == 2); + + // (5,1) + REQUIRE(hhIO.getHelixE(5, 1) == -1); + REQUIRE(hhIO.getHelixLength1(5, 1) == 2); + REQUIRE(hhIO.getHelixLength2(5, 1) == 3); + + // (5,3) + REQUIRE(hhIO.getHelixE(5, 3) == -1); + REQUIRE(hhIO.getHelixLength1(5, 3) == 2); + REQUIRE(hhIO.getHelixLength2(5, 3) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (2,2) + ////////////////////// + Interaction interaction(r1,r2); + hhIO.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 6); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + } + + SECTION("Helix: Case 9 - unpaired bases, many unpaired bases --only allow 1 unpaired base", "[HelixHandlerUnpaired]") { + RnaSequence r1("r1", "AAGAAGAGG"); + RnaSequence r2("r2", "CAACACCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 1, 999, 0, false); + HelixHandlerIdxOffset hhIO(new HelixHandlerUnpaired(energy, hC)); + + // set offsets + hhIO.setOffset1(2); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhIO.fillHelix(0, energy.size1()-hhIO.getOffset1() - 1, 0, energy.size2()-hhIO.getOffset2() - 1) == 3); + + // (3,0) + REQUIRE(hhIO.getHelixE(3, 0) == -2); + REQUIRE(hhIO.getHelixLength1(3, 0) == 4); + REQUIRE(hhIO.getHelixLength2(3, 0) == 4); + + // (5,0) + REQUIRE(hhIO.getHelixE(5, 0) == -1); + REQUIRE(hhIO.getHelixLength1(5, 0) == 2); + REQUIRE(hhIO.getHelixLength2(5, 0) == 2); + + // (5,1) + REQUIRE(hhIO.getHelixE(5, 1) == -1); + REQUIRE(hhIO.getHelixLength1(5, 1) == 2); + REQUIRE(hhIO.getHelixLength2(5, 1) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (5,0) + ////////////////////// + Interaction interaction(r1,r2); + hhIO.traceBackHelix(interaction, 5, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + } +} diff --git a/tests/HelixHandlerUnpairedSeedIdxOffset_test.cpp b/tests/HelixHandlerUnpairedSeedIdxOffset_test.cpp new file mode 100644 index 00000000..49694cdc --- /dev/null +++ b/tests/HelixHandlerUnpairedSeedIdxOffset_test.cpp @@ -0,0 +1,1034 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerUnpaired.h" +#include "IntaRNA/HelixHandlerIdxOffset.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/SeedHandlerMfe.h" + +using namespace IntaRNA; + +TEST_CASE( "HelixSeed for Unpaired with offset", "[HelixHandlerUnpaired]" ) { + + SECTION("getter", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 10, 2, 999, 0, false); + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO (sH); + HelixHandlerIdxOffset hhIO(hhU); + + + // Initial offset of 0 + REQUIRE(hhIO.getOffset1() == 0); + REQUIRE(hhIO.getOffset2() == 0); + + // Set offset + hhIO.setOffset1(1); + hhIO.setOffset2(4); + REQUIRE(hhIO.getOffset1() == 1); + REQUIRE(hhIO.getOffset2() == 4); + + // get Constraints + REQUIRE(hhIO.getConstraint().getMinBasePairs() == 2); + REQUIRE(hhIO.getConstraint().getMaxBasePairs() == 10); + + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 1 - offset 1", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGGGG"); + RnaSequence r2("r2", "CCCCCG"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 9); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(0, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(0, 2) == 3); + + // (0,3) + REQUIRE(hhIO.getHelixSeedE(0, 3) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 3) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 3) == 0); + + // (1,1) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 4); + + // (2,2) + REQUIRE(hhIO.getHelixSeedE(2, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(2, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(2, 2) == 3); + + // (4,4) + REQUIRE(hhIO.getHelixSeedE(4, 4) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(4, 4) == 0); + REQUIRE(hhIO.getHelixSeedLength2(4, 4) == 0); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helixSeed + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (4,4) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 4, 4); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 2 - 'A' disrupting complementarity", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCACCG"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 0); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(0, 2) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 2) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 2) == 0); + + // (1,3) + REQUIRE(hhIO.getHelixSeedE(1, 3) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(1, 3) == 0); + REQUIRE(hhIO.getHelixSeedLength2(1, 3) == 0); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 3 - only seed possible", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AAGGGA"); + RnaSequence r2("r2", "ACCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 1); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(1, 0) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(1, 0) == 0); + REQUIRE(hhIO.getHelixSeedLength2(1, 0) == 0); + + // (1,3) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -2); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 3); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("HelixSeed (Unpaired) with Offset : Case 4 - unpaired allowed in seed", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "AGGAGG"); + RnaSequence r2("r2", "CCACCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 2, 1, 1, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 4); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 5); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 5); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(1, 0) == -2); + REQUIRE(hhIO.getHelixSeedLength1(1, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 0) == 4); + + // (1,3) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -2); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (0,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 5 - uneven offset", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGGGGG"); + RnaSequence r2("r2", "CCCCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(2); + hhIO.setOffset1(1); + hhIO.setOffset2(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 9); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhIO.getHelixSeedE(0, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(0, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(0, 2) == 3); + + // (0,3) + REQUIRE(hhIO.getHelixSeedE(0, 3) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 3) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 3) == 0); + + // (1,1) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 4); + + // (2,2) + REQUIRE(hhIO.getHelixSeedE(2, 2) == -2); + REQUIRE(hhIO.getHelixSeedLength1(2, 2) == 3); + REQUIRE(hhIO.getHelixSeedLength2(2, 2) == 3); + + // (4,4) + REQUIRE(hhIO.getHelixSeedE(4, 4) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(4, 4) == 0); + REQUIRE(hhIO.getHelixSeedLength2(4, 4) == 0); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helixSeed + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (4,4) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 4, 4); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 6 - leading unpaired bases 1", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGGAGGG"); + RnaSequence r2("r2", "CCCACCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 3); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 6); + + // (1,1) + REQUIRE(hhIO.getHelixSeedE(1, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(1, 1) == 5); + REQUIRE(hhIO.getHelixSeedLength2(1, 1) == 5); + + // (3,3) + REQUIRE(hhIO.getHelixSeedE(3, 3) == -2); + REQUIRE(hhIO.getHelixSeedLength1(3, 3) == 3); + REQUIRE(hhIO.getHelixSeedLength2(3, 3) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (0,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 0); + + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 7 - leading unpaired bases 2", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGAGGGG"); + RnaSequence r2("r2", "CCCACCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 5); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 6); + + // (0,1) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 5); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 5); + +// // (2,0) +// REQUIRE(hhIO.getHelixSeedE(2, 0) == -3); +// REQUIRE(hhIO.getHelixSeedLength1(2, 0) == 4); +// REQUIRE(hhIO.getHelixSeedLength2(2, 0) == 6); + + // (2,1) + REQUIRE(hhIO.getHelixSeedE(2, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(2, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(2, 1) == 5); + + // (2,3) + REQUIRE(hhIO.getHelixSeedE(2, 3) == -2); + REQUIRE(hhIO.getHelixSeedLength1(2, 3) == 3); + REQUIRE(hhIO.getHelixSeedLength2(2, 3) == 3); + + // (3,3) + REQUIRE(hhIO.getHelixSeedE(3, 3) == -2); + REQUIRE(hhIO.getHelixSeedLength1(3, 3) == 3); + REQUIRE(hhIO.getHelixSeedLength2(3, 3) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 0); + + // Case (0,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 8 - trailing unpaired bases 1", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGGGAGG"); + RnaSequence r2("r2", "CCACCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, + 0, energy.size2() - hhIO.getOffset2() - 1) == 1); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 6); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 9 - trailing unpaired bases 2", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGGGAGG"); + RnaSequence r2("r2", "CCACCCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 2); + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 6); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 6); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Case (0,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 10 - Leading + trailing unpaired bases", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGAGGGG"); + RnaSequence r2("r2", "CACCCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 5); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 6); + + // (0,1) - Not working + REQUIRE(hhIO.getHelixSeedE(0, 1) == E_INF); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 0); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 0); + + // (2,0) + REQUIRE(hhIO.getHelixSeedE(2, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(2, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(2, 0) == 4); + + // (2,1) + REQUIRE(hhIO.getHelixSeedE(2, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(2, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(2, 1) == 5); + + // (3,0) + REQUIRE(hhIO.getHelixSeedE(3, 0) == -2); + REQUIRE(hhIO.getHelixSeedLength1(3, 0) == 3); + REQUIRE(hhIO.getHelixSeedLength2(3, 0) == 3); + + // (3,1) + REQUIRE(hhIO.getHelixSeedE(3, 1) == -2); + REQUIRE(hhIO.getHelixSeedLength1(3, 1) == 3); + REQUIRE(hhIO.getHelixSeedLength2(3, 1) == 3); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (3,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 3, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 5); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed (Unpaired) with Offset: Case 11 - Leading + trailing unpaired bases + seed allows 1 unpaired", "[HelixHandlerIdxOffset]") { + + RnaSequence r1("r1", "AGAGGAGG"); + RnaSequence r2("r2", "CACCCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 1, 1, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + HelixHandler *hhU = new HelixHandlerUnpaired(energy, hC); + SeedHandler *sH = new SeedHandlerMfe(energy, sC); + + hhU->setSeedHandler(*sH); + + SeedHandlerIdxOffset sHIO(sH); + HelixHandlerIdxOffset hhIO(hhU); + + // Set the offsets + sHIO.setOffset1(1); + sHIO.setOffset2(1); + hhIO.setOffset1(1); + hhIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + hhIO.fillHelix(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + sHIO.fillSeed(0, energy.size1() - sHIO.getOffset1() - 1, 0, energy.size2() - sHIO.getOffset2() - 1); + REQUIRE(hhIO.fillHelixSeed(0, energy.size1() - hhIO.getOffset1() - 1, 0, + energy.size2() - hhIO.getOffset2() - 1) == 6); + + // (0,0) + REQUIRE(hhIO.getHelixSeedE(0, 0) == -4); + REQUIRE(hhIO.getHelixSeedLength1(0, 0) == 7); + REQUIRE(hhIO.getHelixSeedLength2(0, 0) == 6); + + // (0,1) + REQUIRE(hhIO.getHelixSeedE(0, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(0, 1) == 6); + REQUIRE(hhIO.getHelixSeedLength2(0, 1) == 5); + + // (2,0) + REQUIRE(hhIO.getHelixSeedE(2, 0) == -3); + REQUIRE(hhIO.getHelixSeedLength1(2, 0) == 5); + REQUIRE(hhIO.getHelixSeedLength2(2, 0) == 4); + + // (2,1) + REQUIRE(hhIO.getHelixSeedE(2, 1) == -3); + REQUIRE(hhIO.getHelixSeedLength1(2, 1) == 5); + REQUIRE(hhIO.getHelixSeedLength2(2, 1) == 5); + + // (3,0) + REQUIRE(hhIO.getHelixSeedE(3, 0) == -2); + REQUIRE(hhIO.getHelixSeedLength1(3, 0) == 4); + REQUIRE(hhIO.getHelixSeedLength2(3, 0) == 3); + + // (3,1) + REQUIRE(hhIO.getHelixSeedE(3, 1) == -2); + REQUIRE(hhIO.getHelixSeedLength1(3, 1) == 4); + REQUIRE(hhIO.getHelixSeedLength2(3, 1) == 3); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhIO.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 6); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (3,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 3, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 6); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 6); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Case (1,1) + interaction.clear(); + hhIO.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } +} diff --git a/tests/HelixHandlerUnpairedSeed_test.cpp b/tests/HelixHandlerUnpairedSeed_test.cpp new file mode 100644 index 00000000..9ac67b43 --- /dev/null +++ b/tests/HelixHandlerUnpairedSeed_test.cpp @@ -0,0 +1,896 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerUnpaired.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/SeedHandlerMfe.h" + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerUnpairedSeed", "[HelixHandlerUnpaired]" ) { + + SECTION("HelixSeed: Case 1 - all complementary", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 9); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -3); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 4); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhU.getHelixSeedE(0, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(0, 1) == 4); + REQUIRE(hhU.getHelixSeedLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhU.getHelixSeedE(0, 2) == -2); + REQUIRE(hhU.getHelixSeedLength1(0, 2) == 3); + REQUIRE(hhU.getHelixSeedLength2(0, 2) == 3); + + // (0,3) + REQUIRE(hhU.getHelixSeedE(0, 3) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(0, 3) == 0); + REQUIRE(hhU.getHelixSeedLength2(0, 3) == 0); + + // (1,1) + REQUIRE(hhU.getHelixSeedE(1, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhU.getHelixSeedLength2(1, 1) == 4); + + // (2,2) + REQUIRE(hhU.getHelixSeedE(2, 2) == -2); + REQUIRE(hhU.getHelixSeedLength1(2, 2) == 3); + REQUIRE(hhU.getHelixSeedLength2(2, 2) == 3); + + // (4,4) + REQUIRE(hhU.getHelixSeedE(4, 4) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(4, 4) == 0); + REQUIRE(hhU.getHelixSeedLength2(4, 4) == 0); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helixSeed + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (4,4) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 4, 4); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 2 - 'A' disrupting complementarity", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGAGG"); + RnaSequence r2("r2", "CCACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 0); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhU.getHelixSeedE(0, 2) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(0, 2) == 0); + REQUIRE(hhU.getHelixSeedLength2(0, 2) == 0); + + // (1,3) + REQUIRE(hhU.getHelixSeedE(1, 3) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(1, 3) == 0); + REQUIRE(hhU.getHelixSeedLength2(1, 3) == 0); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 3 - only seed possible", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "AGGGA"); + RnaSequence r2("r2", "ACCCA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 1); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 0); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 0); + + // (0,2) + REQUIRE(hhU.getHelixSeedE(1, 0) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(1, 0) == 0); + REQUIRE(hhU.getHelixSeedLength2(1, 0) == 0); + + // (1,3) + REQUIRE(hhU.getHelixSeedE(1, 1) == -2); + REQUIRE(hhU.getHelixSeedLength1(1, 1) == 3); + REQUIRE(hhU.getHelixSeedLength2(1, 1) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("HelixSeed: Case4 - unpaired allowed in seed", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGAGG"); + RnaSequence r2("r2", "CCACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 2, 1, 1, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 4); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -3); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 5); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 5); + + // (0,2) + REQUIRE(hhU.getHelixSeedE(1, 0) == -2); + REQUIRE(hhU.getHelixSeedLength1(1, 0) == 4); + REQUIRE(hhU.getHelixSeedLength2(1, 0) == 4); + + // (1,3) + REQUIRE(hhU.getHelixSeedE(1, 1) == -2); + REQUIRE(hhU.getHelixSeedLength1(1, 1) == 4); + REQUIRE(hhU.getHelixSeedLength2(1, 1) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (0,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 1); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("HelixSeed: Case 5 - leading unpaired bases 1", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGAGGG"); + RnaSequence r2("r2", "CCCACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 3); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 6); + + // (1,1) + REQUIRE(hhU.getHelixSeedE(1, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(1, 1) == 5); + REQUIRE(hhU.getHelixSeedLength2(1, 1) == 5); + + // (3,3) + REQUIRE(hhU.getHelixSeedE(3, 3) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 3) == 3); + REQUIRE(hhU.getHelixSeedLength2(3, 3) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (0,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 0); + + } + + SECTION("HelixSeed: Case 6 - leading unpaired bases 2", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GAGGGG"); + RnaSequence r2("r2", "CCCACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 5); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 6); + + // (0,1) + REQUIRE(hhU.getHelixSeedE(0, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(0, 1) == 5); + REQUIRE(hhU.getHelixSeedLength2(0, 1) == 5); + +// // (2,0) +// REQUIRE(hhU.getHelixSeedE(2, 0) == -3); +// REQUIRE(hhU.getHelixSeedLength1(2, 0) == 4); +// REQUIRE(hhU.getHelixSeedLength2(2, 0) == 6); + + // (2,1) + REQUIRE(hhU.getHelixSeedE(2, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(2, 1) == 4); + REQUIRE(hhU.getHelixSeedLength2(2, 1) == 5); + + // (2,3) + REQUIRE(hhU.getHelixSeedE(2, 3) == -2); + REQUIRE(hhU.getHelixSeedLength1(2, 3) == 3); + REQUIRE(hhU.getHelixSeedLength2(2, 3) == 3); + + // (3,3) + REQUIRE(hhU.getHelixSeedE(3, 3) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 3) == 3); + REQUIRE(hhU.getHelixSeedLength2(3, 3) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 0); + + // Case (0,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("HelixSeed: Case 7 - trailing unpaired bases 1", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 1); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 6); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 7 - trailing unpaired bases 2", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCACCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 2); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 6); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 1) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 1) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 1) == 6); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Case (0,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 1, 1); + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 8 - Leading + trailing unpaired bases", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GAGGGG"); + RnaSequence r2("r2", "CACCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 5); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 6); + + // (0,1) - Not working + REQUIRE(hhU.getHelixSeedE(0, 1) == E_INF); + REQUIRE(hhU.getHelixSeedLength1(0, 1) == 0); + REQUIRE(hhU.getHelixSeedLength2(0, 1) == 0); + + // (2,0) + REQUIRE(hhU.getHelixSeedE(2, 0) == -3); + REQUIRE(hhU.getHelixSeedLength1(2, 0) == 4); + REQUIRE(hhU.getHelixSeedLength2(2, 0) == 4); + + // (2,1) + REQUIRE(hhU.getHelixSeedE(2, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(2, 1) == 4); + REQUIRE(hhU.getHelixSeedLength2(2, 1) == 5); + + // (3,0) + REQUIRE(hhU.getHelixSeedE(3, 0) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 0) == 3); + REQUIRE(hhU.getHelixSeedLength2(3, 0) == 3); + + // (3,1) + REQUIRE(hhU.getHelixSeedE(3, 1) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 1) == 3); + REQUIRE(hhU.getHelixSeedLength2(3, 1) == 3); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (3,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 3, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 0); + } + + SECTION("HelixSeed: Case 9 - Leading + trailing unpaired bases + seed allows 1 unpaired", + "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GAGGAGG"); + RnaSequence r2("r2", "CACCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 1, 1, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 6); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 7); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 6); + + // (0,1) + REQUIRE(hhU.getHelixSeedE(0, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(0, 1) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 1) == 5); + + // (2,0) + REQUIRE(hhU.getHelixSeedE(2, 0) == -3); + REQUIRE(hhU.getHelixSeedLength1(2, 0) == 5); + REQUIRE(hhU.getHelixSeedLength2(2, 0) == 4); + + // (2,1) + REQUIRE(hhU.getHelixSeedE(2, 1) == -3); + REQUIRE(hhU.getHelixSeedLength1(2, 1) == 5); + REQUIRE(hhU.getHelixSeedLength2(2, 1) == 5); + + // (3,0) + REQUIRE(hhU.getHelixSeedE(3, 0) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 0) == 4); + REQUIRE(hhU.getHelixSeedLength2(3, 0) == 3); + + // (3,1) + REQUIRE(hhU.getHelixSeedE(3, 1) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 1) == 4); + REQUIRE(hhU.getHelixSeedLength2(3, 1) == 3); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 3); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (3,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 3, 1); + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 5); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + // Case (1,1) + interaction.clear(); + hhU.traceBackHelixSeed(interaction, 0, 1); + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 2); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("HelixSeed: Case 10 - seed surrounded by unpaired bases", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGAGGGAAGG"); + RnaSequence r2("r2", "CACCCCACAAC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 7, 2, 999, 0, false); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + SeedHandlerMfe sH(energy, sC); + HelixHandlerUnpaired hhU(energy, hC); + + sH.fillSeed(0, energy.size1() - 1, 0, energy.size2() - 1); + hhU.setSeedHandler(sH); + hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////// FILLHELIXSEED ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelixSeed(0, energy.size1() - 1, 0, energy.size2() - 1) == 6); + + // (0,0) + REQUIRE(hhU.getHelixSeedE(0, 0) == -6); + REQUIRE(hhU.getHelixSeedLength1(0, 0) == 10); + REQUIRE(hhU.getHelixSeedLength2(0, 0) == 11); + + // (0,3) + REQUIRE(hhU.getHelixSeedE(0, 3) == -4); + REQUIRE(hhU.getHelixSeedLength1(0, 3) == 6); + REQUIRE(hhU.getHelixSeedLength2(0, 3) == 6); + + // (1,3) + REQUIRE(hhU.getHelixSeedE(1, 3) == -5); + REQUIRE(hhU.getHelixSeedLength1(1, 3) == 9); + REQUIRE(hhU.getHelixSeedLength2(1, 3) == 8); + + // (1,5) + REQUIRE(hhU.getHelixSeedE(1, 5) == -3); + REQUIRE(hhU.getHelixSeedLength1(1, 5) == 5); + REQUIRE(hhU.getHelixSeedLength2(1, 5) == 4); + + // (3,5) + REQUIRE(hhU.getHelixSeedE(3, 5) == -4); + REQUIRE(hhU.getHelixSeedLength1(3, 5) == 7); + REQUIRE(hhU.getHelixSeedLength2(3, 5) == 6); + + // (3,6) + REQUIRE(hhU.getHelixSeedE(3, 6) == -2); + REQUIRE(hhU.getHelixSeedLength1(3, 6) == 3); + REQUIRE(hhU.getHelixSeedLength2(3, 6) == 3); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + Interaction interaction(r1, r2); + hhU.traceBackHelixSeed(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 5); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 7); + + REQUIRE(interaction.basePairs.rbegin()->first == 8); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + } +} diff --git a/tests/HelixHandlerUnpaired_test.cpp b/tests/HelixHandlerUnpaired_test.cpp new file mode 100644 index 00000000..e31bfe16 --- /dev/null +++ b/tests/HelixHandlerUnpaired_test.cpp @@ -0,0 +1,876 @@ + +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerUnpaired.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/ReverseAccessibility.h" + + +using namespace IntaRNA; + +TEST_CASE( "HelixHandlerUnpaired", "[HelixHandlerUnpaired]") { + + +// SECTION("getter", "[HelixHandlerUnpaired]") { +// +// RnaSequence r1("r1", "GGGGG"); +// RnaSequence r2("r2", "CCCCC"); +// AccessibilityDisabled acc1(r1, 0, NULL); +// AccessibilityDisabled acc2(r2, 0, NULL); +// ReverseAccessibility racc(acc2); +// InteractionEnergyBasePair energy(acc1, racc); +// +// HelixConstraint hC(2, 10, 2, 999, 0, false); +// HelixHandlerUnpaired hhU(energy, hC); +// +// REQUIRE(&hhU.getInteractionEnergy() == &energy); +// REQUIRE(&hhU.getConstraint() == &hC); +// +// } +// + SECTION("Helix: Case 1 - Everything is complementary", "[HelixHandlerUnpaired]") { + + // Case 1 Perfect sequence + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // When counting all non-inf values for helices -> 29 + // When only counting the optimal helices that are non-inf -> 16 + // (0,0) -> 4 ; (1,0) -> 4 ; (2,0) -> 3 ; (3,0) -> 2 + // (0,1) -> 4 ; (1,1) -> 4 ; (2,1) -> 3 ; (3,1) -> 2 + // (0,2) -> 3 ; (1,2) -> 3 ; (2,2) -> 3 ; (3,2) -> 2 + // (0,3) -> 2 ; (1,3) -> 2 ; (2,3) -> 2 ; (3,3) -> 2 + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 16); + + + // All optimal combinations + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -3); + REQUIRE(hhU.getHelixLength1(0, 0) == 4); + REQUIRE(hhU.getHelixLength2(0, 0) == hhU.getHelixLength1(0, 0)); + + // (0,1) + REQUIRE(hhU.getHelixE(0, 1) == -3); + REQUIRE(hhU.getHelixLength1(0, 1) == 4); + REQUIRE(hhU.getHelixLength2(0, 1) == hhU.getHelixLength1(0, 1)); + + // (0,2) + REQUIRE(hhU.getHelixE(0, 2) == -2); + REQUIRE(hhU.getHelixLength1(0, 2) == 3); + REQUIRE(hhU.getHelixLength2(0, 2) == hhU.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhU.getHelixE(0, 3) == -1); + REQUIRE(hhU.getHelixLength1(0, 3) == 2); + REQUIRE(hhU.getHelixLength2(0, 3) == hhU.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhU.getHelixE(1, 0) == -3); + REQUIRE(hhU.getHelixLength1(1, 0) == 4); + REQUIRE(hhU.getHelixLength2(1, 0) == hhU.getHelixLength1(1, 0)); + + // (1,1) + REQUIRE(hhU.getHelixE(1, 1) == -3); + REQUIRE(hhU.getHelixLength1(1, 1) == 4); + REQUIRE(hhU.getHelixLength2(1, 1) == hhU.getHelixLength1(1, 1)); + + // (1,2) + REQUIRE(hhU.getHelixE(1, 2) == -2); + REQUIRE(hhU.getHelixLength1(1, 2) == 3); + REQUIRE(hhU.getHelixLength2(1, 2) == hhU.getHelixLength1(1, 2)); + + // (1,3) + REQUIRE(hhU.getHelixE(1, 3) == -1); + REQUIRE(hhU.getHelixLength1(1, 3) == 2); + REQUIRE(hhU.getHelixLength2(1, 3) == hhU.getHelixLength1(1, 3)); + + // (2,0) + REQUIRE(hhU.getHelixE(2, 0) == -2); + REQUIRE(hhU.getHelixLength1(2, 0) == 3); + REQUIRE(hhU.getHelixLength2(2, 0) == hhU.getHelixLength1(2, 0)); + + // (2,1) + REQUIRE(hhU.getHelixE(2, 1) == -2); + REQUIRE(hhU.getHelixLength1(2, 1) == 3); + REQUIRE(hhU.getHelixLength2(2, 1) == hhU.getHelixLength1(2, 1)); + + // (2,2) + REQUIRE(hhU.getHelixE(2, 2) == -2); + REQUIRE(hhU.getHelixLength1(2, 2) == 3); + REQUIRE(hhU.getHelixLength2(2, 2) == hhU.getHelixLength1(2, 2)); + + // (2,3) + REQUIRE(hhU.getHelixE(2, 3) == -1); + REQUIRE(hhU.getHelixLength1(2, 3) == 2); + REQUIRE(hhU.getHelixLength2(2, 3) == hhU.getHelixLength1(2, 3)); + + // (3,0) + REQUIRE(hhU.getHelixE(3, 0) == -1); + REQUIRE(hhU.getHelixLength1(3, 0) == 2); + REQUIRE(hhU.getHelixLength2(3, 0) == hhU.getHelixLength1(3, 0)); + + // (3,1) + REQUIRE(hhU.getHelixE(3, 1) == -1); + REQUIRE(hhU.getHelixLength1(3, 1) == 2); + REQUIRE(hhU.getHelixLength2(3, 1) == hhU.getHelixLength1(3, 1)); + + // (3,2) + REQUIRE(hhU.getHelixE(3, 2) == -1); + REQUIRE(hhU.getHelixLength1(3, 2) == 2); + REQUIRE(hhU.getHelixLength2(3, 2) == hhU.getHelixLength1(3, 2)); + + // (3,3) + REQUIRE(hhU.getHelixE(3, 3) == -1); + REQUIRE(hhU.getHelixLength1(3, 3) == 2); + REQUIRE(hhU.getHelixLength2(3, 3) == hhU.getHelixLength1(3, 3)); + + + // Several cases that do not allow a helix + + // (4,4) + REQUIRE(hhU.getHelixE(4, 4) == E_INF); + REQUIRE(hhU.getHelixLength1(4, 4) == 0); + REQUIRE(hhU.getHelixLength2(4, 4) == hhU.getHelixLength1(4, 4)); + + // (4,3) + REQUIRE(hhU.getHelixE(0, 4) == E_INF); + REQUIRE(hhU.getHelixLength1(0, 4) == 0); + REQUIRE(hhU.getHelixLength2(0, 4) == hhU.getHelixLength1(0, 4)); + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1, r2); + + hhU.traceBackHelix(interaction, 0, 0); + + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + + + // Case (2,1) + ////////////////////// + interaction.clear(); + + hhU.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 3); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + } + + SECTION("Helix: Case 2 - Sequence 1 contains an A", "[HelixHandlerUnpaired]") { + // Case 2 - sequence containing an "A" to disrupt perfect stacking + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 16); + + + // All optimal combinations + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -3); + REQUIRE(hhU.getHelixLength1(0, 0) == 5); + REQUIRE(hhU.getHelixLength2(0, 0) == 4); + + // (0,1) + REQUIRE(hhU.getHelixE(0, 1) == -3); + REQUIRE(hhU.getHelixLength1(0, 1) == 5); + REQUIRE(hhU.getHelixLength2(0, 1) == 4); + + // (0,2) + REQUIRE(hhU.getHelixE(0, 2) == -2); + REQUIRE(hhU.getHelixLength1(0, 2) == 3); + REQUIRE(hhU.getHelixLength2(0, 2) == hhU.getHelixLength1(0, 2)); + + // (0,3) + REQUIRE(hhU.getHelixE(0, 3) == -1); + REQUIRE(hhU.getHelixLength1(0, 3) == 2); + REQUIRE(hhU.getHelixLength2(0, 3) == hhU.getHelixLength1(0, 3)); + + // (1,0) + REQUIRE(hhU.getHelixE(1, 0) == -3); + REQUIRE(hhU.getHelixLength1(1, 0) == 5); + REQUIRE(hhU.getHelixLength2(1, 0) == 4); + + // (1,1) + REQUIRE(hhU.getHelixE(1, 1) == -3); + REQUIRE(hhU.getHelixLength1(1, 1) == 5); + REQUIRE(hhU.getHelixLength2(1, 1) == 4); + + // (1,2) + REQUIRE(hhU.getHelixE(1, 2) == -2); + REQUIRE(hhU.getHelixLength1(1, 2) == 4); + REQUIRE(hhU.getHelixLength2(1, 2) == 3); + + // (1,3) + REQUIRE(hhU.getHelixE(1, 3) == -1); + REQUIRE(hhU.getHelixLength1(1, 3) == 2); + REQUIRE(hhU.getHelixLength2(1, 3) == hhU.getHelixLength1(1, 3)); + + // (2,0) + REQUIRE(hhU.getHelixE(2, 0) == -2); + REQUIRE(hhU.getHelixLength1(2, 0) == 4); + REQUIRE(hhU.getHelixLength2(2, 0) == 3); + + // (2,1) + REQUIRE(hhU.getHelixE(2, 1) == -2); + REQUIRE(hhU.getHelixLength1(2, 1) == 4); + REQUIRE(hhU.getHelixLength2(2, 1) == 3); + + // (2,2) + REQUIRE(hhU.getHelixE(2, 2) == -2); + REQUIRE(hhU.getHelixLength1(2, 2) == 4); + REQUIRE(hhU.getHelixLength2(2, 2) == 3); + + // (2,3) + REQUIRE(hhU.getHelixE(2, 3) == -1); + REQUIRE(hhU.getHelixLength1(2, 3) == 3); + REQUIRE(hhU.getHelixLength2(2, 3) == 2); + + // (4,0) + REQUIRE(hhU.getHelixE(4, 0) == -1); + REQUIRE(hhU.getHelixLength1(4, 0) == 2); + REQUIRE(hhU.getHelixLength2(4, 0) == hhU.getHelixLength1(4, 0)); + + // (4,1) + REQUIRE(hhU.getHelixE(4, 1) == -1); + REQUIRE(hhU.getHelixLength1(4, 1) == 2); + REQUIRE(hhU.getHelixLength2(4, 1) == hhU.getHelixLength1(4, 1)); + + // (4,2) + REQUIRE(hhU.getHelixE(4, 2) == -1); + REQUIRE(hhU.getHelixLength1(4, 2) == 2); + REQUIRE(hhU.getHelixLength2(4, 2) == hhU.getHelixLength1(4, 2)); + + // (4,3) + REQUIRE(hhU.getHelixE(4, 3) == -1); + REQUIRE(hhU.getHelixLength1(4, 3) == 2); + REQUIRE(hhU.getHelixLength2(4, 3) == hhU.getHelixLength1(4, 3)); + + + // Not viable cases + // (3,0) Not complementary + REQUIRE(hhU.getHelixE(3,0) == E_INF); + REQUIRE(hhU.getHelixLength1(3,0) == 0); + REQUIRE(hhU.getHelixLength2(3,0) == hhU.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhU.getHelixE(5,4) == E_INF); + REQUIRE(hhU.getHelixLength1(5,4) == 0); + REQUIRE(hhU.getHelixLength2(5,4) == hhU.getHelixLength1(5,4)); + + // (5,3) no helix possible + REQUIRE(hhU.getHelixE(5,3) == E_INF); + REQUIRE(hhU.getHelixLength1(5,3) == 0); + REQUIRE(hhU.getHelixLength2(5,3) == hhU.getHelixLength1(5,3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + hhU.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 3); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + // Case (2,1) + ////////////////////// + interaction.clear(); + + hhU.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 2); + + } + + SECTION("Helix: Case 3 - A 'wall' of A's disrupts the possible helices", "[HelixHandlerUnpaired]") { + // Case 2 - sequence containing an "A"-wall to disrupt perfect stacking + RnaSequence r1("r1", "GGGAAGG"); + RnaSequence r2("r2", "CCAACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 15); + + REQUIRE_FALSE(energy.areComplementary(5,4)); + // All optimal combinations + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -3); + REQUIRE(hhU.getHelixLength1(0, 0) == 6); + REQUIRE(hhU.getHelixLength2(0, 0) == 7); + + // (0,1) + REQUIRE(hhU.getHelixE(0, 1) == -3); + REQUIRE(hhU.getHelixLength1(0, 1) == 6); + REQUIRE(hhU.getHelixLength2(0, 1) == 6); + + // (0,2) + REQUIRE(hhU.getHelixE(0, 2) == -2); + REQUIRE(hhU.getHelixLength1(0, 2) == 3); + REQUIRE(hhU.getHelixLength2(0, 2) == 5); + + // (0,5) + REQUIRE(hhU.getHelixE(0, 5) == -1); + REQUIRE(hhU.getHelixLength1(0, 5) == 2); + REQUIRE(hhU.getHelixLength2(0, 5) == hhU.getHelixLength1(0, 5)); + + // (1,0) + REQUIRE(hhU.getHelixE(1, 0) == -3); + REQUIRE(hhU.getHelixLength1(1, 0) == 6); + REQUIRE(hhU.getHelixLength2(1, 0) == 6); + + // (1,1) + REQUIRE(hhU.getHelixE(1, 1) == -1); + REQUIRE(hhU.getHelixLength1(1, 1) == 2); + REQUIRE(hhU.getHelixLength2(1, 1) == hhU.getHelixLength1(1, 1)); + + // (1,2) + REQUIRE(hhU.getHelixE(1, 2) == -2); + REQUIRE(hhU.getHelixLength1(1, 2) == 5); + REQUIRE(hhU.getHelixLength2(1, 2) == 5); + + // (1,5) + REQUIRE(hhU.getHelixE(1, 5) == -1); + REQUIRE(hhU.getHelixLength1(1, 5) == 2); + REQUIRE(hhU.getHelixLength2(1, 5) == hhU.getHelixLength1(1, 5)); + + // (2,0) + REQUIRE(hhU.getHelixE(2, 0) == -2); + REQUIRE(hhU.getHelixLength1(2, 0) == 5); + REQUIRE(hhU.getHelixLength2(2, 0) == 3); + + // (2,1) + REQUIRE(hhU.getHelixE(2, 1) == -2); + REQUIRE(hhU.getHelixLength1(2, 1) == 5); + REQUIRE(hhU.getHelixLength2(2, 1) == 5); + + // (2,5) + REQUIRE(hhU.getHelixE(2, 5) == -1); + REQUIRE(hhU.getHelixLength1(2, 5) == 4); + REQUIRE(hhU.getHelixLength2(2, 5) == 2); + + // (5,0) + REQUIRE(hhU.getHelixE(5, 0) == -1); + REQUIRE(hhU.getHelixLength1(5, 0) == 2); + REQUIRE(hhU.getHelixLength2(5, 0) == hhU.getHelixLength1(5, 0)); + + // (5,1) + REQUIRE(hhU.getHelixE(5, 1) == -1); + REQUIRE(hhU.getHelixLength1(5, 1) == 2); + REQUIRE(hhU.getHelixLength2(5, 1) == hhU.getHelixLength1(5, 1)); + + // (5,2) + REQUIRE(hhU.getHelixE(5, 2) == -1); + REQUIRE(hhU.getHelixLength1(5, 2) == 2); + REQUIRE(hhU.getHelixLength2(5, 2) == 4); + + // (5,5) + REQUIRE(hhU.getHelixE(5, 5) == -1); + REQUIRE(hhU.getHelixLength1(5, 5) == 2); + REQUIRE(hhU.getHelixLength2(5, 5) == hhU.getHelixLength1(5, 5)); + + + + // Not viable cases + // (2,2) Not possible. Too many unpaired bases. + REQUIRE(hhU.getHelixE(2, 2) == E_INF); + REQUIRE(hhU.getHelixLength1(2, 2) == 0); + REQUIRE(hhU.getHelixLength2(2, 2) == 0); + + // (3,0) Not complementary + REQUIRE(hhU.getHelixE(3,0) == E_INF); + REQUIRE(hhU.getHelixLength1(3,0) == 0); + REQUIRE(hhU.getHelixLength2(3,0) == hhU.getHelixLength1(3,0)); + + // (5,4) no Helix possible (both sides too short) + REQUIRE(hhU.getHelixE(2,2) == E_INF); + REQUIRE(hhU.getHelixLength1(2,2) == 0); + REQUIRE(hhU.getHelixLength2(2,2) == hhU.getHelixLength1(2,2)); + + // (5,3) no helix possible + REQUIRE(hhU.getHelixE(5,3) == E_INF); + REQUIRE(hhU.getHelixLength1(5,3) == 0); + REQUIRE(hhU.getHelixLength2(5,3) == hhU.getHelixLength1(5,3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhU.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 2); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + // Case (2,1) - two unpaired bases in sequence 1 + ////////////////////// + + interaction.clear(); + hhU.traceBackHelix(interaction, 2, 1); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 5); + REQUIRE(interaction.basePairs.begin()->second == 4); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 4); + + // Case (5,5) - Possible but only 2 base pairs long (e.g no bp needs to be reported) + ////////////////////// + + interaction.clear(); + hhU.traceBackHelix(interaction, 5, 5); + + REQUIRE(interaction.basePairs.size() == 0); + + + // Case (2,0) + ///////////////////// + interaction.clear(); + hhU.traceBackHelix(interaction, 2, 0); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 5); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 5); + + } + + SECTION("Helix: Case 4 - No interaction possible", "[HelixHandlerUnpaired]") { + // Case 4 -NO HELIX POSSIBLE + RnaSequence r1("r1", "AAAAAAA"); + RnaSequence r2("r2", "AAAAAAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 0); + + // NO POSSIBLE HELICES + // (2,2) + REQUIRE(hhU.getHelixE(2, 2) == E_INF); + REQUIRE(hhU.getHelixLength1(2, 2) == 0); + REQUIRE(hhU.getHelixLength2(2, 2) == hhU.getHelixLength1(2, 2)); + + // (0,3) + REQUIRE(hhU.getHelixE(0, 3) == E_INF); + REQUIRE(hhU.getHelixLength1(0, 3) == 0); + REQUIRE(hhU.getHelixLength2(0, 3) == hhU.getHelixLength1(0, 3)); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Exceptions are only thrown in debug mode +#if INTARNA_IN_DEBUG_MODE + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + + REQUIRE_THROWS_WITH(hhU.traceBackHelix(interaction, 0, 0), "HelixHandlerUnpaired::traceBackHelix(i1=0,i2=0) no helix known (E_INF)"); + + // Case (1,3) + ////////////////////// + interaction.clear(); + + REQUIRE_THROWS_WITH(hhU.traceBackHelix(interaction, 1, 3), "HelixHandlerUnpaired::traceBackHelix(i1=1,i2=3) no helix known (E_INF)"); +#endif + + } + + SECTION("Helix: Case 5 - unpaired bases in sequence 1 ", "[HelixHandlerUnpaired]") { + // Case 4 -NO HELIX POSSIBLE + RnaSequence r1("r1", "GGAAGG"); + RnaSequence r2("r2", "CCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 9); + + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -3); + REQUIRE(hhU.getHelixLength1(0, 0) == 6); + REQUIRE(hhU.getHelixLength2(0, 0) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhU.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 1); + REQUIRE(interaction.basePairs.begin()->second == 2); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("Helix: Case 6 - 2 base pairs ", "[HelixHandlerUnpaired]") { + // Case 4 -NO HELIX POSSIBLE + RnaSequence r1("r1", "GAG"); + RnaSequence r2("r2", "CAC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 1); + + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -1); + REQUIRE(hhU.getHelixLength1(0, 0) == 3); + REQUIRE(hhU.getHelixLength2(0, 0) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (0,0) + ////////////////////// + Interaction interaction(r1,r2); + hhU.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + } + + SECTION("Helix: Case 7 - unpaired bases after 2,2 ", "[HelixHandlerUnpaired]") { + + RnaSequence r1("r1", "GGGAGG"); + RnaSequence r2("r2", "CCACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 5, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 16); + + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -4); + REQUIRE(hhU.getHelixLength1(0, 0) == 6); + REQUIRE(hhU.getHelixLength2(0, 0) == 6); + + // (0,1) + REQUIRE(hhU.getHelixE(0, 1) == -3); + REQUIRE(hhU.getHelixLength1(0, 1) == 5); + REQUIRE(hhU.getHelixLength2(0, 1) == 5); + + // (0,2) + REQUIRE(hhU.getHelixE(0, 2) == -2); + REQUIRE(hhU.getHelixLength1(0, 2) == 3); + REQUIRE(hhU.getHelixLength2(0, 2) == 4); + + // (0,4) + REQUIRE(hhU.getHelixE(0, 4) == -1); + REQUIRE(hhU.getHelixLength1(0, 4) == 2); + REQUIRE(hhU.getHelixLength2(0, 4) == 2); + + // (1,0) + REQUIRE(hhU.getHelixE(1, 0) == -3); + REQUIRE(hhU.getHelixLength1(1, 0) == 5); + REQUIRE(hhU.getHelixLength2(1, 0) == 5); + + // (1,1) + REQUIRE(hhU.getHelixE(1, 1) == -3); + REQUIRE(hhU.getHelixLength1(1, 1) == 5); + REQUIRE(hhU.getHelixLength2(1, 1) == 5); + + // (1,2) + REQUIRE(hhU.getHelixE(1, 2) == -2); + REQUIRE(hhU.getHelixLength1(1, 2) == 4); + REQUIRE(hhU.getHelixLength2(1, 2) == 4); + + // (1,4) + REQUIRE(hhU.getHelixE(1, 4) == -1); + REQUIRE(hhU.getHelixLength1(1, 4) == 2); + REQUIRE(hhU.getHelixLength2(1, 4) == 2); + + // (2,0) + REQUIRE(hhU.getHelixE(2, 0) == -2); + REQUIRE(hhU.getHelixLength1(2, 0) == 4); + REQUIRE(hhU.getHelixLength2(2, 0) == 3); + + // (2,1) + REQUIRE(hhU.getHelixE(2, 1) == -2); + REQUIRE(hhU.getHelixLength1(2, 1) == 4); + REQUIRE(hhU.getHelixLength2(2, 1) == 4); + + // (2,2) + REQUIRE(hhU.getHelixE(2, 2) == -2); + REQUIRE(hhU.getHelixLength1(2, 2) == 4); + REQUIRE(hhU.getHelixLength2(2, 2) == 4); + + // (2,4) + REQUIRE(hhU.getHelixE(2, 4) == -1); + REQUIRE(hhU.getHelixLength1(2, 4) == 3); + REQUIRE(hhU.getHelixLength2(2, 4) == 2); + + // (4,0) + REQUIRE(hhU.getHelixE(4, 0) == -1); + REQUIRE(hhU.getHelixLength1(4, 0) == 2); + REQUIRE(hhU.getHelixLength2(4, 0) == 2); + + // (4,1) + REQUIRE(hhU.getHelixE(4, 1) == -1); + REQUIRE(hhU.getHelixLength1(4, 1) == 2); + REQUIRE(hhU.getHelixLength2(4, 1) == 2); + + // (4,2) + REQUIRE(hhU.getHelixE(4, 2) == -1); + REQUIRE(hhU.getHelixLength1(4, 2) == 2); + REQUIRE(hhU.getHelixLength2(4, 2) == 3); + + // (4,4) + REQUIRE(hhU.getHelixE(4, 4) == -1); + REQUIRE(hhU.getHelixLength1(4, 4) == 2); + REQUIRE(hhU.getHelixLength2(4, 4) == 2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (2,2) + ////////////////////// + Interaction interaction(r1,r2); + hhU.traceBackHelix(interaction, 2, 2); + + REQUIRE(interaction.basePairs.size() == 1); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 4); + REQUIRE(interaction.basePairs.begin()->second == 1); + + REQUIRE(interaction.basePairs.rbegin()->first == 4); + REQUIRE(interaction.basePairs.rbegin()->second == 1); + + } + + SECTION("Helix: Case 8 - unpaired bases, many unpaired bases", "[HelixHandlerUnpaired]") { + RnaSequence r1("r1", "GAAGAGG"); + RnaSequence r2("r2", "CAACACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 2, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 6); + + // (0,0) + REQUIRE(hhU.getHelixE(0, 0) == -3); + REQUIRE(hhU.getHelixLength1(0, 0) == 7); + REQUIRE(hhU.getHelixLength2(0, 0) == 7); + + // (3,0) + REQUIRE(hhU.getHelixE(3, 0) == -2); + REQUIRE(hhU.getHelixLength1(3, 0) == 4); + REQUIRE(hhU.getHelixLength2(3, 0) == 4); + + // (3,1) + REQUIRE(hhU.getHelixE(3, 1) == -2); + REQUIRE(hhU.getHelixLength1(3, 1) == 4); + REQUIRE(hhU.getHelixLength2(3, 1) == 6); + + // (5,0) + REQUIRE(hhU.getHelixE(5, 0) == -1); + REQUIRE(hhU.getHelixLength1(5, 0) == 2); + REQUIRE(hhU.getHelixLength2(5, 0) == 2); + + // (5,1) + REQUIRE(hhU.getHelixE(5, 1) == -1); + REQUIRE(hhU.getHelixLength1(5, 1) == 2); + REQUIRE(hhU.getHelixLength2(5, 1) == 3); + + // (5,3) + REQUIRE(hhU.getHelixE(5, 3) == -1); + REQUIRE(hhU.getHelixLength1(5, 3) == 2); + REQUIRE(hhU.getHelixLength2(5, 3) == 4); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (2,2) + ////////////////////// + Interaction interaction(r1,r2); + hhU.traceBackHelix(interaction, 0, 0); + + REQUIRE(interaction.basePairs.size() == 2); + // First / last base pair of helix + REQUIRE(interaction.basePairs.begin()->first == 3); + REQUIRE(interaction.basePairs.begin()->second == 5); + + REQUIRE(interaction.basePairs.rbegin()->first == 5); + REQUIRE(interaction.basePairs.rbegin()->second == 3); + + } + + SECTION("Helix: Case 9 - unpaired bases, many unpaired bases --only allow 1 unpaired base", "[HelixHandlerUnpaired]") { + RnaSequence r1("r1", "GAAGAGG"); + RnaSequence r2("r2", "CAACACC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hC(2, 4, 1, 999, 0, false); + HelixHandlerUnpaired hhU(energy, hC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// FILLHELIX //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + REQUIRE(hhU.fillHelix(0, energy.size1() - 1, 0, energy.size2() - 1) == 3); + + // (3,0) + REQUIRE(hhU.getHelixE(3, 0) == -2); + REQUIRE(hhU.getHelixLength1(3, 0) == 4); + REQUIRE(hhU.getHelixLength2(3, 0) == 4); + + // (5,0) + REQUIRE(hhU.getHelixE(5, 0) == -1); + REQUIRE(hhU.getHelixLength1(5, 0) == 2); + REQUIRE(hhU.getHelixLength2(5, 0) == 2); + + // (5,1) + REQUIRE(hhU.getHelixE(5, 1) == -1); + REQUIRE(hhU.getHelixLength1(5, 1) == 2); + REQUIRE(hhU.getHelixLength2(5, 1) == 3); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////// TRACEBACK /////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Case (5,0) + ////////////////////// + Interaction interaction(r1,r2); + hhU.traceBackHelix(interaction, 5, 0); + + REQUIRE(interaction.basePairs.size() == 0); + + } +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 10756e32..a2e0fd24 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,12 +16,23 @@ runApiTests_SOURCES = \ AccessibilityConstraint_test.cpp \ AccessibilityFromStream_test.cpp \ AccessibilityBasePair_test.cpp \ + HelixConstraint_test.cpp \ + HelixHandlerStackingOnly_test.cpp \ + HelixHandlerStackingOnlyIdxOffset_test.cpp \ + HelixHandlerStackingOnlySeed_test.cpp \ + HelixHandlerStackingOnlySeedIdxOffset_test.cpp \ + HelixHandlerUnpaired_test.cpp \ + HelixHandlerUnpairedIdxOffset_test.cpp \ + HelixHandlerUnpairedSeed_test.cpp \ + HelixHandlerUnpairedSeedIdxOffset_test.cpp \ IndexRange_test.cpp \ IndexRangeList_test.cpp \ Interaction_test.cpp \ InteractionEnergyBasePair_test.cpp \ InteractionRange_test.cpp \ PredictionTrackerProfileMinE_test.cpp \ + PredictorMfe2dHelixHeuristic_test.cpp \ + PredictorMfe2dHelixHeuristicSeed_test.cpp \ PredictionTrackerSpotProb_test.cpp \ NussinovHandler_test.cpp \ RnaSequence_test.cpp \ @@ -29,6 +40,8 @@ runApiTests_SOURCES = \ OutputHandlerInteractionList_test.cpp \ SeedHandlerExplicit_test.cpp \ SeedHandlerNoBulge_test.cpp \ + SeedHandlerMfe_test.cpp \ + SeedHandlerIdxOffset_test.cpp \ runTests.cpp diff --git a/tests/PredictorMfe2dHelixHeuristicSeed_test.cpp b/tests/PredictorMfe2dHelixHeuristicSeed_test.cpp new file mode 100644 index 00000000..80f22abe --- /dev/null +++ b/tests/PredictorMfe2dHelixHeuristicSeed_test.cpp @@ -0,0 +1,96 @@ + +#include "catch.hpp" + +#undef NDEBUG +#define protected public + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/ReverseAccessibility.h" +#include "IntaRNA/PredictorMfe2dHelixHeuristicSeed.h" +#include "IntaRNA/SeedHandlerMfe.h" +#include "IntaRNA/OutputHandlerInteractionList.h" + +using namespace IntaRNA; + +TEST_CASE( "PredictorMfe2dHelixHeuristcSeed", "[PredictorMfe2dHelixHeuristicSeed]") { + + SECTION("Predictor: Case 1", "[PredictorMfe2dHelixHeuristicSeed]") { + + RnaSequence r1("r1", "GGGAAGG"); + RnaSequence r2("r2", "CCAACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 0, 999, 0, false); + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), + ""); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristicSeed pLSH(energy, out, NULL, hc, new SeedHandlerMfe(energy, sC)); + + IndexRange idx1(0, r1.lastPos); + IndexRange idx2(0, r2.lastPos); + OutputConstraint outC(1, OutputConstraint::OVERLAP_SEQ2, 0, 100); + + pLSH.predict(idx1, idx2, outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction *interaction((*out.begin())); + REQUIRE(interaction->basePairs.begin()->first == 0); + REQUIRE(interaction->basePairs.begin()->second == 6); + + REQUIRE(interaction->basePairs.rbegin()->first == 6); + REQUIRE(interaction->basePairs.rbegin()->second == 0); + + REQUIRE(interaction->dotBracket(*interaction) == "(((..((&))..)))"); + } + +// SECTION("Predictor: Case 2", "[PredictorMfe2dHelixHeuristicSeed]") { +// +// RnaSequence r1("r1", "GGGAGG"); +// RnaSequence r2("r2", "CCAACCC"); +// AccessibilityDisabled acc1(r1, 0, NULL); +// AccessibilityDisabled acc2(r2, 0, NULL); +// ReverseAccessibility racc(acc2); +// InteractionEnergyBasePair energy(acc1, racc); +// +// HelixConstraint hc(2, 4, 0, 999, 0, false); +// // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ +// SeedConstraint sC(3, 0, 0, 0, 0, AccessibilityDisabled::ED_UPPER_BOUND, IndexRangeList(""), IndexRangeList(""), +// ""); +// +// SeedHandlerMfe sH(energy, sC); +// OutputHandlerInteractionList out(1); +// +// PredictorMfe2dHelixHeuristicSeed pLSH(energy, out, NULL, hc, new SeedHandlerMfe(energy, sC)); +// +// IndexRange idx1(0, r1.lastPos); +// IndexRange idx2(0, r2.lastPos); +// OutputConstraint outC(1, OutputConstraint::OVERLAP_SEQ2, 0, 100); +// +// pLSH.predict(idx1, idx2, outC); +// +// REQUIRE_FALSE(out.empty()); +// REQUIRE(out.reported() == 1); +// +// const Interaction *interaction((*out.begin())); +// REQUIRE(interaction->basePairs.begin()->first == 0); +// REQUIRE(interaction->basePairs.begin()->second == 6); +// +// REQUIRE(interaction->basePairs.rbegin()->first == 6); +// REQUIRE(interaction->basePairs.rbegin()->second == 0); +// +// REQUIRE(interaction->dotBracket(*interaction) == "(((..((&))..)))"); +// } + +} diff --git a/tests/PredictorMfe2dHelixHeuristic_test.cpp b/tests/PredictorMfe2dHelixHeuristic_test.cpp new file mode 100644 index 00000000..341602fc --- /dev/null +++ b/tests/PredictorMfe2dHelixHeuristic_test.cpp @@ -0,0 +1,253 @@ + +#include "catch.hpp" + +#undef NDEBUG +#define protected public + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/HelixConstraint.h" +#include "IntaRNA/HelixHandlerStackingOnly.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/Interaction.h" +#include "IntaRNA/ReverseAccessibility.h" +#include "IntaRNA/PredictorMfe2dHelixHeuristic.h" +#include "IntaRNA/OutputHandlerInteractionList.h" + +using namespace IntaRNA; + +TEST_CASE( "PredictorMfe2dHelixHeuristc", "[PredictorMfe2dHelixHeuristic]") { + + SECTION("Predictor: Case 1", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "GGGAAGG"); + RnaSequence r2("r2", "CCAACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 0, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction * interaction((*out.begin())); + REQUIRE(interaction->basePairs.begin()->first == 0); + REQUIRE(interaction->basePairs.begin()->second == 6); + + REQUIRE(interaction->basePairs.rbegin()->first == 6); + REQUIRE(interaction->basePairs.rbegin()->second == 0); + + REQUIRE(interaction->dotBracket(*interaction) == "(((..((&))..)))"); + } + + SECTION("Predictor: Case 2", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "AAGGGAAGGA"); + RnaSequence r2("r2", "ACCAACCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 0, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction * interaction((*out.begin())); + REQUIRE(interaction->basePairs.begin()->first == 2); + REQUIRE(interaction->basePairs.begin()->second == 7); + + REQUIRE(interaction->basePairs.rbegin()->first == 8); + REQUIRE(interaction->basePairs.rbegin()->second == 1); + + REQUIRE(interaction->dotBracket(*interaction) == "(((..((&))..)))"); + } + + SECTION("Predictor: Case 3 - no favorable output", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "AAAAAAAA"); + RnaSequence r2("r2", "AAAAAAAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 0, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE(out.empty()); + + } + + SECTION("Predictor: Case 4", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "AAAAAAAAGGG"); + RnaSequence r2("r2", "AAAAAACCCAA"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 0, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction * interaction((*out.begin())); + REQUIRE(interaction->basePairs.begin()->first == 8); + REQUIRE(interaction->basePairs.begin()->second == 8); + + REQUIRE(interaction->basePairs.rbegin()->first == 10); + REQUIRE(interaction->basePairs.rbegin()->second == 6); + + REQUIRE(interaction->dotBracket(*interaction) == "(((&)))"); + } + + SECTION("Predictor: Case 5 - unpaired bases allowed", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "GGGAGGGG"); + RnaSequence r2("r2", "CCCCACCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 2, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction * interaction((*out.begin())); + + REQUIRE(interaction->dotBracket(*interaction) == "((.((.(&)..).)))"); + + REQUIRE(interaction->basePairs.begin()->first == 1); + REQUIRE(interaction->basePairs.begin()->second == 7); + + REQUIRE(interaction->basePairs.rbegin()->first == 7); + REQUIRE(interaction->basePairs.rbegin()->second == 0); + + } + + + SECTION("Predictor: Case 6 - unpaired bases allowed", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "GGGAAGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 5, 2, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction * interaction((*out.begin())); + REQUIRE(interaction->basePairs.begin()->first == 0); + REQUIRE(interaction->basePairs.begin()->second == 4); + + REQUIRE(interaction->basePairs.rbegin()->first == 6); + REQUIRE(interaction->basePairs.rbegin()->second == 0); + + REQUIRE(interaction->dotBracket(*interaction) == "(((..((&)))))"); + } + + SECTION("Predictor: Case 7 - only stacking allowed", "[PredictorMfe2dHelixHeuristic]") { + + RnaSequence r1("r1", "GGGGGG"); + RnaSequence r2("r2", "CCCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + HelixConstraint hc(2, 4, 0, 999, 0, false); + + OutputHandlerInteractionList out(1); + + PredictorMfe2dHelixHeuristic pLSH(energy, out, NULL, hc); + + IndexRange idx1(0,r1.lastPos); + IndexRange idx2(0,r2.lastPos); + OutputConstraint outC(1,OutputConstraint::OVERLAP_SEQ2,0,100); + + pLSH.predict(idx1,idx2,outC); + + REQUIRE_FALSE(out.empty()); + REQUIRE(out.reported() == 1); + + const Interaction * interaction((*out.begin())); + REQUIRE(interaction->dotBracket(*interaction) == "(((((&).))))"); + REQUIRE(interaction->basePairs.begin()->first == 1); + REQUIRE(interaction->basePairs.begin()->second == 5); + + REQUIRE(interaction->basePairs.rbegin()->first == 5); + REQUIRE(interaction->basePairs.rbegin()->second == 0); + + } + +} diff --git a/tests/SeedHandlerIdxOffset_test.cpp b/tests/SeedHandlerIdxOffset_test.cpp new file mode 100644 index 00000000..55ace7d9 --- /dev/null +++ b/tests/SeedHandlerIdxOffset_test.cpp @@ -0,0 +1,43 @@ + +#include "catch.hpp" + +#undef NDEBUG +#define protected public + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/SeedHandlerMfe.h" +#include "IntaRNA/SeedHandlerIdxOffset.h" + + +using namespace IntaRNA; + +TEST_CASE( "SeedHandlerMfe with offset", "[SeedHandlerIdxOffset]") { + + SECTION("SeedHandlerMfe: Case 1 - offset 1", "[SeedHandlerIdxOffset]") { + RnaSequence r1("r1", "GGGGGG"); + RnaSequence r2("r2", "CCCCCG"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3,0,0,0,0 + , AccessibilityDisabled::ED_UPPER_BOUND + , IndexRangeList("") + , IndexRangeList("") + , ""); + + SeedHandlerIdxOffset sHIO(new SeedHandlerMfe(energy, sC)); + + sHIO.setOffset1(1); + sHIO.setOffset2(1); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////// FILLSEED //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + REQUIRE(sHIO.fillSeed(0,energy.size1()-sHIO.getOffset1()-1, 0,energy.size2()-sHIO.getOffset2()-1) > 0); + } +} \ No newline at end of file diff --git a/tests/SeedHandlerMfe_test.cpp b/tests/SeedHandlerMfe_test.cpp new file mode 100644 index 00000000..cf5a97e5 --- /dev/null +++ b/tests/SeedHandlerMfe_test.cpp @@ -0,0 +1,39 @@ + +#include "catch.hpp" + +#undef NDEBUG +#define protected public + +#include "IntaRNA/RnaSequence.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/SeedHandlerMfe.h" + + +using namespace IntaRNA; + +TEST_CASE( "SeedHandlerMfe", "[SeedHandlerMfe]") { + + SECTION("SeedHandlerMfe: Case 1 - offset 1", "[SeedHandlerMfe]") { + RnaSequence r1("r1", "GGGGG"); + RnaSequence r2("r2", "CCCCC"); + AccessibilityDisabled acc1(r1, 0, NULL); + AccessibilityDisabled acc2(r2, 0, NULL); + ReverseAccessibility racc(acc2); + InteractionEnergyBasePair energy(acc1, racc); + + // seedBP / seedMaxUP / seedTMaxUP / seedQMaxUP / seedMaxE / seedMaxED / seedTRange / seedQRange / seedTQ + SeedConstraint sC(3,0,0,0,0 + , AccessibilityDisabled::ED_UPPER_BOUND + , IndexRangeList("") + , IndexRangeList("") + , ""); + + SeedHandlerMfe sHM(energy, sC); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////// FILLSEED //////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + REQUIRE(sHM.fillSeed(0,energy.size1()-1, 0,energy.size2()-1) > 0); + } +} \ No newline at end of file