diff --git a/CMakeLists.txt b/CMakeLists.txt
index 530ae96..38e4fc9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,7 +25,7 @@
 cmake_minimum_required(VERSION 3.16)
 
 # set the project name
-project(cifpp VERSION 2.0.5 LANGUAGES CXX)
+project(cifpp VERSION 3.0.2 LANGUAGES CXX)
 
 list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 
@@ -43,6 +43,9 @@ set(CXX_EXTENSIONS OFF)
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+# https://stackoverflow.com/questions/63902528/program-crashes-when-filesystempath-is-destroyed
+find_package(Filesystem REQUIRED)
+
 if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
 	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
 elseif(MSVC)
diff --git a/changelog b/changelog
index d01d27b..2e6d957 100644
--- a/changelog
+++ b/changelog
@@ -1,5 +1,21 @@
-Version 2.0.5
-- Backporting updated CMakeLists.txt file
+Version 3.0.3
+- Better configuration checks, for atomic e.g.
+- Fixed a problem introduced in refactoring mmcif::Atom
+- Version string creation
+
+Version 3.0.2
+- refactored mmcif::Atom for performance reasons
+
+Version 3.0.1
+- Fixed processing of proline restraints file from CCP4, proline
+  is a peptide, really.
+- Added code to facilitate DSSP
+
+Version 3.0.0
+- Replaced many strings in the API with string_view for
+  performance reasons.
+- Upgraded mmcif::Structure
+- various other small fixes
 
 Version 2.0.4
 - Reverted a too strict test when reading cif files.
diff --git a/cmake/FindFilesystem.cmake b/cmake/FindFilesystem.cmake
index 21c98f2..e778394 100644
--- a/cmake/FindFilesystem.cmake
+++ b/cmake/FindFilesystem.cmake
@@ -59,8 +59,10 @@ if(_found)
 		# Nothing to add...
 	elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
 		set_target_properties(std::filesystem PROPERTIES IMPORTED_LIBNAME stdc++fs)
+		set(STDCPPFS_LIBRARY stdc++fs)
 	elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
 		set_target_properties(std::filesystem PROPERTIES IMPORTED_LIBNAME c++fs)
+		set(STDCPPFS_LIBRARY c++fs)
 	endif()
 endif()
 
diff --git a/include/cif++/AtomType.hpp b/include/cif++/AtomType.hpp
index 8c4284d..500412a 100644
--- a/include/cif++/AtomType.hpp
+++ b/include/cif++/AtomType.hpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -29,150 +29,151 @@
 #pragma once
 
 #include <cstdint>
-#include <string>
 #include <stdexcept>
+#include <string>
 
 namespace mmcif
 {
 
 enum AtomType : uint8_t
 {
-	Nn = 0,		// Unknown
-	
-	H = 1,		// Hydro­gen
-	He = 2,		// He­lium
-
-	Li = 3,		// Lith­ium
-	Be = 4,		// Beryl­lium
-	B = 5,		// Boron
-	C = 6,		// Carbon
-	N = 7,		// Nitro­gen
-	O = 8,		// Oxy­gen
-	F = 9,		// Fluor­ine
-	Ne = 10,	// Neon
-
-	Na = 11,	// So­dium
-	Mg = 12,	// Magne­sium
-	Al = 13,	// Alumin­ium
-	Si = 14,	// Sili­con
-	P = 15,		// Phos­phorus
-	S = 16,		// Sulfur
-	Cl = 17,	// Chlor­ine
-	Ar = 18,	// Argon
-
-	K = 19,		// Potas­sium
-	Ca = 20,	// Cal­cium
-	Sc = 21,	// Scan­dium
-	Ti = 22,	// Tita­nium
-	V = 23,		// Vana­dium
-	Cr = 24,	// Chrom­ium
-	Mn = 25,	// Manga­nese
-	Fe = 26,	// Iron
-	Co = 27,	// Cobalt
-	Ni = 28,	// Nickel
-	Cu = 29,	// Copper
-	Zn = 30,	// Zinc
-	Ga = 31,	// Gallium
-	Ge = 32,	// Germa­nium
-	As = 33,	// Arsenic
-	Se = 34,	// Sele­nium
-	Br = 35,	// Bromine
-	Kr = 36,	// Kryp­ton
-
-	Rb = 37,	// Rubid­ium
-	Sr = 38,	// Stront­ium
-	Y = 39,		// Yttrium
-	Zr = 40,	// Zirco­nium
-	Nb = 41,	// Nio­bium
-	Mo = 42,	// Molyb­denum
-	Tc = 43,	// Tech­netium
-	Ru = 44,	// Ruthe­nium
-	Rh = 45,	// Rho­dium
-	Pd = 46,	// Pallad­ium
-	Ag = 47,	// Silver
-	Cd = 48,	// Cad­mium
-	In = 49,	// Indium
-	Sn = 50,	// Tin
-	Sb = 51,	// Anti­mony
-	Te = 52,	// Tellurium
-	I = 53,		// Iodine
-	Xe = 54,	// Xenon
-	Cs = 55,	// Cae­sium
-	Ba = 56,	// Ba­rium
-	La = 57,	// Lan­thanum
-
-	Hf = 72,	// Haf­nium
-	Ta = 73,	// Tanta­lum
-	W = 74,		// Tung­sten
-	Re = 75,	// Rhe­nium
-	Os = 76,	// Os­mium
-	Ir = 77,	// Iridium
-	Pt = 78,	// Plat­inum
-	Au = 79,	// Gold
-	Hg = 80,	// Mer­cury
-	Tl = 81,	// Thallium
-	Pb = 82,	// Lead
-	Bi = 83,	// Bis­muth
-	Po = 84,	// Polo­nium
-	At = 85,	// Asta­tine
-	Rn = 86,	// Radon
-	Fr = 87,	// Fran­cium
-	Ra = 88,	// Ra­dium
-	Ac = 89,	// Actin­ium
-
-	Rf = 104,	// Ruther­fordium
-	Db = 105,	// Dub­nium
-	Sg = 106,	// Sea­borgium
-	Bh = 107,	// Bohr­ium
-	Hs = 108,	// Has­sium
-	Mt = 109,	// Meit­nerium
-	Ds = 110,	// Darm­stadtium
-	Rg = 111,	// Roent­genium
-	Cn = 112,	// Coper­nicium
-	Nh = 113,	// Nihon­ium
-	Fl = 114,	// Flerov­ium
-	Mc = 115,	// Moscov­ium
-	Lv = 116,	// Liver­morium
-	Ts = 117,	// Tenness­ine
-	Og = 118,	// Oga­nesson
-
-	Ce = 58,	// Cerium
-	Pr = 59,	// Praseo­dymium
-	Nd = 60,	// Neo­dymium
-	Pm = 61,	// Prome­thium
-	Sm = 62,	// Sama­rium
-	Eu = 63,	// Europ­ium
-	Gd = 64,	// Gadolin­ium
-	Tb = 65,	// Ter­bium
-	Dy = 66,	// Dyspro­sium
-	Ho = 67,	// Hol­mium
-	Er = 68,	// Erbium
-	Tm = 69,	// Thulium
-	Yb = 70,	// Ytter­bium
-	Lu = 71,	// Lute­tium
-
-	Th = 90,	// Thor­ium
-	Pa = 91,	// Protac­tinium
-	U = 92,		// Ura­nium
-	Np = 93,	// Neptu­nium
-	Pu = 94,	// Pluto­nium
-	Am = 95,	// Ameri­cium
-	Cm = 96,	// Curium
-	Bk = 97,	// Berkel­ium
-	Cf = 98,	// Califor­nium
-	Es = 99,	// Einstei­nium
-	Fm = 100,	// Fer­mium
-	Md = 101,	// Mende­levium
-	No = 102,	// Nobel­ium
-	Lr = 103,	// Lawren­cium
-
-	D = 129,	// Deuterium
+	Nn = 0, // Unknown
+
+	H = 1,  // Hydro­gen
+	He = 2, // He­lium
+
+	Li = 3,  // Lith­ium
+	Be = 4,  // Beryl­lium
+	B = 5,   // Boron
+	C = 6,   // Carbon
+	N = 7,   // Nitro­gen
+	O = 8,   // Oxy­gen
+	F = 9,   // Fluor­ine
+	Ne = 10, // Neon
+
+	Na = 11, // So­dium
+	Mg = 12, // Magne­sium
+	Al = 13, // Alumin­ium
+	Si = 14, // Sili­con
+	P = 15,  // Phos­phorus
+	S = 16,  // Sulfur
+	Cl = 17, // Chlor­ine
+	Ar = 18, // Argon
+
+	K = 19,  // Potas­sium
+	Ca = 20, // Cal­cium
+	Sc = 21, // Scan­dium
+	Ti = 22, // Tita­nium
+	V = 23,  // Vana­dium
+	Cr = 24, // Chrom­ium
+	Mn = 25, // Manga­nese
+	Fe = 26, // Iron
+	Co = 27, // Cobalt
+	Ni = 28, // Nickel
+	Cu = 29, // Copper
+	Zn = 30, // Zinc
+	Ga = 31, // Gallium
+	Ge = 32, // Germa­nium
+	As = 33, // Arsenic
+	Se = 34, // Sele­nium
+	Br = 35, // Bromine
+	Kr = 36, // Kryp­ton
+
+	Rb = 37, // Rubid­ium
+	Sr = 38, // Stront­ium
+	Y = 39,  // Yttrium
+	Zr = 40, // Zirco­nium
+	Nb = 41, // Nio­bium
+	Mo = 42, // Molyb­denum
+	Tc = 43, // Tech­netium
+	Ru = 44, // Ruthe­nium
+	Rh = 45, // Rho­dium
+	Pd = 46, // Pallad­ium
+	Ag = 47, // Silver
+	Cd = 48, // Cad­mium
+	In = 49, // Indium
+	Sn = 50, // Tin
+	Sb = 51, // Anti­mony
+	Te = 52, // Tellurium
+	I = 53,  // Iodine
+	Xe = 54, // Xenon
+	Cs = 55, // Cae­sium
+	Ba = 56, // Ba­rium
+	La = 57, // Lan­thanum
+
+	Hf = 72, // Haf­nium
+	Ta = 73, // Tanta­lum
+	W = 74,  // Tung­sten
+	Re = 75, // Rhe­nium
+	Os = 76, // Os­mium
+	Ir = 77, // Iridium
+	Pt = 78, // Plat­inum
+	Au = 79, // Gold
+	Hg = 80, // Mer­cury
+	Tl = 81, // Thallium
+	Pb = 82, // Lead
+	Bi = 83, // Bis­muth
+	Po = 84, // Polo­nium
+	At = 85, // Asta­tine
+	Rn = 86, // Radon
+	Fr = 87, // Fran­cium
+	Ra = 88, // Ra­dium
+	Ac = 89, // Actin­ium
+
+	Rf = 104, // Ruther­fordium
+	Db = 105, // Dub­nium
+	Sg = 106, // Sea­borgium
+	Bh = 107, // Bohr­ium
+	Hs = 108, // Has­sium
+	Mt = 109, // Meit­nerium
+	Ds = 110, // Darm­stadtium
+	Rg = 111, // Roent­genium
+	Cn = 112, // Coper­nicium
+	Nh = 113, // Nihon­ium
+	Fl = 114, // Flerov­ium
+	Mc = 115, // Moscov­ium
+	Lv = 116, // Liver­morium
+	Ts = 117, // Tenness­ine
+	Og = 118, // Oga­nesson
+
+	Ce = 58, // Cerium
+	Pr = 59, // Praseo­dymium
+	Nd = 60, // Neo­dymium
+	Pm = 61, // Prome­thium
+	Sm = 62, // Sama­rium
+	Eu = 63, // Europ­ium
+	Gd = 64, // Gadolin­ium
+	Tb = 65, // Ter­bium
+	Dy = 66, // Dyspro­sium
+	Ho = 67, // Hol­mium
+	Er = 68, // Erbium
+	Tm = 69, // Thulium
+	Yb = 70, // Ytter­bium
+	Lu = 71, // Lute­tium
+
+	Th = 90,  // Thor­ium
+	Pa = 91,  // Protac­tinium
+	U = 92,   // Ura­nium
+	Np = 93,  // Neptu­nium
+	Pu = 94,  // Pluto­nium
+	Am = 95,  // Ameri­cium
+	Cm = 96,  // Curium
+	Bk = 97,  // Berkel­ium
+	Cf = 98,  // Califor­nium
+	Es = 99,  // Einstei­nium
+	Fm = 100, // Fer­mium
+	Md = 101, // Mende­levium
+	No = 102, // Nobel­ium
+	Lr = 103, // Lawren­cium
+
+	D = 129, // Deuterium
 };
 
 // --------------------------------------------------------------------
 // AtomTypeInfo
 
-enum RadiusType {
+enum RadiusType
+{
 	eRadiusCalculated,
 	eRadiusEmpirical,
 	eRadiusCovalentEmpirical,
@@ -188,12 +189,12 @@ enum RadiusType {
 
 struct AtomTypeInfo
 {
-	AtomType		type;
-	std::string		name;
-	std::string		symbol;
-	float			weight;
-	bool			metal;
-	float			radii[eRadiusTypeCount];
+	AtomType type;
+	std::string name;
+	std::string symbol;
+	float weight;
+	bool metal;
+	float radii[eRadiusTypeCount];
 };
 
 extern const AtomTypeInfo kKnownAtoms[];
@@ -205,25 +206,25 @@ class AtomTypeTraits
 {
   public:
 	AtomTypeTraits(AtomType a);
-	AtomTypeTraits(const std::string& symbol);
-	
-	AtomType type() const			{ return mInfo->type; }
-	std::string	name() const		{ return mInfo->name; }
-	std::string	symbol() const		{ return mInfo->symbol; }
-	float weight() const			{ return mInfo->weight; }
-	
-	bool isMetal() const			{ return mInfo->metal; }
-	
-	static bool isElement(const std::string& symbol);
-	static bool isMetal(const std::string& symbol);
-	
+	AtomTypeTraits(const std::string &symbol);
+
+	AtomType type() const { return mInfo->type; }
+	std::string name() const { return mInfo->name; }
+	std::string symbol() const { return mInfo->symbol; }
+	float weight() const { return mInfo->weight; }
+
+	bool isMetal() const { return mInfo->metal; }
+
+	static bool isElement(const std::string &symbol);
+	static bool isMetal(const std::string &symbol);
+
 	float radius(RadiusType type = eRadiusSingleBond) const
 	{
 		if (type >= eRadiusTypeCount)
 			throw std::invalid_argument("invalid radius requested");
 		return mInfo->radii[type] / 100.f;
 	}
-	
+
 	// data type encapsulating the Waasmaier & Kirfel scattering factors
 	// in a simplified form (only a and b).
 	// Added the electrion scattering factors as well
@@ -231,15 +232,18 @@ class AtomTypeTraits
 	{
 		double a[6], b[6];
 	};
-	
+
 	// to get the Cval and Siva values, use this constant as charge:
-	enum { kWKSFVal = -99 };
-	
-	const SFData& wksf(int charge = 0) const;
-	const SFData& elsf() const;
+	enum
+	{
+		kWKSFVal = -99
+	};
+
+	const SFData &wksf(int charge = 0) const;
+	const SFData &elsf() const;
 
   private:
-	const struct AtomTypeInfo*	mInfo;
+	const struct AtomTypeInfo *mInfo;
 };
 
-}
+} // namespace mmcif
diff --git a/include/cif++/BondMap.hpp b/include/cif++/BondMap.hpp
index 76d9bb2..f4a108c 100644
--- a/include/cif++/BondMap.hpp
+++ b/include/cif++/BondMap.hpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -26,9 +26,9 @@
 
 #pragma once
 
-#include <unordered_map>
 #include <filesystem>
 #include <stdexcept>
+#include <unordered_map>
 
 #include "cif++/Structure.hpp"
 
@@ -38,39 +38,40 @@ namespace mmcif
 class BondMapException : public std::runtime_error
 {
   public:
-	BondMapException(const std::string& msg)
-		: runtime_error(msg) {}
+	BondMapException(const std::string &msg)
+		: runtime_error(msg)
+	{
+	}
 };
 
 class BondMap
 {
   public:
-	BondMap(const Structure& p);
-	
-	BondMap(const BondMap&) = delete;
-	BondMap& operator=(const BondMap&) = delete;
+	BondMap(const Structure &p);
+
+	BondMap(const BondMap &) = delete;
+	BondMap &operator=(const BondMap &) = delete;
 
-	bool operator()(const Atom& a, const Atom& b) const
+	bool operator()(const Atom &a, const Atom &b) const
 	{
 		return isBonded(index.at(a.id()), index.at(b.id()));
 	}
 
-	bool is1_4(const Atom& a, const Atom& b) const
+	bool is1_4(const Atom &a, const Atom &b) const
 	{
 		uint32_t ixa = index.at(a.id());
 		uint32_t ixb = index.at(b.id());
-	
+
 		return bond_1_4.count(key(ixa, ixb));
 	}
-	
+
 	// links coming from the struct_conn records:
-	std::vector<std::string> linked(const Atom& a) const;
+	std::vector<std::string> linked(const Atom &a) const;
 
 	// This list of atomID's is comming from either CCD or the CCP4 dictionaries loaded
-	static std::vector<std::string> atomIDsForCompound(const std::string& compoundID);
-	
-  private:
+	static std::vector<std::string> atomIDsForCompound(const std::string &compoundID);
 
+  private:
 	bool isBonded(uint32_t ai, uint32_t bi) const
 	{
 		return bond.count(key(ai, bi)) != 0;
@@ -82,20 +83,19 @@ class BondMap
 			std::swap(a, b);
 		return static_cast<uint64_t>(a) | (static_cast<uint64_t>(b) << 32);
 	}
-	
-	std::tuple<uint32_t,uint32_t> dekey(uint64_t k) const
+
+	std::tuple<uint32_t, uint32_t> dekey(uint64_t k) const
 	{
 		return std::make_tuple(
 			static_cast<uint32_t>(k >> 32),
-			static_cast<uint32_t>(k)
-		);
+			static_cast<uint32_t>(k));
 	}
-	
+
 	uint32_t dim;
-	std::unordered_map<std::string,uint32_t> index;
+	std::unordered_map<std::string, uint32_t> index;
 	std::set<uint64_t> bond, bond_1_4;
 
-	std::map<std::string,std::set<std::string>> link;
+	std::map<std::string, std::set<std::string>> link;
 };
 
-}
+} // namespace mmcif
diff --git a/include/cif++/Cif++.hpp b/include/cif++/Cif++.hpp
index 42b0135..5a454dd 100644
--- a/include/cif++/Cif++.hpp
+++ b/include/cif++/Cif++.hpp
@@ -36,6 +36,10 @@
 #include <regex>
 #include <set>
 #include <sstream>
+#include <iomanip>
+#include <shared_mutex>
+
+#include <boost/format.hpp>
 
 #include "cif++/CifUtils.hpp"
 
@@ -141,14 +145,27 @@ class Item
   public:
 	Item() {}
 
+	Item(std::string_view name, char value)
+		: mName(name)
+		, mValue({ value })
+	{
+	}
+
+	template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
+	Item(std::string_view name, const T& value, const char *fmt)
+		: mName(name)
+		, mValue((boost::format(fmt) % value).str())
+	{
+	}
+
 	template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
-	Item(const std::string &name, const T &value)
+	Item(const std::string_view name, const T &value)
 		: mName(name)
 		, mValue(std::to_string(value))
 	{
 	}
 
-	Item(const std::string &name, const std::string &value)
+	Item(const std::string_view name, const std::string_view value)
 		: mName(name)
 		, mValue(value)
 	{
@@ -221,7 +238,7 @@ class Datablock
 	using iterator = CategoryList::iterator;
 	using const_iterator = CategoryList::const_iterator;
 
-	Datablock(const std::string &name);
+	Datablock(const std::string_view name);
 	~Datablock();
 
 	Datablock(const Datablock &) = delete;
@@ -230,33 +247,31 @@ class Datablock
 	std::string getName() const { return mName; }
 	void setName(const std::string &n) { mName = n; }
 
-	std::string firstItem(const std::string &tag) const;
-
 	iterator begin() { return mCategories.begin(); }
 	iterator end() { return mCategories.end(); }
 
 	const_iterator begin() const { return mCategories.begin(); }
 	const_iterator end() const { return mCategories.end(); }
 
-	Category &operator[](const std::string &name);
+	Category &operator[](std::string_view name);
 
-	std::tuple<iterator, bool> emplace(const std::string &name);
+	std::tuple<iterator, bool> emplace(std::string_view name);
 
 	bool isValid();
 	void validateLinks() const;
 
-	void setValidator(Validator *v);
+	void setValidator(const Validator *v);
 
 	// this one only looks up a Category, returns nullptr if it does not exist
-	const Category *get(const std::string &name) const;
-	Category *get(const std::string &name);
+	const Category *get(std::string_view name) const;
+	Category *get(std::string_view name);
 
 	void getTagOrder(std::vector<std::string> &tags) const;
 	void write(std::ostream &os, const std::vector<std::string> &order);
 	void write(std::ostream &os);
 
 	// convenience function, add a line to the software category
-	void add_software(const std::string &name, const std::string &classification,
+	void add_software(const std::string_view name, const std::string &classification,
 		const std::string &versionNr, const std::string &versionDate);
 
 	friend bool operator==(const Datablock &lhs, const Datablock &rhs);
@@ -264,9 +279,10 @@ class Datablock
 	friend std::ostream& operator<<(std::ostream &os, const Datablock &data);
 
   private:
-	std::list<Category> mCategories;
+	CategoryList mCategories;		// LRU
+	mutable std::shared_mutex mLock;
 	std::string mName;
-	Validator *mValidator;
+	const Validator *mValidator;
 	Datablock *mNext;
 };
 
@@ -350,14 +366,14 @@ namespace detail
 	  private:
 		friend class ::cif::Row;
 
-		ItemReference(const char *name, size_t column, Row &row)
+		ItemReference(std::string_view name, size_t column, Row &row)
 			: mName(name)
 			, mColumn(column)
 			, mRow(row)
 		{
 		}
 
-		ItemReference(const char *name, size_t column, const Row &row)
+		ItemReference(std::string_view name, size_t column, const Row &row)
 			: mName(name)
 			, mColumn(column)
 			, mRow(const_cast<Row &>(row))
@@ -365,7 +381,7 @@ namespace detail
 		{
 		}
 
-		const char *mName;
+		std::string_view mName;
 		size_t mColumn;
 		Row &mRow;
 		bool mConst = false;
@@ -746,16 +762,16 @@ class Row
 		return detail::ItemReference(itemTag, column, *this);
 	}
 
-	const detail::ItemReference operator[](const std::string &itemTag) const
+	const detail::ItemReference operator[](std::string_view itemTag) const
 	{
-		size_t column = ColumnForItemTag(itemTag.c_str());
-		return detail::ItemReference(itemTag.c_str(), column, *this);
+		size_t column = ColumnForItemTag(itemTag);
+		return detail::ItemReference(itemTag, column, *this);
 	}
 
-	detail::ItemReference operator[](const std::string &itemTag)
+	detail::ItemReference operator[](std::string_view itemTag)
 	{
-		size_t column = ColumnForItemTag(itemTag.c_str());
-		return detail::ItemReference(itemTag.c_str(), column, *this);
+		size_t column = ColumnForItemTag(itemTag);
+		return detail::ItemReference(itemTag, column, *this);
 	}
 
 	template <typename... Ts, size_t N>
@@ -776,7 +792,7 @@ class Row
 	}
 
 	void assign(const std::vector<Item> &values);
-	void assign(const std::string &name, const std::string &value, bool updateLinked);
+	void assign(std::string_view name, const std::string &value, bool updateLinked, bool validate = true);
 
 	bool operator==(const Row &rhs) const
 	{
@@ -798,12 +814,12 @@ class Row
 	friend std::ostream &operator<<(std::ostream &os, const Row &row);
 
   private:
-	void assign(size_t column, const std::string &value, bool updateLinked);
+	void assign(size_t column, const std::string &value, bool updateLinked, bool validate = true);
 	void assign(const Item &i, bool updateLinked);
 
 	static void swap(size_t column, ItemRow *a, ItemRow *b);
 
-	size_t ColumnForItemTag(const char *itemTag) const;
+	size_t ColumnForItemTag(std::string_view itemTag) const;
 
 	mutable ItemRow *mData;
 	uint32_t mLineNr = 0;
@@ -1169,11 +1185,11 @@ struct Empty
 
 struct Key
 {
-	Key(const std::string &itemTag)
+	explicit Key(const std::string &itemTag)
 		: mItemTag(itemTag)
 	{
 	}
-	Key(const char *itemTag)
+	explicit Key(const char *itemTag)
 		: mItemTag(itemTag)
 	{
 	}
@@ -1816,12 +1832,12 @@ class Category
 	friend class Row;
 	friend class detail::ItemReference;
 
-	Category(Datablock &db, const std::string &name, Validator *Validator);
+	Category(Datablock &db, const std::string_view name, const Validator *Validator);
 	Category(const Category &) = delete;
 	Category &operator=(const Category &) = delete;
 	~Category();
 
-	const std::string name() const { return mName; }
+	const std::string &name() const { return mName; }
 
 	using iterator = iterator_impl<Row>;
 	using const_iterator = iterator_impl<const Row>;
@@ -2064,7 +2080,7 @@ class Category
 
 	Datablock &db() { return mDb; }
 
-	void setValidator(Validator *v);
+	void setValidator(const Validator *v);
 
 	iset fields() const;
 	iset mandatoryFields() const;
@@ -2077,8 +2093,8 @@ class Category
 	void getTagOrder(std::vector<std::string> &tags) const;
 
 	// return index for known column, or the next available column index
-	size_t getColumnIndex(const std::string &name) const;
-	bool hasColumn(const std::string &name) const;
+	size_t getColumnIndex(std::string_view name) const;
+	bool hasColumn(std::string_view name) const;
 	const std::string &getColumnName(size_t columnIndex) const;
 	std::vector<std::string> getColumnNames() const;
 
@@ -2119,16 +2135,27 @@ class Category
 	void write(std::ostream &os, const std::vector<std::string> &order);
 	void write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns);
 
-	size_t addColumn(const std::string &name);
+	size_t addColumn(std::string_view name);
+
+	struct Linked
+	{
+		Category *linked;
+		const ValidateLink *v;
+	};
+
+	void updateLinks();
 
 	Datablock &mDb;
 	std::string mName;
-	Validator *mValidator;
+	const Validator *mValidator;
 	const ValidateCategory *mCatValidator = nullptr;
 	std::vector<ItemColumn> mColumns;
 	ItemRow *mHead;
 	ItemRow *mTail;
+	size_t mLastUniqueNr = 0;
 	class CatIndex *mIndex;
+
+	std::vector<Linked> mParentLinks, mChildLinks;
 };
 
 // --------------------------------------------------------------------
@@ -2162,7 +2189,8 @@ class File
 
 	void loadDictionary();                 // load the default dictionary, that is mmcifDdl in this case
 	void loadDictionary(const char *dict); // load one of the compiled in dictionaries
-	void loadDictionary(std::istream &is); // load dictionary from input stream
+
+	void setValidator(const Validator *v);
 
 	bool isValid();
 	void validateLinks() const;
@@ -2183,8 +2211,8 @@ class File
 
 	void append(Datablock *e);
 
-	Datablock *get(const std::string &name) const;
-	Datablock &operator[](const std::string &name);
+	Datablock *get(std::string_view name) const;
+	Datablock &operator[](std::string_view name);
 
 	struct iterator
 	{
@@ -2226,10 +2254,8 @@ class File
 	void getTagOrder(std::vector<std::string> &tags) const;
 
   private:
-	void setValidator(Validator *v);
-
 	Datablock *mHead;
-	Validator *mValidator;
+	const Validator *mValidator;
 };
 
 // --------------------------------------------------------------------
diff --git a/include/cif++/CifParser.hpp b/include/cif++/CifParser.hpp
index 12ddb5f..a43b277 100644
--- a/include/cif++/CifParser.hpp
+++ b/include/cif++/CifParser.hpp
@@ -28,8 +28,8 @@
 
 #include "cif++/Cif++.hpp"
 
-#include <stack>
 #include <map>
+#include <stack>
 
 namespace cif
 {
@@ -39,7 +39,7 @@ namespace cif
 class CifParserError : public std::runtime_error
 {
   public:
-	CifParserError(uint32_t lineNr, const std::string& message);
+	CifParserError(uint32_t lineNr, const std::string &message);
 };
 
 // --------------------------------------------------------------------
@@ -48,7 +48,8 @@ extern const uint32_t kMaxLineLength;
 
 extern const uint8_t kCharTraitsTable[128];
 
-enum CharTraitsMask: uint8_t {
+enum CharTraitsMask : uint8_t
+{
 	kOrdinaryMask = 1 << 0,
 	kNonBlankMask = 1 << 1,
 	kTextLeadMask = 1 << 2,
@@ -75,13 +76,13 @@ inline bool isTextLead(int ch)
 	return ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kTextLeadMask) != 0;
 }
 
-inline bool isAnyPrint(int ch)	
+inline bool isAnyPrint(int ch)
 {
-	return ch == '\t' or 
-		(ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kAnyPrintMask) != 0);
+	return ch == '\t' or
+	       (ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kAnyPrintMask) != 0);
 }
 
-inline bool isUnquotedString(const char* s)
+inline bool isUnquotedString(const char *s)
 {
 	bool result = isOrdinary(*s++);
 	while (result and *s != 0)
@@ -94,11 +95,7 @@ inline bool isUnquotedString(const char* s)
 
 // --------------------------------------------------------------------
 
-std::tuple<std::string,std::string> splitTagName(const std::string& tag);
-
-// --------------------------------------------------------------------
-
-using DatablockIndex = std::map<std::string,std::size_t>;
+using DatablockIndex = std::map<std::string, std::size_t>;
 
 // --------------------------------------------------------------------
 // sac Parser, analogous to SAX Parser (simple api for xml)
@@ -106,15 +103,15 @@ using DatablockIndex = std::map<std::string,std::size_t>;
 class SacParser
 {
   public:
-	SacParser(std::istream& is, bool init = true);
+	SacParser(std::istream &is, bool init = true);
 	virtual ~SacParser() {}
 
 	enum CIFToken
 	{
 		eCIFTokenUnknown,
-		
+
 		eCIFTokenEOF,
-	
+
 		eCIFTokenDATA,
 		eCIFTokenLOOP,
 		eCIFTokenGLOBAL,
@@ -124,7 +121,7 @@ class SacParser
 		eCIFTokenValue,
 	};
 
-	static const char* kTokenName[];
+	static const char *kTokenName[];
 
 	enum CIFValueType
 	{
@@ -137,40 +134,39 @@ class SacParser
 		eCIFValueUnknown
 	};
 
-	static const char* kValueName[];
-	
+	static const char *kValueName[];
+
 	int getNextChar();
 
 	void retract();
 	void restart();
-	
+
 	CIFToken getNextToken();
 	void match(CIFToken token);
 
-	bool parseSingleDatablock(const std::string& datablock);
+	bool parseSingleDatablock(const std::string &datablock);
 
 	DatablockIndex indexDatablocks();
-	bool parseSingleDatablock(const std::string& datablock, const DatablockIndex &index);
+	bool parseSingleDatablock(const std::string &datablock, const DatablockIndex &index);
 
 	void parseFile();
 	void parseGlobal();
 	void parseDataBlock();
 
 	virtual void parseSaveFrame();
-	
+
 	void parseDictionary();
-	
-	void error(const std::string& msg);
-	
+
+	void error(const std::string &msg);
+
 	// production methods, these are pure virtual here
-	
-	virtual void produceDatablock(const std::string& name) = 0;
-	virtual void produceCategory(const std::string& name) = 0;
+
+	virtual void produceDatablock(const std::string &name) = 0;
+	virtual void produceCategory(const std::string &name) = 0;
 	virtual void produceRow() = 0;
-	virtual void produceItem(const std::string& category, const std::string& item, const std::string& value) = 0;
+	virtual void produceItem(const std::string &category, const std::string &item, const std::string &value) = 0;
 
   protected:
-
 	enum State
 	{
 		eStateStart,
@@ -185,21 +181,21 @@ class SacParser
 		eStateTextField,
 		eStateFloat = 100,
 		eStateInt = 110,
-//		eStateNumericSuffix = 200,
+		//		eStateNumericSuffix = 200,
 		eStateValue = 300
 	};
 
-	std::istream&			mData;
+	std::istream &mData;
 
 	// Parser state
-	bool					mValidate;
-	uint32_t					mLineNr;
-	bool					mBol;
-	int						mState, mStart;
-	CIFToken				mLookahead;
-	std::string				mTokenValue;
-	CIFValueType			mTokenType;
-	std::stack<int>			mBuffer;
+	bool mValidate;
+	uint32_t mLineNr;
+	bool mBol;
+	int mState, mStart;
+	CIFToken mLookahead;
+	std::string mTokenValue;
+	CIFValueType mTokenType;
+	std::stack<int> mBuffer;
 };
 
 // --------------------------------------------------------------------
@@ -207,18 +203,18 @@ class SacParser
 class Parser : public SacParser
 {
   public:
-	Parser(std::istream& is, File& f, bool init = true);
+	Parser(std::istream &is, File &f, bool init = true);
 
-	virtual void produceDatablock(const std::string& name);
-	virtual void produceCategory(const std::string& name);
+	virtual void produceDatablock(const std::string &name);
+	virtual void produceCategory(const std::string &name);
 	virtual void produceRow();
-	virtual void produceItem(const std::string& category, const std::string& item, const std::string& value);
+	virtual void produceItem(const std::string &category, const std::string &item, const std::string &value);
 
   protected:
-	File&					mFile;
-	Datablock*				mDataBlock;
-	Datablock::iterator		mCat;
-	Row						mRow;
+	File &mFile;
+	Datablock *mDataBlock;
+	Datablock::iterator mCat;
+	Row mRow;
 };
 
 // --------------------------------------------------------------------
@@ -226,23 +222,21 @@ class Parser : public SacParser
 class DictParser : public Parser
 {
   public:
-
-	DictParser(Validator& validator, std::istream& is);
+	DictParser(Validator &validator, std::istream &is);
 	~DictParser();
-	
+
 	void loadDictionary();
-	
-  private:
 
+  private:
 	virtual void parseSaveFrame();
-	
+
 	bool collectItemTypes();
 	void linkItems();
 
-	Validator&						mValidator;
-	File							mFile;
-	struct DictParserDataImpl*		mImpl;
-	bool							mCollectedItemTypes = false;
+	Validator &mValidator;
+	File mFile;
+	struct DictParserDataImpl *mImpl;
+	bool mCollectedItemTypes = false;
 };
 
-}
+} // namespace cif
diff --git a/include/cif++/CifUtils.hpp b/include/cif++/CifUtils.hpp
index 2ee5c6a..7896208 100644
--- a/include/cif++/CifUtils.hpp
+++ b/include/cif++/CifUtils.hpp
@@ -26,11 +26,7 @@
 
 #pragma once
 
-#include <cassert>
 #include <filesystem>
-#include <iostream>
-#include <list>
-#include <memory>
 #include <set>
 #include <vector>
 
@@ -67,8 +63,8 @@ std::string get_version_nr();
 // some basic utilities: Since we're using ASCII input only, we define for optimisation
 // our own case conversion routines.
 
-bool iequals(const std::string &a, const std::string &b);
-int icompare(const std::string &a, const std::string &b);
+bool iequals(std::string_view a, std::string_view b);
+int icompare(std::string_view a, std::string_view b);
 
 bool iequals(const char *a, const char *b);
 int icompare(const char *a, const char *b);
@@ -100,7 +96,7 @@ inline char tolower(int ch)
 
 // --------------------------------------------------------------------
 
-std::tuple<std::string, std::string> splitTagName(const std::string &tag);
+std::tuple<std::string, std::string> splitTagName(std::string_view tag);
 
 // --------------------------------------------------------------------
 // generate a cif name, mainly used to generate asym_id's
diff --git a/include/cif++/CifValidator.hpp b/include/cif++/CifValidator.hpp
index 3b97da0..5e7fa3c 100644
--- a/include/cif++/CifValidator.hpp
+++ b/include/cif++/CifValidator.hpp
@@ -36,18 +36,19 @@
 
 namespace cif
 {
-	
+
 struct ValidateCategory;
+class ValidatorFactory;
 
 // --------------------------------------------------------------------
 
 class ValidationError : public std::exception
 {
   public:
-	ValidationError(const std::string& msg);
-	ValidationError(const std::string& cat, const std::string& item,
-		const std::string& msg);
-	const char* what() const noexcept		{ return mMsg.c_str(); }
+	ValidationError(const std::string &msg);
+	ValidationError(const std::string &cat, const std::string &item,
+		const std::string &msg);
+	const char *what() const noexcept { return mMsg.c_str(); }
 	std::string mMsg;
 };
 
@@ -55,58 +56,60 @@ class ValidationError : public std::exception
 
 enum class DDL_PrimitiveType
 {
-	Char, UChar, Numb
+	Char,
+	UChar,
+	Numb
 };
 
-DDL_PrimitiveType mapToPrimitiveType(const std::string& s);
+DDL_PrimitiveType mapToPrimitiveType(std::string_view s);
 
 struct ValidateType
 {
-	std::string				mName;
-	DDL_PrimitiveType		mPrimitiveType;
+	std::string mName;
+	DDL_PrimitiveType mPrimitiveType;
 	// std::regex				mRx;
-	boost::regex			mRx;
+	boost::regex mRx;
 
-	bool operator<(const ValidateType& rhs) const
+	bool operator<(const ValidateType &rhs) const
 	{
 		return icompare(mName, rhs.mName) < 0;
 	}
 
-	// compare values based on type	
-//	int compare(const std::string& a, const std::string& b) const
-//	{
-//		return compare(a.c_str(), b.c_str());
-//	}
-	
-	int compare(const char* a, const char* b) const;
+	// compare values based on type
+	//	int compare(const std::string& a, const std::string& b) const
+	//	{
+	//		return compare(a.c_str(), b.c_str());
+	//	}
+
+	int compare(const char *a, const char *b) const;
 };
 
 struct ValidateItem
 {
-	std::string				mTag;
-	bool					mMandatory;
-	const ValidateType*		mType;
-	cif::iset				mEnums;
-	std::string				mDefault;
-	bool					mDefaultIsNull;
-	ValidateCategory*		mCategory = nullptr;
+	std::string mTag;
+	bool mMandatory;
+	const ValidateType *mType;
+	cif::iset mEnums;
+	std::string mDefault;
+	bool mDefaultIsNull;
+	ValidateCategory *mCategory = nullptr;
 
 	// ItemLinked is used for non-key links
 	struct ItemLinked
 	{
-		ValidateItem*		mParent;
-		std::string			mParentItem;
-		std::string			mChildItem;
+		ValidateItem *mParent;
+		std::string mParentItem;
+		std::string mChildItem;
 	};
 
-	std::vector<ItemLinked>	mLinked;
-	
-	bool operator<(const ValidateItem& rhs) const
+	std::vector<ItemLinked> mLinked;
+
+	bool operator<(const ValidateItem &rhs) const
 	{
 		return icompare(mTag, rhs.mTag) < 0;
 	}
 
-	bool operator==(const ValidateItem& rhs) const
+	bool operator==(const ValidateItem &rhs) const
 	{
 		return iequals(mTag, rhs.mTag);
 	}
@@ -116,22 +119,22 @@ struct ValidateItem
 
 struct ValidateCategory
 {
-	std::string					mName;
-	std::vector<std::string>	mKeys;
-	cif::iset					mGroups;
-	cif::iset					mMandatoryFields;
-	std::set<ValidateItem>		mItemValidators;
+	std::string mName;
+	std::vector<std::string> mKeys;
+	cif::iset mGroups;
+	cif::iset mMandatoryFields;
+	std::set<ValidateItem> mItemValidators;
 
-	bool operator<(const ValidateCategory& rhs) const
+	bool operator<(const ValidateCategory &rhs) const
 	{
 		return icompare(mName, rhs.mName) < 0;
 	}
 
-	void addItemValidator(ValidateItem&& v);
-	
-	const ValidateItem* getValidatorForItem(std::string tag) const;
-	
-	const std::set<ValidateItem>& itemValidators() const
+	void addItemValidator(ValidateItem &&v);
+
+	const ValidateItem *getValidatorForItem(std::string_view tag) const;
+
+	const std::set<ValidateItem> &itemValidators() const
 	{
 		return mItemValidators;
 	}
@@ -139,12 +142,12 @@ struct ValidateCategory
 
 struct ValidateLink
 {
-	int							mLinkGroupID;
-	std::string					mParentCategory;
-	std::vector<std::string>	mParentKeys;
-	std::string					mChildCategory;
-	std::vector<std::string>	mChildKeys;
-	std::string					mLinkGroupLabel;
+	int mLinkGroupID;
+	std::string mParentCategory;
+	std::vector<std::string> mParentKeys;
+	std::string mChildCategory;
+	std::vector<std::string> mChildKeys;
+	std::string mLinkGroupLabel;
 };
 
 // --------------------------------------------------------------------
@@ -152,47 +155,72 @@ struct ValidateLink
 class Validator
 {
   public:
-	friend class DictParser;
 
-	Validator();
+	Validator(std::string_view name, std::istream &is);
 	~Validator();
 
-	Validator(const Validator& rhs) = delete;
-	Validator& operator=(const Validator& rhs) = delete;
-	
-	Validator(Validator&& rhs);
-	Validator& operator=(Validator&& rhs);
-	
-	void addTypeValidator(ValidateType&& v);
-	const ValidateType* getValidatorForType(std::string typeCode) const;
+	Validator(const Validator &rhs) = delete;
+	Validator &operator=(const Validator &rhs) = delete;
 
-	void addCategoryValidator(ValidateCategory&& v);
-	const ValidateCategory* getValidatorForCategory(std::string category) const;
+	Validator(Validator &&rhs);
+	Validator &operator=(Validator &&rhs);
 
-	void addLinkValidator(ValidateLink&& v);
-	std::vector<const ValidateLink*> getLinksForParent(const std::string& category) const;
-	std::vector<const ValidateLink*> getLinksForChild(const std::string& category) const;
+	friend class DictParser;
+	friend class ValidatorFactory;
 
-	void reportError(const std::string& msg, bool fatal);
-	
-	std::string dictName() const					{ return mName; }
-	void dictName(const std::string& name)			{ mName = name; }
+	void addTypeValidator(ValidateType &&v);
+	const ValidateType *getValidatorForType(std::string_view typeCode) const;
 
-	std::string dictVersion() const				{ return mVersion; }
-	void dictVersion(const std::string& version)	{ mVersion = version; }
+	void addCategoryValidator(ValidateCategory &&v);
+	const ValidateCategory *getValidatorForCategory(std::string_view category) const;
+
+	void addLinkValidator(ValidateLink &&v);
+	std::vector<const ValidateLink *> getLinksForParent(std::string_view category) const;
+	std::vector<const ValidateLink *> getLinksForChild(std::string_view category) const;
+
+	void reportError(const std::string &msg, bool fatal) const;
+
+	std::string dictName() const { return mName; }
+	void dictName(const std::string &name) { mName = name; }
+
+	std::string dictVersion() const { return mVersion; }
+	void dictVersion(const std::string &version) { mVersion = version; }
 
   private:
 
 	// name is fully qualified here:
-	ValidateItem* getValidatorForItem(std::string name) const;
-
-	std::string					mName;
-	std::string					mVersion;
-	bool						mStrict = false;
-//	std::set<uint32_t>			mSubCategories;
-	std::set<ValidateType>		mTypeValidators;
-	std::set<ValidateCategory>	mCategoryValidators;
-	std::vector<ValidateLink>	mLinkValidators;
+	ValidateItem *getValidatorForItem(std::string_view name) const;
+
+	std::string mName;
+	std::string mVersion;
+	bool mStrict = false;
+	//	std::set<uint32_t>			mSubCategories;
+	std::set<ValidateType> mTypeValidators;
+	std::set<ValidateCategory> mCategoryValidators;
+	std::vector<ValidateLink> mLinkValidators;
+};
+
+// --------------------------------------------------------------------
+
+class ValidatorFactory
+{
+  public:
+
+	static ValidatorFactory &instance()
+	{
+		return sInstance;
+	}
+
+	const Validator &operator[](std::string_view dictionary);
+
+  private:
+
+	static ValidatorFactory sInstance;
+
+	ValidatorFactory();
+
+	std::mutex mMutex;
+	std::list<Validator> mValidators;
 };
 
-}
+} // namespace cif
diff --git a/include/cif++/Compound.hpp b/include/cif++/Compound.hpp
index 18a79f8..fb3b704 100644
--- a/include/cif++/Compound.hpp
+++ b/include/cif++/Compound.hpp
@@ -104,6 +104,7 @@ class Compound
 	std::string id() const { return mID; }
 	std::string name() const { return mName; }
 	std::string type() const { return mType; }
+	std::string group() const { return mGroup; }
 	std::string formula() const { return mFormula; }
 	float formulaWeight() const { return mFormulaWeight; }
 	int formalCharge() const { return mFormalCharge; }
@@ -130,11 +131,12 @@ class Compound
 	friend class CCP4CompoundFactoryImpl;
 
 	Compound(cif::Datablock &db);
-	Compound(cif::Datablock &db, const std::string &id, const std::string &name, const std::string &type);
+	Compound(cif::Datablock &db, const std::string &id, const std::string &name, const std::string &type, const std::string &group);
 
 	std::string mID;
 	std::string mName;
 	std::string mType;
+	std::string mGroup;
 	std::string mFormula;
 	float mFormulaWeight = 0;
 	int mFormalCharge = 0;
diff --git a/include/cif++/Matrix.hpp b/include/cif++/Matrix.hpp
deleted file mode 100644
index 4fa03eb..0000000
--- a/include/cif++/Matrix.hpp
+++ /dev/null
@@ -1,391 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- * 
- * Copyright Maarten L. Hekkelman, Radboud University 2008-2011.
- * Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * 
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-// --------------------------------------------------------------------
-// uBlas compatible matrix types
-
-#pragma once
-
-#include <iostream>
-#include <vector>
-
-// matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
-// element m i,j is mapped to [i * n + j] and thus storage is row major
-
-template <typename T>
-class MatrixBase
-{
-  public:
-	using value_type = T;
-
-	virtual ~MatrixBase() {}
-
-	virtual uint32_t dim_m() const = 0;
-	virtual uint32_t dim_n() const = 0;
-
-	virtual value_type &operator()(uint32_t i, uint32_t j) { throw std::runtime_error("unimplemented method"); }
-	virtual value_type operator()(uint32_t i, uint32_t j) const = 0;
-
-	MatrixBase &operator*=(const value_type &rhs);
-
-	MatrixBase &operator-=(const value_type &rhs);
-};
-
-template <typename T>
-MatrixBase<T> &MatrixBase<T>::operator*=(const T &rhs)
-{
-	for (uint32_t i = 0; i < dim_m(); ++i)
-	{
-		for (uint32_t j = 0; j < dim_n(); ++j)
-		{
-			operator()(i, j) *= rhs;
-		}
-	}
-
-	return *this;
-}
-
-template <typename T>
-MatrixBase<T> &MatrixBase<T>::operator-=(const T &rhs)
-{
-	for (uint32_t i = 0; i < dim_m(); ++i)
-	{
-		for (uint32_t j = 0; j < dim_n(); ++j)
-		{
-			operator()(i, j) -= rhs;
-		}
-	}
-
-	return *this;
-}
-
-template <typename T>
-std::ostream &operator<<(std::ostream &lhs, const MatrixBase<T> &rhs)
-{
-	lhs << '[' << rhs.dim_m() << ',' << rhs.dim_n() << ']' << '(';
-	for (uint32_t i = 0; i < rhs.dim_m(); ++i)
-	{
-		lhs << '(';
-		for (uint32_t j = 0; j < rhs.dim_n(); ++j)
-		{
-			if (j > 0)
-				lhs << ',';
-			lhs << rhs(i, j);
-		}
-		lhs << ')';
-	}
-	lhs << ')';
-
-	return lhs;
-}
-
-template <typename T>
-class Matrix : public MatrixBase<T>
-{
-  public:
-	using value_type = T;
-
-	template <typename T2>
-	Matrix(const MatrixBase<T2> &m)
-		: m_m(m.dim_m())
-		, m_n(m.dim_n())
-	{
-		m_data = new value_type[m_m * m_n];
-		for (uint32_t i = 0; i < m_m; ++i)
-		{
-			for (uint32_t j = 0; j < m_n; ++j)
-				operator()(i, j) = m(i, j);
-		}
-	}
-
-	Matrix()
-		: m_data(nullptr)
-		, m_m(0)
-		, m_n(0)
-	{
-	}
-
-	Matrix(const Matrix &m)
-		: m_m(m.m_m)
-		, m_n(m.m_n)
-	{
-		m_data = new value_type[m_m * m_n];
-		std::copy(m.m_data, m.m_data + (m_m * m_n), m_data);
-	}
-
-	Matrix &operator=(const Matrix &m)
-	{
-		value_type *t = new value_type[m.m_m * m.m_n];
-		std::copy(m.m_data, m.m_data + (m.m_m * m.m_n), t);
-
-		delete[] m_data;
-		m_data = t;
-		m_m = m.m_m;
-		m_n = m.m_n;
-
-		return *this;
-	}
-
-	Matrix(uint32_t m, uint32_t n, T v = T())
-		: m_m(m)
-		, m_n(n)
-	{
-		m_data = new value_type[m_m * m_n];
-		std::fill(m_data, m_data + (m_m * m_n), v);
-	}
-
-	virtual ~Matrix()
-	{
-		delete[] m_data;
-	}
-
-	virtual uint32_t dim_m() const { return m_m; }
-	virtual uint32_t dim_n() const { return m_n; }
-
-	virtual value_type operator()(uint32_t i, uint32_t j) const
-	{
-		assert(i < m_m);
-		assert(j < m_n);
-		return m_data[i * m_n + j];
-	}
-
-	virtual value_type &operator()(uint32_t i, uint32_t j)
-	{
-		assert(i < m_m);
-		assert(j < m_n);
-		return m_data[i * m_n + j];
-	}
-
-	template <typename Func>
-	void each(Func f)
-	{
-		for (uint32_t i = 0; i < m_m * m_n; ++i)
-			f(m_data[i]);
-	}
-
-	template <typename U>
-	Matrix &operator/=(U v)
-	{
-		for (uint32_t i = 0; i < m_m * m_n; ++i)
-			m_data[i] /= v;
-
-		return *this;
-	}
-
-  private:
-	value_type *m_data;
-	uint32_t m_m, m_n;
-};
-
-// --------------------------------------------------------------------
-
-template <typename T>
-class SymmetricMatrix : public MatrixBase<T>
-{
-  public:
-	typedef typename MatrixBase<T>::value_type value_type;
-
-	SymmetricMatrix(uint32_t n, T v = T())
-		: m_owner(true)
-		, m_n(n)
-	{
-		uint32_t N = (m_n * (m_n + 1)) / 2;
-		m_data = new value_type[N];
-		std::fill(m_data, m_data + N, v);
-	}
-
-	SymmetricMatrix(const T *data, uint32_t n)
-		: m_owner(false)
-		, m_data(const_cast<T *>(data))
-		, m_n(n)
-	{
-	}
-
-	virtual ~SymmetricMatrix()
-	{
-		if (m_owner)
-			delete[] m_data;
-	}
-
-	virtual uint32_t dim_m() const { return m_n; }
-	virtual uint32_t dim_n() const { return m_n; }
-
-	T operator()(uint32_t i, uint32_t j) const;
-	virtual T &operator()(uint32_t i, uint32_t j);
-
-	// erase two rows, add one at the end (for neighbour joining)
-	void erase_2(uint32_t i, uint32_t j);
-
-	template <typename Func>
-	void each(Func f)
-	{
-		uint32_t N = (m_n * (m_n + 1)) / 2;
-
-		for (uint32_t i = 0; i < N; ++i)
-			f(m_data[i]);
-	}
-
-	template <typename U>
-	SymmetricMatrix &operator/=(U v)
-	{
-		uint32_t N = (m_n * (m_n + 1)) / 2;
-
-		for (uint32_t i = 0; i < N; ++i)
-			m_data[i] /= v;
-
-		return *this;
-	}
-
-  private:
-	bool m_owner;
-	value_type *m_data;
-	uint32_t m_n;
-};
-
-template <typename T>
-inline T SymmetricMatrix<T>::operator()(uint32_t i, uint32_t j) const
-{
-	return i < j
-	           ? m_data[(j * (j + 1)) / 2 + i]
-	           : m_data[(i * (i + 1)) / 2 + j];
-}
-
-template <typename T>
-inline T &SymmetricMatrix<T>::operator()(uint32_t i, uint32_t j)
-{
-	if (i > j)
-		std::swap(i, j);
-	assert(j < m_n);
-	return m_data[(j * (j + 1)) / 2 + i];
-}
-
-template <typename T>
-void SymmetricMatrix<T>::erase_2(uint32_t di, uint32_t dj)
-{
-	uint32_t s = 0, d = 0;
-	for (uint32_t i = 0; i < m_n; ++i)
-	{
-		for (uint32_t j = 0; j < i; ++j)
-		{
-			if (i != di and j != dj and i != dj and j != di)
-			{
-				if (s != d)
-					m_data[d] = m_data[s];
-				++d;
-			}
-
-			++s;
-		}
-	}
-
-	--m_n;
-}
-
-template <typename T>
-class IdentityMatrix : public MatrixBase<T>
-{
-  public:
-	typedef typename MatrixBase<T>::value_type value_type;
-
-	IdentityMatrix(uint32_t n)
-		: m_n(n)
-	{
-	}
-
-	virtual uint32_t dim_m() const { return m_n; }
-	virtual uint32_t dim_n() const { return m_n; }
-
-	virtual value_type operator()(uint32_t i, uint32_t j) const
-	{
-		value_type result = 0;
-		if (i == j)
-			result = 1;
-		return result;
-	}
-
-  private:
-	uint32_t m_n;
-};
-
-// --------------------------------------------------------------------
-// matrix functions
-
-template <typename T>
-Matrix<T> operator*(const MatrixBase<T> &lhs, const MatrixBase<T> &rhs)
-{
-	Matrix<T> result(std::min(lhs.dim_m(), rhs.dim_m()), std::min(lhs.dim_n(), rhs.dim_n()));
-
-	for (uint32_t i = 0; i < result.dim_m(); ++i)
-	{
-		for (uint32_t j = 0; j < result.dim_n(); ++j)
-		{
-			for (uint32_t li = 0, rj = 0; li < lhs.dim_m() and rj < rhs.dim_n(); ++li, ++rj)
-				result(i, j) += lhs(li, j) * rhs(i, rj);
-		}
-	}
-
-	return result;
-}
-
-template <typename T>
-Matrix<T> operator*(const MatrixBase<T> &lhs, T rhs)
-{
-	Matrix<T> result(lhs);
-	result *= rhs;
-
-	return result;
-}
-
-template <typename T>
-Matrix<T> operator-(const MatrixBase<T> &lhs, const MatrixBase<T> &rhs)
-{
-	Matrix<T> result(std::min(lhs.dim_m(), rhs.dim_m()), std::min(lhs.dim_n(), rhs.dim_n()));
-
-	for (uint32_t i = 0; i < result.dim_m(); ++i)
-	{
-		for (uint32_t j = 0; j < result.dim_n(); ++j)
-		{
-			result(i, j) = lhs(i, j) - rhs(i, j);
-		}
-	}
-
-	return result;
-}
-
-template <typename T>
-Matrix<T> operator-(const MatrixBase<T> &lhs, T rhs)
-{
-	Matrix<T> result(lhs.dim_m(), lhs.dim_n());
-	result -= rhs;
-	return result;
-}
-
-// template <typename T>
-// symmetric_matrix<T> hammingDistance(const MatrixBase<T> &lhs, T rhs);
-
-// template <typename T>
-// std::vector<T> sum(const MatrixBase<T> &m);
diff --git a/include/cif++/Point.hpp b/include/cif++/Point.hpp
index c04c079..bce3ee7 100644
--- a/include/cif++/Point.hpp
+++ b/include/cif++/Point.hpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -37,7 +37,7 @@
 namespace mmcif
 {
 
-typedef boost::math::quaternion<float>	Quaternion;
+typedef boost::math::quaternion<float> Quaternion;
 
 const double
 	kPI = 3.141592653589793238462643383279502884;
@@ -51,26 +51,43 @@ const double
 //	float x, y, z;
 //	tie(x, y, z) = atom.loc();
 
-template<typename F>
+template <typename F>
 struct PointF
 {
 	typedef F FType;
 
 	FType mX, mY, mZ;
-	
-	PointF()							: mX(0), mY(0), mZ(0) {}
-	PointF(FType x, FType y, FType z)	: mX(x), mY(y), mZ(z) {}
 
-	template<typename PF>
-	PointF(const PointF<PF>& pt)
+	PointF()
+		: mX(0)
+		, mY(0)
+		, mZ(0)
+	{
+	}
+	PointF(FType x, FType y, FType z)
+		: mX(x)
+		, mY(y)
+		, mZ(z)
+	{
+	}
+
+	template <typename PF>
+	PointF(const PointF<PF> &pt)
 		: mX(static_cast<F>(pt.mX))
 		, mY(static_cast<F>(pt.mY))
-		, mZ(static_cast<F>(pt.mZ)) {}
+		, mZ(static_cast<F>(pt.mZ))
+	{
+	}
 
-#if HAVE_LIBCLIPPER	
-	PointF(const clipper::Coord_orth& pt): mX(pt[0]), mY(pt[1]), mZ(pt[2]) {}
+#if HAVE_LIBCLIPPER
+	PointF(const clipper::Coord_orth &pt)
+		: mX(pt[0])
+		, mY(pt[1])
+		, mZ(pt[2])
+	{
+	}
 
-	PointF& operator=(const clipper::Coord_orth& rhs)
+	PointF &operator=(const clipper::Coord_orth &rhs)
 	{
 		mX = rhs[0];
 		mY = rhs[1];
@@ -79,72 +96,72 @@ struct PointF
 	}
 #endif
 
-	template<typename PF>
-	PointF& operator=(const PointF<PF>& rhs)
+	template <typename PF>
+	PointF &operator=(const PointF<PF> &rhs)
 	{
 		mX = static_cast<F>(rhs.mX);
 		mY = static_cast<F>(rhs.mY);
 		mZ = static_cast<F>(rhs.mZ);
 		return *this;
 	}
-	
-	FType& getX()			{ return mX; }
-	FType getX() const		{ return mX; }
-	void setX(FType x)		{ mX = x; }
-
-	FType& getY()			{ return mY; }
-	FType getY() const		{ return mY; }
-	void setY(FType y)		{ mY = y; }
-
-	FType& getZ()			{ return mZ; }
-	FType getZ() const		{ return mZ; }
-	void setZ(FType z)		{ mZ = z; }
-	
-	PointF& operator+=(const PointF& rhs)
+
+	FType &getX() { return mX; }
+	FType getX() const { return mX; }
+	void setX(FType x) { mX = x; }
+
+	FType &getY() { return mY; }
+	FType getY() const { return mY; }
+	void setY(FType y) { mY = y; }
+
+	FType &getZ() { return mZ; }
+	FType getZ() const { return mZ; }
+	void setZ(FType z) { mZ = z; }
+
+	PointF &operator+=(const PointF &rhs)
 	{
 		mX += rhs.mX;
 		mY += rhs.mY;
 		mZ += rhs.mZ;
-		
+
 		return *this;
 	}
-	
-	PointF& operator+=(FType d)
+
+	PointF &operator+=(FType d)
 	{
 		mX += d;
 		mY += d;
 		mZ += d;
-		
+
 		return *this;
 	}
 
-	PointF& operator-=(const PointF& rhs)
+	PointF &operator-=(const PointF &rhs)
 	{
 		mX -= rhs.mX;
 		mY -= rhs.mY;
 		mZ -= rhs.mZ;
-		
+
 		return *this;
 	}
 
-	PointF& operator-=(FType d)
+	PointF &operator-=(FType d)
 	{
 		mX -= d;
 		mY -= d;
 		mZ -= d;
-		
+
 		return *this;
 	}
 
-	PointF& operator*=(FType rhs)
+	PointF &operator*=(FType rhs)
 	{
 		mX *= rhs;
 		mY *= rhs;
 		mZ *= rhs;
 		return *this;
 	}
-	
-	PointF& operator/=(FType rhs)
+
+	PointF &operator/=(FType rhs)
 	{
 		mX /= rhs;
 		mY /= rhs;
@@ -162,18 +179,18 @@ struct PointF
 		}
 		return length;
 	}
-	
-	void rotate(const boost::math::quaternion<FType>& q)
+
+	void rotate(const boost::math::quaternion<FType> &q)
 	{
 		boost::math::quaternion<FType> p(0, mX, mY, mZ);
-		
+
 		p = q * p * boost::math::conj(q);
-	
+
 		mX = p.R_component_2();
 		mY = p.R_component_3();
 		mZ = p.R_component_4();
 	}
-	
+
 #if HAVE_LIBCLIPPER
 	operator clipper::Coord_orth() const
 	{
@@ -181,21 +198,21 @@ struct PointF
 	}
 #endif
 
-	operator std::tuple<const FType&, const FType&, const FType&>() const
+	operator std::tuple<const FType &, const FType &, const FType &>() const
 	{
 		return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
 	}
 
-	operator std::tuple<FType&,FType&,FType&>()
+	operator std::tuple<FType &, FType &, FType &>()
 	{
 		return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
 	}
-	
-	bool operator==(const PointF& rhs) const
+
+	bool operator==(const PointF &rhs) const
 	{
 		return mX == rhs.mX and mY == rhs.mY and mZ == rhs.mZ;
 	}
-	
+
 	// consider point as a vector... perhaps I should rename Point?
 	FType lengthsq() const
 	{
@@ -211,45 +228,45 @@ struct PointF
 typedef PointF<float> Point;
 typedef PointF<double> DPoint;
 
-template<typename F>
-inline std::ostream& operator<<(std::ostream& os, const PointF<F>& pt)
+template <typename F>
+inline std::ostream &operator<<(std::ostream &os, const PointF<F> &pt)
 {
 	os << '(' << pt.mX << ',' << pt.mY << ',' << pt.mZ << ')';
-	return os; 
+	return os;
 }
 
-template<typename F>
-inline PointF<F> operator+(const PointF<F>& lhs, const PointF<F>& rhs)
+template <typename F>
+inline PointF<F> operator+(const PointF<F> &lhs, const PointF<F> &rhs)
 {
 	return PointF<F>(lhs.mX + rhs.mX, lhs.mY + rhs.mY, lhs.mZ + rhs.mZ);
 }
 
-template<typename F>
-inline PointF<F> operator-(const PointF<F>& lhs, const PointF<F>& rhs)
+template <typename F>
+inline PointF<F> operator-(const PointF<F> &lhs, const PointF<F> &rhs)
 {
 	return PointF<F>(lhs.mX - rhs.mX, lhs.mY - rhs.mY, lhs.mZ - rhs.mZ);
 }
 
-template<typename F>
-inline PointF<F> operator-(const PointF<F>& pt)
+template <typename F>
+inline PointF<F> operator-(const PointF<F> &pt)
 {
 	return PointF<F>(-pt.mX, -pt.mY, -pt.mZ);
 }
 
-template<typename F>
-inline PointF<F> operator*(const PointF<F>& pt, F f)
+template <typename F>
+inline PointF<F> operator*(const PointF<F> &pt, F f)
 {
 	return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
 }
 
-template<typename F>
-inline PointF<F> operator*(F f, const PointF<F>& pt)
+template <typename F>
+inline PointF<F> operator*(F f, const PointF<F> &pt)
 {
 	return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
 }
 
-template<typename F>
-inline PointF<F> operator/(const PointF<F>& pt, F f)
+template <typename F>
+inline PointF<F> operator/(const PointF<F> &pt, F f)
 {
 	return PointF<F>(pt.mX / f, pt.mY / f, pt.mZ / f);
 }
@@ -257,17 +274,16 @@ inline PointF<F> operator/(const PointF<F>& pt, F f)
 // --------------------------------------------------------------------
 // several standard 3d operations
 
-template<typename F>
-inline double DistanceSquared(const PointF<F>& a, const PointF<F>& b)
+template <typename F>
+inline double DistanceSquared(const PointF<F> &a, const PointF<F> &b)
 {
-	return
-		(a.mX - b.mX) * (a.mX - b.mX) +
-		(a.mY - b.mY) * (a.mY - b.mY) +
-		(a.mZ - b.mZ) * (a.mZ - b.mZ);
+	return (a.mX - b.mX) * (a.mX - b.mX) +
+	       (a.mY - b.mY) * (a.mY - b.mY) +
+	       (a.mZ - b.mZ) * (a.mZ - b.mZ);
 }
 
-template<typename F>
-inline double Distance(const PointF<F>& a, const PointF<F>& b)
+template <typename F>
+inline double Distance(const PointF<F> &a, const PointF<F> &b)
 {
 	return sqrt(
 		(a.mX - b.mX) * (a.mX - b.mX) +
@@ -275,44 +291,44 @@ inline double Distance(const PointF<F>& a, const PointF<F>& b)
 		(a.mZ - b.mZ) * (a.mZ - b.mZ));
 }
 
-template<typename F>
-inline F DotProduct(const PointF<F>& a, const PointF<F>& b)
+template <typename F>
+inline F DotProduct(const PointF<F> &a, const PointF<F> &b)
 {
 	return a.mX * b.mX + a.mY * b.mY + a.mZ * b.mZ;
 }
 
-template<typename F>
-inline PointF<F> CrossProduct(const PointF<F>& a, const PointF<F>& b)
+template <typename F>
+inline PointF<F> CrossProduct(const PointF<F> &a, const PointF<F> &b)
 {
 	return PointF<F>(a.mY * b.mZ - b.mY * a.mZ,
-				  a.mZ * b.mX - b.mZ * a.mX,
-				  a.mX * b.mY - b.mX * a.mY);
+		a.mZ * b.mX - b.mZ * a.mX,
+		a.mX * b.mY - b.mX * a.mY);
 }
 
-template<typename F>
-double Angle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3)
+template <typename F>
+double Angle(const PointF<F> &p1, const PointF<F> &p2, const PointF<F> &p3)
 {
 	PointF<F> v1 = p1 - p2;
 	PointF<F> v2 = p3 - p2;
-	
+
 	return std::acos(DotProduct(v1, v2) / (v1.length() * v2.length())) * 180 / kPI;
 }
 
-template<typename F>
-double DihedralAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
+template <typename F>
+double DihedralAngle(const PointF<F> &p1, const PointF<F> &p2, const PointF<F> &p3, const PointF<F> &p4)
 {
-	PointF<F> v12 = p1 - p2;	// vector from p2 to p1
-	PointF<F> v43 = p4 - p3;	// vector from p3 to p4
-	
-	PointF<F> z = p2 - p3;		// vector from p3 to p2
-	
+	PointF<F> v12 = p1 - p2; // vector from p2 to p1
+	PointF<F> v43 = p4 - p3; // vector from p3 to p4
+
+	PointF<F> z = p2 - p3; // vector from p3 to p2
+
 	PointF<F> p = CrossProduct(z, v12);
 	PointF<F> x = CrossProduct(z, v43);
 	PointF<F> y = CrossProduct(z, x);
-	
+
 	double u = DotProduct(x, x);
 	double v = DotProduct(y, y);
-	
+
 	double result = 360;
 	if (u > 0 and v > 0)
 	{
@@ -321,33 +337,33 @@ double DihedralAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>&
 		if (u != 0 or v != 0)
 			result = atan2(v, u) * 180 / kPI;
 	}
-	
+
 	return result;
 }
 
-template<typename F>
-double CosinusAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
+template <typename F>
+double CosinusAngle(const PointF<F> &p1, const PointF<F> &p2, const PointF<F> &p3, const PointF<F> &p4)
 {
 	PointF<F> v12 = p1 - p2;
 	PointF<F> v34 = p3 - p4;
-	
+
 	double result = 0;
-	
+
 	double x = DotProduct(v12, v12) * DotProduct(v34, v34);
 	if (x > 0)
 		result = DotProduct(v12, v34) / sqrt(x);
-	
+
 	return result;
 }
 
-template<typename F>
+template <typename F>
 auto DistancePointToLine(const PointF<F> &l1, const PointF<F> &l2, const PointF<F> &p)
 {
-	auto line       = l2 - l1;
-    auto p_to_l1    = p - l1;
-    auto p_to_l2    = p - l2;
-    auto cross      = CrossProduct(p_to_l1, p_to_l2);
-    return cross.length() / line.length();
+	auto line = l2 - l1;
+	auto p_to_l1 = p - l1;
+	auto p_to_l2 = p - l2;
+	auto cross = CrossProduct(p_to_l1, p_to_l2);
+	return cross.length() / line.length();
 }
 
 // --------------------------------------------------------------------
@@ -355,74 +371,84 @@ auto DistancePointToLine(const PointF<F> &l1, const PointF<F> &l2, const PointF<
 // a random direction with a distance randomly chosen from a normal
 // distribution with a stddev of offset.
 
-template<typename F>
-PointF<F> Nudge(PointF<F> p, F offset);
+Point Nudge(Point p, float offset);
 
 // --------------------------------------------------------------------
 // We use quaternions to do rotations in 3d space
 
 Quaternion Normalize(Quaternion q);
 
-std::tuple<double,Point> QuaternionToAngleAxis(Quaternion q);
-Point Centroid(std::vector<Point>& Points);
-Point CenterPoints(std::vector<Point>& Points);
-Quaternion AlignPoints(const std::vector<Point>& a, const std::vector<Point>& b);
-double RMSd(const std::vector<Point>& a, const std::vector<Point>& b);
+std::tuple<double, Point> QuaternionToAngleAxis(Quaternion q);
+Point Centroid(const std::vector<Point> &Points);
+Point CenterPoints(std::vector<Point> &Points);
+
+/// \brief Returns how the two sets of points \a a and \b b can be aligned
+///        
+/// \param a	The first set of points
+/// \param b    The second set of points
+/// \result     The quaternion which should be applied to the points in \a a to
+///             obtain the best superposition.
+Quaternion AlignPoints(const std::vector<Point> &a, const std::vector<Point> &b);
+
+/// \brief The RMSd for the points in \a a and \a b
+double RMSd(const std::vector<Point> &a, const std::vector<Point> &b);
 
 // --------------------------------------------------------------------
 // Helper class to generate evenly divided Points on a sphere
 // we use a fibonacci sphere to calculate even distribution of the dots
 
-template<int N>
+template <int N>
 class SphericalDots
 {
   public:
-	enum { P = 2 * N + 1 };
-	typedef typename std::array<Point,P>	array_type;
-	typedef typename array_type::const_iterator	iterator;
+	enum
+	{
+		P = 2 * N + 1
+	};
+	typedef typename std::array<Point, P> array_type;
+	typedef typename array_type::const_iterator iterator;
 
-	static SphericalDots& instance()
+	static SphericalDots &instance()
 	{
 		static SphericalDots sInstance;
 		return sInstance;
 	}
-	
-	size_t size() const							{ return mPoints.size(); }
-	const Point operator[](uint32_t inIx) const	{ return mPoints[inIx]; }
-	iterator begin() const						{ return mPoints.begin(); }
-	iterator end() const						{ return mPoints.end(); }
 
-	double weight() const						{ return mWeight; }
+	size_t size() const { return mPoints.size(); }
+	const Point operator[](uint32_t inIx) const { return mPoints[inIx]; }
+	iterator begin() const { return mPoints.begin(); }
+	iterator end() const { return mPoints.end(); }
+
+	double weight() const { return mWeight; }
 
 	SphericalDots()
 	{
-				
+
 		const double
 			kGoldenRatio = (1 + std::sqrt(5.0)) / 2;
-		
+
 		mWeight = (4 * kPI) / P;
-		
+
 		auto p = mPoints.begin();
-		
+
 		for (int32_t i = -N; i <= N; ++i)
 		{
 			double lat = std::asin((2.0 * i) / P);
 			double lon = std::fmod(i, kGoldenRatio) * 2 * kPI / kGoldenRatio;
-			
+
 			p->mX = sin(lon) * cos(lat);
 			p->mY = cos(lon) * cos(lat);
-			p->mZ =            sin(lat);
+			p->mZ = sin(lat);
 
 			++p;
 		}
 	}
 
   private:
-
-	array_type				mPoints;
-	double					mWeight;
+	array_type mPoints;
+	double mWeight;
 };
 
 typedef SphericalDots<50> SphericalDots_50;
 
-}
+} // namespace mmcif
diff --git a/include/cif++/Secondary.hpp b/include/cif++/Secondary.hpp
index 9eac919..ffc6e27 100644
--- a/include/cif++/Secondary.hpp
+++ b/include/cif++/Secondary.hpp
@@ -137,6 +137,22 @@ class DSSP
 	  public:
 		friend class iterator;
 
+		ResidueInfo()
+			: mImpl(nullptr)
+		{
+		}
+
+		ResidueInfo(const ResidueInfo &rhs)
+			: mImpl(rhs.mImpl)
+		{
+		}
+
+		ResidueInfo& operator=(const ResidueInfo &rhs)
+		{
+			mImpl = rhs.mImpl;
+			return *this;
+		}
+
 		explicit operator bool() const		{ return not empty(); }
 		bool empty() const					{ return mImpl == nullptr; }
 
@@ -168,6 +184,15 @@ class DSSP
 		std::tuple<ResidueInfo,double> acceptor(int i) const;
 		std::tuple<ResidueInfo,double> donor(int i) const;
 
+		/// \brief Simple compare equals
+		bool operator==(const ResidueInfo &rhs) const
+		{
+			return mImpl == rhs.mImpl;
+		}
+
+		/// \brief Returns \result true if there is a bond between two residues
+		friend bool TestBond(ResidueInfo const &a, ResidueInfo const &b);
+
 	  private:
 		ResidueInfo(Res* res) : mImpl(res) {}
 
@@ -177,7 +202,7 @@ class DSSP
 	class iterator
 	{
 	  public:
-		using iterator_category = std::input_iterator_tag;
+		using iterator_category = std::bidirectional_iterator_tag;
 		using value_type = ResidueInfo;
 		using difference_type = std::ptrdiff_t;
 		using pointer = value_type*;
@@ -198,6 +223,14 @@ class DSSP
 			return tmp;
 		}
 
+		iterator& operator--();
+		iterator operator--(int)
+		{
+			auto tmp(*this);
+			this->operator--();
+			return tmp;
+		}
+
 		bool operator==(const iterator& rhs) const		{ return mCurrent.mImpl == rhs.mCurrent.mImpl; }
 		bool operator!=(const iterator& rhs) const		{ return mCurrent.mImpl != rhs.mCurrent.mImpl; }
 
diff --git a/include/cif++/Structure.hpp b/include/cif++/Structure.hpp
index 0dd924b..260ca29 100644
--- a/include/cif++/Structure.hpp
+++ b/include/cif++/Structure.hpp
@@ -60,30 +60,108 @@ class File;
 
 class Atom
 {
+  private:
+
+	struct AtomImpl : public std::enable_shared_from_this<AtomImpl>
+	{
+		AtomImpl(cif::Datablock &db, const std::string &id, cif::Row row);
+
+		// constructor for a symmetry copy of an atom
+		AtomImpl(const AtomImpl &impl, const Point &loc, const std::string &sym_op);
+
+		AtomImpl(const AtomImpl &i) = default;
+
+		void prefetch();
+
+		int compare(const AtomImpl &b) const;
+
+		bool getAnisoU(float anisou[6]) const;
+
+		void moveTo(const Point &p);
+
+		const Compound &comp() const;
+
+		const std::string get_property(const std::string_view name) const;
+		void set_property(const std::string_view name, const std::string &value);
+
+		const cif::Datablock &mDb;
+		std::string mID;
+		AtomType mType;
+
+		std::string mAtomID;
+		std::string mCompID;
+		std::string mAsymID;
+		int mSeqID;
+		std::string mAltID;
+		std::string mAuthSeqID;
+
+		Point mLocation;
+		int mRefcount;
+		cif::Row mRow;
+
+		mutable std::vector<std::tuple<std::string,cif::detail::ItemReference>> mCachedRefs;
+
+		mutable const Compound *mCompound = nullptr;
+
+		bool mSymmetryCopy = false;
+		bool mClone = false;
+
+		std::string mSymmetryOperator = "1_555";
+	};
+
   public:
-	Atom();
-	Atom(struct AtomImpl *impl);
-	Atom(const Atom &rhs);
+
+	Atom() {}
+
+	Atom(std::shared_ptr<AtomImpl> impl)
+		: mImpl(impl) {}
+
+	Atom(const Atom &rhs)
+		: mImpl(rhs.mImpl) {}
 
 	Atom(cif::Datablock &db, cif::Row &row);
 
 	// a special constructor to create symmetry copies
 	Atom(const Atom &rhs, const Point &symmmetry_location, const std::string &symmetry_operation);
 
-	~Atom();
-
-	explicit operator bool() const { return mImpl_ != nullptr; }
+	explicit operator bool() const { return (bool)mImpl; }
 
 	// return a copy of this atom, with data copied instead of referenced
-	Atom clone() const;
+	Atom clone() const
+	{
+		auto copy = std::make_shared<AtomImpl>(*mImpl);
+		copy->mClone = true;
+		return Atom(copy);
+	}
+
+	Atom &operator=(const Atom &rhs) = default;
 
-	Atom &operator=(const Atom &rhs);
+	template <typename T>
+	T get_property(const std::string_view name) const;
+
+	void set_property(const std::string_view name, const std::string &value)
+	{
+		if (not mImpl)
+			throw std::logic_error("Error trying to modify an uninitialized atom");
+		mImpl->set_property(name, value);
+	}
+
+	template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
+	void property(const std::string_view name, const T &value)
+	{
+		set_property(name, std::to_string(value));
+	}
 
-	const std::string &id() const;
-	AtomType type() const;
+	const std::string &id() const			{ return impl().mID; }
+	AtomType type() const					{ return impl().mType; }
 
-	Point location() const;
-	void location(Point p);
+	Point location() const					{ return impl().mLocation; }
+	void location(Point p)
+	{
+		if (not mImpl)
+			throw std::logic_error("Error trying to modify an uninitialized atom");
+		mImpl->moveTo(p);
+	}
 
 	/// \brief Translate the position of this atom by \a t
 	void translate(Point t);
@@ -91,47 +169,40 @@ class Atom
 	/// \brief Rotate the position of this atom by \a q
 	void rotate(Quaternion q);
 
+	/// \brief Translate and rotate the position of this atom by \a t and \a q
+	void translateAndRotate(Point t, Quaternion q);
+
+	/// \brief Translate, rotate and translate again the coordinates this atom by \a t1 , \a q and \a t2
+	void translateRotateAndTranslate(Point t1, Quaternion q, Point t2);
+
 	// for direct access to underlying data, be careful!
-	const cif::Row getRow() const;
+	const cif::Row getRow() const			{ return impl().mRow; }
 	const cif::Row getRowAniso() const;
 
-	// Atom symmetryCopy(const Point& d, const clipper::RTop_orth& rt);
-	bool isSymmetryCopy() const;
-	std::string symmetry() const;
-	// const clipper::RTop_orth& symop() const;
+	bool isSymmetryCopy() const				{ return impl().mSymmetryCopy; }
+	std::string symmetry() const			{ return impl().mSymmetryOperator; }
 
-	const Compound &comp() const;
-	bool isWater() const;
+	const Compound &comp() const			{ return impl().comp(); }
+	bool isWater() const					{ return impl().mCompID == "HOH" or impl().mCompID == "H2O" or impl().mCompID == "WAT"; }
 	int charge() const;
 
 	float uIso() const;
-	bool getAnisoU(float anisou[6]) const;
+	bool getAnisoU(float anisou[6]) const	{ return impl().getAnisoU(anisou); }
 	float occupancy() const;
 
-	template <typename T>
-	T property(const std::string &name) const;
-
-	void property(const std::string &name, const std::string &value);
-
-	template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
-	void property(const std::string &name, const T &value)
-	{
-		property(name, std::to_string(value));
-	}
-
 	// specifications
-	std::string labelAtomID() const;
-	std::string labelCompID() const;
-	std::string labelAsymID() const;
+	const std::string& labelAtomID() const		{ return impl().mAtomID; }
+	const std::string& labelCompID() const		{ return impl().mCompID; }
+	const std::string& labelAsymID() const		{ return impl().mAsymID; }
 	std::string labelEntityID() const;
-	int labelSeqID() const;
-	std::string labelAltID() const;
-	bool isAlternate() const;
+	int labelSeqID() const						{ return impl().mSeqID; }
+	const std::string& labelAltID() const		{ return impl().mAltID; }
+	bool isAlternate() const					{ return not impl().mAltID.empty(); }
 
 	std::string authAtomID() const;
 	std::string authCompID() const;
 	std::string authAsymID() const;
-	std::string authSeqID() const;
+	const std::string& authSeqID() const		{ return impl().mAuthSeqID; }
 	std::string pdbxAuthInsCode() const;
 	std::string pdbxAuthAltID() const;
 
@@ -140,13 +211,6 @@ class Atom
 
 	bool operator==(const Atom &rhs) const;
 
-	// // get clipper format Atom
-	// clipper::Atom toClipper() const;
-
-	// Radius calculation based on integrating the density until perc of electrons is found
-	void calculateRadius(float resHigh, float resLow, float perc);
-	float radius() const;
-
 	// access data in compound for this atom
 
 	// convenience routine
@@ -158,10 +222,10 @@ class Atom
 
 	void swap(Atom &b)
 	{
-		std::swap(mImpl_, b.mImpl_);
+		std::swap(mImpl, b.mImpl);
 	}
 
-	int compare(const Atom &b) const;
+	int compare(const Atom &b) const		{ return impl().compare(*b.mImpl); }
 
 	bool operator<(const Atom &rhs) const
 	{
@@ -172,14 +236,38 @@ class Atom
 
   private:
 	friend class Structure;
+
 	void setID(int id);
 
-	AtomImpl *impl();
-	const AtomImpl *impl() const;
+	const AtomImpl &impl() const
+	{
+		if (not mImpl)
+			throw std::runtime_error("Uninitialized atom, not found?");
+		return *mImpl;
+	}
 
-	struct AtomImpl *mImpl_;
+	std::shared_ptr<AtomImpl> mImpl;
 };
 
+template <>
+inline std::string Atom::get_property<std::string>(const std::string_view name) const
+{
+	return impl().get_property(name);
+}
+
+template <>
+inline int Atom::get_property<int>(const std::string_view name) const
+{
+	auto v = impl().get_property(name);
+	return v.empty() ? 0 : stoi(v);
+}
+
+template <>
+inline float Atom::get_property<float>(const std::string_view name) const
+{
+	return stof(impl().get_property(name));
+}
+
 inline void swap(mmcif::Atom &a, mmcif::Atom &b)
 {
 	a.swap(b);
@@ -202,19 +290,16 @@ typedef std::vector<Atom> AtomView;
 class Residue
 {
   public:
-	// constructors should be private, but that's not possible for now (needed in emplace)
-
-	// constructor for waters
-	Residue(const Structure &structure, const std::string &compoundID,
-		const std::string &asymID, const std::string &authSeqID);
-
-	// constructor for a residue without a sequence number
+	// constructor
 	Residue(const Structure &structure, const std::string &compoundID,
-		const std::string &asymID);
-
-	// constructor for a residue with a sequence number
-	Residue(const Structure &structure, const std::string &compoundID,
-		const std::string &asymID, int seqID, const std::string &authSeqID);
+		const std::string &asymID, int seqID = 0, const std::string &authSeqID = {})
+		: mStructure(&structure)
+		, mCompoundID(compoundID)
+		, mAsymID(asymID)
+		, mSeqID(seqID)
+		, mAuthSeqID(authSeqID)
+	{
+	}
 
 	Residue(const Residue &rhs) = delete;
 	Residue &operator=(const Residue &rhs) = delete;
@@ -227,6 +312,11 @@ class Residue
 	const Compound &compound() const;
 	const AtomView &atoms() const;
 
+	void addAtom(const Atom &atom)
+	{
+		mAtoms.push_back(atom);
+	}
+
 	/// \brief Unique atoms returns only the atoms without alternates and the first of each alternate atom id.
 	AtomView unique_atoms() const;
 
@@ -236,6 +326,8 @@ class Residue
 	Atom atomByID(const std::string &atomID) const;
 
 	const std::string &compoundID() const { return mCompoundID; }
+	void setCompoundID(const std::string &id) { mCompoundID = id; }
+
 	const std::string &asymID() const { return mAsymID; }
 	int seqID() const { return mSeqID; }
 	std::string entityID() const;
@@ -275,6 +367,8 @@ class Residue
 
 	friend std::ostream &operator<<(std::ostream &os, const Residue &res);
 
+	friend Structure;
+
   protected:
 	Residue() {}
 
@@ -404,7 +498,7 @@ class File : public std::enable_shared_from_this<File>
 	File(const File &) = delete;
 	File &operator=(const File &) = delete;
 
-	cif::Datablock& createDatablock(const std::string &name);
+	cif::Datablock& createDatablock(const std::string_view name);
 
 	void load(const std::filesystem::path &path);
 	void save(const std::filesystem::path &path);
@@ -461,9 +555,30 @@ class Structure
 	Atom getAtomByLabel(const std::string &atomID, const std::string &asymID,
 		const std::string &compID, int seqID, const std::string &altID = "");
 
+	/// \brief Return the atom closest to point \a p
+	Atom getAtomByPosition(Point p) const;
+
+	/// \brief Return the atom closest to point \a p with atom type \a type in a residue of type \a res_type
+	Atom getAtomByPositionAndType(Point p, std::string_view type, std::string_view res_type) const;
+
 	/// \brief Get a residue, if \a seqID is zero, the non-polymers are searched
 	const Residue &getResidue(const std::string &asymID, const std::string &compID, int seqID = 0) const;
 
+	/// \brief Get a residue, if \a seqID is zero, the non-polymers are searched
+	Residue &getResidue(const std::string &asymID, const std::string &compID, int seqID = 0);
+
+	/// \brief Get a the single residue for an asym with id \a asymID
+	const Residue &getResidue(const std::string &asymID) const;
+
+	/// \brief Get a the single residue for an asym with id \a asymID
+	Residue &getResidue(const std::string &asymID);
+
+	/// \brief Get a the residue for atom \a atom
+	Residue &getResidue(const mmcif::Atom &atom);
+
+	/// \brief Get a the residue for atom \a atom
+	const Residue &getResidue(const mmcif::Atom &atom) const;
+
 	// map between auth and label locations
 
 	std::tuple<std::string, int, std::string> MapAuthToLabel(const std::string &asymID,
@@ -488,7 +603,7 @@ class Structure
 	void removeAtom(Atom &a);
 	void swapAtoms(Atom &a1, Atom &a2); // swap the labels for these atoms
 	void moveAtom(Atom &a, Point p);    // move atom to a new location
-	void changeResidue(const Residue &res, const std::string &newCompound,
+	void changeResidue(Residue &res, const std::string &newCompound,
 		const std::vector<std::tuple<std::string, std::string>> &remappedAtoms);
 
 	/// \brief Create a new non-polymer entity, returns new ID
@@ -504,6 +619,14 @@ class Structure
 	/// \return				The newly create asym ID
 	std::string createNonpoly(const std::string &entity_id, const std::vector<mmcif::Atom> &atoms);
 
+	/// \brief Create a new NonPolymer struct_asym with atoms constructed from info in \a atom_info, returns asym_id.
+	/// This method creates new atom records filled with info from the info.
+	///
+	/// \param entity_id	The entity ID of the new nonpoly
+	/// \param atoms		The array of sets of cif::item data containing the data for the atoms.
+	/// \return				The newly create asym ID
+	std::string createNonpoly(const std::string &entity_id, std::vector<std::vector<cif::Item>> &atom_info);
+
 	/// \brief To sort the atoms in order of model > asym-id > res-id > atom-id
 	/// Will asssign new atom_id's to all atoms. Be carefull
 	void sortAtoms();
@@ -514,20 +637,27 @@ class Structure
 	/// \brief Rotate the coordinates of all atoms in the structure by \a q
 	void rotate(Quaternion t);
 
+	/// \brief Translate and rotate the coordinates of all atoms in the structure by \a t and \a q
+	void translateAndRotate(Point t, Quaternion q);
+
+	/// \brief Translate, rotate and translate again the coordinates of all atoms in the structure by \a t1 , \a q and \a t2
+	void translateRotateAndTranslate(Point t1, Quaternion q, Point t2);
+
 	const std::vector<Residue> &getNonPolymers() const { return mNonPolymers; }
 	const std::vector<Residue> &getBranchResidues() const { return mBranchResidues; }
 
 	void cleanupEmptyCategories();
 
+	/// \brief Direct access to underlying data
+	cif::Category &category(std::string_view name) const;
+	cif::Datablock &datablock() const;
+
   private:
 	friend Polymer;
 	friend Residue;
 	// friend residue_view;
 	// friend residue_iterator;
 
-	cif::Category &category(const char *name) const;
-	cif::Datablock &datablock() const;
-
 	std::string insertCompound(const std::string &compoundID, bool isEntity);
 
 	void loadData();
diff --git a/include/cif++/Symmetry.hpp b/include/cif++/Symmetry.hpp
index 17236d3..a4061ac 100644
--- a/include/cif++/Symmetry.hpp
+++ b/include/cif++/Symmetry.hpp
@@ -37,6 +37,11 @@ namespace mmcif
 
 // --------------------------------------------------------------------
 
+enum class SpacegroupName
+{
+	full, xHM, Hall
+};
+
 struct Spacegroup
 {
 	const char* name;
@@ -133,6 +138,7 @@ CIFPP_EXPORT extern const std::size_t kSymopNrTableSize;
 
 // --------------------------------------------------------------------
 
-int GetSpacegroupNumber(std::string spacegroup);	// alternative for clipper's parsing code
+int GetSpacegroupNumber(std::string spacegroup);						// alternative for clipper's parsing code, using SpacegroupName::full
+int GetSpacegroupNumber(std::string spacegroup, SpacegroupName type);	// alternative for clipper's parsing code
 
 }
diff --git a/src/AtomType.cpp b/src/AtomType.cpp
index 6815df7..951b0bc 100644
--- a/src/AtomType.cpp
+++ b/src/AtomType.cpp
@@ -175,860 +175,432 @@ struct SFDataArrayElement
 };
 
 SFDataArrayElement kWKSFData[] = {
-
-//{ H,	0,	{{	0.493002,	0.322912,	0.140191,	0.040810,	0.003038 },
-//			 {	10.510900,	26.125700,	3.142360,	57.799698,	0.000000  }}},
-////{ D,	0,	{{	0.493002,	0.322912,	0.140191,	0.040810,	0.003038 },
-////			 {	10.510900,	26.125700,	3.142360,	57.799698,	0.000000  }}},
-//{ H, -1,	{{	0.897661,	0.565616,	0.415815,	0.116973,	0.002389 },
-//			 {	53.136799,	15.187000,	186.575989,	3.567090,	0.000000 }}},
-//{ He,	0,	{{	0.873400,	0.630900,	0.311200,	0.178000,	0.006400 },
-//			 {	9.103700,	3.356800,	22.927601,	0.982100,	0.000000 }}},
-//{ Li,	0,	{{	1.128200,	0.750800,	0.617500,	0.465300,	0.037700 },
-//			 {	3.954600,	1.052400,	85.390503,	168.261002,	0.000000 }}},
-//{ Li, +1,	{{	0.696800,	0.788800,	0.341400,	0.156300,	0.016700 },
-//			 {	4.623700,	1.955700,	0.631600,	10.095300,	0.000000 }}},
-//{ Be,	0,	{{	1.591900,	1.127800,	0.539100,	0.702900,	0.038500 },
-//			 {	43.642700,	1.862300,	103.483002,	0.542000,	0.000000 }}},
-//{ Be, +2,	{{	6.260300,	0.884900,	0.799300,	0.164700,	-6.109200 },
-//			 {	0.002700,	0.831300,	2.275800,	5.114600,	0.000000 }}},
-//{ B,	0,	{{	2.054500,	1.332600,	1.097900,	0.706800,	-0.193200 },
-//			 {	23.218500,	1.021000,	60.349800,	0.140300,	0.000000 }}},
-//{ C,	0,	{{	2.310000,	1.020000,	1.588600,	0.865000,	0.215600 },
-//			 {	20.843899,	10.207500,	0.568700,	51.651199,	0.000000 }}},
-//{ C,	AtomTypeTraits::kWKSFVal,
-//			{{	2.260690,	1.561650,	1.050750,	0.839259,	0.286977 },
-//			 {	22.690701,	0.656665,	9.756180,	55.594898,	0.000000 }}},
-//{ N,	0,	{{	12.212600,	3.132200,	2.012500,	1.166300,	-11.528999 },
-//			 {	0.005700,	9.893300,	28.997499,	0.582600,	0.000000 }}},
-//{ O,	0,	{{	3.048500,	2.286800,	1.546300,	0.867000,	0.250800 },
-//			 {	13.277100,	5.701100,	0.323900,	32.908897,	0.000000 }}},
-//{ O, -1,	{{	4.191600,	1.639690,	1.526730,	-20.306999,	21.941200 },
-//			 {	12.857300,	4.172360,	47.017899,	-0.014040,	0.000000 }}},
-//{ F,	0,	{{	3.539200,	2.641200,	1.517000,	1.024300,	0.277600 },
-//			 {	10.282499,	4.294400,	0.261500,	26.147600,	0.000000 }}},
-//{ F, -1,	{{	3.632200,	3.510570,	1.260640,	0.940706,	0.653396 },
-//			 {	5.277560,	14.735300,	0.442258,	47.343700,	0.000000 }}},
-//{ Ne,	0,	{{	3.955300,	3.112500,	1.454600,	1.125100,	0.351500 },
-//			 {	8.404200,	3.426200,	0.230600,	21.718399,	0.000000 }}},
-//{ Na,	0,	{{	4.762600,	3.173600,	1.267400,	1.112800,	0.676000 },
-//			 {	3.285000,	8.842199,	0.313600,	129.423996,	0.000000 }}},
-//{ Na, +1,	{{	3.256500,	3.936200,	1.399800,	1.003200,	0.404000 },
-//			 {	2.667100,	6.115300,	0.200100,	14.039000,	0.000000 }}},
-//{ Mg,	0,	{{	5.420400,	2.173500,	1.226900,	2.307300,	0.858400 },
-//			 {	2.827500,	79.261101,	0.380800,	7.193700,	0.000000 }}},
-//{ Mg, +2,	{{	3.498800,	3.837800,	1.328400,	0.849700,	0.485300 },
-//			 {	2.167600,	4.754200,	0.185000,	10.141100,	0.000000 }}},
-//{ Al,	0,	{{	6.420200,	1.900200,	1.593600,	1.964600,	1.115100 },
-//			 {	3.038700,	0.742600,	31.547199,	85.088600,	0.000000 }}},
-//{ Al, +3,	{{	4.174480,	3.387600,	1.202960,	0.528137,	0.706786 },
-//			 {	1.938160,	4.145530,	0.228753,	8.285240,	0.000000 }}},
-//{ Si,	0,	{{	6.291500,	3.035300,	1.989100,	1.541000,	1.140700 },
-//			 {	2.438600,	32.333698,	0.678500,	81.693695,	0.000000 }}},
-//{ Si,	AtomTypeTraits::kWKSFVal,
-//			{{	5.662690,	3.071640,	2.624460,	1.393200,	1.247070 },
-//			 {	2.665200,	38.663399,	0.916946,	93.545799,	0.000000 }}},
-//{ Si, +4,	{{	4.439180,	3.203450,	1.194530,	0.416530,	0.746297 },
-//			 {	1.641670,	3.437570,	0.214900,	6.653650,	0.000000 }}},
-//{ P,	0,	{{	6.434500,	4.179100,	1.780000,	1.490800,	1.114900 },
-//			 {	1.906700,	27.157000,	0.526000,	68.164497,	0.000000 }}},
-//{ S,	0,	{{	6.905300,	5.203400,	1.437900,	1.586300,	0.866900 },
-//			 {	1.467900,	22.215099,	0.253600,	56.172001,	0.000000 }}},
-//{ Cl,	0,	{{	11.460400,	7.196400,	6.255600,	1.645500,	-9.557400 },
-//			 {	0.010400,	1.166200,	18.519400,	47.778400,	0.000000 }}},
-//{ Cl, -1,	{{	18.291500,	7.208400,	6.533700,	2.338600,	-16.378000 },
-//			 {	0.006600,	1.171700,	19.542400,	60.448601,	0.000000 }}},
-//{ Ar,	0,	{{	7.484500,	6.772300,	0.653900,	1.644200,	1.444500 },
-//			 {	0.907200,	14.840700,	43.898300,	33.392899,	0.000000 }}},
-//{ K,	0,	{{	8.218599,	7.439800,	1.051900,	0.865900,	1.422800 },
-//			 {	12.794900,	0.774800,	213.186996,	41.684097,	0.000000 }}},
-//{ K, +1,	{{	7.957800,	7.491700,	6.359000,	1.191500,	-4.997800 },
-//			 {	12.633100,	0.767400,	-0.002000,	31.912800,	0.000000 }}},
-//{ Ca,	0,	{{	8.626600,	7.387300,	1.589900,	1.021100,	1.375100 },
-//			 {	10.442100,	0.659900,	85.748398,	178.436996,	0.000000 }}},
-//{ Ca, +2,	{{	15.634800,	7.951800,	8.437200,	0.853700,	-14.875000 },
-//			 {	-0.007400,	0.608900,	10.311600,	25.990499,	0.000000 }}},
-//{ Sc,	0,	{{	9.189000,	7.367900,	1.640900,	1.468000,	1.332900 },
-//			 {	9.021299,	0.572900,	136.108002,	51.353100,	0.000000 }}},
-//{ Sc, +3,	{{	13.400800,	8.027300,	1.659430,	1.579360,	-6.666700 },
-//			 {	0.298540,	7.962900,	-0.286040,	16.066200,	0.000000 }}},
-//{ Ti,	0,	{{	9.759500,	7.355800,	1.699100,	1.902100,	1.280700 },
-//			 {	7.850800,	0.500000,	35.633801,	116.104996,	0.000000 }}},
-//{ Ti, +2,	{{	9.114230,	7.621740,	2.279300,	0.087899,	0.897155 },
-//			 {	7.524300,	0.457585,	19.536100,	61.655800,	0.000000 }}},
-//{ Ti, +3,	{{	17.734400,	8.738160,	5.256910,	1.921340,	-14.652000 },
-//			 {	0.220610,	7.047160,	-0.157620,	15.976800,	0.000000 }}},
-//{ Ti, +4,	{{	19.511400,	8.234730,	2.013410,	1.520800,	-13.280000 },
-//			 {	0.178847,	6.670180,	-0.292630,	12.946400,	0.000000 }}},
-//{ V,	0,	{{	10.297100,	7.351100,	2.070300,	2.057100,	1.219900 },
-//			 {	6.865700,	0.438500,	26.893799,	102.477997,	0.000000 }}},
-//{ V, +2,	{{	10.106000,	7.354100,	2.288400,	0.022300,	1.229800 },
-//			 {	6.881800,	0.440900,	20.300400,	115.122002,	0.000000 }}},
-//{ V, +3,	{{	9.431410,	7.741900,	2.153430,	0.016865,	0.656565 },
-//			 {	6.395350,	0.383349,	15.190800,	63.969002,	0.000000 }}},
-//{ V, +5,	{{	15.688700,	8.142080,	2.030810,	-9.576000,	1.714300 },
-//			 {	0.679003,	5.401350,	9.972780,	0.940464,	0.000000 }}},
-//{ Cr,	0,	{{	10.640600,	7.353700,	3.324000,	1.492200,	1.183200 },
-//			 {	6.103800,	0.392000,	20.262600,	98.739899,	0.000000 }}},
-//{ Cr, +2,	{{	9.540340,	7.750900,	3.582740,	0.509107,	0.616898 },
-//			 {	5.660780,	0.344261,	13.307500,	32.422401,	0.000000 }}},
-//{ Cr, +3,	{{	9.680900,	7.811360,	2.876030,	0.113575,	0.518275 },
-//			 {	5.594630,	0.334393,	12.828800,	32.876099,	0.000000 }}},
-//{ Mn,	0,	{{	11.281900,	7.357300,	3.019300,	2.244100,	1.089600 },
-//			 {	5.340900,	0.343200,	17.867399,	83.754303,	0.000000 }}},
-//{ Mn, +2,	{{	10.806100,	7.362000,	3.526800,	0.218400,	1.087400 },
-//			 {	5.279600,	0.343500,	14.343000,	41.323502,	0.000000 }}},
-//{ Mn, +3,	{{	9.845210,	7.871940,	3.565310,	0.323613,	0.393974 },
-//			 {	4.917970,	0.294393,	10.817100,	24.128099,	0.000000 }}},
-//{ Mn, +4,	{{	9.962530,	7.970570,	2.760670,	0.054447,	0.251877 },
-//			 {	4.848500,	0.283303,	10.485200,	27.573000,	0.000000 }}},
-//{ Fe,	0,	{{	11.769500,	7.357300,	3.522200,	2.304500,	1.036900 },
-//			 {	4.761100,	0.307200,	15.353500,	76.880501,	0.000000 }}},
-//{ Fe, +2,	{{	11.042400,	7.374000,	4.134600,	0.439900,	1.009700 },
-//			 {	4.653800,	0.305300,	12.054600,	31.280899,	0.000000 }}},
-//{ Fe, +3,	{{	11.176400,	7.386300,	3.394800,	0.072400,	0.970700 },
-//			 {	4.614700,	0.300500,	11.672900,	38.556599,	0.000000 }}},
-//{ Co,	0,	{{	12.284100,	7.340900,	4.003400,	2.348800,	1.011800 },
-//			 {	4.279100,	0.278400,	13.535900,	71.169197,	0.000000 }}},
-//{ Co, +2,	{{	11.229600,	7.388300,	4.739300,	0.710800,	0.932400 },
-//			 {	4.123100,	0.272600,	10.244300,	25.646599,	0.000000 }}},
-//{ Co, +3,	{{	10.337999,	7.881730,	4.767950,	0.725591,	0.286667 },
-//			 {	3.909690,	0.238668,	8.355830,	18.349100,	0.000000 }}},
-//{ Ni,	0,	{{	12.837600,	7.292000,	4.443800,	2.380000,	1.034100 },
-//			 {	3.878500,	0.256500,	12.176300,	66.342102,	0.000000 }}},
-//{ Ni, +2,	{{	11.416600,	7.400500,	5.344200,	0.977300,	0.861400 },
-//			 {	3.676600,	0.244900,	8.873000,	22.162600,	0.000000 }}},
-//{ Ni, +3,	{{	10.780600,	7.758680,	5.227460,	0.847114,	0.386044 },
-//			 {	3.547700,	0.223140,	7.644680,	16.967300,	0.000000 }}},
-//{ Cu,	0,	{{	13.337999,	7.167600,	5.615800,	1.673500,	1.191000 },
-//			 {	3.582800,	0.247000,	11.396600,	64.812599,	0.000000 }}},
-//{ Cu, +1,	{{	11.947500,	7.357300,	6.245500,	1.557800,	0.890000 },
-//			 {	3.366900,	0.227400,	8.662500,	25.848700,	0.000000 }}},
-//{ Cu, +2,	{{	11.816800,	7.111810,	5.781350,	1.145230,	1.144310 },
-//			 {	3.374840,	0.244078,	7.987600,	19.896999,	0.000000 }}},
-//{ Zn,	0,	{{	14.074300,	7.031800,	5.162500,	2.410000,	1.304100 },
-//			 {	3.265500,	0.233300,	10.316299,	58.709702,	0.000000 }}},
-//{ Zn, +2,	{{	11.971900,	7.386200,	6.466800,	1.394000,	0.780700 },
-//			 {	2.994600,	0.203100,	7.082600,	18.099499,	0.000000 }}},
-//{ Ga,	0,	{{	15.235400,	6.700600,	4.359100,	2.962300,	1.718900 },
-//			 {	3.066900,	0.241200,	10.780500,	61.413498,	0.000000 }}},
-//{ Ga, +3,	{{	12.691999,	6.698830,	6.066920,	1.006600,	1.535450 },
-//			 {	2.812620,	0.227890,	6.364410,	14.412200,	0.000000 }}},
-//{ Ge,	0,	{{	16.081600,	6.374700,	3.706800,	3.683000,	2.131300 },
-//			 {	2.850900,	0.251600,	11.446800,	54.762501,	0.000000 }}},
-//{ Ge, +4,	{{	12.917200,	6.700030,	6.067910,	0.859041,	1.455720 },
-//			 {	2.537180,	0.205855,	5.479130,	11.603000,	0.000000 }}},
-//{ As,	0,	{{	16.672300,	6.070100,	3.431300,	4.277900,	2.531000 },
-//			 {	2.634500,	0.264700,	12.947900,	47.797199,	0.000000 }}},
-//{ Se,	0,	{{	17.000599,	5.819600,	3.973100,	4.354300,	2.840900 },
-//			 {	2.409800,	0.272600,	15.237200,	43.816299,	0.000000 }}},
-//{ Br,	0,	{{	17.178900,	5.235800,	5.637700,	3.985100,	2.955700 },
-//			 {	2.172300,	16.579599,	0.260900,	41.432800,	0.000000 }}},
-//{ Br, -1,	{{	17.171799,	6.333800,	5.575400,	3.727200,	3.177600 },
-//			 {	2.205900,	19.334499,	0.287100,	58.153500,	0.000000 }}},
-//{ Kr,	0,	{{	17.355499,	6.728600,	5.549300,	3.537500,	2.825000 },
-//			 {	1.938400,	16.562300,	0.226100,	39.397202,	0.000000 }}},
-//{ Rb,	0,	{{	17.178400,	9.643499,	5.139900,	1.529200,	3.487300 },
-//			 {	1.788800,	17.315100,	0.274800,	164.933990,	0.000000 }}},
-//{ Rb, +1,	{{	17.581600,	7.659800,	5.898100,	2.781700,	2.078200 },
-//			 {	1.713900,	14.795700,	0.160300,	31.208700,	0.000000 }}},
-//{ Sr,	0,	{{	17.566299,	9.818399,	5.422000,	2.669400,	2.506400 },
-//			 {	1.556400,	14.098800,	0.166400,	132.376007,	0.000000 }}},
-//{ Sr, +2,	{{	18.087400,	8.137300,	2.565400,	-34.193001,	41.402500 },
-//			 {	1.490700,	12.696300,	24.565100,	-0.013800,	0.000000 }}},
-//{ Y,	0,	{{	17.775999,	10.294600,	5.726290,	3.265880,	1.912130 },
-//			 {	1.402900,	12.800600,	0.125599,	104.353996,	0.000000 }}},
-//{ Y, +3,	{{	17.926800,	9.153100,	1.767950,	-33.108002,	40.260201 },
-//			 {	1.354170,	11.214500,	22.659901,	-0.013190,	0.000000 }}},
-//{ Zr,	0,	{{	17.876499,	10.948000,	5.417320,	3.657210,	2.069290 },
-//			 {	1.276180,	11.916000,	0.117622,	87.662697,	0.000000 }}},
-//{ Zr, +4,	{{	18.166800,	10.056200,	1.011180,	-2.647900,	9.414539 },
-//			 {	1.214800,	10.148300,	21.605400,	-0.102760,	0.000000 }}},
-//{ Nb,	0,	{{	17.614201,	12.014400,	4.041830,	3.533460,	3.755910 },
-//			 {	1.188650,	11.766000,	0.204785,	69.795700,	0.000000 }}},
-//{ Nb, +3,	{{	19.881199,	18.065300,	11.017700,	1.947150,	-12.912000 },
-//			 {	0.019175,	1.133050,	10.162100,	28.338900,	0.000000 }}},
-//{ Nb, +5,	{{	17.916300,	13.341700,	10.799000,	0.337905,	-6.393400 },
-//			 {	1.124460,	0.028781,	9.282060,	25.722799,	0.000000 }}},
-//{ Mo,	0,	{{	3.702500,	17.235600,	12.887600,	3.742900,	4.387500 },
-//			 {	0.277200,	1.095800,	11.004000,	61.658401,	0.000000 }}},
-//{ Mo, +3,	{{	21.166401,	18.201700,	11.742300,	2.309510,	-14.421000 },
-//			 {	0.014734,	1.030310,	9.536590,	26.630699,	0.000000 }}},
-//{ Mo, +5,	{{	21.014900,	18.099199,	11.463200,	0.740625,	-14.316000 },
-//			 {	0.014345,	1.022380,	8.788090,	23.345200,	0.000000 }}},
-//{ Mo, +6,	{{	17.887100,	11.175000,	6.578910,	0.000000,	0.344941 },
-//			 {	1.036490,	8.480610,	0.058881,	0.000000,	0.000000 }}},
-//{ Tc,	0,	{{	19.130100,	11.094800,	4.649010,	2.712630,	5.404280 },
-//			 {	0.864132,	8.144870,	21.570700,	86.847198,	0.000000 }}},
-//{ Ru,	0,	{{	19.267399,	12.918200,	4.863370,	1.567560,	5.378740 },
-//			 {	0.808520,	8.434669,	24.799700,	94.292801,	0.000000 }}},
-//{ Ru, +3,	{{	18.563801,	13.288500,	9.326019,	3.009640,	-3.189200 },
-//			 {	0.847329,	8.371640,	0.017662,	22.886999,	0.000000 }}},
-//{ Ru, +4,	{{	18.500299,	13.178699,	4.713040,	2.185350,	1.423570 },
-//			 {	0.844582,	8.125340,	0.364950,	20.850399,	0.000000 }}},
-//{ Rh,	0,	{{	19.295700,	14.350100,	4.734250,	1.289180,	5.328000 },
-//			 {	0.751536,	8.217580,	25.874901,	98.606201,	0.000000 }}},
-//{ Rh, +3,	{{	18.878500,	14.125900,	3.325150,	-6.198900,	11.867800 },
-//			 {	0.764252,	7.844380,	21.248699,	-0.010360,	0.000000 }}},
-//{ Rh, +4,	{{	18.854500,	13.980600,	2.534640,	-5.652600,	11.283500 },
-//			 {	0.760825,	7.624360,	19.331699,	-0.010200,	0.000000 }}},
-//{ Pd,	0,	{{	19.331900,	15.501699,	5.295370,	0.605844,	5.265930 },
-//			 {	0.698655,	7.989290,	25.205200,	76.898598,	0.000000 }}},
-//{ Pd, +2,	{{	19.170099,	15.209600,	4.322340,	0.000000,	5.291600 },
-//			 {	0.696219,	7.555730,	22.505699,	0.000000,	0.000000 }}},
-//{ Pd, +4,	{{	19.249300,	14.790000,	2.892890,	-7.949200,	13.017400 },
-//			 {	0.683839,	7.148330,	17.914400,	0.005127,	0.000000 }}},
-//{ Ag,	0,	{{	19.280800,	16.688499,	4.804500,	1.046300,	5.179000 },
-//			 {	0.644600,	7.472600,	24.660500,	99.815598,	0.000000 }}},
-//{ Ag, +1,	{{	19.181200,	15.971900,	5.274750,	0.357534,	5.215720 },
-//			 {	0.646179,	7.191230,	21.732599,	66.114700,	0.000000 }}},
-//{ Ag, +2,	{{	19.164299,	16.245600,	4.370900,	0.000000,	5.214040 },
-//			 {	0.645643,	7.185440,	21.407200,	0.000000,	0.000000 }}},
-//{ Cd,	0,	{{	19.221399,	17.644400,	4.461000,	1.602900,	5.069400 },
-//			 {	0.594600,	6.908900,	24.700800,	87.482498,	0.000000 }}},
-//{ Cd, +2,	{{	19.151400,	17.253500,	4.471280,	0.000000,	5.119370 },
-//			 {	0.597922,	6.806390,	20.252100,	0.000000,	0.000000 }}},
-//{ In,	0,	{{	19.162399,	18.559601,	4.294800,	2.039600,	4.939100 },
-//			 {	0.547600,	6.377600,	25.849899,	92.802902,	0.000000 }}},
-//{ In, +3,	{{	19.104500,	18.110800,	3.788970,	0.000000,	4.996350 },
-//			 {	0.551522,	6.324700,	17.359501,	0.000000,	0.000000 }}},
-//{ Sn,	0,	{{	19.188900,	19.100500,	4.458500,	2.466300,	4.782100 },
-//			 {	5.830300,	0.503100,	26.890900,	83.957100,	0.000000 }}},
-//{ Sn, +2,	{{	19.109400,	19.054800,	4.564800,	0.487000,	4.786100 },
-//			 {	0.503600,	5.837800,	23.375200,	62.206100,	0.000000 }}},
-//{ Sn, +4,	{{	18.933300,	19.713100,	3.418200,	0.019300,	3.918200 },
-//			 {	5.764000,	0.465500,	14.004900,	-0.758300,	0.000000 }}},
-//{ Sb,	0,	{{	19.641800,	19.045500,	5.037100,	2.682700,	4.590900 },
-//			 {	5.303400,	0.460700,	27.907400,	75.282501,	0.000000 }}},
-//{ Sb, +3,	{{	18.975500,	18.932999,	5.107890,	0.288753,	4.696260 },
-//			 {	0.467196,	5.221260,	19.590200,	55.511299,	0.000000 }}},
-//{ Sb, +5,	{{	19.868500,	19.030199,	2.412530,	0.000000,	4.692630 },
-//			 {	5.448530,	0.467973,	14.125900,	0.000000,	0.000000 }}},
-//{ Te,	0,	{{	19.964399,	19.013800,	6.144870,	2.523900,	4.352000 },
-//			 {	4.817420,	0.420885,	28.528400,	70.840302,	0.000000 }}},
-//{ I,	0,	{{	20.147200,	18.994900,	7.513800,	2.273500,	4.071200 },
-//			 {	4.347000,	0.381400,	27.765999,	66.877602,	0.000000 }}},
-//{ I, -1,	{{	20.233200,	18.997000,	7.806900,	2.886800,	4.071400 },
-//			 {	4.357900,	0.381500,	29.525900,	84.930397,	0.000000 }}},
-//{ Xe,	0,	{{	20.293301,	19.029800,	8.976700,	1.990000,	3.711800 },
-//			 {	3.928200,	0.344000,	26.465900,	64.265800,	0.000000 }}},
-//{ Cs,	0,	{{	20.389200,	19.106199,	10.662000,	1.495300,	3.335200 },
-//			 {	3.569000,	0.310700,	24.387899,	213.903992,	0.000000 }}},
-//{ Cs, +1,	{{	20.352400,	19.127800,	10.282100,	0.961500,	3.279100 },
-//			 {	3.552000,	0.308600,	23.712799,	59.456497,	0.000000 }}},
-//{ Ba,	0,	{{	20.336100,	19.297001,	10.888000,	2.695900,	2.773100 },
-//			 {	3.216000,	0.275600,	20.207300,	167.201996,	0.000000 }}},
-//{ Ba, +2,	{{	20.180700,	19.113600,	10.905399,	0.776340,	3.029020 },
-//			 {	3.213670,	0.283310,	20.055799,	51.745998,	0.000000 }}},
-//{ La,	0,	{{	20.577999,	19.598999,	11.372700,	3.287190,	2.146780 },
-//			 {	2.948170,	0.244475,	18.772600,	133.123993,	0.000000 }}},
-//{ La, +3,	{{	20.248899,	19.376301,	11.632299,	0.336048,	2.408600 },
-//			 {	2.920700,	0.250698,	17.821100,	54.945297,	0.000000 }}},
-//{ Ce,	0,	{{	21.167099,	19.769501,	11.851299,	3.330490,	1.862640 },
-//			 {	2.812190,	0.226836,	17.608299,	127.112999,	0.000000 }}},
-//{ Ce, +3,	{{	20.803600,	19.559000,	11.936900,	0.612376,	2.090130 },
-//			 {	2.776910,	0.231540,	16.540800,	43.169201,	0.000000 }}},
-//{ Ce, +4,	{{	20.323500,	19.818600,	12.123300,	0.144583,	1.591800 },
-//			 {	2.659410,	0.218850,	15.799200,	62.235500,	0.000000 }}},
-//{ Pr,	0,	{{	22.043999,	19.669701,	12.385600,	2.824280,	2.058300 },
-//			 {	2.773930,	0.222087,	16.766899,	143.643997,	0.000000 }}},
-//{ Pr, +3,	{{	21.372700,	19.749100,	12.132900,	0.975180,	1.771320 },
-//			 {	2.645200,	0.214299,	15.323000,	36.406502,	0.000000 }}},
-//{ Pr, +4,	{{	20.941299,	20.053900,	12.466800,	0.296689,	1.242850 },
-//			 {	2.544670,	0.202481,	14.813700,	45.464298,	0.000000 }}},
-//{ Nd,	0,	{{	22.684500,	19.684700,	12.774000,	2.851370,	1.984860 },
-//			 {	2.662480,	0.210628,	15.885000,	137.903000,	0.000000 }}},
-//{ Nd, +3,	{{	21.961000,	19.933899,	12.120000,	1.510310,	1.475880 },
-//			 {	2.527220,	0.199237,	14.178300,	30.871700,	0.000000 }}},
-//{ Pm,	0,	{{	23.340500,	19.609501,	13.123500,	2.875160,	2.028760 },
-//			 {	2.562700,	0.202088,	15.100900,	132.720993,	0.000000 }}},
-//{ Pm, +3,	{{	22.552700,	20.110800,	12.067100,	2.074920,	1.194990 },
-//			 {	2.417400,	0.185769,	13.127500,	27.449100,	0.000000 }}},
-//{ Sm,	0,	{{	24.004200,	19.425800,	13.439600,	2.896040,	2.209630 },
-//			 {	2.472740,	0.196451,	14.399600,	128.007004,	0.000000 }}},
-//{ Sm, +3,	{{	23.150400,	20.259899,	11.920200,	2.714880,	0.954586 },
-//			 {	2.316410,	0.174081,	12.157100,	24.824200,	0.000000 }}},
-//{ Eu,	0,	{{	24.627399,	19.088600,	13.760300,	2.922700,	2.574500 },
-//			 {	2.387900,	0.194200,	13.754600,	123.173996,	0.000000 }}},
-//{ Eu, +2,	{{	24.006300,	19.950399,	11.803400,	3.872430,	1.363890 },
-//			 {	2.277830,	0.173530,	11.609600,	26.515600,	0.000000 }}},
-//{ Eu, +3,	{{	23.749699,	20.374500,	11.850900,	3.265030,	0.759344 },
-//			 {	2.222580,	0.163940,	11.311000,	22.996599,	0.000000 }}},
-//{ Gd,	0,	{{	25.070900,	19.079800,	13.851800,	3.545450,	2.419600 },
-//			 {	2.253410,	0.181951,	12.933100,	101.397995,	0.000000 }}},
-//{ Gd, +3,	{{	24.346600,	20.420799,	11.870800,	3.714900,	0.645089 },
-//			 {	2.135530,	0.155525,	10.578199,	21.702900,	0.000000 }}},
-//{ Tb,	0,	{{	25.897600,	18.218500,	14.316700,	2.953540,	3.582240 },
-//			 {	2.242560,	0.196143,	12.664800,	115.362000,	0.000000 }}},
-//{ Tb, +3,	{{	24.955900,	20.327099,	12.247100,	3.773000,	0.691967 },
-//			 {	2.056010,	0.149525,	10.049900,	21.277300,	0.000000 }}},
-//{ Dy,	0,	{{	26.507000,	17.638300,	14.559600,	2.965770,	4.297280 },
-//			 {	2.180200,	0.202172,	12.189899,	111.874001,	0.000000 }}},
-//{ Dy, +3,	{{	25.539499,	20.286100,	11.981200,	4.500730,	0.689690 },
-//			 {	1.980400,	0.143384,	9.349720,	19.580999,	0.000000 }}},
-//{ Ho,	0,	{{	26.904900,	17.293999,	14.558300,	3.638370,	4.567960 },
-//			 {	2.070510,	0.197940,	11.440700,	92.656601,	0.000000 }}},
-//{ Ho, +3,	{{	26.129601,	20.099400,	11.978800,	4.936760,	0.852795 },
-//			 {	1.910720,	0.139358,	8.800180,	18.590799,	0.000000 }}},
-//{ Er,	0,	{{	27.656300,	16.428499,	14.977900,	2.982330,	5.920460 },
-//			 {	2.073560,	0.223545,	11.360400,	105.703003,	0.000000 }}},
-//{ Er, +3,	{{	26.722000,	19.774799,	12.150600,	5.173790,	1.176130 },
-//			 {	1.846590,	0.137290,	8.362249,	17.897400,	0.000000 }}},
-//{ Tm,	0,	{{	28.181900,	15.885099,	15.154200,	2.987060,	6.756210 },
-//			 {	2.028590,	0.238849,	10.997499,	102.960999,	0.000000 }}},
-//{ Tm, +3,	{{	27.308300,	19.332001,	12.333900,	5.383480,	1.639290 },
-//			 {	1.787110,	0.136974,	7.967780,	17.292200,	0.000000 }}},
-//{ Yb,	0,	{{	28.664101,	15.434500,	15.308700,	2.989630,	7.566720 },
-//			 {	1.988900,	0.257119,	10.664700,	100.417000,	0.000000 }}},
-//{ Yb, +2,	{{	28.120899,	17.681700,	13.333500,	5.146570,	3.709830 },
-//			 {	1.785030,	0.159970,	8.183040,	20.389999,	0.000000 }}},
-//{ Yb, +3,	{{	27.891700,	18.761400,	12.607200,	5.476470,	2.260010 },
-//			 {	1.732720,	0.138790,	7.644120,	16.815300,	0.000000 }}},
-//{ Lu,	0,	{{	28.947599,	15.220800,	15.100000,	3.716010,	7.976280 },
-//			 {	1.901820,	9.985189,	0.261033,	84.329803,	0.000000 }}},
-//{ Lu, +3,	{{	28.462799,	18.121000,	12.842899,	5.594150,	2.975730 },
-//			 {	1.682160,	0.142292,	7.337270,	16.353500,	0.000000 }}},
-//{ Hf,	0,	{{	29.143999,	15.172600,	14.758600,	4.300130,	8.581540 },
-//			 {	1.832620,	9.599899,	0.275116,	72.028999,	0.000000 }}},
-//{ Hf, +4,	{{	28.813099,	18.460100,	12.728500,	5.599270,	2.396990 },
-//			 {	1.591360,	0.128903,	6.762320,	14.036600,	0.000000 }}},
-//{ Ta,	0,	{{	29.202400,	15.229300,	14.513500,	4.764920,	9.243540 },
-//			 {	1.773330,	9.370460,	0.295977,	63.364399,	0.000000 }}},
-//{ Ta, +5,	{{	29.158699,	18.840700,	12.826799,	5.386950,	1.785550 },
-//			 {	1.507110,	0.116741,	6.315240,	12.424400,	0.000000 }}},
-//{ W,	0,	{{	29.081800,	15.430000,	14.432700,	5.119820,	9.887500 },
-//			 {	1.720290,	9.225900,	0.321703,	57.056000,	0.000000 }}},
-//{ W, +6,	{{	29.493599,	19.376301,	13.054399,	5.064120,	1.010740 },
-//			 {	1.427550,	0.104621,	5.936670,	11.197200,	0.000000 }}},
-//{ Re,	0,	{{	28.762100,	15.718900,	14.556400,	5.441740,	10.472000 },
-//			 {	1.671910,	9.092270,	0.350500,	52.086098,	0.000000 }}},
-//{ Os,	0,	{{	28.189400,	16.154999,	14.930500,	5.675890,	11.000500 },
-//			 {	1.629030,	8.979480,	0.382661,	48.164700,	0.000000 }}},
-//{ Os, +4,	{{	30.418999,	15.263700,	14.745800,	5.067950,	6.498040 },
-//			 {	1.371130,	6.847060,	0.165191,	18.003000,	0.000000 }}},
-//{ Ir,	0,	{{	27.304899,	16.729599,	15.611500,	5.833770,	11.472200 },
-//			 {	1.592790,	8.865530,	0.417916,	45.001099,	0.000000 }}},
-//{ Ir, +3,	{{	30.415600,	15.862000,	13.614500,	5.820080,	8.279030 },
-//			 {	1.343230,	7.109090,	0.204633,	20.325399,	0.000000 }}},
-//{ Ir, +4,	{{	30.705799,	15.551200,	14.232600,	5.536720,	6.968240 },
-//			 {	1.309230,	6.719830,	0.167252,	17.491100,	0.000000 }}},
-//{ Pt,	0,	{{	27.005899,	17.763901,	15.713100,	5.783700,	11.688300 },
-//			 {	1.512930,	8.811740,	0.424593,	38.610298,	0.000000 }}},
-//{ Pt, +2,	{{	29.842899,	16.722401,	13.215300,	6.352340,	9.853290 },
-//			 {	1.329270,	7.389790,	0.263297,	22.942600,	0.000000 }}},
-//{ Pt, +4,	{{	30.961201,	15.982900,	13.734800,	5.920340,	7.395340 },
-//			 {	1.248130,	6.608340,	0.168640,	16.939199,	0.000000 }}},
-//{ Au,	0,	{{	16.881901,	18.591299,	25.558201,	5.860000,	12.065800 },
-//			 {	0.461100,	8.621600,	1.482600,	36.395599,	0.000000 }}},
-//{ Au, +1,	{{	28.010899,	17.820400,	14.335899,	6.580770,	11.229900 },
-//			 {	1.353210,	7.739500,	0.356752,	26.404301,	0.000000 }}},
-//{ Au, +3,	{{	30.688599,	16.902901,	12.780100,	6.523540,	9.096800 },
-//			 {	1.219900,	6.828720,	0.212867,	18.659000,	0.000000 }}},
-//{ Hg,	0,	{{	20.680901,	19.041700,	21.657499,	5.967600,	12.608900 },
-//			 {	0.545000,	8.448400,	1.572900,	38.324600,	0.000000 }}},
-//{ Hg, +1,	{{	25.085300,	18.497299,	16.888300,	6.482160,	12.020500 },
-//			 {	1.395070,	7.651050,	0.443378,	28.226200,	0.000000 }}},
-//{ Hg, +2,	{{	29.564100,	18.059999,	12.837400,	6.899120,	10.626800 },
-//			 {	1.211520,	7.056390,	0.284738,	20.748199,	0.000000 }}},
-//{ Tl,	0,	{{	27.544600,	19.158400,	15.538000,	5.525930,	13.174600 },
-//			 {	0.655150,	8.707510,	1.963470,	45.814899,	0.000000 }}},
-//{ Tl, +1,	{{	21.398500,	20.472300,	18.747799,	6.828470,	12.525800 },
-//			 {	1.471100,	0.517394,	7.434630,	28.848200,	0.000000 }}},
-//{ Tl, +3,	{{	30.869499,	18.384100,	11.932800,	7.005740,	9.802700 },
-//			 {	1.100800,	6.538520,	0.219074,	17.211399,	0.000000 }}},
-//{ Pb,	0,	{{	31.061699,	13.063700,	18.441999,	5.969600,	13.411800 },
-//			 {	0.690200,	2.357600,	8.618000,	47.257900,	0.000000 }}},
-//{ Pb, +2,	{{	21.788601,	19.568199,	19.140600,	7.011070,	12.473400 },
-//			 {	1.336600,	0.488383,	6.772700,	23.813200,	0.000000 }}},
-//{ Pb, +4,	{{	32.124397,	18.800301,	12.017500,	6.968860,	8.084280 },
-//			 {	1.005660,	6.109260,	0.147041,	14.714000,	0.000000 }}},
-//{ Bi,	0,	{{	33.368900,	12.951000,	16.587700,	6.469200,	13.578199 },
-//			 {	0.704000,	2.923800,	8.793700,	48.009300,	0.000000 }}},
-//{ Bi, +3,	{{	21.805300,	19.502600,	19.105301,	7.102950,	12.471100 },
-//			 {	1.235600,	6.241490,	0.469999,	20.318501,	0.000000 }}},
-//{ Bi, +5,	{{	33.536400,	25.094601,	19.249699,	6.915550,	-6.799400 },
-//			 {	0.916540,	0.390420,	5.714140,	12.828500,	0.000000 }}},
-//{ Po,	0,	{{	34.672600,	15.473300,	13.113800,	7.025880,	13.677000 },
-//			 {	0.700999,	3.550780,	9.556419,	47.004501,	0.000000 }}},
-//{ At,	0,	{{	35.316299,	19.021099,	9.498870,	7.425180,	13.710800 },
-//			 {	0.685870,	3.974580,	11.382400,	45.471500,	0.000000 }}},
-//{ Rn,	0,	{{	35.563099,	21.281601,	8.003700,	7.443300,	13.690500 },
-//			 {	0.663100,	4.069100,	14.042200,	44.247299,	0.000000 }}},
-//{ Fr,	0,	{{	35.929901,	23.054699,	12.143900,	2.112530,	13.724700 },
-//			 {	0.646453,	4.176190,	23.105200,	150.644989,	0.000000 }}},
-//{ Ra,	0,	{{	35.763000,	22.906399,	12.473900,	3.210970,	13.621099 },
-//			 {	0.616341,	3.871350,	19.988701,	142.324997,	0.000000 }}},
-//{ Ra, +2,	{{	35.215000,	21.670000,	7.913420,	7.650780,	13.543100 },
-//			 {	0.604909,	3.576700,	12.601000,	29.843599,	0.000000 }}},
-//{ Ac,	0,	{{	35.659698,	23.103199,	12.597700,	4.086550,	13.526600 },
-//			 {	0.589092,	3.651550,	18.598999,	117.019997,	0.000000 }}},
-//{ Ac, +3,	{{	35.173599,	22.111200,	8.192160,	7.055450,	13.463699 },
-//			 {	0.579689,	3.414370,	12.918700,	25.944300,	0.000000 }}},
-//{ Th,	0,	{{	35.564499,	23.421900,	12.747300,	4.807030,	13.431400 },
-//			 {	0.563359,	3.462040,	17.830900,	99.172195,	0.000000 }}},
-//{ Th, +4,	{{	35.100700,	22.441799,	9.785540,	5.294440,	13.375999 },
-//			 {	0.555054,	3.244980,	13.466100,	23.953300,	0.000000 }}},
-//{ Pa,	0,	{{	35.884701,	23.294800,	14.189100,	4.172870,	13.428699 },
-//			 {	0.547751,	3.415190,	16.923500,	105.250999,	0.000000 }}},
-//{ U,	0,	{{	36.022800,	23.412800,	14.949100,	4.188000,	13.396600 },
-//			 {	0.529300,	3.325300,	16.092699,	100.612999,	0.000000 }}},
-//{ U, +3,	{{	35.574699,	22.525900,	12.216499,	5.370730,	13.309200 },
-//			 {	0.520480,	3.122930,	12.714800,	26.339399,	0.000000 }}},
-//{ U, +4,	{{	35.371498,	22.532600,	12.029100,	4.798400,	13.267099 },
-//			 {	0.516598,	3.050530,	12.572300,	23.458200,	0.000000 }}},
-//{ U, +6,	{{	34.850899,	22.758400,	14.009900,	1.214570,	13.166500 },
-//			 {	0.507079,	2.890300,	13.176700,	25.201700,	0.000000 }}},
-//{ Np,	0,	{{	36.187401,	23.596399,	15.640200,	4.185500,	13.357300 },
-//			 {	0.511929,	3.253960,	15.362200,	97.490799,	0.000000 }}},
-//{ Np, +3,	{{	35.707397,	22.612999,	12.989799,	5.432270,	13.254400 },
-//			 {	0.502322,	3.038070,	12.144899,	25.492800,	0.000000 }}},
-//{ Np, +4,	{{	35.510300,	22.578699,	12.776600,	4.921590,	13.211599 },
-//			 {	0.498626,	2.966270,	11.948400,	22.750200,	0.000000 }}},
-//{ Np, +6,	{{	35.013599,	22.728600,	14.388400,	1.756690,	13.113000 },
-//			 {	0.489810,	2.810990,	12.330000,	22.658100,	0.000000 }}},
-//{ Pu,	0,	{{	36.525398,	23.808300,	16.770700,	3.479470,	13.381200 },
-//			 {	0.499384,	3.263710,	14.945499,	105.979996,	0.000000 }}},
-//{ Pu, +3,	{{	35.840000,	22.716900,	13.580700,	5.660160,	13.199100 },
-//			 {	0.484936,	2.961180,	11.533100,	24.399200,	0.000000 }}},
-//{ Pu, +4,	{{	35.649300,	22.646000,	13.359500,	5.188310,	13.155500 },
-//			 {	0.481422,	2.890200,	11.316000,	21.830099,	0.000000 }}},
-//{ Pu, +6,	{{	35.173599,	22.718100,	14.763500,	2.286780,	13.058200 },
-//			 {	0.473204,	2.738480,	11.552999,	20.930300,	0.000000 }}},
-//{ Am,	0,	{{	36.670601,	24.099199,	17.341499,	3.493310,	13.359200 },
-//			 {	0.483629,	3.206470,	14.313600,	102.272995,	0.000000 }}},
-//{ Cm,	0,	{{	36.648800,	24.409599,	17.399000,	4.216650,	13.288700 },
-//			 {	0.465154,	3.089970,	13.434600,	88.483398,	0.000000 }}},
-//{ Bk,	0,	{{	36.788101,	24.773600,	17.891899,	4.232840,	13.275400 },
-//			 {	0.451018,	3.046190,	12.894600,	86.002998,	0.000000 }}},
-//{ Cf,	0,	{{	36.918499,	25.199499,	18.331699,	4.243910,	13.267400 },
-//			 {	0.437533,	3.007750,	12.404400,	83.788101,	0.000000}}},
-
-{ H,	0,	{{  0.413048,  0.294953,  0.187491,  0.080701,  0.023736,  0.000049},
-			 { 15.569946, 32.398468,  5.711404, 61.889874,  1.334118,  0.000000}}},
-{ He,	0,	{{  0.732354,  0.753896,  0.283819,  0.190003,  0.039139,  0.000487},
-			 { 11.553918,  4.595831,  1.546299, 26.463964,  0.377523,  0.000000}}},
-{ Li,	0,	{{  0.974637,  0.158472,  0.811855,  0.262416,  0.790108,  0.002542},
-			 {  4.334946,  0.342451, 97.102966,201.363831,  1.409234,  0.000000}}},
-{ Be,	0,	{{  1.533712,  0.638283,  0.601052,  0.106139,  1.118414,  0.002511},
-			 { 42.662079,  0.595420, 99.106499,  0.151340,  1.843093,  0.000000}}},
-{ B,	0,	{{  2.085185,  1.064580,  1.062788,  0.140515,  0.641784,  0.003823},
-			 { 23.494068,  1.137894, 61.238976,  0.114886,  0.399036,  0.000000}}},
-{ C,	0,	{{  2.657506,  1.078079,  1.490909, -4.241070,  0.713791,  4.297983},
-			 { 14.780758,  0.776775, 42.086842, -0.000294,  0.239535,  0.000000}}},
-{ N,	0,	{{ 11.893780,  3.277479,  1.858092,  0.858927,  0.912985,-11.804902},
-			 {  0.000158, 10.232723, 30.344690,  0.656065,  0.217287,  0.000000}}},
-{ O,	0,	{{  2.960427,  2.508818,  0.637853,  0.722838,  1.142756,  0.027014},
-			 { 14.182259,  5.936858,  0.112726, 34.958481,  0.390240,  0.000000}}},
-{ F,	0,	{{  3.511943,  2.772244,  0.678385,  0.915159,  1.089261,  0.032557},
-			 { 10.687859,  4.380466,  0.093982, 27.255203,  0.313066,  0.000000}}},
-{ Ne,	0,	{{  4.183749,  2.905726,  0.520513,  1.135641,  1.228065,  0.025576},
-			 {  8.175457,  3.252536,  0.063295, 21.813910,  0.224952,  0.000000}}},
-{ Na,	0,	{{  4.910127,  3.081783,  1.262067,  1.098938,  0.560991,  0.079712},
-			 {  3.281434,  9.119178,  0.102763,132.013947,  0.405878,  0.000000}}},
-{ Mg,	0,	{{  4.708971,  1.194814,  1.558157,  1.170413,  3.239403,  0.126842},
-			 {  4.875207,108.506081,  0.111516, 48.292408,  1.928171,  0.000000}}},
-{ Al,	0,	{{  4.730796,  2.313951,  1.541980,  1.117564,  3.154754,  0.139509},
-			 {  3.628931, 43.051167,  0.095960,108.932388,  1.555918,  0.000000}}},
-{ Si,	0,	{{  5.275329,  3.191038,  1.511514,  1.356849,  2.519114,  0.145073},
-			 {  2.631338, 33.730728,  0.081119, 86.288643,  1.170087,  0.000000}}},
-{ P,	0,	{{  1.950541,  4.146930,  1.494560,  1.522042,  5.729711,  0.155233},
-			 {  0.908139, 27.044952,  0.071280, 67.520187,  1.981173,  0.000000}}},
-{ S,	0,	{{  6.372157,  5.154568,  1.473732,  1.635073,  1.209372,  0.154722},
-			 {  1.514347, 22.092527,  0.061373, 55.445175,  0.646925,  0.000000}}},
-{ Cl,	0,	{{  1.446071,  6.870609,  6.151801,  1.750347,  0.634168,  0.146773},
-			 {  0.052357,  1.193165, 18.343416, 46.398396,  0.401005,  0.000000}}},
-{ Ar,	0,	{{  7.188004,  6.638454,  0.454180,  1.929593,  1.523654,  0.265954},
-			 {  0.956221, 15.339877, 15.339862, 39.043823,  0.062409,  0.000000}}},
-{ K,	0,	{{  8.163991,  7.146945,  1.070140,  0.877316,  1.486434,  0.253614},
-			 { 12.816323,  0.808945,210.327011, 39.597652,  0.052821,  0.000000}}},
-{ Ca,	0,	{{  8.593655,  1.477324,  1.436254,  1.182839,  7.113258,  0.196255},
-			 { 10.460644,  0.041891, 81.390381,169.847839,  0.688098,  0.000000}}},
-{ Sc,	0,	{{  1.476566,  1.487278,  1.600187,  9.177463,  7.099750,  0.157765},
-			 { 53.131023,  0.035325,137.319489,  9.098031,  0.602102,  0.000000}}},
-{ Ti,	0,	{{  9.818524,  1.522646,  1.703101,  1.768774,  7.082555,  0.102473},
-			 {  8.001879,  0.029763, 39.885422,120.157997,  0.532405,  0.000000}}},
-{ V,	0,	{{ 10.473575,  1.547881,  1.986381,  1.865616,  7.056250,  0.067744},
-			 {  7.081940,  0.026040, 31.909672,108.022842,  0.474882,  0.000000}}},
-{ Cr,	0,	{{ 11.007069,  1.555477,  2.985293,  1.347855,  7.034779,  0.065510},
-			 {  6.366281,  0.023987, 23.244839,105.774498,  0.429369,  0.000000}}},
-{ Mn,	0,	{{ 11.709542,  1.733414,  2.673141,  2.023368,  7.003180, -0.147293},
-			 {  5.597120,  0.017800, 21.788420, 89.517914,  0.383054,  0.000000}}},
-{ Fe,	0,	{{ 12.311098,  1.876623,  3.066177,  2.070451,  6.975185, -0.304931},
-			 {  5.009415,  0.014461, 18.743040, 82.767876,  0.346506,  0.000000}}},
-{ Co,	0,	{{ 12.914510,  2.481908,  3.466894,  2.106351,  6.960892, -0.936572},
-			 {  4.507138,  0.009126, 16.438129, 76.987320,  0.314418,  0.000000}}},
-{ Ni,	0,	{{ 13.521865,  6.947285,  3.866028,  2.135900,  4.284731, -2.762697},
-			 {  4.077277,  0.286763, 14.622634, 71.966080,  0.004437,  0.000000}}},
-{ Cu,	0,	{{ 14.014192,  4.784577,  5.056806,  1.457971,  6.932996, -3.254477},
-			 {  3.738280,  0.003744, 13.034982, 72.554794,  0.265666,  0.000000}}},
-{ Zn,	0,	{{ 14.741002,  6.907748,  4.642337,  2.191766, 38.424042,-36.915829},
-			 {  3.388232,  0.243315, 11.903689, 63.312130,  0.000397,  0.000000}}},
-{ Ga,	0,	{{ 15.758946,  6.841123,  4.121016,  2.714681,  2.395246, -0.847395},
-			 {  3.121754,  0.226057, 12.482196, 66.203621,  0.007238,  0.000000}}},
-{ Ge,	0,	{{ 16.540613,  1.567900,  3.727829,  3.345098,  6.785079,  0.018726},
-			 {  2.866618,  0.012198, 13.432163, 58.866047,  0.210974,  0.000000}}},
-{ As,	0,	{{ 17.025642,  4.503441,  3.715904,  3.937200,  6.790175, -2.984117},
-			 {  2.597739,  0.003012, 14.272119, 50.437996,  0.193015,  0.000000}}},
-{ Se,	0,	{{ 17.354071,  4.653248,  4.259489,  4.136455,  6.749163, -3.160982},
-			 {  2.349787,  0.002550, 15.579460, 45.181202,  0.177432,  0.000000}}},
-{ Br,	0,	{{ 17.550570,  5.411882,  3.937180,  3.880645,  6.707793, -2.492088},
-			 {  2.119226, 16.557184,  0.002481, 42.164009,  0.162121,  0.000000}}},
-{ Kr,	0,	{{ 17.655279,  6.848105,  4.171004,  3.446760,  6.685200, -2.810592},
-			 {  1.908231, 16.606236,  0.001598, 39.917473,  0.146896,  0.000000}}},
-{ Rb,	0,	{{  8.123134,  2.138042,  6.761702,  1.156051, 17.679546,  1.139548},
-			 { 15.142385, 33.542667,  0.129372,224.132507,  1.713368,  0.000000}}},
-{ Sr,	0,	{{ 17.730219,  9.795867,  6.099763,  2.620025,  0.600053,  1.140251},
-			 {  1.563060, 14.310868,  0.120574,135.771317,  0.120574,  0.000000}}},
-{ Y,	0,	{{ 17.792040, 10.253252,  5.714949,  3.170516,  0.918251,  1.131787},
-			 {  1.429691, 13.132816,  0.112173,108.197029,  0.112173,  0.000000}}},
-{ Zr,	0,	{{ 17.859772, 10.911038,  5.821115,  3.512513,  0.746965,  1.124859},
-			 {  1.310692, 12.319285,  0.104353, 91.777542,  0.104353,  0.000000}}},
-{ Nb,	0,	{{ 17.958399, 12.063054,  5.007015,  3.287667,  1.531019,  1.123452},
-			 {  1.211590, 12.246687,  0.098615, 75.011948,  0.098615,  0.000000}}},
-{ Mo,	0,	{{  6.236218, 17.987711, 12.973127,  3.451426,  0.210899,  1.108770},
-			 {  0.090780,  1.108310, 11.468720, 66.684151,  0.090780,  0.000000}}},
-{ Tc,	0,	{{ 17.840963,  3.428236,  1.373012, 12.947364,  6.335469,  1.074784},
-			 {  1.005729, 41.901382,119.320541,  9.781542,  0.083391,  0.000000}}},
-{ Ru,	0,	{{  6.271624, 17.906738, 14.123269,  3.746008,  0.908235,  1.043992},
-			 {  0.077040,  0.928222,  9.555345, 35.860680,123.552246,  0.000000}}},
-{ Rh,	0,	{{  6.216648, 17.919739,  3.854252,  0.840326, 15.173498,  0.995452},
-			 {  0.070789,  0.856121, 33.889484,121.686691,  9.029517,  0.000000}}},
-{ Pd,	0,	{{  6.121511,  4.784063, 16.631683,  4.318258, 13.246773,  0.883099},
-			 {  0.062549,  0.784031,  8.751391, 34.489983,  0.784031,  0.000000}}},
-{ Ag,	0,	{{  6.073874, 17.155437,  4.173344,  0.852238, 17.988686,  0.756603},
-			 {  0.055333,  7.896512, 28.443739,110.376106,  0.716809,  0.000000}}},
-{ Cd,	0,	{{  6.080986, 18.019468,  4.018197,  1.303510, 17.974669,  0.603504},
-			 {  0.048990,  7.273646, 29.119284, 95.831207,  0.661231,  0.000000}}},
-{ In,	0,	{{  6.196477, 18.816183,  4.050479,  1.638929, 17.962912,  0.333097},
-			 {  0.042072,  6.695665, 31.009790,103.284348,  0.610714,  0.000000}}},
-{ Sn,	0,	{{ 19.325171,  6.281571,  4.498866,  1.856934, 17.917318,  0.119024},
-			 {  6.118104,  0.036915, 32.529045, 95.037186,  0.565651,  0.000000}}},
-{ Sb,	0,	{{  5.394956,  6.549570, 19.650681,  1.827820, 17.867832, -0.290506},
-			 { 33.326523,  0.030974,  5.564929, 87.130966,  0.523992,  0.000000}}},
-{ Te,	0,	{{  6.660302,  6.940756, 19.847015,  1.557175, 17.802427, -0.806668},
-			 { 33.031654,  0.025750,  5.065547, 84.101616,  0.487660,  0.000000}}},
-{ I,	0,	{{ 19.884502,  6.736593,  8.110516,  1.170953, 17.548716, -0.448811},
-			 {  4.628591,  0.027754, 31.849096, 84.406387,  0.463550,  0.000000}}},
-{ Xe,	0,	{{ 19.978920, 11.774945,  9.332182,  1.244749, 17.737501, -6.065902},
-			 {  4.143356,  0.010142, 28.796200, 75.280685,  0.413616,  0.000000}}},
-{ Cs,	0,	{{ 17.418674,  8.314444, 10.323193,  1.383834, 19.876251, -2.322802},
-			 {  0.399828,  0.016872, 25.605827,233.339676,  3.826915,  0.000000}}},
-{ Ba,	0,	{{ 19.747343, 17.368477, 10.465718,  2.592602, 11.003653, -5.183497},
-			 {  3.481823,  0.371224, 21.226641,173.834274,  0.010719,  0.000000}}},
-{ La,	0,	{{ 19.966019, 27.329655, 11.018425,  3.086696, 17.335455,-21.745489},
-			 {  3.197408,  0.003446, 19.955492,141.381973,  0.341817,  0.000000}}},
-{ Ce,	0,	{{ 17.355122, 43.988499, 20.546650,  3.130670, 11.353665,-38.386017},
-			 {  0.328369,  0.002047,  3.088196,134.907654, 18.832960,  0.000000}}},
-{ Pr,	0,	{{ 21.551311, 17.161730, 11.903859,  2.679103,  9.564197, -3.871068},
-			 {  2.995675,  0.312491, 17.716705,152.192825,  0.010468,  0.000000}}},
-{ Nd,	0,	{{ 17.331244, 62.783924, 12.160097,  2.663483, 22.239950,-57.189842},
-			 {  0.300269,  0.001320, 17.026001,148.748993,  2.910268,  0.000000}}},
-{ Pm,	0,	{{ 17.286388, 51.560162, 12.478557,  2.675515, 22.960947,-45.973682},
-			 {  0.286620,  0.001550, 16.223755,143.984512,  2.796480,  0.000000}}},
-{ Sm,	0,	{{ 23.700363, 23.072214, 12.777782,  2.684217, 17.204367,-17.452166},
-			 {  2.689539,  0.003491, 15.495437,139.862473,  0.274536,  0.000000}}},
-{ Eu,	0,	{{ 17.186195, 37.156837, 13.103387,  2.707246, 24.419271,-31.586687},
-			 {  0.261678,  0.001995, 14.787360,134.816299,  2.581883,  0.000000}}},
-{ Gd,	0,	{{ 24.898117, 17.104952, 13.222581,  3.266152, 48.995213,-43.505684},
-			 {  2.435028,  0.246961, 13.996325,110.863091,  0.001383,  0.000000}}},
-{ Tb,	0,	{{ 25.910013, 32.344139, 13.765117,  2.751404, 17.064405,-26.851971},
-			 {  2.373912,  0.002034, 13.481969,125.836510,  0.236916,  0.000000}}},
-{ Dy,	0,	{{ 26.671785, 88.687576, 14.065445,  2.768497, 17.067781,-83.279831},
-			 {  2.282593,  0.000665, 12.920230,121.937187,  0.225531,  0.000000}}},
-{ Ho,	0,	{{ 27.150190, 16.999819, 14.059334,  3.386979, 46.546471,-41.165253},
-			 {  2.169660,  0.215414, 12.213148,100.506783,  0.001211,  0.000000}}},
-{ Er,	0,	{{ 28.174887, 82.493271, 14.624002,  2.802756, 17.018515,-77.135223},
-			 {  2.120995,  0.000640, 11.915256,114.529938,  0.207519,  0.000000}}},
-{ Tm,	0,	{{ 28.925894, 76.173798, 14.904704,  2.814812, 16.998117,-70.839813},
-			 {  2.046203,  0.000656, 11.465375,111.411980,  0.199376,  0.000000}}},
-{ Yb,	0,	{{ 29.676760, 65.624069, 15.160854,  2.830288, 16.997850,-60.313812},
-			 {  1.977630,  0.000720, 11.044622,108.139153,  0.192110,  0.000000}}},
-{ Lu,	0,	{{ 30.122866, 15.099346, 56.314899,  3.540980, 16.943729,-51.049416},
-			 {  1.883090, 10.342764,  0.000780, 89.559250,  0.183849,  0.000000}}},
-{ Hf,	0,	{{ 30.617033, 15.145351, 54.933548,  4.096253, 16.896156,-49.719837},
-			 {  1.795613,  9.934469,  0.000739, 76.189705,  0.175914,  0.000000}}},
-{ Ta,	0,	{{ 31.066359, 15.341823, 49.278297,  4.577665, 16.828321,-44.119026},
-			 {  1.708732,  9.618455,  0.000760, 66.346199,  0.168002,  0.000000}}},
-{ W,	0,	{{ 31.507900, 15.682498, 37.960129,  4.885509, 16.792112,-32.864574},
-			 {  1.629485,  9.446448,  0.000898, 59.980675,  0.160798,  0.000000}}},
-{ Re,	0,	{{ 31.888456, 16.117104, 42.390297,  5.211669, 16.767591,-37.412682},
-			 {  1.549238,  9.233474,  0.000689, 54.516373,  0.152815,  0.000000}}},
-{ Os,	0,	{{ 32.210297, 16.678440, 48.559906,  5.455839, 16.735533,-43.677956},
-			 {  1.473531,  9.049695,  0.000519, 50.210201,  0.145771,  0.000000}}},
-{ Ir,	0,	{{ 32.004436,  1.975454, 17.070105, 15.939454,  5.990003,  4.018893},
-			 {  1.353767, 81.014175,  0.128093,  7.661196, 26.659403,  0.000000}}},
-{ Pt,	0,	{{ 31.273891, 18.445440, 17.063745,  5.555933,  1.575270,  4.050394},
-			 {  1.316992,  8.797154,  0.124741, 40.177994,  1.316997,  0.000000}}},
-{ Au,	0,	{{ 16.777390, 19.317156, 32.979683,  5.595453, 10.576854, -6.279078},
-			 {  0.122737,  8.621570,  1.256902, 38.008820,  0.000601,  0.000000}}},
-{ Hg,	0,	{{ 16.839890, 20.023823, 28.428564,  5.881564,  4.714706,  4.076478},
-			 {  0.115905,  8.256927,  1.195250, 39.247227,  1.195250,  0.000000}}},
-{ Tl,	0,	{{ 16.630795, 19.386616, 32.808571,  1.747191,  6.356862,  4.066939},
-			 {  0.110704,  7.181401,  1.119730, 90.660263, 26.014978,  0.000000}}},
-{ Pb,	0,	{{ 16.419567, 32.738590,  6.530247,  2.342742, 19.916475,  4.049824},
-			 {  0.105499,  1.055049, 25.025890, 80.906593,  6.664449,  0.000000}}},
-{ Bi,	0,	{{ 16.282274, 32.725136,  6.678302,  2.694750, 20.576559,  4.040914},
-			 {  0.101180,  1.002287, 25.714146, 77.057549,  6.291882,  0.000000}}},
-{ Po,	0,	{{ 16.289164, 32.807171, 21.095163,  2.505901,  7.254589,  4.046556},
-			 {  0.098121,  0.966265,  6.046622, 76.598068, 28.096128,  0.000000}}},
-{ At,	0,	{{ 16.011461, 32.615547,  8.113899,  2.884082, 21.377867,  3.995684},
-			 {  0.092639,  0.904416, 26.543257, 68.372963,  5.499512,  0.000000}}},
-{ Rn,	0,	{{ 16.070229, 32.641106, 21.489658,  2.299218,  9.480184,  4.020977},
-			 {  0.090437,  0.876409,  5.239687, 69.188477, 27.632641,  0.000000}}},
-{ Fr,	0,	{{ 16.007385, 32.663830, 21.594351,  1.598497, 11.121192,  4.003472},
-			 {  0.087031,  0.840187,  4.954467,199.805801, 26.905106,  0.000000}}},
-{ Ra,	0,	{{ 32.563690, 21.396671, 11.298093,  2.834688, 15.914965,  3.981773},
-			 {  0.801980,  4.590666, 22.758972,160.404388,  0.083544,  0.000000}}},
-{ Ac,	0,	{{ 15.914053, 32.535042, 21.553976, 11.433394,  3.612409,  3.939212},
-			 {  0.080511,  0.770669,  4.352206, 21.381622,130.500748,  0.000000}}},
-{ Th,	0,	{{ 15.784024, 32.454899, 21.849222,  4.239077, 11.736191,  3.922533},
-			 {  0.077067,  0.735137,  4.097976,109.464111, 20.512138,  0.000000}}},
-{ Pa,	0,	{{ 32.740208, 21.973675, 12.957398,  3.683832, 15.744058,  3.886066},
-			 {  0.709545,  4.050881, 19.231543,117.255005,  0.074040,  0.000000}}},
-{ U,	0,	{{ 15.679275, 32.824306, 13.660459,  3.687261, 22.279434,  3.854444},
-			 {  0.071206,  0.681177, 18.236156,112.500038,  3.930325,  0.000000}}},
-{ Np,	0,	{{ 32.999901, 22.638077, 14.219973,  3.672950, 15.683245,  3.769391},
-			 {  0.657086,  3.854918, 17.435474,109.464485,  0.068033,  0.000000}}},
-{ Pu,	0,	{{ 33.281178, 23.148544, 15.153755,  3.031492, 15.704215,  3.664200},
-			 {  0.634999,  3.856168, 16.849735,121.292038,  0.064857,  0.000000}}},
-{ Am,	0,	{{ 33.435162, 23.657259, 15.576339,  3.027023, 15.746100,  3.541160},
-			 {  0.612785,  3.792942, 16.195778,117.757004,  0.061755,  0.000000}}},
-{ Cm,	0,	{{ 15.804837, 33.480801, 24.150198,  3.655563, 15.499866,  3.390840},
-			 {  0.058619,  0.590160,  3.674720,100.736191, 15.408296,  0.000000}}},
-{ Bk,	0,	{{ 15.889072, 33.625286, 24.710381,  3.707139, 15.839268,  3.213169},
-			 {  0.055503,  0.569571,  3.615472, 97.694786, 14.754303,  0.000000}}},
-{ Cf,	0,	{{ 33.794075, 25.467693, 16.048487,  3.657525, 16.008982,  3.005326},
-			 {  0.550447,  3.581973, 14.357388, 96.064972,  0.052450,  0.000000}}},
-{ H,	-1,	{{  0.702260,  0.763666,  0.248678,  0.261323,  0.023017,  0.000425},
-			 { 23.945604, 74.897919,  6.773289,233.583450,  1.337531,  0.000000}}},
-{ Li,	+1,	{{  0.432724,  0.549257,  0.376575, -0.336481,  0.976060,  0.001764},
-			 {  0.260367,  1.042836,  7.885294,  0.260368,  3.042539,  0.000000}}},
-{ Be,	+2,	{{  3.055430, -2.372617,  1.044914,  0.544233,  0.381737, -0.653773},
-			 {  0.001226,  0.001227,  1.542106,  0.456279,  4.047479,  0.000000}}},
-{ C, AtomTypeTraits::kWKSFVal,
-			{{  1.258489,  0.728215,  1.119856,  2.168133,  0.705239,  0.019722},
-			 { 10.683769,  0.208177,  0.836097, 24.603704, 58.954273,  0.000000}}},
-{ O,	-1,	{{  3.106934,  3.235142,  1.148886,  0.783981,  0.676953,  0.046136},
-			 { 19.868080,  6.960252,  0.170043, 65.693512,  0.630757,  0.000000}}},
-{ O,	-2,	{{  3.990247,  2.300563,  0.607200,  1.907882,  1.167080,  0.025429},
-			 { 16.639956,  5.636819,  0.108493, 47.299709,  0.379984,  0.000000}}},
-{ F,	-1,	{{  0.457649,  3.841561,  1.432771,  0.801876,  3.395041,  0.069525},
-			 {  0.917243,  5.507803,  0.164955, 51.076206, 15.821679,  0.000000}}},
-{ Na,	+1,	{{  3.148690,  4.073989,  0.767888,  0.995612,  0.968249,  0.045300},
-			 {  2.594987,  6.046925,  0.070139, 14.122657,  0.217037,  0.000000}}},
-{ Mg,	+2,	{{  3.062918,  4.135106,  0.853742,  1.036792,  0.852520,  0.058851},
-			 {  2.015803,  4.417941,  0.065307,  9.669710,  0.187818,  0.000000}}},
-{ Al,	+3,	{{  4.132015,  0.912049,  1.102425,  0.614876,  3.219136,  0.019397},
-			 {  3.528641,  7.378344,  0.133708,  0.039065,  1.644728,  0.000000}}},
-{ Si, AtomTypeTraits::kWKSFVal,
-			{{  2.879033,  3.072960,  1.515981,  1.390030,  4.995051,  0.146030},
-			 {  1.239713, 38.706276,  0.081481, 93.616333,  2.770293,  0.000000}}},
-{ Si,	+4,	{{  3.676722,  3.828496,  1.258033,  0.419024,  0.720421,  0.097266},
-			 {  1.446851,  3.013144,  0.064397,  0.206254,  5.970222,  0.000000}}},
-{ Cl,	-1,	{{  1.061802,  7.139886,  6.524271,  2.355626, 35.829403,-34.916603},
-			 {  0.144727,  1.171795, 19.467655, 60.320301,  0.000436,  0.000000}}},
-{ K,	+1,	{{-17.609339,  1.494873,  7.150305, 10.899569, 15.808228,  0.257164},
-			 { 18.840979,  0.053453,  0.812940, 22.264105, 14.351593,  0.000000}}},
-{ Ca,	+2,	{{  8.501441, 12.880483,  9.765095,  7.156669,  0.711160,-21.013187},
-			 { 10.525848, -0.004033,  0.010692,  0.684443, 27.231771,  0.000000}}},
-{ Sc,	+3,	{{  7.104348,  1.511488,-53.669773, 38.404816, 24.532240,  0.118642},
-			 {  0.601957,  0.033386, 12.572138, 10.859736, 14.125230,  0.000000}}},
-{ Ti,	+2,	{{  7.040119,  1.496285,  9.657304,  0.006534,  1.649561,  0.150362},
-			 {  0.537072,  0.031914,  8.009958,201.800293, 24.039482,  0.000000}}},
-{ Ti,	+3,	{{ 36.587933,  7.230255, -9.086077,  2.084594, 17.294008,-35.111282},
-			 {  0.000681,  0.522262,  5.262317, 15.881716,  6.149805,  0.000000}}},
-{ Ti,	+4,	{{ 45.355537,  7.092900,  7.483858,-43.498817,  1.678915, -0.110628},
-			 {  9.252186,  0.523046, 13.082852, 10.193876,  0.023064,  0.000000}}},
-{ V,	+2,	{{  7.754356,  2.064100,  2.576998,  2.011404,  7.126177, -0.533379},
-			 {  7.066315,  0.014993,  7.066308, 22.055786,  0.467568,  0.000000}}},
-{ V,	+3,	{{  9.958480,  1.596350,  1.483442,-10.846044, 17.332867,  0.474921},
-			 {  6.763041,  0.056895, 17.750029,  0.328826,  0.388013,  0.000000}}},
-{ V,	+5,	{{ 15.575018,  8.448095,  1.612040, -9.721855,  1.534029,  0.552676},
-			 {  0.682708,  5.566640, 10.527077,  0.907961,  0.066667,  0.000000}}},
-{ Cr,	+2,	{{ 10.598877,  1.565858,  2.728280,  0.098064,  6.959321,  0.049870},
-			 {  6.151846,  0.023519, 17.432816, 54.002388,  0.426301,  0.000000}}},
-{ Cr,	+3,	{{  7.989310,  1.765079,  2.627125,  1.829380,  6.980908, -0.192123},
-			 {  6.068867,  0.018342,  6.068887, 16.309284,  0.420864,  0.000000}}},
-{ Mn,	+2,	{{ 11.287712, 26.042414,  3.058096,  0.090258,  7.088306,-24.566132},
-			 {  5.506225,  0.000774, 16.158575, 54.766354,  0.375580,  0.000000}}},
-{ Mn,	+3,	{{  6.926972,  2.081342, 11.128379,  2.375107, -0.419287, -0.093713},
-			 {  0.378315,  0.015054,  5.379957, 14.429586,  0.004939,  0.000000}}},
-{ Mn,	+4,	{{ 12.409131,  7.466993,  1.809947,-12.138477, 10.780248,  0.672146},
-			 {  0.300400,  0.112814, 12.520756,  0.168653,  5.173237,  0.000000}}},
-{ Fe,	+2,	{{ 11.776765, 11.165097,  3.533495,  0.165345,  7.036932, -9.676919},
-			 {  4.912232,  0.001748, 14.166556, 42.381958,  0.341324,  0.000000}}},
-{ Fe,	+3,	{{  9.721638, 63.403847,  2.141347,  2.629274,  7.033846,-61.930725},
-			 {  4.869297,  0.000293,  4.867602, 13.539076,  0.338520,  0.000000}}},
-{ Co,	+2,	{{  6.993840, 26.285812, 12.254289,  0.246114,  4.017407,-24.796852},
-			 {  0.310779,  0.000684,  4.400528, 35.741447, 12.536393,  0.000000}}},
-{ Co,	+3,	{{  6.861739,  2.678570, 12.281889,  3.501741, -0.179384, -1.147345},
-			 {  0.309794,  0.008142,  4.331703, 11.914167, 11.914167,  0.000000}}},
-{ Ni,	+2,	{{ 12.519017, 37.832058,  4.387257,  0.661552,  6.949072,-36.344471},
-			 {  3.933053,  0.000442, 10.449184, 23.860998,  0.283723,  0.000000}}},
-{ Ni,	+3,	{{ 13.579366,  1.902844, 12.859268,  3.811005, -6.838595, -0.317618},
-			 {  0.313140,  0.012621,  3.906407, 10.894311,  0.344379,  0.000000}}},
-{ Cu,	+1,	{{ 12.960763, 16.342150,  1.110102,  5.520682,  6.915452,-14.849320},
-			 {  3.576010,  0.000975, 29.523218, 10.114283,  0.261326,  0.000000}}},
-{ Cu,	+2,	{{ 11.895569, 16.344978,  5.799817,  1.048804,  6.789088,-14.878383},
-			 {  3.378519,  0.000924,  8.133653, 20.526524,  0.254741,  0.000000}}},
-{ Zn,	+2,	{{ 13.340772, 10.428857,  5.544489,  0.762295,  6.869172, -8.945248},
-			 {  3.215913,  0.001413,  8.542680, 21.891756,  0.239215,  0.000000}}},
-{ Ga,	+3,	{{ 13.123875, 35.288189,  6.126979,  0.611551,  6.724807,-33.875122},
-			 {  2.809960,  0.000323,  6.831534, 16.784311,  0.212002,  0.000000}}},
-{ Ge,	+4,	{{  6.876636,  6.779091,  9.969591,  3.135857,  0.152389,  1.086542},
-			 {  2.025174,  0.176650,  3.573822,  7.685848, 16.677574,  0.000000}}},
-{ Br,	-1,	{{ 17.714310,  6.466926,  6.947385,  4.402674, -0.697279,  1.152674},
-			 {  2.122554, 19.050768,  0.152708, 58.690361, 58.690372,  0.000000}}},
-{ Rb,	+1,	{{ 17.684320,  7.761588,  6.680874,  2.668883,  0.070974,  1.133263},
-			 {  1.710209, 14.919863,  0.128542, 31.654478,  0.128543,  0.000000}}},
-{ Sr,	+2,	{{ 17.694973,  1.275762,  6.154252,  9.234786,  0.515995,  1.125309},
-			 {  1.550888, 30.133041,  0.118774, 13.821799,  0.118774,  0.000000}}},
-{ Y,	+3,	{{ 46.660366, 10.369686,  4.623042,-62.170834, 17.471146, 19.023842},
-			 { -0.019971, 13.180257,  0.176398, -0.016727,  1.467348,  0.000000}}},
-{ Zr,	+4,	{{  6.802956, 17.699253, 10.650647, -0.248108,  0.250338,  0.827902},
-			 {  0.096228,  1.296127, 11.240715, -0.219259, -0.219021,  0.000000}}},
-{ Nb,	+3,	{{ 17.714323,  1.675213,  7.483963,  8.322464, 11.143573, -8.339573},
-			 {  1.172419, 30.102791,  0.080255, -0.002983, 10.456687,  0.000000}}},
-{ Nb,	+5,	{{ 17.580206,  7.633277, 10.793497,  0.180884, 67.837921,-68.024780},
-			 {  1.165852,  0.078558,  9.507652, 31.621656, -0.000438,  0.000000}}},
-{ Mo,	+3,	{{  7.447050, 17.778122, 11.886068,  1.997905,  1.789626, -1.898764},
-			 {  0.072000,  1.073145,  9.834720, 28.221746, -0.011674,  0.000000}}},
-{ Mo,	+5,	{{  7.929879, 17.667669, 11.515987,  0.500402, 77.444084,-78.056595},
-			 {  0.068856,  1.068064,  9.046229, 26.558945, -0.000473,  0.000000}}},
-{ Mo,	+6,	{{ 34.757683,  9.653037,  6.584769,-18.628115,  2.490594,  1.141916},
-			 {  1.301770,  7.123843,  0.094097,  1.617443, 12.335434,  0.000000}}},
-{ Ru,	+3,	{{ 17.894758, 13.579529, 10.729251,  2.474095, 48.227997,-51.905243},
-			 {  0.902827,  8.740579,  0.045125, 24.764954, -0.001699,  0.000000}}},
-{ Ru,	+4,	{{ 17.845776, 13.455084, 10.229087,  1.653524, 14.059795,-17.241762},
-			 {  0.901070,  8.482392,  0.045972, 23.015272, -0.004889,  0.000000}}},
-{ Rh,	+3,	{{ 17.758621, 14.569813,  5.298320,  2.533579,  0.879753,  0.960843},
-			 {  0.841779,  8.319533,  0.069050, 23.709131,  0.069050,  0.000000}}},
-{ Rh,	+4,	{{ 17.716188, 14.446654,  5.185801,  1.703448,  0.989992,  0.959941},
-			 {  0.840572,  8.100647,  0.068995, 22.357307,  0.068995,  0.000000}}},
-{ Pd,	+2,	{{  6.122282, 15.651012,  3.513508,  9.060790,  8.771199,  0.879336},
-			 {  0.062424,  8.018296, 24.784275,  0.776457,  0.776457,  0.000000}}},
-{ Pd,	+4,	{{  6.152421,-96.069023, 31.622141, 81.578255, 17.801403,  0.915874},
-			 {  0.063951, 11.090354, 13.466152,  9.758302,  0.783014,  0.000000}}},
-{ Ag,	+1,	{{  6.091192,  4.019526, 16.948174,  4.258638, 13.889437,  0.785127},
-			 {  0.056305,  0.719340,  7.758938, 27.368349,  0.719340,  0.000000}}},
-{ Ag,	+2,	{{  6.401808, 48.699802,  4.799859,-32.332523, 16.356710,  1.068247},
-			 {  0.068167,  0.942270, 20.639496,  1.100365,  6.883131,  0.000000}}},
-{ Cd,	+2,	{{  6.093711, 43.909691, 17.041306,-39.675117, 17.958918,  0.664795},
-			 {  0.050624,  8.654143, 15.621396, 11.082067,  0.667591,  0.000000}}},
-{ In,	+3,	{{  6.206277, 18.497746,  3.078131, 10.524613,  7.401234,  0.293677},
-			 {  0.041357,  6.605563, 18.792250,  0.608082,  0.608082,  0.000000}}},
-{ Sn,	+2,	{{  6.353672,  4.770377, 14.672025,  4.235959, 18.002131, -0.042519},
-			 {  0.034720,  6.167891,  6.167879, 29.006456,  0.561774,  0.000000}}},
-{ Sn,	+4,	{{ 15.445732,  6.420892,  4.562980,  1.713385, 18.033537, -0.172219},
-			 {  6.280898,  0.033144,  6.280899, 17.983601,  0.557980,  0.000000}}},
-{ Sb,	+3,	{{ 10.189171, 57.461918, 19.356573,  4.862206,-45.394096,  1.516108},
-			 {  0.089485,  0.375256,  5.357987, 22.153736,  0.297768,  0.000000}}},
-{ Sb,	+5,	{{ 17.920622,  6.647932, 12.724075,  1.555545,  7.600591, -0.445371},
-			 {  0.522315,  0.029487,  5.718210, 16.433775,  5.718204,  0.000000}}},
-{ I,	-1,	{{ 20.010330, 17.835524,  8.104130,  2.231118,  9.158548, -3.341004},
-			 {  4.565931,  0.444266, 32.430672, 95.149040,  0.014906,  0.000000}}},
-{ Cs,	+1,	{{ 19.939056, 24.967621, 10.375884,  0.454243, 17.660248,-19.394306},
-			 {  3.770511,  0.004040, 25.311275, 76.537766,  0.384730,  0.000000}}},
-{ Ba,	+2,	{{ 19.750200, 17.513683, 10.884892,  0.321585, 65.149834,-59.618172},
-			 {  3.430748,  0.361590, 21.358307, 70.309402,  0.001418,  0.000000}}},
-{ La,	+3,	{{ 19.688887, 17.345703, 11.356296,  0.099418, 82.358124,-76.846909},
-			 {  3.146211,  0.339586, 18.753832, 90.345459,  0.001072,  0.000000}}},
-{ Ce,	+3,	{{ 26.593231, 85.866432, -6.677695, 12.111847, 17.401903,-80.313423},
-			 {  3.280381,  0.001012,  4.313575, 17.868504,  0.326962,  0.000000}}},
-{ Ce,	+4,	{{ 17.457533, 25.659941, 11.691037, 19.695251,-16.994749, -3.515096},
-			 {  0.311812, -0.003793, 16.568687,  2.886395, -0.008931,  0.000000}}},
-{ Pr,	+3,	{{ 20.879841, 36.035797, 12.135341,  0.283103, 17.167803,-30.500784},
-			 {  2.870897,  0.002364, 16.615236, 53.909359,  0.306993,  0.000000}}},
-{ Pr,	+4,	{{ 17.496082, 21.538509, 20.403114, 12.062211, -7.492043, -9.016722},
-			 {  0.294457, -0.002742,  2.772886, 15.804613, -0.013556,  0.000000}}},
-{ Nd,	+3,	{{ 17.120077, 56.038139, 21.468307, 10.000671,  2.905866,-50.541992},
-			 {  0.291295,  0.001421,  2.743681, 14.581367, 22.485098,  0.000000}}},
-{ Pm,	+3,	{{ 22.221066, 17.068142, 12.805423,  0.435687, 52.238770,-46.767181},
-			 {  2.635767,  0.277039, 14.927315, 45.768017,  0.001455,  0.000000}}},
-{ Sm,	+3,	{{ 15.618565, 19.538092, 13.398946, -4.358811, 24.490461, -9.714854},
-			 {  0.006001,  0.306379, 14.979594,  0.748825,  2.454492,  0.000000}}},
-{ Eu,	+2,	{{ 23.899035, 31.657497, 12.955752,  1.700576, 16.992199,-26.204315},
-			 {  2.467332,  0.002230, 13.625002, 35.089481,  0.253136,  0.000000}}},
-{ Eu,	+3,	{{ 17.758327, 33.498665, 24.067188, 13.436883, -9.019134,-19.768026},
-			 {  0.244474, -0.003901,  2.487526, 14.568011, -0.015628,  0.000000}}},
-{ Gd,	+3,	{{ 24.344999, 16.945311, 13.866931,  0.481674, 93.506378,-88.147179},
-			 {  2.333971,  0.239215, 12.982995, 43.876347,  0.000673,  0.000000}}},
-{ Tb,	+3,	{{ 24.878252, 16.856016, 13.663937,  1.279671, 39.271294,-33.950317},
-			 {  2.223301,  0.227290, 11.812528, 29.910065,  0.001527,  0.000000}}},
-{ Dy,	+3,	{{ 16.864344, 90.383461, 13.675473,  1.687078, 25.540651,-85.150650},
-			 {  0.216275,  0.000593, 11.121207, 26.250975,  2.135930,  0.000000}}},
-{ Ho,	+3,	{{ 16.837524, 63.221336, 13.703766,  2.061602, 26.202621,-58.026505},
-			 {  0.206873,  0.000796, 10.500283, 24.031883,  2.055060,  0.000000}}},
-{ Er,	+3,	{{ 16.810127, 22.681061, 13.864114,  2.294506, 26.864477,-17.513460},
-			 {  0.198293,  0.002126,  9.973341, 22.836388,  1.979442,  0.000000}}},
-{ Tm,	+3,	{{ 16.787500, 15.350905, 14.182357,  2.299111, 27.573771,-10.192087},
-			 {  0.190852,  0.003036,  9.602934, 22.526880,  1.912862,  0.000000}}},
-{ Yb,	+2,	{{ 28.443794, 16.849527, 14.165081,  3.445311, 28.308853,-23.214935},
-			 {  1.863896,  0.183811,  9.225469, 23.691355,  0.001463,  0.000000}}},
-{ Yb,	+3,	{{ 28.191629, 16.828087, 14.167848,  2.744962, 23.171774,-18.103676},
-			 {  1.842889,  0.182788,  9.045957, 20.799847,  0.001759,  0.000000}}},
-{ Lu,	+3,	{{ 28.828693, 16.823227, 14.247617,  3.079559, 25.647667,-20.626528},
-			 {  1.776641,  0.175560,  8.575531, 19.693701,  0.001453,  0.000000}}},
-{ Hf,	+4,	{{ 29.267378, 16.792543, 14.785310,  2.184128, 23.791996,-18.820383},
-			 {  1.697911,  0.168313,  8.190025, 18.277578,  0.001431,  0.000000}}},
-{ Ta,	+5,	{{ 29.539469, 16.741854, 15.182070,  1.642916, 16.437447,-11.542459},
-			 {  1.612934,  0.160460,  7.654408, 17.070732,  0.001858,  0.000000}}},
-{ W,	+6,	{{ 29.729357, 17.247808, 15.184488,  1.154652,  0.739335,  3.945157},
-			 {  1.501648,  0.140803,  6.880573, 14.299601, 14.299618,  0.000000}}},
-{ Os,	+4,	{{ 17.113485, 15.792370, 23.342392,  4.090271,  7.671292,  3.988390},
-			 {  0.131850,  7.288542,  1.389307, 19.629425,  1.389307,  0.000000}}},
-{ Ir,	+3,	{{ 31.537575, 16.363338, 15.597141,  5.051404,  1.436935,  4.009459},
-			 {  1.334144,  7.451918,  0.127514, 21.705648,  0.127515,  0.000000}}},
-{ Ir,	+4,	{{ 30.391249, 16.146996, 17.019068,  4.458904,  0.975372,  4.006865},
-			 {  1.328519,  7.181766,  0.127337, 19.060146,  1.328519,  0.000000}}},
-{ Pt,	+2,	{{ 31.986849, 17.249048, 15.269374,  5.760234,  1.694079,  4.032512},
-			 {  1.281143,  7.625512,  0.123571, 24.190826,  0.123571,  0.000000}}},
-{ Pt,	+4,	{{ 41.932713, 16.339224, 17.653894,  6.012420,-12.036877,  4.094551},
-			 {  1.111409,  6.466086,  0.128917, 16.954155,  0.778721,  0.000000}}},
-{ Au,	+1,	{{ 32.124306, 16.716476, 16.814100,  7.311565,  0.993064,  4.040792},
-			 {  1.216073,  7.165378,  0.118715, 20.442486, 53.095985,  0.000000}}},
-{ Au,	+3,	{{ 31.704271, 17.545767, 16.819551,  5.522640,  0.361725,  4.042679},
-			 {  1.215561,  7.220506,  0.118812, 20.050970,  1.215562,  0.000000}}},
-{ Hg,	+1,	{{ 28.866837, 19.277540, 16.776051,  6.281459,  3.710289,  4.068430},
-			 {  1.173967,  7.583842,  0.115351, 29.055994,  1.173968,  0.000000}}},
-{ Hg,	+2,	{{ 32.411079, 18.690371, 16.711773,  9.974835, -3.847611,  4.052869},
-			 {  1.162980,  7.329806,  0.114518, 22.009489, 22.009493,  0.000000}}},
-{ Tl,	+1,	{{ 32.295044, 16.570049, 17.991013,  1.535355,  7.554591,  4.054030},
-			 {  1.101544,  0.110020,  6.528559, 52.495068, 20.338634,  0.000000}}},
-{ Tl,	+3,	{{ 32.525639, 19.139185, 17.100321,  5.891115, 12.599463, -9.256075},
-			 {  1.094966,  6.900992,  0.103667, 18.489614, -0.001401,  0.000000}}},
-{ Pb,	+2,	{{ 27.392647, 16.496822, 19.984501,  6.813923,  5.233910,  4.065623},
-			 {  1.058874,  0.106305,  6.708123, 24.395554,  1.058874,  0.000000}}},
-{ Pb,	+4,	{{ 32.505657, 20.014240, 14.645661,  5.029499,  1.760138,  4.044678},
-			 {  1.047035,  6.670321,  0.105279, 16.525040,  0.105279,  0.000000}}},
-{ Bi,	+3,	{{ 32.461437, 19.438683, 16.302486,  7.322662,  0.431704,  4.043703},
-			 {  0.997930,  6.038867,  0.101338, 18.371586, 46.361046,  0.000000}}},
-{ Bi,	+5,	{{ 16.734028, 20.580494,  9.452623, 61.155834,-34.041023,  4.113663},
-			 {  0.105076,  4.773282, 11.762162,  1.211775,  1.619408,  0.000000}}},
-{ Ra,	+2,	{{  4.986228, 32.474945, 21.947443, 11.800013, 10.807292,  3.956572},
-			 {  0.082597,  0.791468,  4.608034, 24.792431,  0.082597,  0.000000}}},
-{ Ac,	+3,	{{ 15.584983, 32.022125, 21.456327,  0.757593, 12.341252,  3.838984},
-			 {  0.077438,  0.739963,  4.040735, 47.525002, 19.406845,  0.000000}}},
-{ Th,	+4,	{{ 15.515445, 32.090691, 13.996399, 12.918157,  7.635514,  3.831122},
-			 {  0.074499,  0.711663,  3.871044, 18.596891,  3.871044,  0.000000}}},
-{ U,	+3,	{{ 15.360309, 32.395657, 21.961290,  1.325894, 14.251453,  3.706622},
-			 {  0.067815,  0.654643,  3.643409, 39.604965, 16.330570,  0.000000}}},
-{ U,	+4,	{{ 15.355091, 32.235306,  0.557745, 14.396367, 21.751173,  3.705863},
-			 {  0.067789,  0.652613, 42.354237, 15.908239,  3.553231,  0.000000}}},
-{ U,	+6,	{{ 15.333844, 31.770849, 21.274414, 13.872636,  0.048519,  3.700591},
-			 {  0.067644,  0.646384,  3.317894, 14.650250, 75.339699,  0.000000}}},
-{ Np,	+3,	{{ 15.378152, 32.572132, 22.206125,  1.413295, 14.828381,  3.603370},
-			 {  0.064613,  0.631420,  3.561936, 37.875511, 15.546129,  0.000000}}},
-{ Np,	+4,	{{ 15.373926, 32.423019, 21.969994,  0.662078, 14.969350,  3.603039},
-			 {  0.064597,  0.629658,  3.476389, 39.438942, 15.135764,  0.000000}}},
-{ Np,	+6,	{{ 15.359986, 31.992825, 21.412458,  0.066574, 14.568174,  3.600942},
-			 {  0.064528,  0.624505,  3.253441, 67.658318, 13.980832,  0.000000}}},
-{ Pu,	+3,	{{ 15.356004, 32.769127, 22.680210,  1.351055, 15.416232,  3.428895},
-			 {  0.060590,  0.604663,  3.491509, 37.260635, 14.981921,  0.000000}}},
-{ Pu,	+4,	{{ 15.416219, 32.610569, 22.256662,  0.719495, 15.518152,  3.480408},
-			 {  0.061456,  0.607938,  3.411848, 37.628792, 14.464360,  0.000000}}},
-{ Pu,	+6,	{{ 15.436506, 32.289719, 14.726737, 15.012391,  7.024677,  3.502325},
-			 {  0.061815,  0.606541,  3.245363, 13.616438,  3.245364,  0.000000}}}
+	{ H,	0,	{{  0.413048,  0.294953,  0.187491,  0.080701,  0.023736,  0.000049},
+				{ 15.569946, 32.398468,  5.711404, 61.889874,  1.334118,  0.000000}}},
+	{ He,	0,	{{  0.732354,  0.753896,  0.283819,  0.190003,  0.039139,  0.000487},
+				{ 11.553918,  4.595831,  1.546299, 26.463964,  0.377523,  0.000000}}},
+	{ Li,	0,	{{  0.974637,  0.158472,  0.811855,  0.262416,  0.790108,  0.002542},
+				{  4.334946,  0.342451, 97.102966,201.363831,  1.409234,  0.000000}}},
+	{ Be,	0,	{{  1.533712,  0.638283,  0.601052,  0.106139,  1.118414,  0.002511},
+				{ 42.662079,  0.595420, 99.106499,  0.151340,  1.843093,  0.000000}}},
+	{ B,	0,	{{  2.085185,  1.064580,  1.062788,  0.140515,  0.641784,  0.003823},
+				{ 23.494068,  1.137894, 61.238976,  0.114886,  0.399036,  0.000000}}},
+	{ C,	0,	{{  2.657506,  1.078079,  1.490909, -4.241070,  0.713791,  4.297983},
+				{ 14.780758,  0.776775, 42.086842, -0.000294,  0.239535,  0.000000}}},
+	{ N,	0,	{{ 11.893780,  3.277479,  1.858092,  0.858927,  0.912985,-11.804902},
+				{  0.000158, 10.232723, 30.344690,  0.656065,  0.217287,  0.000000}}},
+	{ O,	0,	{{  2.960427,  2.508818,  0.637853,  0.722838,  1.142756,  0.027014},
+				{ 14.182259,  5.936858,  0.112726, 34.958481,  0.390240,  0.000000}}},
+	{ F,	0,	{{  3.511943,  2.772244,  0.678385,  0.915159,  1.089261,  0.032557},
+				{ 10.687859,  4.380466,  0.093982, 27.255203,  0.313066,  0.000000}}},
+	{ Ne,	0,	{{  4.183749,  2.905726,  0.520513,  1.135641,  1.228065,  0.025576},
+				{  8.175457,  3.252536,  0.063295, 21.813910,  0.224952,  0.000000}}},
+	{ Na,	0,	{{  4.910127,  3.081783,  1.262067,  1.098938,  0.560991,  0.079712},
+				{  3.281434,  9.119178,  0.102763,132.013947,  0.405878,  0.000000}}},
+	{ Mg,	0,	{{  4.708971,  1.194814,  1.558157,  1.170413,  3.239403,  0.126842},
+				{  4.875207,108.506081,  0.111516, 48.292408,  1.928171,  0.000000}}},
+	{ Al,	0,	{{  4.730796,  2.313951,  1.541980,  1.117564,  3.154754,  0.139509},
+				{  3.628931, 43.051167,  0.095960,108.932388,  1.555918,  0.000000}}},
+	{ Si,	0,	{{  5.275329,  3.191038,  1.511514,  1.356849,  2.519114,  0.145073},
+				{  2.631338, 33.730728,  0.081119, 86.288643,  1.170087,  0.000000}}},
+	{ P,	0,	{{  1.950541,  4.146930,  1.494560,  1.522042,  5.729711,  0.155233},
+				{  0.908139, 27.044952,  0.071280, 67.520187,  1.981173,  0.000000}}},
+	{ S,	0,	{{  6.372157,  5.154568,  1.473732,  1.635073,  1.209372,  0.154722},
+				{  1.514347, 22.092527,  0.061373, 55.445175,  0.646925,  0.000000}}},
+	{ Cl,	0,	{{  1.446071,  6.870609,  6.151801,  1.750347,  0.634168,  0.146773},
+				{  0.052357,  1.193165, 18.343416, 46.398396,  0.401005,  0.000000}}},
+	{ Ar,	0,	{{  7.188004,  6.638454,  0.454180,  1.929593,  1.523654,  0.265954},
+				{  0.956221, 15.339877, 15.339862, 39.043823,  0.062409,  0.000000}}},
+	{ K,	0,	{{  8.163991,  7.146945,  1.070140,  0.877316,  1.486434,  0.253614},
+				{ 12.816323,  0.808945,210.327011, 39.597652,  0.052821,  0.000000}}},
+	{ Ca,	0,	{{  8.593655,  1.477324,  1.436254,  1.182839,  7.113258,  0.196255},
+				{ 10.460644,  0.041891, 81.390381,169.847839,  0.688098,  0.000000}}},
+	{ Sc,	0,	{{  1.476566,  1.487278,  1.600187,  9.177463,  7.099750,  0.157765},
+				{ 53.131023,  0.035325,137.319489,  9.098031,  0.602102,  0.000000}}},
+	{ Ti,	0,	{{  9.818524,  1.522646,  1.703101,  1.768774,  7.082555,  0.102473},
+				{  8.001879,  0.029763, 39.885422,120.157997,  0.532405,  0.000000}}},
+	{ V,	0,	{{ 10.473575,  1.547881,  1.986381,  1.865616,  7.056250,  0.067744},
+				{  7.081940,  0.026040, 31.909672,108.022842,  0.474882,  0.000000}}},
+	{ Cr,	0,	{{ 11.007069,  1.555477,  2.985293,  1.347855,  7.034779,  0.065510},
+				{  6.366281,  0.023987, 23.244839,105.774498,  0.429369,  0.000000}}},
+	{ Mn,	0,	{{ 11.709542,  1.733414,  2.673141,  2.023368,  7.003180, -0.147293},
+				{  5.597120,  0.017800, 21.788420, 89.517914,  0.383054,  0.000000}}},
+	{ Fe,	0,	{{ 12.311098,  1.876623,  3.066177,  2.070451,  6.975185, -0.304931},
+				{  5.009415,  0.014461, 18.743040, 82.767876,  0.346506,  0.000000}}},
+	{ Co,	0,	{{ 12.914510,  2.481908,  3.466894,  2.106351,  6.960892, -0.936572},
+				{  4.507138,  0.009126, 16.438129, 76.987320,  0.314418,  0.000000}}},
+	{ Ni,	0,	{{ 13.521865,  6.947285,  3.866028,  2.135900,  4.284731, -2.762697},
+				{  4.077277,  0.286763, 14.622634, 71.966080,  0.004437,  0.000000}}},
+	{ Cu,	0,	{{ 14.014192,  4.784577,  5.056806,  1.457971,  6.932996, -3.254477},
+				{  3.738280,  0.003744, 13.034982, 72.554794,  0.265666,  0.000000}}},
+	{ Zn,	0,	{{ 14.741002,  6.907748,  4.642337,  2.191766, 38.424042,-36.915829},
+				{  3.388232,  0.243315, 11.903689, 63.312130,  0.000397,  0.000000}}},
+	{ Ga,	0,	{{ 15.758946,  6.841123,  4.121016,  2.714681,  2.395246, -0.847395},
+				{  3.121754,  0.226057, 12.482196, 66.203621,  0.007238,  0.000000}}},
+	{ Ge,	0,	{{ 16.540613,  1.567900,  3.727829,  3.345098,  6.785079,  0.018726},
+				{  2.866618,  0.012198, 13.432163, 58.866047,  0.210974,  0.000000}}},
+	{ As,	0,	{{ 17.025642,  4.503441,  3.715904,  3.937200,  6.790175, -2.984117},
+				{  2.597739,  0.003012, 14.272119, 50.437996,  0.193015,  0.000000}}},
+	{ Se,	0,	{{ 17.354071,  4.653248,  4.259489,  4.136455,  6.749163, -3.160982},
+				{  2.349787,  0.002550, 15.579460, 45.181202,  0.177432,  0.000000}}},
+	{ Br,	0,	{{ 17.550570,  5.411882,  3.937180,  3.880645,  6.707793, -2.492088},
+				{  2.119226, 16.557184,  0.002481, 42.164009,  0.162121,  0.000000}}},
+	{ Kr,	0,	{{ 17.655279,  6.848105,  4.171004,  3.446760,  6.685200, -2.810592},
+				{  1.908231, 16.606236,  0.001598, 39.917473,  0.146896,  0.000000}}},
+	{ Rb,	0,	{{  8.123134,  2.138042,  6.761702,  1.156051, 17.679546,  1.139548},
+				{ 15.142385, 33.542667,  0.129372,224.132507,  1.713368,  0.000000}}},
+	{ Sr,	0,	{{ 17.730219,  9.795867,  6.099763,  2.620025,  0.600053,  1.140251},
+				{  1.563060, 14.310868,  0.120574,135.771317,  0.120574,  0.000000}}},
+	{ Y,	0,	{{ 17.792040, 10.253252,  5.714949,  3.170516,  0.918251,  1.131787},
+				{  1.429691, 13.132816,  0.112173,108.197029,  0.112173,  0.000000}}},
+	{ Zr,	0,	{{ 17.859772, 10.911038,  5.821115,  3.512513,  0.746965,  1.124859},
+				{  1.310692, 12.319285,  0.104353, 91.777542,  0.104353,  0.000000}}},
+	{ Nb,	0,	{{ 17.958399, 12.063054,  5.007015,  3.287667,  1.531019,  1.123452},
+				{  1.211590, 12.246687,  0.098615, 75.011948,  0.098615,  0.000000}}},
+	{ Mo,	0,	{{  6.236218, 17.987711, 12.973127,  3.451426,  0.210899,  1.108770},
+				{  0.090780,  1.108310, 11.468720, 66.684151,  0.090780,  0.000000}}},
+	{ Tc,	0,	{{ 17.840963,  3.428236,  1.373012, 12.947364,  6.335469,  1.074784},
+				{  1.005729, 41.901382,119.320541,  9.781542,  0.083391,  0.000000}}},
+	{ Ru,	0,	{{  6.271624, 17.906738, 14.123269,  3.746008,  0.908235,  1.043992},
+				{  0.077040,  0.928222,  9.555345, 35.860680,123.552246,  0.000000}}},
+	{ Rh,	0,	{{  6.216648, 17.919739,  3.854252,  0.840326, 15.173498,  0.995452},
+				{  0.070789,  0.856121, 33.889484,121.686691,  9.029517,  0.000000}}},
+	{ Pd,	0,	{{  6.121511,  4.784063, 16.631683,  4.318258, 13.246773,  0.883099},
+				{  0.062549,  0.784031,  8.751391, 34.489983,  0.784031,  0.000000}}},
+	{ Ag,	0,	{{  6.073874, 17.155437,  4.173344,  0.852238, 17.988686,  0.756603},
+				{  0.055333,  7.896512, 28.443739,110.376106,  0.716809,  0.000000}}},
+	{ Cd,	0,	{{  6.080986, 18.019468,  4.018197,  1.303510, 17.974669,  0.603504},
+				{  0.048990,  7.273646, 29.119284, 95.831207,  0.661231,  0.000000}}},
+	{ In,	0,	{{  6.196477, 18.816183,  4.050479,  1.638929, 17.962912,  0.333097},
+				{  0.042072,  6.695665, 31.009790,103.284348,  0.610714,  0.000000}}},
+	{ Sn,	0,	{{ 19.325171,  6.281571,  4.498866,  1.856934, 17.917318,  0.119024},
+				{  6.118104,  0.036915, 32.529045, 95.037186,  0.565651,  0.000000}}},
+	{ Sb,	0,	{{  5.394956,  6.549570, 19.650681,  1.827820, 17.867832, -0.290506},
+				{ 33.326523,  0.030974,  5.564929, 87.130966,  0.523992,  0.000000}}},
+	{ Te,	0,	{{  6.660302,  6.940756, 19.847015,  1.557175, 17.802427, -0.806668},
+				{ 33.031654,  0.025750,  5.065547, 84.101616,  0.487660,  0.000000}}},
+	{ I,	0,	{{ 19.884502,  6.736593,  8.110516,  1.170953, 17.548716, -0.448811},
+				{  4.628591,  0.027754, 31.849096, 84.406387,  0.463550,  0.000000}}},
+	{ Xe,	0,	{{ 19.978920, 11.774945,  9.332182,  1.244749, 17.737501, -6.065902},
+				{  4.143356,  0.010142, 28.796200, 75.280685,  0.413616,  0.000000}}},
+	{ Cs,	0,	{{ 17.418674,  8.314444, 10.323193,  1.383834, 19.876251, -2.322802},
+				{  0.399828,  0.016872, 25.605827,233.339676,  3.826915,  0.000000}}},
+	{ Ba,	0,	{{ 19.747343, 17.368477, 10.465718,  2.592602, 11.003653, -5.183497},
+				{  3.481823,  0.371224, 21.226641,173.834274,  0.010719,  0.000000}}},
+	{ La,	0,	{{ 19.966019, 27.329655, 11.018425,  3.086696, 17.335455,-21.745489},
+				{  3.197408,  0.003446, 19.955492,141.381973,  0.341817,  0.000000}}},
+	{ Ce,	0,	{{ 17.355122, 43.988499, 20.546650,  3.130670, 11.353665,-38.386017},
+				{  0.328369,  0.002047,  3.088196,134.907654, 18.832960,  0.000000}}},
+	{ Pr,	0,	{{ 21.551311, 17.161730, 11.903859,  2.679103,  9.564197, -3.871068},
+				{  2.995675,  0.312491, 17.716705,152.192825,  0.010468,  0.000000}}},
+	{ Nd,	0,	{{ 17.331244, 62.783924, 12.160097,  2.663483, 22.239950,-57.189842},
+				{  0.300269,  0.001320, 17.026001,148.748993,  2.910268,  0.000000}}},
+	{ Pm,	0,	{{ 17.286388, 51.560162, 12.478557,  2.675515, 22.960947,-45.973682},
+				{  0.286620,  0.001550, 16.223755,143.984512,  2.796480,  0.000000}}},
+	{ Sm,	0,	{{ 23.700363, 23.072214, 12.777782,  2.684217, 17.204367,-17.452166},
+				{  2.689539,  0.003491, 15.495437,139.862473,  0.274536,  0.000000}}},
+	{ Eu,	0,	{{ 17.186195, 37.156837, 13.103387,  2.707246, 24.419271,-31.586687},
+				{  0.261678,  0.001995, 14.787360,134.816299,  2.581883,  0.000000}}},
+	{ Gd,	0,	{{ 24.898117, 17.104952, 13.222581,  3.266152, 48.995213,-43.505684},
+				{  2.435028,  0.246961, 13.996325,110.863091,  0.001383,  0.000000}}},
+	{ Tb,	0,	{{ 25.910013, 32.344139, 13.765117,  2.751404, 17.064405,-26.851971},
+				{  2.373912,  0.002034, 13.481969,125.836510,  0.236916,  0.000000}}},
+	{ Dy,	0,	{{ 26.671785, 88.687576, 14.065445,  2.768497, 17.067781,-83.279831},
+				{  2.282593,  0.000665, 12.920230,121.937187,  0.225531,  0.000000}}},
+	{ Ho,	0,	{{ 27.150190, 16.999819, 14.059334,  3.386979, 46.546471,-41.165253},
+				{  2.169660,  0.215414, 12.213148,100.506783,  0.001211,  0.000000}}},
+	{ Er,	0,	{{ 28.174887, 82.493271, 14.624002,  2.802756, 17.018515,-77.135223},
+				{  2.120995,  0.000640, 11.915256,114.529938,  0.207519,  0.000000}}},
+	{ Tm,	0,	{{ 28.925894, 76.173798, 14.904704,  2.814812, 16.998117,-70.839813},
+				{  2.046203,  0.000656, 11.465375,111.411980,  0.199376,  0.000000}}},
+	{ Yb,	0,	{{ 29.676760, 65.624069, 15.160854,  2.830288, 16.997850,-60.313812},
+				{  1.977630,  0.000720, 11.044622,108.139153,  0.192110,  0.000000}}},
+	{ Lu,	0,	{{ 30.122866, 15.099346, 56.314899,  3.540980, 16.943729,-51.049416},
+				{  1.883090, 10.342764,  0.000780, 89.559250,  0.183849,  0.000000}}},
+	{ Hf,	0,	{{ 30.617033, 15.145351, 54.933548,  4.096253, 16.896156,-49.719837},
+				{  1.795613,  9.934469,  0.000739, 76.189705,  0.175914,  0.000000}}},
+	{ Ta,	0,	{{ 31.066359, 15.341823, 49.278297,  4.577665, 16.828321,-44.119026},
+				{  1.708732,  9.618455,  0.000760, 66.346199,  0.168002,  0.000000}}},
+	{ W,	0,	{{ 31.507900, 15.682498, 37.960129,  4.885509, 16.792112,-32.864574},
+				{  1.629485,  9.446448,  0.000898, 59.980675,  0.160798,  0.000000}}},
+	{ Re,	0,	{{ 31.888456, 16.117104, 42.390297,  5.211669, 16.767591,-37.412682},
+				{  1.549238,  9.233474,  0.000689, 54.516373,  0.152815,  0.000000}}},
+	{ Os,	0,	{{ 32.210297, 16.678440, 48.559906,  5.455839, 16.735533,-43.677956},
+				{  1.473531,  9.049695,  0.000519, 50.210201,  0.145771,  0.000000}}},
+	{ Ir,	0,	{{ 32.004436,  1.975454, 17.070105, 15.939454,  5.990003,  4.018893},
+				{  1.353767, 81.014175,  0.128093,  7.661196, 26.659403,  0.000000}}},
+	{ Pt,	0,	{{ 31.273891, 18.445440, 17.063745,  5.555933,  1.575270,  4.050394},
+				{  1.316992,  8.797154,  0.124741, 40.177994,  1.316997,  0.000000}}},
+	{ Au,	0,	{{ 16.777390, 19.317156, 32.979683,  5.595453, 10.576854, -6.279078},
+				{  0.122737,  8.621570,  1.256902, 38.008820,  0.000601,  0.000000}}},
+	{ Hg,	0,	{{ 16.839890, 20.023823, 28.428564,  5.881564,  4.714706,  4.076478},
+				{  0.115905,  8.256927,  1.195250, 39.247227,  1.195250,  0.000000}}},
+	{ Tl,	0,	{{ 16.630795, 19.386616, 32.808571,  1.747191,  6.356862,  4.066939},
+				{  0.110704,  7.181401,  1.119730, 90.660263, 26.014978,  0.000000}}},
+	{ Pb,	0,	{{ 16.419567, 32.738590,  6.530247,  2.342742, 19.916475,  4.049824},
+				{  0.105499,  1.055049, 25.025890, 80.906593,  6.664449,  0.000000}}},
+	{ Bi,	0,	{{ 16.282274, 32.725136,  6.678302,  2.694750, 20.576559,  4.040914},
+				{  0.101180,  1.002287, 25.714146, 77.057549,  6.291882,  0.000000}}},
+	{ Po,	0,	{{ 16.289164, 32.807171, 21.095163,  2.505901,  7.254589,  4.046556},
+				{  0.098121,  0.966265,  6.046622, 76.598068, 28.096128,  0.000000}}},
+	{ At,	0,	{{ 16.011461, 32.615547,  8.113899,  2.884082, 21.377867,  3.995684},
+				{  0.092639,  0.904416, 26.543257, 68.372963,  5.499512,  0.000000}}},
+	{ Rn,	0,	{{ 16.070229, 32.641106, 21.489658,  2.299218,  9.480184,  4.020977},
+				{  0.090437,  0.876409,  5.239687, 69.188477, 27.632641,  0.000000}}},
+	{ Fr,	0,	{{ 16.007385, 32.663830, 21.594351,  1.598497, 11.121192,  4.003472},
+				{  0.087031,  0.840187,  4.954467,199.805801, 26.905106,  0.000000}}},
+	{ Ra,	0,	{{ 32.563690, 21.396671, 11.298093,  2.834688, 15.914965,  3.981773},
+				{  0.801980,  4.590666, 22.758972,160.404388,  0.083544,  0.000000}}},
+	{ Ac,	0,	{{ 15.914053, 32.535042, 21.553976, 11.433394,  3.612409,  3.939212},
+				{  0.080511,  0.770669,  4.352206, 21.381622,130.500748,  0.000000}}},
+	{ Th,	0,	{{ 15.784024, 32.454899, 21.849222,  4.239077, 11.736191,  3.922533},
+				{  0.077067,  0.735137,  4.097976,109.464111, 20.512138,  0.000000}}},
+	{ Pa,	0,	{{ 32.740208, 21.973675, 12.957398,  3.683832, 15.744058,  3.886066},
+				{  0.709545,  4.050881, 19.231543,117.255005,  0.074040,  0.000000}}},
+	{ U,	0,	{{ 15.679275, 32.824306, 13.660459,  3.687261, 22.279434,  3.854444},
+				{  0.071206,  0.681177, 18.236156,112.500038,  3.930325,  0.000000}}},
+	{ Np,	0,	{{ 32.999901, 22.638077, 14.219973,  3.672950, 15.683245,  3.769391},
+				{  0.657086,  3.854918, 17.435474,109.464485,  0.068033,  0.000000}}},
+	{ Pu,	0,	{{ 33.281178, 23.148544, 15.153755,  3.031492, 15.704215,  3.664200},
+				{  0.634999,  3.856168, 16.849735,121.292038,  0.064857,  0.000000}}},
+	{ Am,	0,	{{ 33.435162, 23.657259, 15.576339,  3.027023, 15.746100,  3.541160},
+				{  0.612785,  3.792942, 16.195778,117.757004,  0.061755,  0.000000}}},
+	{ Cm,	0,	{{ 15.804837, 33.480801, 24.150198,  3.655563, 15.499866,  3.390840},
+				{  0.058619,  0.590160,  3.674720,100.736191, 15.408296,  0.000000}}},
+	{ Bk,	0,	{{ 15.889072, 33.625286, 24.710381,  3.707139, 15.839268,  3.213169},
+				{  0.055503,  0.569571,  3.615472, 97.694786, 14.754303,  0.000000}}},
+	{ Cf,	0,	{{ 33.794075, 25.467693, 16.048487,  3.657525, 16.008982,  3.005326},
+				{  0.550447,  3.581973, 14.357388, 96.064972,  0.052450,  0.000000}}},
+	{ H,	-1,	{{  0.702260,  0.763666,  0.248678,  0.261323,  0.023017,  0.000425},
+				{ 23.945604, 74.897919,  6.773289,233.583450,  1.337531,  0.000000}}},
+	{ Li,	+1,	{{  0.432724,  0.549257,  0.376575, -0.336481,  0.976060,  0.001764},
+				{  0.260367,  1.042836,  7.885294,  0.260368,  3.042539,  0.000000}}},
+	{ Be,	+2,	{{  3.055430, -2.372617,  1.044914,  0.544233,  0.381737, -0.653773},
+				{  0.001226,  0.001227,  1.542106,  0.456279,  4.047479,  0.000000}}},
+	{ C, AtomTypeTraits::kWKSFVal,
+				{{  1.258489,  0.728215,  1.119856,  2.168133,  0.705239,  0.019722},
+				{ 10.683769,  0.208177,  0.836097, 24.603704, 58.954273,  0.000000}}},
+	{ O,	-1,	{{  3.106934,  3.235142,  1.148886,  0.783981,  0.676953,  0.046136},
+				{ 19.868080,  6.960252,  0.170043, 65.693512,  0.630757,  0.000000}}},
+	{ O,	-2,	{{  3.990247,  2.300563,  0.607200,  1.907882,  1.167080,  0.025429},
+				{ 16.639956,  5.636819,  0.108493, 47.299709,  0.379984,  0.000000}}},
+	{ F,	-1,	{{  0.457649,  3.841561,  1.432771,  0.801876,  3.395041,  0.069525},
+				{  0.917243,  5.507803,  0.164955, 51.076206, 15.821679,  0.000000}}},
+	{ Na,	+1,	{{  3.148690,  4.073989,  0.767888,  0.995612,  0.968249,  0.045300},
+				{  2.594987,  6.046925,  0.070139, 14.122657,  0.217037,  0.000000}}},
+	{ Mg,	+2,	{{  3.062918,  4.135106,  0.853742,  1.036792,  0.852520,  0.058851},
+				{  2.015803,  4.417941,  0.065307,  9.669710,  0.187818,  0.000000}}},
+	{ Al,	+3,	{{  4.132015,  0.912049,  1.102425,  0.614876,  3.219136,  0.019397},
+				{  3.528641,  7.378344,  0.133708,  0.039065,  1.644728,  0.000000}}},
+	{ Si, AtomTypeTraits::kWKSFVal,
+				{{  2.879033,  3.072960,  1.515981,  1.390030,  4.995051,  0.146030},
+				{  1.239713, 38.706276,  0.081481, 93.616333,  2.770293,  0.000000}}},
+	{ Si,	+4,	{{  3.676722,  3.828496,  1.258033,  0.419024,  0.720421,  0.097266},
+				{  1.446851,  3.013144,  0.064397,  0.206254,  5.970222,  0.000000}}},
+	{ Cl,	-1,	{{  1.061802,  7.139886,  6.524271,  2.355626, 35.829403,-34.916603},
+				{  0.144727,  1.171795, 19.467655, 60.320301,  0.000436,  0.000000}}},
+	{ K,	+1,	{{-17.609339,  1.494873,  7.150305, 10.899569, 15.808228,  0.257164},
+				{ 18.840979,  0.053453,  0.812940, 22.264105, 14.351593,  0.000000}}},
+	{ Ca,	+2,	{{  8.501441, 12.880483,  9.765095,  7.156669,  0.711160,-21.013187},
+				{ 10.525848, -0.004033,  0.010692,  0.684443, 27.231771,  0.000000}}},
+	{ Sc,	+3,	{{  7.104348,  1.511488,-53.669773, 38.404816, 24.532240,  0.118642},
+				{  0.601957,  0.033386, 12.572138, 10.859736, 14.125230,  0.000000}}},
+	{ Ti,	+2,	{{  7.040119,  1.496285,  9.657304,  0.006534,  1.649561,  0.150362},
+				{  0.537072,  0.031914,  8.009958,201.800293, 24.039482,  0.000000}}},
+	{ Ti,	+3,	{{ 36.587933,  7.230255, -9.086077,  2.084594, 17.294008,-35.111282},
+				{  0.000681,  0.522262,  5.262317, 15.881716,  6.149805,  0.000000}}},
+	{ Ti,	+4,	{{ 45.355537,  7.092900,  7.483858,-43.498817,  1.678915, -0.110628},
+				{  9.252186,  0.523046, 13.082852, 10.193876,  0.023064,  0.000000}}},
+	{ V,	+2,	{{  7.754356,  2.064100,  2.576998,  2.011404,  7.126177, -0.533379},
+				{  7.066315,  0.014993,  7.066308, 22.055786,  0.467568,  0.000000}}},
+	{ V,	+3,	{{  9.958480,  1.596350,  1.483442,-10.846044, 17.332867,  0.474921},
+				{  6.763041,  0.056895, 17.750029,  0.328826,  0.388013,  0.000000}}},
+	{ V,	+5,	{{ 15.575018,  8.448095,  1.612040, -9.721855,  1.534029,  0.552676},
+				{  0.682708,  5.566640, 10.527077,  0.907961,  0.066667,  0.000000}}},
+	{ Cr,	+2,	{{ 10.598877,  1.565858,  2.728280,  0.098064,  6.959321,  0.049870},
+				{  6.151846,  0.023519, 17.432816, 54.002388,  0.426301,  0.000000}}},
+	{ Cr,	+3,	{{  7.989310,  1.765079,  2.627125,  1.829380,  6.980908, -0.192123},
+				{  6.068867,  0.018342,  6.068887, 16.309284,  0.420864,  0.000000}}},
+	{ Mn,	+2,	{{ 11.287712, 26.042414,  3.058096,  0.090258,  7.088306,-24.566132},
+				{  5.506225,  0.000774, 16.158575, 54.766354,  0.375580,  0.000000}}},
+	{ Mn,	+3,	{{  6.926972,  2.081342, 11.128379,  2.375107, -0.419287, -0.093713},
+				{  0.378315,  0.015054,  5.379957, 14.429586,  0.004939,  0.000000}}},
+	{ Mn,	+4,	{{ 12.409131,  7.466993,  1.809947,-12.138477, 10.780248,  0.672146},
+				{  0.300400,  0.112814, 12.520756,  0.168653,  5.173237,  0.000000}}},
+	{ Fe,	+2,	{{ 11.776765, 11.165097,  3.533495,  0.165345,  7.036932, -9.676919},
+				{  4.912232,  0.001748, 14.166556, 42.381958,  0.341324,  0.000000}}},
+	{ Fe,	+3,	{{  9.721638, 63.403847,  2.141347,  2.629274,  7.033846,-61.930725},
+				{  4.869297,  0.000293,  4.867602, 13.539076,  0.338520,  0.000000}}},
+	{ Co,	+2,	{{  6.993840, 26.285812, 12.254289,  0.246114,  4.017407,-24.796852},
+				{  0.310779,  0.000684,  4.400528, 35.741447, 12.536393,  0.000000}}},
+	{ Co,	+3,	{{  6.861739,  2.678570, 12.281889,  3.501741, -0.179384, -1.147345},
+				{  0.309794,  0.008142,  4.331703, 11.914167, 11.914167,  0.000000}}},
+	{ Ni,	+2,	{{ 12.519017, 37.832058,  4.387257,  0.661552,  6.949072,-36.344471},
+				{  3.933053,  0.000442, 10.449184, 23.860998,  0.283723,  0.000000}}},
+	{ Ni,	+3,	{{ 13.579366,  1.902844, 12.859268,  3.811005, -6.838595, -0.317618},
+				{  0.313140,  0.012621,  3.906407, 10.894311,  0.344379,  0.000000}}},
+	{ Cu,	+1,	{{ 12.960763, 16.342150,  1.110102,  5.520682,  6.915452,-14.849320},
+				{  3.576010,  0.000975, 29.523218, 10.114283,  0.261326,  0.000000}}},
+	{ Cu,	+2,	{{ 11.895569, 16.344978,  5.799817,  1.048804,  6.789088,-14.878383},
+				{  3.378519,  0.000924,  8.133653, 20.526524,  0.254741,  0.000000}}},
+	{ Zn,	+2,	{{ 13.340772, 10.428857,  5.544489,  0.762295,  6.869172, -8.945248},
+				{  3.215913,  0.001413,  8.542680, 21.891756,  0.239215,  0.000000}}},
+	{ Ga,	+3,	{{ 13.123875, 35.288189,  6.126979,  0.611551,  6.724807,-33.875122},
+				{  2.809960,  0.000323,  6.831534, 16.784311,  0.212002,  0.000000}}},
+	{ Ge,	+4,	{{  6.876636,  6.779091,  9.969591,  3.135857,  0.152389,  1.086542},
+				{  2.025174,  0.176650,  3.573822,  7.685848, 16.677574,  0.000000}}},
+	{ Br,	-1,	{{ 17.714310,  6.466926,  6.947385,  4.402674, -0.697279,  1.152674},
+				{  2.122554, 19.050768,  0.152708, 58.690361, 58.690372,  0.000000}}},
+	{ Rb,	+1,	{{ 17.684320,  7.761588,  6.680874,  2.668883,  0.070974,  1.133263},
+				{  1.710209, 14.919863,  0.128542, 31.654478,  0.128543,  0.000000}}},
+	{ Sr,	+2,	{{ 17.694973,  1.275762,  6.154252,  9.234786,  0.515995,  1.125309},
+				{  1.550888, 30.133041,  0.118774, 13.821799,  0.118774,  0.000000}}},
+	{ Y,	+3,	{{ 46.660366, 10.369686,  4.623042,-62.170834, 17.471146, 19.023842},
+				{ -0.019971, 13.180257,  0.176398, -0.016727,  1.467348,  0.000000}}},
+	{ Zr,	+4,	{{  6.802956, 17.699253, 10.650647, -0.248108,  0.250338,  0.827902},
+				{  0.096228,  1.296127, 11.240715, -0.219259, -0.219021,  0.000000}}},
+	{ Nb,	+3,	{{ 17.714323,  1.675213,  7.483963,  8.322464, 11.143573, -8.339573},
+				{  1.172419, 30.102791,  0.080255, -0.002983, 10.456687,  0.000000}}},
+	{ Nb,	+5,	{{ 17.580206,  7.633277, 10.793497,  0.180884, 67.837921,-68.024780},
+				{  1.165852,  0.078558,  9.507652, 31.621656, -0.000438,  0.000000}}},
+	{ Mo,	+3,	{{  7.447050, 17.778122, 11.886068,  1.997905,  1.789626, -1.898764},
+				{  0.072000,  1.073145,  9.834720, 28.221746, -0.011674,  0.000000}}},
+	{ Mo,	+5,	{{  7.929879, 17.667669, 11.515987,  0.500402, 77.444084,-78.056595},
+				{  0.068856,  1.068064,  9.046229, 26.558945, -0.000473,  0.000000}}},
+	{ Mo,	+6,	{{ 34.757683,  9.653037,  6.584769,-18.628115,  2.490594,  1.141916},
+				{  1.301770,  7.123843,  0.094097,  1.617443, 12.335434,  0.000000}}},
+	{ Ru,	+3,	{{ 17.894758, 13.579529, 10.729251,  2.474095, 48.227997,-51.905243},
+				{  0.902827,  8.740579,  0.045125, 24.764954, -0.001699,  0.000000}}},
+	{ Ru,	+4,	{{ 17.845776, 13.455084, 10.229087,  1.653524, 14.059795,-17.241762},
+				{  0.901070,  8.482392,  0.045972, 23.015272, -0.004889,  0.000000}}},
+	{ Rh,	+3,	{{ 17.758621, 14.569813,  5.298320,  2.533579,  0.879753,  0.960843},
+				{  0.841779,  8.319533,  0.069050, 23.709131,  0.069050,  0.000000}}},
+	{ Rh,	+4,	{{ 17.716188, 14.446654,  5.185801,  1.703448,  0.989992,  0.959941},
+				{  0.840572,  8.100647,  0.068995, 22.357307,  0.068995,  0.000000}}},
+	{ Pd,	+2,	{{  6.122282, 15.651012,  3.513508,  9.060790,  8.771199,  0.879336},
+				{  0.062424,  8.018296, 24.784275,  0.776457,  0.776457,  0.000000}}},
+	{ Pd,	+4,	{{  6.152421,-96.069023, 31.622141, 81.578255, 17.801403,  0.915874},
+				{  0.063951, 11.090354, 13.466152,  9.758302,  0.783014,  0.000000}}},
+	{ Ag,	+1,	{{  6.091192,  4.019526, 16.948174,  4.258638, 13.889437,  0.785127},
+				{  0.056305,  0.719340,  7.758938, 27.368349,  0.719340,  0.000000}}},
+	{ Ag,	+2,	{{  6.401808, 48.699802,  4.799859,-32.332523, 16.356710,  1.068247},
+				{  0.068167,  0.942270, 20.639496,  1.100365,  6.883131,  0.000000}}},
+	{ Cd,	+2,	{{  6.093711, 43.909691, 17.041306,-39.675117, 17.958918,  0.664795},
+				{  0.050624,  8.654143, 15.621396, 11.082067,  0.667591,  0.000000}}},
+	{ In,	+3,	{{  6.206277, 18.497746,  3.078131, 10.524613,  7.401234,  0.293677},
+				{  0.041357,  6.605563, 18.792250,  0.608082,  0.608082,  0.000000}}},
+	{ Sn,	+2,	{{  6.353672,  4.770377, 14.672025,  4.235959, 18.002131, -0.042519},
+				{  0.034720,  6.167891,  6.167879, 29.006456,  0.561774,  0.000000}}},
+	{ Sn,	+4,	{{ 15.445732,  6.420892,  4.562980,  1.713385, 18.033537, -0.172219},
+				{  6.280898,  0.033144,  6.280899, 17.983601,  0.557980,  0.000000}}},
+	{ Sb,	+3,	{{ 10.189171, 57.461918, 19.356573,  4.862206,-45.394096,  1.516108},
+				{  0.089485,  0.375256,  5.357987, 22.153736,  0.297768,  0.000000}}},
+	{ Sb,	+5,	{{ 17.920622,  6.647932, 12.724075,  1.555545,  7.600591, -0.445371},
+				{  0.522315,  0.029487,  5.718210, 16.433775,  5.718204,  0.000000}}},
+	{ I,	-1,	{{ 20.010330, 17.835524,  8.104130,  2.231118,  9.158548, -3.341004},
+				{  4.565931,  0.444266, 32.430672, 95.149040,  0.014906,  0.000000}}},
+	{ Cs,	+1,	{{ 19.939056, 24.967621, 10.375884,  0.454243, 17.660248,-19.394306},
+				{  3.770511,  0.004040, 25.311275, 76.537766,  0.384730,  0.000000}}},
+	{ Ba,	+2,	{{ 19.750200, 17.513683, 10.884892,  0.321585, 65.149834,-59.618172},
+				{  3.430748,  0.361590, 21.358307, 70.309402,  0.001418,  0.000000}}},
+	{ La,	+3,	{{ 19.688887, 17.345703, 11.356296,  0.099418, 82.358124,-76.846909},
+				{  3.146211,  0.339586, 18.753832, 90.345459,  0.001072,  0.000000}}},
+	{ Ce,	+3,	{{ 26.593231, 85.866432, -6.677695, 12.111847, 17.401903,-80.313423},
+				{  3.280381,  0.001012,  4.313575, 17.868504,  0.326962,  0.000000}}},
+	{ Ce,	+4,	{{ 17.457533, 25.659941, 11.691037, 19.695251,-16.994749, -3.515096},
+				{  0.311812, -0.003793, 16.568687,  2.886395, -0.008931,  0.000000}}},
+	{ Pr,	+3,	{{ 20.879841, 36.035797, 12.135341,  0.283103, 17.167803,-30.500784},
+				{  2.870897,  0.002364, 16.615236, 53.909359,  0.306993,  0.000000}}},
+	{ Pr,	+4,	{{ 17.496082, 21.538509, 20.403114, 12.062211, -7.492043, -9.016722},
+				{  0.294457, -0.002742,  2.772886, 15.804613, -0.013556,  0.000000}}},
+	{ Nd,	+3,	{{ 17.120077, 56.038139, 21.468307, 10.000671,  2.905866,-50.541992},
+				{  0.291295,  0.001421,  2.743681, 14.581367, 22.485098,  0.000000}}},
+	{ Pm,	+3,	{{ 22.221066, 17.068142, 12.805423,  0.435687, 52.238770,-46.767181},
+				{  2.635767,  0.277039, 14.927315, 45.768017,  0.001455,  0.000000}}},
+	{ Sm,	+3,	{{ 15.618565, 19.538092, 13.398946, -4.358811, 24.490461, -9.714854},
+				{  0.006001,  0.306379, 14.979594,  0.748825,  2.454492,  0.000000}}},
+	{ Eu,	+2,	{{ 23.899035, 31.657497, 12.955752,  1.700576, 16.992199,-26.204315},
+				{  2.467332,  0.002230, 13.625002, 35.089481,  0.253136,  0.000000}}},
+	{ Eu,	+3,	{{ 17.758327, 33.498665, 24.067188, 13.436883, -9.019134,-19.768026},
+				{  0.244474, -0.003901,  2.487526, 14.568011, -0.015628,  0.000000}}},
+	{ Gd,	+3,	{{ 24.344999, 16.945311, 13.866931,  0.481674, 93.506378,-88.147179},
+				{  2.333971,  0.239215, 12.982995, 43.876347,  0.000673,  0.000000}}},
+	{ Tb,	+3,	{{ 24.878252, 16.856016, 13.663937,  1.279671, 39.271294,-33.950317},
+				{  2.223301,  0.227290, 11.812528, 29.910065,  0.001527,  0.000000}}},
+	{ Dy,	+3,	{{ 16.864344, 90.383461, 13.675473,  1.687078, 25.540651,-85.150650},
+				{  0.216275,  0.000593, 11.121207, 26.250975,  2.135930,  0.000000}}},
+	{ Ho,	+3,	{{ 16.837524, 63.221336, 13.703766,  2.061602, 26.202621,-58.026505},
+				{  0.206873,  0.000796, 10.500283, 24.031883,  2.055060,  0.000000}}},
+	{ Er,	+3,	{{ 16.810127, 22.681061, 13.864114,  2.294506, 26.864477,-17.513460},
+				{  0.198293,  0.002126,  9.973341, 22.836388,  1.979442,  0.000000}}},
+	{ Tm,	+3,	{{ 16.787500, 15.350905, 14.182357,  2.299111, 27.573771,-10.192087},
+				{  0.190852,  0.003036,  9.602934, 22.526880,  1.912862,  0.000000}}},
+	{ Yb,	+2,	{{ 28.443794, 16.849527, 14.165081,  3.445311, 28.308853,-23.214935},
+				{  1.863896,  0.183811,  9.225469, 23.691355,  0.001463,  0.000000}}},
+	{ Yb,	+3,	{{ 28.191629, 16.828087, 14.167848,  2.744962, 23.171774,-18.103676},
+				{  1.842889,  0.182788,  9.045957, 20.799847,  0.001759,  0.000000}}},
+	{ Lu,	+3,	{{ 28.828693, 16.823227, 14.247617,  3.079559, 25.647667,-20.626528},
+				{  1.776641,  0.175560,  8.575531, 19.693701,  0.001453,  0.000000}}},
+	{ Hf,	+4,	{{ 29.267378, 16.792543, 14.785310,  2.184128, 23.791996,-18.820383},
+				{  1.697911,  0.168313,  8.190025, 18.277578,  0.001431,  0.000000}}},
+	{ Ta,	+5,	{{ 29.539469, 16.741854, 15.182070,  1.642916, 16.437447,-11.542459},
+				{  1.612934,  0.160460,  7.654408, 17.070732,  0.001858,  0.000000}}},
+	{ W,	+6,	{{ 29.729357, 17.247808, 15.184488,  1.154652,  0.739335,  3.945157},
+				{  1.501648,  0.140803,  6.880573, 14.299601, 14.299618,  0.000000}}},
+	{ Os,	+4,	{{ 17.113485, 15.792370, 23.342392,  4.090271,  7.671292,  3.988390},
+				{  0.131850,  7.288542,  1.389307, 19.629425,  1.389307,  0.000000}}},
+	{ Ir,	+3,	{{ 31.537575, 16.363338, 15.597141,  5.051404,  1.436935,  4.009459},
+				{  1.334144,  7.451918,  0.127514, 21.705648,  0.127515,  0.000000}}},
+	{ Ir,	+4,	{{ 30.391249, 16.146996, 17.019068,  4.458904,  0.975372,  4.006865},
+				{  1.328519,  7.181766,  0.127337, 19.060146,  1.328519,  0.000000}}},
+	{ Pt,	+2,	{{ 31.986849, 17.249048, 15.269374,  5.760234,  1.694079,  4.032512},
+				{  1.281143,  7.625512,  0.123571, 24.190826,  0.123571,  0.000000}}},
+	{ Pt,	+4,	{{ 41.932713, 16.339224, 17.653894,  6.012420,-12.036877,  4.094551},
+				{  1.111409,  6.466086,  0.128917, 16.954155,  0.778721,  0.000000}}},
+	{ Au,	+1,	{{ 32.124306, 16.716476, 16.814100,  7.311565,  0.993064,  4.040792},
+				{  1.216073,  7.165378,  0.118715, 20.442486, 53.095985,  0.000000}}},
+	{ Au,	+3,	{{ 31.704271, 17.545767, 16.819551,  5.522640,  0.361725,  4.042679},
+				{  1.215561,  7.220506,  0.118812, 20.050970,  1.215562,  0.000000}}},
+	{ Hg,	+1,	{{ 28.866837, 19.277540, 16.776051,  6.281459,  3.710289,  4.068430},
+				{  1.173967,  7.583842,  0.115351, 29.055994,  1.173968,  0.000000}}},
+	{ Hg,	+2,	{{ 32.411079, 18.690371, 16.711773,  9.974835, -3.847611,  4.052869},
+				{  1.162980,  7.329806,  0.114518, 22.009489, 22.009493,  0.000000}}},
+	{ Tl,	+1,	{{ 32.295044, 16.570049, 17.991013,  1.535355,  7.554591,  4.054030},
+				{  1.101544,  0.110020,  6.528559, 52.495068, 20.338634,  0.000000}}},
+	{ Tl,	+3,	{{ 32.525639, 19.139185, 17.100321,  5.891115, 12.599463, -9.256075},
+				{  1.094966,  6.900992,  0.103667, 18.489614, -0.001401,  0.000000}}},
+	{ Pb,	+2,	{{ 27.392647, 16.496822, 19.984501,  6.813923,  5.233910,  4.065623},
+				{  1.058874,  0.106305,  6.708123, 24.395554,  1.058874,  0.000000}}},
+	{ Pb,	+4,	{{ 32.505657, 20.014240, 14.645661,  5.029499,  1.760138,  4.044678},
+				{  1.047035,  6.670321,  0.105279, 16.525040,  0.105279,  0.000000}}},
+	{ Bi,	+3,	{{ 32.461437, 19.438683, 16.302486,  7.322662,  0.431704,  4.043703},
+				{  0.997930,  6.038867,  0.101338, 18.371586, 46.361046,  0.000000}}},
+	{ Bi,	+5,	{{ 16.734028, 20.580494,  9.452623, 61.155834,-34.041023,  4.113663},
+				{  0.105076,  4.773282, 11.762162,  1.211775,  1.619408,  0.000000}}},
+	{ Ra,	+2,	{{  4.986228, 32.474945, 21.947443, 11.800013, 10.807292,  3.956572},
+				{  0.082597,  0.791468,  4.608034, 24.792431,  0.082597,  0.000000}}},
+	{ Ac,	+3,	{{ 15.584983, 32.022125, 21.456327,  0.757593, 12.341252,  3.838984},
+				{  0.077438,  0.739963,  4.040735, 47.525002, 19.406845,  0.000000}}},
+	{ Th,	+4,	{{ 15.515445, 32.090691, 13.996399, 12.918157,  7.635514,  3.831122},
+				{  0.074499,  0.711663,  3.871044, 18.596891,  3.871044,  0.000000}}},
+	{ U,	+3,	{{ 15.360309, 32.395657, 21.961290,  1.325894, 14.251453,  3.706622},
+				{  0.067815,  0.654643,  3.643409, 39.604965, 16.330570,  0.000000}}},
+	{ U,	+4,	{{ 15.355091, 32.235306,  0.557745, 14.396367, 21.751173,  3.705863},
+				{  0.067789,  0.652613, 42.354237, 15.908239,  3.553231,  0.000000}}},
+	{ U,	+6,	{{ 15.333844, 31.770849, 21.274414, 13.872636,  0.048519,  3.700591},
+				{  0.067644,  0.646384,  3.317894, 14.650250, 75.339699,  0.000000}}},
+	{ Np,	+3,	{{ 15.378152, 32.572132, 22.206125,  1.413295, 14.828381,  3.603370},
+				{  0.064613,  0.631420,  3.561936, 37.875511, 15.546129,  0.000000}}},
+	{ Np,	+4,	{{ 15.373926, 32.423019, 21.969994,  0.662078, 14.969350,  3.603039},
+				{  0.064597,  0.629658,  3.476389, 39.438942, 15.135764,  0.000000}}},
+	{ Np,	+6,	{{ 15.359986, 31.992825, 21.412458,  0.066574, 14.568174,  3.600942},
+				{  0.064528,  0.624505,  3.253441, 67.658318, 13.980832,  0.000000}}},
+	{ Pu,	+3,	{{ 15.356004, 32.769127, 22.680210,  1.351055, 15.416232,  3.428895},
+				{  0.060590,  0.604663,  3.491509, 37.260635, 14.981921,  0.000000}}},
+	{ Pu,	+4,	{{ 15.416219, 32.610569, 22.256662,  0.719495, 15.518152,  3.480408},
+				{  0.061456,  0.607938,  3.411848, 37.628792, 14.464360,  0.000000}}},
+	{ Pu,	+6,	{{ 15.436506, 32.289719, 14.726737, 15.012391,  7.024677,  3.502325},
+				{  0.061815,  0.606541,  3.245363, 13.616438,  3.245364,  0.000000}}}
 };
 
 SFDataArrayElement kELSFData[] = {
diff --git a/src/BondMap.cpp b/src/BondMap.cpp
index e03b055..db6a762 100644
--- a/src/BondMap.cpp
+++ b/src/BondMap.cpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -24,14 +24,14 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <fstream>
 #include <algorithm>
+#include <fstream>
 #include <mutex>
 
+#include "cif++/BondMap.hpp"
 #include "cif++/Cif++.hpp"
-#include "cif++/Compound.hpp"
 #include "cif++/CifUtils.hpp"
-#include "cif++/BondMap.hpp"
+#include "cif++/Compound.hpp"
 
 namespace mmcif
 {
@@ -39,223 +39,79 @@ namespace mmcif
 namespace
 {
 
-union IDType
-{
-	IDType() : id_n(0){}
-	IDType(const IDType& rhs) : id_n(rhs.id_n) {}
-	IDType(const std::string& s)
-		: IDType()
-	{
-		assert(s.length() <= 4);
-		if (s.length() > 4)
-			throw BondMapException("Atom ID '" + s + "' is too long");
-		std::copy(s.begin(), s.end(), id_s);
-	}
-
-	IDType& operator=(const IDType& rhs)
-	{
-		id_n = rhs.id_n;
-		return *this;
-	}
-
-	IDType& operator=(const std::string& s)
+	union IDType
 	{
-		id_n = 0;
-		assert(s.length() <= 4);
-		if (s.length() > 4)
-			throw BondMapException("Atom ID '" + s + "' is too long");
-		std::copy(s.begin(), s.end(), id_s);
-		return *this;
-	}
-
-	bool operator<(const IDType& rhs) const
-	{
-		return id_n < rhs.id_n;
-	}
-
-	bool operator<=(const IDType& rhs) const
-	{
-		return id_n <= rhs.id_n;
-	}
-
-	bool operator==(const IDType& rhs) const
-	{
-		return id_n == rhs.id_n;
-	}
-
-	bool operator!=(const IDType& rhs) const
-	{
-		return id_n != rhs.id_n;
-	}
-
-	char		id_s[4];
-	uint32_t	id_n;
-};
-
-static_assert(sizeof(IDType) == 4, "atom_id_type should be 4 bytes");	
-}
-
-// // --------------------------------------------------------------------
-
-// void createBondInfoFile(const fs::path& components, const fs::path& infofile)
-// {
-// 	std::ofstream outfile(infofile.string() + ".tmp", std::ios::binary);
-// 	if (not outfile.is_open())
-// 		throw BondMapException("Could not create bond info file " + infofile.string() + ".tmp");
-
-// 	cif::File infile(components);
-
-// 	std::set<atom_id_type> atomIDs;
-// 	std::vector<atom_id_type> compoundIDs;
-
-// 	for (auto& db: infile)
-// 	{
-// 		auto chem_comp_bond = db.get("chem_comp_bond");
-// 		if (not chem_comp_bond)
-// 		{
-// 			if (cif::VERBOSE > 1)
-// 				std::cerr << "Missing chem_comp_bond category in data block " << db.getName() << std::endl;
-// 			continue;
-// 		}
-
-// 		for (const auto& [atom_id_1, atom_id_2]: chem_comp_bond->rows<std::string,std::string>({"atom_id_1", "atom_id_2"}))
-// 		{
-// 			atomIDs.insert(atom_id_1);
-// 			atomIDs.insert(atom_id_2);
-// 		}
-
-// 		compoundIDs.push_back({ db.getName() });
-// 	}
-
-// 	if (cif::VERBOSE)
-// 		std::cout << "Number of unique atom names is " << atomIDs.size() << std::endl
-// 				  << "Number of unique residue names is " << compoundIDs.size() << std::endl;
-
-// 	CompoundBondInfoFileHeader header = {};
-// 	header.indexEntries = compoundIDs.size();
-// 	header.atomEntries = atomIDs.size();
-
-// 	outfile << header;
-	
-// 	for (auto atomID: atomIDs)
-// 		outfile << atomID;
-
-// 	auto dataOffset = outfile.tellp();
-
-// 	std::vector<CompoundBondInfo> entries;
-// 	entries.reserve(compoundIDs.size());
-
-// 	std::map<atom_id_type, uint16_t> atomIDMap;
-// 	for (auto& atomID: atomIDs)
-// 		atomIDMap[atomID] = atomIDMap.size();
-
-// 	for (auto& db: infile)
-// 	{
-// 		auto chem_comp_bond = db.get("chem_comp_bond");
-// 		if (not chem_comp_bond)
-// 			continue;
-
-// 		std::set<uint16_t> bondedAtoms;
-
-// 		for (const auto& [atom_id_1, atom_id_2]: chem_comp_bond->rows<std::string,std::string>({"atom_id_1", "atom_id_2"}))
-// 		{
-// 			bondedAtoms.insert(atomIDMap[atom_id_1]);
-// 			bondedAtoms.insert(atomIDMap[atom_id_2]);
-// 		}
-
-// 		std::map<uint16_t, int32_t> bondedAtomMap;
-// 		for (auto id: bondedAtoms)
-// 			bondedAtomMap[id] = static_cast<int32_t>(bondedAtomMap.size());
-		
-// 		CompoundBondInfo info = {
-// 			db.getName(),
-// 			static_cast<uint32_t>(bondedAtomMap.size()),
-// 			outfile.tellp() - dataOffset
-// 		};
-
-// 		entries.push_back(info);
-
-// 		// An now first write the array of atom ID's in this compound
-// 		for (uint16_t id: bondedAtoms)
-// 			write(outfile, id);
-
-// 		// And then the symmetric matrix with bonds
-// 		size_t N = bondedAtoms.size();
-// 		size_t M = (N * (N - 1)) / 2;
-
-// 		size_t K = M / 8;
-// 		if (M % 8)
-// 			K += 1;
-		
-// 		std::vector<uint8_t> m(K);
-
-// 		for (const auto& [atom_id_1, atom_id_2]: chem_comp_bond->rows<std::string,std::string>({"atom_id_1", "atom_id_2"}))
-// 		{
-// 			auto a = bondedAtomMap[atomIDMap[atom_id_1]];
-// 			auto b = bondedAtomMap[atomIDMap[atom_id_2]];
-
-// 			assert(a != b);
-// 			assert((int)b < (int)N);
-
-// 			if (a > b)
-// 				std::swap(a, b);
-			
-// 			size_t ix = ((b - 1) * b) / 2 + a;
-// 			assert(ix < M);
-
-// 			auto Bix = ix / 8;
-// 			auto bix = ix % 8;
-
-// 			m[Bix] |= 1 << bix;
-// 		}
-
-// 		outfile.write(reinterpret_cast<char*>(m.data()), m.size());
-// 	}
-
-// 	header.dataSize = outfile.tellp() - dataOffset;
+		IDType()
+			: id_n(0)
+		{
+		}
+		IDType(const IDType &rhs)
+			: id_n(rhs.id_n)
+		{
+		}
+		IDType(const std::string &s)
+			: IDType()
+		{
+			assert(s.length() <= 4);
+			if (s.length() > 4)
+				throw BondMapException("Atom ID '" + s + "' is too long");
+			std::copy(s.begin(), s.end(), id_s);
+		}
 
-// 	std::sort(entries.begin(), entries.end(), [](CompoundBondInfo& a, CompoundBondInfo& b)
-// 	{
-// 		return a.id < b.id;
-// 	});
+		IDType &operator=(const IDType &rhs)
+		{
+			id_n = rhs.id_n;
+			return *this;
+		}
 
-// 	for (auto& info: entries)
-// 		outfile << info;
+		IDType &operator=(const std::string &s)
+		{
+			id_n = 0;
+			assert(s.length() <= 4);
+			if (s.length() > 4)
+				throw BondMapException("Atom ID '" + s + "' is too long");
+			std::copy(s.begin(), s.end(), id_s);
+			return *this;
+		}
 
-// 	outfile.seekp(0);
-// 	outfile << header;
+		bool operator<(const IDType &rhs) const
+		{
+			return id_n < rhs.id_n;
+		}
 
-// 	// compress
-// 	outfile.close();
+		bool operator<=(const IDType &rhs) const
+		{
+			return id_n <= rhs.id_n;
+		}
 
-// 	std::ifstream in(infofile.string() + ".tmp", std::ios::binary);
-// 	std::ofstream out(infofile, std::ios::binary);
+		bool operator==(const IDType &rhs) const
+		{
+			return id_n == rhs.id_n;
+		}
 
-// 	{
-// 		io::filtering_stream<io::output> os;
-// 		os.push(io::gzip_compressor());
-// 		os.push(out);
-// 		io::copy(in, os);
-// 	}
+		bool operator!=(const IDType &rhs) const
+		{
+			return id_n != rhs.id_n;
+		}
 
-// 	in.close();
-// 	out.close();
+		char id_s[4];
+		uint32_t id_n;
+	};
 
-// 	fs::remove(infofile.string() + ".tmp");
-// }
+	static_assert(sizeof(IDType) == 4, "atom_id_type should be 4 bytes");
+} // namespace
 
 // --------------------------------------------------------------------
 
 struct CompoundBondInfo
 {
-	IDType	mID;
-	std::set<std::tuple<uint32_t,uint32_t>> mBonded;
+	IDType mID;
+	std::set<std::tuple<uint32_t, uint32_t>> mBonded;
 
 	bool bonded(uint32_t a1, uint32_t a2) const
 	{
-		return mBonded.count({ a1, a2 }) > 0;
+		return mBonded.count({a1, a2}) > 0;
 	}
-
 };
 
 // --------------------------------------------------------------------
@@ -263,20 +119,18 @@ struct CompoundBondInfo
 class CompoundBondMap
 {
   public:
-
 	static CompoundBondMap &instance()
 	{
 		static std::unique_ptr<CompoundBondMap> s_instance(new CompoundBondMap);
 		return *s_instance;
 	}
 
-	bool bonded(const std::string& compoundID, const std::string& atomID1, const std::string& atomID2);
+	bool bonded(const std::string &compoundID, const std::string &atomID1, const std::string &atomID2);
 
   private:
-
 	CompoundBondMap() {}
 
-	uint32_t getAtomID(const std::string& atomID)
+	uint32_t getAtomID(const std::string &atomID)
 	{
 		IDType id(atomID);
 
@@ -290,16 +144,16 @@ class CompoundBondMap
 		}
 		else
 			result = i->second;
-		
+
 		return result;
 	}
 
-	std::map<IDType,uint32_t> mAtomIDIndex;
+	std::map<IDType, uint32_t> mAtomIDIndex;
 	std::vector<CompoundBondInfo> mCompounds;
 	std::mutex mMutex;
 };
 
-bool CompoundBondMap::bonded(const std::string &compoundID, const std::string& atomID1, const std::string& atomID2)
+bool CompoundBondMap::bonded(const std::string &compoundID, const std::string &atomID1, const std::string &atomID2)
 {
 	std::lock_guard lock(mMutex);
 
@@ -310,32 +164,35 @@ bool CompoundBondMap::bonded(const std::string &compoundID, const std::string& a
 	uint32_t a2 = getAtomID(atomID2);
 	if (a1 > a2)
 		std::swap(a1, a2);
-	
-	for (auto &bi: mCompounds)
+
+	for (auto &bi : mCompounds)
 	{
 		if (bi.mID != id)
 			continue;
-		
+
 		return bi.bonded(a1, a2);
 	}
 
 	bool result = false;
 
 	// not found in our cache, calculate
-	CompoundBondInfo bondInfo{ id };
+	CompoundBondInfo bondInfo{id};
 
 	auto compound = mmcif::CompoundFactory::instance().create(compoundID);
 	if (not compound)
-		std::cerr << "Missing compound bond info for " << compoundID << std::endl;
+	{
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Missing compound bond info for " << compoundID << std::endl;
+	}
 	else
 	{
-		for (auto &atom: compound->bonds())
+		for (auto &atom : compound->bonds())
 		{
 			uint32_t ca1 = getAtomID(atom.atomID[0]);
 			uint32_t ca2 = getAtomID(atom.atomID[1]);
 			if (ca1 > ca2)
 				std::swap(ca1, ca2);
-			
+
 			bondInfo.mBonded.insert({ca1, ca2});
 			result = result or (a1 == ca1 and a2 == ca2);
 		}
@@ -348,27 +205,27 @@ bool CompoundBondMap::bonded(const std::string &compoundID, const std::string& a
 
 // --------------------------------------------------------------------
 
-BondMap::BondMap(const Structure& p)
+BondMap::BondMap(const Structure &p)
 {
-	auto& compoundBondInfo = CompoundBondMap::instance();
+	auto &compoundBondInfo = CompoundBondMap::instance();
 
 	auto atoms = p.atoms();
 	dim = uint32_t(atoms.size());
 
-//	bond = std::vector<bool>(dim * (dim - 1), false);
+	//	bond = std::vector<bool>(dim * (dim - 1), false);
 
-	for (auto& atom: atoms)
+	for (auto &atom : atoms)
 		index[atom.id()] = uint32_t(index.size());
-	
-	auto bindAtoms = [this](const std::string& a, const std::string& b)
+
+	auto bindAtoms = [this](const std::string &a, const std::string &b)
 	{
 		uint32_t ixa = index[a];
 		uint32_t ixb = index[b];
-		
+
 		bond.insert(key(ixa, ixb));
 	};
 
-	auto linkAtoms = [this,&bindAtoms](const std::string& a, const std::string& b)
+	auto linkAtoms = [this, &bindAtoms](const std::string &a, const std::string &b)
 	{
 		bindAtoms(a, b);
 
@@ -376,20 +233,20 @@ BondMap::BondMap(const Structure& p)
 		link[b].insert(a);
 	};
 
-	cif::Datablock& db = p.getFile().data();
+	cif::Datablock &db = p.getFile().data();
 
 	// collect all compounds first
 	std::set<std::string> compounds;
-	for (auto c: db["chem_comp"])
+	for (auto c : db["chem_comp"])
 		compounds.insert(c["id"].as<std::string>());
-	
+
 	// make sure we also have all residues in the polyseq
-	for (auto m: db["entity_poly_seq"])
+	for (auto m : db["entity_poly_seq"])
 	{
 		std::string c = m["mon_id"].as<std::string>();
 		if (compounds.count(c))
 			continue;
-		
+
 		if (cif::VERBOSE > 1)
 			std::cerr << "Warning: mon_id " << c << " is missing in the chem_comp category" << std::endl;
 		compounds.insert(c);
@@ -398,68 +255,68 @@ BondMap::BondMap(const Structure& p)
 	cif::Progress progress(compounds.size(), "Creating bond map");
 
 	// some helper indices to speed things up a bit
-	std::map<std::tuple<std::string,int,std::string>,std::string> atomMapByAsymSeqAndAtom;
-	for (auto& a: p.atoms())
+	std::map<std::tuple<std::string, int, std::string>, std::string> atomMapByAsymSeqAndAtom;
+	for (auto &a : p.atoms())
 	{
 		auto key = make_tuple(a.labelAsymID(), a.labelSeqID(), a.labelAtomID());
 		atomMapByAsymSeqAndAtom[key] = a.id();
 	}
-	
+
 	// first link all residues in a polyseq
-	
+
 	std::string lastAsymID;
 	int lastSeqID = 0;
-	for (auto r: db["pdbx_poly_seq_scheme"])
+	for (auto r : db["pdbx_poly_seq_scheme"])
 	{
 		std::string asymID;
 		int seqID;
 
 		cif::tie(asymID, seqID) = r.get("asym_id", "seq_id");
 
-		if (asymID != lastAsymID)		// first in a new sequece
+		if (asymID != lastAsymID) // first in a new sequece
 		{
 			lastAsymID = asymID;
 			lastSeqID = seqID;
 			continue;
 		}
-		
+
 		auto c = atomMapByAsymSeqAndAtom[make_tuple(asymID, lastSeqID, "C")];
 		auto n = atomMapByAsymSeqAndAtom[make_tuple(asymID, seqID, "N")];
 
-		if (not (c.empty() or n.empty()))
+		if (not(c.empty() or n.empty()))
 			bindAtoms(c, n);
-		
+
 		lastSeqID = seqID;
 	}
 
-	for (auto l: db["struct_conn"])
+	for (auto l : db["struct_conn"])
 	{
 		std::string asym1, asym2, atomId1, atomId2;
 		int seqId1 = 0, seqId2 = 0;
 		cif::tie(asym1, asym2, atomId1, atomId2, seqId1, seqId2) =
 			l.get("ptnr1_label_asym_id", "ptnr2_label_asym_id",
-				  "ptnr1_label_atom_id", "ptnr2_label_atom_id",
-				  "ptnr1_label_seq_id", "ptnr2_label_seq_id");
+				"ptnr1_label_atom_id", "ptnr2_label_atom_id",
+				"ptnr1_label_seq_id", "ptnr2_label_seq_id");
 
 		std::string a = atomMapByAsymSeqAndAtom[make_tuple(asym1, seqId1, atomId1)];
 		std::string b = atomMapByAsymSeqAndAtom[make_tuple(asym2, seqId2, atomId2)];
-			
-		if (not (a.empty() or b.empty()))
+
+		if (not(a.empty() or b.empty()))
 			linkAtoms(a, b);
 	}
 
 	// then link all atoms in the compounds
-	
-	for (auto c: compounds)
+
+	for (auto c : compounds)
 	{
 		if (c == "HOH" or c == "H2O" or c == "WAT")
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "skipping water in bond map calculation" << std::endl;
 			continue;
 		}
 
-		auto bonded = [c, &compoundBondInfo](const Atom& a, const Atom& b)
+		auto bonded = [c, &compoundBondInfo](const Atom &a, const Atom &b)
 		{
 			auto label_a = a.labelAtomID();
 			auto label_b = b.labelAtomID();
@@ -468,16 +325,17 @@ BondMap::BondMap(const Structure& p)
 		};
 
 		// loop over poly_seq_scheme
-		for (auto r: db["pdbx_poly_seq_scheme"].find(cif::Key("mon_id") == c))
+		for (auto r : db["pdbx_poly_seq_scheme"].find(cif::Key("mon_id") == c))
 		{
 			std::string asymID;
 			int seqID;
 			cif::tie(asymID, seqID) = r.get("asym_id", "seq_id");
-			
+
 			std::vector<Atom> rAtoms;
 			copy_if(atoms.begin(), atoms.end(), back_inserter(rAtoms),
-				[&](auto& a) { return a.labelAsymID() == asymID and a.labelSeqID() == seqID; });
-			
+				[&](auto &a)
+				{ return a.labelAsymID() == asymID and a.labelSeqID() == seqID; });
+
 			for (uint32_t i = 0; i + 1 < rAtoms.size(); ++i)
 			{
 				for (uint32_t j = i + 1; j < rAtoms.size(); ++j)
@@ -489,15 +347,16 @@ BondMap::BondMap(const Structure& p)
 		}
 
 		// loop over pdbx_nonpoly_scheme
-		for (auto r: db["pdbx_nonpoly_scheme"].find(cif::Key("mon_id") == c))
+		for (auto r : db["pdbx_nonpoly_scheme"].find(cif::Key("mon_id") == c))
 		{
 			std::string asymID;
 			cif::tie(asymID) = r.get("asym_id");
-			
+
 			std::vector<Atom> rAtoms;
 			copy_if(atoms.begin(), atoms.end(), back_inserter(rAtoms),
-				[&](auto& a) { return a.labelAsymID() == asymID; });
-			
+				[&](auto &a)
+				{ return a.labelAsymID() == asymID; });
+
 			for (uint32_t i = 0; i + 1 < rAtoms.size(); ++i)
 			{
 				for (uint32_t j = i + 1; j < rAtoms.size(); ++j)
@@ -506,7 +365,7 @@ BondMap::BondMap(const Structure& p)
 					{
 						uint32_t ixa = index[rAtoms[i].id()];
 						uint32_t ixb = index[rAtoms[j].id()];
-						
+
 						bond.insert(key(ixa, ixb));
 					}
 				}
@@ -514,15 +373,16 @@ BondMap::BondMap(const Structure& p)
 		}
 
 		// loop over pdbx_branch_scheme
-		for (auto r: db["pdbx_branch_scheme"].find(cif::Key("mon_id") == c))
+		for (auto r : db["pdbx_branch_scheme"].find(cif::Key("mon_id") == c))
 		{
 			std::string asymID;
 			cif::tie(asymID) = r.get("asym_id");
-			
+
 			std::vector<Atom> rAtoms;
 			copy_if(atoms.begin(), atoms.end(), back_inserter(rAtoms),
-				[&](auto& a) { return a.labelAsymID() == asymID; });
-			
+				[&](auto &a)
+				{ return a.labelAsymID() == asymID; });
+
 			for (uint32_t i = 0; i + 1 < rAtoms.size(); ++i)
 			{
 				for (uint32_t j = i + 1; j < rAtoms.size(); ++j)
@@ -531,31 +391,31 @@ BondMap::BondMap(const Structure& p)
 					{
 						uint32_t ixa = index[rAtoms[i].id()];
 						uint32_t ixb = index[rAtoms[j].id()];
-						
+
 						bond.insert(key(ixa, ixb));
 					}
 				}
 			}
 		}
 	}
-	
+
 	// start by creating an index for single bonds
-	
-	std::multimap<uint32_t,uint32_t> b1_2;
-	for (auto& bk: bond)
+
+	std::multimap<uint32_t, uint32_t> b1_2;
+	for (auto &bk : bond)
 	{
 		uint32_t a, b;
 		std::tie(a, b) = dekey(bk);
-		
-		b1_2.insert({ a, b });
-		b1_2.insert({ b, a });
+
+		b1_2.insert({a, b});
+		b1_2.insert({b, a});
 	}
-	
-	std::multimap<uint32_t,uint32_t> b1_3;
+
+	std::multimap<uint32_t, uint32_t> b1_3;
 	for (uint32_t i = 0; i < dim; ++i)
 	{
 		auto a = b1_2.equal_range(i);
-		
+
 		std::vector<uint32_t> s;
 		for (auto j = a.first; j != a.second; ++j)
 			s.push_back(j->second);
@@ -566,12 +426,12 @@ BondMap::BondMap(const Structure& p)
 			{
 				uint32_t x = s[si1];
 				uint32_t y = s[si2];
-				
+
 				if (isBonded(x, y))
 					continue;
-				
-				b1_3.insert({ x, y });
-				b1_3.insert({ y, x });
+
+				b1_3.insert({x, y});
+				b1_3.insert({y, x});
 			}
 		}
 	}
@@ -580,48 +440,48 @@ BondMap::BondMap(const Structure& p)
 	{
 		auto a1 = b1_2.equal_range(i);
 		auto a2 = b1_3.equal_range(i);
-		
+
 		for (auto ai1 = a1.first; ai1 != a1.second; ++ai1)
 		{
 			for (auto ai2 = a2.first; ai2 != a2.second; ++ai2)
 			{
 				uint32_t b1 = ai1->second;
 				uint32_t b2 = ai2->second;
-				
+
 				if (isBonded(b1, b2))
 					continue;
-				
+
 				bond_1_4.insert(key(b1, b2));
 			}
 		}
 	}
 }
 
-std::vector<std::string> BondMap::linked(const Atom& a) const
+std::vector<std::string> BondMap::linked(const Atom &a) const
 {
 	auto i = link.find(a.id());
-	
+
 	std::vector<std::string> result;
-	
+
 	if (i != link.end())
 		result = std::vector<std::string>(i->second.begin(), i->second.end());
 
 	return result;
 }
 
-std::vector<std::string> BondMap::atomIDsForCompound(const std::string& compoundID)
+std::vector<std::string> BondMap::atomIDsForCompound(const std::string &compoundID)
 {
 	std::vector<std::string> result;
 
-	auto* compound = mmcif::CompoundFactory::instance().create(compoundID);
+	auto *compound = mmcif::CompoundFactory::instance().create(compoundID);
 
 	if (compound == nullptr)
 		throw BondMapException("Missing bond information for compound " + compoundID);
 
-	for (auto& compAtom: compound->atoms())
+	for (auto &compAtom : compound->atoms())
 		result.push_back(compAtom.id);
 
 	return result;
 }
 
-}
+} // namespace mmcif
diff --git a/src/Cif++.cpp b/src/Cif++.cpp
index 24304ad..ae830f2 100644
--- a/src/Cif++.cpp
+++ b/src/Cif++.cpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -30,6 +30,7 @@
 #include <numeric>
 #include <regex>
 #include <set>
+#include <shared_mutex>
 #include <stack>
 #include <tuple>
 #include <unordered_map>
@@ -351,7 +352,7 @@ namespace detail
 // --------------------------------------------------------------------
 // Datablock implementation
 
-Datablock::Datablock(const std::string &name)
+Datablock::Datablock(const std::string_view name)
 	: mName(name)
 	, mValidator(nullptr)
 	, mNext(nullptr)
@@ -363,70 +364,81 @@ Datablock::~Datablock()
 	delete mNext;
 }
 
-std::string Datablock::firstItem(const std::string &tag) const
+auto Datablock::emplace(std::string_view name) -> std::tuple<iterator, bool>
 {
-	std::string result;
+	// LRU code
 
-	std::string catName, itemName;
-	std::tie(catName, itemName) = splitTagName(tag);
+	std::shared_lock lock(mLock);
 
-	for (auto &cat : mCategories)
+	bool isNew = true;
+
+	auto i = begin();
+	while (i != end())
 	{
-		if (iequals(cat.name(), catName))
+		if (iequals(name, i->name()))
 		{
-			for (auto row : cat)
+			isNew = false;
+
+			if (i != begin())
 			{
-				result = row[itemName].as<std::string>();
-				break;
+				auto n = std::next(i);
+				mCategories.splice(begin(), mCategories, i, n);
 			}
 
 			break;
 		}
-	}
 
-	return result;
-}
-
-auto Datablock::emplace(const std::string &name) -> std::tuple<iterator, bool>
-{
-	bool isNew = false;
-	iterator i = find_if(begin(), end(), [name](const Category &cat) -> bool
-		{ return iequals(cat.name(), name); });
+		++i;
+	}
 
-	if (i == end())
+	if (isNew)
 	{
-		isNew = true;
-		i = mCategories.emplace(end(), *this, name, mValidator);
+		mCategories.emplace(begin(), *this, std::string(name), mValidator);
+
+		for (auto &cat : mCategories)
+			cat.updateLinks();
 	}
 
-	return std::make_tuple(i, isNew);
+	return std::make_tuple(begin(), isNew);
 }
 
-Category &Datablock::operator[](const std::string &name)
+Category &Datablock::operator[](std::string_view name)
 {
 	iterator i;
 	std::tie(i, std::ignore) = emplace(name);
 	return *i;
 }
 
-Category *Datablock::get(const std::string &name)
+Category *Datablock::get(std::string_view name)
 {
-	auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
-		{ return iequals(cat.name(), name); });
+	std::shared_lock lock(mLock);
 
-	return i == end() ? nullptr : &*i;
+	for (auto &cat : mCategories)
+	{
+		if (iequals(cat.name(), name))
+			return &cat;
+	}
+
+	return nullptr;
 }
 
-const Category *Datablock::get(const std::string &name) const
+const Category *Datablock::get(std::string_view name) const
 {
-	auto i = find_if(begin(), end(), [name](const Category &cat) -> bool
-		{ return iequals(cat.name(), name); });
+	std::shared_lock lock(mLock);
 
-	return i == end() ? nullptr : &*i;
+	for (auto &cat : mCategories)
+	{
+		if (iequals(cat.name(), name))
+			return &cat;
+	}
+
+	return nullptr;
 }
 
 bool Datablock::isValid()
 {
+	std::shared_lock lock(mLock);
+
 	if (mValidator == nullptr)
 		throw std::runtime_error("Validator not specified");
 
@@ -438,20 +450,26 @@ bool Datablock::isValid()
 
 void Datablock::validateLinks() const
 {
+	std::shared_lock lock(mLock);
+
 	for (auto &cat : *this)
 		cat.validateLinks();
 }
 
-void Datablock::setValidator(Validator *v)
+void Datablock::setValidator(const Validator *v)
 {
+	std::shared_lock lock(mLock);
+
 	mValidator = v;
 
 	for (auto &cat : *this)
 		cat.setValidator(v);
 }
 
-void Datablock::add_software(const std::string &name, const std::string &classification, const std::string &versionNr, const std::string &versionDate)
+void Datablock::add_software(const std::string_view name, const std::string &classification, const std::string &versionNr, const std::string &versionDate)
 {
+	std::shared_lock lock(mLock);
+
 	Category &cat = operator[]("software");
 	auto ordNr = cat.size() + 1;
 	// TODO: should we check this ordinal number???
@@ -465,12 +483,16 @@ void Datablock::add_software(const std::string &name, const std::string &classif
 
 void Datablock::getTagOrder(std::vector<std::string> &tags) const
 {
+	std::shared_lock lock(mLock);
+
 	for (auto &cat : *this)
 		cat.getTagOrder(tags);
 }
 
 void Datablock::write(std::ostream &os)
 {
+	std::shared_lock lock(mLock);
+
 	os << "data_" << mName << std::endl
 	   << "# " << std::endl;
 
@@ -505,6 +527,8 @@ void Datablock::write(std::ostream &os)
 
 void Datablock::write(std::ostream &os, const std::vector<std::string> &order)
 {
+	std::shared_lock lock(mLock);
+
 	os << "data_" << mName << std::endl
 	   << "# " << std::endl;
 
@@ -546,40 +570,15 @@ void Datablock::write(std::ostream &os, const std::vector<std::string> &order)
 
 		cat.write(os);
 	}
-
-	//	// mmcif support, sort of. First write the 'entry' Category
-	//	// and if it exists, _AND_ we have a Validator, write out the
-	//	// auditConform record.
-	//
-	//	for (auto& cat: mCategories)
-	//	{
-	//		if (cat.name() == "entry")
-	//		{
-	//			cat.write(os);
-	//
-	//			if (mValidator != nullptr)
-	//			{
-	//				Category auditConform(*this, "audit_conform", nullptr);
-	//				auditConform.emplace({
-	//					{ "dict_name", mValidator->dictName() },
-	//					{ "dict_version", mValidator->dictVersion() }
-	//				});
-	//				auditConform.write(os);
-	//			}
-	//
-	//			break;
-	//		}
-	//	}
-	//
-	//	for (auto& cat: mCategories)
-	//	{
-	//		if (cat.name() != "entry" and cat.name() != "audit_conform")
-	//			cat.write(os);
-	//	}
 }
 
 bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
 {
+	bool result = true;
+
+	std::shared_lock lockA(dbA.mLock);
+	std::shared_lock lockB(dbB.mLock);
+
 	std::vector<std::string> catA, catB;
 
 	for (auto &cat : dbA)
@@ -605,14 +604,59 @@ bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
 
 	while (catA_i != catA.end() and catB_i != catB.end())
 	{
-		if (not iequals(*catA_i, *catB_i))
-			return false;
+		std::string nA = *catA_i;
+		ba::to_lower(nA);
+
+		std::string nB = *catB_i;
+		ba::to_lower(nB);
+
+		int d = nA.compare(nB);
+		if (d > 0)
+		{
+			auto cat = dbB.get(*catB_i);
+
+			if (cat == nullptr)
+				missingA.push_back(*catB_i);
+
+			++catB_i;
+		}
+		else if (d < 0)
+		{
+			auto cat = dbA.get(*catA_i);
+
+			if (cat == nullptr)
+				missingB.push_back(*catA_i);
 
-		++catA_i, ++catB_i;
+			++catA_i;
+		}
+		else
+			++catA_i, ++catB_i;
 	}
 
-	if (catA_i != catA.end() or catB_i != catB.end())
-		return false;
+	while (catA_i != catA.end())
+		missingB.push_back(*catA_i++);
+
+	while (catB_i != catB.end())
+		missingA.push_back(*catB_i++);
+
+	if (not(missingA.empty() and missingB.empty()))
+	{
+		if (cif::VERBOSE > 1)
+		{
+			std::cerr << "compare of datablocks failed" << std::endl;
+			if (not missingA.empty())
+				std::cerr << "Categories missing in A: " << ba::join(missingA, ", ") << std::endl
+						  << std::endl;
+
+			if (not missingB.empty())
+				std::cerr << "Categories missing in B: " << ba::join(missingB, ", ") << std::endl
+						  << std::endl;
+
+			result = false;
+		}
+		else
+			return false;
+	}
 
 	// Second loop, now compare category values
 	catA_i = catA.begin(), catB_i = catB.begin();
@@ -632,20 +676,28 @@ bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
 			++catA_i;
 		else
 		{
-			if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
-				return false;
+			if (not(*dbA.get(*catA_i) == *dbB.get(*catB_i)))
+			{
+				if (cif::VERBOSE > 1)
+				{
+					std::cerr << "Compare of datablocks failed due to unequal values in category " << *catA_i << std::endl;
+					result = false;
+				}
+				else
+					return false;
+			}
 			++catA_i;
 			++catB_i;
 		}
 	}
 
-	return true;
+	return result;
 }
 
-std::ostream& operator<<(std::ostream &os, const Datablock &data)
+std::ostream &operator<<(std::ostream &os, const Datablock &data)
 {
 	// whoohoo... this sucks!
-	const_cast<Datablock&>(data).write(os);
+	const_cast<Datablock &>(data).write(os);
 	return os;
 }
 
@@ -1080,7 +1132,7 @@ void CatIndex::reconstruct()
 		insert(r.mData);
 
 	// maybe reconstruction can be done quicker by using the following commented code.
-	// however, I've not had the time to think of a way to std::set the red/black flag correctly in that case.
+	// however, I've not had the time to think of a way to set the red/black flag correctly in that case.
 
 	//	std::vector<ItemRow*> rows;
 	//	transform(mCat.begin(), mCat.end(), backInserter(rows),
@@ -1172,82 +1224,15 @@ size_t CatIndex::size() const
 	return result;
 }
 
-//bool CatIndex::isValid() const
-//{
-//	bool result = true;
-//
-//	if (mRoot != nullptr)
-//	{
-//		uint32_t minBlack = numeric_limits<uint32_t>::max();
-//		uint32_t maxBlack = 0;
-//
-//		assert(not mRoot->mRed);
-//
-//		result = isValid(mRoot, false, 0, minBlack, maxBlack);
-//		assert(minBlack == maxBlack);
-//	}
-//
-//	return result;
-//}
-//
-//bool CatIndex::validate(entry* h, bool isParentRed, uint32_t blackDepth, uint32_t& minBlack, uint32_t& maxBlack) const
-//{
-//	bool result = true;
-//
-//	if (h->mRed)
-//		assert(not isParentRed);
-//	else
-//		++blackDepth;
-//
-//	if (isParentRed)
-//		assert(not h->mRed);
-//
-//	if (h->mLeft != nullptr and h->mRight != nullptr)
-//	{
-//		if (isRed(h->mLeft))
-//			assert(not isRed(h->mRight));
-//		if (isRed(h->mRight))
-//			assert(not isRed(h->mLeft));
-//	}
-//
-//	if (h->mLeft != nullptr)
-//	{
-//		assert(mComp(h->mLeft->mRow, h->mRow) < 0);
-//		validate(h->mLeft, h->mRed, blackDepth, minBlack, maxBlack);
-//	}
-//	else
-//	{
-//		if (minBlack > blackDepth)
-//			minBlack = blackDepth;
-//		if (maxBlack < blackDepth)
-//			maxBlack = blackDepth;
-//	}
-//
-//	if (h->mRight != nullptr)
-//	{
-//		assert(mComp(h->mRight->mRow, h->mRow) > 0);
-//		validate(h->mRight, h->mRight, blackDepth, minBlack, maxBlack);
-//	}
-//	else
-//	{
-//		if (minBlack > blackDepth)
-//			minBlack = blackDepth;
-//		if (maxBlack < blackDepth)
-//			maxBlack = blackDepth;
-//	}
-//}
-
 // --------------------------------------------------------------------
 
 RowSet::RowSet(Category &cat)
 	: mCat(&cat)
-// , mCond(nullptr)
 {
 }
 
 RowSet::RowSet(Category &cat, Condition &&cond)
 	: mCat(&cat)
-// , mCond(new Condition(std::forward<Condition>(cond)))
 {
 	cond.prepare(cat);
 
@@ -1261,21 +1246,17 @@ RowSet::RowSet(Category &cat, Condition &&cond)
 RowSet::RowSet(const RowSet &rhs)
 	: mCat(rhs.mCat)
 	, mItems(rhs.mItems)
-// , mCond(nullptr)
 {
 }
 
 RowSet::RowSet(RowSet &&rhs)
 	: mCat(rhs.mCat)
 	, mItems(std::move(rhs.mItems))
-// , mCond(rhs.mCond)
 {
-	// rhs.mCond = nullptr;
 }
 
 RowSet::~RowSet()
 {
-	// delete mCond;
 }
 
 RowSet &RowSet::operator=(const RowSet &rhs)
@@ -1311,7 +1292,7 @@ RowSet &RowSet::orderBy(std::initializer_list<std::string> items)
 
 // --------------------------------------------------------------------
 
-Category::Category(Datablock &db, const std::string &name, Validator *Validator)
+Category::Category(Datablock &db, const std::string_view name, const Validator *Validator)
 	: mDb(db)
 	, mName(name)
 	, mValidator(Validator)
@@ -1346,7 +1327,7 @@ Category::~Category()
 	delete mIndex;
 }
 
-void Category::setValidator(Validator *v)
+void Category::setValidator(const Validator *v)
 {
 	mValidator = v;
 
@@ -1371,14 +1352,41 @@ void Category::setValidator(Validator *v)
 	}
 	else
 		mCatValidator = nullptr;
+
+	updateLinks();
+}
+
+void Category::updateLinks()
+{
+	mChildLinks.clear();
+	mParentLinks.clear();
+
+	if (mValidator != nullptr)
+	{
+		for (auto link : mValidator->getLinksForParent(mName))
+		{
+			auto childCat = mDb.get(link->mChildCategory);
+			if (childCat == nullptr)
+				continue;
+			mChildLinks.push_back({childCat, link});
+		}
+
+		for (auto link : mValidator->getLinksForChild(mName))
+		{
+			auto parentCat = mDb.get(link->mParentCategory);
+			if (parentCat == nullptr)
+				continue;
+			mParentLinks.push_back({parentCat, link});
+		}
+	}
 }
 
-bool Category::hasColumn(const std::string &name) const
+bool Category::hasColumn(std::string_view name) const
 {
 	return getColumnIndex(name) < mColumns.size();
 }
 
-size_t Category::getColumnIndex(const std::string &name) const
+size_t Category::getColumnIndex(std::string_view name) const
 {
 	size_t result;
 
@@ -1388,11 +1396,11 @@ size_t Category::getColumnIndex(const std::string &name) const
 			break;
 	}
 
-	if (VERBOSE and result == mColumns.size() and mCatValidator != nullptr) // validate the name, if it is known at all (since it was not found)
+	if (VERBOSE > 0 and result == mColumns.size() and mCatValidator != nullptr) // validate the name, if it is known at all (since it was not found)
 	{
 		auto iv = mCatValidator->getValidatorForItem(name);
 		if (iv == nullptr)
-			std::cerr << "Invalid name used '" + name + "' is not a known column in " + mName << std::endl;
+			std::cerr << "Invalid name used '" << name << "' is not a known column in " + mName << std::endl;
 	}
 
 	return result;
@@ -1411,8 +1419,10 @@ std::vector<std::string> Category::getColumnNames() const
 	return result;
 }
 
-size_t Category::addColumn(const std::string &name)
+size_t Category::addColumn(std::string_view name)
 {
+	using namespace std::literals;
+
 	size_t result = getColumnIndex(name);
 
 	if (result == mColumns.size())
@@ -1423,30 +1433,15 @@ size_t Category::addColumn(const std::string &name)
 		{
 			itemValidator = mCatValidator->getValidatorForItem(name);
 			if (itemValidator == nullptr)
-				mValidator->reportError("tag " + name + " not allowed in Category " + mName, false);
+				mValidator->reportError("tag " + std::string(name) + " not allowed in Category " + mName, false);
 		}
 
-		mColumns.push_back({name, itemValidator});
+		mColumns.push_back(ItemColumn{std::string(name), itemValidator});
 	}
 
 	return result;
 }
 
-// RowSet Category::find(Condition&& cond)
-// {
-// 	RowSet result(*this);
-
-// 	cond.prepare(*this);
-
-// 	for (auto r: *this)
-// 	{
-// 		if (cond(*this, r))
-// 			result.push_back(r);
-// 	}
-
-// 	return result;
-// }
-
 void Category::reorderByIndex()
 {
 	if (mIndex != nullptr)
@@ -1490,11 +1485,13 @@ std::string Category::getUniqueID(std::function<std::string(int)> generator)
 	if (mCatValidator != nullptr and mCatValidator->mKeys.size() == 1)
 		key = mCatValidator->mKeys.front();
 
-	size_t nr = size();
+	// calling size() often is a waste of resources
+	if (mLastUniqueNr == 0)
+		mLastUniqueNr = size();
 
 	for (;;)
 	{
-		std::string result = generator(int(nr++));
+		std::string result = generator(static_cast<int>(mLastUniqueNr++));
 
 		if (exists(Key(key) == result))
 			continue;
@@ -1554,21 +1551,6 @@ Row Category::operator[](Condition &&cond)
 	return result;
 }
 
-// RowSet Category::find(Condition&& cond)
-// {
-// 	// return RowSet(*this, std::forward<Condition>(cond));
-// 	RowSet result(*this);
-
-// 	cond.prepare(*this);
-
-// 	for (auto r: *this)
-// 	{
-// 		if (cond(*this, r))
-// 			result.insert(result.end(), r);
-// 	}
-// 	return result;
-// }
-
 bool Category::exists(Condition &&cond) const
 {
 	bool result = false;
@@ -1814,12 +1796,8 @@ auto Category::erase(iterator pos) -> iterator
 
 	if (mValidator != nullptr)
 	{
-		for (auto &link : mValidator->getLinksForParent(mName))
+		for (auto &&[childCat, link] : mChildLinks)
 		{
-			auto childCat = mDb.get(link->mChildCategory);
-			if (childCat == nullptr)
-				continue;
-
 			Condition cond;
 
 			for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix)
@@ -1957,12 +1935,8 @@ bool Category::isOrphan(Row r)
 		return false;
 
 	bool isOrphan = true;
-	for (auto &link : mValidator->getLinksForChild(mName))
+	for (auto &&[parentCat, link] : mParentLinks)
 	{
-		auto parentCat = mDb.get(link->mParentCategory);
-		if (parentCat == nullptr)
-			continue;
-
 		Condition cond;
 		for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix)
 		{
@@ -1993,12 +1967,8 @@ bool Category::hasChildren(Row r) const
 
 	bool result = false;
 
-	for (auto &link : mValidator->getLinksForParent(mName))
+	for (auto &&[childCat, link] : mChildLinks)
 	{
-		auto childCat = mDb.get(link->mChildCategory);
-		if (childCat == nullptr)
-			continue;
-
 		Condition cond;
 
 		for (size_t ix = 0; ix < link->mParentKeys.size(); ++ix)
@@ -2024,12 +1994,8 @@ bool Category::hasParents(Row r) const
 
 	bool result = false;
 
-	for (auto &link : mValidator->getLinksForChild(mName))
+	for (auto &&[parentCat, link] : mParentLinks)
 	{
-		auto parentCat = mDb.get(link->mParentCategory);
-		if (parentCat == nullptr)
-			continue;
-
 		Condition cond;
 
 		for (size_t ix = 0; ix < link->mChildKeys.size(); ++ix)
@@ -2238,23 +2204,17 @@ bool Category::isValid()
 
 void Category::validateLinks() const
 {
-	auto &validator = getValidator();
-
-	for (auto linkValidator : validator.getLinksForChild(mName))
+	for (auto &&[parentCat, link] : mParentLinks)
 	{
-		auto parent = mDb.get(linkValidator->mParentCategory);
-		if (parent == nullptr)
-			continue;
-
 		size_t missing = 0;
 		for (auto r : *this)
-			if (not hasParent(r, *parent, *linkValidator))
+			if (not hasParent(r, *parentCat, *link))
 				++missing;
 
-		if (missing)
+		if (missing and VERBOSE >= 0)
 		{
-			std::cerr << "Links for " << linkValidator->mLinkGroupLabel << " are incomplete" << std::endl
-					  << "  There are " << missing << " items in " << mName << " that don't have matching parent items in " << parent->mName << std::endl;
+			std::cerr << "Links for " << link->mLinkGroupLabel << " are incomplete" << std::endl
+					  << "  There are " << missing << " items in " << mName << " that don't have matching parent items in " << parentCat->mName << std::endl;
 		}
 	}
 }
@@ -2319,24 +2279,26 @@ std::set<size_t> Category::keyFieldsByIndex() const
 
 bool operator==(const Category &a, const Category &b)
 {
-	using namespace std::placeholders; 
-	
-//	set<std::string> tagsA(a.fields()), tagsB(b.fields());
-//	
-//	if (tagsA != tagsB)
-//		std::cout << "Unequal number of fields" << std::endl;
+	using namespace std::placeholders;
+
+	bool result = true;
 
-	auto& validator = a.getValidator();
+	//	set<std::string> tagsA(a.fields()), tagsB(b.fields());
+	//
+	//	if (tagsA != tagsB)
+	//		std::cout << "Unequal number of fields" << std::endl;
+
+	auto &validator = a.getValidator();
 	auto catValidator = validator.getValidatorForCategory(a.name());
 	if (catValidator == nullptr)
 		throw std::runtime_error("missing cat validator");
-	
-	typedef std::function<int(const char*,const char*)> compType;
-	std::vector<std::tuple<std::string,compType>> tags;
+
+	typedef std::function<int(const char *, const char *)> compType;
+	std::vector<std::tuple<std::string, compType>> tags;
 	auto keys = catValidator->mKeys;
 	std::vector<size_t> keyIx;
-	
-	for (auto& tag: a.fields())
+
+	for (auto &tag : a.fields())
 	{
 		auto iv = catValidator->getValidatorForItem(tag);
 		if (iv == nullptr)
@@ -2345,32 +2307,37 @@ bool operator==(const Category &a, const Category &b)
 		if (tv == nullptr)
 			throw std::runtime_error("missing type validator");
 		tags.push_back(std::make_tuple(tag, std::bind(&cif::ValidateType::compare, tv, std::placeholders::_1, std::placeholders::_2)));
-		
-		auto pred = [tag](const std::string& s) -> bool { return cif::iequals(tag, s) == 0; };
+
+		auto pred = [tag](const std::string &s) -> bool
+		{ return cif::iequals(tag, s) == 0; };
 		if (find_if(keys.begin(), keys.end(), pred) == keys.end())
 			keyIx.push_back(tags.size() - 1);
 	}
-	
+
 	// a.reorderByIndex();
 	// b.reorderByIndex();
-	
-	auto rowEqual = [&](const cif::Row& a, const cif::Row& b)
+
+	auto rowEqual = [&](const cif::Row &ra, const cif::Row &rb)
 	{
 		int d = 0;
 
-		for (auto kix: keyIx)
+		for (auto kix : keyIx)
 		{
 			std::string tag;
 			compType compare;
-			
+
 			std::tie(tag, compare) = tags[kix];
 
-			d = compare(a[tag].c_str(), b[tag].c_str());
+			d = compare(ra[tag].c_str(), rb[tag].c_str());
 
 			if (d != 0)
+			{
+				if (cif::VERBOSE > 1)
+					std::cerr << "Values in _" << a.name() << '.' << tag << " are not equal: '" << ra[tag].c_str() << "' != '" << rb[tag].c_str() << '\'' << std::endl;
 				break;
+			}
 		}
-		
+
 		return d == 0;
 	};
 
@@ -2378,50 +2345,64 @@ bool operator==(const Category &a, const Category &b)
 	while (ai != a.end() or bi != b.end())
 	{
 		if (ai == a.end() or bi == b.end())
-			return false;
-		
+		{
+			if (cif::VERBOSE > 1)
+			{
+				std::cerr << "Unequal number of rows in " << a.name() << std::endl;
+				result = false;
+				break;
+			}
+			else
+				return false;
+		}
+
 		cif::Row ra = *ai, rb = *bi;
-		
+
 		if (not rowEqual(ra, rb))
-			return false;
-		
+		{
+			if (cif::VERBOSE > 1)
+				result = false;
+			else
+				return false;
+		}
+
 		std::vector<std::string> missingA, missingB, different;
-		
-		for (auto& tt: tags)
+
+		for (auto &tt : tags)
 		{
 			std::string tag;
 			compType compare;
-			
+
 			std::tie(tag, compare) = tt;
-			
+
 			// make it an option to compare unapplicable to empty or something
-			
-			const char* ta = ra[tag].c_str();	if (strcmp(ta, ".") == 0 or strcmp(ta, "?") == 0) ta = "";
-			const char* tb = rb[tag].c_str();	if (strcmp(tb, ".") == 0 or strcmp(tb, "?") == 0) tb = "";
-			
+
+			const char *ta = ra[tag].c_str();
+			if (strcmp(ta, ".") == 0 or strcmp(ta, "?") == 0)
+				ta = "";
+			const char *tb = rb[tag].c_str();
+			if (strcmp(tb, ".") == 0 or strcmp(tb, "?") == 0)
+				tb = "";
+
 			if (compare(ta, tb) != 0)
-				return false;
+			{
+				if (cif::VERBOSE > 1)
+				{
+					std::cerr << "Values in _" << a.name() << '.' << tag << " are not equal: '" << ta << "' != '" << tb << '\'' << std::endl;
+					result = false;
+				}
+				else
+					return false;
+			}
 		}
-		
+
 		++ai;
 		++bi;
 	}
 
-	return true;
+	return result;
 }
 
-// auto Category::iterator::operator++() -> iterator&
-// {
-// 	mCurrent = Row(mCurrent.data()->mNext);
-// 	return *this;
-// }
-
-// auto Category::const_iterator::operator++() -> const_iterator&
-// {
-// 	mCurrent = Row(mCurrent.data()->mNext);
-// 	return *this;
-// }
-
 namespace detail
 {
 
@@ -2695,17 +2676,10 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
 		row.assign(colIx, value, true);
 
 	// see if we need to update any child categories that depend on this value
-	auto &validator = getValidator();
-	auto &db = mDb;
-
 	for (auto parent : rows)
 	{
-		for (auto linked : validator.getLinksForParent(mName))
+		for (auto &&[childCat, linked] : mChildLinks)
 		{
-			auto childCat = db.get(linked->mChildCategory);
-			if (childCat == nullptr)
-				continue;
-
 			if (std::find(linked->mParentKeys.begin(), linked->mParentKeys.end(), tag) == linked->mParentKeys.end())
 				continue;
 
@@ -2790,7 +2764,7 @@ void Category::update_value(RowSet &&rows, const std::string &tag, const std::st
 				}
 
 				// cannot update this...
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Cannot update child " << childCat->mName << "." << childTag << " with value " << value << std::endl;
 			}
 
@@ -2862,18 +2836,8 @@ void Row::assign(const std::vector<Item> &values)
 	// auto iv = col.mValidator;
 	if (mCascade)
 	{
-		auto &validator = cat->getValidator();
-		auto &db = cat->db();
-
-		for (auto linked : validator.getLinksForParent(cat->mName))
+		for (auto &&[childCat, linked] : cat->mChildLinks)
 		{
-			auto childCat = db.get(linked->mChildCategory);
-			if (childCat == nullptr)
-				continue;
-
-			// if (find(linked->mParentKeys.begin(), linked->mParentKeys.end(), iv->mTag) == linked->mParentKeys.end())
-			// 	continue;
-
 			Condition cond;
 			std::string childTag;
 
@@ -2909,21 +2873,22 @@ void Row::assign(const Item &value, bool skipUpdateLinked)
 	assign(value.name(), value.value(), skipUpdateLinked);
 }
 
-void Row::assign(const std::string &name, const std::string &value, bool skipUpdateLinked)
+void Row::assign(std::string_view name, const std::string &value, bool skipUpdateLinked, bool validate)
 {
 	try
 	{
 		auto cat = mData->mCategory;
-		assign(cat->addColumn(name), value, skipUpdateLinked);
+		assign(cat->addColumn(name), value, skipUpdateLinked, validate);
 	}
 	catch (const std::exception &ex)
 	{
-		std::cerr << "Could not assign value '" << value << "' to column _" << mData->mCategory->name() << '.' << name << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Could not assign value '" << value << "' to column _" << mData->mCategory->name() << '.' << name << std::endl;
 		throw;
 	}
 }
 
-void Row::assign(size_t column, const std::string &value, bool skipUpdateLinked)
+void Row::assign(size_t column, const std::string &value, bool skipUpdateLinked, bool validate)
 {
 	if (mData == nullptr)
 		throw std::logic_error("invalid Row, no data assigning value '" + value + "' to column with index " + std::to_string(column));
@@ -2949,7 +2914,7 @@ void Row::assign(size_t column, const std::string &value, bool skipUpdateLinked)
 	std::string oldStrValue = oldValue ? oldValue : "";
 
 	// check the value
-	if (col.mValidator)
+	if (col.mValidator and validate)
 		(*col.mValidator)(value);
 
 	// If the field is part of the Key for this Category, remove it from the index
@@ -3014,15 +2979,8 @@ void Row::assign(size_t column, const std::string &value, bool skipUpdateLinked)
 	auto iv = col.mValidator;
 	if (not skipUpdateLinked and iv != nullptr and mCascade)
 	{
-		auto &validator = cat->getValidator();
-		auto &db = cat->db();
-
-		for (auto linked : validator.getLinksForParent(cat->mName))
+		for (auto &&[childCat, linked] : cat->mChildLinks)
 		{
-			auto childCat = db.get(linked->mChildCategory);
-			if (childCat == nullptr)
-				continue;
-
 			if (find(linked->mParentKeys.begin(), linked->mParentKeys.end(), iv->mTag) == linked->mParentKeys.end())
 				continue;
 
@@ -3088,7 +3046,7 @@ void Row::assign(size_t column, const std::string &value, bool skipUpdateLinked)
 			auto rows_n = childCat->find(std::move(cond_n));
 			if (not rows_n.empty())
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Will not rename in child category since there are already rows that link to the parent" << std::endl;
 
 				continue;
@@ -3201,18 +3159,13 @@ void Row::swap(size_t cix, ItemRow *a, ItemRow *b)
 		auto parentColName = cat->getColumnName(cix);
 
 		// see if we need to update any child categories that depend on these values
-		auto &validator = cat->getValidator();
 		auto parentCatValidator = cat->getCatValidator();
 
-		for (auto &link : validator.getLinksForParent(cat->mName))
+		for (auto &&[childCat, link] : cat->mChildLinks)
 		{
 			if (find(link->mParentKeys.begin(), link->mParentKeys.end(), parentColName) == link->mParentKeys.end())
 				continue;
 
-			auto childCat = cat->db().get(link->mChildCategory);
-			if (childCat == nullptr or childCat->empty())
-				continue;
-
 			auto childCatValidator = childCat->getCatValidator();
 			if (childCatValidator == nullptr)
 				continue;
@@ -3299,7 +3252,7 @@ void Row::swap(size_t cix, ItemRow *a, ItemRow *b)
 					}
 					else
 					{
-						if (VERBOSE)
+						if (VERBOSE > 0)
 							std::cerr << "In " << childCat->mName << " changing " << linkChildColName << ": " << r[linkChildColName].as<std::string>() << " => " << (i ? i->mText : "") << std::endl;
 						r[linkChildColName] = i ? i->mText : "";
 					}
@@ -3309,7 +3262,7 @@ void Row::swap(size_t cix, ItemRow *a, ItemRow *b)
 	}
 }
 
-size_t Row::ColumnForItemTag(const char *itemTag) const
+size_t Row::ColumnForItemTag(std::string_view itemTag) const
 {
 	size_t result = 0;
 	if (mData != nullptr)
@@ -3408,7 +3361,8 @@ File::File(const std::filesystem::path &path, bool validate)
 	}
 	catch (const std::exception &ex)
 	{
-		std::cerr << "Error while loading file " << path << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Error while loading file " << path << std::endl;
 		throw;
 	}
 }
@@ -3424,7 +3378,6 @@ File::File(File &&rhs)
 File::~File()
 {
 	delete mHead;
-	delete mValidator;
 }
 
 void File::append(Datablock *e)
@@ -3477,7 +3430,8 @@ void File::load(const std::filesystem::path &p)
 	}
 	catch (const std::exception &ex)
 	{
-		std::cerr << "Error loading file " << path << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Error loading file " << path << std::endl;
 		throw;
 	}
 }
@@ -3501,7 +3455,7 @@ void File::save(const std::filesystem::path &p)
 
 void File::load(std::istream &is)
 {
-	Validator *saved = mValidator;
+	auto saved = mValidator;
 	setValidator(nullptr);
 
 	Parser p(is, *this);
@@ -3516,7 +3470,7 @@ void File::load(std::istream &is)
 
 void File::load(std::istream &is, const std::string &datablock)
 {
-	Validator *saved = mValidator;
+	auto saved = mValidator;
 	setValidator(nullptr);
 
 	Parser p(is, *this);
@@ -3549,7 +3503,7 @@ void File::write(std::ostream &os, const std::vector<std::string> &order)
 	}
 }
 
-Datablock *File::get(const std::string &name) const
+Datablock *File::get(std::string_view name) const
 {
 	const Datablock *result = mHead;
 	while (result != nullptr and not iequals(result->mName, name))
@@ -3557,13 +3511,15 @@ Datablock *File::get(const std::string &name) const
 	return const_cast<Datablock *>(result);
 }
 
-Datablock &File::operator[](const std::string &name)
+Datablock &File::operator[](std::string_view name)
 {
+	using namespace std::literals;
+
 	Datablock *result = mHead;
 	while (result != nullptr and not iequals(result->mName, name))
 		result = result->mNext;
 	if (result == nullptr)
-		throw std::runtime_error("Datablock " + name + " does not exist");
+		throw std::runtime_error("Datablock " + std::string(name) + " does not exist");
 	return *result;
 }
 
@@ -3571,7 +3527,7 @@ bool File::isValid()
 {
 	if (mValidator == nullptr)
 	{
-		if (VERBOSE)
+		if (VERBOSE > 0)
 			std::cerr << "No dictionary loaded explicitly, loading default" << std::endl;
 
 		loadDictionary();
@@ -3603,67 +3559,10 @@ void File::loadDictionary()
 
 void File::loadDictionary(const char *dict)
 {
-	fs::path dict_name(dict);
-
-	auto data = loadResource(dict);
-
-	if (not data and dict_name.extension().string() != ".dic")
-		data = loadResource(dict_name.parent_path() / (dict_name.filename().string() + ".dic"));
-
-	if (data)
-		loadDictionary(*data);
-	else
-	{
-		// might be a compressed dictionary on disk
-		fs::path p = dict;
-		if (p.extension() == ".dic")
-			p = p.parent_path() / (p.filename().string() + ".gz");
-		else
-			p = p.parent_path() / (p.filename().string() + ".dic.gz");
-
-#if defined(CACHE_DIR) and defined(DATA_DIR)
-		if (not fs::exists(p))
-		{
-			for (const char *dir : {CACHE_DIR, DATA_DIR})
-			{
-				auto p2 = fs::path(dir) / p;
-				if (fs::exists(p2))
-				{
-					swap(p, p2);
-					break;
-				}
-			}
-		}
-#endif
-
-		if (fs::exists(p))
-		{
-			std::ifstream file(p, std::ios::binary);
-			if (not file.is_open())
-				throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
-
-			io::filtering_stream<io::input> in;
-			in.push(io::gzip_decompressor());
-			in.push(file);
-
-			loadDictionary(in);
-		}
-		else
-			throw std::runtime_error("Dictionary not found or defined (" + dict_name.string() + ")");
-	}
-}
-
-void File::loadDictionary(std::istream &is)
-{
-	std::unique_ptr<Validator> v(new Validator());
-
-	DictParser p(*v, is);
-	p.loadDictionary();
-
-	setValidator(v.release());
+	setValidator(&ValidatorFactory::instance()[dict]);
 }
 
-void File::setValidator(Validator *v)
+void File::setValidator(const Validator *v)
 {
 	mValidator = v;
 
diff --git a/src/Cif2PDB.cpp b/src/Cif2PDB.cpp
index 1ffb7d4..017d8cf 100644
--- a/src/Cif2PDB.cpp
+++ b/src/Cif2PDB.cpp
@@ -249,7 +249,7 @@ size_t WriteContinuedLine(std::ostream& pdbFile, std::string header, int& count,
 
 	for (auto& line: lines)
 	{
-		ba::to_upper(line);
+		// ba::to_upper(line);
 
 		pdbFile << header;
 		
@@ -753,7 +753,8 @@ class Ff : public FBase
             }
             catch (const std::exception& ex)
             {
-                std::cerr << "Failed to write '" << s << "' as a double, this indicates an error in the code for writing PDB files" << std::endl;
+				if (cif::VERBOSE >= 0)
+	                std::cerr << "Failed to write '" << s << "' as a double, this indicates an error in the code for writing PDB files" << std::endl;
                 os << s;
             }
         }
@@ -811,7 +812,7 @@ typedef RM<3> RM3;
 template<int N>
 std::ostream& operator<<(std::ostream& os, RM<N>&& rm)
 {
-	os << "REMARK " << std::setw(3) << std::right << N << " " << rm.mDesc << (rm.mWidth > 0 ? std::left : std::right) << std::fixed << std::setw(abs(rm.mWidth)) << std::setprecision(rm.mPrecision);
+	os << "REMARK " << std::setw(3) << std::right << N << " " << rm.mDesc << (rm.mWidth > 0 ? std::left : std::right) << std::fixed << std::setw(std::abs(rm.mWidth)) << std::setprecision(rm.mPrecision);
 	return os;
 }
 
@@ -824,7 +825,7 @@ struct SEP
 
 std::ostream& operator<<(std::ostream& os, SEP&& sep)
 {
-	os << sep.mText << (sep.mWidth > 0 ? std::left : std::right) << std::fixed << std::setw(abs(sep.mWidth)) << std::setprecision(sep.mPrecision);
+	os << sep.mText << (sep.mWidth > 0 ? std::left : std::right) << std::fixed << std::setw(std::abs(sep.mWidth)) << std::setprecision(sep.mPrecision);
 	return os;
 }
 
@@ -2329,7 +2330,8 @@ void WriteRemark200(std::ostream& pdbFile, Datablock& db)
 	}
 	catch (const std::exception& ex)
 	{
-		std::cerr << ex.what() << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << ex.what() << std::endl;
 	}
 }
 
@@ -2390,7 +2392,8 @@ void WriteRemark280(std::ostream& pdbFile, Datablock& db)
 	}
 	catch (const std::exception& ex)
 	{
-		std::cerr << ex.what() << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << ex.what() << std::endl;
 	}
 }
 
diff --git a/src/CifParser.cpp b/src/CifParser.cpp
index feac840..0df994f 100644
--- a/src/CifParser.cpp
+++ b/src/CifParser.cpp
@@ -42,25 +42,25 @@ namespace cif
 const uint32_t kMaxLineLength = 132;
 
 const uint8_t kCharTraitsTable[128] = {
-	//	0	1	2	3	4	5	6	7	8	9	a	b	c	d	e	f
-		14,	15,	14,	14,	14,	15,	15,	14,	15,	15,	15,	15,	15,	15,	15,	15,	//	2
-		15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	10,	15,	15,	15,	15,	//	3
-		15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	//	4
-		15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	14,	15,	14,	15,	14,	//	5
-		15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	//	6
-		15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	15,	0,	//	7
+//	0	1	2	3	4	5	6	7	8	9	a	b	c	d	e	f
+	14, 15, 14, 14, 14, 15, 15, 14, 15, 15, 15, 15, 15, 15, 15, 15, //	2
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 10, 15, 15, 15, 15, //	3
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, //	4
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 14, 15, 14, //	5
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, //	6
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0,  //	7
 };
 
 // --------------------------------------------------------------------
 
-CifParserError::CifParserError(uint32_t lineNr, const std::string& message)
+CifParserError::CifParserError(uint32_t lineNr, const std::string &message)
 	: std::runtime_error("parse error at line " + std::to_string(lineNr) + ": " + message)
 {
 }
 
 // --------------------------------------------------------------------
 
-const char* SacParser::kTokenName[] = {
+const char *SacParser::kTokenName[] = {
 	"unknown",
 	"EOF",
 	"DATA",
@@ -69,22 +69,20 @@ const char* SacParser::kTokenName[] = {
 	"SAVE",
 	"STOP",
 	"Tag",
-	"Value"
-};
+	"Value"};
 
-const char* SacParser::kValueName[] = {
+const char *SacParser::kValueName[] = {
 	"Int",
 	"Float",
 	"Numeric",
 	"String",
 	"TextField",
 	"Inapplicable",
-	"Unknown"
-};
+	"Unknown"};
 
 // --------------------------------------------------------------------
 
-SacParser::SacParser(std::istream& is, bool init)
+SacParser::SacParser(std::istream &is, bool init)
 	: mData(is)
 {
 	mValidate = true;
@@ -95,7 +93,7 @@ SacParser::SacParser(std::istream& is, bool init)
 		mLookahead = getNextToken();
 }
 
-void SacParser::error(const std::string& msg)
+void SacParser::error(const std::string &msg)
 {
 	throw CifParserError(mLineNr, msg);
 }
@@ -114,7 +112,7 @@ int SacParser::getNextChar()
 		result = mBuffer.top();
 		mBuffer.pop();
 	}
-	
+
 	// very simple CR/LF translation into LF
 	if (result == '\r')
 	{
@@ -123,12 +121,12 @@ int SacParser::getNextChar()
 			mBuffer.push(lookahead);
 		result = '\n';
 	}
-	
+
 	mTokenValue += static_cast<char>(result);
-	
+
 	if (result == '\n')
 		++mLineNr;
-	
+
 	if (VERBOSE >= 6)
 	{
 		std::cerr << "getNextChar => ";
@@ -137,7 +135,7 @@ int SacParser::getNextChar()
 		else
 			std::cerr << char(result) << std::endl;
 	}
-	
+
 	return result;
 }
 
@@ -148,7 +146,7 @@ void SacParser::retract()
 	char ch = mTokenValue.back();
 	if (ch == '\n')
 		--mLineNr;
-	
+
 	mBuffer.push(ch);
 	mTokenValue.pop_back();
 }
@@ -157,25 +155,25 @@ void SacParser::restart()
 {
 	while (not mTokenValue.empty())
 		retract();
-	
+
 	switch (mStart)
 	{
 		case eStateStart:
 			mState = mStart = eStateFloat;
 			break;
-		
+
 		case eStateFloat:
 			mState = mStart = eStateInt;
 			break;
-		
+
 		case eStateInt:
 			mState = mStart = eStateValue;
 			break;
-		
+
 		default:
 			error("Invalid state in SacParser");
 	}
-	
+
 	mBol = false;
 }
 
@@ -183,26 +181,26 @@ void SacParser::match(SacParser::CIFToken t)
 {
 	if (mLookahead != t)
 		error(std::string("Unexpected token, expected ") + kTokenName[t] + " but found " + kTokenName[mLookahead]);
-	
+
 	mLookahead = getNextToken();
 }
 
 SacParser::CIFToken SacParser::getNextToken()
 {
 	const auto kEOF = std::char_traits<char>::eof();
-	
+
 	CIFToken result = eCIFTokenUnknown;
 	int quoteChar = 0;
 	mState = mStart = eStateStart;
 	mBol = false;
-	
+
 	mTokenValue.clear();
 	mTokenType = eCIFValueUnknown;
-	
+
 	while (result == eCIFTokenUnknown)
 	{
 		auto ch = getNextChar();
-		
+
 		switch (mState)
 		{
 			case eStateStart:
@@ -233,7 +231,7 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
+
 			case eStateWhite:
 				if (ch == kEOF)
 					result = eCIFTokenEOF;
@@ -246,7 +244,7 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					mBol = (ch == '\n');
 				break;
-			
+
 			case eStateComment:
 				if (ch == '\n')
 				{
@@ -259,7 +257,7 @@ SacParser::CIFToken SacParser::getNextToken()
 				else if (not isAnyPrint(ch))
 					error("invalid character in comment");
 				break;
-			
+
 			case eStateQuestionMark:
 				if (isNonBlank(ch))
 					mState = eStateValue;
@@ -290,11 +288,11 @@ SacParser::CIFToken SacParser::getNextToken()
 					mState = eStateTextField + 1;
 				else if (ch == kEOF)
 					error("unterminated textfield");
-				else if (not isAnyPrint(ch))
-//					error("invalid character in text field '" + string({ static_cast<char>(ch) }) + "' (" + to_string((int)ch) + ")");
-					std::cerr << "invalid character in text field '" << std::string({ static_cast<char>(ch) }) << "' (" << ch << ") line: " << mLineNr << std::endl;
+				else if (not isAnyPrint(ch) and cif::VERBOSE >= 0)
+					//					error("invalid character in text field '" + string({ static_cast<char>(ch) }) + "' (" + to_string((int)ch) + ")");
+					std::cerr << "invalid character in text field '" << std::string({static_cast<char>(ch)}) << "' (" << ch << ") line: " << mLineNr << std::endl;
 				break;
-			
+
 			case eStateTextField + 1:
 				if (isTextLead(ch) or ch == ' ' or ch == '\t')
 					mState = eStateTextField;
@@ -310,7 +308,7 @@ SacParser::CIFToken SacParser::getNextToken()
 				else if (ch != '\n')
 					error("invalid character in text field");
 				break;
-			
+
 			case eStateQuotedString:
 				if (ch == kEOF)
 					error("unterminated quoted string");
@@ -319,14 +317,14 @@ SacParser::CIFToken SacParser::getNextToken()
 				else if (not isAnyPrint(ch))
 					error("invalid character in quoted string");
 				break;
-			
+
 			case eStateQuotedStringQuote:
 				if (isWhite(ch))
 				{
 					retract();
 					result = eCIFTokenValue;
 					mTokenType = eCIFValueString;
-					
+
 					assert(mTokenValue.length() >= 3);
 					mTokenValue = mTokenValue.substr(1, mTokenValue.length() - 2);
 				}
@@ -339,7 +337,7 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					error("invalid character in quoted string");
 				break;
-			
+
 			case eStateTag:
 				if (not isNonBlank(ch))
 				{
@@ -347,7 +345,7 @@ SacParser::CIFToken SacParser::getNextToken()
 					result = eCIFTokenTag;
 				}
 				break;
-			
+
 			case eStateFloat:
 				if (ch == '+' or ch == '-')
 				{
@@ -358,11 +356,11 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
+
 			case eStateFloat + 1:
-//				if (ch == '(')	// numeric???
-//					mState = eStateNumericSuffix;
-//				else
+				//				if (ch == '(')	// numeric???
+				//					mState = eStateNumericSuffix;
+				//				else
 				if (ch == '.')
 					mState = eStateFloat + 2;
 				else if (tolower(ch) == 'e')
@@ -376,12 +374,12 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
+
 			// parsed '.'
 			case eStateFloat + 2:
-//				if (ch == '(')	// numeric???
-//					mState = eStateNumericSuffix;
-//				else
+				//				if (ch == '(')	// numeric???
+				//					mState = eStateNumericSuffix;
+				//				else
 				if (tolower(ch) == 'e')
 					mState = eStateFloat + 3;
 				else if (isWhite(ch) or ch == kEOF)
@@ -393,7 +391,7 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
+
 			// parsed 'e'
 			case eStateFloat + 3:
 				if (ch == '-' or ch == '+')
@@ -410,11 +408,11 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
+
 			case eStateFloat + 5:
-//				if (ch == '(')
-//					mState = eStateNumericSuffix;
-//				else
+				//				if (ch == '(')
+				//					mState = eStateNumericSuffix;
+				//				else
 				if (isWhite(ch) or ch == kEOF)
 				{
 					retract();
@@ -424,14 +422,14 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
+
 			case eStateInt:
 				if (isdigit(ch) or ch == '+' or ch == '-')
 					mState = eStateInt + 1;
 				else
 					restart();
 				break;
-			
+
 			case eStateInt + 1:
 				if (isWhite(ch) or ch == kEOF)
 				{
@@ -442,36 +440,36 @@ SacParser::CIFToken SacParser::getNextToken()
 				else
 					restart();
 				break;
-			
-//			case eStateNumericSuffix:
-//				if (isdigit(ch))
-//					mState = eStateNumericSuffix + 1;
-//				else
-//					restart();
-//				break;
-//			
-//			case eStateNumericSuffix + 1:
-//				if (ch == ')')
-//				{
-//					result = eCIFTokenValue;
-//					mTokenType = eCIFValueNumeric;
-//				}
-//				else if (not isdigit(ch))
-//					restart();
-//				break;
-			
+
+				//			case eStateNumericSuffix:
+				//				if (isdigit(ch))
+				//					mState = eStateNumericSuffix + 1;
+				//				else
+				//					restart();
+				//				break;
+				//
+				//			case eStateNumericSuffix + 1:
+				//				if (ch == ')')
+				//				{
+				//					result = eCIFTokenValue;
+				//					mTokenType = eCIFValueNumeric;
+				//				}
+				//				else if (not isdigit(ch))
+				//					restart();
+				//				break;
+
 			case eStateValue:
 				if (isNonBlank(ch))
 					mState = eStateValue + 1;
 				else
 					error("invalid character at this position");
 				break;
-			
+
 			case eStateValue + 1:
-				if (ch == '_')		// first _, check for keywords
+				if (ch == '_') // first _, check for keywords
 				{
 					std::string s = toLowerCopy(mTokenValue);
-					
+
 					if (s == "global_")
 						result = eCIFTokenGLOBAL;
 					else if (s == "stop_")
@@ -493,16 +491,16 @@ SacParser::CIFToken SacParser::getNextToken()
 				if (not isNonBlank(ch))
 				{
 					retract();
-					
+
 					if (tolower(mTokenValue[0]) == 'd')
 						result = eCIFTokenDATA;
 					else
 						result = eCIFTokenSAVE;
-					
-					mTokenValue.erase(mTokenValue.begin(), mTokenValue.begin() + 5); 
+
+					mTokenValue.erase(mTokenValue.begin(), mTokenValue.begin() + 5);
 				}
 				break;
-			
+
 			default:
 				assert(false);
 				error("Invalid state in getNextToken");
@@ -519,7 +517,7 @@ SacParser::CIFToken SacParser::getNextToken()
 			std::cerr << " '" << mTokenValue << '\'';
 		std::cerr << std::endl;
 	}
-	
+
 	return result;
 }
 
@@ -530,8 +528,15 @@ DatablockIndex SacParser::indexDatablocks()
 	// first locate the start, as fast as we can
 	auto &sb = *mData.rdbuf();
 
-	enum {
-		start, comment, string, string_quote, qstring, data, data_name
+	enum
+	{
+		start,
+		comment,
+		string,
+		string_quote,
+		qstring,
+		data,
+		data_name
 	} state = start;
 
 	int quote = 0;
@@ -547,7 +552,7 @@ DatablockIndex SacParser::indexDatablocks()
 			case start:
 				switch (ch)
 				{
-					case '#':	state = comment;	break;
+					case '#': state = comment; break;
 					case 'd':
 					case 'D':
 						state = data;
@@ -564,7 +569,7 @@ DatablockIndex SacParser::indexDatablocks()
 						break;
 				}
 				break;
-			
+
 			case comment:
 				if (ch == '\n')
 					state = start;
@@ -574,29 +579,29 @@ DatablockIndex SacParser::indexDatablocks()
 				if (ch == quote)
 					state = string_quote;
 				break;
-			
+
 			case string_quote:
 				if (std::isspace(ch))
 					state = start;
 				else
 					state = string;
 				break;
-			
+
 			case qstring:
 				if (ch == ';' and bol)
 					state = start;
 				break;
-			
+
 			case data:
 				if (dblk[si] == 0 and isNonBlank(ch))
 				{
-					datablock = { static_cast<char>(ch) };
+					datablock = {static_cast<char>(ch)};
 					state = data_name;
 				}
 				else if (dblk[si++] != ch)
 					state = start;
 				break;
-			
+
 			case data_name:
 				if (isNonBlank(ch))
 					datablock.insert(datablock.end(), char(ch));
@@ -604,7 +609,7 @@ DatablockIndex SacParser::indexDatablocks()
 				{
 					if (not datablock.empty())
 						index[datablock] = mData.tellg();
-					
+
 					state = start;
 				}
 				else
@@ -618,13 +623,19 @@ DatablockIndex SacParser::indexDatablocks()
 	return index;
 }
 
-bool SacParser::parseSingleDatablock(const std::string& datablock)
+bool SacParser::parseSingleDatablock(const std::string &datablock)
 {
 	// first locate the start, as fast as we can
 	auto &sb = *mData.rdbuf();
 
-	enum {
-		start, comment, string, string_quote, qstring, data
+	enum
+	{
+		start,
+		comment,
+		string,
+		string_quote,
+		qstring,
+		data
 	} state = start;
 
 	int quote = 0;
@@ -640,7 +651,7 @@ bool SacParser::parseSingleDatablock(const std::string& datablock)
 			case start:
 				switch (ch)
 				{
-					case '#':	state = comment;	break;
+					case '#': state = comment; break;
 					case 'd':
 					case 'D':
 						state = data;
@@ -657,7 +668,7 @@ bool SacParser::parseSingleDatablock(const std::string& datablock)
 						break;
 				}
 				break;
-			
+
 			case comment:
 				if (ch == '\n')
 					state = start;
@@ -667,19 +678,19 @@ bool SacParser::parseSingleDatablock(const std::string& datablock)
 				if (ch == quote)
 					state = string_quote;
 				break;
-			
+
 			case string_quote:
 				if (std::isspace(ch))
 					state = start;
 				else
 					state = string;
 				break;
-			
+
 			case qstring:
 				if (ch == ';' and bol)
 					state = start;
 				break;
-			
+
 			case data:
 				if (isspace(ch) and dblk[si] == 0)
 					found = true;
@@ -701,7 +712,7 @@ bool SacParser::parseSingleDatablock(const std::string& datablock)
 	return found;
 }
 
-bool SacParser::parseSingleDatablock(const std::string& datablock, const DatablockIndex &index)
+bool SacParser::parseSingleDatablock(const std::string &datablock, const DatablockIndex &index)
 {
 	bool result = false;
 
@@ -729,14 +740,14 @@ void SacParser::parseFile()
 			case eCIFTokenGLOBAL:
 				parseGlobal();
 				break;
-			
+
 			case eCIFTokenDATA:
 				produceDatablock(mTokenValue);
 
 				match(eCIFTokenDATA);
 				parseDataBlock();
 				break;
-			
+
 			default:
 				error("This file does not seem to be an mmCIF file");
 				break;
@@ -757,24 +768,24 @@ void SacParser::parseGlobal()
 void SacParser::parseDataBlock()
 {
 	std::string cat;
-	
+
 	while (mLookahead == eCIFTokenLOOP or mLookahead == eCIFTokenTag or mLookahead == eCIFTokenSAVE)
 	{
 		switch (mLookahead)
 		{
 			case eCIFTokenLOOP:
 			{
-				cat.clear();	// should start a new category
-				
+				cat.clear(); // should start a new category
+
 				match(eCIFTokenLOOP);
-				
+
 				std::vector<std::string> tags;
-				
+
 				while (mLookahead == eCIFTokenTag)
 				{
 					std::string catName, itemName;
 					std::tie(catName, itemName) = splitTagName(mTokenValue);
-					
+
 					if (cat.empty())
 					{
 						produceCategory(catName);
@@ -782,27 +793,27 @@ void SacParser::parseDataBlock()
 					}
 					else if (not iequals(cat, catName))
 						error("inconsistent categories in loop_");
-					
+
 					tags.push_back(itemName);
 
 					match(eCIFTokenTag);
 				}
-				
+
 				while (mLookahead == eCIFTokenValue)
 				{
 					produceRow();
-					
-					for (auto tag: tags)
+
+					for (auto tag : tags)
 					{
 						produceItem(cat, tag, mTokenValue);
 						match(eCIFTokenValue);
 					}
 				}
-				
+
 				cat.clear();
 				break;
 			}
-		
+
 			case eCIFTokenTag:
 			{
 				std::string catName, itemName;
@@ -816,17 +827,17 @@ void SacParser::parseDataBlock()
 				}
 
 				match(eCIFTokenTag);
-				
+
 				produceItem(cat, itemName, mTokenValue);
 
 				match(eCIFTokenValue);
 				break;
 			}
-			
+
 			case eCIFTokenSAVE:
 				parseSaveFrame();
 				break;
-			
+
 			default:
 				assert(false);
 				break;
@@ -841,18 +852,20 @@ void SacParser::parseSaveFrame()
 
 // --------------------------------------------------------------------
 
-Parser::Parser(std::istream& is, File& f, bool init)
-	: SacParser(is, init), mFile(f), mDataBlock(nullptr)
+Parser::Parser(std::istream &is, File &f, bool init)
+	: SacParser(is, init)
+	, mFile(f)
+	, mDataBlock(nullptr)
 {
 }
 
-void Parser::produceDatablock(const std::string& name)
+void Parser::produceDatablock(const std::string &name)
 {
 	mDataBlock = new Datablock(name);
 	mFile.append(mDataBlock);
 }
 
-void Parser::produceCategory(const std::string& name)
+void Parser::produceCategory(const std::string &name)
 {
 	if (VERBOSE >= 4)
 		std::cerr << "producing category " << name << std::endl;
@@ -870,7 +883,7 @@ void Parser::produceRow()
 	mRow.lineNr(mLineNr);
 }
 
-void Parser::produceItem(const std::string& category, const std::string& item, const std::string& value)
+void Parser::produceItem(const std::string &category, const std::string &item, const std::string &value)
 {
 	if (VERBOSE >= 4)
 		std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
@@ -886,13 +899,15 @@ void Parser::produceItem(const std::string& category, const std::string& item, c
 struct DictParserDataImpl
 {
 	// temporary values for constructing dictionaries
-	std::vector<ValidateCategory>					mCategoryValidators;
-	std::map<std::string,std::vector<ValidateItem>>	mItemValidators;
-	std::set<std::tuple<std::string,std::string>>	mLinkedItems;
+	std::vector<ValidateCategory> mCategoryValidators;
+	std::map<std::string, std::vector<ValidateItem>> mItemValidators;
+	std::set<std::tuple<std::string, std::string>> mLinkedItems;
 };
 
-DictParser::DictParser(Validator& validator, std::istream& is)
-	: Parser(is, mFile), mValidator(validator), mImpl(new DictParserDataImpl)
+DictParser::DictParser(Validator &validator, std::istream &is)
+	: Parser(is, mFile)
+	, mValidator(validator)
+	, mImpl(new DictParserDataImpl)
 {
 }
 
@@ -910,9 +925,9 @@ void DictParser::parseSaveFrame()
 
 	if (saveFrameName.empty())
 		error("Invalid save frame, should contain more than just 'save_' here");
-	
+
 	bool isCategorySaveFrame = mTokenValue[0] != '_';
-	
+
 	Datablock dict(mTokenValue);
 	Datablock::iterator cat = dict.end();
 
@@ -921,37 +936,37 @@ void DictParser::parseSaveFrame()
 	{
 		if (mLookahead == eCIFTokenLOOP)
 		{
-			cat = dict.end();	// should start a new category
-				
+			cat = dict.end(); // should start a new category
+
 			match(eCIFTokenLOOP);
-			
+
 			std::vector<std::string> tags;
 			while (mLookahead == eCIFTokenTag)
 			{
 				std::string catName, itemName;
 				std::tie(catName, itemName) = splitTagName(mTokenValue);
-					
+
 				if (cat == dict.end())
 					std::tie(cat, std::ignore) = dict.emplace(catName);
 				else if (not iequals(cat->name(), catName))
 					error("inconsistent categories in loop_");
-				
+
 				tags.push_back(itemName);
 				match(eCIFTokenTag);
 			}
-			
+
 			while (mLookahead == eCIFTokenValue)
 			{
 				cat->emplace({});
 				auto row = cat->back();
-				
-				for (auto tag: tags)
+
+				for (auto tag : tags)
 				{
 					row[tag] = mTokenValue;
 					match(eCIFTokenValue);
 				}
 			}
-			
+
 			cat = dict.end();
 		}
 		else
@@ -963,75 +978,78 @@ void DictParser::parseSaveFrame()
 				std::tie(cat, std::ignore) = dict.emplace(catName);
 
 			match(eCIFTokenTag);
-			
+
 			if (cat->empty())
 				cat->emplace({});
 			cat->back()[itemName] = mTokenValue;
-			
+
 			match(eCIFTokenValue);
 		}
 	}
 
 	match(eCIFTokenSAVE);
-	
+
 	if (isCategorySaveFrame)
 	{
-		std::string category = dict.firstItem("_category.id");
+		std::string category;
+		cif::tie(category) = dict["category"].front().get("id");
 
 		std::vector<std::string> keys;
-		for (auto k: dict["category_key"])
+		for (auto k : dict["category_key"])
 			keys.push_back(std::get<1>(splitTagName(k["name"].as<std::string>())));
-		
+
 		iset groups;
-		for (auto g: dict["category_group"])
+		for (auto g : dict["category_group"])
 			groups.insert(g["id"].as<std::string>());
-			
+
 		mImpl->mCategoryValidators.push_back(ValidateCategory{category, keys, groups});
 	}
 	else
 	{
 		// if the type code is missing, this must be a pointer, just skip it
-		std::string typeCode = dict.firstItem("_item_type.code");
+		std::string typeCode;
+		cif::tie(typeCode) = dict["item_type"].front().get("code");
 
-		const ValidateType* tv = nullptr;
-		if (not (typeCode.empty() or typeCode == "?"))
+		const ValidateType *tv = nullptr;
+		if (not(typeCode.empty() or typeCode == "?"))
 			tv = mValidator.getValidatorForType(typeCode);
 
 		iset ess;
-		for (auto e: dict["item_enumeration"])
+		for (auto e : dict["item_enumeration"])
 			ess.insert(e["value"].as<std::string>());
-		
-		std::string defaultValue = dict.firstItem("_item_default.value");
+
+		std::string defaultValue;
+		cif::tie(defaultValue) = dict["item_default"].front().get("value");
 		bool defaultIsNull = false;
 		if (defaultValue.empty())
 		{
-			for (auto& r: dict["_item_default"])
+			for (auto &r : dict["_item_default"])
 			{
 				defaultIsNull = r["value"].is_null();
 				break;
 			}
 		}
-		
+
 		// collect the dict from our dataBlock and construct validators
-		for (auto i: dict["item"])
+		for (auto i : dict["item"])
 		{
 			std::string tagName, category, mandatory;
-			
+
 			cif::tie(tagName, category, mandatory) = i.get("name", "category_id", "mandatory_code");
-			
+
 			std::string catName, itemName;
 			std::tie(catName, itemName) = splitTagName(tagName);
-			
+
 			if (catName.empty() or itemName.empty())
 				error("Invalid tag name in _item.name " + tagName);
 
-			if (not iequals(category, catName) and not (category.empty() or category == "?"))
+			if (not iequals(category, catName) and not(category.empty() or category == "?"))
 				error("specified category id does match the implicit category name for tag '" + tagName + '\'');
 			else
 				category = catName;
-			
-			auto& ivs = mImpl->mItemValidators[category];
-			
+
+			auto &ivs = mImpl->mItemValidators[category];
+
 			auto vi = find(ivs.begin(), ivs.end(), ValidateItem{itemName});
 			if (vi == ivs.end())
 				ivs.push_back(ValidateItem{itemName, iequals(mandatory, "yes"), tv, ess, defaultValue, defaultIsNull});
@@ -1043,7 +1061,7 @@ void DictParser::parseSaveFrame()
 					if (VERBOSE > 2)
 					{
 						std::cerr << "inconsistent mandatory value for " << tagName << " in dictionary" << std::endl;
-						
+
 						if (iequals(tagName, saveFrameName))
 							std::cerr << "choosing " << mandatory << std::endl;
 						else
@@ -1060,7 +1078,7 @@ void DictParser::parseSaveFrame()
 						std::cerr << "inconsistent type for " << tagName << " in dictionary" << std::endl;
 				}
 
-//				vi->mMandatory = (iequals(mandatory, "yes"));
+				//				vi->mMandatory = (iequals(mandatory, "yes"));
 				if (vi->mType == nullptr)
 					vi->mType = tv;
 
@@ -1070,14 +1088,14 @@ void DictParser::parseSaveFrame()
 				// ...
 			}
 		}
-		
+
 		// collect the dict from our dataBlock and construct validators
-		for (auto i: dict["item_linked"])
+		for (auto i : dict["item_linked"])
 		{
 			std::string childTagName, parentTagName;
-			
+
 			cif::tie(childTagName, parentTagName) = i.get("child_name", "parent_name");
-			
+
 			mImpl->mLinkedItems.emplace(childTagName, parentTagName);
 		}
 	}
@@ -1088,20 +1106,20 @@ void DictParser::linkItems()
 	if (not mDataBlock)
 		error("no datablock");
 
-	auto& dict = *mDataBlock;
+	auto &dict = *mDataBlock;
 
 	// links are identified by a parent category, a child category and a group ID
 
-	using key_type = std::tuple<std::string,std::string,int>;
+	using key_type = std::tuple<std::string, std::string, int>;
 
-	std::map<key_type,size_t> linkIndex;
+	std::map<key_type, size_t> linkIndex;
 
 	// Each link group consists of a set of keys
-	std::vector<std::tuple<std::vector<std::string>,std::vector<std::string>>> linkKeys;
+	std::vector<std::tuple<std::vector<std::string>, std::vector<std::string>>> linkKeys;
 
-	auto addLink = [&](size_t ix, const std::string& pk, const std::string& ck)
+	auto addLink = [&](size_t ix, const std::string &pk, const std::string &ck)
 	{
-		auto&& [pkeys, ckeys] = linkKeys.at(ix);
+		auto &&[pkeys, ckeys] = linkKeys.at(ix);
 
 		bool found = false;
 		for (size_t i = 0; i < pkeys.size(); ++i)
@@ -1120,29 +1138,29 @@ void DictParser::linkItems()
 		}
 	};
 
-	auto& linkedGroupList = dict["pdbx_item_linked_group_list"];
+	auto &linkedGroupList = dict["pdbx_item_linked_group_list"];
 
-	for (auto gl: linkedGroupList)
+	for (auto gl : linkedGroupList)
 	{
 		std::string child, parent;
 		int link_group_id;
 		cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
-		
+
 		auto civ = mValidator.getValidatorForItem(child);
 		if (civ == nullptr)
 			error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
-		
+
 		auto piv = mValidator.getValidatorForItem(parent);
 		if (piv == nullptr)
 			error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
-		
-		key_type key{ piv->mCategory->mName, civ->mCategory->mName, link_group_id };
+
+		key_type key{piv->mCategory->mName, civ->mCategory->mName, link_group_id};
 		if (not linkIndex.count(key))
 		{
 			linkIndex[key] = linkKeys.size();
 			linkKeys.push_back({});
 		}
-		
+
 		size_t ix = linkIndex.at(key);
 		addLink(ix, piv->mTag, civ->mTag);
 	}
@@ -1151,35 +1169,35 @@ void DictParser::linkItems()
 	if (linkedGroupList.empty())
 	{
 		// for links recorded in categories but not in pdbx_item_linked_group_list
-		for (auto li: mImpl->mLinkedItems)
+		for (auto li : mImpl->mLinkedItems)
 		{
 			std::string child, parent;
 			std::tie(child, parent) = li;
-			
+
 			auto civ = mValidator.getValidatorForItem(child);
 			if (civ == nullptr)
 				error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
-			
+
 			auto piv = mValidator.getValidatorForItem(parent);
 			if (piv == nullptr)
 				error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
 
-			key_type key{ piv->mCategory->mName, civ->mCategory->mName, 0 };
+			key_type key{piv->mCategory->mName, civ->mCategory->mName, 0};
 			if (not linkIndex.count(key))
 			{
 				linkIndex[key] = linkKeys.size();
 				linkKeys.push_back({});
 			}
-			
+
 			size_t ix = linkIndex.at(key);
 			addLink(ix, piv->mTag, civ->mTag);
 		}
 	}
 
-	auto& linkedGroup = dict["pdbx_item_linked_group"];
+	auto &linkedGroup = dict["pdbx_item_linked_group"];
 
 	// now store the links in the validator
-	for (auto& kv: linkIndex)
+	for (auto &kv : linkIndex)
 	{
 		ValidateLink link = {};
 		std::tie(link.mParentCategory, link.mChildCategory, link.mLinkGroupID) = kv.first;
@@ -1187,7 +1205,7 @@ void DictParser::linkItems()
 		std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second];
 
 		// look up the label
-		for (auto r: linkedGroup.find(cif::Key("category_id") == link.mChildCategory and cif::Key("link_group_id") == link.mLinkGroupID))
+		for (auto r : linkedGroup.find(cif::Key("category_id") == link.mChildCategory and cif::Key("link_group_id") == link.mLinkGroupID))
 		{
 			link.mLinkGroupLabel = r["label"].as<std::string>();
 			break;
@@ -1197,22 +1215,22 @@ void DictParser::linkItems()
 	}
 
 	// now make sure the itemType is specified for all itemValidators
-	
-	for (auto& cv: mValidator.mCategoryValidators)
+
+	for (auto &cv : mValidator.mCategoryValidators)
 	{
-		for (auto& iv: cv.mItemValidators)
+		for (auto &iv : cv.mItemValidators)
 		{
-			if (iv.mType == nullptr)
+			if (iv.mType == nullptr and cif::VERBOSE >= 0)
 				std::cerr << "Missing item_type for " << iv.mTag << std::endl;
 		}
-	}	
+	}
 }
 
 void DictParser::loadDictionary()
 {
 	std::unique_ptr<Datablock> dict;
-	Datablock* savedDatablock = mDataBlock;
-	
+	Datablock *savedDatablock = mDataBlock;
+
 	try
 	{
 		while (mLookahead != eCIFTokenEOF)
@@ -1222,12 +1240,12 @@ void DictParser::loadDictionary()
 				case eCIFTokenGLOBAL:
 					parseGlobal();
 					break;
-				
+
 				default:
 				{
-					dict.reset(new Datablock(mTokenValue));	// dummy datablock, for constructing the validator only
+					dict.reset(new Datablock(mTokenValue)); // dummy datablock, for constructing the validator only
 					mDataBlock = dict.get();
-					
+
 					match(eCIFTokenDATA);
 					parseDataBlock();
 					break;
@@ -1235,29 +1253,30 @@ void DictParser::loadDictionary()
 			}
 		}
 	}
-	catch (const std::exception&)
+	catch (const std::exception &)
 	{
-		std::cerr << "Error parsing dictionary" << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Error parsing dictionary" << std::endl;
 		throw;
 	}
 
 	// store all validators
-	for (auto& ic: mImpl->mCategoryValidators)
+	for (auto &ic : mImpl->mCategoryValidators)
 		mValidator.addCategoryValidator(std::move(ic));
 	mImpl->mCategoryValidators.clear();
-	
-	for (auto& iv: mImpl->mItemValidators)
+
+	for (auto &iv : mImpl->mItemValidators)
 	{
 		auto cv = mValidator.getValidatorForCategory(iv.first);
 		if (cv == nullptr)
 			error("Undefined category '" + iv.first);
 
-		for (auto& v: iv.second)
-			const_cast<ValidateCategory*>(cv)->addItemValidator(std::move(v));
+		for (auto &v : iv.second)
+			const_cast<ValidateCategory *>(cv)->addItemValidator(std::move(v));
 	}
-		
+
 	// check all item validators for having a typeValidator
-	
+
 	if (dict)
 		linkItems();
 
@@ -1280,47 +1299,45 @@ void DictParser::loadDictionary()
 bool DictParser::collectItemTypes()
 {
 	bool result = false;
-	
+
 	if (not mDataBlock)
 		error("no datablock");
-	
-	auto& dict = *mDataBlock;
-	
-	for (auto& t: dict["item_type_list"])
+
+	auto &dict = *mDataBlock;
+
+	for (auto &t : dict["item_type_list"])
 	{
 		std::string code, primitiveCode, construct;
 		cif::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct");
-		
+
 		ba::replace_all(construct, "\\n", "\n");
 		ba::replace_all(construct, "\\t", "\t");
 		ba::replace_all(construct, "\\\n", "");
-		
+
 		try
 		{
 			ValidateType v = {
-				code, mapToPrimitiveType(primitiveCode), boost::regex(construct, boost::regex::extended | boost::regex::optimize)
-			};
-			
+				code, mapToPrimitiveType(primitiveCode), boost::regex(construct, boost::regex::extended | boost::regex::optimize)};
+
 			mValidator.addTypeValidator(std::move(v));
 		}
-		catch (const std::exception&)
+		catch (const std::exception &)
 		{
 			throw_with_nested(CifParserError(t.lineNr(), "error in regular expression"));
 		}
 
-// Do not replace an already defined type validator, this won't work with pdbx_v40
-// as it has a name that is too strict for its own names :-)
-//		if (mFileImpl.mTypeValidators.count(v))
-//			mFileImpl.mTypeValidators.erase(v);
+		// Do not replace an already defined type validator, this won't work with pdbx_v40
+		// as it has a name that is too strict for its own names :-)
+		//		if (mFileImpl.mTypeValidators.count(v))
+		//			mFileImpl.mTypeValidators.erase(v);
 
 		if (VERBOSE >= 5)
 			std::cerr << "Added type " << code << " (" << primitiveCode << ") => " << construct << std::endl;
-		
+
 		result = true;
 	}
-	
+
 	return result;
 }
 
-
-}
+} // namespace cif
diff --git a/src/CifUtils.cpp b/src/CifUtils.cpp
index 60b9d4f..b297def 100644
--- a/src/CifUtils.cpp
+++ b/src/CifUtils.cpp
@@ -25,19 +25,14 @@
  */
 
 #include <atomic>
-#include <chrono>
 #include <cmath>
-#include <cstdio>
-#include <filesystem>
 #include <fstream>
-#include <iomanip>
 #include <iostream>
 #include <map>
 #include <mutex>
 #include <regex>
 #include <sstream>
 #include <thread>
-#include <tuple>
 
 #if defined(_MSC_VER)
 #define TERM_WIDTH 80
@@ -49,6 +44,7 @@
 #include <boost/algorithm/string.hpp>
 
 #include "cif++/CifUtils.hpp"
+
 #include "revision.hpp"
 
 namespace ba = boost::algorithm;
@@ -94,11 +90,12 @@ const uint8_t kCharToLowerMap[256] =
 
 // --------------------------------------------------------------------
 
-bool iequals(const std::string &a, const std::string &b)
+bool iequals(std::string_view a, std::string_view b)
 {
 	bool result = a.length() == b.length();
-	for (auto ai = a.begin(), bi = b.begin(); result and ai != a.end() and bi != b.end(); ++ai, ++bi)
-		result = tolower(*ai) == tolower(*bi);
+	for (auto ai = a.begin(), bi = b.begin(); result and ai != a.end(); ++ai, ++bi)
+		result = kCharToLowerMap[uint8_t(*ai)] == kCharToLowerMap[uint8_t(*bi)];
+		// result = tolower(*ai) == tolower(*bi);
 	return result;
 }
 
@@ -111,7 +108,7 @@ bool iequals(const char *a, const char *b)
 	return result and *a == *b;
 }
 
-int icompare(const std::string &a, const std::string &b)
+int icompare(std::string_view a, std::string_view b)
 {
 	int d = 0;
 	auto ai = a.begin(), bi = b.begin();
@@ -164,7 +161,7 @@ std::string toLowerCopy(const std::string &s)
 
 // --------------------------------------------------------------------
 
-std::tuple<std::string, std::string> splitTagName(const std::string &tag)
+std::tuple<std::string, std::string> splitTagName(std::string_view tag)
 {
 	if (tag.empty())
 		throw std::runtime_error("empty tag");
@@ -1207,7 +1204,7 @@ std::filesystem::path gDataDir;
 
 void addDataDirectory(std::filesystem::path dataDir)
 {
-	if (VERBOSE and not fs::exists(dataDir))
+	if (VERBOSE > 0 and not fs::exists(dataDir))
 		std::cerr << "The specified data directory " << dataDir << " does not exist" << std::endl;
 	gDataDir = dataDir;
 }
diff --git a/src/CifValidator.cpp b/src/CifValidator.cpp
index a7fb7ca..ad3a09c 100644
--- a/src/CifValidator.cpp
+++ b/src/CifValidator.cpp
@@ -24,32 +24,39 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <fstream>
+#include <filesystem>
+
 #include <boost/algorithm/string.hpp>
+#include <boost/iostreams/filtering_stream.hpp>
+#include <boost/iostreams/filter/gzip.hpp>
 
 #include "cif++/Cif++.hpp"
 #include "cif++/CifParser.hpp"
 #include "cif++/CifValidator.hpp"
 
 namespace ba = boost::algorithm;
+namespace fs = std::filesystem;
+namespace io = boost::iostreams;
 
 extern int VERBOSE;
 
 namespace cif
 {
 
-ValidationError::ValidationError(const std::string& msg)
+ValidationError::ValidationError(const std::string &msg)
 	: mMsg(msg)
 {
 }
 
-ValidationError::ValidationError(const std::string& cat, const std::string& item, const std::string& msg)
+ValidationError::ValidationError(const std::string &cat, const std::string &item, const std::string &msg)
 	: mMsg("When validating _" + cat + '.' + item + ": " + msg)
 {
 }
 
 // --------------------------------------------------------------------
 
-DDL_PrimitiveType mapToPrimitiveType(const std::string& s)
+DDL_PrimitiveType mapToPrimitiveType(std::string_view s)
 {
 	DDL_PrimitiveType result;
 	if (iequals(s, "char"))
@@ -65,10 +72,10 @@ DDL_PrimitiveType mapToPrimitiveType(const std::string& s)
 
 // --------------------------------------------------------------------
 
-int ValidateType::compare(const char* a, const char* b) const
+int ValidateType::compare(const char *a, const char *b) const
 {
 	int result = 0;
-	
+
 	if (*a == 0)
 		result = *b == 0 ? 0 : -1;
 	else if (*b == 0)
@@ -83,7 +90,7 @@ int ValidateType::compare(const char* a, const char* b) const
 				{
 					double da = strtod(a, nullptr);
 					double db = strtod(b, nullptr);
-					
+
 					auto d = da - db;
 					if (std::abs(d) > std::numeric_limits<double>::epsilon())
 					{
@@ -94,13 +101,13 @@ int ValidateType::compare(const char* a, const char* b) const
 					}
 					break;
 				}
-				
+
 				case DDL_PrimitiveType::UChar:
 				case DDL_PrimitiveType::Char:
 				{
 					// CIF is guaranteed to have ascii only, therefore this primitive code will do
 					// also, we're collapsing spaces
-					
+
 					auto ai = a, bi = b;
 					for (;;)
 					{
@@ -115,7 +122,7 @@ int ValidateType::compare(const char* a, const char* b) const
 							result = 1;
 							break;
 						}
-						
+
 						char ca = *ai;
 						char cb = *bi;
 
@@ -124,12 +131,12 @@ int ValidateType::compare(const char* a, const char* b) const
 							ca = tolower(ca);
 							cb = tolower(cb);
 						}
-						
+
 						result = ca - cb;
-						
+
 						if (result != 0)
 							break;
-						
+
 						if (ca == ' ')
 						{
 							while (ai[1] == ' ')
@@ -137,21 +144,21 @@ int ValidateType::compare(const char* a, const char* b) const
 							while (bi[1] == ' ')
 								++bi;
 						}
-						
+
 						++ai;
 						++bi;
 					}
-					
+
 					break;
 				}
 			}
 		}
-		catch (const std::invalid_argument& ex)
+		catch (const std::invalid_argument &ex)
 		{
 			result = 1;
 		}
 	}
-	
+
 	return result;
 }
 
@@ -165,13 +172,13 @@ int ValidateType::compare(const char* a, const char* b) const
 //
 //	if (mType == nullptr and parent != nullptr)
 //		mType = parent->mType;
-//		
+//
 //	if (parent != nullptr)
 //	{
 //		mLinked.push_back({parent, parentItem, childItem});
 //
 //		parent->mChildren.insert(this);
-////	
+////
 ////		if (mCategory->mKeys == std::vector<std::string>{mTag})
 ////			parent->mForeignKeys.insert(this);
 //	}
@@ -194,7 +201,7 @@ void ValidateItem::operator()(std::string value) const
 
 // --------------------------------------------------------------------
 
-void ValidateCategory::addItemValidator(ValidateItem&& v)
+void ValidateCategory::addItemValidator(ValidateItem &&v)
 {
 	if (v.mMandatory)
 		mMandatoryFields.insert(v.mTag);
@@ -206,10 +213,10 @@ void ValidateCategory::addItemValidator(ValidateItem&& v)
 		std::cout << "Could not add validator for item " << v.mTag << " to category " << mName << std::endl;
 }
 
-const ValidateItem* ValidateCategory::getValidatorForItem(std::string tag) const
+const ValidateItem *ValidateCategory::getValidatorForItem(std::string_view tag) const
 {
-	const ValidateItem* result = nullptr;
-	auto i = mItemValidators.find(ValidateItem{tag});
+	const ValidateItem *result = nullptr;
+	auto i = mItemValidators.find(ValidateItem{std::string(tag)});
 	if (i != mItemValidators.end())
 		result = &*i;
 	else if (VERBOSE > 4)
@@ -219,26 +226,29 @@ const ValidateItem* ValidateCategory::getValidatorForItem(std::string tag) const
 
 // --------------------------------------------------------------------
 
-Validator::Validator()
+Validator::Validator(std::string_view name, std::istream &is)
+	: mName(name)
 {
+	DictParser p(*this, is);
+	p.loadDictionary();
 }
 
 Validator::~Validator()
 {
 }
 
-void Validator::addTypeValidator(ValidateType&& v)
+void Validator::addTypeValidator(ValidateType &&v)
 {
 	auto r = mTypeValidators.insert(std::move(v));
 	if (not r.second and VERBOSE > 4)
 		std::cout << "Could not add validator for type " << v.mName << std::endl;
 }
 
-const ValidateType* Validator::getValidatorForType(std::string typeCode) const
+const ValidateType *Validator::getValidatorForType(std::string_view typeCode) const
 {
-	const ValidateType* result = nullptr;
-	
-	auto i = mTypeValidators.find(ValidateType{ typeCode, DDL_PrimitiveType::Char, boost::regex() });
+	const ValidateType *result = nullptr;
+
+	auto i = mTypeValidators.find(ValidateType{std::string(typeCode), DDL_PrimitiveType::Char, boost::regex()});
 	if (i != mTypeValidators.end())
 		result = &*i;
 	else if (VERBOSE > 4)
@@ -246,17 +256,17 @@ const ValidateType* Validator::getValidatorForType(std::string typeCode) const
 	return result;
 }
 
-void Validator::addCategoryValidator(ValidateCategory&& v)
+void Validator::addCategoryValidator(ValidateCategory &&v)
 {
 	auto r = mCategoryValidators.insert(std::move(v));
 	if (not r.second and VERBOSE > 4)
 		std::cout << "Could not add validator for category " << v.mName << std::endl;
 }
 
-const ValidateCategory* Validator::getValidatorForCategory(std::string category) const
+const ValidateCategory *Validator::getValidatorForCategory(std::string_view category) const
 {
-	const ValidateCategory* result = nullptr;
-	auto i = mCategoryValidators.find(ValidateCategory{category});
+	const ValidateCategory *result = nullptr;
+	auto i = mCategoryValidators.find(ValidateCategory{std::string(category)});
 	if (i != mCategoryValidators.end())
 		result = &*i;
 	else if (VERBOSE > 4)
@@ -264,16 +274,16 @@ const ValidateCategory* Validator::getValidatorForCategory(std::string category)
 	return result;
 }
 
-ValidateItem* Validator::getValidatorForItem(std::string tag) const
+ValidateItem *Validator::getValidatorForItem(std::string_view tag) const
 {
-	ValidateItem* result = nullptr;
-	
+	ValidateItem *result = nullptr;
+
 	std::string cat, item;
 	std::tie(cat, item) = splitTagName(tag);
 
-	auto* cv = getValidatorForCategory(cat);
+	auto *cv = getValidatorForCategory(cat);
 	if (cv != nullptr)
-		result = const_cast<ValidateItem*>(cv->getValidatorForItem(item));
+		result = const_cast<ValidateItem *>(cv->getValidatorForItem(item));
 
 	if (result == nullptr and VERBOSE > 4)
 		std::cout << "No validator for item " << tag << std::endl;
@@ -281,15 +291,15 @@ ValidateItem* Validator::getValidatorForItem(std::string tag) const
 	return result;
 }
 
-void Validator::addLinkValidator(ValidateLink&& v)
+void Validator::addLinkValidator(ValidateLink &&v)
 {
 	assert(v.mParentKeys.size() == v.mChildKeys.size());
 	if (v.mParentKeys.size() != v.mChildKeys.size())
 		throw std::runtime_error("unequal number of keys for parent and child in link");
-	
+
 	auto pcv = getValidatorForCategory(v.mParentCategory);
 	auto ccv = getValidatorForCategory(v.mChildCategory);
-	
+
 	if (pcv == nullptr)
 		throw std::runtime_error("unknown parent category " + v.mParentCategory);
 
@@ -299,53 +309,127 @@ void Validator::addLinkValidator(ValidateLink&& v)
 	for (size_t i = 0; i < v.mParentKeys.size(); ++i)
 	{
 		auto piv = pcv->getValidatorForItem(v.mParentKeys[i]);
-	
+
 		if (piv == nullptr)
 			throw std::runtime_error("unknown parent tag _" + v.mParentCategory + '.' + v.mParentKeys[i]);
 
 		auto civ = ccv->getValidatorForItem(v.mChildKeys[i]);
 		if (civ == nullptr)
 			throw std::runtime_error("unknown child tag _" + v.mChildCategory + '.' + v.mChildKeys[i]);
-		
+
 		if (civ->mType == nullptr and piv->mType != nullptr)
-			const_cast<ValidateItem*>(civ)->mType = piv->mType;
-	}		
-	
+			const_cast<ValidateItem *>(civ)->mType = piv->mType;
+	}
+
 	mLinkValidators.emplace_back(std::move(v));
 }
 
-std::vector<const ValidateLink*> Validator::getLinksForParent(const std::string& category) const
+std::vector<const ValidateLink *> Validator::getLinksForParent(std::string_view category) const
 {
-	std::vector<const ValidateLink*> result;
+	std::vector<const ValidateLink *> result;
 
-	for (auto& l: mLinkValidators)
+	for (auto &l : mLinkValidators)
 	{
 		if (l.mParentCategory == category)
 			result.push_back(&l);
 	}
-	
+
 	return result;
 }
 
-std::vector<const ValidateLink*> Validator::getLinksForChild(const std::string& category) const
+std::vector<const ValidateLink *> Validator::getLinksForChild(std::string_view category) const
 {
-	std::vector<const ValidateLink*> result;
+	std::vector<const ValidateLink *> result;
 
-	for (auto& l: mLinkValidators)
+	for (auto &l : mLinkValidators)
 	{
 		if (l.mChildCategory == category)
 			result.push_back(&l);
 	}
-	
+
 	return result;
 }
 
-void Validator::reportError(const std::string& msg, bool fatal)
+void Validator::reportError(const std::string &msg, bool fatal) const
 {
 	if (mStrict or fatal)
 		throw ValidationError(msg);
-	else if (VERBOSE)
+	else if (VERBOSE > 0)
 		std::cerr << msg << std::endl;
 }
 
+// --------------------------------------------------------------------
+
+ValidatorFactory ValidatorFactory::sInstance;
+
+ValidatorFactory::ValidatorFactory()
+{
+}
+
+const Validator &ValidatorFactory::operator[](std::string_view dictionary)
+{
+	std::lock_guard lock(mMutex);
+
+	for (auto &validator : mValidators)
+	{
+		if (iequals(validator.mName, dictionary))
+			return validator;
+	}
+
+	// not found, add it
+
+	fs::path dict_name(dictionary);
+
+	auto data = loadResource(dictionary);
+
+	if (not data and dict_name.extension().string() != ".dic")
+		data = loadResource(dict_name.parent_path() / (dict_name.filename().string() + ".dic"));
+
+	if (data)
+		mValidators.emplace_back(dictionary, *data);
+	else
+	{
+		// might be a compressed dictionary on disk
+		fs::path p = dictionary;
+		if (p.extension() == ".dic")
+			p = p.parent_path() / (p.filename().string() + ".gz");
+		else
+			p = p.parent_path() / (p.filename().string() + ".dic.gz");
+
+#if defined(CACHE_DIR) and defined(DATA_DIR)
+		if (not fs::exists(p))
+		{
+			for (const char *dir : {CACHE_DIR, DATA_DIR})
+			{
+				auto p2 = fs::path(dir) / p;
+				if (fs::exists(p2))
+				{
+					swap(p, p2);
+					break;
+				}
+			}
+		}
+#endif
+
+		if (fs::exists(p))
+		{
+			std::ifstream file(p, std::ios::binary);
+			if (not file.is_open())
+				throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
+
+			io::filtering_stream<io::input> in;
+			in.push(io::gzip_decompressor());
+			in.push(file);
+
+			mValidators.emplace_back(dictionary, in);
+		}
+		else
+			throw std::runtime_error("Dictionary not found or defined (" + dict_name.string() + ")");
+	}
+
+	assert(iequals(mValidators.back().mName, dictionary));
+
+	return mValidators.back();
 }
+
+} // namespace cif
diff --git a/src/Compound.cpp b/src/Compound.cpp
index 0afd222..5f2249f 100644
--- a/src/Compound.cpp
+++ b/src/Compound.cpp
@@ -128,6 +128,8 @@ Compound::Compound(cif::Datablock &db)
 	// The name should not contain newline characters since that triggers validation errors later on
 	ba::replace_all(mName, "\n", "");
 
+	mGroup = "non-polymer";
+
 	auto &chemCompAtom = db["chem_comp_atom"];
 	for (auto row : chemCompAtom)
 	{
@@ -151,10 +153,11 @@ Compound::Compound(cif::Datablock &db)
 	}
 }
 
-Compound::Compound(cif::Datablock &db, const std::string &id, const std::string &name, const std::string &type)
+Compound::Compound(cif::Datablock &db, const std::string &id, const std::string &name, const std::string &type, const std::string &group)
 	: mID(id)
 	, mName(name)
 	, mType(type)
+	, mGroup(group)
 {
 	auto &chemCompAtom = db["chem_comp_atom"];
 	for (auto row : chemCompAtom)
@@ -190,7 +193,7 @@ Compound::Compound(cif::Datablock &db, const std::string &id, const std::string
 			bond.type = BondType::delo;
 		else
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Unimplemented chem_comp_bond.type " << btype << " in " << id << std::endl;
 			bond.type = BondType::sing;
 		}
@@ -400,7 +403,7 @@ CompoundFactoryImpl::CompoundFactoryImpl(const std::filesystem::path &file, std:
 
 			if (cif::iequals(id, "gly"))
 				type = "peptide linking";
-			else if (cif::iequals(group, "l-peptide") or cif::iequals(group, "L-peptide linking") or cif::iequals(group, "peptide"))
+			else if (cif::iequals(group, "l-peptide") or cif::iequals(group, "L-peptide linking") or cif::iequals(group, "peptide") or cif::iequals(group, "p-peptide"))
 				type = "L-peptide linking";
 			else if (cif::iequals(group, "DNA"))
 				type = "DNA linking";
@@ -411,7 +414,7 @@ CompoundFactoryImpl::CompoundFactoryImpl(const std::filesystem::path &file, std:
 
 			auto &db = cifFile["comp_" + id];
 
-			mCompounds.push_back(new Compound(db, id, name, type));
+			mCompounds.push_back(new Compound(db, id, name, type, group));
 		}
 	}
 	else
@@ -517,7 +520,7 @@ Compound *CCDCompoundFactoryImpl::create(const std::string &id)
 		}
 	}
 
-	if (result == nullptr and cif::VERBOSE)
+	if (result == nullptr and cif::VERBOSE > 0)
 		std::cerr << "Could not locate compound " << id << " in the CCD components file" << std::endl;
 
 	return result;
@@ -608,7 +611,7 @@ Compound *CCP4CompoundFactoryImpl::create(const std::string &id)
 
 			if (cif::iequals(id, "gly"))
 				type = "peptide linking";
-			else if (cif::iequals(group, "l-peptide") or cif::iequals(group, "L-peptide linking") or cif::iequals(group, "peptide"))
+			else if (cif::iequals(group, "l-peptide") or cif::iequals(group, "L-peptide linking") or cif::iequals(group, "peptide") or cif::iequals(group, "p-peptide"))
 				type = "L-peptide linking";
 			else if (cif::iequals(group, "DNA"))
 				type = "DNA linking";
@@ -617,7 +620,7 @@ Compound *CCP4CompoundFactoryImpl::create(const std::string &id)
 			else
 				type = "non-polymer";
 
-			mCompounds.push_back(new Compound(db, id, name, type));
+			mCompounds.push_back(new Compound(db, id, name, type, group));
 			result = mCompounds.back();
 		}
 	}
@@ -642,13 +645,13 @@ CompoundFactory::CompoundFactory()
 	auto ccd = cif::loadResource("components.cif");
 	if (ccd)
 		mImpl.reset(new CCDCompoundFactoryImpl(mImpl));
-	else if (cif::VERBOSE)
+	else if (cif::VERBOSE > 0)
 		std::cerr << "CCD components.cif file was not found" << std::endl;
 
 	const char *clibd_mon = getenv("CLIBD_MON");
 	if (clibd_mon != nullptr and fs::is_directory(clibd_mon))
 		mImpl.reset(new CCP4CompoundFactoryImpl(clibd_mon));
-	else if (cif::VERBOSE)
+	else if (cif::VERBOSE > 0)
 		std::cerr << "CCP4 monomers library not found, CLIBD_MON is not defined" << std::endl;
 
 }
@@ -692,7 +695,8 @@ void CompoundFactory::setDefaultDictionary(const std::filesystem::path &inDictFi
 	}
 	catch (const std::exception &)
 	{
-		std::cerr << "Error loading dictionary " << inDictFile << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Error loading dictionary " << inDictFile << std::endl;
 		throw;
 	}
 }
@@ -712,7 +716,8 @@ void CompoundFactory::pushDictionary(const std::filesystem::path &inDictFile)
 	}
 	catch (const std::exception &)
 	{
-		std::cerr << "Error loading dictionary " << inDictFile << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Error loading dictionary " << inDictFile << std::endl;
 		throw;
 	}
 }
diff --git a/src/PDB2Cif.cpp b/src/PDB2Cif.cpp
index 1c99972..f86c4ac 100644
--- a/src/PDB2Cif.cpp
+++ b/src/PDB2Cif.cpp
@@ -268,7 +268,8 @@ int PDBRecord::vI(int columnFirst, int columnLast)
 	}
 	catch (const std::exception &ex)
 	{
-		std::cerr << "Trying to parse '" << std::string(mValue + columnFirst - 7, mValue + columnLast - 7) << '\'' << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Trying to parse '" << std::string(mValue + columnFirst - 7, mValue + columnLast - 7) << '\'' << std::endl;
 		throw;
 	}
 
@@ -337,7 +338,7 @@ std::tuple<std::string, std::string> SpecificationListParser::GetNextSpecificati
 				}
 				else if (not isspace(ch))
 				{
-					if (cif::VERBOSE)
+					if (cif::VERBOSE > 0)
 						std::cerr << "skipping invalid character in SOURCE ID: " << ch << std::endl;
 				}
 				break;
@@ -354,7 +355,7 @@ std::tuple<std::string, std::string> SpecificationListParser::GetNextSpecificati
 			case eColon:
 				if (ch == ';')
 				{
-					if (cif::VERBOSE)
+					if (cif::VERBOSE > 0)
 						std::cerr << "Empty value for SOURCE: " << id << std::endl;
 					state = eStart;
 				}
@@ -418,7 +419,7 @@ std::tuple<std::string, std::string> SpecificationListParser::GetNextSpecificati
 			case eError:
 				if (ch == ';')
 				{
-					if (cif::VERBOSE)
+					if (cif::VERBOSE > 0)
 						std::cerr << "Skipping invalid header line: '" << std::string(start, mP) << std::endl;
 					state = eStart;
 				}
@@ -656,7 +657,7 @@ class PDBFileParser
 		int mSeqNum;
 		char mIcode;
 
-		int mDbSeqNum;
+		int mDbSeqNum = 0;
 		bool mSeen = false;
 		std::set<std::string> mAlts;
 
@@ -832,7 +833,7 @@ class PDBFileParser
 		if (not mChainSeq2AsymSeq.count(key))
 		{
 			ec = error::make_error_code(error::pdbErrors::residueNotFound);
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Residue " << chainID << resSeq << iCode << " could not be mapped" << std::endl;
 		}
 		else
@@ -929,7 +930,7 @@ class PDBFileParser
 		}
 		catch (const std::exception &ex)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << ex.what() << std::endl;
 			ec = error::make_error_code(error::pdbErrors::invalidDate);
 		}
@@ -940,7 +941,10 @@ class PDBFileParser
 	std::string pdb2cifDate(std::string s)
 	{
 		std::error_code ec;
-		return pdb2cifDate(s, ec);
+		auto result = pdb2cifDate(s, ec);
+		if (ec and cif::VERBOSE > 0)
+			std::cerr << "Invalid date(" << s << "): " << ec.message() << std::endl;
+		return result;
 	}
 
 	std::string pdb2cifAuth(std::string author)
@@ -1160,7 +1164,8 @@ void PDBFileParser::PreParseInput(std::istream &is)
 			if (is.eof())
 				break;
 
-			std::cerr << "Line number " << lineNr << " is empty!" << std::endl;
+			if (cif::VERBOSE > 0)
+				std::cerr << "Line number " << lineNr << " is empty!" << std::endl;
 
 			getline(is, lookahead);
 			++lineNr;
@@ -1278,7 +1283,8 @@ void PDBFileParser::PreParseInput(std::istream &is)
 				}
 				catch (const std::exception &ex)
 				{
-					std::cerr << "Dropping FORMUL line (" << (lineNr - 1) << ") with invalid component number '" << value.substr(1, 3) << '\'' << std::endl;
+					if (cif::VERBOSE >= 0)
+						std::cerr << "Dropping FORMUL line (" << (lineNr - 1) << ") with invalid component number '" << value.substr(1, 3) << '\'' << std::endl;
 					continue;
 					// throw_with_nested(std::runtime_error("Invalid component number '" + value.substr(1, 3) + '\''));
 				}
@@ -1305,7 +1311,8 @@ void PDBFileParser::PreParseInput(std::istream &is)
 			}
 			catch (const std::exception &ex)
 			{
-				std::cerr << "Error parsing FORMUL at line " << lineNr << std::endl;
+				if (cif::VERBOSE >= 0)
+					std::cerr << "Error parsing FORMUL at line " << lineNr << std::endl;
 				throw;
 			}
 		}
@@ -1412,7 +1419,8 @@ void PDBFileParser::PreParseInput(std::istream &is)
 
 	if (not dropped.empty())
 	{
-		std::cerr << "Dropped unsupported records: " << ba::join(dropped, ", ") << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Dropped unsupported records: " << ba::join(dropped, ", ") << std::endl;
 	}
 
 	if (mData == nullptr)
@@ -1440,7 +1448,7 @@ void PDBFileParser::Match(const std::string &expected, bool throwIfMissing)
 	{
 		if (throwIfMissing)
 			throw std::runtime_error("Expected record " + expected + " but found " + mRec->mName);
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "Expected record " + expected + " but found " + mRec->mName << std::endl;
 	}
 }
@@ -1540,7 +1548,8 @@ void PDBFileParser::ParseTitle()
 	while (mRec->is("CAVEAT")) //	  1 - 6       Record name   "CAVEAT"
 	{
 		getCategory("database_PDB_caveat")->emplace({
-			{"id", caveatID++}, {"text", std::string{mRec->vS(20)}} //	20 - 79       String        comment        Free text giving the reason for the  CAVEAT.
+			{"id", caveatID++},
+			{"text", std::string{mRec->vS(20)}} //	20 - 79       String        comment        Free text giving the reason for the  CAVEAT.
 		});
 
 		GetNextRecord();
@@ -1574,7 +1583,8 @@ void PDBFileParser::ParseTitle()
 
 			if (not iequals(key, "MOL_ID") and mCompounds.empty())
 			{
-				std::cerr << "Ignoring invalid COMPND record" << std::endl;
+				if (cif::VERBOSE >= 0)
+					std::cerr << "Ignoring invalid COMPND record" << std::endl;
 				break;
 			}
 
@@ -1624,7 +1634,7 @@ void PDBFileParser::ParseTitle()
 		//			auto colon = s.find(": ");
 		//			if (colon == std::string::npos)
 		//			{
-		//				if (cif::VERBOSE)
+		//				if (cif::VERBOSE > 0)
 		//					std::cerr << "invalid source field, missing colon (" << s << ')' << std::endl;
 		//				continue;
 		//			}
@@ -1674,7 +1684,9 @@ void PDBFileParser::ParseTitle()
 
 	if (not(keywords.empty() and pdbxKeywords.empty()))
 	{
-		getCategory("struct_keywords")->emplace({{"entry_id", mStructureID}, {"pdbx_keywords", keywords}, {"text", pdbxKeywords}});
+		getCategory("struct_keywords")->emplace({{"entry_id", mStructureID},
+		{"pdbx_keywords", keywords},
+		{"text", pdbxKeywords}});
 	}
 
 	// EXPDTA
@@ -1710,7 +1722,7 @@ void PDBFileParser::ParseTitle()
 	// NUMMDL
 	if (mRec->is("NUMMDL"))
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "skipping unimplemented NUMMDL record" << std::endl;
 		GetNextRecord();
 	}
@@ -1794,13 +1806,18 @@ void PDBFileParser::ParseTitle()
 	sort(revdats.begin(), revdats.end());
 	for (auto &revdat : revdats)
 	{
-		getCategory("database_PDB_rev")->emplace({{"num", revdat.revNum}, {"date", revdat.date}, {"date_original", revdat.dateOriginal}, {"replaces", revdat.replaces}, {"mod_type", revdat.modType}});
+		getCategory("database_PDB_rev")->emplace({{"num", revdat.revNum},
+		{"date", revdat.date},
+		{"date_original", revdat.dateOriginal},
+		{"replaces", revdat.replaces},
+		{"mod_type", revdat.modType}});
 
 		for (auto &type : revdat.types)
 		{
 			if (type.empty())
 				continue;
-			getCategory("database_PDB_rev_record")->emplace({{"rev_num", revdat.revNum}, {"type", type}});
+			getCategory("database_PDB_rev_record")->emplace({{"rev_num", revdat.revNum},
+			{"type", type}});
 		}
 	}
 	//*/
@@ -1808,7 +1825,7 @@ void PDBFileParser::ParseTitle()
 	// SPRSDE
 	if (mRec->is("SPRSDE"))
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "skipping unimplemented SPRSDE record" << std::endl;
 		GetNextRecord();
 	}
@@ -2036,7 +2053,10 @@ void PDBFileParser::ParseRemarks()
 					if (desc == "NULL")
 						desc.clear();
 
-					getCategory("exptl_crystal")->emplace({{"id", 1}, {"density_Matthews", iequals(density_Matthews, "NULL") ? "" : density_Matthews}, {"density_percent_sol", iequals(densityPercentSol, "NULL") ? "" : densityPercentSol}, {"description", desc}});
+					getCategory("exptl_crystal")->emplace({{"id", 1},
+					{"density_Matthews", iequals(density_Matthews, "NULL") ? "" : density_Matthews},
+					{"density_percent_sol", iequals(densityPercentSol, "NULL") ? "" : densityPercentSol},
+					{"description", desc}});
 
 					// now try to parse the conditions
 					const std::regex rx3(R"(TEMPERATURE +(\d+)K)"), rx4(R"(PH *(?:: *)?(\d+(?:\.\d+)?))") /*, rx5(R"(\b(\d+)C\b)")*/;
@@ -2065,7 +2085,11 @@ void PDBFileParser::ParseRemarks()
 
 					if (not(method.empty() and temp.empty() and ph.empty() and (conditions.empty() or conditions == "NULL")))
 					{
-						getCategory("exptl_crystal_grow")->emplace({{"crystal_id", 1}, {"method", method}, {"temp", temp}, {"pH", ph}, {"pdbx_details", conditions}});
+						getCategory("exptl_crystal_grow")->emplace({{"crystal_id", 1},
+						{"method", method},
+						{"temp", temp},
+						{"pH", ph},
+						{"pdbx_details", conditions}});
 					}
 
 					break;
@@ -2250,7 +2274,7 @@ void PDBFileParser::ParseRemarks()
 									state = eMCP;
 								else if (subtopic == "CHIRAL CENTERS")
 									state = eChC;
-								else if (cif::VERBOSE)
+								else if (cif::VERBOSE > 0)
 									throw std::runtime_error("Unknown subtopic in REMARK 500: " + subtopic);
 
 								headerSeen = false;
@@ -2283,7 +2307,21 @@ void PDBFileParser::ParseRemarks()
 
 									std::string distance = vF(63, 71);
 
-									getCategory("pdbx_validate_close_contact")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", 1}, {"auth_atom_id_1", atom1}, {"auth_asym_id_1", std::string{chain1}}, {"auth_comp_id_1", res1}, {"auth_seq_id_1", seq1}, {"PDB_ins_code_1", iCode1}, {"label_alt_id_1", alt1}, {"auth_atom_id_2", atom2}, {"auth_asym_id_2", std::string{chain2}}, {"auth_comp_id_2", res2}, {"auth_seq_id_2", seq2}, {"PDB_ins_code_2", iCode2}, {"label_alt_id_2", alt2}, {"dist", distance}});
+									getCategory("pdbx_validate_close_contact")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", 1},
+									{"auth_atom_id_1", atom1},
+									{"auth_asym_id_1", std::string{chain1}},
+									{"auth_comp_id_1", res1},
+									{"auth_seq_id_1", seq1},
+									{"PDB_ins_code_1", iCode1},
+									{"label_alt_id_1", alt1},
+									{"auth_atom_id_2", atom2},
+									{"auth_asym_id_2", std::string{chain2}},
+									{"auth_comp_id_2", res2},
+									{"auth_seq_id_2", seq2},
+									{"PDB_ins_code_2", iCode2},
+									{"label_alt_id_2", alt2},
+									{"dist", distance}});
 								}
 								break;
 							}
@@ -2313,14 +2351,19 @@ void PDBFileParser::ParseRemarks()
 									}
 									catch (const std::exception &ex)
 									{
-										if (cif::VERBOSE)
+										if (cif::VERBOSE > 0)
 											std::cerr << "Dropping REMARK 500 at line " << mRec->mLineNr << " due to invalid symmetry operation" << std::endl;
 										continue;
 									}
 
 									std::string distance = vF(63, 71);
 
-									getCategory("pdbx_validate_symm_contact")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", 1}, {"auth_atom_id_1", atom1}, {"auth_asym_id_1", std::string{chain1}}, {"auth_comp_id_1", res1}, {"auth_seq_id_1", seq1},
+									getCategory("pdbx_validate_symm_contact")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", 1},
+									{"auth_atom_id_1", atom1},
+									{"auth_asym_id_1", std::string{chain1}},
+									{"auth_comp_id_1", res1},
+									{"auth_seq_id_1", seq1},
 									                                                    //									{ "PDB_ins_code_1", "" },
 									                                                    //									{ "label_alt_id_1", "" },
 									                                                    {"site_symmetry_1", "1_555"},
@@ -2371,7 +2414,21 @@ void PDBFileParser::ParseRemarks()
 									if (iCode2 == " ")
 										iCode2.clear();
 
-									getCategory("pdbx_validate_rmsd_bond")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", model ? model : 1}, {"auth_atom_id_1", atm1}, {"auth_asym_id_1", chainID1}, {"auth_comp_id_1", resNam1}, {"auth_seq_id_1", seqNum1}, {"PDB_ins_code_1", iCode1}, {"label_alt_id_1", alt1}, {"auth_atom_id_2", atm2}, {"auth_asym_id_2", chainID2}, {"auth_comp_id_2", resNam2}, {"auth_seq_id_2", seqNum2}, {"PDB_ins_code_2", iCode2}, {"label_alt_id_2", alt2}, {"bond_deviation", deviation}});
+									getCategory("pdbx_validate_rmsd_bond")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", model ? model : 1},
+									{"auth_atom_id_1", atm1},
+									{"auth_asym_id_1", chainID1},
+									{"auth_comp_id_1", resNam1},
+									{"auth_seq_id_1", seqNum1},
+									{"PDB_ins_code_1", iCode1},
+									{"label_alt_id_1", alt1},
+									{"auth_atom_id_2", atm2},
+									{"auth_asym_id_2", chainID2},
+									{"auth_comp_id_2", resNam2},
+									{"auth_seq_id_2", seqNum2},
+									{"PDB_ins_code_2", iCode2},
+									{"label_alt_id_2", alt2},
+									{"bond_deviation", deviation}});
 								}
 
 								break;
@@ -2403,7 +2460,24 @@ void PDBFileParser::ParseRemarks()
 									if (deviation == "*****")
 										deviation.clear();
 
-									getCategory("pdbx_validate_rmsd_angle")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", model ? model : 1}, {"auth_atom_id_1", atoms[0]}, {"auth_asym_id_1", chainID}, {"auth_comp_id_1", resNam}, {"auth_seq_id_1", seqNum}, {"PDB_ins_code_1", iCode}, {"auth_atom_id_2", atoms[1]}, {"auth_asym_id_2", chainID}, {"auth_comp_id_2", resNam}, {"auth_seq_id_2", seqNum}, {"PDB_ins_code_2", iCode}, {"auth_atom_id_3", atoms[2]}, {"auth_asym_id_3", chainID}, {"auth_comp_id_3", resNam}, {"auth_seq_id_3", seqNum}, {"PDB_ins_code_3", iCode}, {"angle_deviation", deviation}});
+									getCategory("pdbx_validate_rmsd_angle")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", model ? model : 1},
+									{"auth_atom_id_1", atoms[0]},
+									{"auth_asym_id_1", chainID},
+									{"auth_comp_id_1", resNam},
+									{"auth_seq_id_1", seqNum},
+									{"PDB_ins_code_1", iCode},
+									{"auth_atom_id_2", atoms[1]},
+									{"auth_asym_id_2", chainID},
+									{"auth_comp_id_2", resNam},
+									{"auth_seq_id_2", seqNum},
+									{"PDB_ins_code_2", iCode},
+									{"auth_atom_id_3", atoms[2]},
+									{"auth_asym_id_3", chainID},
+									{"auth_comp_id_3", resNam},
+									{"auth_seq_id_3", seqNum},
+									{"PDB_ins_code_3", iCode},
+									{"angle_deviation", deviation}});
 								}
 
 								break;
@@ -2432,7 +2506,14 @@ void PDBFileParser::ParseRemarks()
 									std::string psi = vF(27, 35);
 									std::string phi = vF(37, 45);
 
-									getCategory("pdbx_validate_torsion")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", model ? model : 1}, {"auth_comp_id", resNam}, {"auth_asym_id", chainID}, {"auth_seq_id", seqNum}, {"PDB_ins_code", iCode}, {"phi", phi}, {"psi", psi}});
+									getCategory("pdbx_validate_torsion")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", model ? model : 1},
+									{"auth_comp_id", resNam},
+									{"auth_asym_id", chainID},
+									{"auth_seq_id", seqNum},
+									{"PDB_ins_code", iCode},
+									{"phi", phi},
+									{"psi", psi}});
 								}
 								break;
 
@@ -2463,7 +2544,17 @@ void PDBFileParser::ParseRemarks()
 
 									std::string omega = vF(54, 60);
 
-									getCategory("pdbx_validate_peptide_omega")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", model ? model : 1}, {"auth_comp_id_1", resNam1}, {"auth_asym_id_1", chainID1}, {"auth_seq_id_1", seqNum1}, {"PDB_ins_code_1", iCode1}, {"auth_comp_id_2", resNam2}, {"auth_asym_id_2", chainID2}, {"auth_seq_id_2", seqNum2}, {"PDB_ins_code_2", iCode2}, {"omega", omega}});
+									getCategory("pdbx_validate_peptide_omega")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", model ? model : 1},
+									{"auth_comp_id_1", resNam1},
+									{"auth_asym_id_1", chainID1},
+									{"auth_seq_id_1", seqNum1},
+									{"PDB_ins_code_1", iCode1},
+									{"auth_comp_id_2", resNam2},
+									{"auth_asym_id_2", chainID2},
+									{"auth_seq_id_2", seqNum2},
+									{"PDB_ins_code_2", iCode2},
+									{"omega", omega}});
 								}
 								break;
 
@@ -2486,7 +2577,14 @@ void PDBFileParser::ParseRemarks()
 									std::string rmsd = vF(32, 36);
 									std::string type = vS(41);
 
-									getCategory("pdbx_validate_planes")->emplace({{"id", std::to_string(++id)}, {"PDB_model_num", model ? model : 1}, {"auth_comp_id", resNam}, {"auth_asym_id", chainID}, {"auth_seq_id", seqNum}, {"PDB_ins_code", iCode}, {"rmsd", rmsd}, {"type", type}});
+									getCategory("pdbx_validate_planes")->emplace({{"id", std::to_string(++id)},
+									{"PDB_model_num", model ? model : 1},
+									{"auth_comp_id", resNam},
+									{"auth_asym_id", chainID},
+									{"auth_seq_id", seqNum},
+									{"PDB_ins_code", iCode},
+									{"rmsd", rmsd},
+									{"type", type}});
 								}
 								break;
 
@@ -2566,7 +2664,13 @@ void PDBFileParser::ParseRemarks()
 							throw std::runtime_error("Invalid REMARK 800, no SITE record for id " + id);
 
 						// next record, store what we have
-						getCategory("struct_site")->emplace({{"id", id}, {"details", desc}, {"pdbx_auth_asym_id", pdbxAuthAsymID}, {"pdbx_auth_comp_id", pdbxAuthCompID}, {"pdbx_auth_seq_id", pdbxAuthSeqID}, {"pdbx_num_residues", site->vI(16, 17)}, {"pdbx_evidence_code", evidence}});
+						getCategory("struct_site")->emplace({{"id", id},
+						{"details", desc},
+						{"pdbx_auth_asym_id", pdbxAuthAsymID},
+						{"pdbx_auth_comp_id", pdbxAuthCompID},
+						{"pdbx_auth_seq_id", pdbxAuthSeqID},
+						{"pdbx_num_residues", site->vI(16, 17)},
+						{"pdbx_evidence_code", evidence}});
 					};
 
 					for (; mRec->is("REMARK 800"); GetNextRecord())
@@ -2580,7 +2684,7 @@ void PDBFileParser::ParseRemarks()
 							case sStart:
 								if (s == "SITE")
 									state = sID;
-								else if (cif::VERBOSE)
+								else if (cif::VERBOSE > 0)
 									throw std::runtime_error("Invalid REMARK 800 record, expected SITE");
 								break;
 
@@ -2590,7 +2694,7 @@ void PDBFileParser::ParseRemarks()
 									id = m[1].str();
 									state = sEvidence;
 								}
-								else if (cif::VERBOSE)
+								else if (cif::VERBOSE > 0)
 									throw std::runtime_error("Invalid REMARK 800 record, expected SITE_IDENTIFIER");
 								break;
 
@@ -2600,7 +2704,7 @@ void PDBFileParser::ParseRemarks()
 									evidence = m[1].str();
 									state = sDesc;
 								}
-								else if (cif::VERBOSE)
+								else if (cif::VERBOSE > 0)
 									throw std::runtime_error("Invalid REMARK 800 record, expected SITE_IDENTIFIER");
 								break;
 
@@ -2680,7 +2784,8 @@ void PDBFileParser::ParseRemarks()
 						GetNextRecord();
 					}
 
-					getCategory("pdbx_database_remark")->emplace({{"id", remarkNr}, {"text", s.str()}});
+					getCategory("pdbx_database_remark")->emplace({{"id", remarkNr},
+					{"text", s.str()}});
 
 					break;
 				}
@@ -2694,7 +2799,10 @@ void PDBFileParser::ParseRemarks()
 
 	if (not(compoundDetails.empty() and sequenceDetails.empty() and sourceDetails.empty()))
 	{
-		getCategory("pdbx_entry_details")->emplace({{"entry_id", mStructureID}, {"compound_details", compoundDetails}, {"sequence_details", sequenceDetails}, {"source_details", sourceDetails}});
+		getCategory("pdbx_entry_details")->emplace({{"entry_id", mStructureID},
+		{"compound_details", compoundDetails},
+		{"sequence_details", sequenceDetails},
+		{"source_details", sourceDetails}});
 	}
 
 	// store remark 200 info (special case)
@@ -2774,7 +2882,10 @@ void PDBFileParser::ParseRemark200()
 		if (mRemark200[sw.b].empty())
 			continue;
 
-		getCategory("software")->emplace({{"name", mRemark200[sw.b]}, {"classification", sw.a}, {"version", "."}, {"pdbx_ordinal", mNextSoftwareOrd++}});
+		getCategory("software")->emplace({{"name", mRemark200[sw.b]},
+		{"classification", sw.a},
+		{"version", "."},
+		{"pdbx_ordinal", mNextSoftwareOrd++}});
 	}
 
 	std::string scatteringType;
@@ -2804,7 +2915,7 @@ void PDBFileParser::ParseRemark200()
 		collectionDate = pdb2cifDate(rm200("DATE OF DATA COLLECTION", diffrnNr), ec);
 		if (ec)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << ec.message() << " for pdbx_collection_date" << std::endl;
 
 			// The date field can become truncated when multiple values are available
@@ -2812,10 +2923,19 @@ void PDBFileParser::ParseRemark200()
 				collectionDate.clear();
 		}
 
-		getCategory("diffrn_detector")->emplace({{"diffrn_id", diffrnNr}, {"detector", rm200("DETECTOR TYPE", diffrnNr)}, {"type", rm200("DETECTOR MANUFACTURER", diffrnNr)}, {"pdbx_collection_date", collectionDate}, {"details", rm200("OPTICS", diffrnNr)}});
+		getCategory("diffrn_detector")->emplace({{"diffrn_id", diffrnNr},
+		{"detector", rm200("DETECTOR TYPE", diffrnNr)},
+		{"type", rm200("DETECTOR MANUFACTURER", diffrnNr)},
+		{"pdbx_collection_date", collectionDate},
+		{"details", rm200("OPTICS", diffrnNr)}});
 
 		if (inRM200({"MONOCHROMATIC OR LAUE (M/L)", "MONOCHROMATOR", "DIFFRACTION PROTOCOL"}) or not scatteringType.empty())
-			getCategory("diffrn_radiation")->emplace({{"diffrn_id", diffrnNr}, {"wavelength_id", 1}, {"pdbx_monochromatic_or_laue_m_l", rm200("MONOCHROMATIC OR LAUE (M/L)", diffrnNr)}, {"monochromator", rm200("MONOCHROMATOR", diffrnNr)}, {"pdbx_diffrn_protocol", rm200("DIFFRACTION PROTOCOL", diffrnNr)}, {"pdbx_scattering_type", scatteringType}});
+			getCategory("diffrn_radiation")->emplace({{"diffrn_id", diffrnNr},
+			{"wavelength_id", 1},
+			{"pdbx_monochromatic_or_laue_m_l", rm200("MONOCHROMATIC OR LAUE (M/L)", diffrnNr)},
+			{"monochromator", rm200("MONOCHROMATOR", diffrnNr)},
+			{"pdbx_diffrn_protocol", rm200("DIFFRACTION PROTOCOL", diffrnNr)},
+			{"pdbx_scattering_type", scatteringType}});
 
 		std::vector<std::string> wavelengths;
 		std::string wl = rm200("WAVELENGTH OR RANGE (A)", diffrnNr);
@@ -2856,7 +2976,9 @@ void PDBFileParser::ParseRemark200()
 		if (ba::ends_with(wl, "A"))
 			wl.erase(wl.length() - 1, 1);
 
-		getCategory("diffrn_radiation_wavelength")->emplace({{"id", wavelengthNr++}, {"wavelength", wl.empty() ? "." : wl}, {"wt", "1.0"}});
+		getCategory("diffrn_radiation_wavelength")->emplace({{"id", wavelengthNr++},
+		{"wavelength", wl.empty() ? "." : wl},
+		{"wt", "1.0"}});
 	}
 
 	if (inRM200({"METHOD USED TO DETERMINE THE STRUCTURE", "STARTING MODEL"}))
@@ -2899,12 +3021,20 @@ void PDBFileParser::ParseRemark200()
 
 	if (inRM200({"HIGHEST RESOLUTION SHELL, RANGE HIGH (A)"})) // that one field is mandatory...
 	{
-		getCategory("reflns_shell")->emplace({{"d_res_high", mRemark200["HIGHEST RESOLUTION SHELL, RANGE HIGH (A)"]}, {"d_res_low", mRemark200["HIGHEST RESOLUTION SHELL, RANGE LOW (A)"]}, {"percent_possible_all", mRemark200["COMPLETENESS FOR SHELL (%)"]}, {"Rmerge_I_obs", mRemark200["R MERGE FOR SHELL (I)"]}, {"pdbx_Rsym_value", mRemark200["R SYM FOR SHELL (I)"]}, {"meanI_over_sigI_obs", mRemark200["<I/SIGMA(I)> FOR SHELL"]}, {"pdbx_redundancy", mRemark200["DATA REDUNDANCY IN SHELL"]}, {"pdbx_ordinal", 1}, {"pdbx_diffrn_id", 1}});
+		getCategory("reflns_shell")->emplace({{"d_res_high", mRemark200["HIGHEST RESOLUTION SHELL, RANGE HIGH (A)"]},
+		{"d_res_low", mRemark200["HIGHEST RESOLUTION SHELL, RANGE LOW (A)"]},
+		{"percent_possible_all", mRemark200["COMPLETENESS FOR SHELL (%)"]},
+		{"Rmerge_I_obs", mRemark200["R MERGE FOR SHELL (I)"]},
+		{"pdbx_Rsym_value", mRemark200["R SYM FOR SHELL (I)"]},
+		{"meanI_over_sigI_obs", mRemark200["<I/SIGMA(I)> FOR SHELL"]},
+		{"pdbx_redundancy", mRemark200["DATA REDUNDANCY IN SHELL"]},
+		{"pdbx_ordinal", 1},
+		{"pdbx_diffrn_id", 1}});
 	}
 	else if (inRM200({"HIGHEST RESOLUTION SHELL, RANGE LOW (A)", "COMPLETENESS FOR SHELL (%)",
 	                  "R MERGE FOR SHELL (I)", "R SYM FOR SHELL (I)", "<I/SIGMA(I)> FOR SHELL", "DATA REDUNDANCY IN SHELL"}))
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "Not writing reflns_shell record since d_res_high is missing" << std::endl;
 	}
 }
@@ -3091,7 +3221,11 @@ void PDBFileParser::ParseRemark350()
 							else
 								details = "author_and_software_defined_assembly";
 
-							getCategory("pdbx_struct_assembly")->emplace({{"id", biomolecule}, {"details", details}, {"method_details", values["SOFTWARE USED"]}, {"oligomeric_details", oligomer}, {"oligomeric_count", count > 0 ? std::to_string(count) : ""}});
+							getCategory("pdbx_struct_assembly")->emplace({{"id", biomolecule},
+							{"details", details},
+							{"method_details", values["SOFTWARE USED"]},
+							{"oligomeric_details", oligomer},
+							{"oligomeric_count", count > 0 ? std::to_string(count) : ""}});
 
 							auto cat = getCategory("pdbx_struct_assembly_prop");
 
@@ -3115,21 +3249,23 @@ void PDBFileParser::ParseRemark350()
 
 						boost::format fmt("%12.10f");
 
-						getCategory("pdbx_struct_oper_list")->emplace({{"id", operID}, {"type", mat == std::vector<double>{1, 0, 0, 0, 1, 0, 0, 0, 1} and vec == std::vector<double>{0, 0, 0} ? "identity operation" : "crystal symmetry operation"},
-						                                               //										{ "name", "" },
-						                                               //										{ "symmetryOperation", "" },
-						                                               {"matrix[1][1]", (fmt % mat[0]).str()},
-						                                               {"matrix[1][2]", (fmt % mat[1]).str()},
-						                                               {"matrix[1][3]", (fmt % mat[2]).str()},
-						                                               {"vector[1]", (fmt % vec[0]).str()},
-						                                               {"matrix[2][1]", (fmt % mat[3]).str()},
-						                                               {"matrix[2][2]", (fmt % mat[4]).str()},
-						                                               {"matrix[2][3]", (fmt % mat[5]).str()},
-						                                               {"vector[2]", (fmt % vec[1]).str()},
-						                                               {"matrix[3][1]", (fmt % mat[6]).str()},
-						                                               {"matrix[3][2]", (fmt % mat[7]).str()},
-						                                               {"matrix[3][3]", (fmt % mat[8]).str()},
-						                                               {"vector[3]", (fmt % vec[2]).str()}});
+						getCategory("pdbx_struct_oper_list")->emplace({
+							{"id", operID},
+							{"type", mat == std::vector<double>{1, 0, 0, 0, 1, 0, 0, 0, 1} and vec == std::vector<double>{0, 0, 0} ? "identity operation" : "crystal symmetry operation"},
+							// { "name", "" },
+							// { "symmetryOperation", "" },
+							{"matrix[1][1]", (fmt % mat[0]).str()},
+							{"matrix[1][2]", (fmt % mat[1]).str()},
+							{"matrix[1][3]", (fmt % mat[2]).str()},
+							{"vector[1]", (fmt % vec[0]).str()},
+							{"matrix[2][1]", (fmt % mat[3]).str()},
+							{"matrix[2][2]", (fmt % mat[4]).str()},
+							{"matrix[2][3]", (fmt % mat[5]).str()},
+							{"vector[2]", (fmt % vec[1]).str()},
+							{"matrix[3][1]", (fmt % mat[6]).str()},
+							{"matrix[3][2]", (fmt % mat[7]).str()},
+							{"matrix[3][3]", (fmt % mat[8]).str()},
+							{"vector[3]", (fmt % vec[2]).str()}});
 
 						mat.clear();
 						vec.clear();
@@ -3140,7 +3276,9 @@ void PDBFileParser::ParseRemark350()
 					if (not(vec.empty() and mat.empty()))
 						throw std::runtime_error("Invalid REMARK 350");
 
-					getCategory("pdbx_struct_assembly_gen")->emplace({{"assembly_id", biomolecule}, {"oper_expression", ba::join(operExpression, ",")}, {"asym_id_list", ba::join(asymIdList, ",")}});
+					getCategory("pdbx_struct_assembly_gen")->emplace({{"assembly_id", biomolecule},
+					{"oper_expression", ba::join(operExpression, ",")},
+					{"asym_id_list", ba::join(asymIdList, ",")}});
 
 					biomolecule = stoi(m[1].str());
 					asymIdList.clear();
@@ -3154,7 +3292,9 @@ void PDBFileParser::ParseRemark350()
 
 	if (not operExpression.empty())
 	{
-		getCategory("pdbx_struct_assembly_gen")->emplace({{"assembly_id", biomolecule}, {"oper_expression", ba::join(operExpression, ",")}, {"asym_id_list", ba::join(asymIdList, ",")}});
+		getCategory("pdbx_struct_assembly_gen")->emplace({{"assembly_id", biomolecule},
+		{"oper_expression", ba::join(operExpression, ",")},
+		{"asym_id_list", ba::join(asymIdList, ",")}});
 	}
 
 	mRec = saved;
@@ -3464,7 +3604,7 @@ void PDBFileParser::ConstructEntities()
 			{
 				auto &r = chain.mResiduesSeen[lastResidueIndex + 1];
 
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 				{
 					std::cerr << "Detected residues that cannot be aligned to SEQRES" << std::endl
 							  << "First residue is " << chain.mDbref.chainID << ':' << r.mSeqNum << r.mIcode << std::endl;
@@ -3641,10 +3781,11 @@ void PDBFileParser::ConstructEntities()
 		mAsymID2EntityID[asymID] = entityID;
 
 		getCategory("struct_asym")->emplace({
-			{"id", asymID}, {"pdbx_blank_PDB_chainid_flag", chain.mDbref.chainID == ' ' ? "Y" : "N"},
-			//			pdbx_modified
+			{"id", asymID},
+			{"pdbx_blank_PDB_chainid_flag", chain.mDbref.chainID == ' ' ? "Y" : "N"},
+			// pdbx_modified
 			{"entity_id", entityID},
-			//			details
+			// details
 		});
 
 		int seqNr = 1;
@@ -3708,7 +3849,32 @@ void PDBFileParser::ConstructEntities()
 		{
 			srcMethod = "man";
 
-			getCategory("entity_src_gen")->emplace({{"entity_id", mMolID2EntityID[cmp.mMolID]}, {"pdbx_src_id", structRefID}, {"gene_src_common_name", cmp.mSource["ORGANISM_COMMON"]}, {"pdbx_gene_src_gene", cmp.mSource["GENE"]}, {"gene_src_strain", cmp.mSource["STRAIN"]}, {"gene_src_tissue", cmp.mSource["TISSUE"]}, {"gene_src_tissue_fraction", cmp.mSource["TISSUE_FRACTION"]}, {"pdbx_gene_src_cell_line", cmp.mSource["CELL_LINE"]}, {"pdbx_gene_src_organelle", cmp.mSource["ORGANELLE"]}, {"pdbx_gene_src_cell", cmp.mSource["CELL"]}, {"pdbx_gene_src_cellular_location", cmp.mSource["CELLULAR_LOCATION"]}, {"host_org_common_name", cmp.mSource["EXPRESSION_SYSTEM_COMMON"]}, {"pdbx_gene_src_scientific_name", cmp.mSource["ORGANISM_SCIENTIFIC"]}, {"pdbx_gene_src_ncbi_taxonomy_id", cmp.mSource["ORGANISM_TAXID"]}, {"pdbx_host_org_scientific_name", cmp.mSource["EXPRESSION_SYSTEM"]}, {"pdbx_host_org_ncbi_taxonomy_id", cmp.mSource["EXPRESSION_SYSTEM_TAXID"]}, {"pdbx_host_org_strain", cmp.mSource["EXPRESSION_SYSTEM_STRAIN"]}, {"pdbx_host_org_variant", cmp.mSource["EXPRESSION_SYSTEM_VARIANT"]}, {"pdbx_host_org_cell_line", cmp.mSource["EXPRESSION_SYSTEM_CELL_LINE"]}, {"pdbx_host_org_cellular_location", cmp.mSource["EXPRESSION_SYSTEM_CELLULAR_LOCATION"]}, {"pdbx_host_org_vector_type", cmp.mSource["EXPRESSION_SYSTEM_VECTOR_TYPE"]}, {"pdbx_host_org_vector", cmp.mSource["EXPRESSION_SYSTEM_VECTOR"]}, {"pdbx_host_org_gene", cmp.mSource["EXPRESSION_SYSTEM_GENE"]}, {"plasmid_name", cmp.mSource["EXPRESSION_SYSTEM_PLASMID"]}, {"pdbx_description", cmp.mSource["OTHER_DETAILS"]}});
+			getCategory("entity_src_gen")->emplace({
+				{"entity_id", mMolID2EntityID[cmp.mMolID]},
+				{"pdbx_src_id", structRefID},
+				{"gene_src_common_name", cmp.mSource["ORGANISM_COMMON"]},
+				{"pdbx_gene_src_gene", cmp.mSource["GENE"]},
+				{"gene_src_strain", cmp.mSource["STRAIN"]},
+				{"gene_src_tissue", cmp.mSource["TISSUE"]},
+				{"gene_src_tissue_fraction", cmp.mSource["TISSUE_FRACTION"]},
+				{"pdbx_gene_src_cell_line", cmp.mSource["CELL_LINE"]},
+				{"pdbx_gene_src_organelle", cmp.mSource["ORGANELLE"]},
+				{"pdbx_gene_src_cell", cmp.mSource["CELL"]},
+				{"pdbx_gene_src_cellular_location", cmp.mSource["CELLULAR_LOCATION"]},
+				{"host_org_common_name", cmp.mSource["EXPRESSION_SYSTEM_COMMON"]},
+				{"pdbx_gene_src_scientific_name", cmp.mSource["ORGANISM_SCIENTIFIC"]},
+				{"pdbx_gene_src_ncbi_taxonomy_id", cmp.mSource["ORGANISM_TAXID"]},
+				{"pdbx_host_org_scientific_name", cmp.mSource["EXPRESSION_SYSTEM"]},
+				{"pdbx_host_org_ncbi_taxonomy_id", cmp.mSource["EXPRESSION_SYSTEM_TAXID"]},
+				{"pdbx_host_org_strain", cmp.mSource["EXPRESSION_SYSTEM_STRAIN"]},
+				{"pdbx_host_org_variant", cmp.mSource["EXPRESSION_SYSTEM_VARIANT"]},
+				{"pdbx_host_org_cell_line", cmp.mSource["EXPRESSION_SYSTEM_CELL_LINE"]},
+				{"pdbx_host_org_cellular_location", cmp.mSource["EXPRESSION_SYSTEM_CELLULAR_LOCATION"]},
+				{"pdbx_host_org_vector_type", cmp.mSource["EXPRESSION_SYSTEM_VECTOR_TYPE"]},
+				{"pdbx_host_org_vector", cmp.mSource["EXPRESSION_SYSTEM_VECTOR"]},
+				{"pdbx_host_org_gene", cmp.mSource["EXPRESSION_SYSTEM_GENE"]},
+				{"plasmid_name", cmp.mSource["EXPRESSION_SYSTEM_PLASMID"]},
+				{"pdbx_description", cmp.mSource["OTHER_DETAILS"]}});
 		}
 		else if (not cmp.mSource["ORGANISM_SCIENTIFIC"].empty())
 		{
@@ -3741,7 +3907,10 @@ void PDBFileParser::ConstructEntities()
 
 		if (not cmp.mInfo["SYNONYM"].empty())
 		{
-			getCategory("entity_name_com")->emplace({{"entity_id", mMolID2EntityID[cmp.mMolID]}, {"name", cmp.mInfo["SYNONYM"]}});
+			getCategory("entity_name_com")->emplace({
+				{"entity_id", mMolID2EntityID[cmp.mMolID]},
+				{"name", cmp.mInfo["SYNONYM"]}
+			});
 		}
 
 		std::string desc = cmp.mInfo["MOLECULE"];
@@ -3761,7 +3930,11 @@ void PDBFileParser::ConstructEntities()
 		if (ci != mChains.end() and not ci->mDbref.dbIdCode.empty())
 		{
 			getCategory("struct_ref")->emplace({
-				{"id", structRefID}, {"entity_id", mMolID2EntityID[cmp.mMolID]}, {"db_name", ci->mDbref.database}, {"db_code", ci->mDbref.dbIdCode}, {"pdbx_db_accession", ci->mDbref.dbAccession},
+				{"id", structRefID},
+				{"entity_id", mMolID2EntityID[cmp.mMolID]},
+				{"db_name", ci->mDbref.database},
+				{"db_code", ci->mDbref.dbIdCode},
+				{"pdbx_db_accession", ci->mDbref.dbAccession},
 				//				{ "pdbx_align_begin", ci->mDbref.dbSeqBegin }
 			});
 		}
@@ -3810,7 +3983,23 @@ void PDBFileParser::ConstructEntities()
 				{
 				}
 
-				getCategory("struct_ref_seq")->emplace({{"align_id", structRefSeqAlignID}, {"ref_id", structRefID}, {"pdbx_PDB_id_code", dbref.PDBIDCode}, {"pdbx_strand_id", std::string{chain.mDbref.chainID}}, {"seq_align_beg", seqAlignBeg}, {"pdbx_seq_align_beg_ins_code", insToStr(dbref.insertBegin)}, {"seq_align_end", seqAlignEnd}, {"pdbx_seq_align_end_ins_code", insToStr(dbref.insertEnd)}, {"pdbx_db_accession", dbref.dbAccession}, {"db_align_beg", dbref.dbSeqBegin}, {"pdbx_db_align_beg_ins_code", insToStr(dbref.dbinsBeg)}, {"db_align_end", dbref.dbSeqEnd}, {"pdbx_db_align_end_ins_code", insToStr(dbref.dbinsEnd)}, {"pdbx_auth_seq_align_beg", dbref.seqBegin}, {"pdbx_auth_seq_align_end", dbref.seqEnd}});
+				getCategory("struct_ref_seq")->emplace({
+					{"align_id", structRefSeqAlignID},
+					{"ref_id", structRefID},
+					{"pdbx_PDB_id_code", dbref.PDBIDCode},
+					{"pdbx_strand_id", std::string{chain.mDbref.chainID}},
+					{"seq_align_beg", seqAlignBeg},
+					{"pdbx_seq_align_beg_ins_code", insToStr(dbref.insertBegin)},
+					{"seq_align_end", seqAlignEnd},
+					{"pdbx_seq_align_end_ins_code", insToStr(dbref.insertEnd)},
+					{"pdbx_db_accession", dbref.dbAccession},
+					{"db_align_beg", dbref.dbSeqBegin},
+					{"pdbx_db_align_beg_ins_code", insToStr(dbref.dbinsBeg)},
+					{"db_align_end", dbref.dbSeqEnd},
+					{"pdbx_db_align_end_ins_code", insToStr(dbref.dbinsEnd)},
+					{"pdbx_auth_seq_align_beg", dbref.seqBegin},
+					{"pdbx_auth_seq_align_end", dbref.seqEnd}
+				});
 
 				// write the struct_ref_seq_dif
 				for (auto &seqadv : mSeqadvs)
@@ -3825,14 +4014,28 @@ void PDBFileParser::ConstructEntities()
 					tie(asym, labelSeq, std::ignore) = MapResidue(seqadv.chainID, seqadv.seqNum, seqadv.iCode, ec);
 					if (ec)
 					{
-						if (cif::VERBOSE)
+						if (cif::VERBOSE > 0)
 							std::cerr << "dropping unmatched SEQADV record" << std::endl;
 						continue;
 					}
 
 					seqNum = std::to_string(labelSeq);
 
-					getCategory("struct_ref_seq_dif")->emplace({{"align_id", structRefSeqAlignID}, {"pdbx_PDB_id_code", dbref.PDBIDCode}, {"mon_id", seqadv.resName}, {"pdbx_pdb_strand_id", seqadv.chainID}, {"seq_num", seqNum}, {"pdbx_pdb_ins_code", seqadv.iCode == ' ' ? std::string{} : std::string{seqadv.iCode}}, {"pdbx_seq_db_name", seqadv.database}, {"pdbx_seq_db_accession_code", seqadv.dbAccession}, {"db_mon_id", seqadv.dbRes}, {"pdbx_seq_db_seq_num", seqadv.dbSeq}, {"details", seqadv.conflict}, {"pdbx_auth_seq_num", seqadv.seqNum}, {"pdbx_ordinal", ++mPdbxDifOrdinal}});
+					getCategory("struct_ref_seq_dif")->emplace({
+						{"align_id", structRefSeqAlignID},
+						{"pdbx_PDB_id_code", dbref.PDBIDCode},
+						{"mon_id", seqadv.resName},
+						{"pdbx_pdb_strand_id", seqadv.chainID},
+						{"seq_num", seqNum},
+						{"pdbx_pdb_ins_code", seqadv.iCode == ' ' ? std::string{} : std::string{seqadv.iCode}},
+						{"pdbx_seq_db_name", seqadv.database},
+						{"pdbx_seq_db_accession_code", seqadv.dbAccession},
+						{"db_mon_id", seqadv.dbRes},
+						{"pdbx_seq_db_seq_num", seqadv.dbSeq},
+						{"details", seqadv.conflict},
+						{"pdbx_auth_seq_num", seqadv.seqNum},
+						{"pdbx_ordinal", ++mPdbxDifOrdinal}
+					});
 				}
 			}
 
@@ -3932,15 +4135,23 @@ void PDBFileParser::ConstructEntities()
 		else if (mightBeDNA and not mightBePolyPeptide)
 			type = "polyribonucleotide";
 
-		getCategory("entity_poly")->emplace({{"entity_id", mMolID2EntityID[cmp.mMolID]}, {"pdbx_seq_one_letter_code", seq}, {"pdbx_seq_one_letter_code_can", seqCan}, {"nstd_monomer", (nstdMonomer ? "yes" : "no")}, {"pdbx_strand_id", ba::join(chains, ",")}, {"nstd_linkage", nonstandardLinkage ? "yes" : "no"}, {"type", type}});
+		getCategory("entity_poly")->emplace({{"entity_id", mMolID2EntityID[cmp.mMolID]},
+		{"pdbx_seq_one_letter_code", seq},
+		{"pdbx_seq_one_letter_code_can", seqCan},
+		{"nstd_monomer", (nstdMonomer ? "yes" : "no")},
+		{"pdbx_strand_id", ba::join(chains, ",")},
+		{"nstd_linkage", nonstandardLinkage ? "yes" : "no"},
+		{"type", type}});
 	}
 
 	if (not(structTitle.empty() and structDescription.empty()))
 	{
-		getCategory("struct")->emplace({{"entry_id", mStructureID},
-		                                {"title", ba::join(structTitle, ", ")},
-		                                {"pdbx_descriptor", ba::join(structDescription, ", ")},
-		                                {"pdbx_model_type_details", mModelTypeDetails}});
+		getCategory("struct")->emplace({
+			{"entry_id", mStructureID},
+			{"title", ba::join(structTitle, ", ")},
+			{"pdbx_descriptor", ba::join(structDescription, ", ")},
+			{"pdbx_model_type_details", mModelTypeDetails}
+		});
 	}
 
 	// build sugar trees first
@@ -4026,7 +4237,9 @@ void PDBFileParser::ConstructEntities()
 			std::string name = mHetnams[hetID];
 			if (name.empty() and hetID == mWaterHetID)
 				name = "water";
-			getCategory("pdbx_entity_nonpoly")->emplace({{"entity_id", entityID}, {"name", name}, {"comp_id", hetID}});
+			getCategory("pdbx_entity_nonpoly")->emplace({{"entity_id", entityID},
+			{"name", name},
+			{"comp_id", hetID}});
 		}
 
 		// create an asym for this het/chain combo, if needed
@@ -4063,7 +4276,8 @@ void PDBFileParser::ConstructEntities()
 			{
 				writtenAsyms.insert(asymID);
 				getCategory("struct_asym")->emplace({
-					{"id", asymID}, {"pdbx_blank_PDB_chainid_flag", het.chainID == ' ' ? "Y" : "N"},
+					{"id", asymID},
+					{"pdbx_blank_PDB_chainid_flag", het.chainID == ' ' ? "Y" : "N"},
 					//					pdbx_modified
 					{"entity_id", mHet2EntityID[hetID]},
 					//					details
@@ -4078,12 +4292,18 @@ void PDBFileParser::ConstructEntities()
 		if (iCode.empty())
 			iCode = {'.'};
 
-		getCategory("pdbx_nonpoly_scheme")->emplace({{"asym_id", asymID}, {"entity_id", mHet2EntityID[hetID]}, {"mon_id", hetID}, {"ndb_seq_num", seqNr}, {"pdb_seq_num", het.seqNum},
-		                                             //			{ "auth_seq_num", het.seqNum },	// ????
-		                                             {"pdb_mon_id", hetID},
-		                                             //			{ "auth_mon_id", hetID },
-		                                             {"pdb_strand_id", std::string{het.chainID}},
-		                                             {"pdb_ins_code", iCode}});
+		getCategory("pdbx_nonpoly_scheme")->emplace({
+			{"asym_id", asymID},
+			{"entity_id", mHet2EntityID[hetID]},
+			{"mon_id", hetID},
+			{"ndb_seq_num", seqNr},
+			{"pdb_seq_num", het.seqNum},
+			// { "auth_seq_num", het.seqNum },	// ????
+			{"pdb_mon_id", hetID},
+			// { "auth_mon_id", hetID },
+			{"pdb_strand_id", std::string{het.chainID}},
+			{"pdb_ins_code", iCode}
+		});
 
 		// mapping needed?
 		mChainSeq2AsymSeq[std::make_tuple(het.chainID, het.seqNum, het.iCode)] = std::make_tuple(asymID, seqNr, false);
@@ -4108,12 +4328,23 @@ void PDBFileParser::ConstructEntities()
 		tie(asymID, seq, std::ignore) = MapResidue(chainID, seqNum, iCode, ec);
 		if (ec) // no need to write a modres if it could not be found
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "dropping unmapped MODRES record" << std::endl;
 			continue;
 		}
 
-		getCategory("pdbx_struct_mod_residue")->emplace({{"id", modResID++}, {"label_asym_id", asymID}, {"label_seq_id", seq}, {"label_comp_id", resName}, {"auth_asym_id", std::string(1, chainID)}, {"auth_seq_id", seqNum}, {"auth_comp_id", resName}, {"PDB_ins_code", iCode == ' ' ? "" : std::string{iCode}}, {"parent_comp_id", stdRes}, {"details", comment}});
+		getCategory("pdbx_struct_mod_residue")->emplace({
+			{"id", modResID++},
+			{"label_asym_id", asymID},
+			{"label_seq_id", seq},
+			{"label_comp_id", resName},
+			{"auth_asym_id", std::string(1, chainID)},
+			{"auth_seq_id", seqNum},
+			{"auth_comp_id", resName},
+			{"PDB_ins_code", iCode == ' ' ? "" : std::string{iCode}},
+			{"parent_comp_id", stdRes},
+			{"details", comment}
+		});
 
 		modResSet.insert(resName);
 	}
@@ -4162,7 +4393,12 @@ void PDBFileParser::ConstructEntities()
 		if (modResSet.count(cc))
 			nstd = "n";
 
-		getCategory("chem_comp")->emplace({{"id", cc}, {"name", name}, {"formula", formula}, {"formula_weight", formulaWeight}, {"mon_nstd_flag", nstd}, {"type", type}});
+		getCategory("chem_comp")->emplace({{"id", cc},
+		{"name", name},
+		{"formula", formula},
+		{"formula_weight", formulaWeight},
+		{"mon_nstd_flag", nstd},
+		{"type", type}});
 	}
 
 	getCategory("chem_comp")->reorderByIndex();
@@ -4188,23 +4424,46 @@ void PDBFileParser::ConstructEntities()
 		tie(asymID, seqNr, isPolymer) = MapResidue(unobs.chain, unobs.seq, unobs.iCode, ec);
 		if (ec)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "error mapping unobserved residue" << std::endl;
 			continue;
 		}
 
 		if (unobs.atoms.empty())
 		{
-			getCategory("pdbx_unobs_or_zero_occ_residues")->emplace({{"id", std::to_string(++idRes)}, {"polymer_flag", isPolymer ? "Y" : "N"}, {"occupancy_flag", 1}, {"PDB_model_num", unobs.modelNr ? unobs.modelNr : 1}, {"auth_asym_id", std::string{unobs.chain}}, {"auth_comp_id", unobs.res}, {"auth_seq_id", unobs.seq}, {"PDB_ins_code", unobs.iCode == ' ' ? "" : std::string{unobs.iCode}}, {"label_asym_id", asymID}, {"label_comp_id", compID}, // TODO: change to correct comp_id
-			                                                         {"label_seq_id", seqNr > 0 ? std::to_string(seqNr) : ""}});
+			getCategory("pdbx_unobs_or_zero_occ_residues")->emplace({
+				{"id", std::to_string(++idRes)},
+				{"polymer_flag", isPolymer ? "Y" : "N"},
+				{"occupancy_flag", 1},
+				{"PDB_model_num", unobs.modelNr ? unobs.modelNr : 1},
+				{"auth_asym_id", std::string{unobs.chain}},
+				{"auth_comp_id", unobs.res},
+				{"auth_seq_id", unobs.seq},
+				{"PDB_ins_code", unobs.iCode == ' ' ? "" : std::string{unobs.iCode}},
+				{"label_asym_id", asymID},
+				{"label_comp_id", compID}, // TODO: change to correct comp_id
+				{"label_seq_id", seqNr > 0 ? std::to_string(seqNr) : ""}
+			});
 		}
 		else
 		{
 			for (auto &atom : unobs.atoms)
 			{
-				getCategory("pdbx_unobs_or_zero_occ_atoms")->emplace({{"id", std::to_string(++idAtom)}, {"polymer_flag", isPolymer ? "Y" : "N"}, {"occupancy_flag", 1}, {"PDB_model_num", unobs.modelNr ? unobs.modelNr : 1}, {"auth_asym_id", std::string{unobs.chain}}, {"auth_comp_id", unobs.res}, {"auth_seq_id", unobs.seq}, {"PDB_ins_code", unobs.iCode == ' ' ? "" : std::string{unobs.iCode}}, {"auth_atom_id", atom}, {"label_asym_id", asymID}, {"label_comp_id", compID}, // TODO: change to correct comp_id
-				                                                      {"label_seq_id", seqNr > 0 ? std::to_string(seqNr) : ""},
-				                                                      {"label_atom_id", atom}});
+				getCategory("pdbx_unobs_or_zero_occ_atoms")->emplace({
+					{"id", std::to_string(++idAtom)},
+					{"polymer_flag", isPolymer ? "Y" : "N"},
+					{"occupancy_flag", 1},
+					{"PDB_model_num", unobs.modelNr ? unobs.modelNr : 1},
+					{"auth_asym_id", std::string{unobs.chain}},
+					{"auth_comp_id", unobs.res},
+					{"auth_seq_id", unobs.seq},
+					{"PDB_ins_code", unobs.iCode == ' ' ? "" : std::string{unobs.iCode}},
+					{"auth_atom_id", atom},
+					{"label_asym_id", asymID},
+					{"label_comp_id", compID}, // TODO: change to correct comp_id
+					{"label_seq_id", seqNr > 0 ? std::to_string(seqNr) : ""},
+					{"label_atom_id", atom}
+				});
 			}
 		}
 	}
@@ -4288,14 +4547,18 @@ void PDBFileParser::ConstructSugarTrees(int &asymNr)
 					                                {"src_method", "man"},
 					                                {"pdbx_description", branchName}});
 
-					getCategory("pdbx_entity_branch")->emplace({{"entity_id", entityID}, {"type", "oligosaccharide"}});
+					getCategory("pdbx_entity_branch")->emplace({{"entity_id", entityID},
+					{"type", "oligosaccharide"}});
 
 					int num = 0;
 					std::map<ATOM_REF, int> branch_list;
 
 					for (auto &s : sugarTree)
 					{
-						getCategory("pdbx_entity_branch_list")->emplace({{"entity_id", entityID}, {"comp_id", s.c1.resName}, {"num", ++num}, {"hetero", ci.size() == 1 ? "n" : "y"}});
+						getCategory("pdbx_entity_branch_list")->emplace({{"entity_id", entityID},
+						{"comp_id", s.c1.resName},
+						{"num", ++num},
+						{"hetero", ci.size() == 1 ? "n" : "y"}});
 
 						branch_list[s.c1] = num;
 					}
@@ -4327,7 +4590,10 @@ void PDBFileParser::ConstructSugarTrees(int &asymNr)
 
 				std::string asymID = cif::cifIdForNumber(asymNr++);
 
-				getCategory("struct_asym")->emplace({{"id", asymID}, {"pdbx_blank_PDB_chainid_flag", si->chainID == ' ' ? "Y" : "N"}, {"pdbx_modified", "N"}, {"entity_id", entityID}});
+				getCategory("struct_asym")->emplace({{"id", asymID},
+				{"pdbx_blank_PDB_chainid_flag", si->chainID == ' ' ? "Y" : "N"},
+				{"pdbx_modified", "N"},
+				{"entity_id", entityID}});
 
 				std::string iCode{si->iCode};
 				ba::trim(iCode);
@@ -4337,7 +4603,19 @@ void PDBFileParser::ConstructSugarTrees(int &asymNr)
 				int num = 0;
 				for (auto s : sugarTree)
 				{
-					getCategory("pdbx_branch_scheme")->emplace({{"asym_id", asymID}, {"entity_id", entityID}, {"mon_id", s.c1.resName}, {"num", ++num}, {"pdb_asym_id", asymID}, {"pdb_mon_id", s.c1.resName}, {"pdb_seq_num", num}, {"auth_asym_id", std::string{s.c1.chainID}}, {"auth_mon_id", s.next.resName}, {"auth_seq_num", s.c1.resSeq}, {"hetero", ci.size() == 1 ? "n" : "y"}});
+					getCategory("pdbx_branch_scheme")->emplace({
+						{"asym_id", asymID},
+						{"entity_id", entityID},
+						{"mon_id", s.c1.resName},
+						{"num", ++num},
+						{"pdb_asym_id", asymID},
+						{"pdb_mon_id", s.c1.resName},
+						{"pdb_seq_num", num},
+						{"auth_asym_id", std::string{s.c1.chainID}},
+						{"auth_mon_id", s.next.resName},
+						{"auth_seq_num", s.c1.resSeq},
+						{"hetero", ci.size() == 1 ? "n" : "y"}
+					});
 
 					auto k = std::make_tuple(s.c1.chainID, s.c1.resSeq, s.c1.iCode);
 					assert(mChainSeq2AsymSeq.count(k) == 0);
@@ -4407,7 +4685,7 @@ void PDBFileParser::ParseSecondaryStructure()
 
 		if (ec)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Could not map residue for HELIX " << vI(8, 10) << std::endl;
 		}
 		else
@@ -4506,7 +4784,10 @@ void PDBFileParser::ParseSecondaryStructure()
 
 		if (sense != 0)
 		{
-			getCategory("struct_sheet_order")->emplace({{"sheet_id", sheetID}, {"range_id_1", rangeID}, {"range_id_2", rangeID + 1}, {"sense", sense == -1 ? "anti-parallel" : "parallel"}});
+			getCategory("struct_sheet_order")->emplace({{"sheet_id", sheetID},
+			{"range_id_1", rangeID},
+			{"range_id_2", rangeID + 1},
+			{"sense", sense == -1 ? "anti-parallel" : "parallel"}});
 		}
 
 		std::string begAsymID, endAsymID;
@@ -4519,7 +4800,7 @@ void PDBFileParser::ParseSecondaryStructure()
 
 		if (ec)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Dropping SHEET record " << vI(8, 10) << std::endl;
 		}
 		else
@@ -4555,21 +4836,34 @@ void PDBFileParser::ParseSecondaryStructure()
 
 				if (ec)
 				{
-					if (cif::VERBOSE)
+					if (cif::VERBOSE > 0)
 						std::cerr << "skipping unmatched pdbx_struct_sheet_hbond record" << std::endl;
 				}
 				else
-					getCategory("pdbx_struct_sheet_hbond")->emplace({{"sheet_id", sheetID}, {"range_id_1", rangeID}, {"range_id_2", rangeID + 1}, {"range_1_label_atom_id", vS(57, 60)}, {"range_1_label_comp_id", vS(61, 63)}, {"range_1_label_asym_id", r1AsymID}, {"range_1_label_seq_id", r1Seq}, {"range_1_PDB_ins_code", vS(70, 70)}, {"range_1_auth_atom_id", vS(57, 60)}, {"range_1_auth_comp_id", vS(61, 63)}, {"range_1_auth_asym_id", vS(65, 65)}, {"range_1_auth_seq_id", vI(66, 69)},
-
-					                                                 {"range_2_label_atom_id", vS(42, 45)},
-					                                                 {"range_2_label_comp_id", vS(46, 48)},
-					                                                 {"range_2_label_asym_id", r2AsymID},
-					                                                 {"range_2_label_seq_id", r2Seq},
-					                                                 {"range_2_PDB_ins_code", vS(55, 55)},
-					                                                 {"range_2_auth_atom_id", vS(42, 45)},
-					                                                 {"range_2_auth_comp_id", vS(46, 48)},
-					                                                 {"range_2_auth_asym_id", vS(50, 50)},
-					                                                 {"range_2_auth_seq_id", vI(51, 54)}});
+					getCategory("pdbx_struct_sheet_hbond")->emplace({
+						{"sheet_id", sheetID},
+						{"range_id_1", rangeID},
+						{"range_id_2", rangeID + 1},
+						{"range_1_label_atom_id", vS(57, 60)},
+						{"range_1_label_comp_id", vS(61, 63)},
+						{"range_1_label_asym_id", r1AsymID},
+						{"range_1_label_seq_id", r1Seq},
+						{"range_1_PDB_ins_code", vS(70, 70)},
+						{"range_1_auth_atom_id", vS(57, 60)},
+						{"range_1_auth_comp_id", vS(61, 63)},
+						{"range_1_auth_asym_id", vS(65, 65)},
+						{"range_1_auth_seq_id", vI(66, 69)},
+
+						{"range_2_label_atom_id", vS(42, 45)},
+						{"range_2_label_comp_id", vS(46, 48)},
+						{"range_2_label_asym_id", r2AsymID},
+						{"range_2_label_seq_id", r2Seq},
+						{"range_2_PDB_ins_code", vS(55, 55)},
+						{"range_2_auth_atom_id", vS(42, 45)},
+						{"range_2_auth_comp_id", vS(46, 48)},
+						{"range_2_auth_asym_id", vS(50, 50)},
+						{"range_2_auth_seq_id", vI(51, 54)}
+					});
 			}
 
 			if (sense != 0)
@@ -4642,7 +4936,7 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 
 			if (ec)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Dropping SSBOND " << vI(8, 10) << std::endl;
 				continue;
 			}
@@ -4663,7 +4957,7 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 			}
 			catch (const std::exception &ex)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Dropping SSBOND " << vI(8, 10) << " due to invalid symmetry operation" << std::endl;
 				continue;
 			}
@@ -4708,7 +5002,7 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 
 		if (mRec->is("LINK  ") or mRec->is("LINKR "))
 		{
-			if (cif::VERBOSE and mRec->is("LINKR "))
+			if (cif::VERBOSE > 0 and mRec->is("LINKR "))
 				std::cerr << "Accepting non-standard LINKR record, but ignoring extra information" << std::endl;
 
 			//	 1 -  6         Record name    "LINK  "
@@ -4761,7 +5055,7 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 
 			if (ec)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Dropping LINK record at line " << mRec->mLineNr << std::endl;
 				continue;
 			}
@@ -4777,7 +5071,7 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 				}
 				catch (const std::invalid_argument &)
 				{
-					if (cif::VERBOSE)
+					if (cif::VERBOSE > 0)
 						std::cerr << "Distance value '" << distance << "' is not a valid float in LINK record" << std::endl;
 					swap(ccp4LinkID, distance); // assume this is a ccp4_link_id... oh really?
 				}
@@ -4793,12 +5087,13 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 			}
 			catch (const std::exception &ex)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Dropping LINK record at line " << mRec->mLineNr << " due to invalid symmetry operation" << std::endl;
 				continue;
 			}
 
-			getCategory("struct_conn")->emplace({{"id", type + std::to_string(linkNr)}, {"conn_type_id", type},
+			getCategory("struct_conn")->emplace({{"id", type + std::to_string(linkNr)},
+			{"conn_type_id", type},
 
 			                                     // { "ccp4_link_id", ccp4LinkID },
 
@@ -4863,7 +5158,7 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 
 			if (ec)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Dropping CISPEP record at line " << mRec->mLineNr << std::endl;
 				continue;
 			}
@@ -4871,7 +5166,24 @@ void PDBFileParser::ParseConnectivtyAnnotation()
 			std::string iCode1str = iCode1 == ' ' ? std::string() : std::string{iCode1};
 			std::string iCode2str = iCode2 == ' ' ? std::string() : std::string{iCode2};
 
-			getCategory("struct_mon_prot_cis")->emplace({{"pdbx_id", serNum}, {"label_comp_id", pep1}, {"label_seq_id", lResSeq1}, {"label_asym_id", lAsym1}, {"label_alt_id", "."}, {"pdbx_PDB_ins_code", iCode1str}, {"auth_comp_id", pep1}, {"auth_seq_id", seqNum1}, {"auth_asym_id", std::string{chainID1}}, {"pdbx_label_comp_id_2", pep2}, {"pdbx_label_seq_id_2", lResSeq2}, {"pdbx_label_asym_id_2", lAsym2}, {"pdbx_PDB_ins_code_2", iCode2str}, {"pdbx_auth_comp_id_2", pep2}, {"pdbx_auth_seq_id_2", seqNum2}, {"pdbx_auth_asym_id_2", std::string{chainID2}}, {"pdbx_PDB_model_num", modNum}, {"pdbx_omega_angle", measure}});
+			getCategory("struct_mon_prot_cis")->emplace({{"pdbx_id", serNum},
+			{"label_comp_id", pep1},
+			{"label_seq_id", lResSeq1},
+			{"label_asym_id", lAsym1},
+			{"label_alt_id", "."},
+			{"pdbx_PDB_ins_code", iCode1str},
+			{"auth_comp_id", pep1},
+			{"auth_seq_id", seqNum1},
+			{"auth_asym_id", std::string{chainID1}},
+			{"pdbx_label_comp_id_2", pep2},
+			{"pdbx_label_seq_id_2", lResSeq2},
+			{"pdbx_label_asym_id_2", lAsym2},
+			{"pdbx_PDB_ins_code_2", iCode2str},
+			{"pdbx_auth_comp_id_2", pep2},
+			{"pdbx_auth_seq_id_2", seqNum2},
+			{"pdbx_auth_asym_id_2", std::string{chainID2}},
+			{"pdbx_PDB_model_num", modNum},
+			{"pdbx_omega_angle", measure}});
 
 			continue;
 		}
@@ -4912,7 +5224,7 @@ void PDBFileParser::ParseMiscellaneousFeatures()
 
 			if (ec)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "skipping struct_site_gen record" << std::endl;
 			}
 			else
@@ -4964,7 +5276,9 @@ void PDBFileParser::ParseCrystallographic()
 	{
 	}
 
-	getCategory("symmetry")->emplace({{"entry_id", mStructureID}, {"space_group_name_H-M", spaceGroup}, {"Int_Tables_number", intTablesNr}});
+	getCategory("symmetry")->emplace({{"entry_id", mStructureID},
+	{"space_group_name_H-M", spaceGroup},
+	{"Int_Tables_number", intTablesNr}});
 
 	GetNextRecord();
 }
@@ -5056,7 +5370,20 @@ void PDBFileParser::ParseCoordinateTransformation()
 			GetNextRecord();          //	                                           transformations  of the molecule are
 		}                             //	                                           contained in the datablock. Otherwise, blank.
 
-		getCategory("struct_ncs_oper")->emplace({{"id", serial}, {"matrix[1][1]", m[0][0]}, {"matrix[1][2]", m[0][1]}, {"matrix[1][3]", m[0][2]}, {"matrix[2][1]", m[1][0]}, {"matrix[2][2]", m[1][1]}, {"matrix[2][3]", m[1][2]}, {"matrix[3][1]", m[2][0]}, {"matrix[3][2]", m[2][1]}, {"matrix[3][3]", m[2][2]}, {"vector[1]", v[0]}, {"vector[2]", v[1]}, {"vector[3]", v[2]}, {"code", igiven ? "given" : ""}});
+		getCategory("struct_ncs_oper")->emplace({{"id", serial},
+		{"matrix[1][1]", m[0][0]},
+		{"matrix[1][2]", m[0][1]},
+		{"matrix[1][3]", m[0][2]},
+		{"matrix[2][1]", m[1][0]},
+		{"matrix[2][2]", m[1][1]},
+		{"matrix[2][3]", m[1][2]},
+		{"matrix[3][1]", m[2][0]},
+		{"matrix[3][2]", m[2][1]},
+		{"matrix[3][3]", m[2][2]},
+		{"vector[1]", v[0]},
+		{"vector[2]", v[1]},
+		{"vector[3]", v[2]},
+		{"code", igiven ? "given" : ""}});
 	}
 }
 
@@ -5200,7 +5527,7 @@ void PDBFileParser::ParseCoordinate(int modelNr)
 		{
 			if (groupPDB == "HETATM")
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Changing atom from HETATM to ATOM at line " << mRec->mLineNr << std::endl;
 				groupPDB = "ATOM";
 			}
@@ -5209,13 +5536,33 @@ void PDBFileParser::ParseCoordinate(int modelNr)
 		{
 			if (groupPDB == "ATOM")
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Changing atom from ATOM to HETATM at line " << mRec->mLineNr << std::endl;
 				groupPDB = "HETATM";
 			}
 		}
 
-		getCategory("atom_site")->emplace({{"group_PDB", groupPDB}, {"id", mAtomID}, {"type_symbol", element}, {"label_atom_id", name}, {"label_alt_id", altLoc != ' ' ? std::string{altLoc} : "."}, {"label_comp_id", resName}, {"label_asym_id", asymID}, {"label_entity_id", entityID}, {"label_seq_id", (isResseq and seqID > 0) ? std::to_string(seqID) : "."}, {"pdbx_PDB_ins_code", iCode == ' ' ? "" : std::string{iCode}}, {"Cartn_x", x}, {"Cartn_y", y}, {"Cartn_z", z}, {"occupancy", occupancy}, {"B_iso_or_equiv", tempFactor}, {"pdbx_formal_charge", charge}, {"auth_seq_id", resSeq}, {"auth_comp_id", resName}, {"auth_asym_id", std::string{chainID}}, {"auth_atom_id", name}, {"pdbx_PDB_model_num", modelNr}});
+		getCategory("atom_site")->emplace({{"group_PDB", groupPDB},
+		{"id", mAtomID},
+		{"type_symbol", element},
+		{"label_atom_id", name},
+		{"label_alt_id", altLoc != ' ' ? std::string{altLoc} : "."},
+		{"label_comp_id", resName},
+		{"label_asym_id", asymID},
+		{"label_entity_id", entityID},
+		{"label_seq_id", (isResseq and seqID > 0) ? std::to_string(seqID) : "."},
+		{"pdbx_PDB_ins_code", iCode == ' ' ? "" : std::string{iCode}},
+		{"Cartn_x", x},
+		{"Cartn_y", y},
+		{"Cartn_z", z},
+		{"occupancy", occupancy},
+		{"B_iso_or_equiv", tempFactor},
+		{"pdbx_formal_charge", charge},
+		{"auth_seq_id", resSeq},
+		{"auth_comp_id", resName},
+		{"auth_asym_id", std::string{chainID}},
+		{"auth_atom_id", name},
+		{"pdbx_PDB_model_num", modelNr}});
 
 		InsertAtomType(element);
 
@@ -5237,7 +5584,23 @@ void PDBFileParser::ParseCoordinate(int modelNr)
 			auto f = [](float f) -> std::string
 			{ return (boost::format("%6.4f") % f).str(); };
 
-			getCategory("atom_site_anisotrop")->emplace({{"id", mAtomID}, {"type_symbol", element}, {"pdbx_label_atom_id", name}, {"pdbx_label_alt_id", altLoc != ' ' ? std::string{altLoc} : "."}, {"pdbx_label_comp_id", resName}, {"pdbx_label_asym_id", asymID}, {"pdbx_label_seq_id", (isResseq and seqID > 0) ? std::to_string(seqID) : "."}, {"U[1][1]", f(u11 / 10000.f)}, {"U[2][2]", f(u22 / 10000.f)}, {"U[3][3]", f(u33 / 10000.f)}, {"U[1][2]", f(u12 / 10000.f)}, {"U[1][3]", f(u13 / 10000.f)}, {"U[2][3]", f(u23 / 10000.f)}, {"pdbx_auth_seq_id", resSeq}, {"pdbx_auth_comp_id", resName}, {"pdbx_auth_asym_id", std::string{chainID}}, {"pdbx_auth_atom_id", name}});
+			getCategory("atom_site_anisotrop")->emplace({{"id", mAtomID},
+			{"type_symbol", element},
+			{"pdbx_label_atom_id", name},
+			{"pdbx_label_alt_id", altLoc != ' ' ? std::string{altLoc} : "."},
+			{"pdbx_label_comp_id", resName},
+			{"pdbx_label_asym_id", asymID},
+			{"pdbx_label_seq_id", (isResseq and seqID > 0) ? std::to_string(seqID) : "."},
+			{"U[1][1]", f(u11 / 10000.f)},
+			{"U[2][2]", f(u22 / 10000.f)},
+			{"U[3][3]", f(u33 / 10000.f)},
+			{"U[1][2]", f(u12 / 10000.f)},
+			{"U[1][3]", f(u13 / 10000.f)},
+			{"U[2][3]", f(u23 / 10000.f)},
+			{"pdbx_auth_seq_id", resSeq},
+			{"pdbx_auth_comp_id", resName},
+			{"pdbx_auth_asym_id", std::string{chainID}},
+			{"pdbx_auth_atom_id", name}});
 		}
 	}
 
@@ -5344,7 +5707,8 @@ void PDBFileParser::Parse(std::istream &is, cif::File &result)
 		}
 		catch (const std::exception &ex)
 		{
-			std::cerr << "Error parsing REMARK 3" << std::endl;
+			if (cif::VERBOSE >= 0)
+				std::cerr << "Error parsing REMARK 3" << std::endl;
 			throw;
 		}
 		//
@@ -5396,12 +5760,12 @@ void PDBFileParser::Parse(std::istream &is, cif::File &result)
 
 				if ((symm1.empty() or symm1 == "1_555") and (symm2.empty() or symm2 == "1_555"))
 					distance = static_cast<float>(mmcif::Distance(mmcif::Point{x1, y1, z1}, mmcif::Point{x2, y2, z2}));
-				else if (cif::VERBOSE)
+				else if (cif::VERBOSE > 0)
 					std::cerr << "Cannot calculate distance for link since one of the atoms is in another dimension" << std::endl;
 			}
 			catch (std::exception &ex)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Error finding atom for LINK distance calculation: " << ex.what() << std::endl;
 			}
 
@@ -5410,10 +5774,13 @@ void PDBFileParser::Parse(std::istream &is, cif::File &result)
 	}
 	catch (const std::exception &ex)
 	{
-		std::cerr << "Error parsing PDB";
-		if (mRec != nullptr)
-			std::cerr << " at line " << mRec->mLineNr;
-		std::cerr << std::endl;
+		if (cif::VERBOSE >= 0)
+		{
+			std::cerr << "Error parsing PDB";
+			if (mRec != nullptr)
+				std::cerr << " at line " << mRec->mLineNr;
+			std::cerr << std::endl;
+		}
 		throw;
 	}
 }
@@ -5593,7 +5960,7 @@ int PDBFileParser::PDBChain::AlignResToSeqRes()
 			switch (tb(x, y))
 			{
 				case -1:
-					//					if (cif::VERBOSE)
+					//					if (cif::VERBOSE > 0)
 					//						std::cerr << "A residue found in the ATOM records "
 					//							 << "(" << ry[y].mMonID << " @ " << mDbref.chainID << ":" << ry[y].mSeqNum
 					//							 	<<  ((ry[y].mIcode == ' ' or ry[y].mIcode == 0) ? "" : std::string{ ry[y].mIcode }) << ")"
@@ -5680,7 +6047,6 @@ void ReadPDBFile(std::istream &pdbFile, cif::File &cifFile)
 
 	p.Parse(pdbFile, cifFile);
 
-	if (not cifFile.isValid())
-		// throw std::runtime_error("Resulting mmCIF file is invalid");
+	if (not cifFile.isValid() and cif::VERBOSE >= 0)
 		std::cerr << "Resulting mmCIF file is not valid!" << std::endl;
 }
diff --git a/src/PDB2CifRemark3.cpp b/src/PDB2CifRemark3.cpp
index 7264a0f..6a0ad7b 100644
--- a/src/PDB2CifRemark3.cpp
+++ b/src/PDB2CifRemark3.cpp
@@ -1320,7 +1320,7 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
 
 	if (line != "REFINEMENT.")
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "Unexpected data in REMARK 3" << std::endl;
 		return false;
 	}
@@ -1332,7 +1332,7 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
 
 	if (not std::regex_match(line, m, rxp))
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "Expected valid PROGRAM line in REMARK 3" << std::endl;
 		return false;
 	}
@@ -1367,8 +1367,9 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
 		}
 		catch(const std::exception& e)
 		{
-			std::cerr << "Error parsing REMARK 3 with " << parser->program() << std::endl
-					  << e.what() << '\n';
+			if (cif::VERBOSE >= 0)
+				std::cerr << "Error parsing REMARK 3 with " << parser->program() << std::endl
+						  << e.what() << '\n';
 			score = 0;
 		}
 
@@ -1411,7 +1412,7 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
 			tryParser(new TNT_Remark3Parser(program, expMethod, r, db));
 		else if (ba::starts_with(program, "X-PLOR"))
 			tryParser(new XPLOR_Remark3Parser(program, expMethod, r, db));
-		else if (cif::VERBOSE)
+		else if (cif::VERBOSE > 0)
 			std::cerr << "Skipping unknown program (" << program << ") in REMARK 3" << std::endl;
 	}
 
@@ -1420,7 +1421,8 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
 	bool guessProgram = scores.empty() or scores.front().score < 0.9f;;
 	if (guessProgram)
 	{
-		std::cerr << "Unknown or untrusted program in REMARK 3, trying all parsers to see if there is a match" << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Unknown or untrusted program in REMARK 3, trying all parsers to see if there is a match" << std::endl;
 		
 		tryParser(new BUSTER_TNT_Remark3Parser("BUSTER-TNT", expMethod, r, db));
 		tryParser(new CNS_Remark3Parser("CNS", expMethod, r, db));
@@ -1444,7 +1446,7 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
 
 		auto& best = scores.front();
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "Choosing " << best.parser->program() << " version '" << best.parser->version() << "' as refinement program. Score = " << best.score << std::endl;
 
 		auto& software = db["software"];
diff --git a/src/Point.cpp b/src/Point.cpp
index 54e95f6..65ec464 100644
--- a/src/Point.cpp
+++ b/src/Point.cpp
@@ -28,11 +28,248 @@
 #include <valarray>
 
 #include "cif++/Point.hpp"
-#include "cif++/Matrix.hpp"
 
 namespace mmcif
 {
 
+// --------------------------------------------------------------------
+// We're using expression templates here
+
+template <typename M>
+class MatrixExpression
+{
+  public:
+	uint32_t dim_m() const { return static_cast<const M&>(*this).dim_m(); }
+	uint32_t dim_n() const { return static_cast<const M&>(*this).dim_n(); }
+
+	double &operator()(uint32_t i, uint32_t j)
+	{
+		return static_cast<M&>(*this).operator()(i, j);
+	}
+
+	double operator()(uint32_t i, uint32_t j) const
+	{
+		return static_cast<const M&>(*this).operator()(i, j);
+	}
+};
+
+// --------------------------------------------------------------------
+// matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
+// element m i,j is mapped to [i * n + j] and thus storage is row major
+
+class Matrix : public MatrixExpression<Matrix>
+{
+  public:
+	template <typename M2>
+	Matrix(const MatrixExpression<M2> &m)
+		: m_m(m.dim_m())
+		, m_n(m.dim_n())
+		, m_data(m_m * m_n)
+	{
+		for (uint32_t i = 0; i < m_m; ++i)
+		{
+			for (uint32_t j = 0; j < m_n; ++j)
+				operator()(i, j) = m(i, j);
+		}
+	}
+
+	Matrix(size_t m, size_t n, double v = 0)
+		: m_m(m)
+		, m_n(n)
+		, m_data(m_m * m_n)
+	{
+		std::fill(m_data.begin(), m_data.end(), v);
+	}
+
+	Matrix() = default;
+	Matrix(Matrix &&m) = default;
+	Matrix(const Matrix &m) = default;
+	Matrix &operator=(Matrix &&m) = default;
+	Matrix &operator=(const Matrix &m) = default;
+
+	uint32_t dim_m() const { return m_m; }
+	uint32_t dim_n() const { return m_n; }
+
+	double operator()(uint32_t i, uint32_t j) const
+	{
+		assert(i < m_m);
+		assert(j < m_n);
+		return m_data[i * m_n + j];
+	}
+
+	double &operator()(uint32_t i, uint32_t j)
+	{
+		assert(i < m_m);
+		assert(j < m_n);
+		return m_data[i * m_n + j];
+	}
+
+  private:
+	uint32_t m_m = 0, m_n = 0;
+	std::vector<double> m_data;
+};
+
+// --------------------------------------------------------------------
+
+class SymmetricMatrix : public MatrixExpression<SymmetricMatrix>
+{
+  public:
+	SymmetricMatrix(uint32_t n, double v = 0)
+		: m_n(n)
+		, m_data((m_n * (m_n + 1)) / 2)
+	{
+		std::fill(m_data.begin(), m_data.end(), v);
+	}
+
+	SymmetricMatrix() = default;
+	SymmetricMatrix(SymmetricMatrix &&m) = default;
+	SymmetricMatrix(const SymmetricMatrix &m) = default;
+	SymmetricMatrix &operator=(SymmetricMatrix &&m) = default;
+	SymmetricMatrix &operator=(const SymmetricMatrix &m) = default;
+
+	uint32_t dim_m() const { return m_n; }
+	uint32_t dim_n() const { return m_n; }
+
+	double operator()(uint32_t i, uint32_t j) const
+	{
+		return i < j
+				? m_data[(j * (j + 1)) / 2 + i]
+				: m_data[(i * (i + 1)) / 2 + j];
+	}
+
+	double &operator()(uint32_t i, uint32_t j)
+	{
+		if (i > j)
+			std::swap(i, j);
+		assert(j < m_n);
+		return m_data[(j * (j + 1)) / 2 + i];
+	}
+
+  private:
+	uint32_t m_n;
+	std::vector<double> m_data;
+};
+
+class IdentityMatrix : public MatrixExpression<IdentityMatrix>
+{
+  public:
+	IdentityMatrix(uint32_t n)
+		: m_n(n)
+	{
+	}
+
+	uint32_t dim_m() const { return m_n; }
+	uint32_t dim_n() const { return m_n; }
+
+	double operator()(uint32_t i, uint32_t j) const
+	{
+		return i == j ? 1 : 0;
+	}
+
+  private:
+	uint32_t m_n;
+};
+
+// --------------------------------------------------------------------
+// matrix functions, implemented as expression templates
+
+template<typename M1, typename M2>
+class MatrixSubtraction : public MatrixExpression<MatrixSubtraction<M1, M2>>
+{
+  public:
+	MatrixSubtraction(const M1 &m1, const M2 &m2)
+		: m_m1(m1), m_m2(m2)
+	{
+		assert(m_m1.dim_m() == m_m2.dim_m());
+		assert(m_m1.dim_n() == m_m2.dim_n());
+	}
+
+	uint32_t dim_m() const { return m_m1.dim_m(); }
+	uint32_t dim_n() const { return m_m1.dim_n(); }
+
+	double operator()(uint32_t i, uint32_t j) const
+	{
+		return m_m1(i, j) - m_m2(i, j);
+	}
+
+  private:
+	const M1 &m_m1;
+	const M2 &m_m2;
+};
+
+template<typename M1, typename M2>
+MatrixSubtraction<M1, M2> operator-(const MatrixExpression<M1> &m1, const MatrixExpression<M2> &m2)
+{
+	return MatrixSubtraction(*static_cast<const M1*>(&m1), *static_cast<const M2*>(&m2));
+}
+
+template<typename M>
+class MatrixMultiplication : public MatrixExpression<MatrixMultiplication<M>>
+{
+  public:
+	MatrixMultiplication(const M &m, double v)
+		: m_m(m), m_v(v)
+	{
+	}
+
+	uint32_t dim_m() const { return m_m.dim_m(); }
+	uint32_t dim_n() const { return m_m.dim_n(); }
+
+	double operator()(uint32_t i, uint32_t j) const
+	{
+		return m_m(i, j) * m_v;
+	}
+
+  private:
+	const M &m_m;
+	double m_v;
+};
+
+template<typename M>
+MatrixMultiplication<M> operator*(const MatrixExpression<M> &m, double v)
+{
+	return MatrixMultiplication(*static_cast<const M*>(&m), v);
+}
+
+// --------------------------------------------------------------------
+
+template<class M1>
+Matrix Cofactors(const M1& m)
+{
+	Matrix cf(m.dim_m(), m.dim_m());
+
+    const size_t ixs[4][3] =
+    {
+        { 1, 2, 3 },
+        { 0, 2, 3 },
+        { 0, 1, 3 },
+        { 0, 1, 2 }
+    };
+
+    for (size_t x = 0; x < 4; ++x)
+    {
+        const size_t* ix = ixs[x];
+
+        for (size_t y = 0; y < 4; ++y)
+        {
+            const size_t* iy = ixs[y];
+
+            cf(x, y) =
+                m(ix[0], iy[0]) * m(ix[1], iy[1]) * m(ix[2], iy[2]) +
+                m(ix[0], iy[1]) * m(ix[1], iy[2]) * m(ix[2], iy[0]) +
+                m(ix[0], iy[2]) * m(ix[1], iy[0]) * m(ix[2], iy[1]) -
+                m(ix[0], iy[2]) * m(ix[1], iy[1]) * m(ix[2], iy[0]) -
+                m(ix[0], iy[1]) * m(ix[1], iy[0]) * m(ix[2], iy[2]) -
+                m(ix[0], iy[0]) * m(ix[1], iy[2]) * m(ix[2], iy[1]);
+			
+			if ((x + y) % 2 == 1)
+				cf(x, y) *= -1;
+        }
+    }
+
+	return cf;
+}
+
 // --------------------------------------------------------------------
 
 Quaternion Normalize(Quaternion q)
@@ -102,14 +339,14 @@ Point CenterPoints(std::vector<Point>& Points)
 	return t;
 }
 
-Point Centroid(std::vector<Point>& Points)
+Point Centroid(const std::vector<Point>& pts)
 {
 	Point result;
 	
-	for (Point& pt : Points)
+	for (auto &pt : pts)
 		result += pt;
 	
-	result /= static_cast<float>(Points.size());
+	result /= static_cast<float>(pts.size());
 	
 	return result;
 }
@@ -175,7 +412,7 @@ double LargestDepressedQuarticSolution(double a, double b, double c)
 Quaternion AlignPoints(const std::vector<Point>& pa, const std::vector<Point>& pb)
 {
 	// First calculate M, a 3x3 Matrix containing the sums of products of the coordinates of A and B
-	Matrix<double> M(3, 3, 0);
+	Matrix M(3, 3, 0);
 
 	for (uint32_t i = 0; i < pa.size(); ++i)
 	{
@@ -188,7 +425,7 @@ Quaternion AlignPoints(const std::vector<Point>& pa, const std::vector<Point>& p
 	}
 	
 	// Now calculate N, a symmetric 4x4 Matrix
-	SymmetricMatrix<double> N(4);
+	SymmetricMatrix N(4);
 	
 	N(0, 0) =  M(0, 0) + M(1, 1) + M(2, 2);
 	N(0, 1) =  M(1, 2) - M(2, 1);
@@ -225,6 +462,7 @@ Quaternion AlignPoints(const std::vector<Point>& pa, const std::vector<Point>& p
 					M(1, 2) * M(2, 0) * M(0, 1) +
 					M(2, 1) * M(1, 0) * M(0, 2));
 	
+	// E is the determinant of N:
 	double E = 
 		(N(0,0) * N(1,1) - N(0,1) * N(0,1)) * (N(2,2) * N(3,3) - N(2,3) * N(2,3)) +
 		(N(0,1) * N(0,2) - N(0,0) * N(2,1)) * (N(2,1) * N(3,3) - N(2,3) * N(1,3)) +
@@ -234,47 +472,22 @@ Quaternion AlignPoints(const std::vector<Point>& pa, const std::vector<Point>& p
 		(N(0,2) * N(1,3) - N(2,1) * N(0,3)) * (N(0,2) * N(1,3) - N(2,1) * N(0,3));
 	
 	// solve quartic
-	double lm = LargestDepressedQuarticSolution(C, D, E);
+	double lambda = LargestDepressedQuarticSolution(C, D, E);
 	
 	// calculate t = (N - λI)
-	Matrix<double> li = IdentityMatrix<double>(4) * lm;
-	Matrix<double> t = N - li;
+	Matrix t = N - IdentityMatrix(4) * lambda;
 	
 	// calculate a Matrix of cofactors for t
-	Matrix<double> cf(4, 4);
+	Matrix cf = Cofactors(t);
 
-	const uint32_t ixs[4][3] =
+	int maxR = 0;
+	for (int r = 1; r < 4; ++r)
 	{
-		{ 1, 2, 3 },
-		{ 0, 2, 3 },
-		{ 0, 1, 3 },
-		{ 0, 1, 2 }
-	};
-
-	uint32_t maxR = 0;
-	for (uint32_t r = 0; r < 4; ++r)
-	{
-		const uint32_t* ir = ixs[r];
-		
-		for (uint32_t c = 0; c < 4; ++c)
-		{
-			const uint32_t* ic = ixs[c];
-
-			cf(r, c) =
-				t(ir[0], ic[0]) * t(ir[1], ic[1]) * t(ir[2], ic[2]) +
-				t(ir[0], ic[1]) * t(ir[1], ic[2]) * t(ir[2], ic[0]) +
-				t(ir[0], ic[2]) * t(ir[1], ic[0]) * t(ir[2], ic[1]) -
-				t(ir[0], ic[2]) * t(ir[1], ic[1]) * t(ir[2], ic[0]) -
-				t(ir[0], ic[1]) * t(ir[1], ic[0]) * t(ir[2], ic[2]) -
-				t(ir[0], ic[0]) * t(ir[1], ic[2]) * t(ir[2], ic[1]);
-		}
-		
-		if (r > maxR and cf(r, 0) > cf(maxR, 0))
+		if (std::abs(cf(r, 0)) > std::abs(cf(maxR, 0)))
 			maxR = r;
 	}
 	
-	// NOTE the negation of the y here, why? Maybe I swapped r/c above?
-	Quaternion q(cf(maxR, 0), cf(maxR, 1), -cf(maxR, 2), cf(maxR, 3));
+	Quaternion q(cf(maxR, 0), cf(maxR, 1), cf(maxR, 2), cf(maxR, 3));
 	q = Normalize(q);
 	
 	return q;
diff --git a/src/Secondary.cpp b/src/Secondary.cpp
index dd1907e..fb3d8fb 100644
--- a/src/Secondary.cpp
+++ b/src/Secondary.cpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -26,14 +26,14 @@
 
 // Calculate DSSP-like secondary structure information
 
-#include <numeric>
 #include <iomanip>
+#include <numeric>
 #include <thread>
 
 #include <boost/algorithm/string.hpp>
 
-#include "cif++/Structure.hpp"
 #include "cif++/Secondary.hpp"
+#include "cif++/Structure.hpp"
 
 namespace ba = boost::algorithm;
 
@@ -47,37 +47,37 @@ struct Res;
 enum ResidueType
 {
 	kUnknownResidue,
-	
+
 	//
-	kAlanine,				// A	ala
-	kArginine,				// R	arg
-	kAsparagine,			// N	asn
-	kAsparticAcid,			// D	asp
-	kCysteine,				// C	cys
-	kGlutamicAcid,			// E	glu
-	kGlutamine,				// Q	gln
-	kGlycine,				// G	gly
-	kHistidine,				// H	his
-	kIsoleucine,			// I	ile
-	kLeucine,				// L	leu
-	kLysine,				// K	lys
-	kMethionine,			// M	met
-	kPhenylalanine,			// F	phe
-	kProline,				// P	pro
-	kSerine,				// S	ser
-	kThreonine,				// T	thr
-	kTryptophan,			// W	trp
-	kTyrosine,				// Y	tyr
-	kValine,				// V	val
-	
+	kAlanine,       // A	ala
+	kArginine,      // R	arg
+	kAsparagine,    // N	asn
+	kAsparticAcid,  // D	asp
+	kCysteine,      // C	cys
+	kGlutamicAcid,  // E	glu
+	kGlutamine,     // Q	gln
+	kGlycine,       // G	gly
+	kHistidine,     // H	his
+	kIsoleucine,    // I	ile
+	kLeucine,       // L	leu
+	kLysine,        // K	lys
+	kMethionine,    // M	met
+	kPhenylalanine, // F	phe
+	kProline,       // P	pro
+	kSerine,        // S	ser
+	kThreonine,     // T	thr
+	kTryptophan,    // W	trp
+	kTyrosine,      // Y	tyr
+	kValine,        // V	val
+
 	kResidueTypeCount
 };
 
 struct ResidueInfo
 {
-	ResidueType		type;
-	char			code;
-	char			name[4];
+	ResidueType type;
+	char code;
+	char name[4];
 };
 
 const ResidueInfo kResidueInfo[] = {
@@ -109,8 +109,8 @@ ResidueType MapResidue(std::string inName)
 	ba::trim(inName);
 
 	ResidueType result = kUnknownResidue;
-	
-	for (auto& ri: kResidueInfo)
+
+	for (auto &ri : kResidueInfo)
 	{
 		if (inName == ri.name)
 		{
@@ -118,37 +118,39 @@ ResidueType MapResidue(std::string inName)
 			break;
 		}
 	}
-	
+
 	return result;
 }
 
 struct HBond
 {
-	Res*	residue;
-	double	energy;
+	Res *residue;
+	double energy;
 };
 
 enum BridgeType
 {
-	btNoBridge, btParallel, btAntiParallel
+	btNoBridge,
+	btParallel,
+	btAntiParallel
 };
 
 struct Bridge
 {
-	BridgeType				type;
-	uint32_t				sheet, ladder;
-	std::set<Bridge*>		link;
-	std::deque<uint32_t>	i, j;
-	std::string				chainI, chainJ;
-	
-	bool			operator<(const Bridge& b) const		{ return chainI < b.chainI or (chainI == b.chainI and i.front() < b.i.front()); }
+	BridgeType type;
+	uint32_t sheet, ladder;
+	std::set<Bridge *> link;
+	std::deque<uint32_t> i, j;
+	std::string chainI, chainJ;
+
+	bool operator<(const Bridge &b) const { return chainI < b.chainI or (chainI == b.chainI and i.front() < b.i.front()); }
 };
 
 struct BridgeParner
 {
-	Res*		residue;
-	uint32_t	ladder;
-	bool		parallel;
+	Res *residue;
+	uint32_t ladder;
+	bool parallel;
 };
 
 // --------------------------------------------------------------------
@@ -159,7 +161,7 @@ const float
 	kMinimalCADistance = 9.0f,
 	kMinHBondEnergy = -9.9f,
 	kMaxHBondEnergy = -0.5f,
-	kCouplingConstant = -27.888f,	//	= -332 * 0.42 * 0.2
+	kCouplingConstant = -27.888f, //	= -332 * 0.42 * 0.2
 	kMaxPeptideBondLength = 2.5f;
 
 const float
@@ -172,18 +174,19 @@ const float
 
 struct Res
 {
-	Res(const Monomer& m, int nr, ChainBreak brk)
-		: mM(m), mNumber(nr)
+	Res(const Monomer &m, int nr, ChainBreak brk)
+		: mM(m)
+		, mNumber(nr)
 		, mType(MapResidue(m.compoundID()))
 		, mChainBreak(brk)
 	{
 		// update the box containing all atoms
-		mBox[0].mX = mBox[0].mY = mBox[0].mZ =  std::numeric_limits<float>::max();
+		mBox[0].mX = mBox[0].mY = mBox[0].mZ = std::numeric_limits<float>::max();
 		mBox[1].mX = mBox[1].mY = mBox[1].mZ = -std::numeric_limits<float>::max();
 
-		mH = mmcif::Point{ std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
+		mH = mmcif::Point{std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()};
 
-		for (auto& a: mM.unique_atoms())
+		for (auto &a : mM.unique_atoms())
 		{
 			if (a.labelAtomID() == "CA")
 			{
@@ -227,44 +230,44 @@ struct Res
 	{
 		// assign the Hydrogen
 		mH = mN;
-		
+
 		if (mType != kProline and mPrev != nullptr)
 		{
 			auto pc = mPrev->mC;
 			auto po = mPrev->mO;
-			
+
 			float CODistance = static_cast<float>(Distance(pc, po));
-			
-			mH.mX += (pc.mX - po.mX) / CODistance; 
-			mH.mY += (pc.mY - po.mY) / CODistance; 
-			mH.mZ += (pc.mZ - po.mZ) / CODistance; 
+
+			mH.mX += (pc.mX - po.mX) / CODistance;
+			mH.mY += (pc.mY - po.mY) / CODistance;
+			mH.mZ += (pc.mZ - po.mZ) / CODistance;
 		}
 	}
 
-	void SetSecondaryStructure(SecondaryStructureType inSS)	{ mSecondaryStructure = inSS; }
-	SecondaryStructureType GetSecondaryStructure() const	{ return mSecondaryStructure; }
-	
-	void SetBetaPartner(uint32_t n, Res& inResidue, uint32_t inLadder, bool inParallel)
+	void SetSecondaryStructure(SecondaryStructureType inSS) { mSecondaryStructure = inSS; }
+	SecondaryStructureType GetSecondaryStructure() const { return mSecondaryStructure; }
+
+	void SetBetaPartner(uint32_t n, Res &inResidue, uint32_t inLadder, bool inParallel)
 	{
 		assert(n == 0 or n == 1);
-		
+
 		mBetaPartner[n].residue = &inResidue;
 		mBetaPartner[n].ladder = inLadder;
 		mBetaPartner[n].parallel = inParallel;
 	}
-	
+
 	BridgeParner GetBetaPartner(uint32_t n) const
 	{
 		assert(n == 0 or n == 1);
 		return mBetaPartner[n];
 	}
-						
-	void SetSheet(uint32_t inSheet)						{ mSheet = inSheet; }
-	uint32_t GetSheet() const							{ return mSheet; }
-	
-	bool IsBend() const									{ return mBend; }
-	void SetBend(bool inBend)							{ mBend = inBend; }
-	
+
+	void SetSheet(uint32_t inSheet) { mSheet = inSheet; }
+	uint32_t GetSheet() const { return mSheet; }
+
+	bool IsBend() const { return mBend; }
+	void SetBend(bool inBend) { mBend = inBend; }
+
 	Helix GetHelixFlag(HelixType helixType) const
 	{
 		size_t stride = static_cast<size_t>(helixType);
@@ -292,7 +295,7 @@ struct Res
 			throw std::runtime_error("Only cysteine residues can form sulphur bridges");
 		mSSBridgeNr = inBridgeNr;
 	}
-	
+
 	uint8_t GetSSBridgeNr() const
 	{
 		if (mType != kCysteine)
@@ -300,21 +303,20 @@ struct Res
 		return mSSBridgeNr;
 	}
 
-	double CalculateSurface(const std::vector<Res>& inResidues);
-	double CalculateSurface(const Point& inAtom, float inRadius, const std::vector<Res*>& inNeighbours);
+	double CalculateSurface(const std::vector<Res> &inResidues);
+	double CalculateSurface(const Point &inAtom, float inRadius, const std::vector<Res *> &inNeighbours);
 
-	bool AtomIntersectsBox(const Point& atom, float inRadius) const
+	bool AtomIntersectsBox(const Point &atom, float inRadius) const
 	{
-		return
-			atom.mX + inRadius >= mBox[0].mX and
-			atom.mX - inRadius <= mBox[1].mX and
-			atom.mY + inRadius >= mBox[0].mY and
-			atom.mY - inRadius <= mBox[1].mY and
-			atom.mZ + inRadius >= mBox[0].mZ and
-			atom.mZ - inRadius <= mBox[1].mZ;
+		return atom.mX + inRadius >= mBox[0].mX and
+		       atom.mX - inRadius <= mBox[1].mX and
+		       atom.mY + inRadius >= mBox[0].mY and
+		       atom.mY - inRadius <= mBox[1].mY and
+		       atom.mZ + inRadius >= mBox[0].mZ and
+		       atom.mZ - inRadius <= mBox[1].mZ;
 	}
 
-	void ExtendBox(const Point& atom, float inRadius)
+	void ExtendBox(const Point &atom, float inRadius)
 	{
 		if (mBox[0].mX > atom.mX - inRadius)
 			mBox[0].mX = atom.mX - inRadius;
@@ -330,10 +332,10 @@ struct Res
 			mBox[1].mZ = atom.mZ + inRadius;
 	}
 
-	Res* mNext = nullptr;
-	Res* mPrev = nullptr;
+	Res *mNext = nullptr;
+	Res *mPrev = nullptr;
 
-	const Monomer& mM;
+	const Monomer &mM;
 	std::string mAltID;
 
 	int mNumber;
@@ -344,14 +346,14 @@ struct Res
 	Point mCenter;
 	std::vector<Point> mSideChain;
 	double mAccessibility = 0;
-	
+
 	ResidueType mType;
 	uint8_t mSSBridgeNr = 0;
 	SecondaryStructureType mSecondaryStructure = ssLoop;
 	HBond mHBondDonor[2] = {}, mHBondAcceptor[2] = {};
 	BridgeParner mBetaPartner[2] = {};
 	uint32_t mSheet = 0;
-	Helix mHelixFlags[4] = { Helix::None, Helix::None, Helix::None, Helix::None };	//
+	Helix mHelixFlags[4] = {Helix::None, Helix::None, Helix::None, Helix::None}; //
 	bool mBend = false;
 	ChainBreak mChainBreak = ChainBreak::None;
 };
@@ -360,19 +362,20 @@ struct Res
 
 class Accumulator
 {
-	public:
-
+  public:
 	struct candidate
 	{
-		Point	location;
-		double	radius;
-		double	distance;
+		Point location;
+		double radius;
+		double distance;
 
-		bool operator<(const candidate& rhs) const
-				{ return distance < rhs.distance; }
+		bool operator<(const candidate &rhs) const
+		{
+			return distance < rhs.distance;
+		}
 	};
 
-	void operator()(const Point& a, const Point& b, double d, double r)
+	void operator()(const Point &a, const Point &b, double d, double r)
 	{
 		double distance = DistanceSquared(a, b);
 
@@ -384,7 +387,7 @@ class Accumulator
 
 		if (distance < test and distance > 0.0001)
 		{
-			candidate c = { b - a, r * r, distance };
+			candidate c = {b - a, r * r, distance};
 
 			m_x.push_back(c);
 			push_heap(m_x.begin(), m_x.end());
@@ -396,19 +399,18 @@ class Accumulator
 		sort_heap(m_x.begin(), m_x.end());
 	}
 
-	std::vector<candidate>  m_x;
+	std::vector<candidate> m_x;
 };
 
 // we use a fibonacci sphere to calculate the even distribution of the dots
 class MSurfaceDots
 {
   public:
+	static MSurfaceDots &Instance();
 
-	static MSurfaceDots&  Instance();
-
-	size_t size() const							{ return mPoints.size(); }
-	const Point& operator[](size_t inIx) const	{ return mPoints[inIx]; }
-	double weight() const						{ return mWeight; }
+	size_t size() const { return mPoints.size(); }
+	const Point &operator[](size_t inIx) const { return mPoints[inIx]; }
+	double weight() const { return mWeight; }
 
   private:
 	MSurfaceDots(int32_t inN);
@@ -417,7 +419,7 @@ class MSurfaceDots
 	double mWeight;
 };
 
-MSurfaceDots& MSurfaceDots::Instance()
+MSurfaceDots &MSurfaceDots::Instance()
 {
 	const int32_t kN = 200;
 
@@ -442,11 +444,11 @@ MSurfaceDots::MSurfaceDots(int32_t N)
 	}
 }
 
-double Res::CalculateSurface(const Point& inAtom, float inRadius, const std::vector<Res*>& inNeighbours)
+double Res::CalculateSurface(const Point &inAtom, float inRadius, const std::vector<Res *> &inNeighbours)
 {
 	Accumulator accumulate;
 
-	for (auto r: inNeighbours)
+	for (auto r : inNeighbours)
 	{
 		if (r->AtomIntersectsBox(inAtom, inRadius))
 		{
@@ -455,7 +457,7 @@ double Res::CalculateSurface(const Point& inAtom, float inRadius, const std::vec
 			accumulate(inAtom, r->mC, inRadius, kRadiusC);
 			accumulate(inAtom, r->mO, inRadius, kRadiusO);
 
-			for (auto& atom: r->mSideChain)
+			for (auto &atom : r->mSideChain)
 				accumulate(inAtom, atom, inRadius, kRadiusSideAtom);
 		}
 	}
@@ -465,7 +467,7 @@ double Res::CalculateSurface(const Point& inAtom, float inRadius, const std::vec
 	float radius = inRadius + kRadiusWater;
 	double surface = 0;
 
-	MSurfaceDots& surfaceDots = MSurfaceDots::Instance();
+	MSurfaceDots &surfaceDots = MSurfaceDots::Instance();
 
 	for (size_t i = 0; i < surfaceDots.size(); ++i)
 	{
@@ -482,51 +484,51 @@ double Res::CalculateSurface(const Point& inAtom, float inRadius, const std::vec
 	return surface * radius * radius;
 }
 
-double Res::CalculateSurface(const std::vector<Res>& inResidues)
+double Res::CalculateSurface(const std::vector<Res> &inResidues)
 {
-	std::vector<Res*> neighbours;
+	std::vector<Res *> neighbours;
 
-	for (auto& r: inResidues)
+	for (auto &r : inResidues)
 	{
 		Point center = r.mCenter;
 		double radius = r.mRadius;
 
 		if (Distance(mCenter, center) < mRadius + radius)
-			neighbours.push_back(const_cast<Res*>(&r));
+			neighbours.push_back(const_cast<Res *>(&r));
 	}
 
 	mAccessibility = CalculateSurface(mN, kRadiusN, neighbours) +
-					 CalculateSurface(mCAlpha, kRadiusCA, neighbours) +
-					 CalculateSurface(mC, kRadiusC, neighbours) +
-					 CalculateSurface(mO, kRadiusO, neighbours);
+	                 CalculateSurface(mCAlpha, kRadiusCA, neighbours) +
+	                 CalculateSurface(mC, kRadiusC, neighbours) +
+	                 CalculateSurface(mO, kRadiusO, neighbours);
 
-	for (auto& atom: mSideChain)
+	for (auto &atom : mSideChain)
 		mAccessibility += CalculateSurface(atom, kRadiusSideAtom, neighbours);
-	
+
 	return mAccessibility;
 }
 
-void CalculateAccessibilities(std::vector<Res>& inResidues, DSSP_Statistics& stats)
+void CalculateAccessibilities(std::vector<Res> &inResidues, DSSP_Statistics &stats)
 {
 	stats.accessibleSurface = 0;
-	for (auto& residue: inResidues)
+	for (auto &residue : inResidues)
 		stats.accessibleSurface += residue.CalculateSurface(inResidues);
 }
 
 // --------------------------------------------------------------------
 // TODO: use the angle to improve bond energy calculation.
 
-double CalculateHBondEnergy(Res& inDonor, Res& inAcceptor)
+double CalculateHBondEnergy(Res &inDonor, Res &inAcceptor)
 {
 	double result = 0;
-	
+
 	if (inDonor.mType != kProline)
 	{
 		double distanceHO = Distance(inDonor.mH, inAcceptor.mO);
 		double distanceHC = Distance(inDonor.mH, inAcceptor.mC);
 		double distanceNC = Distance(inDonor.mN, inAcceptor.mC);
 		double distanceNO = Distance(inDonor.mN, inAcceptor.mO);
-		
+
 		if (distanceHO < kMinimalDistance or distanceHC < kMinimalDistance or distanceNC < kMinimalDistance or distanceNO < kMinimalDistance)
 			result = kMinHBondEnergy;
 		else
@@ -550,7 +552,7 @@ double CalculateHBondEnergy(Res& inDonor, Res& inAcceptor)
 	{
 		inDonor.mHBondAcceptor[1].residue = &inAcceptor;
 		inDonor.mHBondAcceptor[1].energy = result;
-	}		
+	}
 
 	// and acceptor
 	if (result < inAcceptor.mHBondDonor[0].energy)
@@ -563,25 +565,24 @@ double CalculateHBondEnergy(Res& inDonor, Res& inAcceptor)
 	{
 		inAcceptor.mHBondDonor[1].residue = &inDonor;
 		inAcceptor.mHBondDonor[1].energy = result;
-	}		
-	
+	}
+
 	return result;
 }
 
-
 // --------------------------------------------------------------------
 
-void CalculateHBondEnergies(std::vector<Res>& inResidues)
+void CalculateHBondEnergies(std::vector<Res> &inResidues)
 {
 	// Calculate the HBond energies
 	for (uint32_t i = 0; i + 1 < inResidues.size(); ++i)
 	{
-		auto& ri = inResidues[i];
-		
+		auto &ri = inResidues[i];
+
 		for (uint32_t j = i + 1; j < inResidues.size(); ++j)
 		{
-			auto& rj = inResidues[j];
-			
+			auto &rj = inResidues[j];
+
 			if (Distance(ri.mCAlpha, rj.mCAlpha) < kMinimalCADistance)
 			{
 				CalculateHBondEnergy(ri, rj);
@@ -594,7 +595,7 @@ void CalculateHBondEnergies(std::vector<Res>& inResidues)
 
 // --------------------------------------------------------------------
 
-bool NoChainBreak(const Res* a, const Res* b)
+bool NoChainBreak(const Res *a, const Res *b)
 {
 	bool result = a->mM.asymID() == b->mM.asymID();
 	for (auto r = a; result and r != b; r = r->mNext)
@@ -608,35 +609,39 @@ bool NoChainBreak(const Res* a, const Res* b)
 	return result;
 }
 
-bool NoChainBreak(const Res& a, const Res& b)
+bool NoChainBreak(const Res &a, const Res &b)
 {
 	return NoChainBreak(&a, &b);
 }
 
 // --------------------------------------------------------------------
 
-bool TestBond(const Res* a, const Res* b)
+bool TestBond(const Res *a, const Res *b)
+{
+	return (a->mHBondAcceptor[0].residue == b and a->mHBondAcceptor[0].energy < kMaxHBondEnergy) or
+	       (a->mHBondAcceptor[1].residue == b and a->mHBondAcceptor[1].energy < kMaxHBondEnergy);
+}
+
+bool TestBond(DSSP::ResidueInfo const &a, DSSP::ResidueInfo const &b)
 {
-	return
-		(a->mHBondAcceptor[0].residue == b and a->mHBondAcceptor[0].energy < kMaxHBondEnergy) or
-		(a->mHBondAcceptor[1].residue == b and a->mHBondAcceptor[1].energy < kMaxHBondEnergy);
+	return a and b and TestBond(a.mImpl, b.mImpl);
 }
 
 // --------------------------------------------------------------------
 
-BridgeType TestBridge(const Res& r1, const Res& r2)
-{										// I.	a	d	II.	a	d		parallel    
-	auto a = r1.mPrev;					//		  \			  /
-	auto b = &r1;						//		b	e		b	e
-	auto c = r1.mNext;					// 		  /			  \                      ..
-	auto d = r2.mPrev;					//		c	f		c	f
-	auto e = &r2;						//
-	auto f = r2.mNext;					// III.	a <- f	IV. a	  f		antiparallel
-										//		                                   
-	BridgeType result = btNoBridge;		//		b	 e      b <-> e                  
-										//                                          
-										//		c -> d		c     d
-										
+BridgeType TestBridge(const Res &r1, const Res &r2)
+{                                   // I.	a	d	II.	a	d		parallel
+	auto a = r1.mPrev;              //		  \			  /
+	auto b = &r1;                   //		b	e		b	e
+	auto c = r1.mNext;              // 		  /			  \                      ..
+	auto d = r2.mPrev;              //		c	f		c	f
+	auto e = &r2;                   //
+	auto f = r2.mNext;              // III.	a <- f	IV. a	  f		antiparallel
+	                                //
+	BridgeType result = btNoBridge; //		b	 e      b <-> e
+	                                //
+	                                //		c -> d		c     d
+
 	if (a and c and NoChainBreak(a, c) and d and f and NoChainBreak(d, f))
 	{
 		if ((TestBond(c, e) and TestBond(e, a)) or (TestBond(f, b) and TestBond(b, d)))
@@ -644,83 +649,80 @@ BridgeType TestBridge(const Res& r1, const Res& r2)
 		else if ((TestBond(c, d) and TestBond(f, a)) or (TestBond(e, b) and TestBond(b, e)))
 			result = btAntiParallel;
 	}
-	
+
 	return result;
 }
 
 // --------------------------------------------------------------------
 // return true if any of the residues in bridge a is identical to any of the residues in bridge b
-bool Linked(const Bridge& a, const Bridge& b)
+bool Linked(const Bridge &a, const Bridge &b)
 {
-	return
-		find_first_of(a.i.begin(), a.i.end(), b.i.begin(), b.i.end()) != a.i.end() or
-		find_first_of(a.i.begin(), a.i.end(), b.j.begin(), b.j.end()) != a.i.end() or
-		find_first_of(a.j.begin(), a.j.end(), b.i.begin(), b.i.end()) != a.j.end() or
-		find_first_of(a.j.begin(), a.j.end(), b.j.begin(), b.j.end()) != a.j.end();
+	return find_first_of(a.i.begin(), a.i.end(), b.i.begin(), b.i.end()) != a.i.end() or
+	       find_first_of(a.i.begin(), a.i.end(), b.j.begin(), b.j.end()) != a.i.end() or
+	       find_first_of(a.j.begin(), a.j.end(), b.i.begin(), b.i.end()) != a.j.end() or
+	       find_first_of(a.j.begin(), a.j.end(), b.j.begin(), b.j.end()) != a.j.end();
 }
 
 // --------------------------------------------------------------------
 
-void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
+void CalculateBetaSheets(std::vector<Res> &inResidues, DSSP_Statistics &stats)
 {
 	// Calculate Bridges
 	std::vector<Bridge> bridges;
-	if (inResidues.size() > 4)
+
+	for (uint32_t i = 1; i + 4 < inResidues.size(); ++i)
 	{
-		for (uint32_t i = 1; i + 4 < inResidues.size(); ++i)
+		auto &ri = inResidues[i];
+
+		for (uint32_t j = i + 3; j + 1 < inResidues.size(); ++j)
 		{
-			auto& ri = inResidues[i];
-			
-			for (uint32_t j = i + 3; j + 1 < inResidues.size(); ++j)
+			auto &rj = inResidues[j];
+
+			BridgeType type = TestBridge(ri, rj);
+			if (type == btNoBridge)
+				continue;
+
+			bool found = false;
+			for (Bridge &bridge : bridges)
 			{
-				auto& rj = inResidues[j];
-				
-				BridgeType type = TestBridge(ri, rj);
-				if (type == btNoBridge)
+				if (type != bridge.type or i != bridge.i.back() + 1)
 					continue;
-				
-				bool found = false;
-				for (Bridge& bridge : bridges)
+
+				if (type == btParallel and bridge.j.back() + 1 == j)
 				{
-					if (type != bridge.type or i != bridge.i.back() + 1)
-						continue;
-					
-					if (type == btParallel and bridge.j.back() + 1 == j)
-					{
-						bridge.i.push_back(i);
-						bridge.j.push_back(j);
-						found = true;
-						break;
-					}
-	
-					if (type == btAntiParallel and bridge.j.front() - 1 == j)
-					{
-						bridge.i.push_back(i);
-						bridge.j.push_front(j);
-						found = true;
-						break;
-					}
+					bridge.i.push_back(i);
+					bridge.j.push_back(j);
+					found = true;
+					break;
 				}
-				
-				if (not found)
+
+				if (type == btAntiParallel and bridge.j.front() - 1 == j)
 				{
-					Bridge bridge = {};
-					
-					bridge.type = type;
 					bridge.i.push_back(i);
-					bridge.chainI = ri.mM.asymID();
-					bridge.j.push_back(j);
-					bridge.chainJ = rj.mM.asymID();
-					
-					bridges.push_back(bridge);
+					bridge.j.push_front(j);
+					found = true;
+					break;
 				}
 			}
+
+			if (not found)
+			{
+				Bridge bridge = {};
+
+				bridge.type = type;
+				bridge.i.push_back(i);
+				bridge.chainI = ri.mM.asymID();
+				bridge.j.push_back(j);
+				bridge.chainJ = rj.mM.asymID();
+
+				bridges.push_back(bridge);
+			}
 		}
 	}
 
 	// extend ladders
 	std::sort(bridges.begin(), bridges.end());
-	
+
 	for (uint32_t i = 0; i < bridges.size(); ++i)
 	{
 		for (uint32_t j = i + 1; j < bridges.size(); ++j)
@@ -742,7 +744,7 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 			{
 				continue;
 			}
-			
+
 			bool bulge;
 			if (bridges[i].type == btParallel)
 				bulge = ((jbj - jei < 6 and ibj - iei < 3) or (jbj - jei < 3));
@@ -763,25 +765,25 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 	}
 
 	// Sheet
-	std::set<Bridge*> ladderset;
-	for (Bridge& bridge : bridges)
+	std::set<Bridge *> ladderset;
+	for (Bridge &bridge : bridges)
 	{
 		ladderset.insert(&bridge);
-		
+
 		size_t n = bridge.i.size();
 		if (n > kHistogramSize)
 			n = kHistogramSize;
-		
+
 		if (bridge.type == btParallel)
 			stats.parallelBridgesPerLadderHistogram[n - 1] += 1;
 		else
 			stats.antiparallelBridgesPerLadderHistogram[n - 1] += 1;
 	}
-	
+
 	uint32_t sheet = 1, ladder = 0;
 	while (not ladderset.empty())
 	{
-		std::set<Bridge*> sheetset;
+		std::set<Bridge *> sheetset;
 		sheetset.insert(*ladderset.begin());
 		ladderset.erase(ladderset.begin());
 
@@ -789,9 +791,9 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 		while (not done)
 		{
 			done = true;
-			for (Bridge* a : sheetset)
+			for (Bridge *a : sheetset)
 			{
-				for (Bridge* b : ladderset)
+				for (Bridge *b : ladderset)
 				{
 					if (Linked(*a, *b))
 					{
@@ -806,15 +808,15 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 			}
 		}
 
-		for (Bridge* bridge : sheetset)
+		for (Bridge *bridge : sheetset)
 		{
 			bridge->ladder = ladder;
 			bridge->sheet = sheet;
 			bridge->link = sheetset;
-			
+
 			++ladder;
 		}
-		
+
 		size_t nrOfLaddersPerSheet = sheetset.size();
 		if (nrOfLaddersPerSheet > kHistogramSize)
 			nrOfLaddersPerSheet = kHistogramSize;
@@ -822,17 +824,17 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 			stats.laddersPerSheetHistogram[0] += 1;
 		else if (nrOfLaddersPerSheet > 1)
 			stats.laddersPerSheetHistogram[nrOfLaddersPerSheet - 1] += 1;
-		
+
 		++sheet;
 	}
 
-	for (Bridge& bridge : bridges)
+	for (Bridge &bridge : bridges)
 	{
 		// find out if any of the i and j set members already have
 		// a bridge assigned, if so, we're assigning bridge 2
-		
+
 		uint32_t betai = 0, betaj = 0;
-		
+
 		for (uint32_t l : bridge.i)
 		{
 			if (inResidues[l].GetBetaPartner(0).residue != nullptr)
@@ -850,15 +852,15 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 				break;
 			}
 		}
-		
+
 		SecondaryStructureType ss = ssBetabridge;
 		if (bridge.i.size() > 1)
 			ss = ssStrand;
-		
+
 		if (bridge.type == btParallel)
 		{
 			stats.nrOfHBondsInParallelBridges += bridge.i.back() - bridge.i.front() + 2;
-			
+
 			std::deque<uint32_t>::iterator j = bridge.j.begin();
 			for (uint32_t i : bridge.i)
 				inResidues[i].SetBetaPartner(betai, inResidues[*j++], bridge.ladder, true);
@@ -897,12 +899,11 @@ void CalculateBetaSheets(std::vector<Res>& inResidues, DSSP_Statistics& stats)
 }
 
 // --------------------------------------------------------------------
-// TODO: improve alpha helix calculation by better recognizing pi-helices 
 
-void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, bool inPreferPiHelices = true)
+void CalculateAlphaHelices(std::vector<Res> &inResidues, DSSP_Statistics &stats, bool inPreferPiHelices = true)
 {
 	// Helix and Turn
-	for (HelixType helixType: { HelixType::rh_3_10, HelixType::rh_alpha, HelixType::rh_pi })
+	for (HelixType helixType : {HelixType::rh_3_10, HelixType::rh_alpha, HelixType::rh_pi})
 	{
 		uint32_t stride = static_cast<uint32_t>(helixType) + 3;
 
@@ -916,7 +917,7 @@ void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats,
 					if (inResidues[j].GetHelixFlag(helixType) == Helix::None)
 						inResidues[j].SetHelixFlag(helixType, Helix::Middle);
 				}
-				
+
 				if (inResidues[i].GetHelixFlag(helixType) == Helix::End)
 					inResidues[i].SetHelixFlag(helixType, Helix::StartAndEnd);
 				else
@@ -924,8 +925,8 @@ void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats,
 			}
 		}
 	}
-	
-	for (auto& r : inResidues)
+
+	for (auto &r : inResidues)
 	{
 		double kappa = r.mM.kappa();
 		r.SetBend(kappa != 360 and kappa > 70);
@@ -962,7 +963,7 @@ void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats,
 			bool empty = true;
 			for (uint32_t j = i; empty and j <= i + 4; ++j)
 				empty = inResidues[j].GetSecondaryStructure() == ssLoop or inResidues[j].GetSecondaryStructure() == ssHelix_5 or
-							(inPreferPiHelices and inResidues[j].GetSecondaryStructure() == ssAlphahelix);
+				        (inPreferPiHelices and inResidues[j].GetSecondaryStructure() == ssAlphahelix);
 			if (empty)
 			{
 				for (uint32_t j = i; j <= i + 4; ++j)
@@ -970,19 +971,19 @@ void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats,
 			}
 		}
 	}
-			
+
 	for (uint32_t i = 1; i + 1 < inResidues.size(); ++i)
 	{
 		if (inResidues[i].GetSecondaryStructure() == ssLoop)
 		{
 			bool isTurn = false;
-			for (HelixType helixType: { HelixType::rh_3_10, HelixType::rh_alpha, HelixType::rh_pi })
+			for (HelixType helixType : {HelixType::rh_3_10, HelixType::rh_alpha, HelixType::rh_pi})
 			{
 				uint32_t stride = 3 + static_cast<uint32_t>(helixType);
 				for (uint32_t k = 1; k < stride and not isTurn; ++k)
 					isTurn = (i >= k) and inResidues[i - k].IsHelixStart(helixType);
 			}
-			
+
 			if (isTurn)
 				inResidues[i].SetSecondaryStructure(ssTurn);
 			else if (inResidues[i].IsBend())
@@ -992,7 +993,7 @@ void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats,
 
 	std::string asym;
 	size_t helixLength = 0;
-	for (auto r: inResidues)
+	for (auto r : inResidues)
 	{
 		if (r.mM.asymID() != asym)
 		{
@@ -1015,7 +1016,7 @@ void CalculateAlphaHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats,
 
 // --------------------------------------------------------------------
 
-void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, int stretch_length)
+void CalculatePPHelices(std::vector<Res> &inResidues, DSSP_Statistics &stats, int stretch_length)
 {
 	size_t N = inResidues.size();
 
@@ -1050,7 +1051,7 @@ void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, in
 				// auto phi_avg = (phi[i + 0] + phi[i + 1]) / 2;
 				// auto phi_sq = (phi[i + 0] - phi_avg) * (phi[i + 0] - phi_avg) +
 				// 			  (phi[i + 1] - phi_avg) * (phi[i + 1] - phi_avg);
-				
+
 				// if (phi_sq >= 200)
 				// 	continue;
 
@@ -1066,11 +1067,11 @@ void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, in
 					case Helix::None:
 						inResidues[i].SetHelixFlag(HelixType::rh_pp, Helix::Start);
 						break;
-					
+
 					case Helix::End:
 						inResidues[i].SetHelixFlag(HelixType::rh_pp, Helix::Middle);
 						break;
-					
+
 					default:
 						break;
 				}
@@ -1100,7 +1101,7 @@ void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, in
 				// auto phi_sq = (phi[i + 0] - phi_avg) * (phi[i + 0] - phi_avg) +
 				// 			  (phi[i + 1] - phi_avg) * (phi[i + 1] - phi_avg) +
 				// 			  (phi[i + 2] - phi_avg) * (phi[i + 2] - phi_avg);
-				
+
 				// if (phi_sq >= 300)
 				// 	continue;
 
@@ -1117,11 +1118,11 @@ void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, in
 					case Helix::None:
 						inResidues[i].SetHelixFlag(HelixType::rh_pp, Helix::Start);
 						break;
-					
+
 					case Helix::End:
 						inResidues[i].SetHelixFlag(HelixType::rh_pp, Helix::StartAndEnd);
 						break;
-					
+
 					default:
 						break;
 				}
@@ -1134,7 +1135,7 @@ void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, in
 
 				if (inResidues[i + 1].GetSecondaryStructure() == SecondaryStructureType::ssLoop)
 					inResidues[i + 1].SetSecondaryStructure(SecondaryStructureType::ssHelix_PPII);
-				
+
 				if (inResidues[i + 2].GetSecondaryStructure() == SecondaryStructureType::ssLoop)
 					inResidues[i + 2].SetSecondaryStructure(SecondaryStructureType::ssHelix_PPII);
 
@@ -1151,50 +1152,52 @@ void CalculatePPHelices(std::vector<Res>& inResidues, DSSP_Statistics& stats, in
 
 struct DSSPImpl
 {
-	DSSPImpl(const Structure& s, int min_poly_proline_stretch_length);
-	
-	const Structure&			mStructure;
-	const std::list<Polymer>&	mPolymers;
-	std::vector<Res>			mResidues;
-	std::vector<std::pair<Res*,Res*>> mSSBonds;
+	DSSPImpl(const Structure &s, int min_poly_proline_stretch_length);
+
+	const Structure &mStructure;
+	const std::list<Polymer> &mPolymers;
+	std::vector<Res> mResidues;
+	std::vector<std::pair<Res *, Res *>> mSSBonds;
 
 	int m_min_poly_proline_stretch_length;
 
-	auto findRes(const std::string& asymID, int seqID)
+	auto findRes(const std::string &asymID, int seqID)
 	{
-		return std::find_if(mResidues.begin(), mResidues.end(), [&](auto& r) { return r.mM.asymID() == asymID and r.mM.seqID() == seqID; });
+		return std::find_if(mResidues.begin(), mResidues.end(), [&](auto &r)
+			{ return r.mM.asymID() == asymID and r.mM.seqID() == seqID; });
 	}
 
 	void calculateSurface();
 	void calculateSecondaryStructure();
 
-	DSSP_Statistics				mStats = {};
+	DSSP_Statistics mStats = {};
 };
 
 // --------------------------------------------------------------------
 
-DSSPImpl::DSSPImpl(const Structure& s, int min_poly_proline_stretch_length)
+DSSPImpl::DSSPImpl(const Structure &s, int min_poly_proline_stretch_length)
 	: mStructure(s)
 	, mPolymers(mStructure.polymers())
 	, m_min_poly_proline_stretch_length(min_poly_proline_stretch_length)
 {
 	size_t nRes = accumulate(mPolymers.begin(), mPolymers.end(),
-		0ULL, [](size_t s, auto& p) { return s + p.size(); });
+		0ULL, [](size_t s, auto &p)
+		{ return s + p.size(); });
 
 	mStats.nrOfChains = static_cast<uint32_t>(mPolymers.size());
 
 	mResidues.reserve(nRes);
 	int resNumber = 0;
-	
-	for (auto& p: mPolymers)
+
+	for (auto &p : mPolymers)
 	{
 		ChainBreak brk = ChainBreak::NewChain;
 
-		for (auto& m: p)
+		for (auto &m : p)
 		{
 			if (not m.isComplete())
 				continue;
-			
+
 			++resNumber;
 
 			if (not mResidues.empty() and
@@ -1220,15 +1223,15 @@ DSSPImpl::DSSPImpl(const Structure& s, int min_poly_proline_stretch_length)
 	{
 		mResidues[i].mNext = &mResidues[i + 1];
 		mResidues[i + 1].mPrev = &mResidues[i];
-		
+
 		mResidues[i + 1].assignHydrogen();
 	}
 }
 
 void DSSPImpl::calculateSecondaryStructure()
 {
-	auto& db = mStructure.getFile().data();
-	for (auto r: db["struct_conn"].find(cif::Key("conn_type_id") == "disulf"))
+	auto &db = mStructure.getFile().data();
+	for (auto r : db["struct_conn"].find(cif::Key("conn_type_id") == "disulf"))
 	{
 		std::string asym1, asym2;
 		int seq1, seq2;
@@ -1237,7 +1240,7 @@ void DSSPImpl::calculateSecondaryStructure()
 		auto r1 = findRes(asym1, seq1);
 		if (r1 == mResidues.end())
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Missing (incomplete?) residue for SS bond when trying to find " << asym1 << '/' << seq1 << std::endl;
 			continue;
 			// throw std::runtime_error("Invalid file, missing residue for SS bond");
@@ -1246,7 +1249,7 @@ void DSSPImpl::calculateSecondaryStructure()
 		auto r2 = findRes(asym2, seq2);
 		if (r2 == mResidues.end())
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Missing (incomplete?) residue for SS bond when trying to find " << asym2 << '/' << seq2 << std::endl;
 			continue;
 			// throw std::runtime_error("Invalid file, missing residue for SS bond");
@@ -1262,29 +1265,29 @@ void DSSPImpl::calculateSecondaryStructure()
 
 	if (cif::VERBOSE > 1)
 	{
-		for (auto& r: mResidues)
+		for (auto &r : mResidues)
 		{
-			auto& m = r.mM;
-			
-			char helix[5] = { };
-			for (HelixType helixType: { HelixType::rh_3_10, HelixType::rh_alpha, HelixType::rh_pi, HelixType::rh_pp	})
+			auto &m = r.mM;
+
+			char helix[5] = {};
+			for (HelixType helixType : {HelixType::rh_3_10, HelixType::rh_alpha, HelixType::rh_pi, HelixType::rh_pp})
 			{
 				switch (r.GetHelixFlag(helixType))
 				{
-					case Helix::Start:			helix[static_cast<int>(helixType)] = '>'; break;
-					case Helix::Middle:			helix[static_cast<int>(helixType)] = helixType == HelixType::rh_pp ? 'P' : '3' + static_cast<char>(helixType); break;
-					case Helix::StartAndEnd:	helix[static_cast<int>(helixType)] = 'X'; break;
-					case Helix::End:			helix[static_cast<int>(helixType)] = '<'; break;
-					case Helix::None:			helix[static_cast<int>(helixType)] = ' '; break;
+					case Helix::Start: helix[static_cast<int>(helixType)] = '>'; break;
+					case Helix::Middle: helix[static_cast<int>(helixType)] = helixType == HelixType::rh_pp ? 'P' : '3' + static_cast<char>(helixType); break;
+					case Helix::StartAndEnd: helix[static_cast<int>(helixType)] = 'X'; break;
+					case Helix::End: helix[static_cast<int>(helixType)] = '<'; break;
+					case Helix::None: helix[static_cast<int>(helixType)] = ' '; break;
 				}
 			}
-			
+
 			auto id = m.asymID() + ':' + std::to_string(m.seqID()) + '/' + m.compoundID();
-			
+
 			std::cerr << id << std::string(12 - id.length(), ' ')
-					<< char(r.mSecondaryStructure) << ' '
-					<< helix
-					<< std::endl;
+					  << char(r.mSecondaryStructure) << ' '
+					  << helix
+					  << std::endl;
 		}
 	}
 
@@ -1293,11 +1296,11 @@ void DSSPImpl::calculateSecondaryStructure()
 
 	mStats.nrOfIntraChainSSBridges = 0;
 	uint8_t ssBondNr = 0;
-	for (const auto& [a, b]: mSSBonds)
+	for (const auto &[a, b] : mSSBonds)
 	{
 		if (a == b)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "In the SS bonds list, the residue " << a->mM << " is bonded to itself" << std::endl;
 			continue;
 		}
@@ -1309,7 +1312,7 @@ void DSSPImpl::calculateSecondaryStructure()
 	}
 
 	mStats.nrOfHBonds = 0;
-	for (auto& r: mResidues)
+	for (auto &r : mResidues)
 	{
 		auto donor = r.mHBondDonor;
 
@@ -1333,7 +1336,7 @@ void DSSPImpl::calculateSurface()
 
 // --------------------------------------------------------------------
 
-const Monomer& DSSP::ResidueInfo::residue() const
+const Monomer &DSSP::ResidueInfo::residue() const
 {
 	return mImpl->mM;
 }
@@ -1378,7 +1381,7 @@ double DSSP::ResidueInfo::accessibility() const
 	return mImpl->mAccessibility;
 }
 
-std::tuple<DSSP::ResidueInfo,int,bool> DSSP::ResidueInfo::bridgePartner(int i) const
+std::tuple<DSSP::ResidueInfo, int, bool> DSSP::ResidueInfo::bridgePartner(int i) const
 {
 	auto bp = mImpl->GetBetaPartner(i);
 
@@ -1392,45 +1395,51 @@ int DSSP::ResidueInfo::sheet() const
 	return mImpl->GetSheet();
 }
 
-std::tuple<DSSP::ResidueInfo,double> DSSP::ResidueInfo::acceptor(int i) const
+std::tuple<DSSP::ResidueInfo, double> DSSP::ResidueInfo::acceptor(int i) const
 {
-	auto& a = mImpl->mHBondAcceptor[i];
-	return { ResidueInfo(a.residue), a.energy };
+	auto &a = mImpl->mHBondAcceptor[i];
+	return {ResidueInfo(a.residue), a.energy};
 }
 
-std::tuple<DSSP::ResidueInfo,double> DSSP::ResidueInfo::donor(int i) const
+std::tuple<DSSP::ResidueInfo, double> DSSP::ResidueInfo::donor(int i) const
 {
-	auto& d = mImpl->mHBondDonor[i];
-	return { ResidueInfo(d.residue), d.energy };
+	auto &d = mImpl->mHBondDonor[i];
+	return {ResidueInfo(d.residue), d.energy};
 }
 
 // --------------------------------------------------------------------
 
-DSSP::iterator::iterator(Res* res)
+DSSP::iterator::iterator(Res *res)
 	: mCurrent(res)
 {
 }
 
-DSSP::iterator::iterator(const iterator& i)
+DSSP::iterator::iterator(const iterator &i)
 	: mCurrent(i.mCurrent)
 {
 }
 
-DSSP::iterator& DSSP::iterator::operator=(const iterator& i)
+DSSP::iterator &DSSP::iterator::operator=(const iterator &i)
 {
 	mCurrent = i.mCurrent;
 	return *this;
 }
 
-DSSP::iterator& DSSP::iterator::operator++()
+DSSP::iterator &DSSP::iterator::operator++()
 {
 	++mCurrent.mImpl;
 	return *this;
 }
 
+DSSP::iterator &DSSP::iterator::operator--()
+{
+	--mCurrent.mImpl;
+	return *this;
+}
+
 // --------------------------------------------------------------------
 
-DSSP::DSSP(const Structure& s, int min_poly_proline_stretch, bool calculateSurfaceAccessibility)
+DSSP::DSSP(const Structure &s, int min_poly_proline_stretch, bool calculateSurfaceAccessibility)
 	: mImpl(new DSSPImpl(s, min_poly_proline_stretch))
 {
 	if (calculateSurfaceAccessibility)
@@ -1456,7 +1465,7 @@ DSSP::iterator DSSP::begin() const
 DSSP::iterator DSSP::end() const
 {
 	// careful now, MSVC is picky when it comes to dereferencing iterators that are at the end.
-	Res* res = nullptr;
+	Res *res = nullptr;
 	if (not mImpl->mResidues.empty())
 	{
 		res = mImpl->mResidues.data();
@@ -1466,55 +1475,58 @@ DSSP::iterator DSSP::end() const
 	return iterator(res);
 }
 
-SecondaryStructureType DSSP::operator()(const std::string& inAsymID, int inSeqID) const
+SecondaryStructureType DSSP::operator()(const std::string &inAsymID, int inSeqID) const
 {
 	SecondaryStructureType result = ssLoop;
 	auto i = find_if(mImpl->mResidues.begin(), mImpl->mResidues.end(),
-		[&](auto& r) { return r.mM.asymID() == inAsymID and r.mM.seqID() == inSeqID; });
+		[&](auto &r)
+		{ return r.mM.asymID() == inAsymID and r.mM.seqID() == inSeqID; });
 	if (i != mImpl->mResidues.end())
 		result = i->mSecondaryStructure;
-	else if (cif::VERBOSE)
+	else if (cif::VERBOSE > 0)
 		std::cerr << "Could not find secondary structure for " << inAsymID << ':' << inSeqID << std::endl;
 	return result;
 }
 
-SecondaryStructureType DSSP::operator()(const Monomer& m) const
+SecondaryStructureType DSSP::operator()(const Monomer &m) const
 {
 	return operator()(m.asymID(), m.seqID());
 }
 
-double DSSP::accessibility(const std::string& inAsymID, int inSeqID) const
+double DSSP::accessibility(const std::string &inAsymID, int inSeqID) const
 {
 	SecondaryStructureType result = ssLoop;
 	auto i = find_if(mImpl->mResidues.begin(), mImpl->mResidues.end(),
-		[&](auto& r) { return r.mM.asymID() == inAsymID and r.mM.seqID() == inSeqID; });
+		[&](auto &r)
+		{ return r.mM.asymID() == inAsymID and r.mM.seqID() == inSeqID; });
 	if (i != mImpl->mResidues.end())
 		result = i->mSecondaryStructure;
-	else if (cif::VERBOSE)
+	else if (cif::VERBOSE > 0)
 		std::cerr << "Could not find secondary structure for " << inAsymID << ':' << inSeqID << std::endl;
 	return result;
 }
 
-double DSSP::accessibility(const Monomer& m) const
+double DSSP::accessibility(const Monomer &m) const
 {
 	return accessibility(m.asymID(), m.seqID());
 }
 
-bool DSSP::isAlphaHelixEndBeforeStart(const Monomer& m) const
+bool DSSP::isAlphaHelixEndBeforeStart(const Monomer &m) const
 {
 	return isAlphaHelixEndBeforeStart(m.asymID(), m.seqID());
 }
 
-bool DSSP::isAlphaHelixEndBeforeStart(const std::string& inAsymID, int inSeqID) const
+bool DSSP::isAlphaHelixEndBeforeStart(const std::string &inAsymID, int inSeqID) const
 {
 	auto i = find_if(mImpl->mResidues.begin(), mImpl->mResidues.end(),
-		[&](auto& r) { return r.mM.asymID() == inAsymID and r.mM.seqID() == inSeqID; });
+		[&](auto &r)
+		{ return r.mM.asymID() == inAsymID and r.mM.seqID() == inSeqID; });
 
 	bool result = false;
 
 	if (i != mImpl->mResidues.end() and i + 1 != mImpl->mResidues.end())
 		result = i->GetHelixFlag(HelixType::rh_alpha) == Helix::End and (i + 1)->GetHelixFlag(HelixType::rh_alpha) == Helix::Start;
-	else if (cif::VERBOSE)
+	else if (cif::VERBOSE > 0)
 		std::cerr << "Could not find secondary structure for " << inAsymID << ':' << inSeqID << std::endl;
 
 	return result;
@@ -1525,4 +1537,4 @@ DSSP_Statistics DSSP::GetStatistics() const
 	return mImpl->mStats;
 }
 
-}
+} // namespace mmcif
diff --git a/src/Structure.cpp b/src/Structure.cpp
index 14efe05..7cedd73 100644
--- a/src/Structure.cpp
+++ b/src/Structure.cpp
@@ -1,17 +1,17 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
- * 
+ *
  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -108,8 +108,8 @@ void FileImpl::load_data(const char *data, size_t length)
 	// And validate, otherwise lots of functionality won't work
 	//	if (mData.getValidator() == nullptr)
 	mData.loadDictionary("mmcif_pdbx_v50");
-	if (not mData.isValid())
-		std::cerr << "Invalid mmCIF file" << (cif::VERBOSE ? "." : " use --verbose option to see errors") << std::endl;
+	if (not mData.isValid() and cif::VERBOSE >= 0)
+		std::cerr << "Invalid mmCIF file" << (cif::VERBOSE > 0 ? "." : " use --verbose option to see errors") << std::endl;
 }
 
 void FileImpl::load(const std::filesystem::path &path)
@@ -140,14 +140,14 @@ void FileImpl::load(const std::filesystem::path &path)
 		{
 			try
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "unrecognized file extension, trying cif" << std::endl;
 
 				mData.load(in);
 			}
 			catch (const cif::CifParserError &e)
 			{
-				if (cif::VERBOSE)
+				if (cif::VERBOSE > 0)
 					std::cerr << "Not cif, trying plain old PDB" << std::endl;
 
 				// pffft...
@@ -169,7 +169,8 @@ void FileImpl::load(const std::filesystem::path &path)
 	}
 	catch (const std::exception &ex)
 	{
-		std::cerr << "Error trying to load file " << path << std::endl;
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Error trying to load file " << path << std::endl;
 		throw;
 	}
 
@@ -179,8 +180,8 @@ void FileImpl::load(const std::filesystem::path &path)
 	// And validate, otherwise lots of functionality won't work
 	//	if (mData.getValidator() == nullptr)
 	mData.loadDictionary("mmcif_pdbx_v50");
-	if (not mData.isValid())
-		std::cerr << "Invalid mmCIF file" << (cif::VERBOSE ? "." : " use --verbose option to see errors") << std::endl;
+	if (not mData.isValid() and cif::VERBOSE >= 0)
+		std::cerr << "Invalid mmCIF file" << (cif::VERBOSE > 0 ? "." : " use --verbose option to see errors") << std::endl;
 }
 
 void FileImpl::save(const std::filesystem::path &path)
@@ -202,485 +203,237 @@ void FileImpl::save(const std::filesystem::path &path)
 // --------------------------------------------------------------------
 // Atom
 
-struct AtomImpl
+Atom::AtomImpl::AtomImpl(cif::Datablock &db, const std::string &id, cif::Row row)
+	: mDb(db)
+	, mID(id)
+	, mRefcount(1)
+	, mRow(row)
+	, mCompound(nullptr)
 {
-	AtomImpl(const AtomImpl &i)
-		: mDb(i.mDb)
-		, mID(i.mID)
-		, mType(i.mType)
-		, mAtomID(i.mAtomID)
-		, mCompID(i.mCompID)
-		, mAsymID(i.mAsymID)
-		, mSeqID(i.mSeqID)
-		, mAltID(i.mAltID)
-		, mLocation(i.mLocation)
-		, mRefcount(1)
-		, mRow(i.mRow)
-		, mCompound(i.mCompound)
-		, mRadius(i.mRadius)
-		, mCachedProperties(i.mCachedProperties)
-		, mSymmetryCopy(i.mSymmetryCopy)
-		, mClone(true)
-	// , mRTop(i.mRTop), mD(i.mD)
-	{
-	}
-
-	AtomImpl(cif::Datablock &db, const std::string &id)
-		: mDb(db)
-		, mID(id)
-		, mRefcount(1)
-		, mCompound(nullptr)
-	{
-		auto &cat = db["atom_site"];
-
-		mRow = cat[cif::Key("id") == mID];
-
-		prefetch();
-	}
-
-	AtomImpl(cif::Datablock &db, cif::Row &row)
-		: mDb(db)
-		, mID(row["id"].as<std::string>())
-		, mRefcount(1)
-		, mRow(row)
-		, mCompound(nullptr)
-	{
-		prefetch();
-	}
+	prefetch();
+}
 
-	AtomImpl(cif::Datablock &db, const std::string &id, cif::Row row)
-		: mDb(db)
-		, mID(id)
-		, mRefcount(1)
-		, mRow(row)
-		, mCompound(nullptr)
-	{
-		prefetch();
-	}
+// constructor for a symmetry copy of an atom
+Atom::AtomImpl::AtomImpl(const AtomImpl &impl, const Point &loc, const std::string &sym_op)
+	: mDb(impl.mDb)
+	, mID(impl.mID)
+	, mType(impl.mType)
 
-	AtomImpl(const AtomImpl &impl, const Point &loc, const std::string &sym_op)
-		: mDb(impl.mDb)
-		, mID(impl.mID)
-		, mType(impl.mType)
-		, mAtomID(impl.mAtomID)
-		, mCompID(impl.mCompID)
-		, mAsymID(impl.mAsymID)
-		, mSeqID(impl.mSeqID)
-		, mAltID(impl.mAltID)
-		, mLocation(loc)
-		, mRefcount(1)
-		, mRow(impl.mRow)
-		, mCompound(impl.mCompound)
-		, mRadius(impl.mRadius)
-		, mCachedProperties(impl.mCachedProperties)
-		, mSymmetryCopy(true)
-		, mSymmetryOperator(sym_op)
-	{
-	}
+	, mAtomID(impl.mAtomID)
+	, mCompID(impl.mCompID)
+	, mAsymID(impl.mAsymID)
+	, mSeqID(impl.mSeqID)
+	, mAltID(impl.mAltID)
+	, mAuthSeqID(impl.mAuthSeqID)
 
-	void prefetch()
-	{
-		// Prefetch some data
-		std::string symbol;
-		cif::tie(symbol, mAtomID, mCompID, mAsymID, mSeqID, mAltID) =
-			mRow.get("type_symbol", "label_atom_id", "label_comp_id", "label_asym_id", "label_seq_id", "label_alt_id");
+	, mLocation(loc)
+	, mRefcount(1)
+	, mRow(impl.mRow)
+	, mCachedRefs(impl.mCachedRefs)
+	, mCompound(impl.mCompound)
+	, mSymmetryCopy(true)
+	, mSymmetryOperator(sym_op)
+{
+}
 
-		if (symbol != "X")
-			mType = AtomTypeTraits(symbol).type();
+void Atom::AtomImpl::prefetch()
+{
+	// Prefetch some data
+	std::string symbol;
+	cif::tie(symbol, mAtomID, mCompID, mAsymID, mSeqID, mAltID, mAuthSeqID) =
+		mRow.get("type_symbol", "label_atom_id", "label_comp_id", "label_asym_id", "label_seq_id", "label_alt_id", "auth_seq_id");
 
-		float x, y, z;
-		cif::tie(x, y, z) = mRow.get("Cartn_x", "Cartn_y", "Cartn_z");
+	if (symbol != "X")
+		mType = AtomTypeTraits(symbol).type();
 
-		mLocation = Point(x, y, z);
+	float x, y, z;
+	cif::tie(x, y, z) = mRow.get("Cartn_x", "Cartn_y", "Cartn_z");
 
-		std::string compID;
-		cif::tie(compID) = mRow.get("label_comp_id");
+	mLocation = Point(x, y, z);
+}
 
-		// mCompound = CompoundFactory::instance().create(compID);
-	}
+int Atom::AtomImpl::compare(const AtomImpl &b) const
+{
+	int d = mAsymID.compare(b.mAsymID);
+	if (d == 0)
+		d = mSeqID - b.mSeqID;
+	if (d == 0)
+		d = mAtomID.compare(b.mAtomID);
+	if (d == 0)
+		d = mAuthSeqID.compare(b.mAuthSeqID);
 
-	void reference()
-	{
-		++mRefcount;
-	}
+	return d;
+}
 
-	void release()
-	{
-		if (--mRefcount <= 0)
-			delete this;
-	}
+bool Atom::AtomImpl::getAnisoU(float anisou[6]) const
+{
+	bool result = false;
 
-	bool getAnisoU(float anisou[6]) const
+	auto cat = mDb.get("atom_site_anisotrop");
+	if (cat)
 	{
-		bool result = false;
-
-		auto cat = mDb.get("atom_site_anisotrop");
-		if (cat)
+		try
 		{
 			auto r = cat->find1(cif::Key("id") == mID);
-
-			if (not r.empty())
-			{
-				result = true;
-				cif::tie(anisou[0], anisou[1], anisou[2], anisou[3], anisou[4], anisou[5]) =
-					r.get("U[1][1]", "U[1][2]", "U[1][3]", "U[2][2]", "U[2][3]", "U[3][3]");
-			}
+			cif::tie(anisou[0], anisou[1], anisou[2], anisou[3], anisou[4], anisou[5]) =
+				r.get("U[1][1]", "U[1][2]", "U[1][3]", "U[2][2]", "U[2][3]", "U[3][3]");
+			result = true;
 		}
-
-		return result;
-	}
-
-	void moveTo(const Point &p)
-	{
-		assert(not mSymmetryCopy);
-		if (mSymmetryCopy)
-			throw std::runtime_error("Moving symmetry copy");
-
-		if (not mClone)
+		catch (const std::exception &e)
 		{
-			mRow["Cartn_x"] = p.getX();
-			mRow["Cartn_y"] = p.getY();
-			mRow["Cartn_z"] = p.getZ();
 		}
-
-		//		boost::format kPosFmt("%.3f");
-		//
-		//		mRow["Cartn_x"] = (kPosFmt % p.getX()).str();
-		//		mRow["Cartn_y"] = (kPosFmt % p.getY()).str();
-		//		mRow["Cartn_z"] = (kPosFmt % p.getZ()).str();
-
-		mLocation = p;
 	}
 
-	const Compound &comp() const
-	{
-		if (mCompound == nullptr)
-		{
-			std::string compID;
-			cif::tie(compID) = mRow.get("label_comp_id");
-
-			mCompound = CompoundFactory::instance().create(compID);
-
-			if (cif::VERBOSE and mCompound == nullptr)
-				std::cerr << "Compound not found: '" << compID << '\'' << std::endl;
-		}
-
-		if (mCompound == nullptr)
-			throw std::runtime_error("no compound");
+	return result;
+}
 
-		return *mCompound;
-	}
+void Atom::AtomImpl::moveTo(const Point &p)
+{
+	assert(not mSymmetryCopy);
+	if (mSymmetryCopy)
+		throw std::runtime_error("Moving symmetry copy");
 
-	bool isWater() const
+	if (not mClone)
 	{
-		// mCompound may still be null here, and besides, this check is not that exciting anyway
-		return mCompID == "HOH" or mCompID == "H2O" or mCompID == "WAT";
+		mRow.assign("Cartn_x", std::to_string(p.getX()), true, false);
+		mRow.assign("Cartn_y", std::to_string(p.getY()), true, false);
+		mRow.assign("Cartn_z", std::to_string(p.getZ()), true, false);
 	}
 
-	float radius() const
-	{
-		return mRadius;
-	}
+	mLocation = p;
+}
 
-	const std::string &property(const std::string &name) const
+const Compound &Atom::AtomImpl::comp() const
+{
+	if (mCompound == nullptr)
 	{
-		static std::string kEmptyString;
+		std::string compID;
+		cif::tie(compID) = mRow.get("label_comp_id");
 
-		auto i = mCachedProperties.find(name);
-		if (i == mCachedProperties.end())
-		{
-			auto v = mRow[name];
-			if (v.empty())
-				return kEmptyString;
+		mCompound = CompoundFactory::instance().create(compID);
 
-			return mCachedProperties[name] = v.as<std::string>();
-		}
-		else
-			return i->second;
+		if (cif::VERBOSE > 0 and mCompound == nullptr)
+			std::cerr << "Compound not found: '" << compID << '\'' << std::endl;
 	}
 
-	void property(const std::string &name, const std::string &value)
-	{
-		mRow[name] = value;
-	}
+	if (mCompound == nullptr)
+		throw std::runtime_error("no compound");
 
-	int compare(const AtomImpl &b) const
-	{
-		int d = mAsymID.compare(b.mAsymID);
-		if (d == 0)
-			d = mSeqID - b.mSeqID;
-		if (d == 0)
-			d = mAtomID.compare(b.mAtomID);
-		return d;
-	}
+	return *mCompound;
+}
 
-	void swapAtomLabels(AtomImpl &b)
+const std::string Atom::AtomImpl::get_property(const std::string_view name) const
+{
+	for (auto &&[tag, ref] : mCachedRefs)
 	{
-		std::swap(mAtomID, b.mAtomID);
+		if (tag == name)
+			return ref.as<std::string>();
 	}
 
-	const cif::Datablock &mDb;
-	std::string mID;
-	AtomType mType;
-
-	std::string mAtomID;
-	std::string mCompID;
-	std::string mAsymID;
-	int mSeqID;
-	std::string mAltID;
-
-	Point mLocation;
-	int mRefcount;
-	cif::Row mRow;
-	mutable const Compound *mCompound = nullptr;
-	float mRadius = std::nanf("4");
-	mutable std::map<std::string, std::string> mCachedProperties;
-
-	bool mSymmetryCopy = false;
-	bool mClone = false;
-
-	std::string mSymmetryOperator = "1_555";
-	// clipper::RTop_orth	mRTop;
-	// Point				mD;
-	// int32_t				mRTix;
-};
-
-//Atom::Atom(const File& f, const std::string& id)
-//	: mImpl(new AtomImpl(f, id))
-//{
-//}
-//
-
-Atom::Atom()
-	: mImpl_(nullptr)
-{
-}
-
-Atom::Atom(AtomImpl *impl)
-	: mImpl_(impl)
-{
+	mCachedRefs.emplace_back(name, mRow[name]);
+	return std::get<1>(mCachedRefs.back()).as<std::string>();
 }
 
-Atom::Atom(cif::Datablock &db, cif::Row &row)
-	: mImpl_(new AtomImpl(db, row))
+void Atom::AtomImpl::set_property(const std::string_view name, const std::string &value)
 {
-}
+	for (auto &&[tag, ref] : mCachedRefs)
+	{
+		if (tag != name)
+			continue;
 
-AtomImpl *Atom::impl()
-{
-	if (mImpl_ == nullptr)
-		throw std::runtime_error("atom is not set");
-	return mImpl_;
-}
+		ref = value;
+		return;
+	}
 
-const AtomImpl *Atom::impl() const
-{
-	if (mImpl_ == nullptr)
-		throw std::runtime_error("atom is not set");
-	return mImpl_;
+	mCachedRefs.emplace_back(name, mRow[name]);
+	std::get<1>(mCachedRefs.back()) = value;
 }
 
-Atom Atom::clone() const
+Atom::Atom(cif::Datablock &db, cif::Row &row)
+	: Atom(std::make_shared<AtomImpl>(db, row["id"].as<std::string>(), row))
 {
-	return Atom(mImpl_ ? new AtomImpl(*mImpl_) : nullptr);
 }
 
 Atom::Atom(const Atom &rhs, const Point &loc, const std::string &sym_op)
-	: mImpl_(new AtomImpl(*rhs.mImpl_, loc, sym_op))
+	: Atom(std::make_shared<AtomImpl>(*rhs.mImpl, loc, sym_op))
 {
 }
 
-Atom::Atom(const Atom &rhs)
-	: mImpl_(rhs.mImpl_)
-{
-	if (mImpl_)
-		mImpl_->reference();
-}
-
-Atom::~Atom()
-{
-	if (mImpl_)
-		mImpl_->release();
-}
-
-Atom &Atom::operator=(const Atom &rhs)
-{
-	if (this != &rhs)
-	{
-		if (mImpl_)
-			mImpl_->release();
-		mImpl_ = rhs.mImpl_;
-		if (mImpl_)
-			mImpl_->reference();
-	}
-
-	return *this;
-}
-
-const cif::Row Atom::getRow() const
-{
-	return mImpl_->mRow;
-}
-
 const cif::Row Atom::getRowAniso() const
 {
-	auto &db = mImpl_->mDb;
+	auto &db = mImpl->mDb;
 	auto cat = db.get("atom_site_anisotrop");
 	if (not cat)
 		return {};
 	else
-		return cat->find1(cif::Key("id") == mImpl_->mID);
-}
-
-template <>
-std::string Atom::property<std::string>(const std::string &name) const
-{
-	return impl()->property(name);
-}
-
-template <>
-int Atom::property<int>(const std::string &name) const
-{
-	auto v = impl()->property(name);
-	return v.empty() ? 0 : stoi(v);
-}
-
-template <>
-float Atom::property<float>(const std::string &name) const
-{
-	return stof(impl()->property(name));
-}
-
-void Atom::property(const std::string &name, const std::string &value)
-{
-	impl()->property(name, value);
-}
-
-const std::string &Atom::id() const
-{
-	return impl()->mID;
-}
-
-AtomType Atom::type() const
-{
-	return impl()->mType;
-}
-
-int Atom::charge() const
-{
-	return property<int>("pdbx_formal_charge");
+		return cat->find1(cif::Key("id") == mImpl->mID);
 }
 
 float Atom::uIso() const
 {
 	float result;
 
-	if (not property<std::string>("U_iso_or_equiv").empty())
-		result = property<float>("U_iso_or_equiv");
-	else if (not property<std::string>("B_iso_or_equiv").empty())
-		result = property<float>("B_iso_or_equiv") / static_cast<float>(8 * kPI * kPI);
+	if (not get_property<std::string>("U_iso_or_equiv").empty())
+		result = get_property<float>("U_iso_or_equiv");
+	else if (not get_property<std::string>("B_iso_or_equiv").empty())
+		result = get_property<float>("B_iso_or_equiv") / static_cast<float>(8 * kPI * kPI);
 	else
 		throw std::runtime_error("Missing B_iso or U_iso");
 
 	return result;
 }
 
-bool Atom::getAnisoU(float anisou[6]) const
-{
-	return impl()->getAnisoU(anisou);
-}
-
-float Atom::occupancy() const
+std::string Atom::labelID() const
 {
-	return property<float>("occupancy");
+	return mImpl->mCompID + '_' + mImpl->mAsymID + '_' + std::to_string(mImpl->mSeqID) + ':' + mImpl->mAtomID;
 }
 
-std::string Atom::labelAtomID() const
+std::string Atom::pdbID() const
 {
-	return impl()->mAtomID;
+	return get_property<std::string>("auth_comp_id") + '_' +
+	       get_property<std::string>("auth_asym_id") + '_' +
+	       get_property<std::string>("auth_seq_id") +
+	       get_property<std::string>("pdbx_PDB_ins_code");
 }
 
-std::string Atom::labelCompID() const
+int Atom::charge() const
 {
-	return impl()->mCompID;
+	return get_property<int>("pdbx_formal_charge");
 }
 
-std::string Atom::labelAsymID() const
+float Atom::occupancy() const
 {
-	return impl()->mAsymID;
+	return get_property<float>("occupancy");
 }
 
 std::string Atom::labelEntityID() const
 {
-	return property<std::string>("label_entity_id");
-}
-
-std::string Atom::labelAltID() const
-{
-	return impl()->mAltID;
-}
-
-bool Atom::isAlternate() const
-{
-	return not impl()->mAltID.empty();
-}
-
-int Atom::labelSeqID() const
-{
-	return impl()->mSeqID;
-}
-
-std::string Atom::authAsymID() const
-{
-	return property<std::string>("auth_asym_id");
+	return get_property<std::string>("label_entity_id");
 }
 
 std::string Atom::authAtomID() const
 {
-	return property<std::string>("auth_atom_id");
-}
-
-std::string Atom::pdbxAuthAltID() const
-{
-	return property<std::string>("pdbx_auth_alt_id");
-}
-
-std::string Atom::pdbxAuthInsCode() const
-{
-	return property<std::string>("pdbx_PDB_ins_code");
+	return get_property<std::string>("auth_atom_id");
 }
 
 std::string Atom::authCompID() const
 {
-	return property<std::string>("auth_comp_id");
-}
-
-std::string Atom::authSeqID() const
-{
-	return property<std::string>("auth_seq_id");
+	return get_property<std::string>("auth_comp_id");
 }
 
-std::string Atom::labelID() const
-{
-	return property<std::string>("label_comp_id") + '_' + impl()->mAsymID + '_' + std::to_string(impl()->mSeqID) + ':' + impl()->mAtomID;
-}
-
-std::string Atom::pdbID() const
+std::string Atom::authAsymID() const
 {
-	return property<std::string>("auth_comp_id") + '_' +
-	       property<std::string>("auth_asym_id") + '_' +
-	       property<std::string>("auth_seq_id") +
-	       property<std::string>("pdbx_PDB_ins_code");
+	return get_property<std::string>("auth_asym_id");
 }
 
-Point Atom::location() const
+std::string Atom::pdbxAuthInsCode() const
 {
-	return impl()->mLocation;
+	return get_property<std::string>("pdbx_PDB_ins_code");
 }
 
-void Atom::location(Point p)
+std::string Atom::pdbxAuthAltID() const
 {
-	impl()->moveTo(p);
+	return get_property<std::string>("pdbx_auth_alt_id");
 }
 
 void Atom::translate(Point t)
@@ -697,80 +450,32 @@ void Atom::rotate(Quaternion q)
 	location(loc);
 }
 
-// Atom Atom::symmetryCopy(const Point& d, const clipper::RTop_orth& rt)
-// {
-// 	return Atom(new AtomImpl(*impl(), d, rt));
-// }
-
-// bool Atom::isSymmetryCopy() const
-// {
-// 	return impl()->mSymmetryCopy;
-// }
-
-// std::string Atom::symmetry() const
-// {
-// 	return clipper::Symop(impl()->mRTop).format() + "\n" + impl()->mRTop.format();
-// }
-
-bool Atom::isSymmetryCopy() const
-{
-	return mImpl_->mSymmetryCopy;
-}
-
-std::string Atom::symmetry() const
+void Atom::translateAndRotate(Point t, Quaternion q)
 {
-	return mImpl_->mSymmetryOperator;
-}
-
-// const clipper::RTop_orth& Atom::symop() const
-// {
-// 	return impl()->mRTop;
-// }
-
-const Compound &Atom::comp() const
-{
-	return impl()->comp();
+	auto loc = location();
+	loc += t;
+	loc.rotate(q);
+	location(loc);
 }
 
-bool Atom::isWater() const
+void Atom::translateRotateAndTranslate(Point t1, Quaternion q, Point t2)
 {
-	return impl()->isWater();
+	auto loc = location();
+	loc += t1;
+	loc.rotate(q);
+	loc += t2;
+	location(loc);
 }
 
 bool Atom::operator==(const Atom &rhs) const
 {
-	return impl() == rhs.impl() or
-	       (&impl()->mDb == &rhs.impl()->mDb and impl()->mID == rhs.impl()->mID);
-}
-
-// clipper::Atom Atom::toClipper() const
-// {
-// 	return impl()->toClipper();
-// }
-
-// void Atom::calculateRadius(float resHigh, float resLow, float perc)
-// {
-// 	AtomShape shape(*this, resHigh, resLow, false);
-// 	impl()->mRadius = shape.radius();
-
-// 	// verbose
-// 	if (cif::VERBOSE > 1)
-// 		cout << "Calculated radius for " << AtomTypeTraits(impl()->mType).name() << " with charge " << charge() << " is " << impl()->mRadius << std::endl;
-// }
-
-float Atom::radius() const
-{
-	return impl()->mRadius;
-}
-
-int Atom::compare(const Atom &b) const
-{
-	return impl() == b.impl() ? 0 : impl()->compare(*b.impl());
+	return mImpl == rhs.mImpl or
+	       (&mImpl->mDb == &rhs.mImpl->mDb and mImpl->mID == rhs.mImpl->mID);
 }
 
 void Atom::setID(int id)
 {
-	impl()->mID = std::to_string(id);
+	mImpl->mID = std::to_string(id);
 }
 
 std::ostream &operator<<(std::ostream &os, const Atom &atom)
@@ -788,66 +493,6 @@ std::ostream &operator<<(std::ostream &os, const Atom &atom)
 // --------------------------------------------------------------------
 // residue
 
-// First constructor used to be for waters only, but now accepts sugars as well.
-
-Residue::Residue(const Structure &structure, const std::string &compoundID,
-	const std::string &asymID, const std::string &authSeqID)
-	: mStructure(&structure)
-	, mCompoundID(compoundID)
-	, mAsymID(asymID)
-	, mAuthSeqID(authSeqID)
-{
-	for (auto &a : mStructure->atoms())
-	{
-		if (a.labelAsymID() != mAsymID or
-			a.labelCompID() != mCompoundID)
-			continue;
-
-		if (compoundID == "HOH")
-		{
-			if (not mAuthSeqID.empty() and a.authSeqID() != mAuthSeqID)
-				continue;
-		}
-		else
-		{
-			if (mSeqID > 0 and a.labelSeqID() != mSeqID)
-				continue;
-		}
-
-		mAtoms.push_back(a);
-	}
-
-	assert(not mAtoms.empty());
-}
-
-Residue::Residue(const Structure &structure, const std::string &compoundID, const std::string &asymID)
-	: Residue(structure, compoundID, asymID, 0, {})
-{
-}
-
-Residue::Residue(const Structure &structure, const std::string &compoundID,
-	const std::string &asymID, int seqID, const std::string &authSeqID)
-	: mStructure(&structure)
-	, mCompoundID(compoundID)
-	, mAsymID(asymID)
-	, mSeqID(seqID)
-	, mAuthSeqID(authSeqID)
-{
-	assert(mCompoundID != "HOH");
-
-	for (auto &a : mStructure->atoms())
-	{
-		if (mSeqID > 0 and a.labelSeqID() != mSeqID)
-			continue;
-
-		if (a.labelAsymID() != mAsymID or
-			a.labelCompID() != mCompoundID)
-			continue;
-
-		mAtoms.push_back(a);
-	}
-}
-
 Residue::Residue(Residue &&rhs)
 	: mStructure(rhs.mStructure)
 	, mCompoundID(std::move(rhs.mCompoundID))
@@ -856,13 +501,13 @@ Residue::Residue(Residue &&rhs)
 	, mAuthSeqID(rhs.mAuthSeqID)
 	, mAtoms(std::move(rhs.mAtoms))
 {
-	//std::cerr << "move constructor residue" << std::endl;
+	// std::cerr << "move constructor residue" << std::endl;
 	rhs.mStructure = nullptr;
 }
 
 Residue &Residue::operator=(Residue &&rhs)
 {
-	//std::cerr << "move assignment residue" << std::endl;
+	// std::cerr << "move assignment residue" << std::endl;
 	mStructure = rhs.mStructure;
 	rhs.mStructure = nullptr;
 	mCompoundID = std::move(rhs.mCompoundID);
@@ -876,7 +521,7 @@ Residue &Residue::operator=(Residue &&rhs)
 
 Residue::~Residue()
 {
-	//std::cerr << "~Residue" << std::endl;
+	// std::cerr << "~Residue" << std::endl;
 }
 
 std::string Residue::entityID() const
@@ -990,7 +635,7 @@ AtomView Residue::unique_atoms() const
 			firstAlt = alt;
 		else if (alt != firstAlt)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "skipping alternate atom " << atom << std::endl;
 			continue;
 		}
@@ -1136,11 +781,6 @@ std::ostream &operator<<(std::ostream &os, const Residue &res)
 // --------------------------------------------------------------------
 // monomer
 
-//Monomer::Monomer(Monomer&& rhs)
-//	: Residue(std::move(rhs)), mPolymer(rhs.mPolymer), mIndex(rhs.mIndex)
-//{
-//}
-
 Monomer::Monomer(const Polymer &polymer, size_t index, int seqID, const std::string &authSeqID, const std::string &compoundID)
 	: Residue(*polymer.structure(), compoundID, polymer.asymID(), seqID, authSeqID)
 	, mPolymer(&polymer)
@@ -1153,23 +793,11 @@ Monomer::Monomer(Monomer &&rhs)
 	, mPolymer(rhs.mPolymer)
 	, mIndex(rhs.mIndex)
 {
-	std::cerr << "move constructor monomer" << std::endl;
-
-	//	mStructure = rhs.mStructure;			rhs.mStructure = nullptr;
-	//	mCompoundID = std::move(rhs.mCompoundID);
-	//	mAsymID = std::move(rhs.mAsymID);
-	//	mSeqID = rhs.mSeqID;
-	//	mAtoms = std::move(rhs.mAtoms);
-	//
-	//	mPolymer = rhs.mPolymer; rhs.mPolymer = nullptr;
-	//	mIndex = rhs.mIndex;
 	rhs.mPolymer = nullptr;
 }
 
 Monomer &Monomer::operator=(Monomer &&rhs)
 {
-	std::cerr << "move assignment monomer" << std::endl;
-
 	Residue::operator=(std::move(rhs));
 	mPolymer = rhs.mPolymer;
 	rhs.mPolymer = nullptr;
@@ -1213,7 +841,7 @@ float Monomer::phi() const
 	}
 	catch (const std::exception &ex)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << ex.what() << std::endl;
 	}
 
@@ -1235,7 +863,7 @@ float Monomer::psi() const
 	}
 	catch (const std::exception &ex)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << ex.what() << std::endl;
 	}
 
@@ -1259,7 +887,7 @@ float Monomer::alpha() const
 	}
 	catch (const std::exception &ex)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << ex.what() << std::endl;
 	}
 
@@ -1287,7 +915,7 @@ float Monomer::kappa() const
 	}
 	catch (const std::exception &ex)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "When trying to calculate kappa for " << asymID() << ':' << seqID() << ": "
 					  << ex.what() << std::endl;
 	}
@@ -1310,7 +938,7 @@ float Monomer::tco() const
 	}
 	catch (const std::exception &ex)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "When trying to calculate tco for " << asymID() << ':' << seqID() << ": "
 					  << ex.what() << std::endl;
 	}
@@ -1329,7 +957,7 @@ float Monomer::omega() const
 	}
 	catch (const std::exception &ex)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "When trying to calculate omega for " << asymID() << ':' << seqID() << ": "
 					  << ex.what() << std::endl;
 	}
@@ -1400,7 +1028,7 @@ float Monomer::chi(size_t nr) const
 	}
 	catch (const std::exception &e)
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << e.what() << std::endl;
 		result = 0;
 	}
@@ -1541,94 +1169,6 @@ bool Monomer::isCis(const mmcif::Monomer &a, const mmcif::Monomer &b)
 
 // --------------------------------------------------------------------
 // polymer
-//
-//Polymer::iterator::iterator(const Polymer& p, uint32_t index)
-//	: mPolymer(&p), mIndex(index), mCurrent(p, index)
-//{
-//	auto& polySeq = mPolymer->mPolySeq;
-//
-//	if (index < polySeq.size())
-//	{
-//		int seqID;
-//		std::string asymID, monID;
-//		cif::tie(asymID, seqID, monID) =
-//			polySeq[mIndex].get("asym_id", "seq_id", "mon_id");
-//
-//		mCurrent = Monomer(*mPolymer, index, seqID, monID, "");
-//	}
-//}
-//
-//Monomer Polymer::operator[](size_t index) const
-//{
-//	if (index >= mPolySeq.size())
-//		throw out_of_range("Invalid index for residue in polymer");
-//
-//	std::string compoundID;
-//	int seqID;
-//
-//	auto r = mPolySeq[index];
-//
-//	cif::tie(seqID, compoundID) =
-//		r.get("seq_id", "mon_id");
-//
-//	return Monomer(const_cast<Polymer&>(*this), index, seqID, compoundID, "");
-//}
-//
-//Polymer::iterator::iterator(const iterator& rhs)
-//	: mPolymer(rhs.mPolymer), mIndex(rhs.mIndex), mCurrent(rhs.mCurrent)
-//{
-//}
-//
-//Polymer::iterator& Polymer::iterator::operator++()
-//{
-//	auto& polySeq = mPolymer->mPolySeq;
-//
-//	if (mIndex < polySeq.size())
-//		++mIndex;
-//
-//	if (mIndex < polySeq.size())
-//	{
-//		int seqID;
-//		std::string asymID, monID;
-//		cif::tie(asymID, seqID, monID) =
-//			polySeq[mIndex].get("asym_id", "seq_id", "mon_id");
-//
-//		mCurrent = Monomer(*mPolymer, mIndex, seqID, monID, "");
-//	}
-//
-//	return *this;
-//}
-
-//Polymer::Polymer(const Structure& s, const std::string& asymID)
-//	: mStructure(const_cast<Structure*>(&s)), mAsymID(asymID)
-//	, mPolySeq(s.category("pdbx_poly_seq_scheme").find(cif::Key("asym_id") == mAsymID))
-//{
-//	mEntityID = mPolySeq.front()["entity_id"].as<std::string>();
-//
-//#if DEBUG
-//	for (auto r: mPolySeq)
-//		assert(r["entity_id"] == mEntityID);
-//#endif
-//
-//}
-
-//Polymer::Polymer(Polymer&& rhs)
-//	: std::vector<Monomer>(std::move(rhs))
-//	, mStructure(rhs.mStructure)
-//	, mEntityID(std::move(rhs.mEntityID)), mAsymID(std::move(rhs.mAsymID)), mPolySeq(std::move(rhs.mPolySeq))
-//{
-//	rhs.mStructure = nullptr;
-//}
-//
-//Polymer& Polymer::operator=(Polymer&& rhs)
-//{
-//	std::vector<Monomer>::operator=(std::move(rhs));
-//	mStructure = rhs.mStructure;			rhs.mStructure = nullptr;
-//	mEntityID = std::move(rhs.mEntityID);
-//	mAsymID = std::move(rhs.mAsymID);
-//	mPolySeq = std::move(rhs.mPolySeq);
-//	return *this;
-//}
 
 Polymer::Polymer(const Structure &s, const std::string &entityID, const std::string &asymID)
 	: mStructure(const_cast<Structure *>(&s))
@@ -1654,7 +1194,7 @@ Polymer::Polymer(const Structure &s, const std::string &entityID, const std::str
 			ix[seqID] = index;
 			emplace_back(*this, index, seqID, authSeqID, compoundID);
 		}
-		else if (cif::VERBOSE)
+		else if (cif::VERBOSE > 0)
 		{
 			Monomer m{*this, index, seqID, authSeqID, compoundID};
 			std::cerr << "Dropping alternate residue " << m << std::endl;
@@ -1736,7 +1276,7 @@ File::~File()
 	delete mImpl;
 }
 
-cif::Datablock& File::createDatablock(const std::string &name)
+cif::Datablock &File::createDatablock(const std::string_view name)
 {
 	auto db = new cif::Datablock(name);
 
@@ -1799,7 +1339,7 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options)
 		cif::tie(model_nr) = atomCat.front().get("pdbx_PDB_model_num");
 		if (model_nr and *model_nr != mModelNr)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "No atoms loaded for model 1, trying model " << *model_nr << std::endl;
 			mModelNr = *model_nr;
 			loadAtomsForModel(options);
@@ -1807,7 +1347,10 @@ Structure::Structure(File &f, size_t modelNr, StructureOpenOptions options)
 	}
 
 	if (mAtoms.empty())
-		std::cerr << "Warning: no atoms loaded" << std::endl;
+	{
+		if (cif::VERBOSE >= 0)
+			std::cerr << "Warning: no atoms loaded" << std::endl;
+	}
 	else
 		loadData();
 }
@@ -1830,11 +1373,10 @@ void Structure::loadAtomsForModel(StructureOpenOptions options)
 		if ((options bitand StructureOpenOptions::SkipHydrogen) and type_symbol == "H")
 			continue;
 
-		mAtoms.emplace_back(new AtomImpl(*db, id, a));
+		mAtoms.emplace_back(std::make_shared<Atom::AtomImpl>(*db, id, a));
 	}
 }
 
-
 Structure::Structure(const Structure &s)
 	: mFile(s.mFile)
 	, mModelNr(s.mModelNr)
@@ -1875,7 +1417,7 @@ void Structure::loadData()
 			r.get("asym_id", "mon_id", "pdb_seq_num");
 
 		if (monID == "HOH")
-			mNonPolymers.emplace_back(*this, monID, asymID, pdbSeqNum);
+			mNonPolymers.emplace_back(*this, monID, asymID, 0, pdbSeqNum);
 		else if (mNonPolymers.empty() or mNonPolymers.back().asymID() != asymID)
 			mNonPolymers.emplace_back(*this, monID, asymID);
 	}
@@ -1888,7 +1430,43 @@ void Structure::loadData()
 		cif::tie(asymID, monID, num) =
 			r.get("asym_id", "mon_id", "num");
 
-		mBranchResidues.emplace_back(*this, monID, asymID, num);
+		mBranchResidues.emplace_back(*this, monID, asymID, 0, num);
+	}
+
+	// place atoms in residues
+
+	using key_type = std::tuple<std::string, std::string, int>;
+	using map_type = std::map<key_type, Residue *>;
+	map_type resMap;
+
+	for (auto &poly : mPolymers)
+	{
+		for (auto &res : poly)
+			resMap[{res.asymID(), res.compoundID(), res.seqID()}] = &res;
+	}
+
+	for (auto &res : mNonPolymers)
+		resMap[{res.asymID(), res.compoundID(), (res.isWater() ? std::stoi(res.mAuthSeqID) : res.seqID())}] = &res;
+
+	for (auto &res : mBranchResidues)
+		resMap[{res.asymID(), res.compoundID(), res.seqID()}] = &res;
+
+	for (auto &atom : mAtoms)
+	{
+		key_type k(atom.labelAsymID(), atom.labelCompID(), atom.isWater() ? std::stoi(atom.authSeqID()) : atom.labelSeqID());
+		auto ri = resMap.find(k);
+
+		if (ri == resMap.end())
+		{
+			if (cif::VERBOSE > 0)
+				std::cerr << "Missing residue for atom " << atom << std::endl;
+
+			assert(false);
+
+			continue;
+		}
+
+		ri->second->addAtom(atom);
 	}
 }
 
@@ -1939,7 +1517,7 @@ AtomView Structure::waters() const
 
 	for (auto &a : mAtoms)
 	{
-		if (a.property<std::string>("label_entity_id") == waterEntityID)
+		if (a.get_property<std::string>("label_entity_id") == waterEntityID)
 			result.push_back(a);
 	}
 
@@ -1978,6 +1556,58 @@ Atom Structure::getAtomByLabel(const std::string &atomID, const std::string &asy
 	throw std::out_of_range("Could not find atom with specified label");
 }
 
+Atom Structure::getAtomByPosition(Point p) const
+{
+	double distance = std::numeric_limits<double>::max();
+	size_t index = std::numeric_limits<size_t>::max();
+
+	for (size_t i = 0; i < mAtoms.size(); ++i)
+	{
+		auto &a = mAtoms.at(i);
+
+		auto d = Distance(a.location(), p);
+		if (d < distance)
+		{
+			distance = d;
+			index = i;
+		}
+	}
+
+	if (index < mAtoms.size())
+		return mAtoms.at(index);
+
+	return {};
+}
+
+Atom Structure::getAtomByPositionAndType(Point p, std::string_view type, std::string_view res_type) const
+{
+	double distance = std::numeric_limits<double>::max();
+	size_t index = std::numeric_limits<size_t>::max();
+
+	for (size_t i = 0; i < mAtoms.size(); ++i)
+	{
+		auto &a = mAtoms.at(i);
+
+		if (a.labelCompID() != res_type)
+			continue;
+
+		if (a.labelAtomID() != type)
+			continue;
+
+		auto d = Distance(a.location(), p);
+		if (d < distance)
+		{
+			distance = d;
+			index = i;
+		}
+	}
+
+	if (index < mAtoms.size())
+		return mAtoms.at(index);
+
+	return {};
+}
+
 const Residue &Structure::getResidue(const std::string &asymID, const std::string &compID, int seqID) const
 {
 	for (auto &poly : mPolymers)
@@ -2014,12 +1644,45 @@ const Residue &Structure::getResidue(const std::string &asymID, const std::strin
 	throw std::out_of_range("Could not find residue " + asymID + '/' + std::to_string(seqID));
 }
 
+Residue &Structure::getResidue(const std::string &asymID, const std::string &compID, int seqID)
+{
+	return const_cast<Residue &>(const_cast<Structure const &>(*this).getResidue(asymID, compID, seqID));
+}
+
+const Residue &Structure::getResidue(const std::string &asymID) const
+{
+	for (auto &res : mNonPolymers)
+	{
+		if (res.asymID() != asymID)
+			continue;
+
+		return res;
+	}
+
+	throw std::out_of_range("Could not find residue " + asymID);
+}
+
+Residue &Structure::getResidue(const std::string &asymID)
+{
+	return const_cast<Residue &>(const_cast<Structure const &>(*this).getResidue(asymID));
+}
+
+Residue &Structure::getResidue(const mmcif::Atom &atom)
+{
+	return getResidue(atom.labelAsymID(), atom.labelCompID(), atom.labelSeqID());
+}
+
+const Residue &Structure::getResidue(const mmcif::Atom &atom) const
+{
+	return getResidue(atom.labelAsymID(), atom.labelCompID(), atom.labelSeqID());
+}
+
 File &Structure::getFile() const
 {
 	return mFile;
 }
 
-cif::Category &Structure::category(const char *name) const
+cif::Category &Structure::category(std::string_view name) const
 {
 	auto &db = datablock();
 	return db[name];
@@ -2197,8 +1860,7 @@ std::string Structure::insertCompound(const std::string &compoundID, bool isEnti
 	auto r = chemComp.find(cif::Key("id") == compoundID);
 	if (r.empty())
 	{
-		chemComp.emplace({
-			{"id", compoundID},
+		chemComp.emplace({{"id", compoundID},
 			{"name", compound->name()},
 			{"formula", compound->formula()},
 			{"formula_weight", compound->formulaWeight()},
@@ -2214,19 +1876,17 @@ std::string Structure::insertCompound(const std::string &compoundID, bool isEnti
 		{
 			entity_id = pdbxEntityNonpoly.find1<std::string>("comp_id"_key == compoundID, "entity_id");
 		}
-		catch(const std::exception& ex)
+		catch (const std::exception &ex)
 		{
 			auto &entity = db["entity"];
 			entity_id = entity.getUniqueID("");
 
-			entity.emplace({
-				{"id", entity_id},
+			entity.emplace({{"id", entity_id},
 				{"type", "non-polymer"},
 				{"pdbx_description", compound->name()},
 				{"formula_weight", compound->formulaWeight()}});
 
-			pdbxEntityNonpoly.emplace({
-				{"entity_id", entity_id},
+			pdbxEntityNonpoly.emplace({{"entity_id", entity_id},
 				{"name", compound->name()},
 				{"comp_id", compoundID}});
 		}
@@ -2235,65 +1895,6 @@ std::string Structure::insertCompound(const std::string &compoundID, bool isEnti
 	return entity_id;
 }
 
-// // --------------------------------------------------------------------
-
-// Structure::residue_iterator::residue_iterator(const Structure* s, poly_iterator polyIter, size_t polyResIndex, size_t nonPolyIndex)
-// 	: mStructure(*s), mPolyIter(polyIter), mPolyResIndex(polyResIndex), mNonPolyIndex(nonPolyIndex)
-// {
-// 	while (mPolyIter != mStructure.mPolymers.end() and mPolyResIndex == mPolyIter->size())
-// 		++mPolyIter;
-// }
-
-// auto Structure::residue_iterator::operator*() -> reference
-// {
-// 	if (mPolyIter != mStructure.mPolymers.end())
-// 		return (*mPolyIter)[mPolyResIndex];
-// 	else
-// 		return mStructure.mNonPolymers[mNonPolyIndex];
-// }
-
-// auto Structure::residue_iterator::operator->() -> pointer
-// {
-// 	if (mPolyIter != mStructure.mPolymers.end())
-// 		return &(*mPolyIter)[mPolyResIndex];
-// 	else
-// 		return &mStructure.mNonPolymers[mNonPolyIndex];
-// }
-
-// Structure::residue_iterator& Structure::residue_iterator::operator++()
-// {
-// 	if (mPolyIter != mStructure.mPolymers.end())
-// 	{
-// 		++mPolyResIndex;
-// 		if (mPolyResIndex >= mPolyIter->size())
-// 		{
-// 			++mPolyIter;
-// 			mPolyResIndex = 0;
-// 		}
-// 	}
-// 	else
-// 		++mNonPolyIndex;
-
-// 	return *this;
-// }
-
-// Structure::residue_iterator Structure::residue_iterator::operator++(int)
-// {
-// 	auto result = *this;
-// 	operator++();
-// 	return result;
-// }
-
-// bool Structure::residue_iterator::operator==(const Structure::residue_iterator& rhs) const
-// {
-// 	return mPolyIter == rhs.mPolyIter and mPolyResIndex == rhs.mPolyResIndex and mNonPolyIndex == rhs.mNonPolyIndex;
-// }
-
-// bool Structure::residue_iterator::operator!=(const Structure::residue_iterator& rhs) const
-// {
-// 	return mPolyIter != rhs.mPolyIter or mPolyResIndex != rhs.mPolyResIndex or mNonPolyIndex != rhs.mNonPolyIndex;
-// }
-
 // --------------------------------------------------------------------
 
 void Structure::removeAtom(Atom &a)
@@ -2340,7 +1941,7 @@ void Structure::swapAtoms(Atom &a1, Atom &a2)
 	auto l2 = r2["label_atom_id"];
 	l1.swap(l2);
 
-	a1.impl()->swapAtomLabels(*a2.impl());
+	std::swap(a1.mImpl->mAtomID, a2.mImpl->mAtomID);
 
 	auto l3 = r1["auth_atom_id"];
 	auto l4 = r2["auth_atom_id"];
@@ -2352,7 +1953,7 @@ void Structure::moveAtom(Atom &a, Point p)
 	a.location(p);
 }
 
-void Structure::changeResidue(const Residue &res, const std::string &newCompound,
+void Structure::changeResidue(Residue &res, const std::string &newCompound,
 	const std::vector<std::tuple<std::string, std::string>> &remappedAtoms)
 {
 	using namespace cif::literals;
@@ -2416,6 +2017,8 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
 	else
 		insertCompound(newCompound, false);
 
+	res.setCompoundID(newCompound);
+
 	auto &atomSites = db["atom_site"];
 	auto atoms = res.atoms();
 
@@ -2428,7 +2031,8 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
 			{ return a.labelAtomID() == a1; });
 		if (i == atoms.end())
 		{
-			std::cerr << "Missing atom for atom ID " << a1 << std::endl;
+			if (cif::VERBOSE >= 0)
+				std::cerr << "Missing atom for atom ID " << a1 << std::endl;
 			continue;
 		}
 
@@ -2437,8 +2041,15 @@ void Structure::changeResidue(const Residue &res, const std::string &newCompound
 		if (r.size() != 1)
 			continue;
 
-		if (a1 != a2)
-			r.front()["label_atom_id"] = a2;
+		if (a2.empty() or a2 == ".")
+			removeAtom(*i);
+		else if (a1 != a2)
+		{
+			auto ra = r.front();
+			ra["label_atom_id"] = a2;
+			ra["auth_atom_id"] = a2;
+			ra["type_symbol"] = AtomTypeTraits(compound->getAtomByID(a2).typeSymbol).symbol();
+		}
 	}
 
 	for (auto a : atoms)
@@ -2461,50 +2072,95 @@ std::string Structure::createNonpoly(const std::string &entity_id, const std::ve
 	auto &struct_asym = db["struct_asym"];
 	std::string asym_id = struct_asym.getUniqueID();
 
-	struct_asym.emplace({
-		{ "id", asym_id },
-		{ "pdbx_blank_PDB_chainid_flag", "N" },
-		{ "pdbx_modified", "N" },
-		{ "entity_id", entity_id },
-		{ "details", "?" }
-	});
+	struct_asym.emplace({{"id", asym_id},
+		{"pdbx_blank_PDB_chainid_flag", "N"},
+		{"pdbx_modified", "N"},
+		{"entity_id", entity_id},
+		{"details", "?"}});
+
+	std::string comp_id = db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
+
+	auto &atom_site = db["atom_site"];
+
+	auto &res = mNonPolymers.emplace_back(*this, comp_id, asym_id);
+
+	for (auto &atom : atoms)
+	{
+		auto atom_id = atom_site.getUniqueID("");
+
+		auto &&[row, inserted] = atom_site.emplace({
+			{"group_PDB", atom.get_property<std::string>("group_PDB")},
+			{"id", atom_id},
+			{"type_symbol", atom.get_property<std::string>("type_symbol")},
+			{"label_atom_id", atom.get_property<std::string>("label_atom_id")},
+			{"label_alt_id", atom.get_property<std::string>("label_alt_id")},
+			{"label_comp_id", comp_id},
+			{"label_asym_id", asym_id},
+			{"label_entity_id", entity_id},
+			{"label_seq_id", "."},
+			{"pdbx_PDB_ins_code", ""},
+			{"Cartn_x", atom.get_property<std::string>("Cartn_x")},
+			{"Cartn_y", atom.get_property<std::string>("Cartn_y")},
+			{"Cartn_z", atom.get_property<std::string>("Cartn_z")},
+			{"occupancy", atom.get_property<std::string>("occupancy")},
+			{"B_iso_or_equiv", atom.get_property<std::string>("B_iso_or_equiv")},
+			{"pdbx_formal_charge", atom.get_property<std::string>("pdbx_formal_charge")},
+			{"auth_seq_id", ""},
+			{"auth_comp_id", comp_id},
+			{"auth_asym_id", asym_id},
+			{"auth_atom_id", atom.get_property<std::string>("label_atom_id")},
+			{"pdbx_PDB_model_num", 1}});
+
+		auto &newAtom = mAtoms.emplace_back(std::make_shared<Atom::AtomImpl>(db, atom_id, row));
+		res.addAtom(newAtom);
+	}
+
+	return asym_id;
+}
+
+std::string Structure::createNonpoly(const std::string &entity_id, std::vector<std::vector<cif::Item>> &atom_info)
+{
+	using namespace cif::literals;
+
+	cif::Datablock &db = *mFile.impl().mDb;
+	auto &struct_asym = db["struct_asym"];
+	std::string asym_id = struct_asym.getUniqueID();
+
+	struct_asym.emplace({{"id", asym_id},
+		{"pdbx_blank_PDB_chainid_flag", "N"},
+		{"pdbx_modified", "N"},
+		{"entity_id", entity_id},
+		{"details", "?"}});
 
 	std::string comp_id = db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
 
 	auto &atom_site = db["atom_site"];
 
-	for (auto& atom: atoms)
+	auto &res = mNonPolymers.emplace_back(*this, comp_id, asym_id);
+
+	for (auto &atom : atom_info)
 	{
 		auto atom_id = atom_site.getUniqueID("");
 
-		auto &&[row, inserted ] = atom_site.emplace({
-			{ "group_PDB",			atom.property<std::string>("group_PDB") },
-			{ "id",					atom_id },
-			{ "type_symbol",		atom.property<std::string>("type_symbol") },
-			{ "label_atom_id",		atom.property<std::string>("label_atom_id") },
-			{ "label_alt_id",		atom.property<std::string>("label_alt_id") },
-			{ "label_comp_id",		comp_id },
-			{ "label_asym_id",		asym_id },
-			{ "label_entity_id",	entity_id },
-			{ "label_seq_id",		"." },
-			{ "pdbx_PDB_ins_code",	"" },
-			{ "Cartn_x",			atom.property<std::string>("Cartn_x") },
-			{ "Cartn_y",			atom.property<std::string>("Cartn_y") },
-			{ "Cartn_z",			atom.property<std::string>("Cartn_z") },
-			{ "occupancy",			atom.property<std::string>("occupancy") },
-			{ "B_iso_or_equiv",		atom.property<std::string>("B_iso_or_equiv") },
-			{ "pdbx_formal_charge",	atom.property<std::string>("pdbx_formal_charge") },
-			{ "auth_seq_id",		"" },
-			{ "auth_comp_id",		comp_id },
-			{ "auth_asym_id",		asym_id },
-			{ "auth_atom_id",		atom.property<std::string>("label_atom_id") },
-			{ "pdbx_PDB_model_num",	1 }
+		atom.insert(atom.end(), {
+			{"group_PDB", "HETATM"},
+			{"id", atom_id},
+			{"label_comp_id", comp_id},
+			{"label_asym_id", asym_id},
+			{"label_seq_id", ""},
+			{"label_entity_id", entity_id},
+			{"auth_comp_id", comp_id},
+			{"auth_asym_id", asym_id},
+			{"auth_seq_id", ""},
+			{"pdbx_PDB_model_num", 1},
+			{"label_alt_id", ""}
 		});
 
-		mAtoms.emplace_back(new AtomImpl(db, atom_id, row));
-	}
+		auto &&[row, inserted] = atom_site.emplace(atom.begin(), atom.end());
 
-	mNonPolymers.emplace_back(*this, comp_id, asym_id);
+		auto &newAtom = mAtoms.emplace_back(std::make_shared<Atom::AtomImpl>(db, atom_id, row));
+		res.addAtom(newAtom);
+	}
 
 	return asym_id;
 }
@@ -2582,7 +2238,7 @@ void Structure::cleanupEmptyCategories()
 		{
 			// is this correct?
 			std::set<std::string> asym_ids;
-			for (const auto &[ asym_id ] : db["pdbx_branch_scheme"].find<std::string>("entity_id"_key == id, "asym_id"))
+			for (const auto &[asym_id] : db["pdbx_branch_scheme"].find<std::string>("entity_id"_key == id, "asym_id"))
 				asym_ids.insert(asym_id);
 			count = asym_ids.size();
 		}
@@ -2593,14 +2249,26 @@ void Structure::cleanupEmptyCategories()
 
 void Structure::translate(Point t)
 {
-	for (auto& a: mAtoms)
+	for (auto &a : mAtoms)
 		a.translate(t);
 }
 
 void Structure::rotate(Quaternion q)
 {
-	for (auto& a: mAtoms)
+	for (auto &a : mAtoms)
 		a.rotate(q);
 }
 
+void Structure::translateAndRotate(Point t, Quaternion q)
+{
+	for (auto &a : mAtoms)
+		a.translateAndRotate(t, q);
+}
+
+void Structure::translateRotateAndTranslate(Point t1, Quaternion q, Point t2)
+{
+	for (auto &a : mAtoms)
+		a.translateRotateAndTranslate(t1, q, t2);
+}
+
 } // namespace mmcif
diff --git a/src/Symmetry.cpp b/src/Symmetry.cpp
index da0ba2f..49f583e 100644
--- a/src/Symmetry.cpp
+++ b/src/Symmetry.cpp
@@ -90,4 +90,66 @@ int GetSpacegroupNumber(std::string spacegroup)
 	return result;
 }
 
+// --------------------------------------------------------------------
+
+int GetSpacegroupNumber(std::string spacegroup, SpacegroupName type)
+{
+	if (spacegroup == "P 21 21 2 A")
+		spacegroup = "P 21 21 2 (a)";
+	else if (spacegroup.empty())
+		throw std::runtime_error("No spacegroup, cannot continue");
+
+	int result = 0;
+
+	if (type == SpacegroupName::full)
+	{
+		const size_t N = kNrOfSpaceGroups;
+		int32_t L = 0, R = static_cast<int32_t>(N - 1);
+		while (L <= R)
+		{
+			int32_t i = (L + R) / 2;
+
+			int d = spacegroup.compare(kSpaceGroups[i].name);
+
+			if (d > 0)
+				L = i + 1;
+			else if (d < 0)
+				R = i - 1;
+			else
+			{
+				result = kSpaceGroups[i].nr;
+				break;
+			}
+		}
+	}
+	else if (type == SpacegroupName::xHM)
+	{
+		for (auto &sg : kSpaceGroups)
+		{
+			if (sg.xHM == spacegroup)
+			{
+				result = sg.nr;
+				break;
+			}
+		}
+	}
+	else
+	{
+		for (auto &sg : kSpaceGroups)
+		{
+			if (sg.Hall == spacegroup)
+			{
+				result = sg.nr;
+				break;
+			}
+		}
+	}
+
+	// not found, see if we can find a match based on xHM name
+	if (result == 0)
+		throw std::runtime_error("Spacegroup name " + spacegroup + " was not found in table");
+	
+	return result;
+}
+
 }
diff --git a/src/TlsParser.cpp b/src/TlsParser.cpp
index b18169f..a1a9fa7 100644
--- a/src/TlsParser.cpp
+++ b/src/TlsParser.cpp
@@ -248,7 +248,7 @@ struct TLSSelectionNot : public TLSSelection
 		for (auto& r: residues)
 			r.selected = not r.selected;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "NOT" << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -267,7 +267,7 @@ struct TLSSelectionAll : public TLSSelection
 		for (auto& r: residues)
 			r.selected = true;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "ALL" << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -287,7 +287,7 @@ struct TLSSelectionChain : public TLSSelectionAll
 		for (auto& r: residues)
 			r.selected = allChains or r.chainID == m_chain;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "CHAIN " << m_chain << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -307,7 +307,7 @@ struct TLSSelectionResID : public TLSSelectionAll
 		for (auto& r: residues)
 			r.selected = r.seqNr == m_seq_nr and r.iCode == m_icode;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "ResID " << m_seq_nr << (m_icode ? std::string { m_icode} : "") << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -331,7 +331,7 @@ struct TLSSelectionRangeSeq : public TLSSelectionAll
 						  (r.seqNr <= m_last or m_last == kResidueNrWildcard));
 		}
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "Range " << m_first << ':' << m_last << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -374,7 +374,7 @@ struct TLSSelectionRangeID : public TLSSelectionAll
 			}
 		}
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "Through " << m_first << ':' << m_last << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -407,7 +407,7 @@ struct TLSSelectionUnion : public TLSSelection
 		for (auto ai = a.begin(), bi = b.begin(), ri = residues.begin(); ri != residues.end(); ++ai, ++bi, ++ri)
 			ri->selected = ai->selected or bi->selected;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "Union" << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -440,7 +440,7 @@ struct TLSSelectionIntersection : public TLSSelection
 		for (auto ai = a.begin(), bi = b.begin(), ri = residues.begin(); ri != residues.end(); ++ai, ++bi, ++ri)
 			ri->selected = ai->selected and bi->selected;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "Intersection" << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -462,7 +462,7 @@ struct TLSSelectionByName : public TLSSelectionAll
 		for (auto& r: residues)
 			r.selected = r.name == m_name;
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "Name " << m_name << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -488,7 +488,7 @@ struct TLSSelectionByElement : public TLSSelectionAll
 		for (auto& r: residues)
 			r.selected = iequals(r.name, m_element);
 
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 		{
 			std::cout << std::string(indentLevel * 2, ' ') << "Element " << m_element << std::endl;
 			DumpSelection(residues, indentLevel);
@@ -890,7 +890,7 @@ TLSSelectionPtr TLSSelectionParserImplPhenix::Parse()
 
 	Match(pt_EOLN);
 	
-	if (extraParenthesis)
+	if (extraParenthesis and cif::VERBOSE > 0)
 		std::cerr << "WARNING: too many closing parenthesis in TLS selection statement" << std::endl;
 	
 	return result;
@@ -931,7 +931,7 @@ TLSSelectionPtr TLSSelectionParserImplPhenix::ParseFactor()
 		case '(':
 			Match('(');
 			result = ParseAtomSelection();
-			if (m_lookahead == pt_EOLN)
+			if (m_lookahead == pt_EOLN and cif::VERBOSE > 0)
 				std::cerr << "WARNING: missing closing parenthesis in TLS selection statement" << std::endl;
 			else
 				Match(')');
@@ -1033,7 +1033,7 @@ TLSSelectionPtr TLSSelectionParserImplPhenix::ParseFactor()
 					result.reset(new TLSSelectionRangeID(from, to, icode_from, icode_to));
 				else
 				{
-					if (cif::VERBOSE and (icode_from or icode_to))
+					if (cif::VERBOSE > 0 and (icode_from or icode_to))
 						std::cerr << "Warning, ignoring insertion codes" << std::endl;
 					
 					result.reset(new TLSSelectionRangeSeq(from, to));
@@ -1231,7 +1231,8 @@ TLSSelectionPtr TLSSelectionParserImplBuster::ParseGroup()
 				std::tie(chain2, seqNr2) = ParseAtom();
 				if (chain1 != chain2)
 				{
-					std::cerr << "Warning, ranges over multiple chains detected" << std::endl;
+					if (cif::VERBOSE > 0)
+						std::cerr << "Warning, ranges over multiple chains detected" << std::endl;
 					
 					TLSSelectionPtr sc1(new TLSSelectionChain(chain1));
 					TLSSelectionPtr sr1(new TLSSelectionRangeSeq(seqNr1, kResidueNrWildcard));
@@ -1289,7 +1290,7 @@ std::tuple<std::string,int> TLSSelectionParserImplBuster::ParseAtom()
 			Match(':');
 			std::string atom = m_value_s;
 			
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Warning: ignoring atom ID '" << atom << "' in TLS selection" << std::endl;
 			
 			Match(bt_IDENT);
@@ -1810,7 +1811,8 @@ class TLSSelectionParser
 		}
 		catch (const std::exception& ex)
 		{
-			std::cerr << "ParseError: " << ex.what() << std::endl;
+			if (cif::VERBOSE >= 0)
+				std::cerr << "ParseError: " << ex.what() << std::endl;
 		}
 		
 		return result;
@@ -1834,14 +1836,14 @@ TLSSelectionPtr ParseSelectionDetails(const std::string& program, const std::str
 
 		if (not result)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Falling back to old BUSTER" << std::endl;
 			result = busterOld.Parse(selection);
 		}
 		
 		if (not result)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Falling back to PHENIX" << std::endl;
 			result = phenix.Parse(selection);
 		}
@@ -1852,35 +1854,35 @@ TLSSelectionPtr ParseSelectionDetails(const std::string& program, const std::str
 
 		if (not result)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Falling back to BUSTER" << std::endl;
 			result = buster.Parse(selection);
 		}
 
 		if (not result)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Falling back to old BUSTER" << std::endl;
 			result = busterOld.Parse(selection);
 		}
 	}
 	else
 	{
-		if (cif::VERBOSE)
+		if (cif::VERBOSE > 0)
 			std::cerr << "No known program specified, trying PHENIX" << std::endl;
 
 		result = phenix.Parse(selection);
 
 		if (not result)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Falling back to BUSTER" << std::endl;
 			result = buster.Parse(selection);
 		}
 
 		if (not result)
 		{
-			if (cif::VERBOSE)
+			if (cif::VERBOSE > 0)
 				std::cerr << "Falling back to old BUSTER" << std::endl;
 			result = busterOld.Parse(selection);
 		}
diff --git a/src/revision.hpp.in b/src/revision.hpp.in
deleted file mode 100644
index 365e5c9..0000000
--- a/src/revision.hpp.in
+++ /dev/null
@@ -1,5 +0,0 @@
-const char kRevision[] = R"(
-lib@PROJECT_NAME@-version: @PROJECT_VERSION@
-@BUILD_VERSION_STRING@
-Date: @BUILD_DATE_TIME@
-)";
diff --git a/test/pdb2cif-test.cpp b/test/pdb2cif-test.cpp
index 167fbd2..1caa9db 100644
--- a/test/pdb2cif-test.cpp
+++ b/test/pdb2cif-test.cpp
@@ -18,7 +18,6 @@ int main(int argc, char* argv[])
 	desc.add_options()
 		("input,i",		po::value<std::string>(),	"Input file")
 		("help,h",									"Display help message")
-		("version",									"Print version")
 		("verbose,v",								"Verbose output")
 		("debug,d",		po::value<int>(),			"Debug level (for even more verbose output)");
 
@@ -29,12 +28,6 @@ int main(int argc, char* argv[])
 	po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
 	po::notify(vm);
 
-	if (vm.count("version"))
-	{
-		std::cout << argv[0] << " version " PACKAGE_VERSION << std::endl;
-		exit(0);
-	}
-
 	if (vm.count("help") or vm.count("input") == 0)
 	{
 		std::cerr << desc << std::endl;
diff --git a/test/rename-compound-test.cpp b/test/rename-compound-test.cpp
index 7989b35..2c61301 100644
--- a/test/rename-compound-test.cpp
+++ b/test/rename-compound-test.cpp
@@ -20,8 +20,11 @@ int main(int argc, char* argv[])
 		if (argc == 3)
 			testdir = argv[2];
 
-		if (std::filesystem::exists(testdir / ".."/"data"/"ccd-subset.cif"))
-			cif::addFileResource("components.cif", testdir / ".."/"data"/"ccd-subset.cif");
+		if (std::filesystem::exists(testdir / ".." / "data" / "ccd-subset.cif"))
+			cif::addFileResource("components.cif", testdir / ".." / "data" / "ccd-subset.cif");
+
+		if (std::filesystem::exists(testdir / ".." / "rsrc" / "mmcif_pdbx_v50.dic"))
+			cif::addFileResource("mmcif_pdbx_v50.dic", testdir / ".." / "rsrc" / "mmcif_pdbx_v50.dic");
 
 		mmcif::CompoundFactory::instance().pushDictionary(testdir / "REA.cif");
 		mmcif::CompoundFactory::instance().pushDictionary(testdir / "RXA.cif");
diff --git a/test/structure-test.cpp b/test/structure-test.cpp
index 8466808..51629d9 100644
--- a/test/structure-test.cpp
+++ b/test/structure-test.cpp
@@ -77,9 +77,6 @@ BOOST_AUTO_TEST_CASE(create_nonpoly_1)
 {
     cif::VERBOSE = 1;
 
-	// do this now, avoids the need for installing
-	cif::addFileResource("mmcif_pdbx_v50.dic", "../rsrc/mmcif_pdbx_v50.dic");
-
 	mmcif::File file;
 	file.file().loadDictionary("mmcif_pdbx_v50.dic");
 	file.createDatablock("TEST");	// create a datablock
@@ -182,3 +179,23 @@ _struct_asym.details                       ?
 				<< structure.getFile().data() << std::endl;
 	}
 }
+
+// // --------------------------------------------------------------------
+
+// BOOST_AUTO_TEST_CASE(test_load_1)
+// {
+// 	mmcif::File cf(gTestDir / "5v3g.cif.gz");
+// 	mmcif::Structure s(cf);
+
+// 	for (auto &poly : s.polymers())
+// 	{
+// 		std::cout << std::string(80, '=') << std::endl;
+// 		for (auto &res : poly)
+// 		{
+// 			std::cout << res << std::endl;
+
+// 			for (auto &atom : res.atoms())
+// 				std::cout << "  " << atom << std::endl;
+// 		}
+// 	}
+// }
diff --git a/test/unit-test.cpp b/test/unit-test.cpp
index bd3c753..7f8c9d3 100644
--- a/test/unit-test.cpp
+++ b/test/unit-test.cpp
@@ -33,6 +33,10 @@
 // #include "cif++/DistanceMap.hpp"
 #include "cif++/Cif++.hpp"
 #include "cif++/BondMap.hpp"
+#include "cif++/CifValidator.hpp"
+
+namespace tt = boost::test_tools;
+
 
 std::filesystem::path gTestDir = std::filesystem::current_path();	// filled in first test
 
@@ -259,8 +263,10 @@ save__cat_2.desc
 
     std::istream is_dict(&buffer);
 
+	cif::Validator validator("test", is_dict);
+
     cif::File f;
-    f.loadDictionary(is_dict);
+    f.setValidator(&validator);
 
     // --------------------------------------------------------------------
 
@@ -387,8 +393,10 @@ save__cat_1.c
 
     std::istream is_dict(&buffer);
 
+	cif::Validator validator("test", is_dict);
+
     cif::File f;
-    f.loadDictionary(is_dict);
+    f.setValidator(&validator);
 
     // --------------------------------------------------------------------
 
@@ -535,8 +543,10 @@ save__cat_2.desc
 
     std::istream is_dict(&buffer);
 
+	cif::Validator validator("test", is_dict);
+
     cif::File f;
-    f.loadDictionary(is_dict);
+    f.setValidator(&validator);
 
     // --------------------------------------------------------------------
 
@@ -741,8 +751,10 @@ save__cat_2.parent_id3
 
     std::istream is_dict(&buffer);
 
+	cif::Validator validator("test", is_dict);
+
     cif::File f;
-    f.loadDictionary(is_dict);
+    f.setValidator(&validator);
 
     // --------------------------------------------------------------------
 
@@ -963,8 +975,10 @@ cat_2 3 cat_2:cat_1:3
 
     std::istream is_dict(&buffer);
 
+	cif::Validator validator("test", is_dict);
+
     cif::File f;
-    f.loadDictionary(is_dict);
+    f.setValidator(&validator);
 
     // --------------------------------------------------------------------
 
@@ -1389,9 +1403,10 @@ cat_2 1 '_cat_2.num'  '_cat_3.num'  cat_3
     } buffer(const_cast<char*>(dict), sizeof(dict) - 1);
 
     std::istream is_dict(&buffer);
+	cif::Validator validator("test", is_dict);
 
     cif::File f;
-    f.loadDictionary(is_dict);
+    f.setValidator(&validator);
 
     // --------------------------------------------------------------------
 
@@ -1687,4 +1702,58 @@ BOOST_AUTO_TEST_CASE(reading_file_1)
 
 	cif::File file;
 	BOOST_CHECK_THROW(file.load(is), std::runtime_error);
-}
\ No newline at end of file
+}
+
+// 3d tests
+
+using namespace mmcif;
+
+BOOST_AUTO_TEST_CASE(t1)
+{
+	// std::random_device rnd;
+	// std::mt19937 gen(rnd());
+	// std::uniform_real_distribution<float> dis(0, 1);
+
+	// Quaternion q{ dis(gen), dis(gen), dis(gen), dis(gen) };
+	// q = Normalize(q);
+
+	// Quaternion q{ 0.1, 0.2, 0.3, 0.4 };
+	Quaternion q{ 0.5, 0.5, 0.5, 0.5 };
+	q = Normalize(q);
+
+	const auto &&[angle0, axis0] = QuaternionToAngleAxis(q);
+
+	std::vector<Point> p1{
+		{ 16.979, 13.301, 44.555 },
+		{ 18.150, 13.525, 43.680 },
+		{ 18.656, 14.966, 43.784 },
+		{ 17.890, 15.889, 44.078 },
+		{ 17.678, 13.270, 42.255 },
+		{ 16.248, 13.734, 42.347 },
+		{ 15.762, 13.216, 43.724 }
+	};
+
+	auto p2 = p1;
+
+	CenterPoints(p1);
+
+	for (auto &p : p2)
+		p.rotate(q);
+	
+	CenterPoints(p2);
+
+	auto q2 = AlignPoints(p1, p2);
+
+	const auto &&[angle, axis] = QuaternionToAngleAxis(q2);
+
+	BOOST_TEST(std::fmod(360 + angle, 360) == std::fmod(360 - angle0, 360), tt::tolerance(0.01));
+
+	for (auto &p : p1)
+		p.rotate(q2);
+
+	float rmsd = RMSd(p1, p2);
+
+	BOOST_TEST(rmsd < 1e-5);
+
+	// std::cout << "rmsd: " << RMSd(p1, p2) << std::endl;
+}