/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/trace_processor/containers/row_map.h"
#include <unordered_set>

#include "src/trace_processor/containers/row_map_algorithms.h"

namespace perfetto {
namespace trace_processor {

namespace {

using Range = RowMap::Range;
using OutputIndex = RowMap::OutputIndex;
using Variant = std::variant<Range, BitVector, std::vector<OutputIndex>>;

RowMap Select(Range range, Range selector) {
  PERFETTO_DCHECK(selector.start <= selector.end);
  PERFETTO_DCHECK(selector.end <= range.size());

  return RowMap(range.start + selector.start, range.start + selector.end);
}

RowMap Select(Range range, const BitVector& selector) {
  PERFETTO_DCHECK(selector.size() <= range.size());

  // If |start| == 0 and |selector.size()| <= |end - start| (which is a
  // precondition for this function), the BitVector we generate is going to be
  // exactly |selector|.
  //
  // This is a fast path for the common situation where, post-filtering,
  // SelectRows is called on all the table RowMaps with a BitVector. The self
  // RowMap will always be a range so we expect this case to be hit at least
  // once every filter operation.
  if (range.start == 0u)
    return RowMap(selector.Copy());

  // We only need to resize to |start| + |selector.size()| as we know any rows
  // not covered by |selector| are going to be removed below.
  BitVector bv(range.start, false);
  bv.Resize(range.start + selector.size(), true);

  bv.UpdateSetBits(selector);
  return RowMap(std::move(bv));
}

RowMap Select(Range range, const std::vector<OutputIndex>& selector) {
  std::vector<uint32_t> iv(selector.size());
  for (uint32_t i = 0; i < selector.size(); ++i) {
    PERFETTO_DCHECK(selector[i] < range.size());
    iv[i] = selector[i] + range.start;
  }
  return RowMap(std::move(iv));
}

RowMap Select(const BitVector& bv, Range selector) {
  PERFETTO_DCHECK(selector.end <= bv.CountSetBits());

  // If we're simply selecting every element in the bitvector, just
  // return a copy of the BitVector without iterating.
  BitVector ret = bv.Copy();
  if (selector.start == 0 && selector.end == bv.CountSetBits()) {
    return RowMap(std::move(ret));
  }

  for (auto it = ret.IterateSetBits(); it; it.Next()) {
    auto set_idx = it.ordinal();
    if (set_idx < selector.start || set_idx >= selector.end)
      it.Clear();
  }
  return RowMap(std::move(ret));
}

RowMap Select(const BitVector& bv, const BitVector& selector) {
  BitVector ret = bv.Copy();
  ret.UpdateSetBits(selector);
  return RowMap(std::move(ret));
}

RowMap Select(const BitVector& bv, const std::vector<uint32_t>& selector) {
  // The value of this constant was found by considering the benchmarks
  // |BM_SelectBvWithIvByConvertToIv| and |BM_SelectBvWithIvByIndexOfNthSet|.
  //
  // We use this to find the ratio between |bv.CountSetBits()| and
  // |selector.size()| where |SelectBvWithIvByIndexOfNthSet| was found to be
  // faster than |SelectBvWithIvByConvertToIv|.
  //
  // Note: as of writing this, the benchmarks do not take into account the fill
  // ratio of the BitVector; they assume 50% rate which almost never happens in
  // practice. In the future, we could also take this into account (by
  // considering the ratio between bv.size() and bv.CountSetBits()) but this
  // causes an explosion in the state space for the benchmark so we're not
  // considering this today.
  //
  // The current value of the constant was picked by running these benchmarks on
  // a E5-2690 v4 and finding the crossover point using a spreadsheet.
  constexpr uint32_t kIndexOfSetBitToSelectorRatio = 4;

  // If the selector is larger than a threshold, it's more efficient to convert
  // the entire BitVector to an index vector and use SelectIvWithIv instead.
  if (bv.CountSetBits() / kIndexOfSetBitToSelectorRatio < selector.size()) {
    return RowMap(
        row_map_algorithms::SelectBvWithIvByConvertToIv(bv, selector));
  }
  return RowMap(
      row_map_algorithms::SelectBvWithIvByIndexOfNthSet(bv, selector));
}

RowMap Select(const std::vector<uint32_t>& iv, Range selector) {
  PERFETTO_DCHECK(selector.end <= iv.size());

  std::vector<uint32_t> ret(selector.size());
  for (uint32_t i = selector.start; i < selector.end; ++i) {
    ret[i - selector.start] = iv[i];
  }
  return RowMap(std::move(ret));
}

RowMap Select(const std::vector<uint32_t>& iv, const BitVector& selector) {
  PERFETTO_DCHECK(selector.size() <= iv.size());

  std::vector<uint32_t> copy = iv;
  copy.resize(selector.size());

  uint32_t idx = 0;
  auto it = std::remove_if(
      copy.begin(), copy.end(),
      [&idx, &selector](uint32_t) { return !selector.IsSet(idx++); });
  copy.erase(it, copy.end());
  return RowMap(std::move(copy));
}

RowMap Select(const std::vector<uint32_t>& iv,
              const std::vector<uint32_t>& selector) {
  return RowMap(row_map_algorithms::SelectIvWithIv(iv, selector));
}

Variant IntersectInternal(BitVector& first, const BitVector& second) {
  first.And(second);
  return std::move(first);
}

Variant IntersectInternal(Range first, Range second) {
  // If both RowMaps have ranges, we can just take the smallest intersection
  // of them as the new RowMap.
  // We have this as an explicit fast path as this is very common for
  // constraints on id and sorted columns to satisfy this condition.
  OutputIndex start = std::max(first.start, second.start);
  OutputIndex end = std::max(start, std::min(first.end, second.end));
  return Range{start, end};
}

Variant IntersectInternal(std::vector<OutputIndex>& first,
                          const std::vector<OutputIndex>& second) {
  std::unordered_set<OutputIndex> lookup(second.begin(), second.end());
  first.erase(std::remove_if(first.begin(), first.end(),
                             [lookup](OutputIndex ind) {
                               return lookup.find(ind) == lookup.end();
                             }),
              first.end());
  return std::move(first);
}

Variant IntersectInternal(Range range, const BitVector& bv) {
  return bv.IntersectRange(range.start, range.end);
}

Variant IntersectInternal(BitVector& bv, Range range) {
  return IntersectInternal(range, bv);
}

Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
                          const BitVector& bv) {
  std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
  new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
                               [&bv](uint32_t i) { return !bv.IsSet(i); }),
                new_vec.end());
  return std::move(new_vec);
}

Variant IntersectInternal(const BitVector& bv,
                          const std::vector<OutputIndex>& index_vec) {
  return IntersectInternal(index_vec, bv);
}

Variant IntersectInternal(Range range,
                          const std::vector<OutputIndex>& index_vec) {
  std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end());
  new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(),
                               [range](uint32_t i) {
                                 return i < range.start || i >= range.end;
                               }),
                new_vec.end());
  return std::move(new_vec);
}

Variant IntersectInternal(const std::vector<OutputIndex>& index_vec,
                          Range range) {
  return IntersectInternal(range, index_vec);
}

}  // namespace

RowMap::RowMap() : RowMap(Range()) {}

RowMap::RowMap(uint32_t start, uint32_t end, OptimizeFor optimize_for)
    : data_(Range{start, end}), optimize_for_(optimize_for) {}

RowMap::RowMap(Variant def) : data_(std::move(def)) {}

RowMap::RowMap(Range r) : data_(r) {}

// Creates a RowMap backed by a BitVector.
RowMap::RowMap(BitVector bit_vector) : data_(std::move(bit_vector)) {}

// Creates a RowMap backed by an std::vector<uint32_t>.
RowMap::RowMap(IndexVector vec) : data_(vec) {}

RowMap RowMap::Copy() const {
  if (auto* range = std::get_if<Range>(&data_)) {
    return RowMap(*range);
  }
  if (auto* bv = std::get_if<BitVector>(&data_)) {
    return RowMap(bv->Copy());
  }
  if (auto* vec = std::get_if<IndexVector>(&data_)) {
    return RowMap(*vec);
  }
  NoVariantMatched();
}

OutputIndex RowMap::Max() const {
  if (auto* range = std::get_if<Range>(&data_)) {
    return range->end;
  }
  if (auto* bv = std::get_if<BitVector>(&data_)) {
    return bv->size();
  }
  if (auto* vec = std::get_if<IndexVector>(&data_)) {
    return vec->empty() ? 0 : *std::max_element(vec->begin(), vec->end()) + 1;
  }
  NoVariantMatched();
}

RowMap RowMap::SelectRowsSlow(const RowMap& selector) const {
  return std::visit(
      [](const auto& def, const auto& selector_def) {
        return Select(def, selector_def);
      },
      data_, selector.data_);
}

void RowMap::Intersect(const RowMap& second) {
  data_ = std::visit(
      [](auto& def, auto& selector_def) {
        return IntersectInternal(def, selector_def);
      },
      data_, second.data_);
}

RowMap::Iterator::Iterator(const RowMap* rm) : rm_(rm) {
  if (auto* range = std::get_if<Range>(&rm_->data_)) {
    ordinal_ = range->start;
    return;
  }
  if (auto* bv = std::get_if<BitVector>(&rm_->data_)) {
    set_bits_it_.reset(new BitVector::SetBitsIterator(bv->IterateSetBits()));
    return;
  }
}
}  // namespace trace_processor
}  // namespace perfetto
