OR-Tools  9.2
ids_validator.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <stddef.h>
17
18#include <algorithm>
19#include <cstdint>
20#include <iterator>
21#include <limits>
22#include <string>
23
24#include "absl/container/flat_hash_set.h"
25#include "absl/status/status.h"
26#include "absl/strings/str_cat.h"
27#include "absl/strings/string_view.h"
28#include "absl/types/span.h"
32
33namespace operations_research {
34namespace math_opt {
35
36constexpr double kInf = std::numeric_limits<double>::infinity();
37
38namespace {
39absl::Status CheckSortedIdsSubsetWithIndexOffset(
40 const absl::Span<const int64_t> ids,
41 const absl::Span<const int64_t> universe, const int64_t offset) {
42 int id_index = 0;
43 int universe_index = 0;
44 // NOTE(user): in the common case where ids and/or universe is consecutive,
45 // we can avoid iterating though the list and do interval based checks.
46 while (id_index < ids.size() && universe_index < universe.size()) {
47 if (universe[universe_index] < ids[id_index]) {
48 ++universe_index;
49 } else if (universe[universe_index] == ids[id_index]) {
50 ++id_index;
51 } else {
52 break;
53 }
54 }
55 if (id_index < ids.size()) {
57 << "Bad id: " << ids[id_index] << " (at index: " << id_index + offset
58 << ") found";
59 }
60 return absl::OkStatus();
61}
62
63// Given a sorted, strictly increasing set of ids, provides `contains()` to
64// check if another id is in the set in O(1) time.
65//
66// Implementation note: when ids are consecutive, they are stored as a single
67// interval [lb, ub), otherwise they are stored as a hash table of integers.
68class FastIdCheck {
69 public:
70 // ids must be sorted with unique strictly increasing entries.
71 explicit FastIdCheck(const absl::Span<const int64_t> ids) {
72 if (ids.empty()) {
73 interval_mode_ = true;
74 } else if (ids.size() == ids.back() + 1 - ids.front()) {
75 interval_mode_ = true;
76 interval_lb_ = ids.front();
77 interval_ub_ = ids.back() + 1;
78 } else {
79 ids_ = absl::flat_hash_set<int64_t>(ids.begin(), ids.end());
80 }
81 }
82 bool contains(int64_t id) const {
83 if (interval_mode_) {
84 return id >= interval_lb_ && id < interval_ub_;
85 } else {
86 return ids_.contains(id);
87 }
88 }
89
90 private:
91 bool interval_mode_ = false;
92 int64_t interval_lb_ = 0;
93 int64_t interval_ub_ = 0;
94 absl::flat_hash_set<int64_t> ids_;
95};
96
97// Checks that the elements of ids and bad_list have no overlap.
98//
99// Assumed: ids and bad_list are sorted in increasing order, repeats allowed.
100absl::Status CheckSortedIdsNotBad(const absl::Span<const int64_t> ids,
101 const absl::Span<const int64_t> bad_list) {
102 int id_index = 0;
103 int bad_index = 0;
104 while (id_index < ids.size() && bad_index < bad_list.size()) {
105 if (bad_list[bad_index] < ids[id_index]) {
106 ++bad_index;
107 } else if (bad_list[bad_index] > ids[id_index]) {
108 ++id_index;
109 } else {
111 << "Bad id: " << ids[id_index] << " (at index: " << id_index
112 << ") found";
113 }
114 }
115 return absl::OkStatus();
116}
117} // namespace
118
119absl::Status CheckIdsRangeAndStrictlyIncreasing(absl::Span<const int64_t> ids) {
120 int64_t previous{-1};
121 for (int i = 0; i < ids.size(); previous = ids[i], ++i) {
122 if (ids[i] < 0 || ids[i] == std::numeric_limits<int64_t>::max()) {
124 << "Expected ids to be nonnegative and not max(int64_t) but at "
125 "index "
126 << i << " found id: " << ids[i];
127 }
128 if (ids[i] <= previous) {
130 << "Expected ids to be strictly increasing, but at index " << i
131 << " found id: " << ids[i] << " and at index " << i - 1
132 << " found id: " << ids[i - 1];
133 }
134 }
135 return absl::OkStatus();
136}
137
138absl::Status CheckSortedIdsSubset(const absl::Span<const int64_t> ids,
139 const absl::Span<const int64_t> universe) {
140 RETURN_IF_ERROR(CheckSortedIdsSubsetWithIndexOffset(ids, universe, 0));
141 return absl::OkStatus();
142}
143
144absl::Status CheckUnsortedIdsSubset(const absl::Span<const int64_t> ids,
145 const absl::Span<const int64_t> universe) {
146 if (ids.empty()) {
147 return absl::OkStatus();
148 }
149 const FastIdCheck id_check(universe);
150 for (int i = 0; i < ids.size(); ++i) {
151 if (!id_check.contains(ids[i])) {
153 << "Bad id: " << ids[i] << " (at index: " << i << ") not found";
154 }
155 }
156 return absl::OkStatus();
157}
158
159absl::Status IdUpdateValidator::IsValid() const {
160 for (int i = 0; i < deleted_ids_.size(); ++i) {
161 const int64_t deleted_id = deleted_ids_[i];
162 if (!old_ids_.HasId(deleted_id)) {
164 << "Tried to delete id: " << deleted_id << " (at index: " << i
165 << ") but it was not present";
166 }
167 }
168 if (!new_ids_.empty() && new_ids_.front() < old_ids_.next_free_id()) {
170 << "All new ids should be greater or equal to the first unused id: "
171 << old_ids_.next_free_id()
172 << " but the first new id was: " << new_ids_.front();
173 }
174 return absl::OkStatus();
175}
176
178 const absl::Span<const int64_t> ids) const {
179 RETURN_IF_ERROR(CheckSortedIdsNotBad(ids, deleted_ids_)) << " was deleted";
180 for (int i = 0; i < ids.size(); ++i) {
181 if (!old_ids_.HasId(ids[i])) {
183 << "Bad id: " << ids[i] << " (at index: " << i << ") not found";
184 }
185 }
186 return absl::OkStatus();
187}
188
190 const absl::Span<const int64_t> ids) const {
191 // Implementation:
192 // * Partition ids into "old" and "new"
193 // * Check that the old ids are in old_ids_ but not deleted_ids_.
194 // * Check that the new ids are in new_ids_.
195 size_t split_point = ids.size();
196 if (!new_ids_.empty()) {
197 split_point = std::distance(
198 ids.begin(), std::lower_bound(ids.begin(), ids.end(), new_ids_[0]));
199 }
201 CheckSortedIdsSubsetOfNotDeleted(ids.subspan(0, split_point)));
202 RETURN_IF_ERROR(CheckSortedIdsSubsetWithIndexOffset(ids.subspan(split_point),
203 new_ids_, split_point));
204 return absl::OkStatus();
205}
206
208 const absl::Span<const int64_t> ids) const {
209 if (ids.empty()) {
210 return absl::OkStatus();
211 }
212 const FastIdCheck deleted_fast(deleted_ids_);
213 const FastIdCheck new_fast(new_ids_);
214 for (int i = 0; i < ids.size(); ++i) {
215 const int64_t id = ids[i];
216 if (!new_ids_.empty() && id >= new_ids_[0]) {
217 if (!new_fast.contains(id)) {
219 << "Bad id: " << id << " (at index: " << i << ") not found";
220 }
221 } else if (!old_ids_.HasId(id)) {
223 << "Bad id: " << id << " (at index: " << i << ") not found";
224 } else if (deleted_fast.contains(id)) {
226 << "Bad id: " << id << " (at index: " << i << ") was deleted";
227 }
228 }
229 return absl::OkStatus();
230}
231
232absl::Status CheckIdsSubset(absl::Span<const int64_t> ids,
233 const IdNameBiMap& universe,
234 absl::string_view ids_description,
235 absl::string_view universe_description) {
236 for (int i = 0; i < ids.size(); ++i) {
237 const int64_t id = ids[i];
238 if (!universe.HasId(id)) {
240 << "Id: " << id << " (at index: " << i << ") in "
241 << ids_description << " is missing from " << universe_description;
242 }
243 }
244 return absl::OkStatus();
245}
246
247absl::Status CheckIdsIdentical(absl::Span<const int64_t> first_ids,
248 const IdNameBiMap& second_ids,
249 absl::string_view first_description,
250 absl::string_view second_description) {
251 if (first_ids.size() != second_ids.Size()) {
253 << first_description << " has size " << first_ids.size() << ", but "
254 << second_description << " has size " << second_ids.Size();
255 }
256 RETURN_IF_ERROR(CheckIdsSubset(first_ids, second_ids, first_description,
257 second_description));
258 return absl::OkStatus();
259}
260
261} // namespace math_opt
262} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
absl::Status CheckSortedIdsSubsetOfFinal(const absl::Span< const int64_t > ids) const
absl::Status CheckIdsSubsetOfFinal(const absl::Span< const int64_t > ids) const
absl::Status CheckSortedIdsSubsetOfNotDeleted(const absl::Span< const int64_t > ids) const
double lower_bound
absl::Status CheckUnsortedIdsSubset(const absl::Span< const int64_t > ids, const absl::Span< const int64_t > universe)
absl::Status CheckIdsRangeAndStrictlyIncreasing(absl::Span< const int64_t > ids)
absl::Status CheckSortedIdsSubset(const absl::Span< const int64_t > ids, const absl::Span< const int64_t > universe)
absl::Status CheckIdsSubset(absl::Span< const int64_t > ids, const IdNameBiMap &universe, absl::string_view ids_description, absl::string_view universe_description)
absl::Status CheckIdsIdentical(absl::Span< const int64_t > first_ids, const IdNameBiMap &second_ids, absl::string_view first_description, absl::string_view second_description)
Collection of objects used to extend the Constraint Solver library.
StatusBuilder InvalidArgumentErrorBuilder()
double distance
#define RETURN_IF_ERROR(expr)
Definition: status_macros.h:29