text: // -----------------------------------------------------------------------------------------------------
  // Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
  // Copyright (c) 2016-2021, 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
  // -----------------------------------------------------------------------------------------------------
  
  /*!\file
   * \brief Provides seqan3::detail::coordinate_matrix.
   * \author Rene Rahn <rene.rahn AT fu-berlin.de>
   */
  
  #pragma once
  
  #include <seqan3/std/concepts>
  #include <seqan3/std/ranges>
  
  #include <seqan3/alignment/matrix/detail/matrix_coordinate.hpp>
  #include <seqan3/core/detail/template_inspection.hpp>
  #include <seqan3/utility/container/aligned_allocator.hpp>
  #include <seqan3/utility/simd/algorithm.hpp>
  #include <seqan3/utility/simd/concept.hpp>
  #include <seqan3/utility/simd/simd_traits.hpp>
  #include <seqan3/utility/simd/views/iota_simd.hpp>
  #include <seqan3/utility/type_traits/lazy_conditional.hpp>
  
  namespace seqan3::detail
  {
  
  //------------------------------------------------------------------------------
  // coordinate_matrix
  //------------------------------------------------------------------------------
  
  /*!\brief A matrix over coordinates.
   * \ingroup alignment_matrix
   * \implements std::ranges::forward_range
   *
   * \tparam index_t The underlying matrix index type; must mode std::integral or seqan3::simd::simd_index.
   *
   * \details
   *
   * This matrix emulates a two-dimensional index, such that each cell inside of the alignment matrix can be located
   * through a unique coordinate (=index). In the alignment algorithm this matrix is paired with the alignment matrix
   * to form an indexed alignment matrix.
   *
   * This matrix is cheap as it stores only the dimensions of the matrix and does not allocate any memory for the
   * coordinates. It uses the seqan3::detail::matrix_coordinate_iterator to iterate
   * over the columns of this virtual matrix. When the seqan3::detail::coordinate_matrix::matrix_coordinate_iterator
   * is dereferenced it returns an on-the-fly constructed range
   * ([prvalue](https://en.cppreference.com/w/cpp/language/value_category))
   * representing the current indexed column. The reference type of this range is seqan3::detail::matrix_coordinate.
   *
   * ### Simd mode
   *
   * If the `index_t` is a simd vector the matrix implements a vectorised coordinate matrix instead. In this case the
   * matrix uses a seqan3::detail::coordinate_matrix::matrix_coordinate_simd_iterator to iterate over the vectorised
   * matrix. When the seqan3::detail::coordinate_matrix::matrix_coordinate_iterator is dereferenced it returns an
   * on-the-fly constructed range ([prvalue](https://en.cppreference.com/w/cpp/language/value_category)) representing the
   * current simd coordinate column. The reference type of this range is seqan3::detail::simd_matrix_coordinate.
   */
  
  template <typename index_t>
  //!\cond
      requires (std::integral<index_t> || simd_index<index_t>)
  //!\endcond
  class coordinate_matrix
  {
  private:
      /*!\brief A function object that converts a column index and a row index to a seqan3::detail::matrix_coordinate.
       * \details
       *
       * This helper function object is used for the seqan3::detail::coordinate_matrix::coordinate_matrix_iterator and
       * seqan3::detail::coordinate_matrix::coordinate_matrix_simd_iterator to convert the column and row index into a
       * seqan3::detail::matrix_index.
       */
      struct convert_to_matrix_coordinate
      {
          //!\brief The index of the represented column.
          index_t column_index{};
  
          /*!\brief The conversion operator
           *
           * \param[in] row_index The index of the represented row.
           *
           * \returns seqan3::detail::matrix_index for the current column and row index.
           */
          auto operator()(index_t const row_index) noexcept
          {
              return matrix_index<index_t>{row_index_type{row_index}, column_index_type{column_index}};
          }
      };
  
      //!\brief Type alias for the scalar type defined by the seqan3::simd::simd_traits type.
      template <typename simd_index_t>
      using lazy_scalar_type_t = typename simd_traits<simd_index_t>::scalar_type;
  
      //!\brief The internal size type which depends on `index_t` being a simd vector or a scalar type.
      using size_type = lazy_conditional_t<simd_concept<index_t>, lazy<lazy_scalar_type_t, index_t>, index_t>;
  
      // The coordinate matrix iterator.
      class iterator;
  
      //!\brief The number of columns (corresponds to the size of one row).
      size_type column_count{};
      //!\brief The number od rows (corresponds to the size of one column).
      size_type row_count{};
  
  public:
      /*!\name Constructors, destructor and assignment
       * \{
       */
      coordinate_matrix() = default; //!< Defaulted.
      coordinate_matrix(coordinate_matrix const &) = default; //!< Defaulted.
      coordinate_matrix(coordinate_matrix &&) = default; //!< Defaulted.
      coordinate_matrix & operator=(coordinate_matrix const &) = default; //!< Defaulted.
      coordinate_matrix & operator=(coordinate_matrix &&) = default; //!< Defaulted.
      ~coordinate_matrix() = default; //!< Defaulted.
  
      /*!\brief Resets the coordinate matrix with the given end column index and end row index representing the new
       *        dimensions of the matrix.
       *\tparam column_index_t The size type for the column count; must model std:integral.
       *\tparam row_index_t The size type for the row count; must model std:integral.
       *
       * \param[in] column_count \copybrief seqan3::detail::coordinate_matrix::column_count
       * \param[in] row_count \copybrief seqan3::detail::coordinate_matrix::row_count
       *
       * \details
       *
       * ### Complexity
       *
       * Constant
       *
       * ### Exception
       *
       * noexcept
       */
      template <std::integral column_index_t, std::integral row_index_t>
      void resize(column_index_type<column_index_t> const column_count,
                  row_index_type<row_index_t> const row_count) noexcept
      {
          this->column_count = column_count.get();
          this->row_count = row_count.get();
      }
      //!\}
  
      /*!\name Iterators
       * \{
       */
      //!\brief Returns the iterator pointing to the first column of the matrix.
      iterator begin() const noexcept
      {
          return iterator{size_type{}, row_count};
      }
  
      //!\brief Returns the iterator pointing to the end column of the matrix.
      iterator end() const noexcept
      {
          return iterator{column_count, row_count};
      }
      //!\}
  };
  
  //------------------------------------------------------------------------------
  // iterator
  //------------------------------------------------------------------------------
  
  /*!\brief The iterator for the seqan3::detail::coordinate_matrix.
   * \implements std::forward_iterator
   *
   * \details
   *
   * Iterates over the columns of the underlying seqan3::detail::coordinate_matrix. The iterator returns a transformed
   * std::ranges::views::iota view over the row indices. In the transformation every row index is converted to a
   * seqan3::detail::matrix_coordinate using the seqan3::detail::convert_to_matrix_coordinate function object.
   */
  template <typename index_t>
  //!\cond
      requires (std::integral<index_t> || simd_index<index_t>)
  //!\endcond
  class coordinate_matrix<index_t>::iterator
  {
  private:
      //!\brief The iota view type which depends on the index type.
      using iota_view_t = lazy_conditional_t<simd_index<index_t>,
                                             lazy<iota_simd_view, index_t>,
                                             decltype(std::views::iota(size_type{}, size_type{}))>;
      //!\brief The currently represented column index.
      index_t column_id{0};
      //!\brief The number of rows (corresponds to the size of a column).
      size_type row_count{0};
  
  public:
      /*!\name Associated types
       * \{
       */
      //!\brief The value type.
      using value_type = decltype(std::declval<iota_view_t>()
                                | std::views::transform(convert_to_matrix_coordinate{index_t{}/*column_id*/}));
      //!\brief The reference type.
      using reference = value_type;
      //!\brief The pointer type.
      using pointer = void;
      //!\brief The difference type.
      using difference_type = std::ptrdiff_t;
      //!\brief The iterator category.
      using iterator_category = std::forward_iterator_tag;
      //!\}
  
      /*!\name Constructor, assignment and destructor
       * \{
       */
      iterator() = default; //!< Defaulted.
      iterator(iterator const &) = default; //!< Defaulted.
      iterator(iterator &&) = default; //!< Defaulted.
      iterator & operator=(iterator const &) = default; //!< Defaulted.
      iterator & operator=(iterator &&) = default; //!< Defaulted.
      ~iterator() = default; //!< Defaulted.
  
      /*!\brief Constructs and initialises the iterator with the current column index and the row index marking the end
       *        of the rows (size of one column).
       *
       * \param[in] column_id \copybrief seqan3::detail::coordinate_matrix::iterator::column_id
       * \param[in] row_count \copybrief seqan3::detail::coordinate_matrix::iterator::row_count
       */
      explicit iterator(size_type column_id, size_type row_count) noexcept :
          row_count{std::move(row_count)}
      {
          if constexpr (simd_index<index_t>)
              this->column_id = simd::fill<index_t>(std::move(column_id));
          else
              this->column_id = std::move(column_id);
      }
      //!\}
  
      /*!\name Element access
       * \{
       */
  
      //!\brief Access the pointed-to matrix coordinate column.
      auto operator*() const
      {
          if constexpr (simd_index<index_t>)
          {
              return views::iota_simd<index_t>(size_type{}, row_count)
                   | std::views::transform(convert_to_matrix_coordinate{column_id});
          }
          else
          {
              return std::views::iota(size_type{}, row_count)
                   | std::views::transform(convert_to_matrix_coordinate{column_id});
          }
      }
      //!\}
  
      /*!\name Arithmetic operators
       * \{
       */
  
      //!\brief Increments the iterator to the next column.
      iterator & operator++()
      {
          // clang: pre-increment of a SIMD vector does not work
          if constexpr (simd_index<index_t>)
              column_id = column_id + simd::fill<index_t>(1);
          else
              ++column_id;
  
          return *this;
      }
  
      //!\brief Increments the iterator to the next column and returns the iterator pointing to the previous column.
      iterator operator++(int)
      {
          iterator tmp{*this};
          ++(*this);
          return tmp;
      }
      //!\}
  
      /*!\name Comparison operators
       * \{
       */
  
      //!\brief Tests whether `lhs == rhs`.
      friend bool operator==(iterator const & lhs, iterator const & rhs)
      {
          if constexpr (simd_index<index_t>)
              return lhs.column_id[0] == rhs.column_id[0];
          else
              return lhs.column_id == rhs.column_id;
      }
  
      //!\brief Tests whether `lhs != rhs`.
      friend bool operator!=(iterator const & lhs, iterator const & rhs)
      {
          return !(lhs == rhs);
      }
      //!\}
  };
  } // namespace seqan3::detail
  