TEST(RWMoleculeTest, setAtomPosition3d)
{
  Molecule m;
  RWMolecule mol(m);
  mol.addAtom(1);
  mol.addAtom(2);
  mol.addAtom(3);
  mol.addAtom(4);
  mol.addAtom(5);
  mol.undoStack().clear();

  EXPECT_TRUE(mol.atomPositions3d().empty());
  mol.setAtomPosition3d(0, Vector3(Real(1), Real(2), Real(3)));
  EXPECT_EQ(mol.atomicNumbers().size(), mol.atomPositions3d().size());
  EXPECT_EQ(Real(1), mol.atomPosition3d(0).x());
  EXPECT_EQ(Real(2), mol.atomPosition3d(0).y());
  EXPECT_EQ(Real(3), mol.atomPosition3d(0).z());
  for (Index i = 1; i < 5; ++i)
    EXPECT_EQ(Vector3::Zero(), mol.atomPosition3d(i));

  mol.undoStack().undo();
  for (Index i = 0; i < 5; ++i)
    EXPECT_EQ(Vector3::Zero(), mol.atomPosition3d(i));

  mol.undoStack().redo();
  EXPECT_EQ(Real(1), mol.atomPosition3d(0).x());
  EXPECT_EQ(Real(2), mol.atomPosition3d(0).y());
  EXPECT_EQ(Real(3), mol.atomPosition3d(0).z());
  for (Index i = 1; i < 5; ++i)
    EXPECT_EQ(Vector3::Zero(), mol.atomPosition3d(i));

  mol.undoStack().undo();
  mol.undoStack().clear();

  // Test command merging for interactive editing:
  mol.setInteractive(true);
  mol.setAtomPosition3d(0, Vector3(Real(1), Real(2), Real(3)));
  mol.setAtomPosition3d(3, Vector3(Real(4), Real(5), Real(6)));
  mol.setAtomPosition3d(0, Vector3(Real(7), Real(8), Real(9)));
  mol.setAtomPosition3d(1, Vector3(Real(6), Real(4), Real(2)));
  mol.setInteractive(false);

  Array<Vector3> pos(mol.atomPositions3d());
  EXPECT_EQ(Vector3(Real(7), Real(8), Real(9)), pos[0]);
  EXPECT_EQ(Vector3(Real(6), Real(4), Real(2)), pos[1]);
  EXPECT_EQ(Vector3::Zero(), pos[2]);
  EXPECT_EQ(Vector3(Real(4), Real(5), Real(6)), pos[3]);
  EXPECT_EQ(Vector3::Zero(), pos[4]);

  EXPECT_EQ(1, mol.undoStack().count());
  mol.undoStack().undo();
  for (Index i = 1; i < 5; ++i)
    EXPECT_EQ(Vector3::Zero(), mol.atomPosition3d(i));
  mol.undoStack().redo();
  EXPECT_TRUE(std::equal(pos.begin(), pos.end(),
                         mol.atomPositions3d().begin()));
}
TEST(RWMoleculeTest, AtomType)
{
  Molecule m;
  RWMolecule mol(m);
  typedef RWMolecule::AtomType Atom;
  Atom a0 = mol.addAtom(1);
  Atom a1 = mol.addAtom(2);

  EXPECT_TRUE(a0.isValid());
  EXPECT_FALSE(Atom().isValid());
  EXPECT_FALSE(Atom(&mol, 2).isValid());

  EXPECT_EQ(&mol, a0.molecule());
  EXPECT_EQ(0, a0.index());

  EXPECT_EQ(1, a0.atomicNumber());
  EXPECT_EQ(1, mol.atomicNumber(0));
  EXPECT_EQ(2, a1.atomicNumber());
  EXPECT_EQ(2, mol.atomicNumber(1));

  a0.setPosition3d(Vector3(Real(3), Real(4), Real(5)));
  a1.setPosition3d(Vector3(Real(6), Real(7), Real(8)));

  EXPECT_EQ(Vector3(Real(3), Real(4), Real(5)), a0.position3d());
  EXPECT_EQ(Vector3(Real(3), Real(4), Real(5)), mol.atomPosition3d(0));
  EXPECT_EQ(Vector3(Real(6), Real(7), Real(8)), a1.position3d());
  EXPECT_EQ(Vector3(Real(6), Real(7), Real(8)), mol.atomPosition3d(1));

  Atom other(&mol, 0);
  EXPECT_EQ(a0, other);
  EXPECT_NE(a1, other);
}
TEST(RWMoleculeTest, clearAtoms)
{
  Molecule m;
  RWMolecule mol(m);
  typedef RWMolecule::AtomType Atom;

  Atom a0 = mol.addAtom(1); // H
  Atom a1 = mol.addAtom(2); // He
  Atom a2 = mol.addAtom(3); // Li
  Atom a3 = mol.addAtom(4); // Be
  Atom a4 = mol.addAtom(5); // B

  const Vector3 pos(Real(1), Real(2), Real(3));
  mol.setAtomPosition3d(0, pos);

  ASSERT_EQ(5, mol.atomCount());
  ASSERT_EQ(std::string("HHeLiBeB"), formula(mol));

  // Add some bonds to ensure that they are properly added/removed when a bonded
  // atom is removed.
  ASSERT_TRUE(mol.addBond(a0, a1, 0).isValid());
  ASSERT_TRUE(mol.addBond(a1, a2, 1).isValid());
  ASSERT_TRUE(mol.addBond(a2, a3, 2).isValid());
  ASSERT_TRUE(mol.addBond(a3, a4, 3).isValid());
  ASSERT_TRUE(mol.addBond(a0, a2, 4).isValid());
  ASSERT_TRUE(mol.addBond(a1, a3, 5).isValid());
  ASSERT_TRUE(mol.addBond(a2, a4, 6).isValid());
  ASSERT_TRUE(mol.addBond(a0, a3, 7).isValid());
  ASSERT_TRUE(mol.addBond(a1, a4, 8).isValid());
  ASSERT_TRUE(mol.addBond(a0, a4, 9).isValid());

  ASSERT_EQ(10, mol.bondCount());

  mol.clearAtoms();

  EXPECT_EQ(0, mol.atomCount());
  EXPECT_EQ(0, mol.bondCount());

  mol.undoStack().undo();

  ASSERT_EQ(5, mol.atomCount());
  ASSERT_EQ(10, mol.bondCount());
  ASSERT_EQ(std::string("HHeLiBeB"), formula(mol));

  for (Index i = 0; i < mol.atomCount(); ++i) {
    EXPECT_EQ(static_cast<unsigned char>(i + 1), mol.atomicNumber(i));
    EXPECT_EQ(i, mol.atomUniqueId(i));
  }

#define VALIDATE_BOND(ind, atom1, atom2, order, uid) \
  EXPECT_EQ(std::make_pair(Index(atom1), Index(atom2)), mol.bondPair(ind)); \
  EXPECT_EQ(static_cast<unsigned char>(order), mol.bondOrder(ind)); \
  EXPECT_EQ(uid, mol.bondUniqueId(ind))

  VALIDATE_BOND(0, 0, 1, 0, 0);
  VALIDATE_BOND(1, 1, 2, 1, 1);
  VALIDATE_BOND(2, 2, 3, 2, 2);
  VALIDATE_BOND(3, 3, 4, 3, 3);
  VALIDATE_BOND(4, 0, 2, 4, 4);
  VALIDATE_BOND(5, 1, 3, 5, 5);
  VALIDATE_BOND(6, 2, 4, 6, 6);
  VALIDATE_BOND(7, 0, 3, 7, 7);
  VALIDATE_BOND(8, 1, 4, 8, 8);
  VALIDATE_BOND(9, 0, 4, 9, 9);
#undef VALIDATE_BOND
}