Fix tests on all platforms
Try to link tests on Mac. Fix inaccurate pad brim size fix build on mac (attempt 2) Fixes for support tree faults and race conditions in release mode. Fix crashing test executable on gcc 4.9 fix warning on msvc
This commit is contained in:
parent
be7428d66e
commit
8ca7e56d0f
@ -271,8 +271,6 @@ ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, Con
|
|||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ConfigOptionDef::nocli = "~~~noCLI";
|
|
||||||
|
|
||||||
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
|
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
|
||||||
{
|
{
|
||||||
// prepare a function for wrapping text
|
// prepare a function for wrapping text
|
||||||
|
@ -1444,7 +1444,7 @@ public:
|
|||||||
std::vector<std::string> cli_args(const std::string &key) const;
|
std::vector<std::string> cli_args(const std::string &key) const;
|
||||||
|
|
||||||
// Assign this key to cli to disable CLI for this option.
|
// Assign this key to cli to disable CLI for this option.
|
||||||
static std::string nocli;
|
static const constexpr char *nocli = "~~~noCLI";
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map from a config option name to its definition.
|
// Map from a config option name to its definition.
|
||||||
|
@ -310,20 +310,13 @@ void breakstick_holes(Points& pts,
|
|||||||
pts.swap(out);
|
pts.swap(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void breakstick_holes(Points &pts, const PadConfig::EmbedObject &c)
|
template<class...Args>
|
||||||
|
ExPolygons breakstick_holes(const ExPolygons &input, Args...args)
|
||||||
{
|
{
|
||||||
breakstick_holes(pts, c.object_gap_mm, c.stick_stride_mm,
|
ExPolygons ret = input;
|
||||||
c.stick_width_mm, c.stick_penetration_mm);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExPolygons breakstick_holes(const ExPolygons &input,
|
|
||||||
const PadConfig::EmbedObject &cfg)
|
|
||||||
{
|
|
||||||
ExPolygons ret = offset_ex(input, scaled(cfg.object_gap_mm), ClipperLib::jtMiter, 1);
|
|
||||||
|
|
||||||
for (ExPolygon &p : ret) {
|
for (ExPolygon &p : ret) {
|
||||||
breakstick_holes(p.contour.points, cfg);
|
breakstick_holes(p.contour.points, args...);
|
||||||
for (auto &h : p.holes) breakstick_holes(h.points, cfg);
|
for (auto &h : p.holes) breakstick_holes(h.points, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -428,7 +421,7 @@ class ConcaveHull {
|
|||||||
Point d(scaled(nx), scaled(ny));
|
Point d(scaled(nx), scaled(ny));
|
||||||
r.points.emplace_back(c + Point(-d.y(), d.x()));
|
r.points.emplace_back(c + Point(-d.y(), d.x()));
|
||||||
r.points.emplace_back(c + Point(d.y(), -d.x()));
|
r.points.emplace_back(c + Point(d.y(), -d.x()));
|
||||||
offset(r, scaled(1.));
|
offset(r, scaled<float>(1.));
|
||||||
|
|
||||||
m_polys.emplace_back(r);
|
m_polys.emplace_back(r);
|
||||||
}
|
}
|
||||||
@ -473,7 +466,7 @@ public:
|
|||||||
|
|
||||||
static inline coord_t get_waffle_offset(const PadConfig &c)
|
static inline coord_t get_waffle_offset(const PadConfig &c)
|
||||||
{
|
{
|
||||||
return scaled(c.brim_size_mm + c.wing_distance() + c.wall_thickness_mm);
|
return scaled(c.brim_size_mm + c.wing_distance());
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline double get_merge_distance(const PadConfig &c)
|
static inline double get_merge_distance(const PadConfig &c)
|
||||||
@ -593,11 +586,19 @@ public:
|
|||||||
|
|
||||||
add_supports_to_index(support_blueprint);
|
add_supports_to_index(support_blueprint);
|
||||||
|
|
||||||
|
auto model_bp_offs =
|
||||||
|
offset_ex(model_blueprint,
|
||||||
|
scaled<float>(cfg.embed_object.object_gap_mm),
|
||||||
|
ClipperLib::jtMiter, 1);
|
||||||
|
|
||||||
ConcaveHull fullcvh =
|
ConcaveHull fullcvh =
|
||||||
wafflized_concave_hull(support_blueprint, model_blueprint, cfg, thr);
|
wafflized_concave_hull(support_blueprint, model_bp_offs, cfg, thr);
|
||||||
|
|
||||||
auto model_bp_sticks =
|
auto model_bp_sticks =
|
||||||
breakstick_holes(model_blueprint, cfg.embed_object);
|
breakstick_holes(model_bp_offs, cfg.embed_object.object_gap_mm,
|
||||||
|
cfg.embed_object.stick_stride_mm,
|
||||||
|
cfg.embed_object.stick_width_mm,
|
||||||
|
cfg.embed_object.stick_penetration_mm);
|
||||||
|
|
||||||
ExPolygons fullpad = diff_ex(fullcvh.to_expolygons(), model_bp_sticks);
|
ExPolygons fullpad = diff_ex(fullcvh.to_expolygons(), model_bp_sticks);
|
||||||
|
|
||||||
@ -673,7 +674,7 @@ public:
|
|||||||
template<class...Args>
|
template<class...Args>
|
||||||
ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args)
|
ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args)
|
||||||
{
|
{
|
||||||
ExPolygons tmp = offset_ex(poly.contour, delta, args...);
|
ExPolygons tmp = offset_ex(poly.contour, float(delta), args...);
|
||||||
|
|
||||||
if (tmp.empty()) return {};
|
if (tmp.empty()) return {};
|
||||||
|
|
||||||
@ -856,8 +857,12 @@ void create_pad(const ExPolygons &sup_blueprint,
|
|||||||
|
|
||||||
std::string PadConfig::validate() const
|
std::string PadConfig::validate() const
|
||||||
{
|
{
|
||||||
if (bottom_offset() > brim_size_mm + wing_distance())
|
static const double constexpr MIN_BRIM_SIZE_MM = .1;
|
||||||
return L("Pad brim size is too low for the current slope.");
|
|
||||||
|
if (brim_size_mm < MIN_BRIM_SIZE_MM ||
|
||||||
|
bottom_offset() > brim_size_mm + wing_distance() ||
|
||||||
|
ConcaveHull::get_waffle_offset(*this) <= MIN_BRIM_SIZE_MM)
|
||||||
|
return L("Pad brim size is too small for the current configuration.");
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ namespace ClipperLib { struct Polygon; }
|
|||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace sla {
|
namespace sla {
|
||||||
|
|
||||||
class Raster;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Raster captures an anti-aliased monochrome canvas where vectorial
|
* @brief Raster captures an anti-aliased monochrome canvas where vectorial
|
||||||
* polygons can be rasterized. Fill color is always white and the background is
|
* polygons can be rasterized. Fill color is always white and the background is
|
||||||
|
@ -70,7 +70,7 @@ std::vector<ExPolygons> SupportTree::slice(
|
|||||||
auto bb = pad_mesh.bounding_box();
|
auto bb = pad_mesh.bounding_box();
|
||||||
auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z());
|
auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z());
|
||||||
|
|
||||||
long cap = grid.end() - maxzit;
|
auto cap = grid.end() - maxzit;
|
||||||
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
|
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
|
||||||
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
|
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
|
||||||
|
|
||||||
|
@ -96,6 +96,8 @@ struct Head {
|
|||||||
// If there is a pillar connecting to this head, then the id will be set.
|
// If there is a pillar connecting to this head, then the id will be set.
|
||||||
long pillar_id = ID_UNSET;
|
long pillar_id = ID_UNSET;
|
||||||
|
|
||||||
|
long bridge_id = ID_UNSET;
|
||||||
|
|
||||||
inline void invalidate() { id = ID_UNSET; }
|
inline void invalidate() { id = ID_UNSET; }
|
||||||
inline bool is_valid() const { return id >= 0; }
|
inline bool is_valid() const { return id >= 0; }
|
||||||
|
|
||||||
@ -335,6 +337,12 @@ public:
|
|||||||
m_pillars[size_t(pillar.id)].links++;
|
m_pillars[size_t(pillar.id)].links++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned bridgecount(const Pillar &pillar) const {
|
||||||
|
std::lock_guard<Mutex> lk(m_mutex);
|
||||||
|
assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
|
||||||
|
return pillar.bridges;
|
||||||
|
}
|
||||||
|
|
||||||
template<class...Args> Pillar& add_pillar(Args&&...args)
|
template<class...Args> Pillar& add_pillar(Args&&...args)
|
||||||
{
|
{
|
||||||
std::lock_guard<Mutex> lk(m_mutex);
|
std::lock_guard<Mutex> lk(m_mutex);
|
||||||
|
@ -444,7 +444,8 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
|
|||||||
return m_builder.pillar(nearpillar_id);
|
return m_builder.pillar(nearpillar_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
|
if (m_builder.bridgecount(nearpillar()) > m_cfg.max_bridges_on_pillar)
|
||||||
|
return false;
|
||||||
|
|
||||||
Vec3d headjp = head.junction_point();
|
Vec3d headjp = head.junction_point();
|
||||||
Vec3d nearjp_u = nearpillar().startpoint();
|
Vec3d nearjp_u = nearpillar().startpoint();
|
||||||
@ -502,14 +503,21 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
|
|||||||
// Cannot insert the bridge. (further search might not worth the hassle)
|
// Cannot insert the bridge. (further search might not worth the hassle)
|
||||||
if(t < distance(bridgestart, bridgeend)) return false;
|
if(t < distance(bridgestart, bridgeend)) return false;
|
||||||
|
|
||||||
|
std::lock_guard<ccr::BlockingMutex> lk(m_bridge_mutex);
|
||||||
|
|
||||||
|
if (m_builder.bridgecount(nearpillar()) < m_cfg.max_bridges_on_pillar) {
|
||||||
// A partial pillar is needed under the starting head.
|
// A partial pillar is needed under the starting head.
|
||||||
if(zdiff > 0) {
|
if(zdiff > 0) {
|
||||||
m_builder.add_pillar(unsigned(head.id), bridgestart, r);
|
m_builder.add_pillar(unsigned(head.id), bridgestart, r);
|
||||||
m_builder.add_junction(bridgestart, r);
|
m_builder.add_junction(bridgestart, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_builder.add_bridge(bridgestart, bridgeend, r);
|
auto &br = m_builder.add_bridge(bridgestart, bridgeend, r);
|
||||||
m_builder.increment_bridges(nearpillar());
|
m_builder.increment_bridges(nearpillar());
|
||||||
|
if (head.pillar_id == ID_UNSET)
|
||||||
|
m_builder.head(unsigned(head.id)).bridge_id = br.id;
|
||||||
|
|
||||||
|
} else return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -646,9 +654,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
|
|||||||
|
|
||||||
if (normal_mode) {
|
if (normal_mode) {
|
||||||
Pillar &plr = head_id >= 0
|
Pillar &plr = head_id >= 0
|
||||||
? m_builder.add_pillar(unsigned(head_id),
|
? m_builder.add_pillar(unsigned(head_id), endp, radius)
|
||||||
endp,
|
|
||||||
radius)
|
|
||||||
: m_builder.add_pillar(jp, endp, radius);
|
: m_builder.add_pillar(jp, endp, radius);
|
||||||
|
|
||||||
if (can_add_base)
|
if (can_add_base)
|
||||||
@ -963,7 +969,7 @@ void SupportTreeBuildsteps::routing_to_model()
|
|||||||
m_builder.add_bridge(hjp, endp, head.r_back_mm);
|
m_builder.add_bridge(hjp, endp, head.r_back_mm);
|
||||||
m_builder.add_junction(endp, head.r_back_mm);
|
m_builder.add_junction(endp, head.r_back_mm);
|
||||||
|
|
||||||
this->create_ground_pillar(endp, dir, head.r_back_mm);
|
this->create_ground_pillar(endp, dir, head.r_back_mm, head.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<unsigned> modelpillars;
|
std::vector<unsigned> modelpillars;
|
||||||
@ -1240,12 +1246,15 @@ void SupportTreeBuildsteps::interconnect_pillars()
|
|||||||
needpillars = 3;
|
needpillars = 3;
|
||||||
else if (pillar().links < 2 && pillar().height > H2) {
|
else if (pillar().links < 2 && pillar().height > H2) {
|
||||||
// Not enough neighbors to support this pillar
|
// Not enough neighbors to support this pillar
|
||||||
needpillars = 2 - pillar().links;
|
needpillars = 2;
|
||||||
} else if (pillar().links < 1 && pillar().height > H1) {
|
} else if (pillar().links < 1 && pillar().height > H1) {
|
||||||
// No neighbors could be found and the pillar is too long.
|
// No neighbors could be found and the pillar is too long.
|
||||||
needpillars = 1;
|
needpillars = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
needpillars = std::max(pillar().links, needpillars) - pillar().links;
|
||||||
|
if (needpillars == 0) continue;
|
||||||
|
|
||||||
// Search for new pillar locations:
|
// Search for new pillar locations:
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@ -1318,6 +1327,7 @@ void SupportTreeBuildsteps::interconnect_pillars()
|
|||||||
|
|
||||||
newpills.emplace_back(pp.id);
|
newpills.emplace_back(pp.id);
|
||||||
m_builder.increment_links(pillar());
|
m_builder.increment_links(pillar());
|
||||||
|
m_builder.increment_links(pp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,8 +136,8 @@ IntegerOnly<DoubleI> pairhash(I a, I b)
|
|||||||
I g = min(a, b), l = max(a, b);
|
I g = min(a, b), l = max(a, b);
|
||||||
|
|
||||||
// Assume the hash will fit into the output variable
|
// Assume the hash will fit into the output variable
|
||||||
assert((g ? (ceil(log2(g))) : 0) < shift);
|
assert((g ? (ceil(log2(g))) : 0) <= shift);
|
||||||
assert((l ? (ceil(log2(l))) : 0) < shift);
|
assert((l ? (ceil(log2(l))) : 0) <= shift);
|
||||||
|
|
||||||
return (DoubleI(g) << shift) + l;
|
return (DoubleI(g) << shift) + l;
|
||||||
}
|
}
|
||||||
@ -176,6 +176,9 @@ class SupportTreeBuildsteps {
|
|||||||
// A spatial index to easily find strong pillars to connect to.
|
// A spatial index to easily find strong pillars to connect to.
|
||||||
PillarIndex m_pillar_index;
|
PillarIndex m_pillar_index;
|
||||||
|
|
||||||
|
// When bridging heads to pillars... TODO: find a cleaner solution
|
||||||
|
ccr::BlockingMutex m_bridge_mutex;
|
||||||
|
|
||||||
inline double ray_mesh_intersect(const Vec3d& s,
|
inline double ray_mesh_intersect(const Vec3d& s,
|
||||||
const Vec3d& dir)
|
const Vec3d& dir)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
|
||||||
|
@ -7,7 +7,10 @@ set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)
|
|||||||
file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR)
|
file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR)
|
||||||
|
|
||||||
add_library(test_common INTERFACE)
|
add_library(test_common INTERFACE)
|
||||||
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR="${TEST_DATA_DIR}")
|
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)")
|
||||||
target_link_libraries(test_common INTERFACE GTest::GTest GTest::Main)
|
target_link_libraries(test_common INTERFACE GTest::GTest GTest::Main)
|
||||||
|
if (APPLE)
|
||||||
|
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(sla_print)
|
add_subdirectory(sla_print)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include <map>
|
#include <unordered_set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -19,9 +21,9 @@
|
|||||||
#include "libslic3r/SVG.hpp"
|
#include "libslic3r/SVG.hpp"
|
||||||
|
|
||||||
#if defined(WIN32) || defined(_WIN32)
|
#if defined(WIN32) || defined(_WIN32)
|
||||||
#define PATH_SEPARATOR "\\"
|
#define PATH_SEPARATOR R"(\)"
|
||||||
#else
|
#else
|
||||||
#define PATH_SEPARATOR "/"
|
#define PATH_SEPARATOR R"(/)"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -30,7 +32,7 @@ using namespace Slic3r;
|
|||||||
TriangleMesh load_model(const std::string &obj_filename)
|
TriangleMesh load_model(const std::string &obj_filename)
|
||||||
{
|
{
|
||||||
TriangleMesh mesh;
|
TriangleMesh mesh;
|
||||||
auto fpath = std::string(TEST_DATA_DIR PATH_SEPARATOR) + obj_filename;
|
auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename;
|
||||||
load_obj(fpath.c_str(), &mesh);
|
load_obj(fpath.c_str(), &mesh);
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
@ -123,6 +125,12 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
|
|||||||
double H1 = cfg.max_solo_pillar_height_mm;
|
double H1 = cfg.max_solo_pillar_height_mm;
|
||||||
double H2 = cfg.max_dual_pillar_height_mm;
|
double H2 = cfg.max_dual_pillar_height_mm;
|
||||||
|
|
||||||
|
for (const sla::Head &head : stree.heads()) {
|
||||||
|
ASSERT_TRUE(!head.is_valid() ||
|
||||||
|
head.pillar_id != sla::ID_UNSET ||
|
||||||
|
head.bridge_id != sla::ID_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
for (const sla::Pillar &pillar : stree.pillars()) {
|
for (const sla::Pillar &pillar : stree.pillars()) {
|
||||||
if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) {
|
if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) {
|
||||||
double h = pillar.height;
|
double h = pillar.height;
|
||||||
@ -144,7 +152,7 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
|
|||||||
double z = n.z();
|
double z = n.z();
|
||||||
double polar = std::acos(z / d);
|
double polar = std::acos(z / d);
|
||||||
double slope = -polar + PI / 2.;
|
double slope = -polar + PI / 2.;
|
||||||
ASSERT_TRUE(slope >= cfg.bridge_slope || slope <= -cfg.bridge_slope);
|
ASSERT_GE(std::abs(slope), cfg.bridge_slope - EPSILON);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen);
|
for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen);
|
||||||
@ -283,24 +291,51 @@ const char *const SUPPORT_TEST_MODELS[] = {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// Test pair hash for 'nums' random number pairs.
|
||||||
template <class I, class II> void test_pairhash()
|
template <class I, class II> void test_pairhash()
|
||||||
{
|
{
|
||||||
std::map<II, std::pair<I, I> > ints;
|
const constexpr size_t nums = 1000;
|
||||||
for (I i = 0; i < 1000; ++i)
|
I A[nums] = {0}, B[nums] = {0};
|
||||||
for (I j = 0; j < 1000; ++j) {
|
std::unordered_set<I> CH;
|
||||||
if (j != i) {
|
std::unordered_map<II, std::pair<I, I>> ints;
|
||||||
II hash_ij = sla::pairhash<I, II>(i, j);
|
|
||||||
II hash_ji = sla::pairhash<I, II>(j, i);
|
|
||||||
ASSERT_EQ(hash_ij, hash_ji);
|
|
||||||
|
|
||||||
auto it = ints.find(hash_ij);
|
std::random_device rd;
|
||||||
|
std::mt19937 gen(rd());
|
||||||
|
|
||||||
|
const I Ibits = int(sizeof(I) * CHAR_BIT);
|
||||||
|
const II IIbits = int(sizeof(II) * CHAR_BIT);
|
||||||
|
const int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits;
|
||||||
|
|
||||||
|
const I Imax = I(std::pow(2., bits) - 1);
|
||||||
|
std::uniform_int_distribution<I> dis(0, Imax);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nums;) {
|
||||||
|
I a = dis(gen);
|
||||||
|
if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nums;) {
|
||||||
|
I b = dis(gen);
|
||||||
|
if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nums; ++i) {
|
||||||
|
I a = A[i], b = B[i];
|
||||||
|
|
||||||
|
ASSERT_TRUE(a != b);
|
||||||
|
|
||||||
|
II hash_ab = sla::pairhash<I, II>(a, b);
|
||||||
|
II hash_ba = sla::pairhash<I, II>(b, a);
|
||||||
|
ASSERT_EQ(hash_ab, hash_ba);
|
||||||
|
|
||||||
|
auto it = ints.find(hash_ab);
|
||||||
|
|
||||||
if (it != ints.end()) {
|
if (it != ints.end()) {
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
(it->second.first == i && it->second.second == j) ||
|
(it->second.first == a && it->second.second == b) ||
|
||||||
(it->second.first == j && it->second.second == i));
|
(it->second.first == b && it->second.second == a));
|
||||||
} else ints[hash_ij] = std::make_pair(i, j);
|
} else
|
||||||
}
|
ints[hash_ab] = std::make_pair(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user