Commit f4a6533f by Maarten L. Hekkelman

eerste fröbelwerk

parent df1b6a13
......@@ -40,7 +40,7 @@ include(Dart)
include(GenerateExportHeader)
set(CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
......@@ -395,7 +395,8 @@ if(CIFPP_BUILD_TESTS)
rename-compound
structure
sugar
unit)
unit
unit-v2)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
......
......@@ -12,7 +12,7 @@ include(CheckCXXSourceRuns)
cmake_push_check_state()
set(CMAKE_CXX_STANDARD 17)
# set(CMAKE_CXX_STANDARD 17)
check_include_file_cxx("atomic" _CXX_ATOMIC_HAVE_HEADER)
mark_as_advanced(_CXX_ATOMIC_HAVE_HEADER)
......
......@@ -12,7 +12,7 @@ include(CheckCXXSourceCompiles)
cmake_push_check_state()
set(CMAKE_CXX_STANDARD 17)
# set(CMAKE_CXX_STANDARD 17)
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
......
/*-
* 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
* 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.
*/
#pragma once
#include <filesystem>
#include <forward_list>
#include <list>
#include <map>
#include <string>
#include <scoped_allocator>
#include "cif++/CifUtils.hpp"
namespace cif_v2
{
template <typename Alloc = std::allocator<void>>
class item_t
{
public:
item_t() = default;
item_t(std::string_view name, char value)
: m_name(name)
, m_value(value)
{
}
#if defined(__cpp_lib_format)
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
item_t(std::string_view name, const T &value, int precision)
: m_name(name)
, m_value(std::format(".{}f", value, precision))
{
}
#endif
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
item_t(const std::string_view name, const T &value)
: m_name(name)
, m_value(std::to_string(value))
{
}
item_t(const std::string_view name, const std::string_view value)
: m_name(name)
, m_value(value)
{
}
item_t(const item_t &rhs) = default;
item_t(item_t &&rhs) noexcept = default;
item_t &operator=(const item_t &rhs) = default;
item_t &operator=(item_t &&rhs) noexcept = default;
const std::string &name() const { return m_name; }
const std::string &value() const { return m_value; }
void value(const std::string &v) { m_value = v; }
/// \brief empty means either null or unknown
bool empty() const { return m_value.empty(); }
/// \brief returns true if the field contains '.'
bool is_null() const { return m_value == "."; }
/// \brief returns true if the field contains '?'
bool is_unknown() const { return m_value == "?"; }
size_t length() const { return m_value.length(); }
const char *c_str() const { return m_value.c_str(); }
private:
std::string m_name;
std::string m_value;
};
using item = item_t<>;
// --------------------------------------------------------------------
// template <
// typename Item = item,
// typename Alloc = std::allocator<Item>>
// class row_t : public std::list<Item, Alloc>
// {
// public:
// using value_type = Item;
// using base_type = std::list<Item, Alloc>;
// using allocator_type = Alloc;
// row_t() = default;
// row_t(const std::string &name, const allocator_type &alloc = allocator_type())
// : base_type(alloc)
// , m_name(name) {}
// row_t(const row_t &) = default;
// row_t(row_t &&) = default;
// template<typename Alloc2>
// row_t(const row_t &r, const Alloc2 &a)
// : base_type(r, a)
// , m_name(r.m_name)
// {
// }
// template<typename Alloc2>
// row_t(row_t &&r, const Alloc2 &a)
// : base_type(std::move(r), a)
// , m_name(r.m_name)
// {
// }
// row_t &operator=(const row_t &) = default;
// row_t &operator=(row_t &&) = default;
// row_t(std::initializer_list<item_type> items, const allocator_type &alloc = allocator_type())
// {
// }
// // --------------------------------------------------------------------
// const std::string &name() const { return m_name; }
// const std::string &value() const { return m_value; }
// private:
// std::string m_name;
// std::string m_value;
// };
// using row = row_t<>;
// --------------------------------------------------------------------
namespace detail
{
struct item_value
{
item_value *m_next;
uint32_t m_column_ix;
char m_text[4];
};
}
// --------------------------------------------------------------------
template <typename Item = item, typename Alloc = std::allocator<Item>>
using row = std::forward_list<Item, Alloc>;
// --------------------------------------------------------------------
template <
typename Alloc = std::allocator<std::byte>>
class category_t
{
public:
using item_type = Item;
using value_type = Row;
using base_type = std::list<Row, Alloc>;
using allocator_type = Alloc;
category_t() = default;
category_t(std::string_view name, const allocator_type &alloc = allocator_type())
: base_type(alloc)
, m_name(name)
{
}
category_t(const category_t &) = default;
category_t(category_t &&) = default;
template <typename Alloc2>
category_t(const category_t &c, const Alloc2 &a)
: base_type(c, a)
, m_name(c.m_name)
{
}
template <typename Alloc2>
category_t(category_t &&c, const Alloc2 &a)
: base_type(std::move(c), a)
, m_name(c.m_name)
{
}
category_t &operator=(const category_t &) = default;
category_t &operator=(category_t &&) = default;
// --------------------------------------------------------------------
const std::string &name() const { return m_name; }
void emplace(value_type &&row);
void emplace(std::initializer_list<item_type> items)
{
this->emplace_back(value_type{items});
}
// void write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns)
// {
// if (empty())
// return;
// // If there are multiple rows in this category, we need a _loop
// if (size() == 1)
// {
// os << "loop_" << std::endl;
// std::vector<size_t> columnWidths;
// for (auto cix : order)
// {
// auto &col = mColumns[cix];
// os << '_' << mName << '.' << col.mName << ' ' << std::endl;
// columnWidths.push_back(2);
// }
// for (auto Row = mHead; Row != nullptr; Row = Row->mNext)
// {
// for (auto v = Row->mValues; v != nullptr; v = v->mNext)
// {
// if (strchr(v->mText, '\n') == nullptr)
// {
// size_t l = strlen(v->mText);
// if (not isUnquotedString(v->mText))
// l += 2;
// if (l > 132)
// continue;
// if (columnWidths[v->mColumnIndex] < l + 1)
// columnWidths[v->mColumnIndex] = l + 1;
// }
// }
// }
// for (auto Row = mHead; Row != nullptr; Row = Row->mNext) // loop over rows
// {
// size_t offset = 0;
// for (size_t cix : order)
// {
// size_t w = columnWidths[cix];
// std::string s;
// for (auto iv = Row->mValues; iv != nullptr; iv = iv->mNext)
// {
// if (iv->mColumnIndex == cix)
// {
// s = iv->mText;
// break;
// }
// }
// if (s.empty())
// s = "?";
// size_t l = s.length();
// if (not isUnquotedString(s.c_str()))
// l += 2;
// if (l < w)
// l = w;
// if (offset + l > 132 and offset > 0)
// {
// os << std::endl;
// offset = 0;
// }
// offset = detail::writeValue(os, s, offset, w);
// if (offset > 132)
// {
// os << std::endl;
// offset = 0;
// }
// }
// if (offset > 0)
// os << std::endl;
// }
// }
// else
// {
// // first find the indent level
// size_t l = 0;
// for (auto &col : mColumns)
// {
// std::string tag = '_' + mName + '.' + col.mName;
// if (l < tag.length())
// l = tag.length();
// }
// l += 3;
// for (size_t cix : order)
// {
// auto &col = mColumns[cix];
// os << '_' << mName << '.' << col.mName << std::string(l - col.mName.length() - mName.length() - 2, ' ');
// std::string s;
// for (auto iv = mHead->mValues; iv != nullptr; iv = iv->mNext)
// {
// if (iv->mColumnIndex == cix)
// {
// s = iv->mText;
// break;
// }
// }
// if (s.empty())
// s = "?";
// size_t offset = l;
// if (s.length() + l >= kMaxLineLength)
// {
// os << std::endl;
// offset = 0;
// }
// if (detail::writeValue(os, s, offset, 1) != 0)
// os << std::endl;
// }
// }
// os << "# " << std::endl;
// }
void write(std::ostream &os) const
{
// std::vector<size_t> order(mColumns.size());
// iota(order.begin(), order.end(), 0);
// write(os, order, false);
os << '#' << m_name << std::endl;
for (auto &r : *this)
{
for (auto &f : r)
os << '_' << m_name << '.' << f.name() << ' ' << f.value() << std::endl;
}
}
// void Category::write(std::ostream &os, const std::vector<std::string> &columns)
// {
// // make sure all columns are present
// for (auto &c : columns)
// addColumn(c);
// std::vector<size_t> order;
// order.reserve(mColumns.size());
// for (auto &c : columns)
// order.push_back(getColumnIndex(c));
// for (size_t i = 0; i < mColumns.size(); ++i)
// {
// if (std::find(order.begin(), order.end(), i) == order.end())
// order.push_back(i);
// }
// write(os, order, true);
// }
private:
struct item_column
{
std::string m_name;
// column_validator *validator;
};
struct item_row
{
};
std::string m_name;
std::vector<item_column> m_columns;
};
using category = category_t<>;
// --------------------------------------------------------------------
template <
typename Category = category,
typename Alloc = std::allocator<Category>>
class datablock_t : public std::list<Category, Alloc>
{
public:
using category_type = Category;
using base_type = std::list<category_type, Alloc>;
using allocator_type = Alloc;
datablock_t(const std::string &name, const allocator_type &alloc = allocator_type())
: base_type(alloc)
, m_name(name)
{
}
datablock_t(const datablock_t &) = default;
datablock_t(datablock_t &&) = default;
template <typename Alloc2>
datablock_t(const datablock_t &db, const Alloc2 &a)
: base_type(db, a)
, m_name(db.m_name)
{
}
template <typename Alloc2>
datablock_t(datablock_t &&db, const Alloc2 &a)
: base_type(std::move(db), a)
, m_name(db.m_name)
{
}
datablock_t &operator=(const datablock_t &) = default;
datablock_t &operator=(datablock_t &&) = default;
// --------------------------------------------------------------------
const std::string &name() const { return m_name; }
// --------------------------------------------------------------------
category_type &operator[](std::string_view name)
{
auto i = std::find_if(this->begin(), this->end(), [name](const category_type &c)
{ return iequals(c.name(), name); });
if (i == this->end())
i = this->emplace(name);
return *i;
}
const category_type &operator[](std::string_view name) const
{
static const category_type s_empty;
auto i = std::find_if(this->begin(), this->end(), [name](const category_type &c)
{ return iequals(c.name(), name); });
return i == this->end() ? s_empty : *i;
}
void write(std::ostream &os) const
{
// std::shared_lock lock(mLock);
os << "data_" << m_name << std::endl
<< "# " << std::endl;
// mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
for (auto &cat : *this)
{
if (cat.name() != "entry")
continue;
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 : *this)
{
if (cat.name() != "entry" and cat.name() != "audit_conform")
cat.write(os);
}
}
friend std::ostream &operator<<(std::ostream &os, const datablock_t &db)
{
db.write(os);
return os;
}
private:
std::string m_name;
};
using datablock = datablock_t<>;
// --------------------------------------------------------------------
template <
typename Datablock = datablock,
typename Alloc = std::allocator<Datablock>>
class file_t : public std::list<Datablock, Alloc>
{
public:
using value_type = Datablock;
using base_type = std::list<value_type, Alloc>;
using allocator_type = Alloc;
file_t() = default;
file_t(std::istream &is, const allocator_type &alloc = allocator_type())
{
}
file_t(const file_t &) = default;
file_t(file_t &&) = default;
file_t &operator=(const file_t &) = default;
file_t &operator=(file_t &&) = default;
};
using file = file_t<>;
} // namespace cif_v2
......@@ -835,7 +835,8 @@ class rsrc_data
private:
rsrc_data()
{
if (gResourceIndex and (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0) and gResourceIndex and gResourceName)
// if (gResourceIndex and (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0) and gResourceIndex and gResourceName)
if (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0)
{
m_index = gResourceIndex;
m_data = gResourceData;
......@@ -1292,7 +1293,8 @@ std::unique_ptr<std::istream> ResourcePool::load(fs::path name)
result = open(p2);
}
if (not result and gResourceData)
// if (not result and gResourceData)
if (not result and (gResourceIndex[0].m_child > 0 or gResourceIndex[0].m_size > 0))
{
mrsrc::rsrc rsrc(name);
if (rsrc)
......
......@@ -5940,7 +5940,7 @@ int PDBFileParser::PDBChain::AlignResToSeqRes()
y = highY;
// C++ is getting closer to Pascal :-)
auto printAlignment = [=]()
auto printAlignment = [=,this]()
{
std::cerr << std::string(cif::get_terminal_width(), '-') << std::endl
<< "Alignment for chain " << mDbref.chainID << std::endl
......
......@@ -356,12 +356,12 @@ struct TLSSelectionRangeID : public TLSSelectionAll
for (std::string chain: chains)
{
auto f = std::find_if(residues.begin(), residues.end(),
[=](auto r) -> bool {
[=,this](auto r) -> bool {
return r.chainID == chain and r.seqNr == m_first and r.iCode == m_icode_first;
});
auto l = std::find_if(residues.begin(), residues.end(),
[=](auto r) -> bool {
[=,this](auto r) -> bool {
return r.chainID == chain and r.seqNr == m_last and r.iCode == m_icode_last;
});
......
/*-
* 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
* 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.
*/
#define BOOST_TEST_ALTERNATIVE_INIT_API
#include <boost/test/included/unit_test.hpp>
#include <stdexcept>
// #include "cif++/DistanceMap.hpp"
// #include "cif++/BondMap.hpp"
#include "cif++/Cif++-v2.hpp"
// #include "cif++/CifValidator.hpp"
// #include "cif++/CifParser.hpp"
namespace tt = boost::test_tools;
std::filesystem::path gTestDir = std::filesystem::current_path(); // filled in first test
// --------------------------------------------------------------------
cif_v2::file operator""_cf(const char *text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(text), length);
std::istream is(&buffer);
return cif_v2::file(is);
}
// --------------------------------------------------------------------
bool init_unit_test()
{
// cif::VERBOSE = 1;
// // not a test, just initialize test dir
// if (boost::unit_test::framework::master_test_suite().argc == 2)
// gTestDir = boost::unit_test::framework::master_test_suite().argv[1];
// // do this now, avoids the need for installing
// cif::addFileResource("mmcif_pdbx_v50.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx_v50.dic");
// // initialize CCD location
// cif::addFileResource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
return true;
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(r_1)
{
cif_v2::category c("foo");
c.emplace({
{ "f-1", 1 },
{ "f-2", "two" },
{ "f-3", 3.0 },
// { "f-4", 3.0, 3 }
});
cif_v2::datablock db("test");
db.emplace_back(std::move(c));
std::cout << db << std::endl;
}
// --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(f_1)
// {
// // using namespace mmcif;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// 1 aap
// 2 noot
// 3 mies
// )"_cf;
// BOOST_ASSERT(not f.empty());
// BOOST_ASSERT(f.size() == 1);
// auto &db = f.front();
// BOOST_CHECK(db.name() == "TEST");
// auto &test = db["test"];
// BOOST_CHECK(test.size() == 3);
// // wrong! the next lines will crash. And that's OK, don't do that
// // for (auto r: test)
// // test.erase(r);
// // BOOST_CHECK(test.empty());
// // test.purge();
// // auto n = test.erase(cif::Key("id") == 1, [](const cif::Row &r)
// // {
// // BOOST_CHECK_EQUAL(r["id"].as<int>(), 1);
// // BOOST_CHECK_EQUAL(r["name"].as<std::string>(), "aap"); });
// // BOOST_CHECK_EQUAL(n, 1);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(ut2)
// {
// // using namespace mmcif;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// _test.value
// 1 aap 1.0
// 2 noot 1.1
// 3 mies 1.2
// )"_cf;
// auto &db = f.firstDatablock();
// BOOST_CHECK(db.getName() == "TEST");
// auto &test = db["test"];
// BOOST_CHECK(test.size() == 3);
// int n = 0;
// for (auto r : test.find(cif::Key("name") == "aap"))
// {
// BOOST_CHECK(++n == 1);
// BOOST_CHECK(r["id"].as<int>() == 1);
// BOOST_CHECK(r["name"].as<std::string>() == "aap");
// BOOST_CHECK(r["value"].as<float>() == 1.0);
// }
// auto t = test.find(cif::Key("id") == 1);
// BOOST_CHECK(not t.empty());
// BOOST_CHECK(t.front()["name"].as<std::string>() == "aap");
// auto t2 = test.find(cif::Key("value") == 1.2);
// BOOST_CHECK(not t2.empty());
// BOOST_CHECK(t2.front()["name"].as<std::string>() == "mies");
// }
// BOOST_AUTO_TEST_CASE(ut3)
// {
// using namespace cif::literals;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// _test.value
// 1 aap 1.0
// 2 noot 1.1
// 3 mies 1.2
// 4 boom .
// 5 roos ?
// )"_cf;
// auto &db = f.firstDatablock();
// BOOST_CHECK(db.getName() == "TEST");
// auto &test = db["test"];
// BOOST_CHECK(test.size() == 5);
// BOOST_CHECK(test.exists("value"_key == cif::null));
// BOOST_CHECK(test.find("value"_key == cif::null).size() == 2);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(d1)
// {
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// _item_type_list.detail
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// ; code item types/single words ...
// ;
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// ; text item types / multi-line text ...
// ;
// int numb
// '[+-]?[0-9]+'
// ; int item types are the subset of numbers that are the negative
// or positive integers.
// ;
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_aliases.dictionary cif_core.dic
// _item_aliases.version 2.0.1
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code code
// save_
// save__cat_1.name
// _item.name '_cat_1.name'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_aliases.dictionary cif_core.dic
// _item_aliases.version 2.0.1
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_aliases.dictionary cif_core.dic
// _item_aliases.version 2.0.1
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_aliases.dictionary cif_core.dic
// _item_aliases.version 2.0.1
// _item_type.code code
// save_
// save__cat_2.desc
// _item.name '_cat_2.desc'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_aliases.dictionary cif_core.dic
// _item_aliases.version 2.0.1
// _item_type.code text
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer);
// cif::Validator validator("test", is_dict);
// cif::File f;
// f.setValidator(&validator);
// // --------------------------------------------------------------------
// const char data[] = R"(
// data_test
// loop_
// _cat_1.id
// _cat_1.name
// 1 Aap
// 2 Noot
// 3 Mies
// loop_
// _cat_2.id
// _cat_2.parent_id
// _cat_2.desc
// 1 1 'Een dier'
// 2 1 'Een andere aap'
// 3 2 'walnoot bijvoorbeeld'
// )";
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer);
// f.load(is_data);
// auto &cat1 = f.firstDatablock()["cat_1"];
// auto &cat2 = f.firstDatablock()["cat_2"];
// BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.size() == 3);
// cat1.erase(cif::Key("id") == 1);
// BOOST_CHECK(cat1.size() == 2);
// BOOST_CHECK(cat2.size() == 1);
// // BOOST_CHECK_THROW(cat2.emplace({
// // { "id", 4 },
// // { "parent_id", 4 },
// // { "desc", "moet fout gaan" }
// // }), std::exception);
// BOOST_CHECK_THROW(cat2.emplace({{"id", "vijf"}, // <- invalid value
// {"parent_id", 2},
// {"desc", "moet fout gaan"}}),
// std::exception);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(d2)
// {
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// _item_type_list.detail
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// ; code item types/single words ...
// ;
// ucode uchar
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// ; code item types/single words, case insensitive
// ;
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// ; text item types / multi-line text ...
// ;
// int numb
// '[+-]?[0-9]+'
// ; int item types are the subset of numbers that are the negative
// or positive integers.
// ;
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_1.c
// _item.name '_cat_1.c'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code ucode
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer);
// cif::Validator validator("test", is_dict);
// cif::File f;
// f.setValidator(&validator);
// // --------------------------------------------------------------------
// const char data[] = R"(
// data_test
// loop_
// _cat_1.id
// _cat_1.c
// aap Aap
// noot Noot
// mies Mies
// )";
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer);
// f.load(is_data);
// auto &cat1 = f.firstDatablock()["cat_1"];
// BOOST_CHECK(cat1.size() == 3);
// cat1.erase(cif::Key("id") == "AAP");
// BOOST_CHECK(cat1.size() == 3);
// cat1.erase(cif::Key("id") == "noot");
// BOOST_CHECK(cat1.size() == 2);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(d3)
// {
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code code
// save_
// save__cat_1.name1
// _item.name '_cat_1.name1'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code text
// save_
// save__cat_1.name2
// _item.name '_cat_1.name2'
// _item.category_id cat_1
// _item.mandatory_code no
// _item_linked.child_name '_cat_2.name2'
// _item_linked.parent_name '_cat_1.name2'
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_2.name2
// _item.name '_cat_2.name2'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code text
// save_
// save__cat_2.desc
// _item.name '_cat_2.desc'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code text
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer);
// cif::Validator validator("test", is_dict);
// cif::File f;
// f.setValidator(&validator);
// // --------------------------------------------------------------------
// const char data[] = R"(
// data_test
// loop_
// _cat_1.id
// _cat_1.name1
// _cat_1.name2
// 1 Aap aap
// 2 Noot noot
// 3 Mies mies
// loop_
// _cat_2.id
// _cat_2.parent_id
// _cat_2.name2
// _cat_2.desc
// 1 1 aap 'Een dier'
// 2 1 . 'Een andere aap'
// 3 2 noot 'walnoot bijvoorbeeld'
// 4 2 n2 hazelnoot
// )";
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer);
// f.load(is_data);
// auto &cat1 = f.firstDatablock()["cat_1"];
// auto &cat2 = f.firstDatablock()["cat_2"];
// // check a rename in parent and child
// for (auto r : cat1.find(cif::Key("id") == 1))
// {
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.size() == 4);
// BOOST_CHECK(cat1.find(cif::Key("id") == 1).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 1).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 2);
// // check a rename in parent and child, this time only one child should be renamed
// for (auto r : cat1.find(cif::Key("id") == 2))
// {
// r["id"] = 20;
// break;
// }
// BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.size() == 4);
// BOOST_CHECK(cat1.find(cif::Key("id") == 2).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 20).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2 and cif::Key("name2") == "noot").size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2 and cif::Key("name2") == "n2").size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20 and cif::Key("name2") == "noot").size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20 and cif::Key("name2") == "n2").size() == 0);
// // // --------------------------------------------------------------------
// // cat1.erase(cif::Key("id") == 10);
// // BOOST_CHECK(cat1.size() == 2);
// // BOOST_CHECK(cat2.size() == 2);
// // cat1.erase(cif::Key("id") == 20);
// // BOOST_CHECK(cat1.size() == 1);
// // BOOST_CHECK(cat2.size() == 1);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(d4)
// {
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code int
// save_
// save__cat_1.id2
// _item.name '_cat_1.id2'
// _item.category_id cat_1
// _item.mandatory_code no
// _item_linked.child_name '_cat_2.parent_id2'
// _item_linked.parent_name '_cat_1.id2'
// _item_type.code code
// save_
// save__cat_1.id3
// _item.name '_cat_1.id3'
// _item.category_id cat_1
// _item.mandatory_code no
// _item_linked.child_name '_cat_2.parent_id3'
// _item_linked.parent_name '_cat_1.id3'
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id2
// _item.name '_cat_2.parent_id2'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// save__cat_2.parent_id3
// _item.name '_cat_2.parent_id3'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer);
// cif::Validator validator("test", is_dict);
// cif::File f;
// f.setValidator(&validator);
// // --------------------------------------------------------------------
// const char data[] = R"(
// data_test
// loop_
// _cat_1.id
// _cat_1.id2
// _cat_1.id3
// 1 aap aap
// 2 . noot
// 3 mies .
// 4 . .
// loop_
// _cat_2.id
// _cat_2.parent_id
// _cat_2.parent_id2
// _cat_2.parent_id3
// 1 1 aap aap
// 2 1 . x
// 3 1 aap .
// 4 2 noot noot
// 5 2 . noot
// 6 2 noot .
// 7 2 . .
// 8 3 mies mies
// 9 3 . mies
// 10 3 mies .
// 11 4 roos roos
// 12 4 . roos
// 13 4 roos .
// )";
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer);
// f.load(is_data);
// auto &cat1 = f.firstDatablock()["cat_1"];
// auto &cat2 = f.firstDatablock()["cat_2"];
// // check a rename in parent and child
// for (auto r : cat1.find(cif::Key("id") == 1))
// {
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
// BOOST_CHECK(cat2.size() == 13);
// BOOST_CHECK(cat1.find(cif::Key("id") == 1).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 1).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 2);
// for (auto r : cat1.find(cif::Key("id") == 2))
// {
// r["id"] = 20;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
// BOOST_CHECK(cat2.size() == 13);
// BOOST_CHECK(cat1.find(cif::Key("id") == 2).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 20).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2).size() == 2);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 2);
// for (auto r : cat1.find(cif::Key("id") == 3))
// {
// r["id"] = 30;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
// BOOST_CHECK(cat2.size() == 13);
// BOOST_CHECK(cat1.find(cif::Key("id") == 3).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 3).size() == 2);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 30).size() == 1);
// for (auto r : cat1.find(cif::Key("id") == 4))
// {
// r["id"] = 40;
// break;
// }
// BOOST_CHECK(cat1.size() == 4);
// BOOST_CHECK(cat2.size() == 13);
// BOOST_CHECK(cat1.find(cif::Key("id") == 4).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 4).size() == 3);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 40).size() == 0);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(d5)
// {
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code int
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id
// _item.name '_cat_2.parent_id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.parent_id2
// _item.name '_cat_2.parent_id2'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// save__cat_2.parent_id3
// _item.name '_cat_2.parent_id3'
// _item.category_id cat_2
// _item.mandatory_code no
// _item_type.code code
// save_
// loop_
// _pdbx_item_linked_group_list.child_category_id
// _pdbx_item_linked_group_list.link_group_id
// _pdbx_item_linked_group_list.child_name
// _pdbx_item_linked_group_list.parent_name
// _pdbx_item_linked_group_list.parent_category_id
// cat_2 1 '_cat_2.parent_id' '_cat_1.id' cat_1
// cat_2 2 '_cat_2.parent_id2' '_cat_1.id' cat_1
// cat_2 3 '_cat_2.parent_id3' '_cat_1.id' cat_1
// loop_
// _pdbx_item_linked_group.category_id
// _pdbx_item_linked_group.link_group_id
// _pdbx_item_linked_group.label
// cat_2 1 cat_2:cat_1:1
// cat_2 2 cat_2:cat_1:2
// cat_2 3 cat_2:cat_1:3
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer);
// cif::Validator validator("test", is_dict);
// cif::File f;
// f.setValidator(&validator);
// // --------------------------------------------------------------------
// const char data[] = R"(
// data_test
// loop_
// _cat_1.id
// 1
// 2
// 3
// loop_
// _cat_2.id
// _cat_2.parent_id
// _cat_2.parent_id2
// _cat_2.parent_id3
// 1 1 ? ?
// 2 ? 1 ?
// 3 ? ? 1
// 4 2 2 ?
// 5 2 ? 2
// 6 ? 2 2
// 7 3 3 3
// )";
// // --------------------------------------------------------------------
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer);
// f.load(is_data);
// auto &cat1 = f.firstDatablock()["cat_1"];
// auto &cat2 = f.firstDatablock()["cat_2"];
// // --------------------------------------------------------------------
// // check iterate children
// auto PR2set = cat1.find(cif::Key("id") == 2);
// BOOST_ASSERT(PR2set.size() == 1);
// auto PR2 = PR2set.front();
// BOOST_CHECK(PR2["id"].as<int>() == 2);
// auto CR2set = cat1.getChildren(PR2, "cat_2");
// BOOST_ASSERT(CR2set.size() == 3);
// std::vector<int> CRids;
// std::transform(CR2set.begin(), CR2set.end(), std::back_inserter(CRids), [](cif::Row r)
// { return r["id"].as<int>(); });
// std::sort(CRids.begin(), CRids.end());
// BOOST_CHECK(CRids == std::vector<int>({4, 5, 6}));
// // check a rename in parent and child
// for (auto r : cat1.find(cif::Key("id") == 1))
// {
// r["id"] = 10;
// break;
// }
// BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::Key("id") == 1).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 1).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 1).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 1).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 10).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 10).size() == 1);
// for (auto r : cat1.find(cif::Key("id") == 2))
// {
// r["id"] = 20;
// break;
// }
// BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::Key("id") == 2).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 20).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 2).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 2);
// BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 20).size() == 2);
// BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 20).size() == 2);
// for (auto r : cat1.find(cif::Key("id") == 3))
// {
// r["id"] = 30;
// break;
// }
// BOOST_CHECK(cat1.size() == 3);
// BOOST_CHECK(cat2.size() == 7);
// BOOST_CHECK(cat1.find(cif::Key("id") == 3).size() == 0);
// BOOST_CHECK(cat1.find(cif::Key("id") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 3).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 3).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 3).size() == 0);
// BOOST_CHECK(cat2.find(cif::Key("parent_id") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id2") == 30).size() == 1);
// BOOST_CHECK(cat2.find(cif::Key("parent_id3") == 30).size() == 1);
// // test delete
// cat1.erase(cif::Key("id") == 10);
// BOOST_CHECK(cat1.size() == 2);
// BOOST_CHECK(cat2.size() == 4);
// cat1.erase(cif::Key("id") == 20);
// BOOST_CHECK(cat1.size() == 1);
// BOOST_CHECK(cat2.size() == 1);
// cat1.erase(cif::Key("id") == 30);
// BOOST_CHECK(cat1.size() == 0);
// BOOST_CHECK(cat2.size() == 0);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(c1)
// {
// cif::VERBOSE = 1;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// 1 aap
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.firstDatablock();
// for (auto r : db["test"].find(cif::Key("id") == 1))
// {
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
// BOOST_CHECK(id == 1);
// BOOST_CHECK(name == "aap");
// }
// for (auto r : db["test"].find(cif::Key("id") == 4))
// {
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
// BOOST_CHECK(id == 4);
// BOOST_CHECK(name.empty());
// }
// for (auto r : db["test"].find(cif::Key("id") == 5))
// {
// const auto &[id, name] = r.get<int, std::string>({"id", "name"});
// BOOST_CHECK(id == 5);
// BOOST_CHECK(name.empty());
// }
// // optional
// for (auto r : db["test"])
// {
// const auto &[id, name] = r.get<int, std::optional<std::string>>({"id", "name"});
// switch (id)
// {
// case 1: BOOST_CHECK(name == "aap"); break;
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// }
// BOOST_AUTO_TEST_CASE(c2)
// {
// cif::VERBOSE = 1;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// 1 aap
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.firstDatablock();
// // query tests
// for (const auto &[id, name] : db["test"].rows<int, std::optional<std::string>>("id", "name"))
// {
// switch (id)
// {
// case 1: BOOST_CHECK(name == "aap"); break;
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// }
// BOOST_AUTO_TEST_CASE(c3)
// {
// cif::VERBOSE = 1;
// auto f = R"(data_TEST
// #
// loop_
// _test.id
// _test.name
// 1 aap
// 2 noot
// 3 mies
// 4 .
// 5 ?
// )"_cf;
// auto &db = f.firstDatablock();
// // query tests
// for (const auto &[id, name] : db["test"].find<int, std::optional<std::string>>(cif::All(), "id", "name"))
// {
// switch (id)
// {
// case 1: BOOST_CHECK(name == "aap"); break;
// case 2: BOOST_CHECK(name == "noot"); break;
// case 3: BOOST_CHECK(name == "mies"); break;
// case 4:
// case 5: BOOST_CHECK(not name); break;
// default:
// BOOST_CHECK(false);
// }
// }
// const auto &[id, name] = db["test"].find1<int, std::string>(cif::Key("id") == 1, "id", "name");
// BOOST_CHECK(id == 1);
// BOOST_CHECK(name == "aap");
// }
// // --------------------------------------------------------------------
// // rename test
// BOOST_AUTO_TEST_CASE(r1)
// {
// /*
// Rationale:
// The pdbx_mmcif dictionary contains inconsistent child-parent relations. E.g. atom_site is parent
// of pdbx_nonpoly_scheme which itself is a parent of pdbx_entity_nonpoly. If I want to rename a residue
// I cannot update pdbx_nonpoly_scheme since changing a parent changes children, but not vice versa.
// But if I change the comp_id in atom_site, the pdbx_nonpoly_scheme is update, that's good, and then
// pdbx_entity_nonpoly is updated and that's bad.
// The idea is now that if we update a parent and a child that must change as well, we first check
// if there are more parents of this child that will not change. In that case we have to split the
// child into two, one with the new value and one with the old. We then of course have to split all
// children of this split row that are direct children.
// */
// const char dict[] = R"(
// data_test_dict.dic
// _datablock.id test_dict.dic
// _datablock.description
// ;
// A test dictionary
// ;
// _dictionary.title test_dict.dic
// _dictionary.datablock_id test_dict.dic
// _dictionary.version 1.0
// loop_
// _item_type_list.code
// _item_type_list.primitive_code
// _item_type_list.construct
// code char
// '[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
// text char
// '[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
// int numb
// '[+-]?[0-9]+'
// save_cat_1
// _category.description 'A simple test category'
// _category.id cat_1
// _category.mandatory_code no
// _category_key.name '_cat_1.id'
// save_
// save__cat_1.id
// _item.name '_cat_1.id'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_linked.child_name '_cat_2.parent_id'
// _item_linked.parent_name '_cat_1.id'
// _item_type.code int
// save_
// save__cat_1.name
// _item.name '_cat_1.name'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_1.desc
// _item.name '_cat_1.desc'
// _item.category_id cat_1
// _item.mandatory_code yes
// _item_type.code text
// save_
// save_cat_2
// _category.description 'A second simple test category'
// _category.id cat_2
// _category.mandatory_code no
// _category_key.name '_cat_2.id'
// save_
// save__cat_2.id
// _item.name '_cat_2.id'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.name
// _item.name '_cat_2.name'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_2.num
// _item.name '_cat_2.num'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_2.desc
// _item.name '_cat_2.desc'
// _item.category_id cat_2
// _item.mandatory_code yes
// _item_type.code text
// save_
// save_cat_3
// _category.description 'A third simple test category'
// _category.id cat_3
// _category.mandatory_code no
// _category_key.name '_cat_3.id'
// save_
// save__cat_3.id
// _item.name '_cat_3.id'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code int
// save_
// save__cat_3.name
// _item.name '_cat_3.name'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code code
// save_
// save__cat_3.num
// _item.name '_cat_3.num'
// _item.category_id cat_3
// _item.mandatory_code yes
// _item_type.code int
// save_
// loop_
// _pdbx_item_linked_group_list.child_category_id
// _pdbx_item_linked_group_list.link_group_id
// _pdbx_item_linked_group_list.child_name
// _pdbx_item_linked_group_list.parent_name
// _pdbx_item_linked_group_list.parent_category_id
// cat_1 1 '_cat_1.name' '_cat_2.name' cat_2
// cat_2 1 '_cat_2.name' '_cat_3.name' cat_3
// cat_2 1 '_cat_2.num' '_cat_3.num' cat_3
// )";
// struct membuf : public std::streambuf
// {
// membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } buffer(const_cast<char *>(dict), sizeof(dict) - 1);
// std::istream is_dict(&buffer);
// cif::Validator validator("test", is_dict);
// cif::File f;
// f.setValidator(&validator);
// // --------------------------------------------------------------------
// const char data[] = R"(
// data_test
// loop_
// _cat_1.id
// _cat_1.name
// _cat_1.desc
// 1 aap Aap
// 2 noot Noot
// 3 mies Mies
// loop_
// _cat_2.id
// _cat_2.name
// _cat_2.num
// _cat_2.desc
// 1 aap 1 'Een dier'
// 2 aap 2 'Een andere aap'
// 3 noot 1 'walnoot bijvoorbeeld'
// loop_
// _cat_3.id
// _cat_3.name
// _cat_3.num
// 1 aap 1
// 2 aap 2
// )";
// using namespace cif::literals;
// struct data_membuf : public std::streambuf
// {
// data_membuf(char *text, size_t length)
// {
// this->setg(text, text, text + length);
// }
// } data_buffer(const_cast<char *>(data), sizeof(data) - 1);
// std::istream is_data(&data_buffer);
// f.load(is_data);
// auto &cat1 = f.firstDatablock()["cat_1"];
// auto &cat2 = f.firstDatablock()["cat_2"];
// auto &cat3 = f.firstDatablock()["cat_3"];
// cat3.update_value("name"_key == "aap" and "num"_key == 1, "name", "aapje");
// BOOST_CHECK(cat3.size() == 2);
// {
// int id, num;
// std::string name;
// cif::tie(id, name, num) = cat3.front().get("id", "name", "num");
// BOOST_CHECK(id == 1);
// BOOST_CHECK(num == 1);
// BOOST_CHECK(name == "aapje");
// cif::tie(id, name, num) = cat3.back().get("id", "name", "num");
// BOOST_CHECK(id == 2);
// BOOST_CHECK(num == 2);
// BOOST_CHECK(name == "aap");
// }
// int i = 0;
// for (const auto &[id, name, num, desc] : cat2.rows<int, std::string, int, std::string>("id", "name", "num", "desc"))
// {
// switch (++i)
// {
// case 1:
// BOOST_CHECK(id == 1);
// BOOST_CHECK(num == 1);
// BOOST_CHECK(name == "aapje");
// BOOST_CHECK(desc == "Een dier");
// break;
// case 2:
// BOOST_CHECK(id == 2);
// BOOST_CHECK(num == 2);
// BOOST_CHECK(name == "aap");
// BOOST_CHECK(desc == "Een andere aap");
// break;
// case 3:
// BOOST_CHECK(id == 3);
// BOOST_CHECK(num == 1);
// BOOST_CHECK(name == "noot");
// BOOST_CHECK(desc == "walnoot bijvoorbeeld");
// break;
// default:
// BOOST_FAIL("Unexpected record");
// }
// }
// BOOST_CHECK(cat1.size() == 4);
// i = 0;
// for (const auto &[id, name, desc] : cat1.rows<int, std::string, std::string>("id", "name", "desc"))
// {
// switch (++i)
// {
// case 1:
// BOOST_CHECK(id == 1);
// BOOST_CHECK(name == "aapje");
// BOOST_CHECK(desc == "Aap");
// break;
// case 2:
// BOOST_CHECK(id == 2);
// BOOST_CHECK(name == "noot");
// BOOST_CHECK(desc == "Noot");
// break;
// case 3:
// BOOST_CHECK(id == 3);
// BOOST_CHECK(name == "mies");
// BOOST_CHECK(desc == "Mies");
// break;
// case 4:
// BOOST_CHECK(id == 4);
// BOOST_CHECK(name == "aap");
// BOOST_CHECK(desc == "Aap");
// break;
// default:
// BOOST_FAIL("Unexpected record");
// }
// }
// f.save(std::cout);
// }
// // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(bondmap_1)
// {
// cif::VERBOSE = 2;
// // sections taken from CCD compounds.cif
// auto components = R"(
// data_ASN
// loop_
// _chem_comp_bond.comp_id
// _chem_comp_bond.atom_id_1
// _chem_comp_bond.atom_id_2
// _chem_comp_bond.value_order
// _chem_comp_bond.pdbx_aromatic_flag
// _chem_comp_bond.pdbx_stereo_config
// _chem_comp_bond.pdbx_ordinal
// ASN N CA SING N N 1
// ASN N H SING N N 2
// ASN N H2 SING N N 3
// ASN CA C SING N N 4
// ASN CA CB SING N N 5
// ASN CA HA SING N N 6
// ASN C O DOUB N N 7
// ASN C OXT SING N N 8
// ASN CB CG SING N N 9
// ASN CB HB2 SING N N 10
// ASN CB HB3 SING N N 11
// ASN CG OD1 DOUB N N 12
// ASN CG ND2 SING N N 13
// ASN ND2 HD21 SING N N 14
// ASN ND2 HD22 SING N N 15
// ASN OXT HXT SING N N 16
// data_PHE
// loop_
// _chem_comp_bond.comp_id
// _chem_comp_bond.atom_id_1
// _chem_comp_bond.atom_id_2
// _chem_comp_bond.value_order
// _chem_comp_bond.pdbx_aromatic_flag
// _chem_comp_bond.pdbx_stereo_config
// _chem_comp_bond.pdbx_ordinal
// PHE N CA SING N N 1
// PHE N H SING N N 2
// PHE N H2 SING N N 3
// PHE CA C SING N N 4
// PHE CA CB SING N N 5
// PHE CA HA SING N N 6
// PHE C O DOUB N N 7
// PHE C OXT SING N N 8
// PHE CB CG SING N N 9
// PHE CB HB2 SING N N 10
// PHE CB HB3 SING N N 11
// PHE CG CD1 DOUB Y N 12
// PHE CG CD2 SING Y N 13
// PHE CD1 CE1 SING Y N 14
// PHE CD1 HD1 SING N N 15
// PHE CD2 CE2 DOUB Y N 16
// PHE CD2 HD2 SING N N 17
// PHE CE1 CZ DOUB Y N 18
// PHE CE1 HE1 SING N N 19
// PHE CE2 CZ SING Y N 20
// PHE CE2 HE2 SING N N 21
// PHE CZ HZ SING N N 22
// PHE OXT HXT SING N N 23
// data_PRO
// loop_
// _chem_comp_bond.comp_id
// _chem_comp_bond.atom_id_1
// _chem_comp_bond.atom_id_2
// _chem_comp_bond.value_order
// _chem_comp_bond.pdbx_aromatic_flag
// _chem_comp_bond.pdbx_stereo_config
// _chem_comp_bond.pdbx_ordinal
// PRO N CA SING N N 1
// PRO N CD SING N N 2
// PRO N H SING N N 3
// PRO CA C SING N N 4
// PRO CA CB SING N N 5
// PRO CA HA SING N N 6
// PRO C O DOUB N N 7
// PRO C OXT SING N N 8
// PRO CB CG SING N N 9
// PRO CB HB2 SING N N 10
// PRO CB HB3 SING N N 11
// PRO CG CD SING N N 12
// PRO CG HG2 SING N N 13
// PRO CG HG3 SING N N 14
// PRO CD HD2 SING N N 15
// PRO CD HD3 SING N N 16
// PRO OXT HXT SING N N 17
// )"_cf;
// const std::filesystem::path example(gTestDir / ".." / "examples" / "1cbs.cif.gz");
// mmcif::File file(example.string());
// mmcif::Structure structure(file);
// (void)file.isValid();
// mmcif::BondMap bm(structure);
// // Test the bonds of the first three residues, that's PRO A 1, ASN A 2, PHE A 3
// for (const auto &[compound, seqnr] : std::initializer_list<std::tuple<std::string, int>>{{"PRO", 1}, {"ASN", 2}, {"PHE", 3}})
// {
// auto &res = structure.getResidue("A", compound, seqnr, "");
// auto atoms = res.atoms();
// auto dc = components.get(compound);
// BOOST_ASSERT(dc != nullptr);
// auto cc = dc->get("chem_comp_bond");
// BOOST_ASSERT(cc != nullptr);
// std::set<std::tuple<std::string, std::string>> bonded;
// for (const auto &[atom_id_1, atom_id_2] : cc->rows<std::string, std::string>("atom_id_1", "atom_id_2"))
// {
// if (atom_id_1 > atom_id_2)
// bonded.insert({atom_id_2, atom_id_1});
// else
// bonded.insert({atom_id_1, atom_id_2});
// }
// for (size_t i = 0; i + 1 < atoms.size(); ++i)
// {
// auto label_i = atoms[i].labelAtomID();
// for (size_t j = i + 1; j < atoms.size(); ++j)
// {
// auto label_j = atoms[j].labelAtomID();
// bool bonded_1 = bm(atoms[i], atoms[j]);
// bool bonded_1_i = bm(atoms[j], atoms[i]);
// bool bonded_t = label_i > label_j
// ? bonded.count({label_j, label_i})
// : bonded.count({label_i, label_j});
// BOOST_CHECK(bonded_1 == bonded_t);
// BOOST_CHECK(bonded_1_i == bonded_t);
// }
// }
// }
// // And check the inter-aminoacid links
// auto &poly = structure.polymers().front();
// for (size_t i = 0; i + 1 < poly.size(); ++i)
// {
// auto C = poly[i].atomByID("C");
// auto N = poly[i + 1].atomByID("N");
// BOOST_CHECK(bm(C, N));
// BOOST_CHECK(bm(N, C));
// }
// }
// BOOST_AUTO_TEST_CASE(bondmap_2)
// {
// BOOST_CHECK_THROW(mmcif::BondMap::atomIDsForCompound("UN_"), mmcif::BondMapException);
// mmcif::CompoundFactory::instance().pushDictionary(gTestDir / "UN_.cif");
// BOOST_CHECK(mmcif::BondMap::atomIDsForCompound("UN_").empty() == false);
// }
// BOOST_AUTO_TEST_CASE(reading_file_1)
// {
// std::istringstream is("Hello, world!");
// cif::File file;
// BOOST_CHECK_THROW(file.load(is), std::runtime_error);
// }
// // 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;
// }
// BOOST_AUTO_TEST_CASE(t2)
// {
// Point p[] = {
// { 1, 1, 0 },
// { 2, 1, 0 },
// { 1, 2, 0 }
// };
// Point xp = mmcif::CrossProduct(p[1] - p[0], p[2] - p[0]);
// Quaternion q = ConstructFromAngleAxis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
// auto &&[angle, axis] = mmcif::QuaternionToAngleAxis(q);
// BOOST_TEST(angle == 45, tt::tolerance(0.01));
// }
// BOOST_AUTO_TEST_CASE(t3)
// {
// Point p[] = {
// { 1, 1, 0 },
// { 2, 1, 0 },
// { 1, 2, 0 }
// };
// Point xp = mmcif::CrossProduct(p[1] - p[0], p[2] - p[0]);
// Quaternion q = ConstructFromAngleAxis(45, xp); //mmcif::Normalize(Quaternion{45 * mmcif::kPI / 180, xp.mX, xp.mY, xp.mZ});
// Point v = p[1];
// v -= p[0];
// v.rotate(q);
// v += p[0];
// std::cout << v << std::endl;
// double a = mmcif::Angle(v, p[0], p[1]);
// BOOST_TEST(a == 45, tt::tolerance(0.01));
// }
// BOOST_AUTO_TEST_CASE(parser_test_1)
// {
// auto data1 = R"(
// data_QM
// _test.text ??
// )"_cf;
// auto &db1 = data1.firstDatablock();
// auto &test1 = db1["test"];
// BOOST_CHECK_EQUAL(test1.size(), 1);
// for (auto r : test1)
// {
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, "??");
// }
// std::stringstream ss;
// data1.save(ss);
// auto data2 = cif::File(ss);
// auto &db2 = data2.firstDatablock();
// auto &test2 = db2["test"];
// BOOST_CHECK_EQUAL(test2.size(), 1);
// for (auto r : test2)
// {
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, "??");
// }
// }
// BOOST_AUTO_TEST_CASE(output_test_1)
// {
// auto data1 = R"(
// data_Q
// loop_
// _test.text
// "stop_the_crap"
// 'and stop_ this too'
// 'data_dinges'
// 'blablaglobal_bla'
// boo.data_.whatever
// )"_cf;
// auto &db1 = data1.firstDatablock();
// auto &test1 = db1["test"];
// struct T {
// const char *s;
// bool q;
// } kS[] = {
// { "stop_the_crap", false },
// { "and stop_ this too", false },
// { "data_dinges", false },
// { "blablaglobal_bla", false },
// { "boo.data_.whatever", true }
// };
// BOOST_CHECK_EQUAL(test1.size(), sizeof(kS) / sizeof(T));
// size_t i = 0;
// for (auto r : test1)
// {
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, kS[i].s);
// BOOST_CHECK_EQUAL(cif::isUnquotedString(kS[i].s), kS[i].q);
// ++i;
// }
// std::stringstream ss;
// data1.save(ss);
// auto data2 = cif::File(ss);
// auto &db2 = data2.firstDatablock();
// auto &test2 = db2["test"];
// BOOST_CHECK_EQUAL(test2.size(), sizeof(kS) / sizeof(T));
// i = 0;
// for (auto r : test2)
// {
// const auto &[text] = r.get<std::string>({"text"});
// BOOST_CHECK_EQUAL(text, kS[i++].s);
// }
// }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment