diff --git a/src/db/dbOASISWriter.cc b/src/db/dbOASISWriter.cc index bbfc31e76..379f22f8d 100644 --- a/src/db/dbOASISWriter.cc +++ b/src/db/dbOASISWriter.cc @@ -1792,7 +1792,7 @@ OASISWriter::write (const Repetition &rep) x = -x; } if (x != 0) { - g = (g == 0) ? x : tl::lcd (g, x); + g = (g == 0) ? x : tl::gcd (g, x); } db::Coord y = safe_scale (m_sf, p->y ()); @@ -1800,7 +1800,7 @@ OASISWriter::write (const Repetition &rep) y = -y; } if (y != 0) { - g = (g == 0) ? y : tl::lcd (g, y); + g = (g == 0) ? y : tl::gcd (g, y); } } diff --git a/src/ext/extXORToolDialog.cc b/src/ext/extXORToolDialog.cc index 4bc2b2c8c..0e05b7a8b 100644 --- a/src/ext/extXORToolDialog.cc +++ b/src/ext/extXORToolDialog.cc @@ -31,6 +31,7 @@ #include "tlProgress.h" #include "tlThreadedWorkers.h" #include "tlExceptions.h" +#include "tlMath.h" #include "layCellView.h" #include "layLayoutView.h" #include "layApplication.h" @@ -1181,7 +1182,12 @@ XORToolDialog::run_xor () } XORJob job (nworkers, output_mode, op, el_handling, dbu, cva, cvb, tolerances, sub_categories, layer_categories, sub_cells, sub_output_layers, rdb, rdb_cell); - for (std::vector::const_iterator box = boxes.begin (); box != boxes.end (); ++box) { + double common_dbu = tl::lcm (cva->layout ().dbu (), cvb->layout ().dbu ()); + + for (std::vector::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + + db::DBox box (tl::round_down (b->left (), common_dbu), tl::round_down (b->bottom (), common_dbu), + tl::round_up (b->right (), common_dbu), tl::round_up (b->top (), common_dbu)); // compute the tiles if required db::Box box_a, box_b, box_out; @@ -1189,33 +1195,34 @@ XORToolDialog::run_xor () db::Coord box_width_b = 0, box_height_b = 0, box_height_out = 0; size_t ntiles_w = 1, ntiles_h = 1; - if (box->empty ()) { + if (box.empty ()) { ntiles_w = ntiles_h = 0; - } else if (tile_size > 0.0) { - - box_a = db::Box (*box * (1.0 / cva->layout ().dbu ())); - box_b = db::Box (*box * (1.0 / cvb->layout ().dbu ())); - box_out = db::Box (*box * (1.0 / dbu)); - - ntiles_w = std::max (size_t (1), size_t (floor (box->width () / tile_size + 0.5))); - ntiles_h = std::max (size_t (1), size_t (floor (box->height () / tile_size + 0.5))); - - box_width_a = box_a.width () / ntiles_w; - box_height_a = box_a.height () / ntiles_h; - - box_width_b = box_b.width () / ntiles_w; - box_height_b = box_b.height () / ntiles_h; - - box_width_out = box_out.width () / ntiles_w; - box_height_out = box_out.height () / ntiles_h; - } else { - box_a = db::Box (*box * (1.0 / cva->layout ().dbu ())); - box_b = db::Box (*box * (1.0 / cvb->layout ().dbu ())); - box_out = db::Box (*box * (1.0 / dbu)); + box_a = db::Box (box * (1.0 / cva->layout ().dbu ())); + box_b = db::Box (box * (1.0 / cvb->layout ().dbu ())); + box_out = db::Box (box * (1.0 / dbu)); + + if (tile_size > 0.0) { + + ntiles_w = std::max (size_t (1), size_t (floor (box.width () / tile_size + 0.5))); + ntiles_h = std::max (size_t (1), size_t (floor (box.height () / tile_size + 0.5))); + + double box_width = tl::round_up (box.width () / ntiles_w, common_dbu); + double box_height = tl::round_up (box.height () / ntiles_h, common_dbu); + + box_width_a = db::coord_traits::rounded (box_width / cva->layout ().dbu ()); + box_height_a = db::coord_traits::rounded (box_height / cva->layout ().dbu ()); + + box_width_b = db::coord_traits::rounded (box_width / cvb->layout ().dbu ()); + box_height_b = db::coord_traits::rounded (box_height / cvb->layout ().dbu ()); + + box_width_out = db::coord_traits::rounded (box_width / dbu); + box_height_out = db::coord_traits::rounded (box_height / dbu); + + } } diff --git a/src/tl/tlMath.h b/src/tl/tlMath.h index 77e4d1bcf..8ad8afd73 100644 --- a/src/tl/tlMath.h +++ b/src/tl/tlMath.h @@ -26,30 +26,116 @@ #include "tlAssert.h" +#include + namespace tl { /** - * @brief Compute the largest common divider of two numbers using the euclidian method + * @brief A generic less operator */ template -T lcd (T a, T b) +bool less (T a, T b) { - while (true) { - if (a < b) { - b %= a; - if (b == 0) { - return a; - } - } else if (b < a) { - a %= b; - if (a == 0) { - return b; - } - } else { - return a; - } + return a < b; +} + +/** + * @brief A generic equal operator + */ +template +bool equal (T a, T b) +{ + return a == b; +} + +/** + * @brief A generalization of the modulo operator + */ +template +T modulo (T a, T b) +{ + return a % b; +} + +/** + * @brief A common uncertainty value for double compares + * This implementation uses an uncertainty value of 1e-10 + * which is suitable for values in the order of 1. + */ +const double epsilon = 1e-10; + +/** + * @brief A specialization for double values + */ +bool less (double a, double b) +{ + return a < b - tl::epsilon; +} + +/** + * @brief A specialization for double values + */ +bool equal (double a, double b) +{ + return fabs (a - b) < tl::epsilon; +} + +/** + * @brief A specialization of the modulo operator for doubles + * a % b == a - b * floor (a / b) + */ +double modulo (double a, double b) +{ + return a - b * floor (a / b + tl::epsilon); +} + +/** + * @brief Compute the greatest common divider of two numbers using the euclidian method + */ +template +T gcd (T a, T b) +{ + while (! equal (b, T (0))) { + T h = modulo (a, b); + a = b; + b = h; } + return a; +} + +/** + * @brief Compute the lowest common multiple of two numbers using the euclidian method + */ +template +T lcm (T a, T b) +{ + return a * (b / gcd (a, b)); +} + +/** + * @brief Rounding down to the closest multiple of g + */ +double round_down (double x, double g) +{ + return g * floor (x / g + tl::epsilon); +} + +/** + * @brief Rounding up to the closest multiple of g + */ +double round_up (double x, double g) +{ + return g * ceil (x / g - tl::epsilon); +} + +/** + * @brief Rounding to the closest multiple of g + * A value of (n+1/2)*g is rounded down. + */ +double round (double x, double g) +{ + return g * floor (0.5 + x / g - tl::epsilon); } } diff --git a/src/unit_tests/tlMath.cc b/src/unit_tests/tlMath.cc index 50f47c23b..32806225e 100644 --- a/src/unit_tests/tlMath.cc +++ b/src/unit_tests/tlMath.cc @@ -26,15 +26,62 @@ TEST(1) { int x; - x = tl::lcd (17, 6); + x = tl::gcd (17, 6); EXPECT_EQ (x, 1); - x = tl::lcd (27, 6); + x = tl::gcd (27, 6); EXPECT_EQ (x, 3); - x = tl::lcd (30, 6); + x = tl::gcd (30, 6); EXPECT_EQ (x, 6); - x = tl::lcd (31*17, 371*17); + x = tl::gcd (31*17, 371*17); EXPECT_EQ (x, 17); - x = tl::lcd (702*17, 372*17); + x = tl::gcd (702*17, 372*17); EXPECT_EQ (x, 102); + + EXPECT_EQ (tl::less (100, 200), true); + EXPECT_EQ (tl::less (200, 100), false); + EXPECT_EQ (tl::less (100, 100), false); + EXPECT_EQ (tl::equal (200, 100), false); + EXPECT_EQ (tl::equal (100, 100), true); + + EXPECT_EQ (tl::less (0.1, 0.2), true); + EXPECT_EQ (tl::less (0.2, 0.1), false); + EXPECT_EQ (tl::less (0.1, 0.1), false); + EXPECT_EQ (tl::less (0.1, 0.1 + 1e-7), true); + EXPECT_EQ (tl::less (0.1, 0.1 + 1e-10), false); + EXPECT_EQ (tl::equal (0.2, 0.1), false); + EXPECT_EQ (tl::equal (0.1, 0.1), true); + EXPECT_EQ (tl::equal (0.1, 0.1 + 1e-7), false); + EXPECT_EQ (tl::equal (0.1, 0.1 + 1e-12), true); + + double d; + d = tl::gcd (702*1.7, 372*1.7); + EXPECT_EQ (tl::equal (d, 10.2), true); + d = tl::gcd (0.0025, 0.001); + EXPECT_EQ (tl::equal (d, 0.0005), true); + d = tl::gcd (0.0025, 0.001 + 1e-12); + EXPECT_EQ (tl::equal (d, 0.0005), true); + + d = tl::lcm (0.0025, 0.001); + EXPECT_EQ (tl::equal (d, 0.005), true); + d = tl::lcm (0.0025, 0.001 + 1e-12); + EXPECT_EQ (tl::equal (d, 0.005), true); + + EXPECT_EQ (tl::equal (tl::round_down (1.3, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round_down (-1.3, 1.0), -2.0), true); + EXPECT_EQ (tl::equal (tl::round_down (1.0 + 1e-7, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round_down (1.0 - 1e-7, 1.0), 0.0), true); + EXPECT_EQ (tl::equal (tl::round_down (1.0 - 1e-12, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round_up (1.3, 1.0), 2.0), true); + EXPECT_EQ (tl::equal (tl::round_up (-1.3, 1.0), -1.0), true); + EXPECT_EQ (tl::equal (tl::round_up (1.0 - 1e-7, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round_up (1.0 + 1e-7, 1.0), 2.0), true); + EXPECT_EQ (tl::equal (tl::round_up (1.0 + 1e-12, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round (1.3, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round (1.5, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round (1.5 + 1e-12, 1.0), 1.0), true); + EXPECT_EQ (tl::equal (tl::round (1.5 + 1e-7, 1.0), 2.0), true); + EXPECT_EQ (tl::equal (tl::round (1.7, 1.0), 2.0), true); + EXPECT_EQ (tl::equal (tl::round (-1.3, 1.0), -1.0), true); + EXPECT_EQ (tl::equal (tl::round (-1.7, 1.0), -2.0), true); }