diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index dab2753916..df1f2d88e1 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -19,7 +19,6 @@ package(default_visibility = ["//visibility:public"]) filegroup( name = "base_swig", srcs = ["base.i"], - visibility = ["//visibility:public"], ) cc_library( @@ -33,7 +32,6 @@ cc_library( "adjustable_priority_queue.h", "adjustable_priority_queue-inl.h", ], - deps = [":base"], ) cc_library( @@ -64,21 +62,13 @@ cc_library( ":commandlineflags", ":logging", ":types", - "@abseil-cpp//absl/base", - "@abseil-cpp//absl/container:flat_hash_map", - "@abseil-cpp//absl/container:flat_hash_set", - "@abseil-cpp//absl/container:node_hash_map", - "@abseil-cpp//absl/container:node_hash_set", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/flags:parse", "@abseil-cpp//absl/flags:usage", "@abseil-cpp//absl/log", - "@abseil-cpp//absl/log:check", - "@abseil-cpp//absl/log:die_if_null", - "@abseil-cpp//absl/log:globals", "@abseil-cpp//absl/log:initialize", "@abseil-cpp//absl/strings", - "@abseil-cpp//absl/synchronization", + "@abseil-cpp//absl/strings:string_view", ], ) @@ -108,7 +98,6 @@ cc_library( name = "constant_divisor", srcs = ["constant_divisor.cc"], hdrs = ["constant_divisor.h"], - visibility = ["//visibility:public"], deps = [ "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/numeric:int128", @@ -120,19 +109,18 @@ cc_test( srcs = ["constant_divisor_test.cc"], deps = [ ":constant_divisor", + "//ortools/base:gmock_main", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/random", "@abseil-cpp//absl/random:bit_gen_ref", "@abseil-cpp//absl/random:distributions", "@google_benchmark//:benchmark", - "@googletest//:gtest_main", ], ) cc_library( name = "container_logging", hdrs = ["container_logging.h"], - deps = [":base"], ) cc_library( @@ -143,7 +131,6 @@ cc_library( "//conditions:default": [], }), deps = [ - ":strong_int", ":strong_vector", "@abseil-cpp//absl/container:inlined_vector", ], @@ -161,17 +148,10 @@ cc_test( ":dump_vars", ":strong_int", ":strong_vector", - "@abseil-cpp//absl/strings", "@googletest//:gtest_main", ], ) -cc_library( - name = "flags", - hdrs = ["flags.h"], - deps = ["@abseil-cpp//absl/flags:flag"], -) - cc_library( name = "file", srcs = [ @@ -189,6 +169,7 @@ cc_library( "@abseil-cpp//absl/log", "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/status", + "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", "@bzip2//:bz2", "@protobuf", @@ -221,10 +202,9 @@ cc_library( srcs = ["gzipfile.cc"], hdrs = ["gzipfile.h"], deps = [ - ":base", ":file", - ":path", - "@abseil-cpp//absl/strings", + ":logging", + "@abseil-cpp//absl/strings:string_view", "@zlib", ], ) @@ -233,7 +213,9 @@ cc_library( name = "gzipstring", hdrs = ["gzipstring.h"], deps = [ - ":base", + ":logging", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/strings:string_view", "@zlib", ], ) @@ -242,41 +224,40 @@ cc_library( name = "hash", srcs = ["hash.cc"], hdrs = ["hash.h"], - deps = ["@abseil-cpp//absl/strings"], ) cc_library( name = "int_type", hdrs = ["int_type.h"], - deps = [":base"], + deps = [ + "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/strings:string_view", + ], ) cc_library( name = "intops", hdrs = ["strong_int.h"], deps = [ - ":int_type", - "@abseil-cpp//absl/log:absl_log", - "@abseil-cpp//absl/numeric:int128", + "@abseil-cpp//absl/meta:type_traits", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:str_format", + "@abseil-cpp//absl/strings:string_view", ], ) cc_library( name = "iterator_adaptors", hdrs = ["iterator_adaptors.h"], - deps = [":base"], ) cc_library( name = "linked_hash_map", hdrs = ["linked_hash_map.h"], deps = [ - ":base", - ":logging", + "//ortools/base", "@abseil-cpp//absl/container:common", - "@abseil-cpp//absl/container:flat_hash_map", + "@abseil-cpp//absl/container:flat_hash_set", ], ) @@ -286,32 +267,38 @@ cc_library( hdrs = ["logging.h"], deps = [ ":base_export", + "@abseil-cpp//absl/base:core_headers", "@abseil-cpp//absl/base:log_severity", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/flags:usage", "@abseil-cpp//absl/log", "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/log:die_if_null", - "@abseil-cpp//absl/log:flags", "@abseil-cpp//absl/log:globals", "@abseil-cpp//absl/log:initialize", + "@abseil-cpp//absl/log:vlog_is_on", "@abseil-cpp//absl/memory", "@abseil-cpp//absl/status", "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:string_view", ], ) cc_library( name = "map_util", hdrs = ["map_util.h"], - deps = [":base"], + deps = ["//ortools/base:logging"], ) cc_library( name = "mathutil", srcs = ["mathutil.cc"], hdrs = ["mathutil.h"], - deps = [":base"], + deps = [ + "//ortools/base:logging", + "@abseil-cpp//absl/base", + "@abseil-cpp//absl/log:check", + ], ) cc_library( @@ -322,17 +309,12 @@ cc_library( cc_library( name = "memutil", hdrs = ["memutil.h"], - deps = ["@abseil-cpp//absl/strings"], ) cc_library( name = "murmur", hdrs = ["murmur.h"], - deps = [ - ":base", - ":hash", - "@abseil-cpp//absl/strings", - ], + deps = ["//ortools/base:hash"], ) cc_library( @@ -356,6 +338,8 @@ cc_library( deps = [ "//ortools/base:status_macros", "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/status", + "@abseil-cpp//absl/strings:string_view", "@protobuf", ], ) @@ -365,8 +349,7 @@ cc_library( testonly = True, hdrs = ["parse_test_proto.h"], deps = [ - ":gmock", - "@abseil-cpp//absl/log:check", + "//ortools/base:gmock", "@protobuf", ], ) @@ -376,18 +359,23 @@ cc_library( srcs = ["path.cc"], hdrs = ["path.h"], deps = [ - ":base", "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:string_view", ], ) cc_library( name = "protobuf_util", hdrs = ["protobuf_util.h"], + deps = [ + "@abseil-cpp//absl/log:check", + "@protobuf", + ], ) cc_library( name = "protocol-buffer-matchers", + testonly = True, srcs = ["protocol-buffer-matchers.cc"], hdrs = ["protocol-buffer-matchers.h"], deps = [ @@ -402,9 +390,9 @@ cc_library( name = "protoutil", hdrs = ["protoutil.h"], deps = [ - ":timer", "@abseil-cpp//absl/status", "@abseil-cpp//absl/status:statusor", + "@abseil-cpp//absl/time", "@protobuf", ], ) @@ -412,10 +400,7 @@ cc_library( cc_library( name = "proto_enum_utils", hdrs = ["proto_enum_utils.h"], - deps = [ - "@abseil-cpp//absl/types:span", - "@protobuf", - ], + deps = ["@protobuf"], ) cc_library( @@ -423,12 +408,9 @@ cc_library( srcs = ["recordio.cc"], hdrs = ["recordio.h"], deps = [ - ":base", ":file", - ":logging", - "@abseil-cpp//absl/status:statusor", - "@abseil-cpp//absl/strings", - "@protobuf", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:check", "@zlib", ], ) @@ -443,7 +425,6 @@ cc_library( name = "status_builder", hdrs = ["status_builder.h"], deps = [ - ":base", "@abseil-cpp//absl/status", "@abseil-cpp//absl/strings", ], @@ -453,7 +434,6 @@ cc_library( name = "status_macros", hdrs = ["status_macros.h"], deps = [ - ":base", ":status_builder", "@abseil-cpp//absl/status", "@abseil-cpp//absl/status:statusor", @@ -463,7 +443,10 @@ cc_library( cc_library( name = "stl_util", hdrs = ["stl_util.h"], - deps = [":base"], + deps = [ + "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/meta:type_traits", + ], ) cc_library( @@ -489,16 +472,15 @@ cc_test( timeout = "long", srcs = ["strong_int_test.cc"], deps = [ - ":gmock", ":logging", ":strong_int", + "//ortools/base:gmock_main", "@abseil-cpp//absl/container:node_hash_map", "@abseil-cpp//absl/flags:marshalling", "@abseil-cpp//absl/hash:hash_testing", "@abseil-cpp//absl/numeric:int128", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:str_format", - "@googletest//:gtest_main", ], ) @@ -506,8 +488,8 @@ cc_library( name = "strong_vector", hdrs = ["strong_vector.h"], deps = [ - ":base", ":intops", + "//ortools/base:logging", ], ) @@ -516,16 +498,27 @@ cc_library( srcs = ["strtoint.cc"], hdrs = ["strtoint.h"], deps = [ + "@abseil-cpp//absl/base:core_headers", "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/strings", ], ) +cc_test( + name = "strtoint_test", + size = "small", + srcs = ["strtoint_test.cc"], + deps = [ + ":strtoint", + "//ortools/base:gmock_main", + "//ortools/base:types", + ], +) + cc_library( name = "sysinfo", srcs = ["sysinfo.cc"], hdrs = ["sysinfo.h"], - deps = ["@abseil-cpp//absl/strings"], ) cc_library( @@ -533,10 +526,10 @@ cc_library( srcs = ["temp_file.cc"], hdrs = ["temp_file.h"], deps = [ - ":base", - ":file", - "@abseil-cpp//absl/status", + "//ortools/base:logging", + "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:str_format", "@abseil-cpp//absl/time", ], ) @@ -551,6 +544,7 @@ cc_library( "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/status", "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:string_view", "@abseil-cpp//absl/time", ], ) @@ -561,7 +555,7 @@ cc_library( hdrs = ["threadpool.h"], deps = [ "@abseil-cpp//absl/log:check", - "@abseil-cpp//absl/synchronization", + "@abseil-cpp//absl/strings", ], ) @@ -578,6 +572,7 @@ cc_library( cc_library( name = "top_n", hdrs = ["top_n.h"], + deps = [":logging"], ) cc_library( @@ -591,9 +586,8 @@ cc_library( hdrs = ["zipfile.h"], deps = [ ":file", - ":path", - ":stl_util", - "@abseil-cpp//absl/strings", + ":logging", + "@abseil-cpp//absl/strings:string_view", "@zlib", ], ) diff --git a/ortools/base/accurate_sum_test.cc b/ortools/base/accurate_sum_test.cc new file mode 100644 index 0000000000..830eca4c23 --- /dev/null +++ b/ortools/base/accurate_sum_test.cc @@ -0,0 +1,51 @@ +// Copyright 2010-2025 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/base/accurate_sum.h" + +#include +#include +#include + +#include "absl/random/random.h" +#include "gtest/gtest.h" +#include "ortools/base/accurate_sum.h" + +namespace { + +TEST(AccurateSumTest, ExactConsistencyWithUtilMath) { + std::vector data; + { + std::mt19937 random(12345); + const int kNumNumbers = 1000000; + data.assign(kNumNumbers, 0.0); + for (int i = 0; i < kNumNumbers; ++i) { + const double abs_value = exp2(absl::Uniform(random, -100, 100.0)); + data[i] = absl::Bernoulli(random, 1.0 / 2) ? abs_value : -abs_value; + } + } + operations_research::AccurateSum forked_sum; + AccurateSum reference_sum; + // Runtime: August 2013, fastbuild, forge: 4 seconds. + const int kNumPasses = 100; + for (int i = 0; i < kNumPasses; ++i) { + for (const double v : data) { + forked_sum.Add(v); + reference_sum.Add(v); + } + } + // We *do* mean to expect a rigorous floating-point equality. + EXPECT_EQ(reference_sum.Value(), forked_sum.Value()); +} + +} // namespace diff --git a/ortools/base/container_logging.h b/ortools/base/container_logging.h index f6d5831bef..63fb113781 100644 --- a/ortools/base/container_logging.h +++ b/ortools/base/container_logging.h @@ -41,8 +41,6 @@ #include #include -#include "absl/base/port.h" - namespace gtl { // Several policy classes below determine how LogRangeToStream will diff --git a/ortools/base/dump_vars_test.cc b/ortools/base/dump_vars_test.cc index 81b4e5ae8d..57697d6188 100644 --- a/ortools/base/dump_vars_test.cc +++ b/ortools/base/dump_vars_test.cc @@ -14,9 +14,9 @@ #include "ortools/base/dump_vars.h" #include -#include #include #include +#include #include #include diff --git a/ortools/base/filesystem.cc b/ortools/base/filesystem.cc index 5fdcab30c1..a45dc9c113 100644 --- a/ortools/base/filesystem.cc +++ b/ortools/base/filesystem.cc @@ -13,11 +13,18 @@ #include "ortools/base/filesystem.h" +#include +#include // IWYU pragma: keep #include // NOLINT(build/c++17) #include // NOLINT +#include +#include +#include // NOLINT +#include #include "absl/status/status.h" #include "absl/strings/str_replace.h" +#include "ortools/base/file.h" namespace fs = std::filesystem; diff --git a/ortools/base/linked_hash_map_test.cc b/ortools/base/linked_hash_map_test.cc new file mode 100644 index 0000000000..28b50c727c --- /dev/null +++ b/ortools/base/linked_hash_map_test.cc @@ -0,0 +1,732 @@ +// Copyright 2010-2025 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. + +// Tests linked_hash_map. +// +// This test was forked from ortools/base/linked_hash_map_test.cc on Feb 25, +// 2021. Some tests were deleted as they depended on code not visible from here. + +#include "ortools/base/linked_hash_map.h" + +#include +#include +#include +#include +#include + +#include "absl/container/internal/unordered_map_constructor_test.h" +#include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" +#include "absl/container/internal/unordered_map_modifiers_test.h" +#include "absl/memory/memory.h" +#include "absl/types/any.h" +#include "gtest/gtest.h" +#include "ortools/base/gmock.h" +#include "util/gtl/map_util_test.h" + +namespace gtl { +namespace { + +// Need to import the namespace to bring the names generated by GoogleTest for +// the typed test suite. +using namespace absl::container_internal; // NOLINT + +using ::absl::container_internal::hash_internal::Enum; +using ::absl::container_internal::hash_internal::EnumClass; + +template +using Map = linked_hash_map>>; + +static_assert(!std::is_standard_layout(), ""); + +using MapTypes = + ::testing::Types, Map, + Map, Map, + Map, Map>; + +INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMap, ModifiersTest, MapTypes); + +using ::testing::ElementsAre; +using ::testing::Pair; +using ::testing::Pointee; + +// Tests that the range constructor works. +TEST(LinkedHashMapTest, RangeConstruct) { + const std::pair items[] = {{1, 2}, {2, 3}, {3, 4}}; + EXPECT_THAT((linked_hash_map(std::begin(items), std::end(items))), + ElementsAre(Pair(1, 2), Pair(2, 3), Pair(3, 4))); +} + +// Tests that copying works. +TEST(LinkedHashMapTest, Copy) { + linked_hash_map m{{2, 12}, {3, 13}}; + // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) + auto copy = m; + + EXPECT_TRUE(copy.contains(2)); + auto found = copy.find(2); + ASSERT_TRUE(found != copy.end()); + for (auto iter = copy.begin(); iter != copy.end(); ++iter) { + if (iter == found) return; + } + FAIL() << "Copied map's find method returned an invalid iterator."; +} + +// Tests that assignment works. +TEST(LinkedHashMapTest, Assign) { + linked_hash_map m{{2, 12}, {3, 13}}; + linked_hash_map n{{4, 14}}; + + n = m; + EXPECT_TRUE(n.contains(2)); + auto found = n.find(2); + ASSERT_TRUE(found != n.end()); + for (auto iter = n.begin(); iter != n.end(); ++iter) { + if (iter == found) return; + } + FAIL() << "Assigned map's find method returned an invalid iterator."; +} + +// Tests that move constructor works. +TEST(LinkedHashMapTest, Move) { + // Use unique_ptr as an example of a non-copyable type. + linked_hash_map> m; + m[2] = std::make_unique(12); + m[3] = std::make_unique(13); + linked_hash_map> n = std::move(m); + EXPECT_THAT(n, ElementsAre(Pair(2, Pointee(12)), Pair(3, Pointee(13)))); +} + +TEST(LinkedHashMapTest, CanInsertMoveOnly) { + linked_hash_map> m; + struct Data { + int k, v; + }; + const Data data[] = {{1, 123}, {3, 345}, {2, 234}, {4, 456}}; + for (const auto& kv : data) + m.insert({kv.k, std::make_unique(int{kv.v})}); + EXPECT_TRUE(m.contains(2)); + auto found = m.find(2); + ASSERT_TRUE(found != m.end()); + EXPECT_EQ(234, *found->second); +} + +TEST(LinkedHashMapTest, CanEmplaceMoveOnly) { + linked_hash_map> m; + struct Data { + int k, v; + }; + const Data data[] = {{1, 123}, {3, 345}, {2, 234}, {4, 456}}; + for (const auto& kv : data) { + m.emplace(std::piecewise_construct, std::make_tuple(kv.k), + std::make_tuple(new int{kv.v})); + } + EXPECT_TRUE(m.contains(2)); + auto found = m.find(2); + ASSERT_TRUE(found != m.end()); + EXPECT_EQ(234, *found->second); +} + +struct NoCopy { + explicit NoCopy(int x) : x(x) {} + NoCopy(const NoCopy&) = delete; + NoCopy& operator=(const NoCopy&) = delete; + NoCopy(NoCopy&&) = delete; + NoCopy& operator=(NoCopy&&) = delete; + int x; +}; + +TEST(LinkedHashMapTest, CanEmplaceNoMoveNoCopy) { + linked_hash_map m; + struct Data { + int k, v; + }; + const Data data[] = {{1, 123}, {3, 345}, {2, 234}, {4, 456}}; + for (const auto& kv : data) { + m.emplace(std::piecewise_construct, std::make_tuple(kv.k), + std::make_tuple(kv.v)); + } + EXPECT_TRUE(m.contains(2)); + auto found = m.find(2); + ASSERT_TRUE(found != m.end()); + EXPECT_EQ(234, found->second.x); +} + +TEST(LinkedHashMapTest, ConstKeys) { + linked_hash_map m; + m.insert(std::make_pair(1, 2)); + // Test that keys are const in iteration. + std::pair& p = *m.begin(); + EXPECT_EQ(1, p.first); +} + +// Tests that iteration from begin() to end() works +TEST(LinkedHashMapTest, Iteration) { + linked_hash_map m; + EXPECT_TRUE(m.begin() == m.end()); + + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(3, 13)); + + linked_hash_map::iterator i = m.begin(); + ASSERT_TRUE(m.begin() == i); + ASSERT_TRUE(m.end() != i); + EXPECT_EQ(2, i->first); + EXPECT_EQ(12, i->second); + + ++i; + ASSERT_TRUE(m.end() != i); + EXPECT_EQ(1, i->first); + EXPECT_EQ(11, i->second); + + ++i; + ASSERT_TRUE(m.end() != i); + EXPECT_EQ(3, i->first); + EXPECT_EQ(13, i->second); + + ++i; // Should be the end of the line. + ASSERT_TRUE(m.end() == i); +} + +// Tests that reverse iteration from rbegin() to rend() works +TEST(LinkedHashMapTest, ReverseIteration) { + linked_hash_map m; + EXPECT_TRUE(m.rbegin() == m.rend()); + + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(3, 13)); + + linked_hash_map::reverse_iterator i = m.rbegin(); + ASSERT_TRUE(m.rbegin() == i); + ASSERT_TRUE(m.rend() != i); + EXPECT_EQ(3, i->first); + EXPECT_EQ(13, i->second); + + ++i; + ASSERT_TRUE(m.rend() != i); + EXPECT_EQ(1, i->first); + EXPECT_EQ(11, i->second); + + ++i; + ASSERT_TRUE(m.rend() != i); + EXPECT_EQ(2, i->first); + EXPECT_EQ(12, i->second); + + ++i; // Should be the end of the line. + ASSERT_TRUE(m.rend() == i); +} + +// Tests that clear() works +TEST(LinkedHashMapTest, Clear) { + linked_hash_map m; + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(3, 13)); + + ASSERT_EQ(3, m.size()); + + m.clear(); + + EXPECT_EQ(0, m.size()); + + m.clear(); // Make sure we can call it on an empty map. + + EXPECT_EQ(0, m.size()); +} + +// Tests that size() works. +TEST(LinkedHashMapTest, Size) { + linked_hash_map m; + EXPECT_EQ(0, m.size()); + m.insert(std::make_pair(2, 12)); + EXPECT_EQ(1, m.size()); + m.insert(std::make_pair(1, 11)); + EXPECT_EQ(2, m.size()); + m.insert(std::make_pair(3, 13)); + EXPECT_EQ(3, m.size()); + m.clear(); + EXPECT_EQ(0, m.size()); +} + +// Tests empty() +TEST(LinkedHashMapTest, Empty) { + linked_hash_map m; + ASSERT_TRUE(m.empty()); + m.insert(std::make_pair(2, 12)); + ASSERT_FALSE(m.empty()); + m.clear(); + ASSERT_TRUE(m.empty()); +} + +TEST(LinkedHashMapTest, Erase) { + linked_hash_map m; + ASSERT_EQ(0, m.size()); + EXPECT_EQ(0, m.erase(2)); // Nothing to erase yet + + m.insert(std::make_pair(2, 12)); + ASSERT_EQ(1, m.size()); + EXPECT_EQ(1, m.erase(2)); + EXPECT_EQ(0, m.size()); + + EXPECT_EQ(0, m.erase(2)); // Make sure nothing bad happens if we repeat. + EXPECT_EQ(0, m.size()); +} + +TEST(LinkedHashMapTest, Erase2) { + linked_hash_map m; + ASSERT_EQ(0, m.size()); + EXPECT_EQ(0, m.erase(2)); // Nothing to erase yet + + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(3, 13)); + m.insert(std::make_pair(4, 14)); + ASSERT_EQ(4, m.size()); + + // Erase middle two + EXPECT_EQ(1, m.erase(1)); + EXPECT_EQ(1, m.erase(3)); + + EXPECT_EQ(2, m.size()); + + // Make sure we can still iterate over everything that's left. + linked_hash_map::iterator it = m.begin(); + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(12, it->second); + ++it; + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(14, it->second); + ++it; + ASSERT_TRUE(it == m.end()); + + EXPECT_EQ(0, m.erase(1)); // Make sure nothing bad happens if we repeat. + ASSERT_EQ(2, m.size()); + + EXPECT_EQ(1, m.erase(2)); + EXPECT_EQ(1, m.erase(4)); + ASSERT_EQ(0, m.size()); + + EXPECT_EQ(0, m.erase(1)); // Make sure nothing bad happens if we repeat. + ASSERT_EQ(0, m.size()); +} + +// Test that erase(iter,iter) and erase(iter) compile and work. +TEST(LinkedHashMapTest, Erase3) { + linked_hash_map m; + + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(3, 13)); + m.insert(std::make_pair(4, 14)); + + // Erase middle two + linked_hash_map::iterator it2 = m.find(2); + linked_hash_map::iterator it4 = m.find(4); + EXPECT_EQ(m.erase(it2, it4), m.find(4)); + EXPECT_EQ(2, m.size()); + + // Make sure we can still iterate over everything that's left. + linked_hash_map::iterator it = m.begin(); + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(11, it->second); + ++it; + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(14, it->second); + ++it; + ASSERT_TRUE(it == m.end()); + + // Erase first one using an iterator. + EXPECT_EQ(m.erase(m.begin()), m.find(4)); + + // Only the last element should be left. + it = m.begin(); + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(14, it->second); + ++it; + ASSERT_TRUE(it == m.end()); +} + +// Test all types of insertion +TEST(LinkedHashMapTest, Insertion) { + linked_hash_map m; + ASSERT_EQ(0, m.size()); + std::pair::iterator, bool> result; + + result = m.insert(std::make_pair(2, 12)); + ASSERT_EQ(1, m.size()); + EXPECT_TRUE(result.second); + EXPECT_EQ(2, result.first->first); + EXPECT_EQ(12, result.first->second); + + result = m.insert(std::make_pair(1, 11)); + ASSERT_EQ(2, m.size()); + EXPECT_TRUE(result.second); + EXPECT_EQ(1, result.first->first); + EXPECT_EQ(11, result.first->second); + + result = m.insert(std::make_pair(3, 13)); + linked_hash_map::iterator result_iterator = result.first; + ASSERT_EQ(3, m.size()); + EXPECT_TRUE(result.second); + EXPECT_EQ(3, result.first->first); + EXPECT_EQ(13, result.first->second); + + result = m.insert(std::make_pair(3, 13)); + EXPECT_EQ(3, m.size()); + EXPECT_FALSE(result.second) << "No insertion should have occurred."; + EXPECT_TRUE(result_iterator == result.first) + << "Duplicate insertion should have given us the original iterator."; + + std::vector> v = {{3, 13}, {4, 14}, {5, 15}}; + m.insert(v.begin(), v.end()); + // Expect 4 and 5 inserted, 3 not inserted. + EXPECT_EQ(5, m.size()); + EXPECT_EQ(14, m.at(4)); + EXPECT_EQ(15, m.at(5)); +} + +static std::pair Pair(int i, int j) { return {i, j}; } + +// Test front accessors. +TEST(LinkedHashMapTest, Front) { + linked_hash_map m; + + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(3, 13)); + + EXPECT_EQ(3, m.size()); + EXPECT_EQ(Pair(2, 12), m.front()); + m.pop_front(); + EXPECT_EQ(2, m.size()); + EXPECT_EQ(Pair(1, 11), m.front()); + m.pop_front(); + EXPECT_EQ(1, m.size()); + EXPECT_EQ(Pair(3, 13), m.front()); + m.pop_front(); + EXPECT_TRUE(m.empty()); +} + +// Test back accessors. +TEST(LinkedHashMapTest, Back) { + linked_hash_map m; + + m.insert(std::make_pair(2, 12)); + m.insert(std::make_pair(1, 11)); + m.insert(std::make_pair(3, 13)); + + EXPECT_EQ(3, m.size()); + EXPECT_EQ(Pair(3, 13), m.back()); + m.pop_back(); + EXPECT_EQ(2, m.size()); + EXPECT_EQ(Pair(1, 11), m.back()); + m.pop_back(); + EXPECT_EQ(1, m.size()); + EXPECT_EQ(Pair(2, 12), m.back()); + m.pop_back(); + EXPECT_TRUE(m.empty()); +} + +TEST(LinkedHashMapTest, Find) { + linked_hash_map m; + + EXPECT_TRUE(m.end() == m.find(1)) + << "We shouldn't find anything in an empty map."; + + m.insert(std::make_pair(2, 12)); + EXPECT_TRUE(m.end() == m.find(1)) + << "We shouldn't find an element that doesn't exist in the map."; + + std::pair::iterator, bool> result = + m.insert(std::make_pair(1, 11)); + ASSERT_TRUE(result.second); + ASSERT_TRUE(m.end() != result.first); + EXPECT_TRUE(result.first == m.find(1)) + << "We should have found an element we know exists in the map."; + EXPECT_EQ(11, result.first->second); + + // Check that a follow-up insertion doesn't affect our original + m.insert(std::make_pair(3, 13)); + linked_hash_map::iterator it = m.find(1); + ASSERT_TRUE(m.end() != it); + EXPECT_EQ(11, it->second); + + m.clear(); + EXPECT_TRUE(m.end() == m.find(1)) + << "We shouldn't find anything in a map that we've cleared."; +} + +TEST(LinkedHashMapTest, Contains) { + linked_hash_map m; + + EXPECT_FALSE(m.contains(1)) << "An empty map shouldn't contain anything."; + + m.insert(std::make_pair(2, 12)); + EXPECT_FALSE(m.contains(1)) + << "The map shouldn't contain an element that doesn't exist."; + + m.insert(std::make_pair(1, 11)); + EXPECT_TRUE(m.contains(1)) + << "The map should contain an element that we know exists."; + + m.clear(); + EXPECT_FALSE(m.contains(1)) + << "A map that we've cleared shouldn't contain anything."; +} + +TEST(LinkedHashMapTest, At) { + linked_hash_map m = {{1, 2}}; + EXPECT_EQ(2, m.at(1)); + EXPECT_DEATH(m.at(3), ".*linked_hash_map::at.*"); + + const linked_hash_map cm = {{1, 2}}; + EXPECT_EQ(2, cm.at(1)); + EXPECT_DEATH(cm.at(3), ".*linked_hash_map::at.*"); +} + +TEST(LinkedHashMapTest, Swap) { + linked_hash_map m1; + linked_hash_map m2; + m1.insert(std::make_pair(1, 1)); + m1.insert(std::make_pair(2, 2)); + m2.insert(std::make_pair(3, 3)); + ASSERT_EQ(2, m1.size()); + ASSERT_EQ(1, m2.size()); + m1.swap(m2); + ASSERT_EQ(1, m1.size()); + ASSERT_EQ(2, m2.size()); +} + +TEST(LinkedHashMapTest, InitializerList) { + linked_hash_map m{{1, 2}, {3, 4}}; + ASSERT_EQ(2, m.size()); + EXPECT_TRUE(m.contains(1)); + linked_hash_map::iterator it = m.find(1); + ASSERT_TRUE(m.end() != it); + EXPECT_EQ(2, it->second); + EXPECT_TRUE(m.contains(3)); + it = m.find(3); + ASSERT_TRUE(m.end() != it); + EXPECT_EQ(4, it->second); +} + +TEST(LinkedHashMapTest, CustomHashAndEquality) { + struct CustomIntHash { + size_t operator()(int x) const { return x; } + }; + struct CustomIntEq { + bool operator()(int x, int y) const { return x == y; } + }; + linked_hash_map m; + m.insert(std::make_pair(1, 1)); + EXPECT_TRUE(m.contains(1)); + EXPECT_EQ(1, m[1]); +} + +TEST(LinkedHashMapTest, EqualRange) { + linked_hash_map m{{3, 11}, {1, 13}}; + const auto& const_m = m; + + EXPECT_THAT(m.equal_range(2), testing::Pair(m.end(), m.end())); + EXPECT_THAT(const_m.equal_range(2), + testing::Pair(const_m.end(), const_m.end())); + + EXPECT_THAT(m.equal_range(1), testing::Pair(m.find(1), ++m.find(1))); + EXPECT_THAT(const_m.equal_range(1), + testing::Pair(const_m.find(1), ++const_m.find(1))); +} + +TEST(LinkedHashMapTest, ReserveWorks) { + linked_hash_map m; + EXPECT_GE(10000, m.capacity()); + EXPECT_EQ(0, m.size()); + EXPECT_EQ(0.0, m.load_factor()); + m.reserve(100'000); + const int capacity_before_insert = m.capacity(); + EXPECT_LE(100'000, capacity_before_insert); + EXPECT_EQ(0, m.size()); + EXPECT_EQ(0.0, m.load_factor()); + m.emplace(1, 1); + m.emplace(2, 2); + EXPECT_EQ(capacity_before_insert, m.capacity()); + EXPECT_EQ(2, m.size()); + EXPECT_LT(0.0, m.load_factor()); +} + +TEST(LinkedHashMapTest, HeterogeneousStringViewLookup) { + linked_hash_map map; + map["foo"] = 1; + map["bar"] = 2; + map["blah"] = 3; + + { + absl::string_view lookup("foo"); + auto itr = map.find(lookup); + ASSERT_NE(itr, map.end()); + EXPECT_EQ(1, itr->second); + } + + // Not found. + { + absl::string_view lookup("foobar"); + EXPECT_EQ(map.end(), map.find(lookup)); + } + + { + absl::string_view lookup("blah"); + auto itr = map.find(lookup); + ASSERT_NE(itr, map.end()); + EXPECT_EQ(3, itr->second); + } +} + +TEST(LinkedHashMapTest, UniversalReferenceWorks) { + linked_hash_map map; + std::string s = "very very very very very long string"; + map[s] = 1; + EXPECT_EQ(s, "very very very very very long string"); +} + +TEST(LinkedHashMap, ExtractInsert) { + linked_hash_map m = {{1, 7}, {2, 9}}; + auto node = m.extract(1); + EXPECT_TRUE(node); + EXPECT_EQ(node.key(), 1); + EXPECT_EQ(node.mapped(), 7); + EXPECT_THAT(m, ElementsAre(Pair(2, 9))); + EXPECT_FALSE(m.contains(1)); + EXPECT_TRUE(m.contains(2)); + + node.mapped() = 17; + m.insert(std::move(node)); + EXPECT_FALSE(node); + EXPECT_THAT(m, ElementsAre(Pair(2, 9), Pair(1, 17))); + EXPECT_TRUE(m.contains(2)); + EXPECT_TRUE(m.contains(1)); + + node = m.extract(m.find(1)); + EXPECT_TRUE(node); + EXPECT_EQ(node.key(), 1); + EXPECT_EQ(node.mapped(), 17); + EXPECT_THAT(m, ElementsAre(Pair(2, 9))); +} + +TEST(LinkedHashMap, Merge) { + linked_hash_map m = {{1, 7}, {3, 6}}; + linked_hash_map src = {{1, 10}, {2, 9}, {4, 16}}; + + m.merge(src); + + EXPECT_THAT(m, ElementsAre(Pair(1, 7), Pair(3, 6), Pair(2, 9), Pair(4, 16))); + EXPECT_THAT(src, ElementsAre(Pair(1, 10))); + for (int i : {2, 9, 4}) { + EXPECT_FALSE(src.contains(i)); + } +} + +TEST(LinkedHashMap, EraseRange) { + linked_hash_map map = {{1, 1}, {2, 4}, {3, 9}, {4, 16}, {5, 25}, + {6, 36}, {7, 49}, {8, 64}, {9, 81}}; + auto start = map.find(3); + auto end = map.find(8); + auto itr = map.erase(start, end); + ASSERT_NE(itr, map.end()); + EXPECT_THAT(*itr, Pair(8, 64)); + EXPECT_THAT(map, + ElementsAre(Pair(1, 1), Pair(2, 4), Pair(8, 64), Pair(9, 81))); + for (int i : {1, 2, 8, 9}) { + EXPECT_TRUE(map.contains(i)); + } + for (int i : {3, 4, 5, 6, 7}) { + EXPECT_FALSE(map.contains(i)); + } +} + +TEST(LinkedHashMap, InsertInitializerList) { + linked_hash_map m; + m.insert({{1, 7}, {2, 9}, {3, 29}}); + EXPECT_THAT(m, ElementsAre(Pair(1, 7), Pair(2, 9), Pair(3, 29))); +} + +TEST(LinkedHashMap, InsertOrAssign) { + linked_hash_map m; + { + auto [itr, elem_inserted] = m.insert_or_assign(10, 20); + EXPECT_THAT(*itr, Pair(10, 20)); + EXPECT_EQ(elem_inserted, true); + } + { + auto [itr, elem_inserted] = m.insert_or_assign(10, 30); + EXPECT_THAT(*itr, Pair(10, 30)); + EXPECT_EQ(elem_inserted, false); + } +} + +TEST(LinkedHashMap, TryEmplace) { + linked_hash_map> m; + { + auto [itr, elem_inserted] = m.try_emplace(10, 20, "string"); + EXPECT_THAT(*itr, Pair(10, Pair(20, "string"))); + EXPECT_EQ(elem_inserted, true); + } + { + std::string s = "some string"; + auto [itr, elem_inserted] = m.try_emplace(10, 2, std::move(s)); + EXPECT_THAT(*itr, Pair(10, Pair(20, "string"))); + EXPECT_EQ(elem_inserted, false); + } +} + +TEST(LinkedHashMapTest, TryEmplace) { + linked_hash_map>> + map; + auto result = map.try_emplace(3, 2, "foo", new float(1.0)); + EXPECT_TRUE(result.second); + EXPECT_EQ(std::get<0>(result.first->second), 2); + EXPECT_EQ(std::get<1>(result.first->second), "foo"); + EXPECT_EQ(*std::get<2>(result.first->second), 1.0); + + // Ptr should not be moved. + auto ptr = std::make_unique(3.0); + auto result2 = map.try_emplace(3, 22, "foo-bar", std::move(ptr)); + EXPECT_FALSE(result2.second); + EXPECT_EQ(std::get<0>(result.first->second), 2); + EXPECT_EQ(std::get<1>(result.first->second), "foo"); + EXPECT_EQ(*std::get<2>(result.first->second), 1.0); + EXPECT_NE(ptr.get(), nullptr); +} + +} // namespace +} // namespace gtl + +REGISTER_TYPED_TEST_SUITE_P(MapUtilIntIntTest, ValueMapTests, + UpdateReturnCopyTest, InsertOrReturnExistingTest, + FindOrDieTest, InsertOrDieTest, InsertKeyOrDieTest); +typedef ::testing::Types> + LinkedHashMapIntIntTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMapUtilTest, MapUtilIntIntTest, + LinkedHashMapIntIntTypes); + +REGISTER_TYPED_TEST_SUITE_P(MapUtilIntIntPtrTest, LookupOrInsertNewTest, + InsertAndDeleteExistingTest, FindPtrOrNullTest, + EraseKeyReturnValuePtrTest); +typedef ::testing::Types> + LinkedHashMapIntIntPtrTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(LinkedHashMapUtilTest, MapUtilIntIntPtrTest, + LinkedHashMapIntIntPtrTypes); diff --git a/ortools/base/strtoint_test.cc b/ortools/base/strtoint_test.cc new file mode 100644 index 0000000000..66fe5f136a --- /dev/null +++ b/ortools/base/strtoint_test.cc @@ -0,0 +1,59 @@ +// Copyright 2010-2025 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/base/strtoint.h" + +#include +#include + +#include "gtest/gtest.h" +#include "ortools/base/types.h" + +namespace operations_research { + +TEST(StrutilTest, StrtoFunctions) { + // 64-bit conversions are pass-through on all current platforms + EXPECT_EQ(0, strtoint64("0")); + + EXPECT_EQ(std::numeric_limits::max(), + strtoint64("9223372036854775807")); + + EXPECT_EQ(std::numeric_limits::min(), + strtoint64("-9223372036854775808")); + + // safe signed 32-bit conversions within 32-bit range + EXPECT_EQ(0, strtoint32("0")); + + EXPECT_EQ(std::numeric_limits::max(), strtoint32("2147483647")); + + EXPECT_EQ(std::numeric_limits::min(), strtoint32("-2147483648")); +} + +TEST(StrutilTest, AtoiFunctions) { + // basic atoi32/64 functions, including checks for overflow equivalency + // even in invalid conversions + EXPECT_EQ(0, atoi64("0")); + EXPECT_EQ(12345, atoi64("12345")); + EXPECT_EQ(-12345, atoi64("-12345")); + EXPECT_EQ(std::numeric_limits::max(), atoi64("9223372036854775807")); + EXPECT_EQ(std::numeric_limits::min(), + atoi64("-9223372036854775808")); + + EXPECT_EQ(0, atoi32("0")); + EXPECT_EQ(12345, atoi32("12345")); + EXPECT_EQ(-12345, atoi32("-12345")); + EXPECT_EQ(std::numeric_limits::max(), atoi32("2147483647")); + EXPECT_EQ(std::numeric_limits::min(), atoi32("-2147483648")); +} + +} // namespace operations_research diff --git a/ortools/flatzinc/BUILD.bazel b/ortools/flatzinc/BUILD.bazel index 8d84487fbd..4c81e76efe 100644 --- a/ortools/flatzinc/BUILD.bazel +++ b/ortools/flatzinc/BUILD.bazel @@ -159,5 +159,7 @@ cc_binary( "//ortools/base:path", "//ortools/base:threadpool", "//ortools/util:logging", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:flags", ], ) diff --git a/ortools/linear_solver/proto_solver/BUILD.bazel b/ortools/linear_solver/proto_solver/BUILD.bazel index 33b4fa4c91..98e396c86c 100644 --- a/ortools/linear_solver/proto_solver/BUILD.bazel +++ b/ortools/linear_solver/proto_solver/BUILD.bazel @@ -118,6 +118,7 @@ cc_library( srcs = ["scip_params.cc"], hdrs = ["scip_params.h"], deps = [ + "//ortools/base:logging", "//ortools/linear_solver:scip_helper_macros", "@abseil-cpp//absl/status", "@abseil-cpp//absl/strings",