Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/src/t8_helper_functions/t8_vector_algorithms.hxx
901 views
/*
  This file is part of t8code.
  t8code is a C library to manage a collection (a forest) of multiple
  connected adaptive space-trees of general element classes in parallel.

  Copyright (C) 2025 the developers

  t8code is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  t8code is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with t8code; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

/**
 * \file t8_vector_algorithms.hxx
 *
 * Contains specialized algorithms on std::vector
 *
 */

#ifndef T8_VECTOR_ALGORITHMS
#define T8_VECTOR_ALGORITHMS

#include <algorithm>
#include <functional>
#include <numeric>
#include <iterator>
#include <ranges>
#include <t8.h>

/**
 * Compute the offsets of categories of elements in a sorted iterable range given by \a begin and \a end, such as a std::vector.
 * The value type of the iterator should be comparable with <.
 * The categories are defined by the std::function \a category_func passed as input argument.
 * This is a re-implementation of sc_array_split.
 *
 * \tparam TIterator            An input iterator type
 * \tparam TSentinel            A sentinel type for the iterator
 * \tparam TContainer           A container type that holds the offsets. It should be a contiguous container
 * \tparam TCategory            The type of the category. It should be an unsigned integral type
 * \tparam Args                 The type of the arguments passed to the category_func
 *
 * \param[in] begin             An iterator pointing to the first element of the range
 * \param[in] end               An iterator pointing to the last element of the range
 * \param[in, out] offsets      A Container holding num_categories + 1 elements. Will hold indices
 *                              j of the range \a begin and \a end that contain objects of category k, such that offsets[k] <0 j < offset[k+1]
 *                              If there are no elements of category k then offsets[k] = offsets[k +1]
 * \param[in] category_func     A function that takes an element of the value type of the iterators \a begin / \a end and
 *                              returns the category of the element. A category should be in [0, num_categories)
 * \param[in] args              A parameter pack of arguments passed to the category_func
 */
template <std::input_iterator TIterator, std::sentinel_for<TIterator> TSentinel,
          std::ranges::contiguous_range TContainer, std::unsigned_integral TCategory, typename... Args>
constexpr void
vector_split (
  const TIterator begin, const TSentinel end, TContainer &offsets,
  std::function<TCategory (const typename std::iterator_traits<TIterator>::value_type, Args...)> &&category_func,
  Args... args)
{
  T8_ASSERT (std::is_sorted (begin, end));
  T8_ASSERT (begin != end);
  T8_ASSERT (!offsets.empty ());
  const TCategory count = std::distance (begin, end);
  const TCategory num_categories = offsets.size () - 1;
  /* Initialize everything with count, except for the first value. */
  std::fill (std::begin (offsets), std::end (offsets), count);
  /* The first offset is set to zero */
  offsets[0] = 0;

  if (count == 0 || num_categories <= 1) {
    return;
  }

  /* We search between low and high for the next category */
  TCategory low = 0;
  TCategory high = count;
  TCategory step = 1;
  while (step < num_categories) {
    // Using binary search to find the next category boundary
    const TCategory guess = std::midpoint (low, high);
    const TCategory category = category_func (*(begin + guess), args...);
    T8_ASSERT (0 <= category && category <= num_categories);

    if (category < step) {
      // If the category is smaller than the current step, adjust low
      low = guess + 1;
    }
    else {
      // Fill offsets for all categories between step and category
      std::fill (std::begin (offsets) + step, std::begin (offsets) + category + 1, guess);
      // Minimize the high value to narrow the search space
      high = guess;
    }

    // Advance step and update high when low equals high
    while (low == high) {
      ++step;
      if (step == num_categories) {
        return;
      }
      high = offsets[step];
    }
  }
}

#endif /* T8_VECTOR_ALGORITHMS */