Skip to content

Session: Ranges

Hannes Hauswedell edited this page Feb 5, 2018 · 6 revisions

Ranges (and views) is a proposal for addition to the C++ Standard, likely to be added in C++20. Luckily it is 99% library code and needs no changes to the language so we can use it now already by including a library! Roughly speaking Ranges and views do what seqan::Infix and seqan::ModifiedString do, but in a well-defined manner and with easy composability.

Study material:

Preparation

  • install GCC >= 6 or Clang >= 3.7
  • clone the above git repository
  • build your apps with -std=c++14

Task 1: Reverse complement view

Template code (click to expand)
#include <iostream>
#include <range/v3/all.hpp>
#include <string>

int main()
{
    std::string s{"ACGTTTATGT"};

    auto complement = [] (auto c)
    {
        switch (c)
        {
            case 'a': return 't';
            case 'A': return 'T';
            case 'c': return 'g';
            case 'C': return 'G';
            case 'g': return 'c';
            case 'G': return 'C';
            case 't': return 'a';
            case 'T': return 'A';
            default: return 'N';
        }
    };

    // make reverse complement view
    auto comp_view = // ?
    std::cout << comp_view << '\n';

    // create infix from [1, 6)
    auto comp_view_inf = // ?
    std::cout << comp_view_inf << '\n';

    // transform back
    auto orig_inf = // ?
    std::cout << orig_inf << '\n';

    return 0;
}
Solution: Click to expand

#include <iostream>
#include <range/v3/all.hpp>
#include <string>

int main()
{
    std::string s{"ACGTTTATGT"};

    auto complement = [] (auto c)
    {
        switch (c)
        {
            case 'a': return 't';
            case 'A': return 'T';
            case 'c': return 'g';
            case 'C': return 'G';
            case 'g': return 'c';
            case 'G': return 'C';
            case 't': return 'a';
            case 'T': return 'A';
            default: return 'N';
        }
    };

    // make reverse complement view
    auto comp_view = s | ranges::view::reverse | ranges::view::transform(complement);
    std::cout << comp_view << '\n';

    // create infix from [1, 7)
    auto comp_view_inf = comp_view | ranges::view::drop(1) | ranges::view::take(5);
    std::cout << comp_view_inf << '\n';

    // transform back
    auto orig_inf = comp_view_inf | ranges::view::reverse | ranges::view::transform(complement);
    std::cout << orig_inf << '\n';

    return 0;
}

Task 2: Translation

Template code (click to expand)

#include <iostream>
#include <range/v3/all.hpp>
#include <string>


char const translation_table[4][4][4] =
{
    { // a??
        { 'K', 'N', 'K', 'N' }, // aa?
        { 'T', 'T', 'T', 'T' }, // ac?
        { 'R', 'S', 'R', 'S' }, // ag?
        { 'I', 'I', 'M', 'I' }  // au?
    }, { // c??
        { 'Q', 'H', 'Q', 'H' }, // ca?
        { 'P', 'P', 'P', 'P' }, // cc?
        { 'R', 'R', 'R', 'R' }, // cg?
        { 'L', 'L', 'L', 'L' }  // cu?
    }, { // g??
        { 'E', 'D', 'E', 'D' }, // ga?
        { 'A', 'A', 'A', 'A' }, // gc?
        { 'G', 'G', 'G', 'G' }, // gg?
        { 'V', 'V', 'V', 'V' }  // gu?
    }, { // u??
        { '*', 'Y', '*', 'Y' }, // ua?
        { 'S', 'S', 'S', 'S' }, // uc?
        { '*', 'C', 'W', 'C' }, // ug?
        { 'L', 'F', 'L', 'F' }  // uu?
    }
};

uint8_t ord_value(char c)
{
    switch (c)
    {
        case 'c': case 'C': return 1;
        case 'g': case 'G': return 2;
        case 't': case 'T': case 'u': case 'U': return 3;
        default: return 0;
    }
}

int main()
{
    std::string s{"ACGTTTATGTTTACGT"};

    auto v = //?

    std::cout << v << '\n';
    return 0;
}
Solution (click to expand)

#include <iostream>
#include <range/v3/all.hpp>
#include <string>


char const translation_table[4][4][4] =
{
    { // a??
        { 'K', 'N', 'K', 'N' }, // aa?
        { 'T', 'T', 'T', 'T' }, // ac?
        { 'R', 'S', 'R', 'S' }, // ag?
        { 'I', 'I', 'M', 'I' }  // au?
    }, { // c??
        { 'Q', 'H', 'Q', 'H' }, // ca?
        { 'P', 'P', 'P', 'P' }, // cc?
        { 'R', 'R', 'R', 'R' }, // cg?
        { 'L', 'L', 'L', 'L' }  // cu?
    }, { // g??
        { 'E', 'D', 'E', 'D' }, // ga?
        { 'A', 'A', 'A', 'A' }, // gc?
        { 'G', 'G', 'G', 'G' }, // gg?
        { 'V', 'V', 'V', 'V' }  // gu?
    }, { // u??
        { '*', 'Y', '*', 'Y' }, // ua?
        { 'S', 'S', 'S', 'S' }, // uc?
        { '*', 'C', 'W', 'C' }, // ug?
        { 'L', 'F', 'L', 'F' }  // uu?
    }
};

uint8_t ord_value(char c)
{
    switch (c)
    {
        case 'c': case 'C': return 1;
        case 'g': case 'G': return 2;
        case 't': case 'T': case 'u': case 'U': return 3;
        default: return 0;
    }
}

int main()
{
    std::string s{"ACGTTTATGTTTACGT"};

    auto v = s | ranges::view::transform(ord_value)
               | ranges::view::chunk(3)
               | ranges::view::transform([] (auto chunk_view)
    {
        if (chunk_view.size() < 3)
            return 'X';
        else
            return translation_table[chunk_view[0]][chunk_view[1]][chunk_view[2]];
    });

    std::cout << v << '\n';
    return 0;
}
Solution 6-frame translation in view-of-views (click to expand)

#include <iostream>
#include <range/v3/all.hpp>
#include <string>

char const translation_table[4][4][4] =
{
    { // a??
        { 'K', 'N', 'K', 'N' }, // aa?
        { 'T', 'T', 'T', 'T' }, // ac?
        { 'R', 'S', 'R', 'S' }, // ag?
        { 'I', 'I', 'M', 'I' }  // au?
    }, { // c??
        { 'Q', 'H', 'Q', 'H' }, // ca?
        { 'P', 'P', 'P', 'P' }, // cc?
        { 'R', 'R', 'R', 'R' }, // cg?
        { 'L', 'L', 'L', 'L' }  // cu?
    }, { // g??
        { 'E', 'D', 'E', 'D' }, // ga?
        { 'A', 'A', 'A', 'A' }, // gc?
        { 'G', 'G', 'G', 'G' }, // gg?
        { 'V', 'V', 'V', 'V' }  // gu?
    }, { // u??
        { '*', 'Y', '*', 'Y' }, // ua?
        { 'S', 'S', 'S', 'S' }, // uc?
        { '*', 'C', 'W', 'C' }, // ug?
        { 'L', 'F', 'L', 'F' }  // uu?
    }
};

uint8_t ord_value(char c)
{
    switch (c)
    {
        case 'c': case 'C': return 1;
        case 'g': case 'G': return 2;
        case 't': case 'T': case 'u': case 'U': return 3;
        default: return 0;
    }
}

int main()
{
    std::string s{"ACGTTTATGTTTACGT"};

    auto complement = [] (auto c)
    {
        switch (c)
        {
            case 'a': return 't';
            case 'A': return 'T';
            case 'c': return 'g';
            case 'C': return 'G';
            case 'g': return 'c';
            case 'G': return 'C';
            case 't': return 'a';
            case 'T': return 'A';
            default: return 'N';
        }
    };

    auto translate = [] (auto chunk_view)
    {
        if (chunk_view.size() < 3)
            return 'X';
        else
            return translation_table[chunk_view[0]][chunk_view[1]][chunk_view[2]];
    };

    // since the views normally resolve to different types, we need to cast to
    // any_random_access_view so that they are compatible
    // we also wrap every view in a view-of-view so that we can easily concatenate
    // without flattening later on
    ranges::any_random_access_view<ranges::any_random_access_view<char>> f[3];
    for (unsigned i : { 0, 1, 2 })
        f[i] = ranges::view::single(s | ranges::view::drop(i)
                                      | ranges::view::transform(ord_value)
                                      | ranges::view::chunk(3)
                                      | ranges::view::transform(translate));

    ranges::any_random_access_view<ranges::any_random_access_view<char>> r[3];
    for (unsigned i : { 0, 1, 2 })
        r[i] = ranges::view::single(s | ranges::view::reverse
                                      | ranges::view::transform(complement)
                                      | ranges::view::drop(i)
                                      | ranges::view::transform(ord_value)
                                      | ranges::view::chunk(3)
                                      | ranges::view::transform(translate));

    auto combined = ranges::view::concat(f[0], f[1], f[2], r[0], r[1], r[2]);
    std::cout << combined << '\n';

    // change string beneath view
    s = "ACTTAGCGGC";

    // print updated view
    std::cout << combined << '\n';
    return 0;
}

The last code generates:

[[T,F,M,F,T,X],[R,L,C,L,R],[V,Y,V,Y,X],[T,*,T,*,T,X],[R,K,H,K,R],[V,N,I,N,X]]
[[T,*,R,H,T,X],[L,S,G,I,R],[L,A,A,Y,X],[T,*,A,A,K,X],[R,K,P,L,S],[V,S,R,*,X]]
Clone this wiki locally