77// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
88// http://www.boost.org/LICENSE_1_0.txt)
99
10+ #include < boost/program_options/value_semantic.hpp>
1011#define BOOST_GEOMETRY_NO_BOOST_TEST
1112
1213#include < bitset>
14+ #include < chrono>
1315#include < iostream>
1416#include < random>
1517#include < utility>
2628#include < boost/geometry/geometries/geometries.hpp>
2729
2830#define BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE
31+ #ifndef BOOST_GEOMETRY_DEFAULT_TEST_TYPE
2932#define BOOST_GEOMETRY_DEFAULT_TEST_TYPE int
33+ #endif
3034#include < geometry_test_common.hpp>
3135
3236constexpr int chunk_size = 64 ;
@@ -46,19 +50,23 @@ struct grid_settings
4650 int height = 5 ;
4751 int count = 1 ;
4852 generator_t ::result_type seed = generator_t ::default_seed;
53+ bool verbose = false ;
54+ std::vector<std::uint64_t > bits1{};
55+ std::vector<std::uint64_t > bits2{};
4956};
5057
5158constexpr int cell_dimension = 2 ;
5259
5360std::vector<box> grid_cells (grid_settings const & settings)
5461{
5562 std::vector<box> out;
63+ out.reserve (settings.height * settings.width );
5664 for (int y = 0 ; y < settings.height ; ++y)
5765 {
5866 for (int x = 0 ; x < settings.width ; ++x)
5967 {
60- out.push_back (box{point{ x * cell_dimension, y * cell_dimension} ,
61- point{( x + 1 ) * cell_dimension, (y + 1 ) * cell_dimension} });
68+ out.push_back (box{point ( x * cell_dimension, y * cell_dimension) ,
69+ point (( x + 1 ) * cell_dimension, (y + 1 ) * cell_dimension) });
6270 }
6371 }
6472 return out;
@@ -67,28 +75,38 @@ std::vector<box> grid_cells(grid_settings const& settings)
6775std::vector<point> test_points (grid_settings const & settings)
6876{
6977 std::vector<point> out;
78+ out.reserve (settings.height * settings.width );
7079 for (int y = 0 ; y < settings.height ; ++y)
7180 {
7281 for (int x = 0 ; x < settings.width ; ++x)
7382 {
74- out.push_back (point{ x * cell_dimension + cell_dimension / 2 ,
75- y * cell_dimension + cell_dimension / 2 } );
83+ out.push_back (point ( x * cell_dimension + cell_dimension / 2 ,
84+ y * cell_dimension + cell_dimension / 2 ) );
7685 }
7786 }
7887 return out;
7988}
8089
8190std::ostream& operator <<(std::ostream& os, std::pair<bits, grid_settings> const & b_gs)
8291{
83- os << ' \n ' ;
84- for (int y = b_gs.second .height - 1 ; y >= 0 ; --y)
92+ if (b_gs.second .verbose )
8593 {
86- for (int x = 0 ; x < b_gs.second .width ; ++x)
94+ os << ' \n ' ;
95+ for (int y = b_gs.second .height - 1 ; y >= 0 ; --y)
8796 {
88- int index = y * b_gs.second .width + x;
89- os << b_gs.first [index / chunk_size][index % chunk_size];
97+ for (int x = 0 ; x < b_gs.second .width ; ++x)
98+ {
99+ int index = y * b_gs.second .width + x;
100+ os << b_gs.first [index / chunk_size][index % chunk_size];
101+ }
102+ os << ' \n ' ;
90103 }
91- os << ' \n ' ;
104+ }
105+ else
106+ {
107+ os << ' {' << b_gs.first [0 ].to_ullong ();
108+ for (size_t i = 1 ; i < b_gs.first .size (); ++i) os << ' ' << b_gs.first [i].to_ullong ();
109+ os << ' }' ;
92110 }
93111 return os;
94112}
@@ -104,7 +122,7 @@ bits geometry_to_bits(mp_t const& geometry, std::vector<point> const& test_point
104122}
105123
106124mp_t bits_to_geometry (bits const & b, std::vector<box> const & grid, std::vector<point> const & points,
107- grid_settings const & settings, bool & all_success )
125+ grid_settings const & settings, std::map<std::string, int >& failures )
108126{
109127 mp_t out;
110128 for (size_t i = 0 ; i < grid.size (); ++i)
@@ -121,15 +139,23 @@ mp_t bits_to_geometry(bits const& b, std::vector<box> const& grid, std::vector<p
121139 std::string reason{};
122140 if (! bg::is_valid (out, reason))
123141 {
124- std::cout << bg::wkt (out) << " \n generated from" << b_gs (b)
125- << " is invalid: " << reason << " .\n\n " ;
126- all_success = false ;
142+ if (settings.verbose )
143+ {
144+ std::cout << bg::wkt (out) << " \n generated from" << b_gs (b)
145+ << " is invalid: " << reason << " .\n\n " ;
146+ }
147+ else std::cout << b_gs (b) << " invalid (" << reason << " )\n " ;
148+ ++failures[" bits_to_geometry validity" ];
127149 }
128150 if (geometry_to_bits (out, points) != b)
129151 {
130- std::cout << " Generating grid from pattern" << b_gs (b)
131- << " results in mismatching geometry: " << bg::wkt (out) << " .\n\n " ;
132- all_success = false ;
152+ if (settings.verbose )
153+ {
154+ std::cout << " Generating grid from pattern" << b_gs (b)
155+ << " results in mismatching geometry: " << bg::wkt (out) << " .\n\n " ;
156+ }
157+ else std::cout << b_gs (b) << " mismatch.\n " ;
158+ ++failures[" bits_to_geometry mismatch" ];
133159 }
134160 return out;
135161}
@@ -148,6 +174,14 @@ bits gen_bits(generator_t& generator, int bits_size)
148174 return b;
149175}
150176
177+ bits to_bits (std::vector<std::uint64_t > const & in)
178+ {
179+ bits out;
180+ out.reserve (in.size ());
181+ for (auto const & ullong : in) out.push_back (std::bitset<chunk_size>(ullong));
182+ return out;
183+ }
184+
151185template <typename BitOp>
152186bits apply_for_each (bits a, bits const & b, BitOp const & bit_op)
153187{
@@ -159,64 +193,114 @@ template<typename BitOp, typename GeoOp>
159193void test_op (bits const & bits1, bits const & bits2, mp_t const & geo1, mp_t const & geo2,
160194 std::string const & op_label, BitOp const & bit_op, GeoOp const & geo_op,
161195 std::vector<point> const & test_points, std::vector<box> const & grid,
162- grid_settings const & settings, bool & success )
196+ grid_settings const & settings, std::map<std::string, int >& failures )
163197{
164198 auto test_geo = geo_op (geo1, geo2);
165199 // Convenience lambda to pair bits with settings to use width/height in operator<<(os, ...)
166200 const auto b_gs = [&settings](bits const & b) { return std::make_pair (b, settings); };
167201 std::string reason{};
168202 if (! bg::is_valid (test_geo, reason))
169203 {
170- std::cout << op_label << " (\n\t " << bg::wkt (geo1) << " ,\n\t " << bg::wkt (geo2) << " \n ),\n "
171- << " generated from" << b_gs (bits1) << " and" << b_gs (bits2) << " is invalid: "
172- << reason << " .\n\n " ;
173- success = false ;
204+ if (settings.verbose )
205+ {
206+ std::cout << op_label << " (\n\t " << bg::wkt (geo1) << " ,\n\t " << bg::wkt (geo2) << " \n ),"
207+ << " \n generated from" << b_gs (bits1) << " and" << b_gs (bits2) << " is invalid: "
208+ << reason << " .\n\n " ;
209+ }
210+ else
211+ {
212+ std::cout << op_label << ' (' << b_gs (bits1) << " , " << b_gs (bits2) << " invalid ("
213+ << reason << " ).\n " ;
214+ }
215+ ++failures[op_label + " validity" ];
174216 }
175217 const bits expected = apply_for_each (bits1, bits2, bit_op);
176218 const bits obtained = geometry_to_bits (test_geo, test_points);
177219 if (obtained != expected)
178220 {
179- std::cout << op_label << " (\n\t " << bg::wkt (geo1) << " ,\n\t " << bg::wkt (geo2) << " \n ),\n "
180- << " generated from" << b_gs (bits1) << " and" << b_gs (bits2)
181- << " is incorrect.\n Expected: "
182- << bg::wkt (bits_to_geometry (expected, grid, test_points, settings, success))
183- << " \n corresponding to" << b_gs (expected) << " Obtained: "
184- << bg::wkt (test_geo) << " \n corresponding to" << b_gs (obtained) << " \n " ;
185- success = false ;
221+ if (settings.verbose )
222+ {
223+ std::cout << op_label << " (\n\t " << bg::wkt (geo1) << " ,\n\t " << bg::wkt (geo2) << " \n ),"
224+ << " \n generated from" << b_gs (bits1) << " and" << b_gs (bits2)
225+ << " is incorrect.\n Expected: "
226+ << bg::wkt (bits_to_geometry (expected, grid, test_points, settings, failures))
227+ << " \n corresponding to" << b_gs (expected) << " Obtained: "
228+ << bg::wkt (test_geo) << " \n corresponding to" << b_gs (obtained) << " \n " ;
229+ }
230+ else std::cout << op_label << ' (' << b_gs (bits1) << " , " << b_gs (bits2) << " ) mismatch.\n " ;
231+ ++failures[op_label + " mismatch" ];
186232 }
187233}
188234
235+ void test_bits (bits const & bits1, bits const & bits2,
236+ std::vector<box> const & grid, std::vector<point> const & test_points,
237+ grid_settings const & settings, std::map<std::string, int >& failures)
238+ {
239+ const auto geo1 = bits_to_geometry (bits1, grid, test_points, settings, failures);
240+ const auto geo2 = bits_to_geometry (bits2, grid, test_points, settings, failures);
241+ test_op (bits1, bits2, geo1, geo2, " union" , std::bit_or<>{},
242+ [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::union_ (g1, g2, g); return g; },
243+ test_points, grid, settings, failures);
244+ test_op (bits1, bits2, geo1, geo2, " intersection" , std::bit_and<>{},
245+ [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::intersection (g1, g2, g); return g; },
246+ test_points, grid, settings, failures);
247+ test_op (bits1, bits2, geo1, geo2, " sym_difference" , std::bit_xor<>{},
248+ [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::sym_difference (g1, g2, g); return g; },
249+ test_points, grid, settings, failures);
250+ test_op (bits1, bits2, geo1, geo2, " difference g1 \\ g2" ,
251+ [](std::bitset<chunk_size> b1, std::bitset<chunk_size> b2) { return b1 & (~b2); },
252+ [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::difference (g1, g2, g); return g; },
253+ test_points, grid, settings, failures);
254+ test_op (bits1, bits2, geo1, geo2, " difference g2 \\ g1" ,
255+ [](std::bitset<chunk_size> b1, std::bitset<chunk_size> b2) { return b2 & (~b1); },
256+ [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::difference (g2, g1, g); return g; },
257+ test_points, grid, settings, failures);
258+ }
259+
189260bool test_all (grid_settings const & settings)
190261{
191262 generator_t genenerator (settings.seed );
192263 const auto grid = grid_cells (settings);
193264 const auto points = test_points (settings);
194- bool all_success = true ;
265+ std::map<std::string, int > failures;
266+ auto const t0 = std::chrono::high_resolution_clock::now ();
195267 for (int i = 0 ; i < settings.count || settings.count == -1 ; i++)
196268 {
197- const bits bits1 = gen_bits (genenerator, settings.width * settings.height );
198- const bits bits2 = gen_bits (genenerator, settings.width * settings.height );
199- const auto geo1 = bits_to_geometry (bits1, grid, points, settings, all_success);
200- const auto geo2 = bits_to_geometry (bits2, grid, points, settings, all_success);
201- test_op (bits1, bits2, geo1, geo2, " union" , std::bit_or<>{},
202- [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::union_ (g1, g2, g); return g; },
203- points, grid, settings, all_success);
204- test_op (bits1, bits2, geo1, geo2, " intersection" , std::bit_and<>{},
205- [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::intersection (g1, g2, g); return g; },
206- points, grid, settings, all_success);
207- test_op (bits1, bits2, geo1, geo2, " sym_difference" , std::bit_xor<>{},
208- [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::sym_difference (g1, g2, g); return g; },
209- points, grid, settings, all_success);
210- test_op (bits1, bits2, geo1, geo2, " difference g1 \\ g2" ,
211- [](std::bitset<chunk_size> b1, std::bitset<chunk_size> b2) { return b1 & (~b2); },
212- [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::difference (g1, g2, g); return g; },
213- points, grid, settings, all_success);
214- test_op (bits1, bits2, geo1, geo2, " difference g2 \\ g1" ,
215- [](std::bitset<chunk_size> b1, std::bitset<chunk_size> b2) { return b2 & (~b1); },
216- [](mp_t const & g1, mp_t const & g2) { mp_t g; bg::difference (g2, g1, g); return g; },
217- points, grid, settings, all_success);
269+ const bits bits1 = settings.bits1 .size () == 0 ?
270+ gen_bits (genenerator, settings.width * settings.height )
271+ : to_bits (settings.bits1 );
272+ const bits bits2 = settings.bits2 .size () == 0 ?
273+ gen_bits (genenerator, settings.width * settings.height )
274+ : to_bits (settings.bits2 );
275+ test_bits (bits1, bits2, grid, points, settings, failures);
276+ }
277+ auto const t = std::chrono::high_resolution_clock::now ();
278+ auto const elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t - t0).count ();
279+ int failure_count = std::accumulate (failures.begin (), failures.end (), 0 ,
280+ [](int acc, auto const & kv) { return acc + kv.second ; });
281+ std::cout << " \n iterations: " << settings.count
282+ << " errors: " << failure_count
283+ << " time: " << elapsed_ms/1000 << ' \n ' ;
284+ if (failure_count != 0 )
285+ {
286+ std::cout << " Failure counts by failure mode:\n " ;
287+ for (auto const & fm : failures) std::cout << ' \t ' << fm.first << " : " << fm.second << ' \n ' ;
288+ }
289+ return failure_count != 0 ;
290+ }
291+
292+ bool validate_bits_input (std::vector<std::uint64_t > const & bits_in, size_t bits_size)
293+ {
294+ if (bits_in.size () == 0 ) return true ;
295+ if (bits_in.size () != (bits_size + chunk_size - 1 ) / chunk_size) return false ;
296+ if (bits_size % chunk_size != 0 )
297+ {
298+ std::bitset<chunk_size> bm;
299+ bm.set ();
300+ bm >>= chunk_size - bits_size % chunk_size;
301+ if (bits_in.back () & ~bm.to_ullong ()) return false ;
218302 }
219- return all_success ;
303+ return true ;
220304}
221305
222306int main (int argc, char ** argv)
@@ -243,12 +327,36 @@ int main(int argc, char** argv)
243327 (" height" ,
244328 po::value<decltype (settings.height )>(&settings.height )->default_value (settings.height ),
245329 " Height of grid (>= 1)" )
330+ (" verbose" ,
331+ po::bool_switch (&settings.verbose ),
332+ " Print WKT and bit patterns for each failure." )
333+ (" bits1" ,
334+ po::value<decltype (settings.bits1 )>(&settings.bits1 )->multitoken (),
335+ " Fixed bit pattern for first operand as list of ullong." )
336+ (" bits2" ,
337+ po::value<decltype (settings.bits2 )>(&settings.bits2 )->multitoken (),
338+ " Fixed bit pattern for second operand as list of ullong." )
246339 ;
247340
248341 po::variables_map varmap;
249342 po::store (po::parse_command_line (argc, argv, description), varmap);
250343 po::notify (varmap);
251344
345+ if (! validate_bits_input (settings.bits1 , settings.height * settings.width ))
346+ {
347+ std::cout << " bits1 was provided but does not match dimensions.\n " ;
348+ return 1 ;
349+ }
350+ if (! validate_bits_input (settings.bits2 , settings.height * settings.width ))
351+ {
352+ std::cout << " bits2 was provided but does not match dimensions.\n " ;
353+ return 1 ;
354+ }
355+ if ( settings.bits1 .size () != 0 && settings.bits2 .size () != 0 && settings.count != 1 )
356+ {
357+ std::cout << " Both bit patterns fixed, count is changed to 1.\n " ;
358+ settings.count = 1 ;
359+ }
252360 if (settings.height < 1 || settings.width < 1 )
253361 {
254362 std::cout << " Invalid dimensions, height and width need to be positive.\n " ;
0 commit comments