234 lines
8.7 KiB
C++
234 lines
8.7 KiB
C++
// Copyright 2010-2021 Google LLC
|
|
// 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 "ortools/util/range_query_function.h"
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "ortools/base/integral_types.h"
|
|
#include "ortools/base/logging.h"
|
|
#include "ortools/base/macros.h"
|
|
#include "ortools/util/range_minimum_query.h"
|
|
|
|
namespace operations_research {
|
|
namespace {
|
|
// This implementation basically calls the function as many times as needed for
|
|
// each query.
|
|
class LinearRangeIntToIntFunction : public RangeIntToIntFunction {
|
|
public:
|
|
explicit LinearRangeIntToIntFunction(
|
|
std::function<int64_t(int64_t)> base_function)
|
|
: base_function_(std::move(base_function)) {}
|
|
|
|
int64_t Query(int64_t argument) const override {
|
|
return base_function_(argument);
|
|
}
|
|
|
|
int64_t RangeMin(int64_t range_begin, int64_t range_end) const override {
|
|
DCHECK_LT(range_begin, range_end);
|
|
int64_t min_val = kint64max;
|
|
for (int64_t i = range_begin; i < range_end; ++i) {
|
|
min_val = std::min(min_val, base_function_(i));
|
|
}
|
|
return min_val;
|
|
}
|
|
|
|
int64_t RangeMax(int64_t range_begin, int64_t range_end) const override {
|
|
DCHECK_LT(range_begin, range_end);
|
|
int64_t max_val = kint64min;
|
|
for (int64_t i = range_begin; i < range_end; ++i) {
|
|
max_val = std::max(max_val, base_function_(i));
|
|
}
|
|
return max_val;
|
|
}
|
|
|
|
int64_t RangeFirstInsideInterval(int64_t range_begin, int64_t range_end,
|
|
int64_t interval_begin,
|
|
int64_t interval_end) const override {
|
|
// domain_start_ <= range_begin < range_end <= domain_start_+array().size()
|
|
DCHECK_LT(range_begin, range_end);
|
|
DCHECK_LT(interval_begin, interval_end);
|
|
int64_t i = range_begin;
|
|
for (; i < range_end; ++i) {
|
|
const int64_t value = base_function_(i);
|
|
if (interval_begin <= value && value < interval_end) break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int64_t RangeLastInsideInterval(int64_t range_begin, int64_t range_end,
|
|
int64_t interval_begin,
|
|
int64_t interval_end) const override {
|
|
// domain_start_ <= range_begin < range_end <= domain_start_+array().size()
|
|
DCHECK_NE(range_begin, kint64max);
|
|
DCHECK_LT(range_begin, range_end);
|
|
DCHECK_LT(interval_begin, interval_end);
|
|
int64_t i = range_end - 1;
|
|
for (; i >= range_begin; --i) {
|
|
const int64_t value = base_function_(i);
|
|
if (interval_begin <= value && value < interval_end) break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
private:
|
|
std::function<int64_t(int64_t)> base_function_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(LinearRangeIntToIntFunction);
|
|
};
|
|
|
|
std::vector<int64_t> FunctionToVector(const std::function<int64_t(int64_t)>& f,
|
|
int64_t domain_start,
|
|
int64_t domain_end) {
|
|
CHECK_LT(domain_start, domain_end);
|
|
std::vector<int64_t> output(domain_end - domain_start, 0);
|
|
for (int64_t i = 0; i < domain_end - domain_start; ++i) {
|
|
output[i] = f(i + domain_start);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
// This implementation caches the underlying function and improves on the
|
|
// non-cached version in two ways:
|
|
// 1. It caches the values returned by the function.
|
|
// 2. It creates a data structure for quick answer to range queries.
|
|
class CachedRangeIntToIntFunction : public RangeIntToIntFunction {
|
|
public:
|
|
CachedRangeIntToIntFunction(
|
|
const std::function<int64_t(int64_t)>& base_function,
|
|
int64_t domain_start, int64_t domain_end)
|
|
: domain_start_(domain_start),
|
|
rmq_min_(FunctionToVector(base_function, domain_start, domain_end)),
|
|
rmq_max_(rmq_min_.array()) {
|
|
CHECK_LT(domain_start, domain_end);
|
|
}
|
|
|
|
int64_t Query(int64_t argument) const override {
|
|
DCHECK_LE(domain_start_, argument);
|
|
DCHECK_LE(argument, domain_start_ + static_cast<int64_t>(array().size()));
|
|
return array()[argument - domain_start_];
|
|
}
|
|
int64_t RangeMin(int64_t from, int64_t to) const override {
|
|
DCHECK_LE(domain_start_, from);
|
|
DCHECK_LT(from, to);
|
|
DCHECK_LE(to, domain_start_ + static_cast<int64_t>(array().size()));
|
|
return rmq_min_.GetMinimumFromRange(from - domain_start_,
|
|
to - domain_start_);
|
|
}
|
|
int64_t RangeMax(int64_t from, int64_t to) const override {
|
|
DCHECK_LE(domain_start_, from);
|
|
DCHECK_LT(from, to);
|
|
DCHECK_LE(to, domain_start_ + static_cast<int64_t>(array().size()));
|
|
return rmq_max_.GetMinimumFromRange(from - domain_start_,
|
|
to - domain_start_);
|
|
}
|
|
int64_t RangeFirstInsideInterval(int64_t range_begin, int64_t range_end,
|
|
int64_t interval_begin,
|
|
int64_t interval_end) const override {
|
|
// domain_start_ <= range_begin < range_end <= domain_start_+array().size()
|
|
DCHECK_LE(domain_start_, range_begin);
|
|
DCHECK_LT(range_begin, range_end);
|
|
DCHECK_LE(range_end, domain_start_ + array().size());
|
|
DCHECK_LT(interval_begin, interval_end);
|
|
int64_t i = range_begin;
|
|
for (; i < range_end; ++i) {
|
|
const int64_t value = array()[i - domain_start_];
|
|
if (interval_begin <= value && value < interval_end) break;
|
|
}
|
|
return i;
|
|
}
|
|
int64_t RangeLastInsideInterval(int64_t range_begin, int64_t range_end,
|
|
int64_t interval_begin,
|
|
int64_t interval_end) const override {
|
|
// domain_start_ <= range_begin < range_end <= domain_start_+array().size()
|
|
DCHECK_LE(domain_start_, range_begin);
|
|
DCHECK_LT(range_begin, range_end);
|
|
DCHECK_LE(range_end, domain_start_ + array().size());
|
|
DCHECK_LT(interval_begin, interval_end);
|
|
int64_t i = range_end - 1;
|
|
for (; i >= range_begin; --i) {
|
|
const int64_t value = array()[i - domain_start_];
|
|
if (interval_begin <= value && value < interval_end) break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
private:
|
|
const std::vector<int64_t>& array() const { return rmq_min_.array(); }
|
|
|
|
const int64_t domain_start_;
|
|
const RangeMinimumQuery<int64_t, std::less<int64_t>> rmq_min_;
|
|
const RangeMinimumQuery<int64_t, std::greater<int64_t>> rmq_max_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CachedRangeIntToIntFunction);
|
|
};
|
|
|
|
class CachedRangeMinMaxIndexFunction : public RangeMinMaxIndexFunction {
|
|
public:
|
|
CachedRangeMinMaxIndexFunction(const std::function<int64_t(int64_t)>& f,
|
|
int64_t domain_start, int64_t domain_end)
|
|
: domain_start_(domain_start),
|
|
domain_end_(domain_end),
|
|
index_rmq_min_(FunctionToVector(f, domain_start, domain_end)),
|
|
index_rmq_max_(index_rmq_min_.array()) {
|
|
CHECK_LT(domain_start, domain_end);
|
|
}
|
|
|
|
inline int64_t RangeMinArgument(int64_t from, int64_t to) const override {
|
|
DCHECK_LE(domain_start_, from);
|
|
DCHECK_LT(from, to);
|
|
DCHECK_LE(to, domain_end_);
|
|
return index_rmq_min_.GetMinimumIndexFromRange(from - domain_start_,
|
|
to - domain_start_) +
|
|
domain_start_;
|
|
}
|
|
inline int64_t RangeMaxArgument(int64_t from, int64_t to) const override {
|
|
DCHECK_LE(domain_start_, from);
|
|
DCHECK_LT(from, to);
|
|
DCHECK_LE(to, domain_end_);
|
|
return index_rmq_max_.GetMinimumIndexFromRange(from - domain_start_,
|
|
to - domain_start_) +
|
|
domain_start_;
|
|
}
|
|
|
|
private:
|
|
const int64_t domain_start_;
|
|
const int64_t domain_end_;
|
|
const RangeMinimumIndexQuery<int64_t, std::less<int64_t>> index_rmq_min_;
|
|
const RangeMinimumIndexQuery<int64_t, std::greater<int64_t>> index_rmq_max_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CachedRangeMinMaxIndexFunction);
|
|
};
|
|
} // namespace
|
|
|
|
RangeIntToIntFunction* MakeBareIntToIntFunction(
|
|
std::function<int64_t(int64_t)> f) {
|
|
return new LinearRangeIntToIntFunction(std::move(f));
|
|
}
|
|
|
|
RangeIntToIntFunction* MakeCachedIntToIntFunction(
|
|
const std::function<int64_t(int64_t)>& f, int64_t domain_start,
|
|
int64_t domain_end) {
|
|
return new CachedRangeIntToIntFunction(f, domain_start, domain_end);
|
|
}
|
|
|
|
RangeMinMaxIndexFunction* MakeCachedRangeMinMaxIndexFunction(
|
|
const std::function<int64_t(int64_t)>& f, int64_t domain_start,
|
|
int64_t domain_end) {
|
|
return new CachedRangeMinMaxIndexFunction(f, domain_start, domain_end);
|
|
}
|
|
} // namespace operations_research
|