
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2017 Matthias Koefferlein

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#include "utHead.h"
#include "dbLayoutDiff.h"
#include "dbLayerProperties.h"
#include "dbLayout.h"

#include <sstream>

class TestDifferenceReceiver
  : public db::DifferenceReceiver
{
public:
  TestDifferenceReceiver ();
  virtual ~TestDifferenceReceiver () { }

  std::string text () const { return m_os.str (); }
  void clear () { m_os.str (std::string ()); }
  void dbu_differs (double dbu_a, double dbu_b);
  void layer_in_a_only (const db::LayerProperties &la);
  void layer_in_b_only (const db::LayerProperties &lb);
  void layer_name_differs (const db::LayerProperties &la, const db::LayerProperties &lb);
  void cell_in_a_only (const std::string &cellname, db::cell_index_type ci);
  void cell_in_b_only (const std::string &cellname, db::cell_index_type ci);
  void cell_name_differs (const std::string &cellname_a, db::cell_index_type cia, const std::string &cellname_b, db::cell_index_type cib);
  void begin_cell (const std::string &cellname, db::cell_index_type cia, db::cell_index_type cib);
  void bbox_differs (const db::Box &ba, const db::Box &bb);
  void begin_inst_differences ();
  void instances_in_a (const std::vector <db::CellInstArrayWithProperties> &insts_a, const std::vector <std::string> &cell_names, const db::PropertiesRepository &props);
  void instances_in_b (const std::vector <db::CellInstArrayWithProperties> &insts_b, const std::vector <std::string> &cell_names, const db::PropertiesRepository &props);
  void instances_in_a_only (const std::vector <db::CellInstArrayWithProperties> &anotb, const db::Layout &a);
  void instances_in_b_only (const std::vector <db::CellInstArrayWithProperties> &bnota, const db::Layout &b);
  void end_inst_differences ();
  void begin_layer (const db::LayerProperties &layer, unsigned int layer_index_a, bool is_valid_a, unsigned int layer_index_b, bool is_valid_b);
  void per_layer_bbox_differs (const db::Box &ba, const db::Box &bb);
  void begin_polygon_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &a, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &b);
  void end_polygon_differences ();
  void begin_path_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Path, db::properties_id_type> > &a, const std::vector <std::pair <db::Path, db::properties_id_type> > &b);
  void end_path_differences ();
  void begin_box_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Box, db::properties_id_type> > &a, const std::vector <std::pair <db::Box, db::properties_id_type> > &b);
  void end_box_differences ();
  void begin_edge_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b);
  void end_edge_differences ();
  void begin_text_differences ();
  void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &b);
  void end_text_differences ();
  void end_layer ();
  void end_cell ();

private:
  std::string m_cellname;
  std::ostringstream m_os;
  db::LayerProperties m_layer;

  void print_cell_inst (const db::CellInstArrayWithProperties &ci, const std::vector <std::string> &cell_names);
  void print_cell_inst (const db::CellInstArrayWithProperties &ci, const db::Layout &l);
  template <class SH> void print_diffs (const db::PropertiesRepository &pr, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b);
};

TestDifferenceReceiver::TestDifferenceReceiver ()
  : m_cellname (), m_layer ()
{
  // .. nothing yet ..
}

void 
TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties &ci, const db::Layout &l)
{
  m_os << "  " << l.cell_name (ci.object ().cell_index ()) << " " << ci.complex_trans ().to_string ();

  db::Vector a, b;
  unsigned long amax, bmax;
  if (ci.is_regular_array (a, b, amax, bmax)) {
    m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]";
  }
  if (ci.properties_id () != 0) {
    m_os << " [" << ci.properties_id () << "]" << std::endl;
  }
}

void 
TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties &ci, const std::vector <std::string> &cell_names)
{
  m_os << "  " << cell_names [ci.object ().cell_index ()] << " " << ci.complex_trans ().to_string ();

  db::Vector a, b;
  unsigned long amax, bmax;
  if (ci.is_regular_array (a, b, amax, bmax)) {
    m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]";
  }
  if (ci.properties_id () != 0) {
    m_os << " [" << ci.properties_id () << "]" << std::endl;
  } else {
    m_os << "" << std::endl;
  }
}

template <class SH>
void
TestDifferenceReceiver::print_diffs (const db::PropertiesRepository & /*pr*/, const std::vector <std::pair <SH, db::properties_id_type> > &a, const std::vector <std::pair <SH, db::properties_id_type> > &b)
{
  std::vector <std::pair <SH, db::properties_id_type> > anotb;
  std::set_difference (a.begin (), a.end (), b.begin (), b.end (), std::back_inserter (anotb));
  for (typename std::vector <std::pair <SH, db::properties_id_type> >::const_iterator s = anotb.begin (); s != anotb.end (); ++s) {
    m_os << "  " << s->first.to_string ();
    if (s->second != 0) {
      m_os << " [" << s->second << "]" << std::endl;
    } else {
      m_os << "" << std::endl;
    }
  }
}

void
TestDifferenceReceiver::dbu_differs (double dbu_a, double dbu_b) 
{
  m_os << "layout_diff: database units differ " << dbu_a << " vs. " << dbu_b << std::endl;
}

void 
TestDifferenceReceiver::layer_in_a_only (const db::LayerProperties &la)
{
  m_os << "layout_diff: layer " << la.to_string () << " is not present in layout b, but in a" << std::endl;
}

void 
TestDifferenceReceiver::layer_in_b_only (const db::LayerProperties &lb)
{
  m_os << "layout_diff: layer " << lb.to_string () << " is not present in layout a, but in b" << std::endl;
}

void
TestDifferenceReceiver::layer_name_differs (const db::LayerProperties &la, const db::LayerProperties &lb)
{
  m_os << "layout_diff: layer names differ between layout a and b for layer " << la.layer << "/" << la.datatype << ": " 
       << la.name << " vs. " << lb.name << std::endl;
}

void 
TestDifferenceReceiver::cell_in_a_only (const std::string &cellname, db::cell_index_type /*ci*/)
{
  m_os << "layout_diff: cell " << cellname << " is not present in layout b, but in a" << std::endl;
}

void 
TestDifferenceReceiver::cell_in_b_only (const std::string &cellname, db::cell_index_type /*ci*/)
{
  m_os << "layout_diff: cell " << cellname << " is not present in layout a, but in b" << std::endl;
}

void 
TestDifferenceReceiver::cell_name_differs (const std::string &cellname_a, db::cell_index_type /*cia*/, const std::string &cellname_b, db::cell_index_type /*cib*/)
{
  m_os << "layout_diff: cell " << cellname_a << " in a is renamed to " << cellname_b << " in b" << std::endl;
}

void 
TestDifferenceReceiver::bbox_differs (const db::Box &ba, const db::Box &bb)
{
  m_os << "layout_diff: bounding boxes differ for cell " << m_cellname << ", " << ba.to_string () << " vs. " << bb.to_string () << std::endl;
}

void 
TestDifferenceReceiver::begin_cell (const std::string &cellname, db::cell_index_type /*cia*/, db::cell_index_type /*cib*/)
{
  m_cellname = cellname;
}

void
TestDifferenceReceiver::begin_inst_differences ()
{
  m_os << "layout_diff: instances differ in cell " << m_cellname << std::endl;
}

void
TestDifferenceReceiver::instances_in_a (const std::vector <db::CellInstArrayWithProperties> &insts_a, const std::vector <std::string> &cell_names, const db::PropertiesRepository & /*props*/)
{
  m_os << "list for a:" << std::endl;
  for (std::vector <db::CellInstArrayWithProperties>::const_iterator s = insts_a.begin (); s != insts_a.end (); ++s) {
    print_cell_inst (*s, cell_names);
  }
}

void
TestDifferenceReceiver::instances_in_b (const std::vector <db::CellInstArrayWithProperties> &insts_b, const std::vector <std::string> &cell_names, const db::PropertiesRepository & /*props*/)
{
  m_os << "list for b:" << std::endl;
  for (std::vector <db::CellInstArrayWithProperties>::const_iterator s = insts_b.begin (); s != insts_b.end (); ++s) {
    print_cell_inst (*s, cell_names);
  }
}

void
TestDifferenceReceiver::instances_in_a_only (const std::vector <db::CellInstArrayWithProperties> &anotb, const db::Layout &a)
{
  m_os << "Not in b but in a:" << std::endl;
  for (std::vector <db::CellInstArrayWithProperties>::const_iterator s = anotb.begin (); s != anotb.end (); ++s) {
    print_cell_inst (*s, a);
  }
}

void
TestDifferenceReceiver::instances_in_b_only (const std::vector <db::CellInstArrayWithProperties> &bnota, const db::Layout &b)
{
  m_os << "Not in a but in b:" << std::endl;
  for (std::vector <db::CellInstArrayWithProperties>::const_iterator s = bnota.begin (); s != bnota.end (); ++s) {
    print_cell_inst (*s, b);
  }
}

void
TestDifferenceReceiver::end_inst_differences ()
{
}

void
TestDifferenceReceiver::begin_layer (const db::LayerProperties &layer, unsigned int /*layer_index_a*/, bool /*is_valid_a*/, unsigned int /*layer_index_b*/, bool /*is_valid_b*/)
{
  m_layer = layer;
}

void 
TestDifferenceReceiver::per_layer_bbox_differs (const db::Box &ba, const db::Box &bb)
{
  m_os << "layout_diff: per-layer bounding boxes differ for cell " << m_cellname << ", layer (" << m_layer.to_string () << "), " 
       << ba.to_string () << " vs. " << bb.to_string () << std::endl;
}

void
TestDifferenceReceiver::begin_polygon_differences ()
{
  m_os << "layout_diff: polygons differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
}

void
TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &a, const std::vector <std::pair <db::Polygon, db::properties_id_type> > &b)
{
  m_os << "Not in b but in a:" << std::endl;
  print_diffs (pr, a, b);
  m_os << "Not in a but in b:" << std::endl;
  print_diffs (pr, b, a);
}

void
TestDifferenceReceiver::end_polygon_differences ()
{
}

void
TestDifferenceReceiver::begin_path_differences ()
{
  m_os << "layout_diff: paths differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
}

void
TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Path, db::properties_id_type> > &a, const std::vector <std::pair <db::Path, db::properties_id_type> > &b)
{
  m_os << "Not in b but in a:" << std::endl;
  print_diffs (pr, a, b);
  m_os << "Not in a but in b:" << std::endl;
  print_diffs (pr, b, a);
}

void
TestDifferenceReceiver::end_path_differences ()
{
}

void
TestDifferenceReceiver::begin_box_differences ()
{
  m_os << "layout_diff: boxes differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
}

void
TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Box, db::properties_id_type> > &a, const std::vector <std::pair <db::Box, db::properties_id_type> > &b)
{
  m_os << "Not in b but in a:" << std::endl;
  print_diffs (pr, a, b);
  m_os << "Not in a but in b:" << std::endl;
  print_diffs (pr, b, a);
}

void
TestDifferenceReceiver::end_box_differences ()
{
}

void
TestDifferenceReceiver::begin_edge_differences ()
{
  m_os << "layout_diff: edges differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
}

void
TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b)
{
  m_os << "Not in b but in a:" << std::endl;
  print_diffs (pr, a, b);
  m_os << "Not in a but in b:" << std::endl;
  print_diffs (pr, b, a);
}

void
TestDifferenceReceiver::end_edge_differences ()
{
}

void
TestDifferenceReceiver::begin_text_differences ()
{
  m_os << "layout_diff: texts differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
}

void
TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &b)
{
  m_os << "Not in b but in a:" << std::endl;
  print_diffs (pr, a, b);
  m_os << "Not in a but in b:" << std::endl;
  print_diffs (pr, b, a);
}

void
TestDifferenceReceiver::end_text_differences ()
{
}

void
TestDifferenceReceiver::end_layer ()
{
}

void 
TestDifferenceReceiver::end_cell ()
{
}

TEST(1) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;

  TestDifferenceReceiver r;
  bool eq;
  
  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, true);
  EXPECT_EQ (r.text (), "");

  {
    //  c2->c5 (2x)
    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);
    g.cell (c2i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5i), t));
    g.cell (c2i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5i), tt));
  }

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), "layout_diff: instances differ in cell c2x\n");

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: instances differ in cell c2x\n"
    "list for a:\n"
    "  c1 r0 *1 10,-20\n"
    "  c1 m45 *1 -10,20\n"
    "  c4 m45 *1 -10,20\n"
    "  c4 m45 *1 -10,20\n"
    "  c5x r0 *1 10,-20\n"
    "  c5x m45 *1 -10,20\n"
    "list for b:\n"
    "  c1 r0 *1 10,-20\n"
    "  c1 m45 *1 -10,20\n"
    "  c4 m45 *1 -10,20\n"
    "  c4 m45 *1 -10,20\n"
    "Not in b but in a:\n"
    "  c5x r0 *1 10,-20  c5x m45 *1 -10,20Not in a but in b:\n"
  );

  g = h;
  g.set_properties (1, db::LayerProperties (42, 2));

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: layer 42/2 is not present in layout b, but in a\n"
    "layout_diff: layer 42/1 is not present in layout a, but in b\n"
  );

  g = h;
  g.rename_cell (c2i, "c2");

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: cell c2 is not present in layout b, but in a\n"
    "layout_diff: cell c2x is not present in layout a, but in b\n"
  );
}

TEST(2) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;

  TestDifferenceReceiver r;
  bool eq;
  
  g.cell (c2i).shapes (0).insert (db::Box (1, 2, 1003, 1004));

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: bounding boxes differ for cell c2x, (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: per-layer bounding boxes differ for cell c2x, layer (17/0), (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004)\n"
    "Not in a but in b:\n"
  );

  db::Layout hh = h;

  db::Cell &c2h = h.cell (c2i);
  c2h.shapes (0).insert (db::Box (1, 2, 1003, 1005));
  c2h.shapes (0).insert (db::Box (2, 2, 1003, 1004));
  c2h.shapes (0).insert (db::Box (1, 2, 1003, 1006));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004)\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1005)\n"
    "  (1,2;1003,1006)\n"
    "  (2,2;1003,1004)\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (2,2;1003,1004)\n"
    "  (1,2;1003,1006)\n"
  );
}

std::string ps2string (const db::PropertiesRepository &rep, db::properties_id_type pi)
{
  std::string s;
  db::PropertiesRepository::properties_set ps = rep.properties (pi);
  for (db::PropertiesRepository::properties_set::const_iterator i = ps.begin (); i != ps.end (); ++i) {
    if (!s.empty ()) {
      s += ",";
    }
    s += rep.prop_name (i->first).to_string ();
    s += ":";
    s += i->second.to_string ();
  }
  return s;
}

TEST(2P) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  {
    db::properties_id_type pi1, pi2, pi3;

    db::PropertiesRepository::properties_set ps;
    ps.insert (std::make_pair (g.properties_repository ().prop_name_id ("A"), tl::Variant (1)));
    pi1 = g.properties_repository ().properties_id (ps);
    EXPECT_EQ (pi1, 1);

    ps.clear ();
    ps.insert (std::make_pair (g.properties_repository ().prop_name_id ("B"), tl::Variant (2)));
    pi2 = g.properties_repository ().properties_id (ps);
    EXPECT_EQ (pi2, 2);

    ps.insert (std::make_pair (g.properties_repository ().prop_name_id ("C"), tl::Variant ("c")));
    pi3 = g.properties_repository ().properties_id (ps);
    EXPECT_EQ (pi3, 3);

    EXPECT_EQ (ps2string (g.properties_repository (), 1), "A:1");
    EXPECT_EQ (ps2string (g.properties_repository (), 2), "B:2");
    EXPECT_EQ (ps2string (g.properties_repository (), 3), "B:2,C:c");
  }

  db::Layout h = g;

  {
    EXPECT_EQ (ps2string (h.properties_repository (), 1), "A:1");
    EXPECT_EQ (ps2string (h.properties_repository (), 2), "B:2");
    EXPECT_EQ (ps2string (h.properties_repository (), 3), "B:2,C:c");
  }

  TestDifferenceReceiver r;
  bool eq;
  
  g.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1004), 1));

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: bounding boxes differ for cell c2x, (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: per-layer bounding boxes differ for cell c2x, layer (17/0), (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004) [1]\n"
    "Not in a but in b:\n"
  );

  db::Layout hh = h;

  //  Note: properties are "normalized" (mapped to a common layout). In order to maintain
  //  their meaning later, keep these inserts sorted by property ID:
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1006), 1));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1005), 2));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (2, 2, 1003, 1004), 3));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004) [1]\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1006) [1]\n"
    "  (1,2;1003,1005) [2]\n"
    "  (2,2;1003,1004) [3]\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004) [1]\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1006) [1]\n"
    "  (1,2;1003,1005) [2]\n"
    "  (2,2;1003,1004) [3]\n"
  );

  h = hh;

  {
    EXPECT_EQ (ps2string (h.properties_repository (), 1), "A:1");
    EXPECT_EQ (ps2string (h.properties_repository (), 2), "B:2");
    EXPECT_EQ (ps2string (h.properties_repository (), 3), "B:2,C:c");
  }

  //  Note: properties are "normalized" (mapped to a common layout). In order to maintain
  //  their meaning later, keep these inserts sorted by property ID:
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1005), 1));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1006), 2));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (2, 2, 1003, 1004), 3));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004) [1]\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1005) [1]\n"
    "  (1,2;1003,1006) [2]\n"
    "  (2,2;1003,1004) [3]\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1006) [2]\n"
    "  (2,2;1003,1004) [3]\n"
  );

  h = hh;

  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (2, 2, 1003, 1004), 1));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1006), 2));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1005), 3));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1006) [2]\n"
    "  (1,2;1003,1005) [3]\n"
  );

  h = hh;

  h.cell (c2i).shapes (0).insert (db::Box (2, 2, 1003, 1004));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1006), 1));
  h.cell (c2i).shapes (0).insert (db::BoxWithProperties (db::Box (1, 2, 1003, 1005), 1));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004) [1]\n"
    "Not in a but in b:\n"
    "  (2,2;1003,1004)\n"
    "  (1,2;1003,1006) [1]\n"
    "  (1,2;1003,1005) [1]\n"
  );
}

TEST(3) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;

  TestDifferenceReceiver r;
  bool eq;
  
  g.cell (c2i).shapes (0).insert (db::Polygon (db::Box (1, 2, 1003, 1004)));

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: bounding boxes differ for cell c2x, (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: per-layer bounding boxes differ for cell c2x, layer (17/0), (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: polygons differ for layer 17/0 in cell c2x\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: polygons differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1,1004;1003,1004;1003,2)\n"
    "Not in a but in b:\n"
  );

  db::Layout hh = h;

  db::Cell &c2h = h.cell (c2i);
  c2h.shapes (0).insert (db::Polygon (db::Box (1, 2, 1003, 1005)));
  c2h.shapes (0).insert (db::Polygon (db::Box (2, 2, 1003, 1004)));
  c2h.shapes (0).insert (db::Polygon (db::Box (1, 2, 1003, 1006)));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: polygons differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1,1004;1003,1004;1003,2)\n"
    "Not in a but in b:\n"
    "  (1,2;1,1005;1003,1005;1003,2)\n"
    "  (1,2;1,1006;1003,1006;1003,2)\n"
    "  (2,2;2,1004;1003,1004;1003,2)\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: polygons differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (2,2;2,1004;1003,1004;1003,2)\n"
    "  (1,2;1,1006;1003,1006;1003,2)\n"
  );
}

TEST(4) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;

  TestDifferenceReceiver r;
  bool eq;
  
  g.cell (c2i).shapes (0).insert (db::Edge (1, 2, 1003, 1004));

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: bounding boxes differ for cell c2x, (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: per-layer bounding boxes differ for cell c2x, layer (17/0), (0,1;1003,1004) vs. (0,1;2,3)\n"
    "layout_diff: edges differ for layer 17/0 in cell c2x\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: edges differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004)\n"
    "Not in a but in b:\n"
  );

  db::Layout hh = h;

  db::Cell &c2h = h.cell (c2i);
  c2h.shapes (0).insert (db::Edge (1, 2, 1003, 1005));
  c2h.shapes (0).insert (db::Edge (2, 2, 1003, 1004));
  c2h.shapes (0).insert (db::Edge (1, 2, 1003, 1006));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: edges differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;1003,1004)\n"
    "Not in a but in b:\n"
    "  (1,2;1003,1005)\n"
    "  (1,2;1003,1006)\n"
    "  (2,2;1003,1004)\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: edges differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (2,2;1003,1004)\n"
    "  (1,2;1003,1006)\n"
  );
}

TEST(5) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;

  TestDifferenceReceiver r;
  bool eq;
  
  db::Text t;
  t = db::Text ("X", db::Trans (1, db::Vector (2, 3)), 17);
  g.cell (c2i).shapes (0).insert (t);

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: texts differ for layer 17/0 in cell c2x\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: texts differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  ('X',r90 2,3)\n"
    "Not in a but in b:\n"
  );

  db::Layout hh = h;
  hh.cell (c2i).shapes (0).insert (t);

  r.clear ();
  eq = db::compare_layouts (g, hh, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, true);
  EXPECT_EQ (r.text (), "");

  hh = h;

  db::Cell &c2h = h.cell (c2i);
  c2h.shapes (0).insert (db::Text ("Y", db::Trans (1, db::Vector (2, 3)), 17));
  c2h.shapes (0).insert (db::Text ("X", db::Trans (2, db::Vector (2, 3)), 17));
  c2h.shapes (0).insert (db::Text ("X", db::Trans (1, db::Vector (3, 4)), 17));
  c2h.shapes (0).insert (db::Text ("X", db::Trans (1, db::Vector (2, 3)), 18));
  //  Text attributes like font and alignment are not compared, hence this text matches the one of g:
  c2h.shapes (0).insert (db::Text ("X", db::Trans (1, db::Vector (2, 3)), 17, db::DefaultFont, db::HAlignCenter, db::VAlignCenter));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: texts differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  ('X',r90 3,4)\n"
    "  ('X',r90 2,3)\n"
    "  ('X',r180 2,3)\n"
    "  ('Y',r90 2,3)\n"
  );

  //  two more to match more of h:
  g.cell (c2i).shapes (0).insert (t);
  g.cell (c2i).shapes (0).insert (t);

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: texts differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  ('X',r180 2,3)\n"
    "  ('Y',r90 2,3)\n"
  );
}

TEST(6) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;

  TestDifferenceReceiver r;
  bool eq;
  
  db::Point pts1[] = { db::Point (1, 2), db::Point (11, 12) };
  db::Point pts2[] = { db::Point (1, 3), db::Point (11, 12) };
  db::Point pts3[] = { db::Point (1, 3), db::Point (11, 11) };

  db::Path p;
  p = db::Path (&pts1[0], &pts1[sizeof (pts1) / sizeof (pts1[0])], 17, 0, 0, false);
  g.cell (c2i).shapes (0).insert (p);

  r.clear ();
  eq = db::compare_layouts (g, h, 0, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: bounding boxes differ for cell c2x, (-5,-4;17,18) vs. (0,1;2,3)\n"
    "layout_diff: per-layer bounding boxes differ for cell c2x, layer (17/0), (-5,-4;17,18) vs. (0,1;2,3)\n"
    "layout_diff: paths differ for layer 17/0 in cell c2x\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: paths differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (1,2;11,12) w=17 bx=0 ex=0 r=false\n"
    "Not in a but in b:\n"
  );

  db::Layout hh = h;
  hh.cell (c2i).shapes (0).insert (p);

  r.clear ();
  eq = db::compare_layouts (g, hh, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, true);
  EXPECT_EQ (r.text (), "");

  hh = h;

  db::Cell &c2h = h.cell (c2i);
  c2h.shapes (0).insert (db::Path (&pts1[0], &pts1[sizeof (pts1) / sizeof (pts1[0])], 18, 0, 0, false));
  c2h.shapes (0).insert (db::Path (&pts1[0], &pts1[sizeof (pts1) / sizeof (pts1[0])], 17, 1, 0, false));
  c2h.shapes (0).insert (db::Path (&pts1[0], &pts1[sizeof (pts1) / sizeof (pts1[0])], 17, 0, -1, false));
  c2h.shapes (0).insert (db::Path (&pts1[0], &pts1[sizeof (pts1) / sizeof (pts1[0])], 17, 0, 0, true));
  c2h.shapes (0).insert (db::Path (&pts2[0], &pts2[sizeof (pts2) / sizeof (pts2[0])], 17, 0, 0, false));
  c2h.shapes (0).insert (db::Path (&pts3[0], &pts3[sizeof (pts3) / sizeof (pts3[0])], 17, 0, 0, false));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: paths differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (1,2;11,12) w=17 bx=0 ex=-1 r=false\n"
    "  (1,3;11,11) w=17 bx=0 ex=0 r=false\n"
    "  (1,2;11,12) w=17 bx=0 ex=0 r=true\n"
    "  (1,2;11,12) w=17 bx=1 ex=0 r=false\n"
    "  (1,2;11,12) w=18 bx=0 ex=0 r=false\n"
  );

  //  two more to match more of h:
  g.cell (c2i).shapes (0).insert (p);
  g.cell (c2i).shapes (0).insert (p);
  g.cell (c2i).shapes (0).insert (p);
  g.cell (c2i).shapes (0).insert (p);

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: paths differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "Not in a but in b:\n"
    "  (1,2;11,12) w=17 bx=0 ex=0 r=true\n"
  );
}

TEST(7) 
{
  db::Layout g;
  g.insert_layer (0);
  g.set_properties (0, db::LayerProperties (17, 0));
  g.insert_layer (1);
  g.set_properties (1, db::LayerProperties (42, 1));

  db::cell_index_type c1i = g.add_cell ("c1");
  db::cell_index_type c2i = g.add_cell ("c2x");
  db::cell_index_type c3i = g.add_cell ("c3");
  db::cell_index_type c4i = g.add_cell ("c4");
  db::cell_index_type c5i = g.add_cell ("c5x");

  {

    db::Cell &c1 (g.cell (c1i));
    db::Cell &c2 (g.cell (c2i));
    db::Cell &c3 (g.cell (c3i));
    db::Cell &c4 (g.cell (c4i));
    db::Cell &c5 (g.cell (c5i));
    c2.shapes (0).insert (db::Box (0, 1, 2, 3));

    db::FTrans f (1, true);
    db::Vector p (-10, 20);
    db::Trans t (f.rot (), p);
    db::Vector pp (10, -20);
    db::Trans tt (0, pp);

    //  c4->c1 (aref)
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
    //  c5->c1
    c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    //  c3->c5 (3x)
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
    c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
    //  c4->c3
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
    //  c4->c1
    c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c1 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
    //  c2->c4 (2x)
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
    c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));

  }

  db::Layout h = g;
  db::Layout hh = g;
  db::Layout hhh = g;

  TestDifferenceReceiver r;
  bool eq;
  
  g.cell (c2i).shapes (0).insert (db::Box (1, 2, 1003, 1004));
  g.cell (c2i).shapes (0).insert (db::Box (2, 3, 1004, 1005));
  g.cell (c2i).shapes (0).insert (db::Box (3, 4, 1005, 1006));
  g.cell (c2i).shapes (0).insert (db::Box (4, 5, 1006, 1007));
  g.cell (c2i).shapes (0).insert (db::Box (5, 6, 1007, 1008));
  g.cell (c2i).shapes (0).insert (db::Box (3, 7, 1008, 1009));
  g.cell (c2i).shapes (0).insert (db::Box (3, 8, 1009, 1010));

  h.cell (c2i).shapes (0).insert (db::Box (3, 8, 1009, 1010));
  h.cell (c2i).shapes (0).insert (db::Box (3, 7, 1008, 1009));
  h.cell (c2i).shapes (0).insert (db::Box (5, 6, 1007, 1008));
  h.cell (c2i).shapes (0).insert (db::Box (4, 5, 1006, 1009));
  h.cell (c2i).shapes (0).insert (db::Box (3, 4, 1005, 1006));
  h.cell (c2i).shapes (0).insert (db::Box (2, 3, 1004, 1005));
  h.cell (c2i).shapes (0).insert (db::Box (1, 2, 1003, 1004));

  hh.cell (c2i).shapes (0).insert (db::Box (3, 8, 1009, 1010));
  hh.cell (c2i).shapes (0).insert (db::Box (3, 7, 1008, 1009));
  hh.cell (c2i).shapes (0).insert (db::Box (5, 6, 1007, 1008));
  hh.cell (c2i).shapes (0).insert (db::Box (4, 5, 1006, 1007));
  hh.cell (c2i).shapes (0).insert (db::Box (3, 4, 1005, 1006));
  hh.cell (c2i).shapes (0).insert (db::Box (2, 3, 1004, 1005));
  hh.cell (c2i).shapes (0).insert (db::Box (1, 2, 1003, 1004));

  hhh.cell (c2i).shapes (0).insert (db::Box (3, 8, 1009, 1010));
  hhh.cell (c2i).shapes (0).insert (db::Box (3, 7, 1008, 1009));
  hhh.cell (c2i).shapes (0).insert (db::Box (5, 6, 1007, 1008));
  hhh.cell (c2i).shapes (0).insert (db::Box (4, 5, 1006, 1008));
  hhh.cell (c2i).shapes (0).insert (db::Box (3, 4, 1005, 1006));
  hhh.cell (c2i).shapes (0).insert (db::Box (2, 3, 1004, 1005));
  hhh.cell (c2i).shapes (0).insert (db::Box (1, 2, 1003, 1004));

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (4,5;1006,1007)\n"
    "Not in a but in b:\n"
    "  (4,5;1006,1009)\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (),  
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (4,5;1006,1007)\n"
    "Not in a but in b:\n"
    "  (4,5;1006,1009)\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, hh, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, true);
  EXPECT_EQ (r.text (), "");

  r.clear ();
  eq = db::compare_layouts (g, hh, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, true);
  EXPECT_EQ (r.text (), "");

  r.clear ();
  eq = db::compare_layouts (g, hhh, db::layout_diff::f_verbose, 0, r); 

  EXPECT_EQ (eq, false);
  EXPECT_EQ (r.text (), 
    "layout_diff: boxes differ for layer 17/0 in cell c2x\n"
    "Not in b but in a:\n"
    "  (4,5;1006,1007)\n"
    "Not in a but in b:\n"
    "  (4,5;1006,1008)\n"
  );

  r.clear ();
  eq = db::compare_layouts (g, hhh, db::layout_diff::f_verbose, 1, r); 

  EXPECT_EQ (eq, true);
  EXPECT_EQ (r.text (), "");
}


