text: // -----------------------------------------------------------------------------------------------------
  // Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
  // Copyright (c) 2016-2020, Knut Reinert & MPI für molekulare Genetik
  // This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
  // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
  // -----------------------------------------------------------------------------------------------------
  
  #include <gtest/gtest.h>
  
  #include <iostream>
  #include <string>
  #include <vector>
  
  #include <seqan3/alphabet/nucleotide/all.hpp>
  #include <seqan3/alphabet/aminoacid/aa27.hpp>
  #include <seqan3/range/concept.hpp>
  #include <seqan3/range/views/char_to.hpp>
  #include <seqan3/range/views/complement.hpp>
  #include <seqan3/range/views/to.hpp>
  #include <seqan3/range/views/translate_join.hpp>
  #include <seqan3/std/ranges>
  
  #include "../iterator_test_template.hpp"
  
  using namespace seqan3;
  
  using iterator_type = decltype(views::translate_join(std::declval<std::vector<std::vector<seqan3::dna4> > &>()).begin());
  
  template <>
  struct iterator_fixture<iterator_type> : public ::testing::Test
  {
      using iterator_tag = std::random_access_iterator_tag;
      static constexpr bool const_iterable = true;
  
      std::vector<std::vector<seqan3::dna4> > vec{"ACGTACGTACGTA"_dna4, "TCGAGAGCTTTAGC"_dna4};
      std::vector<std::vector<aa27> > expected_range{{"TYVR"_aa27}, {"RTYV"_aa27}, {"VRT"_aa27}, {"YVRT"_aa27}, {"TYVR"_aa27}, {"RTY"_aa27},
                                                     {"SRAL"_aa27}, {"REL*"_aa27}, {"ESFS"_aa27}, {"AKAL"_aa27}, {"LKLS"_aa27}, {"*SSR"_aa27}};
      decltype(views::translate_join(vec)) test_range = views::translate_join(vec);
  
      template <typename A, typename B>
      static void expect_eq(A && test_range_value, B && expected_range_value)
      {
          EXPECT_TRUE(std::ranges::equal(test_range_value, expected_range_value));
      }
  };
  
  using test_type = ::testing::Types<iterator_type>;
  INSTANTIATE_TYPED_TEST_SUITE_P(iterator_fixture, iterator_fixture, test_type, );
  
  template <typename T>
  class nucleotide : public ::testing::Test
  {};
  
  // add all alphabets here
  using nucleotide_types = ::testing::Types<dna4, dna5, dna15, rna4, rna5, rna15>;
  
  TYPED_TEST_SUITE(nucleotide, nucleotide_types, );
  
  TYPED_TEST(nucleotide, view_translate)
  {
      std::string const in1{"ACGTACGTACGTA"};
      std::string const in2{"TCGAGAGCTTTAGC"};
      std::vector<std::vector<TypeParam> > vec;
      vec.resize(2);
      vec[0] = in1 | views::char_to<TypeParam> | views::to<std::vector>;
      vec[1] = in2 | views::char_to<TypeParam> | views::to<std::vector>;
  
      std::vector<std::vector<aa27> > cmp1{{"TYVR"_aa27}, {"SRAL"_aa27}};
      std::vector<std::vector<aa27> > cmp2{{"TYVR"_aa27}, {"YVRT"_aa27}, {"SRAL"_aa27}, {"AKAL"_aa27}};
      std::vector<std::vector<aa27> > cmp3{{"TYVR"_aa27}, {"RTYV"_aa27}, {"VRT"_aa27}, {"SRAL"_aa27}, {"REL*"_aa27}, {"ESFS"_aa27}};
      std::vector<std::vector<aa27> > cmp4{{"TYVR"_aa27}, {"RTYV"_aa27}, {"VRT"_aa27}, {"YVRT"_aa27}, {"TYVR"_aa27}, {"RTY"_aa27},
                                           {"SRAL"_aa27}, {"REL*"_aa27}, {"ESFS"_aa27}, {"AKAL"_aa27}, {"LKLS"_aa27}, {"*SSR"_aa27}};
      std::vector<std::vector<aa27> > cmp5{{"TYVR"_aa27}, {"VRT"_aa27}, {"SRAL"_aa27}, {"ESFS"_aa27}};
      std::vector<std::vector<aa27> > cmp6{{"CMHA"_aa27}, {"MHAC"_aa27}, {"SSRN"_aa27}, {"RFRE"_aa27}};
      std::vector<std::vector<aa27> > cmp7{{"CMHA"_aa27}};
  
      // default parameter translation_frames
      auto v1 = vec | views::translate_join;
      // == [[T,Y,V,R],[R,T,Y,V],[V,R,T],[Y,V,R,T],[T,Y,V,R],[R,T,Y],[S,R,A,L],[R,E,L,*],[E,S,F,S],[A,K,A,L],[L,K,L,S],[*,S,S,R]]
      EXPECT_EQ(v1.size(), cmp4.size());
      for (unsigned i = 0; i < v1.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v1[i], cmp4[i])));
  
      // default parameter translation_frames
      auto v2 = vec | views::translate_join();
      // == [[T,Y,V,R],[R,T,Y,V],[V,R,T],[Y,V,R,T],[T,Y,V,R],[R,T,Y],[S,R,A,L],[R,E,L,*],[E,S,F,S],[A,K,A,L],[L,K,L,S],[*,S,S,R]]
      EXPECT_EQ(v2.size(), cmp4.size());
      for (unsigned i = 0; i < v2.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v2[i], cmp4[i])));
  
      // single frame translation
      auto v3 = vec | views::translate_join(translation_frames::FWD_FRAME_0);
      // == [[T,Y,V,R],[S,R,A,L]]
      EXPECT_EQ(v3.size(), cmp1.size());
      for (unsigned i = 0; i < v3.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v3[i], cmp1[i])));
  
      // reverse translation
      auto v4 = vec | views::translate_join(translation_frames::FWD_REV_0);
      // == [[T,Y,V,R],Y,V,R,T],[S,R,A,L],[A,K,A,L]]
      EXPECT_EQ(v4.size(), cmp2.size());
      for (unsigned i = 0; i < v4.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v4[i], cmp2[i])));
  
      // forward frames translation
      auto v5 = vec | views::translate_join(translation_frames::FWD);
      // == [[T,Y,V,R],[R,T,Y,V],[V,R,T],[S,R,A,L],[R,E,L,*],[E,S,F,S]]
      EXPECT_EQ(v5.size(), cmp3.size());
      for (unsigned i = 0; i < v5.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v5[i], cmp3[i])));
  
      // six frame translation
      auto v6 = vec | views::translate_join(translation_frames::SIX_FRAME);
      // == [[T,Y,V,R],[R,T,Y,V],[V,R,T],[Y,V,R,T],[T,Y,V,R],[R,T,Y],[S,R,A,L],[R,E,L,*],[E,S,F,S],[A,K,A,L],[L,K,L,S],[*,S,S,R]]
      EXPECT_EQ(v6.size(), cmp4.size());
      for (unsigned i = 0; i < v6.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v6[i], cmp4[i])));
  
      // user-defined frame combination
      auto v7 = vec | views::translate_join(translation_frames::FWD_FRAME_0 | translation_frames::FWD_FRAME_2);
      // == [[T,Y,V,R],[V,R,T],[S,R,A,L],[E,S,F,S]]
      EXPECT_EQ(v7.size(), cmp5.size());
      for (unsigned i = 0; i < v7.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v7[i], cmp5[i])));
  
      // function syntax
      auto v8 = views::translate_join(vec, translation_frames::FWD_REV_0);
      // == [[T,Y,V,R],[R,T,Y,V],[V,R,T],[Y,V,R,T],[T,Y,V,R],[R,T,Y],[S,R,A,L], [R,E,L,*], [E,S,F,S], [A,K,A,L], [L,K,L,S], [*,S,S,R]]
      EXPECT_EQ(v8.size(), cmp2.size());
      for (unsigned i = 0; i < v8.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v8[i], cmp2[i])));
  
      // combinability
      auto v9 = vec | views::complement | views::translate_join(translation_frames::FWD_REV_0);
      // == [[C,M,H,A],[M,H,A,C],[S,S,R,N],[R,F,R,E]]
      EXPECT_EQ(v9.size(), cmp6.size());
      for (unsigned i = 0; i < v9.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v9[i], cmp6[i])));
  
      // combinability
      auto v10 = vec | views::complement | views::translate_join(translation_frames::FWD_REV_0) | std::views::take(1);
      // == [C,M,H,A]
      EXPECT_EQ(v10.size(), cmp7.size());
      for (unsigned i = 0; i < v10.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v10[i], cmp7[i])));
  
      // combinability and function syntax
      auto v11 = detail::view_translate_join(views::complement(vec), translation_frames::FWD_REV_0);
      // == [[C,M,H,A],[M,H,A,C],[S,S,R,N],[R,F,R,E]]
      EXPECT_EQ(v11.size(), cmp6.size());
      for (unsigned i = 0; i < v11.size(); i++)
          EXPECT_TRUE((std::ranges::equal(v11[i], cmp6[i])));
  }
  
  TYPED_TEST(nucleotide, view_translate_concepts)
  {
      std::string const in1{"ACGTACGTACGTA"};
      std::string const in2{"TCGAGAGCTTTAGC"};
      std::vector<std::vector<TypeParam> > vec;
      vec.resize(2);
      vec[0] = in1 | views::char_to<TypeParam> | views::to<std::vector>;
      vec[1] = in2 | views::char_to<TypeParam> | views::to<std::vector>;
  
      EXPECT_TRUE(std::ranges::forward_range<decltype(vec)>);
      EXPECT_TRUE(std::ranges::random_access_range<decltype(vec)>);
      EXPECT_TRUE(std::ranges::sized_range<decltype(vec)>);
  
      auto v1 = vec | views::translate_join(translation_frames::FWD_REV_0);
  
      EXPECT_TRUE(std::ranges::input_range<decltype(v1)>);
      EXPECT_TRUE(std::ranges::forward_range<decltype(v1)>);
      EXPECT_TRUE(std::ranges::random_access_range<decltype(v1)>);
      EXPECT_TRUE(std::ranges::sized_range<decltype(v1)>);
      EXPECT_TRUE(std::ranges::view<decltype(v1)>);
      EXPECT_TRUE(std::ranges::random_access_range<value_type_t<decltype(v1)>>);
      EXPECT_TRUE(std::ranges::sized_range<value_type_t<decltype(v1)>>);
      EXPECT_TRUE(std::ranges::view<value_type_t<decltype(v1)>>);
      EXPECT_TRUE(std::ranges::random_access_range<reference_t<decltype(v1)>>);
      EXPECT_TRUE(std::ranges::sized_range<reference_t<decltype(v1)>>);
      EXPECT_TRUE(std::ranges::view<reference_t<decltype(v1)>>);
  }
  