mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-04 22:14:24 +08:00
Compare commits
90 Commits
v9.0.0
...
new-item-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7efa8970cc | ||
|
|
0da03af25f | ||
|
|
70a6076242 | ||
|
|
24386adfd6 | ||
|
|
79674aef8f | ||
|
|
6348f59f15 | ||
|
|
276ce57d5e | ||
|
|
cb81411d72 | ||
|
|
6ec7033769 | ||
|
|
341b248175 | ||
|
|
da521fd2d1 | ||
|
|
25e167cfd3 | ||
|
|
83a2def09c | ||
|
|
a6a27aaf00 | ||
|
|
7d31880fce | ||
|
|
81c02d1f77 | ||
|
|
ce21d26705 | ||
|
|
25a73d288d | ||
|
|
abc61afe7d | ||
|
|
a0f4eada6f | ||
|
|
64e6b3cd2d | ||
|
|
f19c6d078e | ||
|
|
73f18a4da2 | ||
|
|
7a9d94bc57 | ||
|
|
a3ba760ab5 | ||
|
|
913abcd1b3 | ||
|
|
510e336306 | ||
|
|
ffff2479d2 | ||
|
|
b550e9b027 | ||
|
|
452bb83ce7 | ||
|
|
6eda9aaf36 | ||
|
|
251fb55d6a | ||
|
|
f94e9aece9 | ||
|
|
c565bb96be | ||
|
|
e51f31dc4c | ||
|
|
4e128885d6 | ||
|
|
b37054228d | ||
|
|
815b33fee0 | ||
|
|
97f55c1639 | ||
|
|
89de73eb6f | ||
|
|
75f2ec3792 | ||
|
|
f4d29e8da9 | ||
|
|
b97b2638b8 | ||
|
|
bc0222dc0e | ||
|
|
10a6b5649b | ||
|
|
743e2800f8 | ||
|
|
32ac884127 | ||
|
|
bec69f7d07 | ||
|
|
a99215ad6a | ||
|
|
e3d2cbd044 | ||
|
|
5fc965789d | ||
|
|
b4596902aa | ||
|
|
cbf8b52f62 | ||
|
|
4e0fa1c916 | ||
|
|
95b007d38f | ||
|
|
b66f7a30ce | ||
|
|
ec7287c503 | ||
|
|
a41c591f0c | ||
|
|
3a6527cdd5 | ||
|
|
5f21a094c0 | ||
|
|
2203a1855d | ||
|
|
7edd2ecc18 | ||
|
|
1d2953c850 | ||
|
|
dbf59ce622 | ||
|
|
1596db8499 | ||
|
|
bd1fb5c5cd | ||
|
|
da500025c3 | ||
|
|
60eeea9a93 | ||
|
|
1220f01f1e | ||
|
|
ad0a34fe98 | ||
|
|
a7425ff1a0 | ||
|
|
1ce25f86ae | ||
|
|
cd93f72b96 | ||
|
|
23500bd303 | ||
|
|
14b4753b4f | ||
|
|
4c37d5db5f | ||
|
|
fc2c4b4172 | ||
|
|
3ac64de16b | ||
|
|
45eecd72b0 | ||
|
|
d1dd558cda | ||
|
|
d19e2c2196 | ||
|
|
72c7aca074 | ||
|
|
683a1087d0 | ||
|
|
35bc139deb | ||
|
|
45ece2fa0d | ||
|
|
11c98f553f | ||
|
|
28aa9b1036 | ||
|
|
d7b5c0a748 | ||
|
|
065e7f5f18 | ||
|
|
4b1623cfdc |
22
.clang-format
Normal file
22
.clang-format
Normal file
@@ -0,0 +1,22 @@
|
||||
BasedOnStyle: LLVM
|
||||
UseTab: AlignWithSpaces
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Allman
|
||||
ColumnLimit: 0
|
||||
NamespaceIndentation: Inner
|
||||
FixNamespaceComments: true
|
||||
AccessModifierOffset: -2
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
IndentCaseLabels: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BraceWrapping:
|
||||
BeforeLambdaBody: false
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
Cpp11BracedListStyle: false
|
||||
IncludeBlocks: Regroup
|
||||
LambdaBodyIndentation: Signature
|
||||
AllowShortLambdasOnASingleLine: Inline
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
IndentPPDirectives: AfterHash
|
||||
PPIndentWidth: 1
|
||||
18
.clang-tidy
Normal file
18
.clang-tidy
Normal file
@@ -0,0 +1,18 @@
|
||||
Checks: '-*,
|
||||
bugprone-*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
cert-*,
|
||||
modernize*,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-use-designated-initializers,
|
||||
performance
|
||||
'
|
||||
|
||||
# HeaderFilterRegex: '.*'
|
||||
ExcludeHeaderFilterRegex: 'Eigen|Eigen/Eigenvalues|eigen3/Eigen/Eigenvalues|sqlite3.h'
|
||||
CheckOptions:
|
||||
- key: bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion
|
||||
value: false
|
||||
- key: bugprone-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion
|
||||
value: false
|
||||
7
.github/workflows/cmake-multi-platform.yml
vendored
7
.github/workflows/cmake-multi-platform.yml
vendored
@@ -33,13 +33,18 @@ jobs:
|
||||
|
||||
- name: Install dependencies Ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt-get update && sudo apt-get install mrc
|
||||
run: sudo apt-get update && sudo apt-get install mrc catch2
|
||||
|
||||
- name: Install dependencies Window
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: ./tools/depends.cmd
|
||||
shell: cmd
|
||||
|
||||
- name: Install Catch2 macOS
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: >
|
||||
brew install catch2
|
||||
|
||||
- name: Configure CMake
|
||||
run: >
|
||||
cmake -B ${{ steps.strings.outputs.build-output-dir }}
|
||||
|
||||
@@ -32,7 +32,7 @@ endif()
|
||||
# set the project name
|
||||
project(
|
||||
libcifpp
|
||||
VERSION 9.0.0
|
||||
VERSION 9.0.5
|
||||
LANGUAGES CXX C)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
@@ -43,6 +43,7 @@ include(GenerateExportHeader)
|
||||
include(CTest)
|
||||
include(ExternalProject)
|
||||
include(FetchContent)
|
||||
include(VersionString)
|
||||
|
||||
# When building with ninja-multiconfig, build both debug and release by default
|
||||
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
|
||||
@@ -168,24 +169,38 @@ if(MSVC)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# First check if <format> is available
|
||||
find_file(FMT NAME format)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
if(FMT EQUAL "FMT-NOTFOUND")
|
||||
if(NOT (fmt_FOUND OR TARGET fmt))
|
||||
find_package(fmt REQUIRED)
|
||||
message(FATAL_ERROR "fmt not found, compiler too old, you're out of luck")
|
||||
endif()
|
||||
# Using fast_float for float parsing, but only if needed
|
||||
try_compile(STD_CHARCONV_COMPILING
|
||||
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-charconv.cpp)
|
||||
|
||||
if(NOT STD_CHARCONV_COMPILING)
|
||||
message(NOTICE "libcifpp: Using fast_float for std::from_chars")
|
||||
FetchContent_Declare(fastfloat
|
||||
GIT_REPOSITORY "https://github.com/fastfloat/fast_float"
|
||||
GIT_TAG v8.0.2
|
||||
EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(fastfloat)
|
||||
endif()
|
||||
|
||||
find_package(Threads)
|
||||
include(FindPCRE2)
|
||||
find_package(ZLIB QUIET)
|
||||
|
||||
if(NOT ZLIB_FOUND)
|
||||
message(FATAL_ERROR "cifpp: The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)")
|
||||
endif()
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
|
||||
endif()
|
||||
|
||||
if(NOT PCRE2_FOUND)
|
||||
add_subdirectory(pcre2-simple)
|
||||
endif()
|
||||
|
||||
# Using Eigen3 is a bit of a thing. We don't want to build it completely since
|
||||
# we only need a couple of header files. Nothing special. But often, eigen3 is
|
||||
# already installed and then we prefer that.
|
||||
@@ -207,12 +222,6 @@ else()
|
||||
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
message(STATUS "cifpp: Eigen include dir is ${EIGEN_INCLUDE_DIR}")
|
||||
|
||||
# Create a revision file, containing the current git version info
|
||||
include(VersionString)
|
||||
write_version_header(${CMAKE_CURRENT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP")
|
||||
|
||||
# SymOp data table
|
||||
if(CIFPP_RECREATE_SYMOP_DATA)
|
||||
# The tool to create the table
|
||||
@@ -234,6 +243,9 @@ if(CIFPP_RECREATE_SYMOP_DATA)
|
||||
"$ENV{CLIBD}/symop.lib")
|
||||
endif()
|
||||
|
||||
# Create a revision file, containing the current git version info
|
||||
write_version_header("${CMAKE_CURRENT_SOURCE_DIR}/src/" LIB_NAME "LibCIFPP")
|
||||
|
||||
# Sources
|
||||
set(project_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
|
||||
@@ -327,10 +339,17 @@ target_include_directories(
|
||||
PRIVATE "${EIGEN_INCLUDE_DIR}")
|
||||
|
||||
target_link_libraries(cifpp
|
||||
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic> $<BUILD_INTERFACE:pcre2-8>)
|
||||
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
|
||||
|
||||
if(fmt_FOUND)
|
||||
target_link_libraries(cifpp PUBLIC fmt)
|
||||
if(PCRE2_FOUND)
|
||||
target_include_directories(cifpp PRIVATE ${PCRE2_INCLUDE_DIRS})
|
||||
target_link_libraries(cifpp PRIVATE ${PCRE2_LINK_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(cifpp PRIVATE $<BUILD_INTERFACE:pcre2s>)
|
||||
endif()
|
||||
|
||||
if(NOT STD_CHARCONV_COMPILING)
|
||||
target_link_libraries(cifpp PRIVATE FastFloat::fast_float)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
@@ -469,7 +488,6 @@ if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
|
||||
endif()
|
||||
|
||||
set(CONFIG_TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
|
||||
set(REQUIRE_FMT ${fmt_FOUND})
|
||||
|
||||
configure_package_config_file(
|
||||
${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
|
||||
|
||||
20
changelog
20
changelog
@@ -1,3 +1,23 @@
|
||||
Version 9.0.5
|
||||
- Added exists to compound_factory
|
||||
- Added sub_matrix, fix and extend determinant calculation
|
||||
- Added yet another structure::create_non_poly
|
||||
- Remove revision.hpp file in make clean (new VersionString.cmake)
|
||||
|
||||
Version 9.0.4
|
||||
- Fix various stopping and reconstruction errors
|
||||
|
||||
Version 9.0.3
|
||||
- Reconstruction fixed when some entity ids are missing
|
||||
|
||||
Version 9.0.2
|
||||
- Fix code that reconstructs sequences, could throw a map::at
|
||||
- Many optimisations in validation and reconstruction code.
|
||||
|
||||
Version 9.0.1
|
||||
- Use pcre2 from pkg-config if available, if not
|
||||
build a version from the original code.
|
||||
|
||||
Version 9.0.0
|
||||
- Rename fields of cif::mm::polymer to match the naming
|
||||
in mmcif_pdbx.dic. Also, related, fix building mm::structure
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
set(PCRE2_USE_STATIC_LIBS ON)
|
||||
# The problem is, find_package(PCRE2) does not work
|
||||
# and using pkg-config results in linking to a shared library
|
||||
# causing all kinds of trouble later on
|
||||
|
||||
# The cmake config files for pcre2 are broken
|
||||
find_path(PCRE2_INCLUDEDIR NAMES pcre2.h HINTS "C:/Program Files (x86)/PCRE2/include" REQUIRED)
|
||||
find_library(PCRE2_LIBRARY NAMES pcre2-8-static libpcre2-8.a HINTS "C:/Program Files (x86)/PCRE2/lib" REQUIRED)
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
|
||||
|
||||
if(PCRE2_FOUND)
|
||||
message(STATUS "Using pcre2 found using pkg-config")
|
||||
|
||||
add_library(pcre2-8 ALIAS PkgConfig::PCRE2)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT PCRE2_FOUND)
|
||||
find_path(PCRE2_INCLUDEDIR NAMES pcre2.h HINTS "C:/Program Files (x86)/PCRE2/include" REQUIRED)
|
||||
find_library(PCRE2_LIBRARY NAMES pcre2-8-static HINTS "C:/Program Files (x86)/PCRE2/lib" REQUIRED)
|
||||
|
||||
add_library(pcre2-8 IMPORTED STATIC)
|
||||
target_include_directories(pcre2-8 INTERFACE ${PCRE2_INCLUDEDIR})
|
||||
target_compile_definitions(pcre2-8 INTERFACE PCRE2_STATIC)
|
||||
# target_link_libraries(pcre2-8 INTERFACE ${PCRE2_LIBRARY})
|
||||
set_target_properties(pcre2-8 PROPERTIES IMPORTED_LOCATION ${PCRE2_LIBRARY})
|
||||
set_target_properties(pcre2-8 PROPERTIES IMPORTED_IMPLIB ${PCRE2_LIBRARY})
|
||||
endif()
|
||||
add_library(pcre2-8 IMPORTED STATIC)
|
||||
target_include_directories(pcre2-8 INTERFACE ${PCRE2_INCLUDEDIR})
|
||||
target_compile_definitions(pcre2-8 INTERFACE PCRE2_STATIC)
|
||||
set_target_properties(pcre2-8 PROPERTIES IMPORTED_LOCATION ${PCRE2_LIBRARY})
|
||||
set_target_properties(pcre2-8 PROPERTIES IMPORTED_IMPLIB ${PCRE2_LIBRARY})
|
||||
|
||||
@@ -238,7 +238,7 @@ function(write_version_header dir)
|
||||
if(res EQUAL 0)
|
||||
set(REVISION_STRING "${out}")
|
||||
else()
|
||||
message(STATUS "Git hash not found, does this project has a 'build' tag?")
|
||||
message(STATUS "Git hash not found, does this project have a 'build' tag?")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Git hash not found")
|
||||
|
||||
@@ -8,8 +8,5 @@ include(CMakeFindDependencyMacro)
|
||||
|
||||
find_dependency(Threads)
|
||||
find_dependency(ZLIB REQUIRED)
|
||||
if(@REQUIRE_FMT@)
|
||||
find_dependency(fmt REQUIRED)
|
||||
endif()
|
||||
|
||||
check_required_components(cifpp)
|
||||
|
||||
17
cmake/test-charconv.cpp
Normal file
17
cmake/test-charconv.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <charconv>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
int main()
|
||||
{
|
||||
float v;
|
||||
char s[] = "1.0";
|
||||
|
||||
auto r = std::from_chars(s, s + strlen(s), v);
|
||||
|
||||
assert(r.ec == std::errc{});
|
||||
assert(r.ptr = s + strlen(s));
|
||||
assert(v == 1.0f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -26,16 +26,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/utilities.hpp"
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
#include "cif++/format.hpp"
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "cif++/compound.hpp"
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/format.hpp"
|
||||
#include "cif++/gzio.hpp"
|
||||
#include "cif++/model.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
#include "cif++/pdb.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/symmetry.hpp"
|
||||
|
||||
#include "cif++/model.hpp"
|
||||
|
||||
#include "cif++/pdb.hpp"
|
||||
#include "cif++/gzio.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "cif++/exports.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
@@ -235,7 +236,7 @@ struct atom_type_info
|
||||
|
||||
/// Array containing all known radii for this element. A value of kNA is
|
||||
/// stored for unknown values
|
||||
float radii[kRadiusTypeCount];
|
||||
std::array<float, kRadiusTypeCount> radii;
|
||||
};
|
||||
|
||||
/// Array of atom_type_info struct for each of the defined elements in atom_type
|
||||
@@ -256,12 +257,12 @@ class atom_type_traits
|
||||
/// Constructor based on the element as a string in \a symbol
|
||||
atom_type_traits(const std::string &symbol);
|
||||
|
||||
atom_type type() const { return m_info->type; } ///< Returns the atom_type
|
||||
std::string name() const { return m_info->name; } ///< Returns the name of the element
|
||||
std::string symbol() const { return m_info->symbol; } ///< Returns the symbol of the element
|
||||
float weight() const { return m_info->weight; } ///< Returns the average weight of the element
|
||||
[[nodiscard]] atom_type type() const { return m_info->type; } ///< Returns the atom_type
|
||||
[[nodiscard]] std::string name() const { return m_info->name; } ///< Returns the name of the element
|
||||
[[nodiscard]] std::string symbol() const { return m_info->symbol; } ///< Returns the symbol of the element
|
||||
[[nodiscard]] float weight() const { return m_info->weight; } ///< Returns the average weight of the element
|
||||
|
||||
bool is_metal() const { return m_info->metal; } ///< Returns true if the element is a metal
|
||||
[[nodiscard]] bool is_metal() const { return m_info->metal; } ///< Returns true if the element is a metal
|
||||
|
||||
/// Return true if the symbol in \a symbol actually exists in the list of known elements in atom_type
|
||||
static bool is_element(const std::string &symbol);
|
||||
@@ -272,7 +273,7 @@ class atom_type_traits
|
||||
/// @brief Return the radius for the element, use \a type to select which radius to return
|
||||
/// @param type The selector for which radius to return
|
||||
/// @return The requested radius or kNA if not known (or applicable)
|
||||
float radius(radius_type type = radius_type::single_bond) const
|
||||
[[nodiscard]] float radius(radius_type type = radius_type::single_bond) const
|
||||
{
|
||||
if (type >= radius_type::type_count)
|
||||
throw std::invalid_argument("invalid radius requested");
|
||||
@@ -283,20 +284,20 @@ class atom_type_traits
|
||||
///
|
||||
/// \param charge The charge of the ion
|
||||
/// \return The radius of the ion
|
||||
float crystal_ionic_radius(int charge) const;
|
||||
[[nodiscard]] float crystal_ionic_radius(int charge) const;
|
||||
|
||||
/// \brief Return the radius for a charged version of this atom in a non-solid environment
|
||||
///
|
||||
/// \param charge The charge of the ion
|
||||
/// \return The radius of the ion
|
||||
float effective_ionic_radius(int charge) const;
|
||||
[[nodiscard]] float effective_ionic_radius(int charge) const;
|
||||
|
||||
/// \brief Return the radius for a charged version of this atom, returns the effective radius by default
|
||||
///
|
||||
/// \param charge The charge of the ion
|
||||
/// \param type The requested ion radius type
|
||||
/// \return The radius of the ion
|
||||
float ionic_radius(int charge, ionic_radius_type type = ionic_radius_type::effective) const
|
||||
[[nodiscard]] float ionic_radius(int charge, ionic_radius_type type = ionic_radius_type::effective) const
|
||||
{
|
||||
return type == ionic_radius_type::effective ? effective_ionic_radius(charge) : crystal_ionic_radius(charge);
|
||||
}
|
||||
@@ -321,16 +322,16 @@ class atom_type_traits
|
||||
///
|
||||
/// @param charge The charge for which the structure values should be returned, use kWSKFVal to return the *Cval* and *Siva* values
|
||||
/// @return The scattering factors as a SFData struct
|
||||
const SFData &wksf(int charge = 0) const;
|
||||
[[nodiscard]] const SFData &wksf(int charge = 0) const;
|
||||
|
||||
/// @brief Return the electron scattering factor values for the element
|
||||
///
|
||||
/// @return The scattering factors as a SFData struct
|
||||
const SFData &elsf() const;
|
||||
[[nodiscard]] const SFData &elsf() const;
|
||||
|
||||
/// Clipper doesn't like atoms with charges that do not have a scattering factor. And
|
||||
/// rightly so, but we need to know in advance if this is the case
|
||||
bool has_sf(int charge) const;
|
||||
[[nodiscard]] bool has_sf(int charge) const;
|
||||
|
||||
private:
|
||||
const struct atom_type_info *m_info;
|
||||
|
||||
@@ -26,14 +26,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/forward_decl.hpp"
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/iterator.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/** \file category.hpp
|
||||
* Documentation for the cif::category class
|
||||
@@ -50,6 +65,7 @@
|
||||
namespace cif
|
||||
{
|
||||
|
||||
class datablock;
|
||||
class validator;
|
||||
struct category_validator;
|
||||
struct item_validator;
|
||||
@@ -82,13 +98,13 @@ class missing_key_error : public std::runtime_error
|
||||
/**
|
||||
* @brief Construct a new duplicate key error object
|
||||
*/
|
||||
missing_key_error(const std::string &msg, const std::string &key)
|
||||
missing_key_error(const std::string &msg, std::string key)
|
||||
: std::runtime_error(msg)
|
||||
, m_key(key)
|
||||
, m_key(std::move(key))
|
||||
{
|
||||
}
|
||||
|
||||
const std::string &get_key() const noexcept { return m_key; }
|
||||
[[nodiscard]] const std::string &get_key() const noexcept { return m_key; }
|
||||
|
||||
private:
|
||||
std::string m_key;
|
||||
@@ -102,22 +118,12 @@ class multiple_results_error : public std::runtime_error
|
||||
/**
|
||||
* @brief Construct a new multiple results error object
|
||||
*/
|
||||
multiple_results_error()
|
||||
multiple_results_error() // NOLINT
|
||||
: std::runtime_error("query should have returned exactly one row")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// These should be moved elsewhere, one day.
|
||||
|
||||
/// \cond
|
||||
template <typename _Tp>
|
||||
inline constexpr bool is_optional_v = false;
|
||||
template <typename _Tp>
|
||||
inline constexpr bool is_optional_v<std::optional<_Tp>> = true;
|
||||
/// \endcond
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/// The class category is a sequence container for rows of data values.
|
||||
@@ -134,14 +140,17 @@ class category
|
||||
|
||||
friend class row_handle;
|
||||
|
||||
template <typename, typename...>
|
||||
friend class iterator_impl;
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
using value_type = row_handle;
|
||||
using reference = value_type;
|
||||
using const_reference = const value_type;
|
||||
using iterator = iterator_impl<category>;
|
||||
using const_iterator = iterator_impl<const category>;
|
||||
using reference = row_handle;
|
||||
using const_reference = const_row_handle;
|
||||
using iterator = iterator_impl<>;
|
||||
using const_iterator = const_iterator_impl<>;
|
||||
|
||||
static_assert(std::input_iterator<iterator>);
|
||||
static_assert(std::input_iterator<const_iterator>);
|
||||
|
||||
/// \endcond
|
||||
|
||||
@@ -179,15 +188,36 @@ class category
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const std::string &name() const { return m_name; } ///< Returns the name of the category
|
||||
[[nodiscard]] const std::string &name() const { return m_name; } ///< Returns the name of the category
|
||||
|
||||
[[deprecated("use key_items instead")]] iset key_fields() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
|
||||
/// \brief Rename category to @a new_name
|
||||
void name(std::string_view new_name)
|
||||
{
|
||||
m_name = new_name;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
iset key_items() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
|
||||
/// \brief Return true if the category has been modified since last open/save
|
||||
[[nodiscard]] constexpr bool is_dirty() const
|
||||
{
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
[[deprecated("use key_item_indices instead")]] std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key items.
|
||||
/// \brief Mark the category as modified according to @a dirty
|
||||
void set_dirty(bool dirty)
|
||||
{
|
||||
m_dirty = dirty;
|
||||
}
|
||||
|
||||
std::set<uint16_t> key_item_indices() const; ///< Returns a set of indices for the key items.
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
[[deprecated("use key_items instead")]] [[nodiscard]] iset key_fields() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
|
||||
|
||||
[[nodiscard]] iset key_items() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
|
||||
|
||||
[[deprecated("use key_item_indices instead")]] [[nodiscard]] std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key items.
|
||||
|
||||
[[nodiscard]] std::set<uint16_t> key_item_indices() const; ///< Returns a set of indices for the key items.
|
||||
|
||||
/// @brief Set the validator for this category to @a v
|
||||
/// @param v The category_validator to assign. A nullptr value is allowed.
|
||||
@@ -200,15 +230,15 @@ class category
|
||||
|
||||
/// @brief Return the global @ref validator for the data
|
||||
/// @return The @ref validator or nullptr if not assigned
|
||||
const validator *get_validator() const { return m_validator; }
|
||||
[[nodiscard]] const validator *get_validator() const { return m_validator; }
|
||||
|
||||
/// @brief Return the category validator for this category
|
||||
/// @return The @ref category_validator or nullptr if not assigned
|
||||
const category_validator *get_cat_validator() const { return m_cat_validator; }
|
||||
[[nodiscard]] const category_validator *get_cat_validator() const { return m_cat_validator; }
|
||||
|
||||
/// @brief Validate the data stored using the assigned @ref category_validator
|
||||
/// @return Returns true is all validations pass
|
||||
bool is_valid() const;
|
||||
[[nodiscard]] bool is_valid() const;
|
||||
|
||||
/// @brief Validate links, that means, values in this category should have an
|
||||
/// accompanying value in parent categories.
|
||||
@@ -221,7 +251,7 @@ class category
|
||||
/// parent in those categories.
|
||||
///
|
||||
/// @return Returns true is all validations pass
|
||||
bool validate_links() const;
|
||||
[[nodiscard]] bool validate_links() const;
|
||||
|
||||
/**
|
||||
* @brief Strip removes items from this category that are invalid according to the assigned validator
|
||||
@@ -248,15 +278,17 @@ class category
|
||||
/// the category is empty.
|
||||
reference front()
|
||||
{
|
||||
assert(size() > 0);
|
||||
return { *this, *m_head };
|
||||
}
|
||||
|
||||
/// @brief Return a const reference to the first row in this category.
|
||||
/// @return const reference to the first row in this category. The result is undefined if
|
||||
/// the category is empty.
|
||||
const_reference front() const
|
||||
[[nodiscard]] const_reference front() const
|
||||
{
|
||||
return { const_cast<category &>(*this), const_cast<row &>(*m_head) };
|
||||
assert(size() > 0);
|
||||
return { *this, *m_head };
|
||||
}
|
||||
|
||||
/// @brief Return a reference to the last row in this category.
|
||||
@@ -264,15 +296,17 @@ class category
|
||||
/// the category is empty.
|
||||
reference back()
|
||||
{
|
||||
assert(size() > 0);
|
||||
return { *this, *m_tail };
|
||||
}
|
||||
|
||||
/// @brief Return a const reference to the last row in this category.
|
||||
/// @return const reference to the last row in this category. The result is undefined if
|
||||
/// the category is empty.
|
||||
const_reference back() const
|
||||
[[nodiscard]] const_reference back() const
|
||||
{
|
||||
return { const_cast<category &>(*this), const_cast<row &>(*m_tail) };
|
||||
assert(size() > 0);
|
||||
return { *this, *m_tail };
|
||||
}
|
||||
|
||||
/// Return an iterator to the first row
|
||||
@@ -288,43 +322,43 @@ class category
|
||||
}
|
||||
|
||||
/// Return a const iterator to the first row
|
||||
const_iterator begin() const
|
||||
[[nodiscard]] const_iterator begin() const
|
||||
{
|
||||
return { *this, m_head };
|
||||
}
|
||||
|
||||
/// Return a const iterator pointing past the last row
|
||||
const_iterator end() const
|
||||
[[nodiscard]] const_iterator end() const
|
||||
{
|
||||
return { *this, nullptr };
|
||||
}
|
||||
|
||||
/// Return a const iterator to the first row
|
||||
const_iterator cbegin() const
|
||||
[[nodiscard]] const_iterator cbegin() const
|
||||
{
|
||||
return { *this, m_head };
|
||||
}
|
||||
|
||||
/// Return an iterator pointing past the last row
|
||||
const_iterator cend() const
|
||||
[[nodiscard]] const_iterator cend() const
|
||||
{
|
||||
return { *this, nullptr };
|
||||
}
|
||||
|
||||
/// Return a count of the rows in this container
|
||||
std::size_t size() const
|
||||
[[nodiscard]] std::size_t size() const
|
||||
{
|
||||
return std::distance(cbegin(), cend());
|
||||
}
|
||||
|
||||
/// Return the theoretical maximum number or rows that can be stored
|
||||
std::size_t max_size() const
|
||||
[[nodiscard]] std::size_t max_size() const
|
||||
{
|
||||
return std::numeric_limits<std::size_t>::max(); // this is a bit optimistic, I guess
|
||||
}
|
||||
|
||||
/// Return true if the category is empty
|
||||
bool empty() const
|
||||
[[nodiscard]] bool empty() const
|
||||
{
|
||||
return m_head == nullptr;
|
||||
}
|
||||
@@ -336,7 +370,7 @@ class category
|
||||
struct key_element_type
|
||||
{
|
||||
std::string name; ///< Name of the item
|
||||
std::string value; ///< Value to be found
|
||||
item_value value; ///< Value to be found
|
||||
bool may_be_null = false; ///< If true, value should be same or empty
|
||||
};
|
||||
|
||||
@@ -348,13 +382,10 @@ class category
|
||||
/// @return The row found in the index, or an undefined row_handle
|
||||
row_handle operator[](const key_type &key);
|
||||
|
||||
/// @brief Return a const row_handle for the row specified by \a key
|
||||
/// @brief Return a const_row_handle for the row specified by \a key
|
||||
/// @param key The value for the key, items specified in the dictionary should have a value
|
||||
/// @return The row found in the index, or an undefined row_handle
|
||||
const row_handle operator[](const key_type &key) const
|
||||
{
|
||||
return const_cast<category *>(this)->operator[](key);
|
||||
}
|
||||
const_row_handle operator[](const key_type &key) const;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -370,10 +401,10 @@ class category
|
||||
/// @param names The names for the items requested
|
||||
|
||||
template <typename... Ts, typename... Ns>
|
||||
iterator_proxy<const category, Ts...> rows(Ns... names) const
|
||||
[[nodiscard]] const_iterator_proxy<Ts...> rows(Ns... names) const
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
|
||||
return iterator_proxy<const category, Ts...>(*this, begin(), { names... });
|
||||
return const_iterator_proxy<Ts...>(*this, begin(), { names... });
|
||||
}
|
||||
|
||||
/// @brief Return a special iterator for all rows in this category.
|
||||
@@ -393,10 +424,10 @@ class category
|
||||
/// @param names The names for the items requested
|
||||
|
||||
template <typename... Ts, typename... Ns>
|
||||
iterator_proxy<category, Ts...> rows(Ns... names)
|
||||
iterator_proxy<Ts...> rows(Ns... names)
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
|
||||
return iterator_proxy<category, Ts...>(*this, begin(), { names... });
|
||||
return iterator_proxy<Ts...>(*this, begin(), { names... });
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -412,7 +443,7 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
|
||||
/// to a @ref row_handle
|
||||
|
||||
conditional_iterator_proxy<category> find(condition &&cond)
|
||||
conditional_iterator_proxy<> find(condition &&cond)
|
||||
{
|
||||
return find(begin(), std::move(cond));
|
||||
}
|
||||
@@ -425,7 +456,7 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
|
||||
/// to a @ref row_handle
|
||||
|
||||
conditional_iterator_proxy<category> find(iterator pos, condition &&cond)
|
||||
conditional_iterator_proxy<> find(iterator pos, condition &&cond)
|
||||
{
|
||||
return { *this, pos, std::move(cond) };
|
||||
}
|
||||
@@ -436,7 +467,7 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
|
||||
/// to a const @ref row_handle
|
||||
|
||||
conditional_iterator_proxy<const category> find(condition &&cond) const
|
||||
const_conditional_iterator_proxy<> find(condition &&cond) const
|
||||
{
|
||||
return find(cbegin(), std::move(cond));
|
||||
}
|
||||
@@ -449,9 +480,9 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
|
||||
/// to a const @ref row_handle
|
||||
|
||||
conditional_iterator_proxy<const category> find(const_iterator pos, condition &&cond) const
|
||||
const_conditional_iterator_proxy<> find(const_iterator pos, condition &&cond) const
|
||||
{
|
||||
return conditional_iterator_proxy<const category>{ *this, pos, std::move(cond) };
|
||||
return const_conditional_iterator_proxy<>{ *this, pos, std::move(cond) };
|
||||
}
|
||||
|
||||
/// @brief Return a special iterator to loop over all rows that conform to @a cond. The resulting
|
||||
@@ -468,10 +499,10 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match.
|
||||
|
||||
template <typename... Ts, typename... Ns>
|
||||
conditional_iterator_proxy<category, Ts...> find(condition &&cond, Ns... names)
|
||||
conditional_iterator_proxy<Ts...> find(condition &&cond, Ns... names)
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
|
||||
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
|
||||
return find<Ts...>(begin(), std::move(cond), std::forward<Ns>(names)...);
|
||||
}
|
||||
|
||||
/// @brief Return a special const iterator to loop over all rows that conform to @a cond. The resulting
|
||||
@@ -483,7 +514,7 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match.
|
||||
|
||||
template <typename... Ts, typename... Ns>
|
||||
conditional_iterator_proxy<const category, Ts...> find(condition &&cond, Ns... names) const
|
||||
const_conditional_iterator_proxy<Ts...> find(condition &&cond, Ns... names) const
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
|
||||
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
|
||||
@@ -499,7 +530,7 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match.
|
||||
|
||||
template <typename... Ts, typename... Ns>
|
||||
conditional_iterator_proxy<category, Ts...> find(const_iterator pos, condition &&cond, Ns... names)
|
||||
conditional_iterator_proxy<Ts...> find(iterator pos, condition &&cond, Ns... names)
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
|
||||
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
|
||||
@@ -515,7 +546,7 @@ class category
|
||||
/// @return A special iterator that loops over all elements that match.
|
||||
|
||||
template <typename... Ts, typename... Ns>
|
||||
conditional_iterator_proxy<const category, Ts...> find(const_iterator pos, condition &&cond, Ns... names) const
|
||||
const_conditional_iterator_proxy<Ts...> find(const_iterator pos, condition &&cond, Ns... names) const
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
|
||||
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
|
||||
@@ -552,7 +583,7 @@ class category
|
||||
/// there are is not exactly one row matching @a cond
|
||||
/// @param cond The condition to search for
|
||||
/// @return Row handle to the row found
|
||||
const row_handle find1(condition &&cond) const
|
||||
const_row_handle find1(condition &&cond) const
|
||||
{
|
||||
return find1(cbegin(), std::move(cond));
|
||||
}
|
||||
@@ -562,7 +593,7 @@ class category
|
||||
/// @param pos The position to start the search
|
||||
/// @param cond The condition to search for
|
||||
/// @return Row handle to the row found
|
||||
const row_handle find1(const_iterator pos, condition &&cond) const
|
||||
const_row_handle find1(const_iterator pos, condition &&cond) const
|
||||
{
|
||||
auto h = find(pos, std::move(cond));
|
||||
|
||||
@@ -592,8 +623,9 @@ class category
|
||||
/// @param cond The condition to search for
|
||||
/// @param item The name of the item to return the value for
|
||||
/// @return The value found
|
||||
template <typename T, std::enable_if_t<not is_optional_v<T>, int> = 0>
|
||||
template <typename T>
|
||||
T find1(const_iterator pos, condition &&cond, std::string_view item) const
|
||||
requires(not is_optional_v<T>)
|
||||
{
|
||||
auto h = find<T>(pos, std::move(cond), item);
|
||||
|
||||
@@ -611,8 +643,9 @@ class category
|
||||
/// @param cond The condition to search for
|
||||
/// @param item The name of the item to return the value for
|
||||
/// @return The value found, can be empty if no row matches the condition
|
||||
template <typename T, std::enable_if_t<is_optional_v<T>, int> = 0>
|
||||
template <typename T>
|
||||
T find1(const_iterator pos, condition &&cond, std::string_view item) const
|
||||
requires(is_optional_v<T>)
|
||||
{
|
||||
auto h = find<typename T::value_type>(pos, std::move(cond), item);
|
||||
|
||||
@@ -685,7 +718,7 @@ class category
|
||||
/// @brief Return a const row handle to the first row that matches @a cond
|
||||
/// @param cond The condition to search for
|
||||
/// @return The const handle to the row that matches or an empty row_handle
|
||||
const row_handle find_first(condition &&cond) const
|
||||
const_row_handle find_first(condition &&cond) const
|
||||
{
|
||||
return find_first(cbegin(), std::move(cond));
|
||||
}
|
||||
@@ -694,11 +727,11 @@ class category
|
||||
/// @param pos The location to start searching
|
||||
/// @param cond The condition to search for
|
||||
/// @return The const handle to the row that matches or an empty row_handle
|
||||
const row_handle find_first(const_iterator pos, condition &&cond) const
|
||||
const_row_handle find_first(const_iterator pos, condition &&cond) const
|
||||
{
|
||||
auto h = find(pos, std::move(cond));
|
||||
|
||||
return h.empty() ? row_handle{} : *h.begin();
|
||||
return h.empty() ? const_row_handle{} : *h.begin();
|
||||
}
|
||||
|
||||
/// @brief Return the value for item @a item for the first row that matches condition @a cond
|
||||
@@ -763,8 +796,9 @@ class category
|
||||
/// @param item The item to use for the value
|
||||
/// @param cond The condition to search for
|
||||
/// @return The value found or the minimal value for the type
|
||||
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
|
||||
template <typename T>
|
||||
T find_max(std::string_view item, condition &&cond) const
|
||||
requires(std::is_arithmetic_v<T>)
|
||||
{
|
||||
T result = std::numeric_limits<T>::min();
|
||||
|
||||
@@ -781,8 +815,9 @@ class category
|
||||
/// @tparam The type of the value to return
|
||||
/// @param item The item to use for the value
|
||||
/// @return The value found or the minimal value for the type
|
||||
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
|
||||
T find_max(std::string_view item) const
|
||||
template <typename T>
|
||||
[[nodiscard]] T find_max(std::string_view item) const
|
||||
requires(std::is_arithmetic_v<T>)
|
||||
{
|
||||
return find_max<T>(item, all());
|
||||
}
|
||||
@@ -792,8 +827,9 @@ class category
|
||||
/// @param item The item to use for the value
|
||||
/// @param cond The condition to search for
|
||||
/// @return The value found or the maximum value for the type
|
||||
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
|
||||
T find_min(std::string_view item, condition &&cond) const
|
||||
template <typename T>
|
||||
[[nodiscard]] T find_min(std::string_view item, condition &&cond) const
|
||||
requires(std::is_arithmetic_v<T>)
|
||||
{
|
||||
T result = std::numeric_limits<T>::max();
|
||||
|
||||
@@ -810,8 +846,9 @@ class category
|
||||
/// @tparam The type of the value to return
|
||||
/// @param item The item to use for the value
|
||||
/// @return The value found or the maximum value for the type
|
||||
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
|
||||
T find_min(std::string_view item) const
|
||||
template <typename T>
|
||||
[[nodiscard]] T find_min(std::string_view item) const
|
||||
requires(std::is_arithmetic_v<T>)
|
||||
{
|
||||
return find_min<T>(item, all());
|
||||
}
|
||||
@@ -831,10 +868,8 @@ class category
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (cond)
|
||||
if (cond and cond.prepare(*this))
|
||||
{
|
||||
cond.prepare(*this);
|
||||
|
||||
auto sh = cond.single();
|
||||
|
||||
if (sh.has_value() and *sh)
|
||||
@@ -862,10 +897,8 @@ class category
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
if (cond)
|
||||
if (cond and cond.prepare(*this))
|
||||
{
|
||||
cond.prepare(*this);
|
||||
|
||||
auto sh = cond.single();
|
||||
|
||||
if (sh.has_value() and *sh)
|
||||
@@ -887,36 +920,38 @@ class category
|
||||
|
||||
/// Using the relations defined in the validator, return whether the row
|
||||
/// in @a r has any children in other categories
|
||||
bool has_children(row_handle r) const;
|
||||
[[nodiscard]] bool has_children(const_row_handle r) const;
|
||||
|
||||
/// Using the relations defined in the validator, return whether the row
|
||||
/// in @a r has any parents in other categories
|
||||
bool has_parents(row_handle r) const;
|
||||
[[nodiscard]] bool has_parents(const_row_handle r) const;
|
||||
|
||||
/// Using the relations defined in the validator, return the row handles
|
||||
/// for all rows in @a childCat that are linked to row @a r
|
||||
std::vector<row_handle> get_children(row_handle r, const category &childCat) const;
|
||||
[[nodiscard]] std::vector<const_row_handle> get_children(const_row_handle r, const category &childCat) const;
|
||||
|
||||
/// Using the relations defined in the validator, return the row handles
|
||||
/// for all rows in @a parentCat that are linked to row @a r
|
||||
std::vector<row_handle> get_parents(row_handle r, const category &parentCat) const;
|
||||
[[nodiscard]] std::vector<const_row_handle> get_parents(const_row_handle r, const category &parentCat) const;
|
||||
|
||||
/// Using the relations defined in the validator, return the row handles
|
||||
/// for all rows in @a cat that are in any way linked to row @a r
|
||||
std::vector<row_handle> get_linked(row_handle r, const category &cat) const;
|
||||
[[nodiscard]] std::vector<const_row_handle> get_linked(const_row_handle r, const category &cat) const;
|
||||
|
||||
/// Using the relations defined in the validator, return the row handles
|
||||
/// for all rows in @a childCat that are linked to row @a r
|
||||
[[nodiscard]] std::vector<row_handle> get_children(row_handle r, category &childCat);
|
||||
|
||||
/// Using the relations defined in the validator, return the row handles
|
||||
/// for all rows in @a parentCat that are linked to row @a r
|
||||
[[nodiscard]] std::vector<row_handle> get_parents(row_handle r, category &parentCat);
|
||||
|
||||
/// Using the relations defined in the validator, return the row handles
|
||||
/// for all rows in @a cat that are in any way linked to row @a r
|
||||
[[nodiscard]] std::vector<row_handle> get_linked(row_handle r, category &cat);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// void insert(const_iterator pos, const row_initializer &row)
|
||||
// {
|
||||
// insert_impl(pos, row);
|
||||
// }
|
||||
|
||||
// void insert(const_iterator pos, row_initializer &&row)
|
||||
// {
|
||||
// insert_impl(pos, std::move(row));
|
||||
// }
|
||||
|
||||
/// Erase the row pointed to by @a pos and return the iterator to the
|
||||
/// row following pos.
|
||||
iterator erase(iterator pos);
|
||||
@@ -961,7 +996,7 @@ class category
|
||||
for (auto i = b; i != e; ++i)
|
||||
{
|
||||
// item_value *new_item = this->create_item(*i);
|
||||
r->append(add_item(i->name()), { i->value() });
|
||||
r->set(add_item(i->name()), i->value());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -1005,7 +1040,7 @@ class category
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
using value_provider_type = std::function<std::string_view(std::string_view)>;
|
||||
using value_provider_type = std::function<item_value(const item_value &)>;
|
||||
|
||||
/// \brief Update a single item named @a item_name in the rows that match
|
||||
/// \a cond to values provided by a callback function \a value_provider
|
||||
@@ -1018,6 +1053,7 @@ class category
|
||||
{
|
||||
auto rs = find(std::move(cond));
|
||||
std::vector<row_handle> rows;
|
||||
// NOLINTNEXTLINE
|
||||
std::copy(rs.begin(), rs.end(), std::back_inserter(rows));
|
||||
update_value(rows, item_name, std::move(value_provider));
|
||||
}
|
||||
@@ -1036,10 +1072,11 @@ class category
|
||||
/// That means, child categories are updated if the links are absolute
|
||||
/// and unique. If they are not, the child category rows are split.
|
||||
|
||||
void update_value(condition &&cond, std::string_view item_name, std::string_view value)
|
||||
void update_value(condition &&cond, std::string_view item_name, const item_value &value)
|
||||
{
|
||||
auto rs = find(std::move(cond));
|
||||
std::vector<row_handle> rows;
|
||||
// NOLINTNEXTLINE
|
||||
std::copy(rs.begin(), rs.end(), std::back_inserter(rows));
|
||||
update_value(rows, item_name, value);
|
||||
}
|
||||
@@ -1049,75 +1086,21 @@ class category
|
||||
/// That means, child categories are updated if the links are absolute
|
||||
/// and unique. If they are not, the child category rows are split.
|
||||
|
||||
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, std::string_view value)
|
||||
void update_value(const std::vector<row_handle> &rows, std::string_view item_name, const item_value &value)
|
||||
{
|
||||
update_value(rows, item_name, [value](std::string_view)
|
||||
update_value(rows, item_name, [value](const item_value &)
|
||||
{ return value; });
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Naming used to be very inconsistent. For backward compatibility,
|
||||
// the old function names are here as deprecated variants.
|
||||
|
||||
/// \brief Return the index number for \a column_name
|
||||
[[deprecated("Use get_item_ix instead")]] uint16_t get_column_ix(std::string_view column_name) const
|
||||
{
|
||||
return get_item_ix(column_name);
|
||||
}
|
||||
|
||||
/// @brief Return the name for column with index @a ix
|
||||
/// @param ix The index number
|
||||
/// @return The name of the column
|
||||
[[deprecated("use get_item_name instead")]] std::string_view get_column_name(uint16_t ix) const
|
||||
{
|
||||
return get_item_name(ix);
|
||||
}
|
||||
|
||||
/// @brief Make sure a item with name @a item_name is known and return its index number
|
||||
/// @param item_name The name of the item
|
||||
/// @return The index number of the item
|
||||
[[deprecated("use add_item instead")]] uint16_t add_column(std::string_view item_name)
|
||||
{
|
||||
return add_item(item_name);
|
||||
}
|
||||
|
||||
/** @brief Remove column name @a colum_name
|
||||
* @param column_name The column to be removed
|
||||
*/
|
||||
[[deprecated("use remove_item instead")]] void remove_column(std::string_view column_name)
|
||||
{
|
||||
remove_item(column_name);
|
||||
}
|
||||
|
||||
/** @brief Rename column @a from_name to @a to_name */
|
||||
[[deprecated("use rename_item instead")]] void rename_column(std::string_view from_name, std::string_view to_name)
|
||||
{
|
||||
rename_item(from_name, to_name);
|
||||
}
|
||||
|
||||
/// @brief Return whether a column with name @a name exists in this category
|
||||
/// @param name The name of the column
|
||||
/// @return True if the column exists
|
||||
[[deprecated("use has_item instead")]] bool has_column(std::string_view name) const
|
||||
{
|
||||
return has_item(name);
|
||||
}
|
||||
|
||||
/// @brief Return the cif::iset of columns in this category
|
||||
[[deprecated("use get_items instead")]] iset get_columns() const
|
||||
{
|
||||
return get_items();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief Return the index number for \a item_name
|
||||
|
||||
uint16_t get_item_ix(std::string_view item_name) const;
|
||||
[[nodiscard]] uint16_t get_item_ix(std::string_view item_name) const;
|
||||
|
||||
/// @brief Return the name for item with index @a ix
|
||||
/// @param ix The index number
|
||||
/// @return The name of the item
|
||||
std::string_view get_item_name(uint16_t ix) const
|
||||
[[nodiscard]] const std::string &get_item_name(uint16_t ix) const
|
||||
{
|
||||
if (ix >= m_items.size())
|
||||
throw std::out_of_range("item index is out of range");
|
||||
@@ -1135,19 +1118,28 @@ class category
|
||||
*/
|
||||
void remove_item(std::string_view item_name);
|
||||
|
||||
/// \brief Drop items in this category that contain empty values in all rows.
|
||||
void drop_empty_items();
|
||||
|
||||
/** @brief Rename item @a from_name to @a to_name */
|
||||
void rename_item(std::string_view from_name, std::string_view to_name);
|
||||
|
||||
/// @brief Return whether a item with name @a name exists in this category
|
||||
/// @param name The name of the item
|
||||
/// @return True if the item exists
|
||||
bool has_item(std::string_view name) const
|
||||
[[nodiscard]] bool has_item(std::string_view name) const
|
||||
{
|
||||
return get_item_ix(name) < m_items.size();
|
||||
}
|
||||
|
||||
/// @brief Return the cif::iset of items in this category
|
||||
iset get_items() const;
|
||||
/// @brief Return the items in this category
|
||||
[[nodiscard]] std::vector<std::string> get_items() const;
|
||||
|
||||
/// @brief Return the number of items (colums) in this category
|
||||
[[nodiscard]] constexpr uint16_t get_item_count() const noexcept
|
||||
{
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -1162,31 +1154,55 @@ class category
|
||||
void reorder_by_index();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/// This function returns effectively the list of fully qualified item
|
||||
/// names, that is category_name + '.' + item_name for each item
|
||||
[[deprecated("use get_item_order instead")]] std::vector<std::string> get_tag_order() const
|
||||
{
|
||||
return get_item_order();
|
||||
}
|
||||
|
||||
/// This function returns effectively the list of fully qualified item
|
||||
/// names, that is category_name + '.' + item_name for each item
|
||||
std::vector<std::string> get_item_order() const;
|
||||
[[nodiscard]] std::vector<std::string> get_item_order() const;
|
||||
|
||||
/// Write the contents of the category to the std::ostream @a os
|
||||
void write(std::ostream &os) const;
|
||||
|
||||
/// \brief Various supported output formats
|
||||
enum class output_format
|
||||
{
|
||||
cif, // Output in mmCIF format
|
||||
csv, // comma separated values
|
||||
tsv, // tab separated values
|
||||
list, // values delimited by a '|' character
|
||||
column, // output in columns
|
||||
markdown, //
|
||||
table, // ascii art table
|
||||
box, // table with unicode line characters
|
||||
};
|
||||
|
||||
/// @brief
|
||||
/// @brief Write the contents of the category to the std::ostream @a os and
|
||||
/// use @a order as the order of the items. If @a addMissingItems is
|
||||
/// false, items that do not contain any value will be suppressed. Use this version
|
||||
/// to write out
|
||||
/// @param os The std::ostream to write to
|
||||
/// @param fmt The format to use
|
||||
/// @param order The order in which the items should appear
|
||||
/// @param addMissingItems When false, empty items are suppressed from the output
|
||||
void write(std::ostream &os, output_format fmt,
|
||||
const std::vector<std::string> &order, bool addMissingItems = true);
|
||||
|
||||
/// @brief Write the contents of the category to the std::ostream @a os and
|
||||
/// use @a order as the order of the items. If @a addMissingItems is
|
||||
/// false, items that do not contain any value will be suppressed
|
||||
/// @param os The std::ostream to write to
|
||||
/// @param order The order in which the items should appear
|
||||
/// @param addMissingItems When false, empty items are suppressed from the output
|
||||
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingItems = true);
|
||||
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingItems = true)
|
||||
{
|
||||
write(os, output_format::cif, order, addMissingItems);
|
||||
}
|
||||
|
||||
private:
|
||||
void write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
void write_cif(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
void write_delimited(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems,
|
||||
std::string_view delimiter, bool aligned, bool header) const;
|
||||
void write_markdown(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
void write_table(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems, bool ascii) const;
|
||||
|
||||
public:
|
||||
/// friend function to make it possible to do:
|
||||
@@ -1200,13 +1216,13 @@ class category
|
||||
}
|
||||
|
||||
private:
|
||||
void update_value(row *row, uint16_t item, std::string_view value, bool updateLinked, bool validate = true);
|
||||
void update_value(row *row, uint16_t item, item_value value, bool updateLinked, bool validate = true);
|
||||
|
||||
void erase_orphans(condition &&cond, category &parent);
|
||||
|
||||
using allocator_type = std::allocator<void>;
|
||||
|
||||
constexpr allocator_type get_allocator() const
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -1235,6 +1251,8 @@ class category
|
||||
|
||||
void delete_row(row *r);
|
||||
|
||||
iterator erase(iterator pos, bool cascade);
|
||||
|
||||
row_handle create_copy(row_handle r);
|
||||
|
||||
struct item_entry
|
||||
@@ -1268,8 +1286,8 @@ class category
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
condition get_parents_condition(row_handle rh, const category &parentCat) const;
|
||||
condition get_children_condition(row_handle rh, const category &childCat) const;
|
||||
[[nodiscard]] condition get_parents_condition(const_row_handle rh, const category &parentCat) const;
|
||||
[[nodiscard]] condition get_children_condition(const_row_handle rh, const category &childCat) const;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -1282,10 +1300,13 @@ class category
|
||||
const validator *m_validator = nullptr;
|
||||
const category_validator *m_cat_validator = nullptr;
|
||||
std::vector<link> m_parent_links, m_child_links;
|
||||
bool m_cascade = true;
|
||||
uint32_t m_last_unique_num = 0;
|
||||
class category_index *m_index = nullptr;
|
||||
row *m_head = nullptr, *m_tail = nullptr;
|
||||
|
||||
bool m_dirty = false; // Keep track of modifications
|
||||
};
|
||||
|
||||
static_assert(std::ranges::input_range<category>);
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -30,11 +30,9 @@
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/exports.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
/// \file compound.hpp
|
||||
@@ -116,7 +114,7 @@ struct compound_atom
|
||||
z; ///< The z component of the coordinates for each atom specified as orthogonal angstroms.
|
||||
|
||||
/// Return the location of the atom as a point
|
||||
point get_location() const
|
||||
[[nodiscard]] point get_location() const
|
||||
{
|
||||
return { x, y, z };
|
||||
}
|
||||
@@ -146,40 +144,40 @@ class compound
|
||||
public:
|
||||
// accessors
|
||||
|
||||
std::string id() const { return m_id; } ///< Return the alphanumeric code for the chemical component.
|
||||
std::string name() const { return m_name; } ///< Return the name of the chemical component.
|
||||
std::string type() const { return m_type; } ///< Return the type of monomer.
|
||||
std::string formula() const { return m_formula; } ///< Return the chemical formula of the chemical component.
|
||||
float formula_weight() const { return m_formula_weight; } ///< Return the formula mass of the chemical component in Daltons.
|
||||
int formal_charge() const { return m_formal_charge; } ///< Return the formal charge on the chemical component.
|
||||
[[nodiscard]] std::string id() const { return m_id; } ///< Return the alphanumeric code for the chemical component.
|
||||
[[nodiscard]] std::string name() const { return m_name; } ///< Return the name of the chemical component.
|
||||
[[nodiscard]] std::string type() const { return m_type; } ///< Return the type of monomer.
|
||||
[[nodiscard]] std::string formula() const { return m_formula; } ///< Return the chemical formula of the chemical component.
|
||||
[[nodiscard]] float formula_weight() const { return m_formula_weight; } ///< Return the formula mass of the chemical component in Daltons.
|
||||
[[nodiscard]] int formal_charge() const { return m_formal_charge; } ///< Return the formal charge on the chemical component.
|
||||
|
||||
const std::vector<compound_atom> &atoms() const { return m_atoms; } ///< Return the list of atoms for this compound
|
||||
const std::vector<compound_bond> &bonds() const { return m_bonds; } ///< Return the list of bonds for this compound
|
||||
[[nodiscard]] const std::vector<compound_atom> &atoms() const { return m_atoms; } ///< Return the list of atoms for this compound
|
||||
[[nodiscard]] const std::vector<compound_bond> &bonds() const { return m_bonds; } ///< Return the list of bonds for this compound
|
||||
|
||||
compound_atom get_atom_by_atom_id(const std::string &atom_id) const; ///< Return the atom with id @a atom_id
|
||||
[[nodiscard]] compound_atom get_atom_by_atom_id(const std::string &atom_id) const; ///< Return the atom with id @a atom_id
|
||||
|
||||
bool atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const; ///< Return true if @a atomId_1 is bonded to @a atomId_2
|
||||
float bond_length(const std::string &atomId_1, const std::string &atomId_2) const; ///< Return the bond length between @a atomId_1 and @a atomId_2
|
||||
[[nodiscard]] bool atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const; ///< Return true if @a atomId_1 is bonded to @a atomId_2
|
||||
[[nodiscard]] float bond_length(const std::string &atomId_1, const std::string &atomId_2) const; ///< Return the bond length between @a atomId_1 and @a atomId_2
|
||||
|
||||
bool is_water() const ///< Return if the compound is actually a water
|
||||
[[nodiscard]] bool is_water() const ///< Return if the compound is actually a water
|
||||
{
|
||||
return m_id == "HOH" or m_id == "H2O" or m_id == "WAT";
|
||||
}
|
||||
|
||||
/** \brief Return whether this compound has a type of either 'peptide linking' or 'L-peptide linking' */
|
||||
bool is_peptide() const;
|
||||
[[nodiscard]] bool is_peptide() const;
|
||||
|
||||
/** \brief Return whether this compound has a type of either 'DNA linking' or 'RNA linking' */
|
||||
bool is_base() const;
|
||||
[[nodiscard]] bool is_base() const;
|
||||
|
||||
char one_letter_code() const { return m_one_letter_code; }; ///< Return the one letter code to use in a canonical sequence. If unknown the value '\0' is returned
|
||||
std::string parent_id() const { return m_parent_id; }; ///< Return the parent id code in case a parent is specified (e.g. MET for MSE)
|
||||
[[nodiscard]] char one_letter_code() const { return m_one_letter_code; }; ///< Return the one letter code to use in a canonical sequence. If unknown the value '\0' is returned
|
||||
[[nodiscard]] std::string parent_id() const { return m_parent_id; }; ///< Return the parent id code in case a parent is specified (e.g. MET for MSE)
|
||||
|
||||
private:
|
||||
friend class compound_factory_impl;
|
||||
friend class local_compound_factory_impl;
|
||||
|
||||
compound(cif::datablock &db);
|
||||
compound(datablock &db);
|
||||
|
||||
std::string m_id;
|
||||
std::string m_name;
|
||||
@@ -201,6 +199,9 @@ class compound
|
||||
class compound_factory
|
||||
{
|
||||
public:
|
||||
compound_factory(const compound_factory &) = delete;
|
||||
compound_factory &operator=(const compound_factory &) = delete;
|
||||
|
||||
/// \brief Initialise a singleton instance.
|
||||
///
|
||||
/// If you have a multithreaded application and want to have different
|
||||
@@ -243,38 +244,42 @@ class compound_factory
|
||||
|
||||
/// Return whether @a res_name is a valid and known peptide
|
||||
[[deprecated("use is_peptide or is_std_peptide instead)")]]
|
||||
bool is_known_peptide(const std::string &res_name) const;
|
||||
[[nodiscard]] bool is_known_peptide(const std::string &res_name) const;
|
||||
|
||||
/// Return whether @a res_name is a valid and known base
|
||||
[[deprecated("use is_base or is_std_base instead)")]]
|
||||
bool is_known_base(const std::string &res_name) const;
|
||||
[[nodiscard]] bool is_known_base(const std::string &res_name) const;
|
||||
|
||||
/// Return whether @a res_name is a peptide
|
||||
bool is_peptide(std::string_view res_name) const;
|
||||
[[nodiscard]] bool is_peptide(std::string_view res_name) const;
|
||||
|
||||
/// Return whether @a res_name is a base
|
||||
bool is_base(std::string_view res_name) const;
|
||||
[[nodiscard]] bool is_base(std::string_view res_name) const;
|
||||
|
||||
/// Return whether @a res_name is one of the standard peptides
|
||||
bool is_std_peptide(std::string_view res_name) const;
|
||||
[[nodiscard]] bool is_std_peptide(std::string_view res_name) const;
|
||||
|
||||
/// Return whether @a res_name is one of the standard bases
|
||||
bool is_std_base(std::string_view res_name) const;
|
||||
[[nodiscard]] bool is_std_base(std::string_view res_name) const;
|
||||
|
||||
/// Return whether @a res_name is a monomer (either base or peptide)
|
||||
bool is_monomer(std::string_view res_name) const;
|
||||
[[nodiscard]] bool is_monomer(std::string_view res_name) const;
|
||||
|
||||
/// Return whether @a res_name is one of the standard bases or peptides
|
||||
bool is_std_monomer(std::string_view res_name) const
|
||||
[[nodiscard]] bool is_std_monomer(std::string_view res_name) const
|
||||
{
|
||||
return is_std_base(res_name) or is_std_peptide(res_name);
|
||||
}
|
||||
|
||||
bool is_water(std::string_view res_name) const
|
||||
/// Return whether @a res_name is water
|
||||
[[nodiscard]] bool is_water(std::string_view res_name) const
|
||||
{
|
||||
return res_name == "HOH" or res_name == "H2O" or res_name == "WAT";
|
||||
}
|
||||
|
||||
/// Return whether @a res_name already exists, without creating it.
|
||||
[[nodiscard]] bool exists(std::string_view res_name) const;
|
||||
|
||||
/// \brief Create the compound object for \a id
|
||||
///
|
||||
/// This will create the compound instance for \a id if it doesn't exist already.
|
||||
@@ -283,14 +288,14 @@ class compound_factory
|
||||
/// \result The compound, or nullptr if it could not be created (missing info)
|
||||
const compound *create(std::string_view id);
|
||||
|
||||
~compound_factory();
|
||||
~compound_factory() = default;
|
||||
|
||||
CIFPP_EXPORT static const std::map<std::string, char> kAAMap, ///< Globally accessible static list of the default amino acids
|
||||
kBaseMap; ///< Globally accessible static list of the default bases
|
||||
|
||||
void report_missing_compound(std::string_view compound_id);
|
||||
|
||||
bool get_report_missing() const { return m_report_missing; }
|
||||
[[nodiscard]] bool get_report_missing() const { return m_report_missing; }
|
||||
|
||||
void set_report_missing(bool report)
|
||||
{
|
||||
@@ -300,9 +305,6 @@ class compound_factory
|
||||
private:
|
||||
compound_factory();
|
||||
|
||||
compound_factory(const compound_factory &) = delete;
|
||||
compound_factory &operator=(const compound_factory &) = delete;
|
||||
|
||||
static std::unique_ptr<compound_factory> s_instance;
|
||||
static thread_local std::unique_ptr<compound_factory> tl_instance;
|
||||
static bool s_use_thread_local_instance;
|
||||
@@ -331,14 +333,14 @@ class compound_factory
|
||||
class compound_source
|
||||
{
|
||||
public:
|
||||
compound_source(const cif::file &file)
|
||||
compound_source(const file &file)
|
||||
{
|
||||
cif::compound_factory::instance().push_dictionary(file);
|
||||
compound_factory::instance().push_dictionary(file);
|
||||
}
|
||||
|
||||
~compound_source()
|
||||
{
|
||||
cif::compound_factory::instance().pop_dictionary();
|
||||
compound_factory::instance().pop_dictionary();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/format.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
@@ -127,9 +128,9 @@ iset get_category_items(const category &cat);
|
||||
*
|
||||
* @param cat The category
|
||||
* @param col The name of the item
|
||||
* @return uint16_t The index
|
||||
* @return uint16_t The index, if item is found
|
||||
*/
|
||||
uint16_t get_item_ix(const category &cat, std::string_view col);
|
||||
std::optional<uint16_t> get_item_ix(const category &cat, std::string_view col);
|
||||
|
||||
/**
|
||||
* @brief Return whether the item @a col in category @a cat has a primitive type of *uchar*
|
||||
@@ -148,19 +149,19 @@ namespace detail
|
||||
{
|
||||
struct condition_impl
|
||||
{
|
||||
virtual ~condition_impl() {}
|
||||
virtual ~condition_impl() = default;
|
||||
|
||||
virtual condition_impl *prepare(const category &) { return this; }
|
||||
virtual bool test(row_handle) const = 0;
|
||||
[[nodiscard]] virtual bool test(const_row_handle) const = 0;
|
||||
virtual void str(std::ostream &) const = 0;
|
||||
virtual std::optional<row_handle> single() const { return {}; };
|
||||
[[nodiscard]] virtual std::optional<const_row_handle> single() const { return {}; };
|
||||
|
||||
virtual bool equals([[maybe_unused]] const condition_impl *rhs) const { return false; }
|
||||
};
|
||||
|
||||
struct all_condition_impl : public condition_impl
|
||||
{
|
||||
bool test(row_handle) const override { return true; }
|
||||
[[nodiscard]] bool test(const_row_handle) const override { return true; }
|
||||
void str(std::ostream &os) const override { os << "*"; }
|
||||
};
|
||||
|
||||
@@ -207,7 +208,7 @@ class condition
|
||||
condition(condition &&rhs) noexcept
|
||||
: m_impl(nullptr)
|
||||
{
|
||||
std::swap(m_impl, rhs.m_impl);
|
||||
swap(*this, rhs);
|
||||
}
|
||||
|
||||
condition &operator=(const condition &) = delete;
|
||||
@@ -217,7 +218,7 @@ class condition
|
||||
*/
|
||||
condition &operator=(condition &&rhs) noexcept
|
||||
{
|
||||
std::swap(m_impl, rhs.m_impl);
|
||||
swap(*this, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -232,8 +233,9 @@ class condition
|
||||
* take care of setting the correct indices for items e.g.
|
||||
*
|
||||
* @param c The category this query should act upon
|
||||
* @result Returns true if the condition might result in rows
|
||||
*/
|
||||
void prepare(const category &c);
|
||||
bool prepare(const category &c);
|
||||
|
||||
/**
|
||||
* @brief This operator returns true if the row referenced by @a r is
|
||||
@@ -243,33 +245,32 @@ class condition
|
||||
* @return true If there is a match
|
||||
* @return false If there is no match
|
||||
*/
|
||||
bool operator()(row_handle r) const
|
||||
bool operator()(const_row_handle r) const
|
||||
{
|
||||
assert(this->m_impl != nullptr);
|
||||
assert(this->m_prepared);
|
||||
return m_impl ? m_impl->test(r) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the condition is not empty
|
||||
*/
|
||||
explicit operator bool() { return not empty(); }
|
||||
explicit operator bool() const { return not empty(); }
|
||||
|
||||
/**
|
||||
* @brief Return true if the condition is empty, has no condition
|
||||
*/
|
||||
bool empty() const { return m_impl == nullptr; }
|
||||
[[nodiscard]] bool empty() const { return m_impl == nullptr; }
|
||||
|
||||
/**
|
||||
* @brief If the prepare step found out there is only one hit
|
||||
* this single hit can be returned by this method.
|
||||
*
|
||||
* @return std::optional<row_handle> The result will contain
|
||||
* @return std::optional<const_row_handle> The result will contain
|
||||
* a row reference if there is a single hit, it will be empty otherwise
|
||||
*/
|
||||
std::optional<row_handle> single() const
|
||||
[[nodiscard]] std::optional<const_row_handle> single() const
|
||||
{
|
||||
return m_impl ? m_impl->single() : std::optional<row_handle>();
|
||||
return m_impl ? m_impl->single() : std::optional<const_row_handle>();
|
||||
}
|
||||
|
||||
friend condition operator||(condition &&a, condition &&b); /**< Return a condition which is the logical OR or condition @a and @b */
|
||||
@@ -284,10 +285,9 @@ class condition
|
||||
/**
|
||||
* @brief Swap two conditions
|
||||
*/
|
||||
void swap(condition &rhs)
|
||||
friend void swap(condition &lhs, condition &rhs) noexcept
|
||||
{
|
||||
std::swap(m_impl, rhs.m_impl);
|
||||
std::swap(m_prepared, rhs.m_prepared);
|
||||
std::swap(lhs.m_impl, rhs.m_impl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,27 +308,31 @@ class condition
|
||||
void optimise(condition_impl *&impl);
|
||||
|
||||
condition_impl *m_impl;
|
||||
bool m_prepared = false;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct key_is_empty_condition_impl : public condition_impl
|
||||
{
|
||||
key_is_empty_condition_impl(const std::string &item_name)
|
||||
: m_item_name(item_name)
|
||||
key_is_empty_condition_impl(std::string item_name)
|
||||
: m_item_name(std::move(item_name))
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
m_item_ix = get_item_ix(c, m_item_name);
|
||||
auto ix = get_item_ix(c, m_item_name);
|
||||
if (ix.has_value())
|
||||
m_item_ix = *ix;
|
||||
else
|
||||
m_missing_key = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
return r[m_item_ix].empty();
|
||||
return m_missing_key or r[m_item_ix].empty();
|
||||
}
|
||||
|
||||
void str(std::ostream &os) const override
|
||||
@@ -338,22 +342,28 @@ namespace detail
|
||||
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix = 0;
|
||||
bool m_missing_key = false;
|
||||
};
|
||||
|
||||
struct key_is_not_empty_condition_impl : public condition_impl
|
||||
{
|
||||
key_is_not_empty_condition_impl(const std::string &item_name)
|
||||
: m_item_name(item_name)
|
||||
key_is_not_empty_condition_impl(std::string item_name)
|
||||
: m_item_name(std::move(item_name))
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
m_item_ix = get_item_ix(c, m_item_name);
|
||||
return this;
|
||||
auto ix = get_item_ix(c, m_item_name);
|
||||
if (ix.has_value())
|
||||
{
|
||||
m_item_ix = *ix;
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
return not r[m_item_ix].empty();
|
||||
}
|
||||
@@ -371,13 +381,13 @@ namespace detail
|
||||
{
|
||||
key_equals_condition_impl(item &&i)
|
||||
: m_item_name(i.name())
|
||||
, m_value(std::forward<item>(i).value())
|
||||
, m_value(std::forward<item_value>(i.value()))
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override;
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
return m_single_hit.has_value() ? *m_single_hit == r : r[m_item_ix].compare(m_value, m_icase) == 0;
|
||||
}
|
||||
@@ -387,12 +397,12 @@ namespace detail
|
||||
os << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value;
|
||||
}
|
||||
|
||||
virtual std::optional<row_handle> single() const override
|
||||
[[nodiscard]] std::optional<const_row_handle> single() const override
|
||||
{
|
||||
return m_single_hit;
|
||||
}
|
||||
|
||||
virtual bool equals(const condition_impl *rhs) const override
|
||||
bool equals(const condition_impl *rhs) const override
|
||||
{
|
||||
if (typeid(*rhs) == typeid(key_equals_condition_impl))
|
||||
{
|
||||
@@ -409,8 +419,8 @@ namespace detail
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix = 0;
|
||||
bool m_icase = false;
|
||||
std::string m_value;
|
||||
std::optional<row_handle> m_single_hit;
|
||||
item_value m_value;
|
||||
std::optional<const_row_handle> m_single_hit;
|
||||
};
|
||||
|
||||
struct key_equals_or_empty_condition_impl : public condition_impl
|
||||
@@ -425,18 +435,29 @@ namespace detail
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
m_item_ix = get_item_ix(c, m_item_name);
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
auto ix = get_item_ix(c, m_item_name);
|
||||
if (ix.has_value())
|
||||
{
|
||||
m_item_ix = *ix;
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
}
|
||||
else
|
||||
m_key_is_missing = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
bool result = false;
|
||||
if (m_single_hit.has_value())
|
||||
|
||||
if (m_key_is_missing)
|
||||
result = true;
|
||||
else if (m_single_hit.has_value())
|
||||
result = *m_single_hit == r;
|
||||
else
|
||||
result = r[m_item_ix].empty() or r[m_item_ix].compare(m_value, m_icase) == 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -445,12 +466,12 @@ namespace detail
|
||||
os << '(' << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_name << " IS NULL)";
|
||||
}
|
||||
|
||||
virtual std::optional<row_handle> single() const override
|
||||
[[nodiscard]] std::optional<const_row_handle> single() const override
|
||||
{
|
||||
return m_single_hit;
|
||||
}
|
||||
|
||||
virtual bool equals(const condition_impl *rhs) const override
|
||||
[[nodiscard]] bool equals(const condition_impl *rhs) const override
|
||||
{
|
||||
if (typeid(*rhs) == typeid(key_equals_or_empty_condition_impl))
|
||||
{
|
||||
@@ -466,129 +487,35 @@ namespace detail
|
||||
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix = 0;
|
||||
std::string m_value;
|
||||
item_value m_value;
|
||||
bool m_icase = false;
|
||||
std::optional<row_handle> m_single_hit;
|
||||
};
|
||||
|
||||
struct key_equals_number_condition_impl : public condition_impl
|
||||
{
|
||||
key_equals_number_condition_impl(const std::string &name, double v)
|
||||
: m_item_name(name)
|
||||
, m_value(v)
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override;
|
||||
|
||||
bool test(row_handle r) const override
|
||||
{
|
||||
return m_single_hit.has_value() ? *m_single_hit == r : r[m_item_ix].compare(m_value) == 0;
|
||||
}
|
||||
|
||||
void str(std::ostream &os) const override
|
||||
{
|
||||
os << m_item_name << " == " << m_value;
|
||||
}
|
||||
|
||||
virtual std::optional<row_handle> single() const override
|
||||
{
|
||||
return m_single_hit;
|
||||
}
|
||||
|
||||
virtual bool equals(const condition_impl *rhs) const override
|
||||
{
|
||||
if (typeid(*rhs) == typeid(key_equals_number_condition_impl))
|
||||
{
|
||||
auto ri = static_cast<const key_equals_number_condition_impl *>(rhs);
|
||||
if (m_single_hit.has_value() or ri->m_single_hit.has_value())
|
||||
return m_single_hit == ri->m_single_hit;
|
||||
else
|
||||
// watch out, both m_item_ix might be the same while item_names might be diffent (in case they both do not exist in the category)
|
||||
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_name == ri->m_item_name;
|
||||
}
|
||||
return this == rhs;
|
||||
}
|
||||
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix = 0;
|
||||
double m_value;
|
||||
std::optional<row_handle> m_single_hit;
|
||||
};
|
||||
|
||||
struct key_equals_number_or_empty_condition_impl : public condition_impl
|
||||
{
|
||||
key_equals_number_or_empty_condition_impl(key_equals_number_condition_impl *equals)
|
||||
: m_item_name(equals->m_item_name)
|
||||
, m_value(equals->m_value)
|
||||
, m_single_hit(equals->m_single_hit)
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
m_item_ix = get_item_ix(c, m_item_name);
|
||||
return this;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
{
|
||||
bool result = false;
|
||||
if (m_single_hit.has_value())
|
||||
result = *m_single_hit == r;
|
||||
else
|
||||
result = r[m_item_ix].empty() or r[m_item_ix].compare(m_value) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void str(std::ostream &os) const override
|
||||
{
|
||||
os << '(' << m_item_name << " == " << m_value << " OR " << m_item_name << " IS NULL)";
|
||||
}
|
||||
|
||||
virtual std::optional<row_handle> single() const override
|
||||
{
|
||||
return m_single_hit;
|
||||
}
|
||||
|
||||
virtual bool equals(const condition_impl *rhs) const override
|
||||
{
|
||||
if (typeid(*rhs) == typeid(key_equals_number_or_empty_condition_impl))
|
||||
{
|
||||
auto ri = static_cast<const key_equals_number_or_empty_condition_impl *>(rhs);
|
||||
if (m_single_hit.has_value() or ri->m_single_hit.has_value())
|
||||
return m_single_hit == ri->m_single_hit;
|
||||
else
|
||||
// watch out, both m_item_ix might be the same while item_names might be diffent (in case they both do not exist in the category)
|
||||
return m_item_ix == ri->m_item_ix and m_value == ri->m_value and m_item_name == ri->m_item_name;
|
||||
}
|
||||
return this == rhs;
|
||||
}
|
||||
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix = 0;
|
||||
double m_value;
|
||||
std::optional<row_handle> m_single_hit;
|
||||
bool m_key_is_missing = false;
|
||||
std::optional<const_row_handle> m_single_hit;
|
||||
};
|
||||
|
||||
struct key_compare_condition_impl : public condition_impl
|
||||
{
|
||||
template <typename COMP>
|
||||
key_compare_condition_impl(const std::string &item_name, COMP &&comp, const std::string &s)
|
||||
: m_item_name(item_name)
|
||||
, m_compare(std::move(comp))
|
||||
, m_str(s)
|
||||
key_compare_condition_impl(std::string item_name, COMP &&comp, std::string s)
|
||||
: m_item_name(std::move(item_name))
|
||||
, m_compare(std::forward<COMP>(comp))
|
||||
, m_str(std::move(s))
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
m_item_ix = get_item_ix(c, m_item_name);
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
return this;
|
||||
auto ix = get_item_ix(c, m_item_name);
|
||||
if (ix.has_value())
|
||||
{
|
||||
m_item_ix = *ix;
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
return m_compare(r, m_icase);
|
||||
}
|
||||
@@ -601,28 +528,32 @@ namespace detail
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix = 0;
|
||||
bool m_icase = false;
|
||||
std::function<bool(row_handle, bool)> m_compare;
|
||||
std::function<bool(const_row_handle, bool)> m_compare;
|
||||
std::string m_str;
|
||||
};
|
||||
|
||||
struct key_matches_condition_impl : public condition_impl
|
||||
{
|
||||
key_matches_condition_impl(const std::string &item_name, const std::regex &rx)
|
||||
: m_item_name(item_name)
|
||||
, m_item_ix(0)
|
||||
, mRx(rx)
|
||||
key_matches_condition_impl(std::string item_name, std::regex rx)
|
||||
: m_item_name(std::move(item_name))
|
||||
, mRx(std::move(rx))
|
||||
{
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
m_item_ix = get_item_ix(c, m_item_name);
|
||||
return this;
|
||||
auto ix = get_item_ix(c, m_item_name);
|
||||
if (ix.has_value())
|
||||
{
|
||||
m_item_ix = *ix;
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
std::string_view txt = r[m_item_ix].text();
|
||||
auto txt = r[m_item_ix].get<std::string>();
|
||||
return std::regex_match(txt.begin(), txt.end(), mRx);
|
||||
}
|
||||
|
||||
@@ -632,37 +563,31 @@ namespace detail
|
||||
}
|
||||
|
||||
std::string m_item_name;
|
||||
uint16_t m_item_ix;
|
||||
uint16_t m_item_ix{};
|
||||
std::regex mRx;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct any_is_condition_impl : public condition_impl
|
||||
{
|
||||
typedef T valueType;
|
||||
using valueType = T;
|
||||
|
||||
any_is_condition_impl(const valueType &value)
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
auto &c = r.get_category();
|
||||
|
||||
bool result = false;
|
||||
for (auto &f : get_category_items(c))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (r[f].compare(mValue) == 0)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
if (r[f].compare(mValue) == 0)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,12 +604,12 @@ namespace detail
|
||||
|
||||
struct any_matches_condition_impl : public condition_impl
|
||||
{
|
||||
any_matches_condition_impl(const std::regex &rx)
|
||||
: mRx(rx)
|
||||
any_matches_condition_impl(std::regex rx)
|
||||
: mRx(std::move(rx))
|
||||
{
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
auto &c = r.get_category();
|
||||
|
||||
@@ -693,14 +618,14 @@ namespace detail
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string_view txt = r[f].text();
|
||||
auto txt = r[f].get<std::string>();
|
||||
if (std::regex_match(txt.begin(), txt.end(), mRx))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
catch (const std::exception &ex) // NOLINT(bugprone-empty-catch)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -727,14 +652,14 @@ namespace detail
|
||||
{
|
||||
if (typeid(*a.m_impl) == typeid(*this))
|
||||
{
|
||||
and_condition_impl *ai = static_cast<and_condition_impl *>(a.m_impl);
|
||||
auto *ai = static_cast<and_condition_impl *>(a.m_impl);
|
||||
|
||||
std::swap(m_sub, ai->m_sub);
|
||||
m_sub.emplace_back(std::exchange(b.m_impl, nullptr));
|
||||
}
|
||||
else if (typeid(*b.m_impl) == typeid(*this))
|
||||
{
|
||||
and_condition_impl *bi = static_cast<and_condition_impl *>(b.m_impl);
|
||||
auto *bi = static_cast<and_condition_impl *>(b.m_impl);
|
||||
|
||||
std::swap(m_sub, bi->m_sub);
|
||||
m_sub.emplace_back(std::exchange(a.m_impl, nullptr));
|
||||
@@ -746,7 +671,7 @@ namespace detail
|
||||
}
|
||||
}
|
||||
|
||||
~and_condition_impl()
|
||||
~and_condition_impl() override // NOLINT(modernize-use-equals-default)
|
||||
{
|
||||
for (auto sub : m_sub)
|
||||
delete sub;
|
||||
@@ -754,7 +679,7 @@ namespace detail
|
||||
|
||||
condition_impl *prepare(const category &c) override;
|
||||
|
||||
bool test(row_handle r) const override;
|
||||
[[nodiscard]] bool test(const_row_handle r) const override;
|
||||
|
||||
void str(std::ostream &os) const override
|
||||
{
|
||||
@@ -774,9 +699,9 @@ namespace detail
|
||||
os << ')';
|
||||
}
|
||||
|
||||
virtual std::optional<row_handle> single() const override
|
||||
[[nodiscard]] std::optional<const_row_handle> single() const override
|
||||
{
|
||||
std::optional<row_handle> result;
|
||||
std::optional<const_row_handle> result;
|
||||
|
||||
for (auto sub : m_sub)
|
||||
{
|
||||
@@ -801,7 +726,7 @@ namespace detail
|
||||
static condition_impl *combine_equal(std::vector<and_condition_impl *> &subs, or_condition_impl *oc);
|
||||
|
||||
std::vector<condition_impl *> m_sub;
|
||||
std::optional<row_handle> m_single; // Potential result of index lookup
|
||||
std::optional<const_row_handle> m_single; // Potential result of index lookup
|
||||
};
|
||||
|
||||
struct or_condition_impl : public condition_impl
|
||||
@@ -810,14 +735,14 @@ namespace detail
|
||||
{
|
||||
if (typeid(*a.m_impl) == typeid(*this))
|
||||
{
|
||||
or_condition_impl *ai = static_cast<or_condition_impl *>(a.m_impl);
|
||||
auto *ai = static_cast<or_condition_impl *>(a.m_impl);
|
||||
|
||||
std::swap(m_sub, ai->m_sub);
|
||||
m_sub.emplace_back(std::exchange(b.m_impl, nullptr));
|
||||
}
|
||||
else if (typeid(*b.m_impl) == typeid(*this))
|
||||
{
|
||||
or_condition_impl *bi = static_cast<or_condition_impl *>(b.m_impl);
|
||||
auto *bi = static_cast<or_condition_impl *>(b.m_impl);
|
||||
|
||||
std::swap(m_sub, bi->m_sub);
|
||||
m_sub.emplace_back(std::exchange(a.m_impl, nullptr));
|
||||
@@ -829,7 +754,7 @@ namespace detail
|
||||
}
|
||||
}
|
||||
|
||||
~or_condition_impl()
|
||||
~or_condition_impl() override // NOLINT(modernize-use-equals-default)
|
||||
{
|
||||
for (auto sub : m_sub)
|
||||
delete sub;
|
||||
@@ -837,7 +762,7 @@ namespace detail
|
||||
|
||||
condition_impl *prepare(const category &c) override;
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
@@ -868,9 +793,9 @@ namespace detail
|
||||
os << ')';
|
||||
}
|
||||
|
||||
virtual std::optional<row_handle> single() const override
|
||||
[[nodiscard]] std::optional<const_row_handle> single() const override
|
||||
{
|
||||
std::optional<row_handle> result;
|
||||
std::optional<const_row_handle> result;
|
||||
|
||||
for (auto sub : m_sub)
|
||||
{
|
||||
@@ -898,23 +823,21 @@ namespace detail
|
||||
struct not_condition_impl : public condition_impl
|
||||
{
|
||||
not_condition_impl(condition &&a)
|
||||
: mA(nullptr)
|
||||
{
|
||||
std::swap(mA, a.m_impl);
|
||||
}
|
||||
|
||||
~not_condition_impl()
|
||||
~not_condition_impl() override
|
||||
{
|
||||
delete mA;
|
||||
}
|
||||
|
||||
condition_impl *prepare(const category &c) override
|
||||
{
|
||||
mA = mA->prepare(c);
|
||||
return this;
|
||||
return mA->prepare(c) ? this : nullptr;
|
||||
}
|
||||
|
||||
bool test(row_handle r) const override
|
||||
[[nodiscard]] bool test(const_row_handle r) const override
|
||||
{
|
||||
return not mA->test(r);
|
||||
}
|
||||
@@ -926,7 +849,7 @@ namespace detail
|
||||
os << ')';
|
||||
}
|
||||
|
||||
condition_impl *mA;
|
||||
condition_impl *mA = nullptr;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
@@ -939,8 +862,8 @@ inline condition operator and(condition &&a, condition &&b)
|
||||
if (a.m_impl and b.m_impl)
|
||||
return condition(new detail::and_condition_impl(std::move(a), std::move(b)));
|
||||
if (a.m_impl)
|
||||
return condition(std::move(a));
|
||||
return condition(std::move(b));
|
||||
return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -970,33 +893,13 @@ inline condition operator or(condition &&a, condition &&b)
|
||||
return condition(new detail::key_equals_or_empty_condition_impl(ci));
|
||||
}
|
||||
|
||||
if (typeid(*a.m_impl) == typeid(detail::key_equals_number_condition_impl) and
|
||||
typeid(*b.m_impl) == typeid(detail::key_is_empty_condition_impl))
|
||||
{
|
||||
auto ci = static_cast<detail::key_equals_number_condition_impl *>(a.m_impl);
|
||||
auto ce = static_cast<detail::key_is_empty_condition_impl *>(b.m_impl);
|
||||
|
||||
if (ci->m_item_name == ce->m_item_name)
|
||||
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
|
||||
}
|
||||
|
||||
if (typeid(*b.m_impl) == typeid(detail::key_equals_number_condition_impl) and
|
||||
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
|
||||
{
|
||||
auto ci = static_cast<detail::key_equals_number_condition_impl *>(b.m_impl);
|
||||
auto ce = static_cast<detail::key_is_empty_condition_impl *>(a.m_impl);
|
||||
|
||||
if (ci->m_item_name == ce->m_item_name)
|
||||
return condition(new detail::key_equals_number_or_empty_condition_impl(ci));
|
||||
}
|
||||
|
||||
return condition(new detail::or_condition_impl(std::move(a), std::move(b)));
|
||||
}
|
||||
|
||||
if (a.m_impl)
|
||||
return condition(std::move(a));
|
||||
return a;
|
||||
|
||||
return condition(std::move(b));
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1032,8 +935,8 @@ struct key
|
||||
*
|
||||
* @param item_name
|
||||
*/
|
||||
explicit key(const std::string &item_name)
|
||||
: m_item_name(item_name)
|
||||
explicit key(std::string item_name)
|
||||
: m_item_name(std::move(item_name))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1063,23 +966,15 @@ struct key
|
||||
std::string m_item_name; ///< The item name
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and not std::is_same_v<T, bool>);
|
||||
|
||||
/**
|
||||
* @brief Operator to create an equals condition based on a key @a key and a numeric value @a v
|
||||
*/
|
||||
template <Numeric T>
|
||||
condition operator==(const key &key, const T &v)
|
||||
{
|
||||
// TODO: change key_equals_etc... to use std::variant<double,int64_t> or something
|
||||
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, static_cast<double>(v)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator to create an equals condition based on a key @a key and a value @a value
|
||||
*/
|
||||
inline condition operator==(const key &key, std::string_view value)
|
||||
|
||||
template <typename T>
|
||||
concept Numeric = ((std::is_floating_point_v<T> or std::is_integral_v<T>) and not std::is_same_v<T, bool>);
|
||||
|
||||
inline condition operator==(const key &key, const item_value &value)
|
||||
{
|
||||
if (not value.empty())
|
||||
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value }));
|
||||
@@ -1087,29 +982,10 @@ inline condition operator==(const key &key, std::string_view value)
|
||||
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator to create an equals condition based on a key @a key and a value @a value
|
||||
*/
|
||||
template <typename T>
|
||||
requires std::is_same_v<T, bool>
|
||||
inline condition operator==(const key &key, T value)
|
||||
{
|
||||
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value ? "y" : "n" }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator to create a not equals condition based on a key @a key and a value @a v
|
||||
*/
|
||||
template <typename T>
|
||||
condition operator!=(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::not_condition_impl(operator==(key, v)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator to create a not equals condition based on a key @a key and a value @a value
|
||||
*/
|
||||
inline condition operator!=(const key &key, std::string_view value)
|
||||
inline condition operator!=(const key &key, const item_value &value)
|
||||
{
|
||||
return condition(new detail::not_condition_impl(operator==(key, value)));
|
||||
}
|
||||
@@ -1121,9 +997,9 @@ template <Numeric T>
|
||||
condition operator>(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) > 0; },
|
||||
cif::format(" > {}", v)));
|
||||
std::format(" > {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1133,9 +1009,9 @@ template <Numeric T>
|
||||
condition operator>=(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) >= 0; },
|
||||
cif::format(" >= {}", v)));
|
||||
std::format(" >= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1145,9 +1021,9 @@ template <Numeric T>
|
||||
condition operator<(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) < 0; },
|
||||
cif::format(" < {}", v)));
|
||||
std::format(" < {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1157,9 +1033,9 @@ template <Numeric T>
|
||||
condition operator<=(const key &key, const T &v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v) <= 0; },
|
||||
cif::format(" <= {}", v)));
|
||||
std::format(" <= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1168,9 +1044,9 @@ condition operator<=(const key &key, const T &v)
|
||||
inline condition operator>(const key &key, std::string_view v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) > 0; },
|
||||
cif::format(" > {}", v)));
|
||||
std::format(" > {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1179,9 +1055,9 @@ inline condition operator>(const key &key, std::string_view v)
|
||||
inline condition operator>=(const key &key, std::string_view v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) >= 0; },
|
||||
cif::format(" >= {}", v)));
|
||||
std::format(" >= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1190,9 +1066,9 @@ inline condition operator>=(const key &key, std::string_view v)
|
||||
inline condition operator<(const key &key, std::string_view v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) < 0; },
|
||||
cif::format(" < {}", v)));
|
||||
std::format(" < {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1201,9 +1077,9 @@ inline condition operator<(const key &key, std::string_view v)
|
||||
inline condition operator<=(const key &key, std::string_view v)
|
||||
{
|
||||
return condition(new detail::key_compare_condition_impl(
|
||||
key.m_item_name, [item_name = key.m_item_name, v](row_handle r, bool icase)
|
||||
key.m_item_name, [item_name = key.m_item_name, v](const_row_handle r, bool icase)
|
||||
{ return r[item_name].compare(v, icase) <= 0; },
|
||||
cif::format(" <= {}", v)));
|
||||
std::format(" <= {}", v)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace cif
|
||||
|
||||
/**
|
||||
* @brief A datablock is a list of category objects with some additional features
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
class datablock : public std::list<category>
|
||||
@@ -53,7 +53,7 @@ class datablock : public std::list<category>
|
||||
|
||||
/**
|
||||
* @brief Construct a new datablock object with name @a name
|
||||
*
|
||||
*
|
||||
* @param name The name for the new datablock
|
||||
*/
|
||||
datablock(std::string_view name)
|
||||
@@ -80,7 +80,7 @@ class datablock : public std::list<category>
|
||||
{
|
||||
std::swap(a.m_name, b.m_name);
|
||||
std::swap(a.m_validator, b.m_validator);
|
||||
std::swap(static_cast<std::list<category>&>(a), static_cast<std::list<category>&>(b));
|
||||
std::swap(static_cast<std::list<category> &>(a), static_cast<std::list<category> &>(b));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -88,11 +88,11 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return the name of this datablock
|
||||
*/
|
||||
const std::string &name() const { return m_name; }
|
||||
[[nodiscard]] const std::string &name() const { return m_name; }
|
||||
|
||||
/**
|
||||
* @brief Set the name of this datablock to @a name
|
||||
*
|
||||
*
|
||||
* @param name The new name
|
||||
*/
|
||||
void set_name(std::string_view name)
|
||||
@@ -102,45 +102,51 @@ class datablock : public std::list<category>
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the dictionary specified in audit_conform category
|
||||
*
|
||||
*
|
||||
*/
|
||||
void load_dictionary();
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the dictionary @a dict
|
||||
*
|
||||
*/
|
||||
void load_dictionary(std::string_view dict);
|
||||
|
||||
/**
|
||||
* @brief Set the validator object to @a v
|
||||
*
|
||||
*
|
||||
* @param v The new validator object, may be null
|
||||
*/
|
||||
void set_validator(const validator *v);
|
||||
|
||||
/**
|
||||
* @brief Get the validator object
|
||||
*
|
||||
*
|
||||
* @return const validator* The validator or nullptr if there is none
|
||||
*/
|
||||
const validator *get_validator() const;
|
||||
[[nodiscard]] const validator *get_validator() const;
|
||||
|
||||
/**
|
||||
* @brief Validates the content of this datablock and all its content
|
||||
*
|
||||
*
|
||||
* @return true If the content is valid
|
||||
* @return false If the content is not valid
|
||||
*/
|
||||
bool is_valid() const;
|
||||
[[nodiscard]] bool is_valid() const;
|
||||
|
||||
/**
|
||||
* @brief Validates all contained data for valid links between parents and children
|
||||
* as defined in the validator
|
||||
*
|
||||
*
|
||||
* @return true If all links are valid
|
||||
* @return false If all links are not valid
|
||||
*/
|
||||
bool validate_links() const;
|
||||
[[nodiscard]] bool validate_links() const;
|
||||
|
||||
/**
|
||||
* @brief Strip removes all categories and items that are invalid according
|
||||
* to the assigned validator. Will also add a valid audit_conform block.
|
||||
*
|
||||
*
|
||||
* @return true if the remaining datablock is valid
|
||||
*/
|
||||
bool strip();
|
||||
@@ -150,7 +156,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return the category named @a name, will create a new and empty
|
||||
* category named @a name if it does not exist.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category to return
|
||||
* @return category& Reference to the named category
|
||||
*/
|
||||
@@ -159,7 +165,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return the const category named @a name, will return a reference
|
||||
* to a static empty category if it was not found.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category to return
|
||||
* @return category& Reference to the named category
|
||||
*/
|
||||
@@ -168,7 +174,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return a pointer to the category named @a name or nullptr if
|
||||
* it does not exist.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category
|
||||
* @return category* Pointer to the category found or nullptr
|
||||
*/
|
||||
@@ -177,17 +183,16 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Return a pointer to the category named @a name or nullptr if
|
||||
* it does not exist.
|
||||
*
|
||||
*
|
||||
* @param name The name of the category
|
||||
* @return category* Pointer to the category found or nullptr
|
||||
*/
|
||||
const category *get(std::string_view name) const;
|
||||
|
||||
[[nodiscard]] const category *get(std::string_view name) const;
|
||||
|
||||
/**
|
||||
* @brief Return true if this datablock contains a non-empty category
|
||||
*/
|
||||
bool contains(std::string_view name) const
|
||||
[[nodiscard]] bool contains(std::string_view name) const
|
||||
{
|
||||
return get(name) != nullptr;
|
||||
}
|
||||
@@ -197,7 +202,7 @@ class datablock : public std::list<category>
|
||||
* new one if it is not found. The result is a tuple of an iterator
|
||||
* pointing to the category and a boolean indicating whether the category
|
||||
* was created or not.
|
||||
*
|
||||
*
|
||||
* @param name The name for the category
|
||||
* @return std::tuple<iterator, bool> A tuple containing an iterator pointing
|
||||
* at the category and a boolean indicating whether the category was newly
|
||||
@@ -208,16 +213,7 @@ class datablock : public std::list<category>
|
||||
/**
|
||||
* @brief Get the preferred order of the categories when writing them
|
||||
*/
|
||||
[[deprecated("use get_item_order instead")]]
|
||||
std::vector<std::string> get_tag_order() const
|
||||
{
|
||||
return get_item_order();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the preferred order of the categories when writing them
|
||||
*/
|
||||
std::vector<std::string> get_item_order() const;
|
||||
[[nodiscard]] std::vector<std::string> get_item_order() const;
|
||||
|
||||
/**
|
||||
* @brief Write out the contents to @a os
|
||||
|
||||
@@ -26,21 +26,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <istream>
|
||||
#include <list>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
/** \file file.hpp
|
||||
*
|
||||
*
|
||||
* The file class defined here encapsulates the contents of an mmCIF file
|
||||
* It is mainly a list of @ref cif::datablock objects
|
||||
*
|
||||
*
|
||||
* The class file has methods to load dictionaries. These dictionaries are
|
||||
* loaded from resources (if available) or from disk from several locations.
|
||||
*
|
||||
*
|
||||
* See the documentation on load_resource() in file: utilities.hpp for more
|
||||
* information on how data is loaded.
|
||||
* information on how data is loaded.
|
||||
*/
|
||||
|
||||
namespace cif
|
||||
@@ -50,7 +55,7 @@ namespace cif
|
||||
|
||||
/**
|
||||
* @brief The class file is actually a list of datablock objects
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
class file : public std::list<datablock>
|
||||
@@ -60,7 +65,7 @@ class file : public std::list<datablock>
|
||||
|
||||
/**
|
||||
* @brief Construct a new file object using the data in the file @a p as content
|
||||
*
|
||||
*
|
||||
* @param p Path to a file containing the data to load
|
||||
*/
|
||||
explicit file(const std::filesystem::path &p)
|
||||
@@ -70,7 +75,7 @@ class file : public std::list<datablock>
|
||||
|
||||
/**
|
||||
* @brief Construct a new file object using the data in the std::istream @a is
|
||||
*
|
||||
*
|
||||
* @param is The istream containing the data to load
|
||||
*/
|
||||
explicit file(std::istream &is)
|
||||
@@ -81,7 +86,7 @@ class file : public std::list<datablock>
|
||||
/**
|
||||
* @brief Construct a new file object with data in the constant string defined
|
||||
* by @a data and @a length
|
||||
*
|
||||
*
|
||||
* @param data The pointer to the character string with data to load
|
||||
* @param length The length of the data
|
||||
*/
|
||||
@@ -100,7 +105,7 @@ class file : public std::list<datablock>
|
||||
}
|
||||
|
||||
/** @cond */
|
||||
file(const file &rhs)
|
||||
file(const file &rhs) // NOLINT
|
||||
: std::list<datablock>(rhs)
|
||||
{
|
||||
}
|
||||
@@ -120,23 +125,23 @@ class file : public std::list<datablock>
|
||||
|
||||
/**
|
||||
* @brief Validate the content and return true if everything was valid.
|
||||
*
|
||||
*
|
||||
* Will throw an exception if there is no validator defined.
|
||||
*
|
||||
*
|
||||
* If each category was valid, validate_links will also be called.
|
||||
*
|
||||
*
|
||||
* @return true If the content is valid
|
||||
* @return false If the content is not valid
|
||||
*/
|
||||
bool is_valid() const;
|
||||
[[nodiscard]] bool is_valid() const;
|
||||
|
||||
/**
|
||||
* @brief Validate the content and return true if everything was valid.
|
||||
*
|
||||
*
|
||||
* Will attempt to load the referenced dictionary if none was specified.
|
||||
*
|
||||
*
|
||||
* If each category was valid, validate_links will also be called.
|
||||
*
|
||||
*
|
||||
* @return true If the content is valid
|
||||
* @return false If the content is not valid
|
||||
*/
|
||||
@@ -144,18 +149,18 @@ class file : public std::list<datablock>
|
||||
|
||||
/**
|
||||
* @brief Validate the links for all datablocks contained.
|
||||
*
|
||||
*
|
||||
* Will throw an exception if no validator was specified.
|
||||
*
|
||||
*
|
||||
* @return true If all links were valid
|
||||
* @return false If all links were not valid
|
||||
*/
|
||||
bool validate_links() const;
|
||||
[[nodiscard]] bool validate_links() const;
|
||||
|
||||
/**
|
||||
* @brief Return true if a datablock with the name @a name is part of this file
|
||||
*/
|
||||
bool contains(std::string_view name) const;
|
||||
[[nodiscard]] bool contains(std::string_view name) const;
|
||||
|
||||
/**
|
||||
* @brief return a reference to the first datablock in the file
|
||||
@@ -169,7 +174,7 @@ class file : public std::list<datablock>
|
||||
/**
|
||||
* @brief return a const reference to the first datablock in the file
|
||||
*/
|
||||
const datablock &front() const
|
||||
[[nodiscard]] const datablock &front() const
|
||||
{
|
||||
assert(not empty());
|
||||
return std::list<datablock>::front();
|
||||
@@ -190,7 +195,7 @@ class file : public std::list<datablock>
|
||||
* new one if it is not found. The result is a tuple of an iterator
|
||||
* pointing to the datablock and a boolean indicating whether the datablock
|
||||
* was created or not.
|
||||
*
|
||||
*
|
||||
* @param name The name for the datablock
|
||||
* @return std::tuple<iterator, bool> A tuple containing an iterator pointing
|
||||
* at the datablock and a boolean indicating whether the datablock was newly
|
||||
@@ -204,12 +209,6 @@ class file : public std::list<datablock>
|
||||
/** Load the data from @a is */
|
||||
void load(std::istream &is);
|
||||
|
||||
/** Load the data from the file specified by @a p using validator @a v */
|
||||
void load(const std::filesystem::path &p, const validator &v);
|
||||
|
||||
/** Load the data from @a is using validator @a v */
|
||||
void load(std::istream &is, const validator &v);
|
||||
|
||||
/** Save the data to the file specified by @a p */
|
||||
void save(const std::filesystem::path &p) const;
|
||||
|
||||
|
||||
@@ -26,29 +26,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<format>)
|
||||
#include <format>
|
||||
#define USE_STD_FORMAT 1
|
||||
#else
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
|
||||
#include <string>
|
||||
|
||||
/** \file format.hpp
|
||||
*
|
||||
* Now using cif::format instead of a home grown rip off
|
||||
* Now using std::format instead of a home grown rip off
|
||||
*/
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
#if USE_STD_FORMAT
|
||||
using std::format;
|
||||
#else
|
||||
using fmt::format;
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// A streambuf that fills out lines with spaces up until a specified width
|
||||
|
||||
@@ -78,7 +67,7 @@ class fill_out_streambuf : public std::streambuf
|
||||
|
||||
/** @cond */
|
||||
|
||||
~fill_out_streambuf()
|
||||
~fill_out_streambuf() override
|
||||
{
|
||||
m_os.rdbuf(m_upstream);
|
||||
}
|
||||
@@ -91,8 +80,7 @@ class fill_out_streambuf : public std::streambuf
|
||||
* wide as the requested width.
|
||||
*/
|
||||
|
||||
virtual int_type
|
||||
overflow(int_type ic = traits_type::eof())
|
||||
int_type overflow(int_type ic = traits_type::eof()) override
|
||||
{
|
||||
char ch = traits_type::to_char_type(ic);
|
||||
|
||||
@@ -122,10 +110,10 @@ class fill_out_streambuf : public std::streambuf
|
||||
}
|
||||
|
||||
/** Return the upstream streambuf */
|
||||
std::streambuf *get_upstream() const { return m_upstream; }
|
||||
[[nodiscard]] std::streambuf *get_upstream() const { return m_upstream; }
|
||||
|
||||
/** Return how many lines have been written */
|
||||
int get_line_count() const { return m_line_count; }
|
||||
[[nodiscard]] int get_line_count() const { return m_line_count; }
|
||||
|
||||
private:
|
||||
std::ostream &m_os;
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @file forward_decl.hpp
|
||||
*
|
||||
@@ -45,9 +42,6 @@ class file;
|
||||
class parser;
|
||||
|
||||
class row;
|
||||
class row_handle;
|
||||
|
||||
class item;
|
||||
struct item_handle;
|
||||
|
||||
} // namespace cif
|
||||
} // namespace cif
|
||||
|
||||
@@ -209,7 +209,7 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
|
||||
return *this;
|
||||
}
|
||||
|
||||
~basic_igzip_streambuf()
|
||||
~basic_igzip_streambuf() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
@@ -245,8 +245,8 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
|
||||
|
||||
close();
|
||||
|
||||
m_zstream.reset(new z_stream_s);
|
||||
m_gzheader.reset(new gz_header_s);
|
||||
m_zstream = std::make_unique<z_stream_s>();
|
||||
m_gzheader = std::make_unique<gz_header_s>();
|
||||
|
||||
auto &zstream = *m_zstream.get();
|
||||
zstream = z_stream_s{};
|
||||
@@ -396,7 +396,7 @@ class basic_ogzip_streambuf : public basic_streambuf<CharT, Traits>
|
||||
return *this;
|
||||
}
|
||||
|
||||
~basic_ogzip_streambuf()
|
||||
~basic_ogzip_streambuf() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
@@ -431,8 +431,8 @@ class basic_ogzip_streambuf : public basic_streambuf<CharT, Traits>
|
||||
|
||||
close();
|
||||
|
||||
m_zstream.reset(new z_stream_s);
|
||||
m_gzheader.reset(new gz_header_s);
|
||||
m_zstream = std::make_unique<z_stream_s>();
|
||||
m_gzheader = std::make_unique<gz_header_s>();
|
||||
|
||||
auto &zstream = *m_zstream.get();
|
||||
zstream = z_stream_s{};
|
||||
@@ -658,7 +658,7 @@ class basic_ifstream : public basic_istream<CharT, Traits>
|
||||
/// \brief Default constructor, does not open a file since none is specified
|
||||
basic_ifstream() = default;
|
||||
|
||||
~basic_ifstream()
|
||||
~basic_ifstream() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
@@ -774,7 +774,7 @@ class basic_ifstream : public basic_istream<CharT, Traits>
|
||||
/// \brief Return true if the file is open
|
||||
/// \return m_filebuf.is_open()
|
||||
|
||||
bool is_open() const
|
||||
[[nodiscard]] bool is_open() const
|
||||
{
|
||||
return m_filebuf.is_open();
|
||||
}
|
||||
@@ -922,7 +922,7 @@ class basic_ofstream : public basic_ostream<CharT, Traits>
|
||||
|
||||
basic_ofstream() = default;
|
||||
|
||||
~basic_ofstream()
|
||||
~basic_ofstream() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
@@ -1054,7 +1054,7 @@ class basic_ofstream : public basic_ostream<CharT, Traits>
|
||||
/// \brief Return true if the file is open
|
||||
/// \return m_filebuf.is_open()
|
||||
|
||||
bool is_open() const
|
||||
[[nodiscard]] bool is_open() const
|
||||
{
|
||||
return m_filebuf.is_open();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,9 +26,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* @file iterator.hpp
|
||||
@@ -44,6 +48,8 @@
|
||||
namespace cif
|
||||
{
|
||||
|
||||
class category;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -55,13 +61,13 @@ namespace cif
|
||||
* @tparam Category The category for this iterator
|
||||
* @tparam Ts The types this iterator can be dereferenced to
|
||||
*/
|
||||
template <typename Category, typename... Ts>
|
||||
class iterator_impl
|
||||
template <bool Const, typename... Ts>
|
||||
class iterator_impl_base
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
template <typename, typename...>
|
||||
friend class iterator_impl;
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
friend class category;
|
||||
/** @endcond */
|
||||
@@ -70,48 +76,47 @@ class iterator_impl
|
||||
static constexpr std::size_t N = sizeof...(Ts);
|
||||
|
||||
/** @cond */
|
||||
using category_type = std::remove_cv_t<Category>;
|
||||
using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
|
||||
|
||||
using tuple_type = std::tuple<Ts...>;
|
||||
|
||||
using row_handle_type = std::conditional_t<Const, const_row_handle, row_handle>;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = tuple_type;
|
||||
using value_type = std::conditional_t<Const, const tuple_type, tuple_type>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
iterator_impl() = default;
|
||||
iterator_impl_base() = default;
|
||||
|
||||
iterator_impl(const iterator_impl &rhs) = default;
|
||||
iterator_impl(iterator_impl &&rhs) = default;
|
||||
iterator_impl_base(const iterator_impl_base &rhs) = default;
|
||||
iterator_impl_base(iterator_impl_base &&rhs) = default;
|
||||
|
||||
template <typename C2, typename... T2s>
|
||||
iterator_impl(const iterator_impl<C2, T2s...> &rhs)
|
||||
: m_current(const_cast<row_handle&>(rhs.m_current))
|
||||
template <bool C, typename... T2s>
|
||||
iterator_impl_base(const iterator_impl_base<C, T2s...> &rhs)
|
||||
: m_current(rhs.m_current)
|
||||
, m_value(rhs.m_value)
|
||||
, m_item_ix(rhs.m_item_ix)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename IRowType>
|
||||
iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
|
||||
: m_current(const_cast<row_handle&>(rhs.m_current))
|
||||
template <bool C>
|
||||
iterator_impl_base(iterator_impl_base<C, Ts...> &rhs)
|
||||
: m_current(rhs.m_current)
|
||||
, m_value(rhs.m_value)
|
||||
, m_item_ix(rhs.m_item_ix)
|
||||
{
|
||||
m_value = get(std::make_index_sequence<N>());
|
||||
}
|
||||
|
||||
template <typename IRowType>
|
||||
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, N> &cix)
|
||||
: m_current(const_cast<row_handle&>(rhs.m_current))
|
||||
template <bool C>
|
||||
iterator_impl_base(const iterator_impl_base<C> &rhs, const std::array<uint16_t, N> &cix)
|
||||
: m_current(rhs.m_current)
|
||||
, m_item_ix(cix)
|
||||
{
|
||||
m_value = get(std::make_index_sequence<N>());
|
||||
}
|
||||
|
||||
iterator_impl &operator=(iterator_impl i)
|
||||
iterator_impl_base &operator=(iterator_impl_base i)
|
||||
{
|
||||
std::swap(m_current, i.m_current);
|
||||
std::swap(m_item_ix, i.m_item_ix);
|
||||
@@ -119,29 +124,39 @@ class iterator_impl
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~iterator_impl() = default;
|
||||
virtual ~iterator_impl_base() = default;
|
||||
|
||||
reference operator*()
|
||||
auto operator*()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
auto operator*() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
auto operator->()
|
||||
{
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
operator const row_handle() const
|
||||
auto operator->() const
|
||||
{
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
operator const_row_handle() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
operator row_handle()
|
||||
operator row_handle_type()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
iterator_impl &operator++()
|
||||
iterator_impl_base &operator++()
|
||||
{
|
||||
if (m_current)
|
||||
m_current.m_row = m_current.m_row->m_next;
|
||||
@@ -151,24 +166,24 @@ class iterator_impl
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl operator++(int)
|
||||
iterator_impl_base operator++(int)
|
||||
{
|
||||
iterator_impl result(*this);
|
||||
iterator_impl_base result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
|
||||
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
|
||||
bool operator==(const iterator_impl_base &rhs) const { return m_current == rhs.m_current; }
|
||||
bool operator!=(const iterator_impl_base &rhs) const { return m_current != rhs.m_current; }
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
|
||||
template <bool C, typename... ITs>
|
||||
bool operator==(const iterator_impl_base<C, ITs...> &rhs) const
|
||||
{
|
||||
return m_current == rhs.m_current;
|
||||
}
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
|
||||
template <bool C, typename... ITs>
|
||||
bool operator!=(const iterator_impl_base<C, ITs...> &rhs) const
|
||||
{
|
||||
return m_current != rhs.m_current;
|
||||
}
|
||||
@@ -177,13 +192,13 @@ class iterator_impl
|
||||
|
||||
private:
|
||||
template <std::size_t... Is>
|
||||
tuple_type get(std::index_sequence<Is...>) const
|
||||
[[nodiscard]] tuple_type get(std::index_sequence<Is...>) const
|
||||
{
|
||||
return m_current ? tuple_type{ m_current[m_item_ix[Is]].template as<Ts>()... } : tuple_type{};
|
||||
}
|
||||
|
||||
row_handle m_current;
|
||||
value_type m_value;
|
||||
row_handle_type m_current;
|
||||
tuple_type m_value;
|
||||
std::array<uint16_t, N> m_item_ix;
|
||||
};
|
||||
|
||||
@@ -193,76 +208,94 @@ class iterator_impl
|
||||
*
|
||||
* @tparam Category The category for this iterator
|
||||
*/
|
||||
template <typename Category>
|
||||
class iterator_impl<Category>
|
||||
template <bool Const>
|
||||
class iterator_impl_base<Const>
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
|
||||
template <typename, typename...>
|
||||
friend class iterator_impl;
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
friend class category;
|
||||
using category_type = std::remove_cv_t<Category>;
|
||||
using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
|
||||
|
||||
using category_type = std::conditional_t<Const, const category, category>;
|
||||
using row_type = std::conditional_t<Const, const row, row>;
|
||||
using row_handle_type = std::conditional_t<Const, const_row_handle, row_handle>;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = row_handle;
|
||||
|
||||
using value_type = std::conditional_t<Const, const_row_handle, row_handle>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
iterator_impl() = default;
|
||||
iterator_impl_base() = default;
|
||||
|
||||
iterator_impl(const iterator_impl &rhs) = default;
|
||||
iterator_impl(iterator_impl &&rhs) = default;
|
||||
iterator_impl_base(const iterator_impl_base &rhs) = default;
|
||||
iterator_impl_base(iterator_impl_base &&rhs) = default;
|
||||
|
||||
template <typename C2>
|
||||
iterator_impl(const iterator_impl<C2> &rhs)
|
||||
: m_current(const_cast<row_handle &>(rhs.m_current))
|
||||
template <bool C>
|
||||
iterator_impl_base(const iterator_impl_base<C> &rhs)
|
||||
: m_current(rhs.m_current)
|
||||
{
|
||||
}
|
||||
|
||||
iterator_impl(Category &cat, row *current)
|
||||
iterator_impl_base(category_type &cat, row_type *current)
|
||||
: m_current(cat, *current)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename IRowType>
|
||||
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, 0> &)
|
||||
: m_current(const_cast<row_handle &>(rhs.m_current))
|
||||
template <bool C>
|
||||
iterator_impl_base(const iterator_impl_base<C> &rhs, const std::array<uint16_t, 0> &)
|
||||
: m_current(rhs.m_current)
|
||||
{
|
||||
}
|
||||
|
||||
iterator_impl &operator=(iterator_impl i)
|
||||
iterator_impl_base &operator=(iterator_impl_base i)
|
||||
{
|
||||
std::swap(m_current, i.m_current);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~iterator_impl() = default;
|
||||
virtual ~iterator_impl_base() = default;
|
||||
|
||||
reference operator*()
|
||||
auto operator*()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
auto operator*() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
auto operator->()
|
||||
{
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
operator const row_handle() const
|
||||
auto operator->() const
|
||||
{
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
operator const_row_handle() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
operator row_handle()
|
||||
operator row_handle_type()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
iterator_impl &operator++()
|
||||
[[nodiscard]] int64_t row_id() const
|
||||
{
|
||||
return reinterpret_cast<int64_t>(m_current.m_row);
|
||||
}
|
||||
|
||||
iterator_impl_base &operator++()
|
||||
{
|
||||
if (m_current)
|
||||
m_current.m_row = m_current.m_row->m_next;
|
||||
@@ -270,24 +303,24 @@ class iterator_impl<Category>
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl operator++(int)
|
||||
iterator_impl_base operator++(int)
|
||||
{
|
||||
iterator_impl result(*this);
|
||||
iterator_impl_base result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
|
||||
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
|
||||
bool operator==(const iterator_impl_base &rhs) const { return m_current == rhs.m_current; }
|
||||
bool operator!=(const iterator_impl_base &rhs) const { return m_current != rhs.m_current; }
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
|
||||
template <bool C, typename... ITs>
|
||||
bool operator==(const iterator_impl_base<C, ITs...> &rhs) const
|
||||
{
|
||||
return m_current == rhs.m_current;
|
||||
}
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
|
||||
template <bool C, typename... ITs>
|
||||
bool operator!=(const iterator_impl_base<C, ITs...> &rhs) const
|
||||
{
|
||||
return m_current != rhs.m_current;
|
||||
}
|
||||
@@ -295,7 +328,7 @@ class iterator_impl<Category>
|
||||
/** @endcond */
|
||||
|
||||
private:
|
||||
row_handle m_current;
|
||||
row_handle_type m_current;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -306,18 +339,18 @@ class iterator_impl<Category>
|
||||
* @tparam T The type this iterator can be dereferenced to
|
||||
*/
|
||||
|
||||
template <typename Category, typename T>
|
||||
class iterator_impl<Category, T>
|
||||
template <bool Const, typename T>
|
||||
class iterator_impl_base<Const, T>
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
template <typename, typename...>
|
||||
friend class iterator_impl;
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
friend class category;
|
||||
|
||||
using category_type = std::remove_cv_t<Category>;
|
||||
using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
|
||||
using category_type = std::conditional_t<Const, const category, category>;
|
||||
using row_handle_type = std::conditional_t<Const, const_row_handle, row_handle>;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
@@ -325,37 +358,37 @@ class iterator_impl<Category, T>
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
iterator_impl() = default;
|
||||
iterator_impl_base() = default;
|
||||
|
||||
iterator_impl(const iterator_impl &rhs) = default;
|
||||
iterator_impl(iterator_impl &&rhs) = default;
|
||||
iterator_impl_base(const iterator_impl_base &rhs) = default;
|
||||
iterator_impl_base(iterator_impl_base &&rhs) = default;
|
||||
|
||||
template <typename C2, typename T2>
|
||||
iterator_impl(const iterator_impl<C2, T2> &rhs)
|
||||
template <bool C, typename T2>
|
||||
iterator_impl_base(const iterator_impl_base<C, T2> &rhs)
|
||||
: m_current(rhs.m_current)
|
||||
, m_value(rhs.m_value)
|
||||
, m_item_ix(rhs.m_item_ix)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename IRowType>
|
||||
iterator_impl(iterator_impl<IRowType, T> &rhs)
|
||||
: m_current(const_cast<row_handle&>(rhs.m_current))
|
||||
template <bool C>
|
||||
iterator_impl_base(iterator_impl_base<C, T> &rhs)
|
||||
: m_current(rhs.m_current)
|
||||
, m_value(rhs.m_value)
|
||||
, m_item_ix(rhs.m_item_ix)
|
||||
{
|
||||
m_value = get();
|
||||
}
|
||||
|
||||
template <typename IRowType>
|
||||
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<uint16_t, 1> &cix)
|
||||
: m_current(const_cast<row_handle&>(rhs.m_current))
|
||||
template <bool C>
|
||||
iterator_impl_base(const iterator_impl_base<C> &rhs, const std::array<uint16_t, 1> &cix)
|
||||
: m_current(rhs.m_current)
|
||||
, m_item_ix(cix[0])
|
||||
{
|
||||
m_value = get();
|
||||
}
|
||||
|
||||
iterator_impl &operator=(iterator_impl i)
|
||||
iterator_impl_base &operator=(iterator_impl_base i)
|
||||
{
|
||||
std::swap(m_current, i.m_current);
|
||||
std::swap(m_item_ix, i.m_item_ix);
|
||||
@@ -363,29 +396,39 @@ class iterator_impl<Category, T>
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~iterator_impl() = default;
|
||||
virtual ~iterator_impl_base() = default;
|
||||
|
||||
reference operator*()
|
||||
auto operator*()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
auto operator*() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
auto operator->()
|
||||
{
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
operator const row_handle() const
|
||||
auto operator->() const
|
||||
{
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
operator const_row_handle() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
operator row_handle()
|
||||
operator row_handle_type()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
iterator_impl &operator++()
|
||||
iterator_impl_base &operator++()
|
||||
{
|
||||
if (m_current)
|
||||
m_current.m_row = m_current.m_row->m_next;
|
||||
@@ -395,24 +438,24 @@ class iterator_impl<Category, T>
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl operator++(int)
|
||||
iterator_impl_base operator++(int)
|
||||
{
|
||||
iterator_impl result(*this);
|
||||
iterator_impl_base result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
|
||||
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
|
||||
bool operator==(const iterator_impl_base &rhs) const { return m_current == rhs.m_current; }
|
||||
bool operator!=(const iterator_impl_base &rhs) const { return m_current != rhs.m_current; }
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
|
||||
template <bool C, typename... ITs>
|
||||
bool operator==(const iterator_impl_base<C, ITs...> &rhs) const
|
||||
{
|
||||
return m_current == rhs.m_current;
|
||||
}
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
|
||||
template <bool C, typename... ITs>
|
||||
bool operator!=(const iterator_impl_base<C, ITs...> &rhs) const
|
||||
{
|
||||
return m_current != rhs.m_current;
|
||||
}
|
||||
@@ -420,16 +463,25 @@ class iterator_impl<Category, T>
|
||||
/** @endcond */
|
||||
|
||||
private:
|
||||
value_type get() const
|
||||
[[nodiscard]] value_type get() const
|
||||
{
|
||||
return m_current ? m_current[m_item_ix].template as<value_type>() : value_type{};
|
||||
return m_current ? m_current[m_item_ix].template get<value_type>() : value_type{};
|
||||
}
|
||||
|
||||
row_handle m_current;
|
||||
row_handle_type m_current;
|
||||
value_type m_value;
|
||||
uint16_t m_item_ix;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template<typename ... Ts>
|
||||
using iterator_impl = iterator_impl_base<false, Ts...>;
|
||||
|
||||
template<typename ... Ts>
|
||||
using const_iterator_impl = iterator_impl_base<true, Ts...>;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// iterator proxy
|
||||
|
||||
@@ -445,43 +497,42 @@ class iterator_impl<Category, T>
|
||||
* @tparam Ts The types the iterators return. See class: iterator
|
||||
*/
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
class iterator_proxy
|
||||
template <bool Const, typename... Ts>
|
||||
class iterator_proxy_base
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
static constexpr const std::size_t N = sizeof...(Ts);
|
||||
|
||||
using category_type = Category;
|
||||
using row_type = std::conditional_t<std::is_const_v<category_type>, const row, row>;
|
||||
using category_type = std::conditional_t<Const, const category, category>;
|
||||
|
||||
using iterator = iterator_impl<category_type, Ts...>;
|
||||
using row_iterator = iterator_impl<category_type>;
|
||||
using iterator = iterator_impl_base<Const, Ts...>;
|
||||
using row_iterator = iterator_impl_base<Const>;
|
||||
|
||||
iterator_proxy(category_type &cat, row_iterator pos, char const *const items[N]);
|
||||
iterator_proxy(category_type &cat, row_iterator pos, std::initializer_list<char const *> items);
|
||||
iterator_proxy_base(category_type &cat, row_iterator pos, char const *const items[N]);
|
||||
iterator_proxy_base(category_type &cat, row_iterator pos, std::initializer_list<char const *> items); // NOLINT(modernize-pass-by-value)
|
||||
|
||||
iterator_proxy(iterator_proxy &&p);
|
||||
iterator_proxy &operator=(iterator_proxy &&p);
|
||||
iterator_proxy_base(iterator_proxy_base &&p);
|
||||
iterator_proxy_base &operator=(iterator_proxy_base &&p);
|
||||
|
||||
iterator_proxy(const iterator_proxy &) = delete;
|
||||
iterator_proxy &operator=(const iterator_proxy &) = delete;
|
||||
iterator_proxy_base(const iterator_proxy_base &) = delete;
|
||||
iterator_proxy_base &operator=(const iterator_proxy_base &) = delete;
|
||||
/** @endcond */
|
||||
|
||||
iterator begin() const { return iterator(m_begin, m_item_ix); } ///< Return the iterator pointing to the first row
|
||||
iterator end() const { return iterator(m_end, m_item_ix); } ///< Return the iterator pointing past the last row
|
||||
[[nodiscard]] iterator begin() const { return iterator(m_begin, m_item_ix); } ///< Return the iterator pointing to the first row
|
||||
[[nodiscard]] iterator end() const { return iterator(m_end, m_item_ix); } ///< Return the iterator pointing past the last row
|
||||
|
||||
bool empty() const { return m_begin == m_end; } ///< Return true if the range is empty
|
||||
[[nodiscard]] bool empty() const { return m_begin == m_end; } ///< Return true if the range is empty
|
||||
explicit operator bool() const { return not empty(); } ///< Easy way to detect if the range is empty
|
||||
std::size_t size() const { return std::distance(begin(), end()); } ///< Return size of the range
|
||||
[[nodiscard]] std::size_t size() const { return std::distance(begin(), end()); } ///< Return size of the range
|
||||
|
||||
// row front() { return *begin(); }
|
||||
// row back() { return *(std::prev(end())); }
|
||||
|
||||
category_type &category() const { return *m_category; } ///< Return the category the iterator belong to
|
||||
[[nodiscard]] category_type &get_category() const { return *m_category; } ///< Return the category the iterator belong to
|
||||
|
||||
/** swap */
|
||||
void swap(iterator_proxy &rhs)
|
||||
void swap(iterator_proxy_base &rhs)
|
||||
{
|
||||
std::swap(m_category, rhs.m_category);
|
||||
std::swap(m_begin, rhs.m_begin);
|
||||
@@ -489,12 +540,23 @@ class iterator_proxy
|
||||
std::swap(m_item_ix, rhs.m_item_ix);
|
||||
}
|
||||
|
||||
protected:
|
||||
iterator_proxy_base(category_type &cat);
|
||||
|
||||
private:
|
||||
category_type *m_category;
|
||||
row_iterator m_begin, m_end;
|
||||
std::array<uint16_t, N> m_item_ix;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename... Ts>
|
||||
using iterator_proxy = iterator_proxy_base<false, Ts...>;
|
||||
|
||||
template <typename... Ts>
|
||||
using const_iterator_proxy = iterator_proxy_base<true, Ts...>;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// conditional iterator proxy
|
||||
|
||||
@@ -504,44 +566,54 @@ class iterator_proxy
|
||||
* In the case of an conditional_iterator_proxy a cif::condition is used
|
||||
* to filter out only those rows that match the condition.
|
||||
*
|
||||
* @tparam CategoryType The category the iterators belong to
|
||||
* @tparam category_type The category the iterators belong to
|
||||
* @tparam Ts The types to which the iterators can be dereferenced
|
||||
*/
|
||||
template <typename CategoryType, typename... Ts>
|
||||
class conditional_iterator_proxy
|
||||
template <bool Const, typename... Ts>
|
||||
class conditional_iterator_proxy_base
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
static constexpr const std::size_t N = sizeof...(Ts);
|
||||
|
||||
using category_type = std::remove_cv_t<CategoryType>;
|
||||
|
||||
using base_iterator = iterator_impl<CategoryType, Ts...>;
|
||||
using category_type = std::conditional_t<Const, const category, category>;
|
||||
using base_iterator = iterator_impl_base<Const, Ts...>;
|
||||
using value_type = typename base_iterator::value_type;
|
||||
using row_type = typename base_iterator::row_type;
|
||||
using row_iterator = iterator_impl<CategoryType>;
|
||||
using row_iterator = iterator_impl_base<Const>;
|
||||
|
||||
class conditional_iterator_impl
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = conditional_iterator_proxy::value_type;
|
||||
using value_type = conditional_iterator_proxy_base::value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type;
|
||||
|
||||
conditional_iterator_impl(CategoryType &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix);
|
||||
conditional_iterator_impl() = default;
|
||||
conditional_iterator_impl(category_type &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix);
|
||||
conditional_iterator_impl(const conditional_iterator_impl &i) = default;
|
||||
conditional_iterator_impl &operator=(const conditional_iterator_impl &i) = default;
|
||||
|
||||
virtual ~conditional_iterator_impl() = default;
|
||||
|
||||
reference operator*()
|
||||
auto operator*()
|
||||
{
|
||||
return *m_begin;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
auto operator*() const
|
||||
{
|
||||
return *m_begin;
|
||||
}
|
||||
|
||||
auto operator->()
|
||||
{
|
||||
m_current = *m_begin;
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
auto operator->() const
|
||||
{
|
||||
m_current = *m_begin;
|
||||
return &m_current;
|
||||
@@ -574,16 +646,16 @@ class conditional_iterator_proxy
|
||||
bool operator==(const row_iterator &rhs) const { return m_begin == rhs; }
|
||||
bool operator!=(const row_iterator &rhs) const { return m_begin != rhs; }
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return m_begin == rhs; }
|
||||
template <bool C, typename... ITs>
|
||||
bool operator==(const iterator_impl_base<C, ITs...> &rhs) const { return m_begin == rhs; }
|
||||
|
||||
template <typename IRowType, typename... ITs>
|
||||
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return m_begin != rhs; }
|
||||
template <bool C, typename... ITs>
|
||||
bool operator!=(const iterator_impl_base<C, ITs...> &rhs) const { return m_begin != rhs; }
|
||||
|
||||
private:
|
||||
CategoryType *m_cat;
|
||||
category_type *m_cat = nullptr;
|
||||
base_iterator m_begin, m_end;
|
||||
value_type m_current;
|
||||
std::remove_cv_t<value_type> m_current;
|
||||
const condition *m_condition;
|
||||
};
|
||||
|
||||
@@ -591,33 +663,42 @@ class conditional_iterator_proxy
|
||||
using reference = typename iterator::reference;
|
||||
|
||||
template <typename... Ns>
|
||||
conditional_iterator_proxy(CategoryType &cat, row_iterator pos, condition &&cond, Ns... names);
|
||||
conditional_iterator_proxy_base(category_type &cat, row_iterator pos, condition &&cond, Ns... names); // NOLINT(modernize-pass-by-value)
|
||||
|
||||
conditional_iterator_proxy(conditional_iterator_proxy &&p);
|
||||
conditional_iterator_proxy &operator=(conditional_iterator_proxy &&p);
|
||||
conditional_iterator_proxy_base(conditional_iterator_proxy_base &&p)
|
||||
{
|
||||
swap(*this, p);
|
||||
}
|
||||
|
||||
conditional_iterator_proxy(const conditional_iterator_proxy &) = delete;
|
||||
conditional_iterator_proxy &operator=(const conditional_iterator_proxy &) = delete;
|
||||
conditional_iterator_proxy_base &operator=(conditional_iterator_proxy_base &&p)
|
||||
{
|
||||
swap(*this, p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
conditional_iterator_proxy_base(const conditional_iterator_proxy_base &) = delete;
|
||||
conditional_iterator_proxy_base &operator=(const conditional_iterator_proxy_base &) = delete;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
iterator begin() const; ///< Return the iterator pointing to the first row
|
||||
iterator end() const; ///< Return the iterator pointing past the last row
|
||||
[[nodiscard]] iterator begin() const; ///< Return the iterator pointing to the first row
|
||||
[[nodiscard]] iterator end() const; ///< Return the iterator pointing past the last row
|
||||
|
||||
bool empty() const; ///< Return true if the range is empty
|
||||
[[nodiscard]] bool empty() const; ///< Return true if the range is empty
|
||||
explicit operator bool() const { return not empty(); } ///< Easy way to detect if the range is empty
|
||||
std::size_t size() const { return std::distance(begin(), end()); } ///< Return size of the range
|
||||
[[nodiscard]] std::size_t size() const { return std::distance(begin(), end()); } ///< Return size of the range
|
||||
|
||||
row_handle front() { return *begin(); } ///< Return reference to the first row
|
||||
auto front() { return *begin(); } ///< Return reference to the first row
|
||||
// row_handle back() { return *begin(); }
|
||||
|
||||
CategoryType &category() const { return *m_cat; } ///< Category the iterators belong to
|
||||
[[nodiscard]] category_type &get_category() const { return *m_cat; } ///< Category the iterators belong to
|
||||
|
||||
/** swap */
|
||||
void swap(conditional_iterator_proxy &rhs);
|
||||
template <bool C2, typename ... T2s>
|
||||
friend void swap(conditional_iterator_proxy_base<C2, T2s...> &lhs, conditional_iterator_proxy_base<C2, T2s...> &rhs);
|
||||
|
||||
private:
|
||||
CategoryType *m_cat;
|
||||
category_type *m_cat;
|
||||
condition m_condition;
|
||||
row_iterator mCBegin, mCEnd;
|
||||
std::array<uint16_t, N> mCix;
|
||||
@@ -625,9 +706,17 @@ class conditional_iterator_proxy
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename... Ts>
|
||||
using conditional_iterator_proxy = conditional_iterator_proxy_base<false, Ts...>;
|
||||
|
||||
template <typename... Ts>
|
||||
using const_conditional_iterator_proxy = conditional_iterator_proxy_base<true, Ts...>;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/** @cond */
|
||||
template <typename Category, typename... Ts>
|
||||
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const items[N])
|
||||
template <bool Const, typename... Ts>
|
||||
iterator_proxy_base<Const, Ts...>::iterator_proxy_base(category_type &cat, row_iterator pos, char const *const items[N])
|
||||
: m_category(&cat)
|
||||
, m_begin(pos)
|
||||
, m_end(cat.end())
|
||||
@@ -636,8 +725,8 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos,
|
||||
m_item_ix[i] = m_category->get_item_ix(items[i]);
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> items)
|
||||
template <bool Const, typename... Ts>
|
||||
iterator_proxy_base<Const, Ts...>::iterator_proxy_base(category_type &cat, row_iterator pos, std::initializer_list<char const *> items)
|
||||
: m_category(&cat)
|
||||
, m_begin(pos)
|
||||
, m_end(cat.end())
|
||||
@@ -649,11 +738,20 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos,
|
||||
m_item_ix[i++] = m_category->get_item_ix(item);
|
||||
}
|
||||
|
||||
template <bool Const, typename... Ts>
|
||||
iterator_proxy_base<Const, Ts...>::iterator_proxy_base(category_type &cat)
|
||||
: m_category(&cat)
|
||||
, m_begin(cat.begin())
|
||||
, m_end(cat.end())
|
||||
{
|
||||
std::iota(m_item_ix.begin(), m_item_ix.end(), 0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditional_iterator_impl(
|
||||
Category &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix)
|
||||
template <bool Const, typename... Ts>
|
||||
conditional_iterator_proxy_base<Const, Ts...>::conditional_iterator_impl::conditional_iterator_impl(
|
||||
category_type &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix)
|
||||
: m_cat(&cat)
|
||||
, m_begin(pos, cix)
|
||||
, m_end(cat.end(), cix)
|
||||
@@ -661,23 +759,13 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditio
|
||||
{
|
||||
if (m_condition == nullptr or m_condition->empty())
|
||||
m_begin = m_end;
|
||||
else
|
||||
m_current = *m_begin;
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy &&p)
|
||||
: m_cat(nullptr)
|
||||
, mCBegin(p.mCBegin)
|
||||
, mCEnd(p.mCEnd)
|
||||
, mCix(p.mCix)
|
||||
{
|
||||
std::swap(m_cat, p.m_cat);
|
||||
std::swap(mCix, p.mCix);
|
||||
m_condition.swap(p.m_condition);
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
template <bool Const, typename... Ts>
|
||||
template <typename... Ns>
|
||||
conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, condition &&cond, Ns... names)
|
||||
conditional_iterator_proxy_base<Const, Ts...>::conditional_iterator_proxy_base(category_type &cat, row_iterator pos, condition &&cond, Ns... names)
|
||||
: m_cat(&cat)
|
||||
, m_condition(std::move(cond))
|
||||
, mCBegin(pos)
|
||||
@@ -685,10 +773,8 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
|
||||
{
|
||||
static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of item names should be equal to number of requested value types");
|
||||
|
||||
if (m_condition)
|
||||
if (m_condition and m_condition.prepare(cat))
|
||||
{
|
||||
m_condition.prepare(cat);
|
||||
|
||||
while (mCBegin != mCEnd and not m_condition(*mCBegin))
|
||||
++mCBegin;
|
||||
}
|
||||
@@ -699,41 +785,39 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
|
||||
((mCix[i++] = m_cat->get_item_ix(names)), ...);
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
conditional_iterator_proxy<Category, Ts...> &conditional_iterator_proxy<Category, Ts...>::operator=(conditional_iterator_proxy &&p)
|
||||
template <bool Const, typename... Ts>
|
||||
auto conditional_iterator_proxy_base<Const, Ts...>::begin() const -> iterator
|
||||
{
|
||||
swap(p);
|
||||
return *this;
|
||||
return iterator{ *m_cat, mCBegin, m_condition, mCix };
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::begin() const
|
||||
template <bool Const, typename... Ts>
|
||||
auto conditional_iterator_proxy_base<Const, Ts...>::end() const -> iterator
|
||||
{
|
||||
return iterator(*m_cat, mCBegin, m_condition, mCix);
|
||||
return iterator{ *m_cat, mCEnd, m_condition, mCix };
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::end() const
|
||||
{
|
||||
return iterator(*m_cat, mCEnd, m_condition, mCix);
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
bool conditional_iterator_proxy<Category, Ts...>::empty() const
|
||||
template <bool Const, typename... Ts>
|
||||
bool conditional_iterator_proxy_base<Const, Ts...>::empty() const
|
||||
{
|
||||
return mCBegin == mCEnd;
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
void conditional_iterator_proxy<Category, Ts...>::swap(conditional_iterator_proxy &rhs)
|
||||
template <bool Const, typename... Ts>
|
||||
void swap(conditional_iterator_proxy_base<Const, Ts...> &lhs, conditional_iterator_proxy_base<Const, Ts...> &rhs)
|
||||
{
|
||||
std::swap(m_cat, rhs.m_cat);
|
||||
m_condition.swap(rhs.m_condition);
|
||||
std::swap(mCBegin, rhs.mCBegin);
|
||||
std::swap(mCEnd, rhs.mCEnd);
|
||||
std::swap(mCix, rhs.mCix);
|
||||
std::swap(lhs.m_cat, rhs.m_cat);
|
||||
std::swap(lhs.m_condition, rhs.m_condition);
|
||||
std::swap(lhs.mCBegin, rhs.mCBegin);
|
||||
std::swap(lhs.mCEnd, rhs.mCEnd);
|
||||
std::swap(lhs.mCix, rhs.mCix);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// template <bool Const, typename... Ts>
|
||||
|
||||
|
||||
/** @endcond */
|
||||
|
||||
} // namespace cif
|
||||
} // namespace cif
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
@@ -56,22 +54,22 @@ namespace cif
|
||||
* @tparam M The type of the derived class
|
||||
*/
|
||||
template <typename M>
|
||||
class matrix_expression
|
||||
class matrix_expression // NOLINT(bugprone-crtp-constructor-accessibility)
|
||||
{
|
||||
public:
|
||||
constexpr std::size_t dim_m() const { return static_cast<const M &>(*this).dim_m(); } ///< Return the size (dimension) in direction m
|
||||
constexpr std::size_t dim_n() const { return static_cast<const M &>(*this).dim_n(); } ///< Return the size (dimension) in direction n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return static_cast<const M &>(*this).dim_m(); } ///< Return the size (dimension) in direction m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return static_cast<const M &>(*this).dim_n(); } ///< Return the size (dimension) in direction n
|
||||
|
||||
constexpr bool empty() const { return dim_m() == 0 or dim_n() == 0; } ///< Convenient way to test for empty matrices
|
||||
[[nodiscard]] constexpr bool empty() const { return dim_m() == 0 or dim_n() == 0; } ///< Convenient way to test for empty matrices
|
||||
|
||||
/** Return a reference to element [ @a i, @a j ] */
|
||||
constexpr auto &operator()(std::size_t i, std::size_t j)
|
||||
[[nodiscard]] constexpr auto &operator()(std::size_t i, std::size_t j)
|
||||
{
|
||||
return static_cast<M &>(*this).operator()(i, j);
|
||||
}
|
||||
|
||||
/** Return the value of element [ @a i, @a j ] */
|
||||
constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return static_cast<const M &>(*this).operator()(i, j);
|
||||
}
|
||||
@@ -124,6 +122,23 @@ class matrix_expression
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename M2>
|
||||
constexpr bool operator==(const matrix_expression<M2> &m) const
|
||||
{
|
||||
bool same = false;
|
||||
if (dim_m() == m.dim_m() and dim_n() == m.dim_n())
|
||||
{
|
||||
same = true;
|
||||
for (std::size_t i = 0; same and i < m.dim_m(); ++i)
|
||||
{
|
||||
for (std::size_t j = 0; same and j < m.dim_n(); ++j)
|
||||
same = operator()(i, j) == m(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return same;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -187,11 +202,11 @@ class matrix : public matrix_expression<matrix<F>>
|
||||
matrix &operator=(const matrix &m) = default;
|
||||
/** @endcond */
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_m; } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_n; } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_m; } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_n; } ///< Return dimension n
|
||||
|
||||
/** Return the value of element [ @a i, @a j ] */
|
||||
constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
assert(i < m_m);
|
||||
assert(j < m_n);
|
||||
@@ -199,7 +214,7 @@ class matrix : public matrix_expression<matrix<F>>
|
||||
}
|
||||
|
||||
/** Return a reference to element [ @a i, @a j ] */
|
||||
constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
[[nodiscard]] constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
{
|
||||
assert(i < m_m);
|
||||
assert(j < m_n);
|
||||
@@ -273,11 +288,11 @@ class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return M; } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return N; } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return M; } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return N; } ///< Return dimension n
|
||||
|
||||
/** Return the value of element [ @a i, @a j ] */
|
||||
constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
assert(i < M);
|
||||
assert(j < N);
|
||||
@@ -285,7 +300,7 @@ class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
|
||||
}
|
||||
|
||||
/** Return a reference to element [ @a i, @a j ] */
|
||||
constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
[[nodiscard]] constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
{
|
||||
assert(i < M);
|
||||
assert(j < N);
|
||||
@@ -337,11 +352,11 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
|
||||
symmetric_matrix &operator=(const symmetric_matrix &m) = default;
|
||||
/** @endcond */
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_n; } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_n; } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_n; } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_n; } ///< Return dimension n
|
||||
|
||||
/** Return the value of element [ @a i, @a j ] */
|
||||
constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return i < j
|
||||
? m_data[(j * (j + 1)) / 2 + i]
|
||||
@@ -349,7 +364,7 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
|
||||
}
|
||||
|
||||
/** Return a reference to element [ @a i, @a j ] */
|
||||
constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
[[nodiscard]] constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
{
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
@@ -393,11 +408,11 @@ class symmetric_matrix_fixed : public matrix_expression<symmetric_matrix_fixed<F
|
||||
symmetric_matrix_fixed &operator=(const symmetric_matrix_fixed &m) = default;
|
||||
/** @endcond */
|
||||
|
||||
constexpr std::size_t dim_m() const { return M; } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return M; } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return M; } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return M; } ///< Return dimension n
|
||||
|
||||
/** Return the value of element [ @a i, @a j ] */
|
||||
constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return i < j
|
||||
? m_data[(j * (j + 1)) / 2 + i]
|
||||
@@ -405,7 +420,7 @@ class symmetric_matrix_fixed : public matrix_expression<symmetric_matrix_fixed<F
|
||||
}
|
||||
|
||||
/** Return a reference to element [ @a i, @a j ] */
|
||||
constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
[[nodiscard]] constexpr value_type &operator()(std::size_t i, std::size_t j)
|
||||
{
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
@@ -449,11 +464,11 @@ class identity_matrix : public matrix_expression<identity_matrix<F>>
|
||||
{
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_n; } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_n; } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_n; } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_n; } ///< Return dimension n
|
||||
|
||||
/** Return the value of element [ @a i, @a j ] */
|
||||
constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr value_type operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return static_cast<value_type>(i == j ? 1 : 0);
|
||||
}
|
||||
@@ -484,11 +499,11 @@ class matrix_subtraction : public matrix_expression<matrix_subtraction<M1, M2>>
|
||||
assert(m_m1.dim_n() == m_m2.dim_n());
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_m1.dim_m(); } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_m1.dim_n(); } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_m1.dim_m(); } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_m1.dim_n(); } ///< Return dimension n
|
||||
|
||||
/** Access to the value of element [ @a i, @a j ] */
|
||||
constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return m_m1(i, j) - m_m2(i, j);
|
||||
}
|
||||
@@ -523,11 +538,11 @@ class matrix_matrix_multiplication : public matrix_expression<matrix_matrix_mult
|
||||
assert(m1.dim_m() == m2.dim_n());
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_m1.dim_m(); } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_m1.dim_n(); } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_m1.dim_m(); } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_m1.dim_n(); } ///< Return dimension n
|
||||
|
||||
/** Access to the value of element [ @a i, @a j ] */
|
||||
constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
using value_type = decltype(m_m1(0, 0));
|
||||
|
||||
@@ -564,11 +579,11 @@ class matrix_scalar_multiplication : public matrix_expression<matrix_scalar_mult
|
||||
{
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_m.dim_m(); } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_m.dim_n(); } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_m.dim_m(); } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_m.dim_n(); } ///< Return dimension n
|
||||
|
||||
/** Access to the value of element [ @a i, @a j ] */
|
||||
constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return m_m(i, j) * m_v;
|
||||
}
|
||||
@@ -579,21 +594,50 @@ class matrix_scalar_multiplication : public matrix_expression<matrix_scalar_mult
|
||||
};
|
||||
|
||||
/** First implementation of operator*, enabled if the second parameter is a scalar */
|
||||
template <typename M1, typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
template <typename M1, typename T>
|
||||
auto operator*(const matrix_expression<M1> &m, T v)
|
||||
{
|
||||
requires (std::is_floating_point_v<T>) {
|
||||
return matrix_scalar_multiplication(m, v);
|
||||
}
|
||||
|
||||
/** First implementation of operator*, enabled if the second parameter is not a scalar and thus must be a matrix, right? */
|
||||
template <typename M1, typename M2, std::enable_if_t<not std::is_floating_point_v<M2>, int> = 0>
|
||||
template <typename M1, typename M2>
|
||||
auto operator*(const matrix_expression<M1> &m1, const matrix_expression<M2> &m2)
|
||||
{
|
||||
requires (not std::is_floating_point_v<M2>) {
|
||||
return matrix_matrix_multiplication(m1, m2);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename M2>
|
||||
class sub_matrix : public matrix_expression<sub_matrix<M2>>
|
||||
{
|
||||
public:
|
||||
sub_matrix(const M2 &m, int i, int j)
|
||||
: m_m(m)
|
||||
, m_i(i)
|
||||
, m_j(j)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_m.dim_m() - 1; } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_m.dim_n() - 1; } ///< Return dimension n
|
||||
|
||||
/** Access to the value of element [ @a i, @a j ] */
|
||||
[[nodiscard]] constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
return m_m(
|
||||
i >= m_i ? i + 1 : i,
|
||||
j >= m_j ? j + 1 : j);
|
||||
}
|
||||
|
||||
private:
|
||||
const M2 &m_m;
|
||||
std::size_t m_i, m_j;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/** Generic routine to calculate the determinant of a matrix
|
||||
*
|
||||
* @note This is currently only implemented for fixed matrices of size 3x3
|
||||
@@ -605,11 +649,23 @@ auto determinant(const M &m);
|
||||
template <typename F = float>
|
||||
auto determinant(const matrix3x3<F> &m)
|
||||
{
|
||||
return (m(0, 0) * (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1)) +
|
||||
m(0, 1) * (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) +
|
||||
m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0)));
|
||||
return (m(0, 0) * ((m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1))) +
|
||||
m(0, 1) * ((m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2))) +
|
||||
m(0, 2) * ((m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0))));
|
||||
}
|
||||
|
||||
/** Implementation of the determinant function for fixed size matrices of size 4x4 */
|
||||
template <typename F = float>
|
||||
F determinant(const matrix4x4<F> &m)
|
||||
{
|
||||
return m(0, 0) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 0))) -
|
||||
m(0, 1) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 1))) +
|
||||
m(0, 2) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 2))) -
|
||||
m(0, 3) * determinant(matrix3x3<F>(sub_matrix<decltype(m)>(m, 0, 3)));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/** Generic routine to calculate the inverse of a matrix
|
||||
*
|
||||
* @note This is currently only implemented for fixed matrices of size 3x3
|
||||
@@ -655,11 +711,11 @@ class matrix_cofactors : public matrix_expression<matrix_cofactors<M>>
|
||||
{
|
||||
}
|
||||
|
||||
constexpr std::size_t dim_m() const { return m_m.dim_m(); } ///< Return dimension m
|
||||
constexpr std::size_t dim_n() const { return m_m.dim_n(); } ///< Return dimension n
|
||||
[[nodiscard]] constexpr std::size_t dim_m() const { return m_m.dim_m(); } ///< Return dimension m
|
||||
[[nodiscard]] constexpr std::size_t dim_n() const { return m_m.dim_n(); } ///< Return dimension n
|
||||
|
||||
/** Access to the value of element [ @a i, @a j ] */
|
||||
constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
[[nodiscard]] constexpr auto operator()(std::size_t i, std::size_t j) const
|
||||
{
|
||||
const std::size_t ixs[4][3] = {
|
||||
{ 1, 2, 3 },
|
||||
|
||||
@@ -29,12 +29,13 @@
|
||||
#include "cif++/atom_type.hpp"
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
#if __cpp_lib_format
|
||||
# include <format>
|
||||
# include <utility>
|
||||
#endif
|
||||
|
||||
/** @file model.hpp
|
||||
@@ -86,14 +87,14 @@ class atom
|
||||
/** @cond */
|
||||
struct atom_impl : public std::enable_shared_from_this<atom_impl>
|
||||
{
|
||||
atom_impl(const datablock &db, std::string_view id)
|
||||
atom_impl(datablock &db, std::string_view id)
|
||||
: m_db(db)
|
||||
, m_cat(db["atom_site"])
|
||||
, m_id(id)
|
||||
{
|
||||
auto r = row();
|
||||
if (r)
|
||||
tie(m_location.m_x, m_location.m_y, m_location.m_z) = r.get("Cartn_x", "Cartn_y", "Cartn_z");
|
||||
std::tie(m_location.m_x, m_location.m_y, m_location.m_z) = r.get<float, float, float>("Cartn_x", "Cartn_y", "Cartn_z");
|
||||
}
|
||||
|
||||
// constructor for a symmetry copy of an atom
|
||||
@@ -106,46 +107,49 @@ class atom
|
||||
|
||||
atom_impl(const atom_impl &i) = default;
|
||||
|
||||
int compare(const atom_impl &b) const;
|
||||
[[nodiscard]] int compare(const atom_impl &b) const;
|
||||
|
||||
// bool getAnisoU(float anisou[6]) const;
|
||||
|
||||
int get_charge() const;
|
||||
[[nodiscard]] int get_charge() const;
|
||||
|
||||
void moveTo(const point &p);
|
||||
|
||||
// const compound *compound() const;
|
||||
|
||||
std::string get_property(std::string_view name) const;
|
||||
int get_property_int(std::string_view name) const;
|
||||
float get_property_float(std::string_view name) const;
|
||||
|
||||
void set_property(const std::string_view name, const std::string &value);
|
||||
[[nodiscard]] const item_value &get_property(std::string_view name) const;
|
||||
void set_property(const std::string_view name, item_value value);
|
||||
|
||||
row_handle row()
|
||||
{
|
||||
return m_cat[{ { "id", m_id } }];
|
||||
return m_cat[{ { .name = "id", .value = m_id } }];
|
||||
}
|
||||
|
||||
const row_handle row() const
|
||||
[[nodiscard]] const_row_handle row() const
|
||||
{
|
||||
return m_cat[{ { "id", m_id } }];
|
||||
return m_cat[{ { .name = "id", .value = m_id } }];
|
||||
}
|
||||
|
||||
row_handle row_aniso()
|
||||
{
|
||||
row_handle result{};
|
||||
auto cat = m_db.get("atom_site_anisotrop");
|
||||
return cat ? cat->operator[]({ { "id", m_id } }) : row_handle{};
|
||||
if (cat)
|
||||
result = cat->operator[]({ { .name = "id", .value = m_id } });
|
||||
return result;
|
||||
}
|
||||
|
||||
const row_handle row_aniso() const
|
||||
[[nodiscard]] const_row_handle row_aniso() const
|
||||
{
|
||||
row_handle result{};
|
||||
auto cat = m_db.get("atom_site_anisotrop");
|
||||
return cat ? cat->operator[]({ { "id", m_id } }) : row_handle{};
|
||||
if (cat)
|
||||
result = cat->operator[]({ { .name = "id", .value = m_id } });
|
||||
return result;
|
||||
}
|
||||
|
||||
const datablock &m_db;
|
||||
const category &m_cat;
|
||||
datablock &m_db;
|
||||
category &m_cat;
|
||||
std::string m_id;
|
||||
point m_location;
|
||||
std::string m_symop = "1_555";
|
||||
@@ -156,7 +160,7 @@ class atom
|
||||
/**
|
||||
* @brief Construct a new, empty atom object
|
||||
*/
|
||||
atom() {}
|
||||
atom() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct a new atom object using @a impl as impl
|
||||
@@ -164,7 +168,7 @@ class atom
|
||||
* @param impl The implementation objectt
|
||||
*/
|
||||
atom(std::shared_ptr<atom_impl> impl)
|
||||
: m_impl(impl)
|
||||
: m_impl(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -172,17 +176,32 @@ class atom
|
||||
* @brief Copy construct a new atom object
|
||||
*/
|
||||
atom(const atom &rhs)
|
||||
: m_impl(rhs.m_impl)
|
||||
: atom(rhs.m_impl)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move construct a new atom object
|
||||
*/
|
||||
atom(atom &&rhs)
|
||||
{
|
||||
std::swap(m_impl, rhs.m_impl);
|
||||
}
|
||||
|
||||
/// \brief Copy assignement operator
|
||||
atom &operator=(atom rhs)
|
||||
{
|
||||
std::swap(m_impl, rhs.m_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new atom object based on a cif::row
|
||||
*
|
||||
* @param db The datablock where the _atom_site category resides
|
||||
* @param row The row containing the data for this atom
|
||||
*/
|
||||
atom(const datablock &db, const row_handle &row)
|
||||
atom(datablock &db, const_row_handle row)
|
||||
: atom(std::make_shared<atom_impl>(db, row["id"].as<std::string>()))
|
||||
{
|
||||
}
|
||||
@@ -200,46 +219,46 @@ class atom
|
||||
}
|
||||
|
||||
/// \brief To quickly test if the atom has data
|
||||
explicit operator bool() const { return (bool)m_impl; }
|
||||
|
||||
/// \brief Copy assignement operator
|
||||
atom &operator=(const atom &rhs) = default;
|
||||
explicit operator bool() const { return m_impl.operator bool(); }
|
||||
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom
|
||||
std::string get_property(std::string_view name) const
|
||||
[[nodiscard]] const item_value &get_property_value(std::string_view name) const
|
||||
{
|
||||
if (not m_impl)
|
||||
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
|
||||
return m_impl->get_property(name);
|
||||
}
|
||||
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom cast to an int
|
||||
int get_property_int(std::string_view name) const
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom as string
|
||||
[[nodiscard]] auto get_property(std::string_view name) const
|
||||
{
|
||||
if (not m_impl)
|
||||
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
|
||||
return m_impl->get_property_int(name);
|
||||
return get_property_value(name).get<std::string>();
|
||||
}
|
||||
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom cast to a float
|
||||
float get_property_float(std::string_view name) const
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom as float
|
||||
[[nodiscard]] auto get_property_float(std::string_view name) const
|
||||
{
|
||||
if (not m_impl)
|
||||
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
|
||||
return m_impl->get_property_float(name);
|
||||
return get_property_value(name).get<float>();
|
||||
}
|
||||
|
||||
/// \brief Return the item named @a name in the _atom_site category for this atom as string
|
||||
[[nodiscard]] auto get_property_int(std::string_view name) const
|
||||
{
|
||||
return get_property_value(name).get<int>();
|
||||
}
|
||||
|
||||
/// \brief Set value for the item named @a name in the _atom_site category to @a value
|
||||
void set_property(const std::string_view name, const std::string &value)
|
||||
void set_property(const std::string_view name, item_value value)
|
||||
{
|
||||
if (not m_impl)
|
||||
throw std::logic_error("Error trying to modify an uninitialized atom");
|
||||
m_impl->set_property(name, value);
|
||||
m_impl->set_property(name, std::move(value));
|
||||
}
|
||||
|
||||
/// \brief Set value for the item named @a name in the _atom_site category to @a value
|
||||
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
|
||||
template <typename T>
|
||||
void set_property(const std::string_view name, const T &value)
|
||||
requires(std::is_arithmetic_v<T>)
|
||||
{
|
||||
set_property(name, std::to_string(value));
|
||||
}
|
||||
@@ -249,13 +268,13 @@ class atom
|
||||
* @note Although I've never seen anything other than integers,
|
||||
* the standard says this should be a string and so we use that.
|
||||
*/
|
||||
const std::string &id() const { return impl().m_id; }
|
||||
[[nodiscard]] const std::string &id() const { return impl().m_id; }
|
||||
|
||||
/// \brief Return the type of the atom
|
||||
cif::atom_type get_type() const { return atom_type_traits(get_property("type_symbol")).type(); }
|
||||
[[nodiscard]] cif::atom_type get_type() const { return atom_type_traits(get_property("type_symbol")).type(); }
|
||||
|
||||
/// \brief Return the cached location of this atom
|
||||
point get_location() const { return impl().m_location; }
|
||||
[[nodiscard]] point get_location() const { return impl().m_location; }
|
||||
|
||||
/// \brief Set the location of this atom, will set both the cached data as well as the data in the underlying _atom_site category
|
||||
void set_location(point p)
|
||||
@@ -307,56 +326,56 @@ class atom
|
||||
}
|
||||
|
||||
/// for direct access to underlying data, be careful!
|
||||
const row_handle get_row() const { return impl().row(); }
|
||||
[[nodiscard]] const_row_handle get_row() const { return impl().row(); }
|
||||
|
||||
/// for direct access to underlying data, be careful!
|
||||
const row_handle get_row_aniso() const { return impl().row_aniso(); }
|
||||
[[nodiscard]] const_row_handle get_row_aniso() const { return impl().row_aniso(); }
|
||||
|
||||
/// Return if the atom is actually a symmetry copy or the original one
|
||||
bool is_symmetry_copy() const { return impl().m_symop != "1_555"; }
|
||||
[[nodiscard]] bool is_symmetry_copy() const { return impl().m_symop != "1_555"; }
|
||||
|
||||
/// Return the symmetry operator used
|
||||
std::string symmetry() const { return impl().m_symop; }
|
||||
[[nodiscard]] std::string symmetry() const { return impl().m_symop; }
|
||||
|
||||
/// Return true if this atom is part of a water molecule
|
||||
bool is_water() const
|
||||
[[nodiscard]] bool is_water() const
|
||||
{
|
||||
auto comp_id = get_label_comp_id();
|
||||
return comp_id == "HOH" or comp_id == "H2O" or comp_id == "WAT";
|
||||
}
|
||||
|
||||
/// Return the charge
|
||||
int get_charge() const { return impl().get_charge(); }
|
||||
[[nodiscard]] int get_charge() const { return impl().get_charge(); }
|
||||
|
||||
/// Return the occupancy
|
||||
float get_occupancy() const { return get_property_float("occupancy"); }
|
||||
[[nodiscard]] float get_occupancy() const { return get_property_float("occupancy"); }
|
||||
|
||||
// specifications
|
||||
|
||||
std::string get_label_asym_id() const { return get_property("label_asym_id"); } ///< Return the label_asym_id property
|
||||
int get_label_seq_id() const { return get_property_int("label_seq_id"); } ///< Return the label_seq_id property
|
||||
std::string get_label_atom_id() const { return get_property("label_atom_id"); } ///< Return the label_atom_id property
|
||||
std::string get_label_alt_id() const { return get_property("label_alt_id"); } ///< Return the label_alt_id property
|
||||
std::string get_label_comp_id() const { return get_property("label_comp_id"); } ///< Return the label_comp_id property
|
||||
std::string get_label_entity_id() const { return get_property("label_entity_id"); } ///< Return the label_entity_id property
|
||||
[[nodiscard]] std::string get_label_asym_id() const { return get_property("label_asym_id"); } ///< Return the label_asym_id property
|
||||
[[nodiscard]] int get_label_seq_id() const { return get_property_int("label_seq_id"); } ///< Return the label_seq_id property
|
||||
[[nodiscard]] std::string get_label_atom_id() const { return get_property("label_atom_id"); } ///< Return the label_atom_id property
|
||||
[[nodiscard]] std::string get_label_alt_id() const { return get_property("label_alt_id"); } ///< Return the label_alt_id property
|
||||
[[nodiscard]] std::string get_label_comp_id() const { return get_property("label_comp_id"); } ///< Return the label_comp_id property
|
||||
[[nodiscard]] std::string get_label_entity_id() const { return get_property("label_entity_id"); } ///< Return the label_entity_id property
|
||||
|
||||
std::string get_auth_asym_id() const { return get_property("auth_asym_id"); } ///< Return the auth_asym_id property
|
||||
std::string get_auth_seq_id() const { return get_property("auth_seq_id"); } ///< Return the auth_seq_id property
|
||||
std::string get_auth_atom_id() const { return get_property("auth_atom_id"); } ///< Return the auth_atom_id property
|
||||
std::string get_auth_alt_id() const { return get_property("pdbx_auth_alt_id"); } ///< Return the auth_alt_id property
|
||||
std::string get_auth_comp_id() const { return get_property("auth_comp_id"); } ///< Return the auth_comp_id property
|
||||
std::string get_pdb_ins_code() const { return get_property("pdbx_PDB_ins_code"); } ///< Return the pdb_ins_code property
|
||||
[[nodiscard]] std::string get_auth_asym_id() const { return get_property("auth_asym_id"); } ///< Return the auth_asym_id property
|
||||
[[nodiscard]] std::string get_auth_seq_id() const { return get_property("auth_seq_id"); } ///< Return the auth_seq_id property
|
||||
[[nodiscard]] std::string get_auth_atom_id() const { return get_property("auth_atom_id"); } ///< Return the auth_atom_id property
|
||||
[[nodiscard]] std::string get_auth_alt_id() const { return get_property("pdbx_auth_alt_id"); } ///< Return the auth_alt_id property
|
||||
[[nodiscard]] std::string get_auth_comp_id() const { return get_property("auth_comp_id"); } ///< Return the auth_comp_id property
|
||||
[[nodiscard]] std::string get_pdb_ins_code() const { return get_property("pdbx_PDB_ins_code"); } ///< Return the pdb_ins_code property
|
||||
|
||||
/// Return true if this atom is an alternate
|
||||
bool is_alternate() const
|
||||
[[nodiscard]] bool is_alternate() const
|
||||
{
|
||||
if (auto alt_id = get_label_alt_id(); alt_id.empty() or alt_id == ".")
|
||||
if (auto alt_id = get_label_alt_id(); alt_id.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Convenience method to return a string that might be ID in PDB space
|
||||
std::string pdb_id() const
|
||||
[[nodiscard]] std::string pdb_id() const
|
||||
{
|
||||
return get_label_comp_id() + '_' + get_auth_asym_id() + '_' + get_auth_seq_id() + get_pdb_ins_code();
|
||||
}
|
||||
@@ -380,7 +399,7 @@ class atom
|
||||
}
|
||||
|
||||
/// Is this atom a backbone atom
|
||||
bool is_back_bone() const
|
||||
[[nodiscard]] bool is_back_bone() const
|
||||
{
|
||||
auto atomID = get_label_atom_id();
|
||||
return atomID == "N" or atomID == "O" or atomID == "C" or atomID == "CA";
|
||||
@@ -393,7 +412,7 @@ class atom
|
||||
}
|
||||
|
||||
/// Compare this atom with @a b
|
||||
int compare(const atom &b) const { return impl().compare(*b.m_impl); }
|
||||
[[nodiscard]] int compare(const atom &b) const { return impl().compare(*b.m_impl); }
|
||||
|
||||
/// Should this atom sort before @a rhs
|
||||
bool operator<(const atom &rhs) const
|
||||
@@ -407,7 +426,7 @@ class atom
|
||||
private:
|
||||
friend class structure;
|
||||
|
||||
const atom_impl &impl() const
|
||||
[[nodiscard]] const atom_impl &impl() const
|
||||
{
|
||||
if (not m_impl)
|
||||
throw std::runtime_error("Uninitialized atom, not found?");
|
||||
@@ -471,17 +490,15 @@ class residue
|
||||
/**
|
||||
* @brief Construct a new residue object based on key items
|
||||
*/
|
||||
residue(structure &structure, const std::string &compoundID,
|
||||
const std::string &asymID, int seqID,
|
||||
const std::string &authAsymID, const std::string &authSeqID,
|
||||
const std::string &pdbInsCode)
|
||||
residue(structure &structure, std::string compoundID, std::string asymID, int seqID,
|
||||
std::string authAsymID, std::string authSeqID, std::string pdbInsCode)
|
||||
: m_structure(&structure)
|
||||
, m_compound_id(compoundID)
|
||||
, m_asym_id(asymID)
|
||||
, m_compound_id(std::move(compoundID))
|
||||
, m_asym_id(std::move(asymID))
|
||||
, m_seq_id(seqID)
|
||||
, m_pdb_strand_id(authAsymID)
|
||||
, m_pdb_seq_num(authSeqID)
|
||||
, m_pdb_ins_code(pdbInsCode)
|
||||
, m_pdb_strand_id(std::move(authAsymID))
|
||||
, m_pdb_seq_num(std::move(authSeqID))
|
||||
, m_pdb_ins_code(std::move(pdbInsCode))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -489,33 +506,51 @@ class residue
|
||||
residue(structure &structure, const std::vector<atom> &atoms);
|
||||
|
||||
/** @cond */
|
||||
residue(const residue &rhs) = delete;
|
||||
residue &operator=(const residue &rhs) = delete;
|
||||
residue(const residue &rhs) = default;
|
||||
residue(residue &&rhs)
|
||||
{
|
||||
swap(*this, rhs);
|
||||
}
|
||||
|
||||
residue(residue &&rhs) = default;
|
||||
residue &operator=(residue &&rhs) = default;
|
||||
residue &operator=(residue rhs)
|
||||
{
|
||||
swap(*this, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend void swap(residue &a, residue &b) noexcept
|
||||
{
|
||||
if (&a != &b)
|
||||
{
|
||||
std::swap(a.m_structure, b.m_structure);
|
||||
std::swap(a.m_asym_id, b.m_asym_id);
|
||||
std::swap(a.m_seq_id, b.m_seq_id);
|
||||
std::swap(a.m_pdb_ins_code, b.m_pdb_ins_code);
|
||||
std::swap(a.m_atoms, b.m_atoms);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~residue() = default;
|
||||
/** @endcond */
|
||||
|
||||
/** Return the entity_id of this residue */
|
||||
std::string get_entity_id() const;
|
||||
[[nodiscard]] std::string get_entity_id() const;
|
||||
|
||||
/** Return the entity type of this residue */
|
||||
EntityType entity_type() const;
|
||||
[[nodiscard]] EntityType entity_type() const;
|
||||
|
||||
const std::string &get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
int get_seq_id() const { return m_seq_id; } ///< Return the seq_id
|
||||
[[nodiscard]] const std::string &get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
[[nodiscard]] int get_seq_id() const { return m_seq_id; } ///< Return the seq_id
|
||||
|
||||
const std::string get_pdb_strand_id() const { return m_pdb_strand_id; } ///< Return the pdb_strand_id
|
||||
const std::string get_pdb_seq_num() const { return m_pdb_seq_num; } ///< Return the pdb_seq_num
|
||||
std::string get_pdb_ins_code() const { return m_pdb_ins_code; } ///< Return the pdb_ins_code
|
||||
[[nodiscard]] const std::string get_pdb_strand_id() const { return m_pdb_strand_id; } ///< Return the pdb_strand_id
|
||||
[[nodiscard]] const std::string get_pdb_seq_num() const { return m_pdb_seq_num; } ///< Return the pdb_seq_num
|
||||
[[nodiscard]] std::string get_pdb_ins_code() const { return m_pdb_ins_code; } ///< Return the pdb_ins_code
|
||||
|
||||
const std::string &get_compound_id() const { return m_compound_id; } ///< Return the compound_id
|
||||
void set_compound_id(const std::string &id) { m_compound_id = id; } ///< Set the compound_id to @a id
|
||||
[[nodiscard]] const std::string &get_compound_id() const { return m_compound_id; } ///< Return the compound_id
|
||||
void set_compound_id(const std::string &id) { m_compound_id = id; } ///< Set the compound_id to @a id
|
||||
|
||||
/** Return the structure this residue belongs to */
|
||||
structure *get_structure() const { return m_structure; }
|
||||
[[nodiscard]] structure *get_structure() const { return m_structure; }
|
||||
|
||||
/** Return a list of the atoms for this residue */
|
||||
std::vector<atom> &atoms()
|
||||
@@ -524,7 +559,7 @@ class residue
|
||||
}
|
||||
|
||||
/** Return a const list of the atoms for this residue */
|
||||
const std::vector<atom> &atoms() const
|
||||
[[nodiscard]] const std::vector<atom> &atoms() const
|
||||
{
|
||||
return m_atoms;
|
||||
}
|
||||
@@ -533,40 +568,40 @@ class residue
|
||||
void add_atom(atom &atom);
|
||||
|
||||
/// \brief Unique atoms returns only the atoms without alternates and the first of each alternate atom id.
|
||||
std::vector<atom> unique_atoms() const;
|
||||
[[nodiscard]] std::vector<atom> unique_atoms() const;
|
||||
|
||||
/// \brief Return the atom with atom_id @a atomID
|
||||
atom get_atom_by_atom_id(const std::string &atomID) const;
|
||||
[[nodiscard]] atom get_atom_by_atom_id(const std::string &atomID) const;
|
||||
|
||||
/// \brief Return the atom with atom_id @a atomID and alternate_id @a altID
|
||||
atom get_atom_by_atom_id(const std::string &atomID, const std::string &altID) const;
|
||||
[[nodiscard]] atom get_atom_by_atom_id(const std::string &atomID, const std::string &altID) const;
|
||||
|
||||
/// \brief Return the list of atoms having ID \a atomID
|
||||
///
|
||||
/// This includes all alternate atoms with this ID
|
||||
/// whereas get_atom_by_atom_id only returns the first unique atom
|
||||
std::vector<atom> get_atoms_by_id(const std::string &atomID) const;
|
||||
[[nodiscard]] std::vector<atom> get_atoms_by_id(const std::string &atomID) const;
|
||||
|
||||
/// \brief Is this residue a single entity?
|
||||
bool is_entity() const;
|
||||
[[nodiscard]] bool is_entity() const;
|
||||
|
||||
/// \brief Is this residue a water molecule?
|
||||
bool is_water() const { return m_compound_id == "HOH"; }
|
||||
[[nodiscard]] bool is_water() const { return m_compound_id == "HOH"; }
|
||||
|
||||
/// \brief Return true if this residue has alternate atoms
|
||||
bool has_alternate_atoms() const;
|
||||
[[nodiscard]] bool has_alternate_atoms() const;
|
||||
|
||||
/// \brief Return true if this residue has alternate atoms for the atom \a atomID
|
||||
bool has_alternate_atoms_for(const std::string &atomID) const;
|
||||
[[nodiscard]] bool has_alternate_atoms_for(const std::string &atomID) const;
|
||||
|
||||
/// \brief Return the list of unique alt ID's present in this residue
|
||||
std::set<std::string> get_alternate_ids() const;
|
||||
[[nodiscard]] std::set<std::string> get_alternate_ids() const;
|
||||
|
||||
/// \brief Return the list of unique atom ID's
|
||||
std::set<std::string> get_atom_ids() const;
|
||||
[[nodiscard]] std::set<std::string> get_atom_ids() const;
|
||||
|
||||
/// \brief Return a tuple containing the center location and the radius for the atoms of this residue
|
||||
std::tuple<point, float> center_and_radius() const;
|
||||
[[nodiscard]] std::tuple<point, float> center_and_radius() const;
|
||||
|
||||
/// \brief Write the residue @a res to the std::ostream @a os
|
||||
friend std::ostream &operator<<(std::ostream &os, const residue &res);
|
||||
@@ -587,7 +622,7 @@ class residue
|
||||
|
||||
protected:
|
||||
/** @cond */
|
||||
residue() {}
|
||||
residue() = default;
|
||||
|
||||
structure *m_structure = nullptr;
|
||||
std::string m_compound_id, m_asym_id;
|
||||
@@ -607,58 +642,70 @@ class residue
|
||||
class monomer : public residue
|
||||
{
|
||||
public:
|
||||
monomer(const monomer &rhs) = delete;
|
||||
monomer &operator=(const monomer &rhs) = delete;
|
||||
|
||||
/// \brief Move constructor
|
||||
monomer(monomer &&rhs);
|
||||
|
||||
/// \brief Move assignment operator
|
||||
monomer &operator=(monomer &&rhs);
|
||||
|
||||
/// \brief constructor with actual values
|
||||
monomer(const polymer &polymer, std::size_t index, int seqID, const std::string &authSeqID,
|
||||
const std::string &pdbInsCode, const std::string &compoundID);
|
||||
|
||||
bool is_first_in_chain() const; ///< Return if this residue is the first residue in the chain
|
||||
bool is_last_in_chain() const; ///< Return if this residue is the last residue in the chain
|
||||
/// \brief Copy constructor
|
||||
monomer(const monomer &rhs) = default;
|
||||
|
||||
const monomer &prev() const; // Return previous monomer in polymer
|
||||
const monomer &next() const; // Return next monomer in polymer
|
||||
/// \brief Move constructor
|
||||
monomer(monomer &&rhs)
|
||||
{
|
||||
swap(*this, rhs);
|
||||
}
|
||||
monomer &operator=(monomer rhs)
|
||||
{
|
||||
swap(*this, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend void swap(monomer &a, monomer &b) noexcept
|
||||
{
|
||||
assert(a.m_polymer == b.m_polymer);
|
||||
std::swap(a.m_index, b.m_index);
|
||||
swap(static_cast<residue &>(a), static_cast<residue &>(b));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_first_in_chain() const; ///< Return if this residue is the first residue in the chain
|
||||
[[nodiscard]] bool is_last_in_chain() const; ///< Return if this residue is the last residue in the chain
|
||||
|
||||
[[nodiscard]] const monomer &prev() const; // Return previous monomer in polymer
|
||||
[[nodiscard]] const monomer &next() const; // Return next monomer in polymer
|
||||
|
||||
// convenience
|
||||
bool has_alpha() const; ///< Return if a alpha value can be calculated (depends on location in chain)
|
||||
bool has_kappa() const; ///< Return if a kappa value can be calculated (depends on location in chain)
|
||||
[[nodiscard]] bool has_alpha() const; ///< Return if a alpha value can be calculated (depends on location in chain)
|
||||
[[nodiscard]] bool has_kappa() const; ///< Return if a kappa value can be calculated (depends on location in chain)
|
||||
|
||||
// Assuming this is really an amino acid...
|
||||
|
||||
float phi() const; ///< Return the phi value for this residue
|
||||
float psi() const; ///< Return the psi value for this residue
|
||||
float alpha() const; ///< Return the alpha value for this residue
|
||||
float kappa() const; ///< Return the kappa value for this residue
|
||||
float tco() const; ///< Return the tco value for this residue
|
||||
float omega() const; ///< Return the omega value for this residue
|
||||
[[nodiscard]] float phi() const; ///< Return the phi value for this residue
|
||||
[[nodiscard]] float psi() const; ///< Return the psi value for this residue
|
||||
[[nodiscard]] float alpha() const; ///< Return the alpha value for this residue
|
||||
[[nodiscard]] float kappa() const; ///< Return the kappa value for this residue
|
||||
[[nodiscard]] float tco() const; ///< Return the tco value for this residue
|
||||
[[nodiscard]] float omega() const; ///< Return the omega value for this residue
|
||||
|
||||
// torsion angles
|
||||
std::size_t nr_of_chis() const; ///< Return how many torsion angles can be calculated
|
||||
float chi(std::size_t i) const; ///< Return torsion angle @a i
|
||||
[[nodiscard]] std::size_t nr_of_chis() const; ///< Return how many torsion angles can be calculated
|
||||
[[nodiscard]] float chi(std::size_t i) const; ///< Return torsion angle @a i
|
||||
|
||||
bool is_cis() const; ///< Return true if this residue is in a cis conformation
|
||||
[[nodiscard]] bool is_cis() const; ///< Return true if this residue is in a cis conformation
|
||||
|
||||
/// \brief Returns true if the four atoms C, CA, N and O are present
|
||||
bool is_complete() const;
|
||||
[[nodiscard]] bool is_complete() const;
|
||||
|
||||
/// \brief Returns true if any of the backbone atoms has an alternate
|
||||
bool has_alternate_backbone_atoms() const;
|
||||
[[nodiscard]] bool has_alternate_backbone_atoms() const;
|
||||
|
||||
atom CAlpha() const { return get_atom_by_atom_id("CA"); } ///< Return the CAlpha atom
|
||||
atom C() const { return get_atom_by_atom_id("C"); } ///< Return the C atom
|
||||
atom N() const { return get_atom_by_atom_id("N"); } ///< Return the N atom
|
||||
atom O() const { return get_atom_by_atom_id("O"); } ///< Return the O atom
|
||||
atom H() const { return get_atom_by_atom_id("H"); } ///< Return the H atom
|
||||
[[nodiscard]] atom CAlpha() const { return get_atom_by_atom_id("CA"); } ///< Return the CAlpha atom
|
||||
[[nodiscard]] atom C() const { return get_atom_by_atom_id("C"); } ///< Return the C atom
|
||||
[[nodiscard]] atom N() const { return get_atom_by_atom_id("N"); } ///< Return the N atom
|
||||
[[nodiscard]] atom O() const { return get_atom_by_atom_id("O"); } ///< Return the O atom
|
||||
[[nodiscard]] atom H() const { return get_atom_by_atom_id("H"); } ///< Return the H atom
|
||||
|
||||
/// \brief Return true if this monomer is bonded to monomer @a rhs
|
||||
bool is_bonded_to(const monomer &rhs) const
|
||||
[[nodiscard]] bool is_bonded_to(const monomer &rhs) const
|
||||
{
|
||||
return this != &rhs and are_bonded(*this, rhs);
|
||||
}
|
||||
@@ -680,7 +727,7 @@ class monomer : public residue
|
||||
static float omega(const monomer &a, const monomer &b);
|
||||
|
||||
/// \brief Return the chiral volume, only for LEU and VAL
|
||||
float chiral_volume() const;
|
||||
[[nodiscard]] float chiral_volume() const;
|
||||
|
||||
/// \brief Compare this monomer with \a rhs
|
||||
bool operator==(const monomer &rhs) const
|
||||
@@ -692,7 +739,7 @@ class monomer : public residue
|
||||
|
||||
private:
|
||||
const polymer *m_polymer;
|
||||
std::size_t m_index;
|
||||
std::size_t m_index{};
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -705,16 +752,16 @@ class polymer : public std::vector<monomer>
|
||||
{
|
||||
public:
|
||||
/// \brief Constructor
|
||||
polymer(structure &s, const std::string &entityID, const std::string &asymID, const std::string &auth_asym_id);
|
||||
polymer(structure &s, std::string entityID, std::string asymID, std::string auth_asym_id);
|
||||
|
||||
polymer(const polymer &) = delete;
|
||||
polymer &operator=(const polymer &) = delete;
|
||||
|
||||
structure *get_structure() const { return m_structure; } ///< Return the structure
|
||||
[[nodiscard]] structure *get_structure() const { return m_structure; } ///< Return the structure
|
||||
|
||||
std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
std::string get_pdb_strand_id() const { return m_pdb_strand_id; } ///< Return the PDB chain ID, actually
|
||||
std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
|
||||
[[nodiscard]] std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
[[nodiscard]] std::string get_pdb_strand_id() const { return m_pdb_strand_id; } ///< Return the PDB chain ID, actually
|
||||
[[nodiscard]] std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
|
||||
|
||||
private:
|
||||
structure *m_structure;
|
||||
@@ -741,8 +788,26 @@ class sugar : public residue
|
||||
const std::string &asymID, int authSeqID);
|
||||
|
||||
/** @cond */
|
||||
sugar(sugar &&rhs);
|
||||
sugar &operator=(sugar &&rhs);
|
||||
sugar(const sugar &rhs) = default;
|
||||
|
||||
sugar(sugar &&rhs)
|
||||
{
|
||||
swap(*this, rhs);
|
||||
}
|
||||
|
||||
sugar &operator=(sugar rhs)
|
||||
{
|
||||
swap(*this, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend void swap(sugar &a, sugar &b) noexcept
|
||||
{
|
||||
assert(a.m_branch == b.m_branch);
|
||||
std::swap(a.m_link, b.m_link);
|
||||
swap(static_cast<residue &>(a), static_cast<residue &>(b));
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
@@ -755,26 +820,26 @@ class sugar : public residue
|
||||
*
|
||||
* @return The sugar number
|
||||
*/
|
||||
int num() const
|
||||
[[nodiscard]] int num() const
|
||||
{
|
||||
int result;
|
||||
auto r = std::from_chars(m_pdb_seq_num.data(), m_pdb_seq_num.data() + m_pdb_seq_num.length(), result);
|
||||
if ((bool)r.ec)
|
||||
if (r.ec != std::errc{})
|
||||
throw std::runtime_error("The auth_seq_id should be a number for a sugar");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief Return the name of this sugar
|
||||
std::string name() const;
|
||||
[[nodiscard]] std::string name() const;
|
||||
|
||||
/// \brief Return the atom the C1 is linked to
|
||||
atom get_link() const { return m_link; }
|
||||
[[nodiscard]] atom get_link() const { return m_link; }
|
||||
|
||||
/// \brief Set the link atom C1 is linked to to @a link
|
||||
void set_link(atom link) { m_link = link; }
|
||||
|
||||
/// \brief Return the sugar number of the sugar linked to C1
|
||||
std::size_t get_link_nr() const
|
||||
[[nodiscard]] std::size_t get_link_nr() const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
if (m_link)
|
||||
@@ -802,7 +867,7 @@ class branch : public std::vector<sugar>
|
||||
{
|
||||
public:
|
||||
/// \brief constructor
|
||||
branch(structure &structure, const std::string &asym_id, const std::string &entity_id);
|
||||
branch(structure &structure, std::string asym_id, std::string entity_id);
|
||||
|
||||
branch(const branch &) = delete;
|
||||
branch &operator=(const branch &) = delete;
|
||||
@@ -816,22 +881,22 @@ class branch : public std::vector<sugar>
|
||||
void link_atoms();
|
||||
|
||||
/// \brief Return the name of the branch
|
||||
std::string name() const;
|
||||
[[nodiscard]] std::string name() const;
|
||||
|
||||
/// \brief Return the weight of the branch based on the formulae of the sugars
|
||||
float weight() const;
|
||||
[[nodiscard]] float weight() const;
|
||||
|
||||
std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
|
||||
[[nodiscard]] std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
|
||||
[[nodiscard]] std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
|
||||
|
||||
structure &get_structure() { return *m_structure; } ///< Return the structure
|
||||
structure &get_structure() const { return *m_structure; } ///< Return the structure
|
||||
[[nodiscard]] structure &get_structure() { return *m_structure; } ///< Return the structure
|
||||
[[nodiscard]] structure &get_structure() const { return *m_structure; } ///< Return the structure
|
||||
|
||||
/// \brief Return a reference to the sugar with number @a num
|
||||
sugar &get_sugar_by_num(int nr);
|
||||
[[nodiscard]] sugar &get_sugar_by_num(int nr);
|
||||
|
||||
/// \brief Return a const reference to the sugar with number @a num
|
||||
const sugar &get_sugar_by_num(int nr) const
|
||||
[[nodiscard]] const sugar &get_sugar_by_num(int nr) const
|
||||
{
|
||||
return const_cast<branch *>(this)->get_sugar_by_num(nr);
|
||||
}
|
||||
@@ -851,7 +916,7 @@ class branch : public std::vector<sugar>
|
||||
private:
|
||||
friend sugar;
|
||||
|
||||
std::string name(const sugar &s) const;
|
||||
[[nodiscard]] std::string name(const sugar &s) const;
|
||||
|
||||
structure *m_structure;
|
||||
std::string m_asym_id, m_entity_id;
|
||||
@@ -918,85 +983,85 @@ class structure
|
||||
~structure() = default;
|
||||
|
||||
/// \brief Return the model number
|
||||
std::size_t get_model_nr() const { return m_model_nr; }
|
||||
[[nodiscard]] std::size_t get_model_nr() const { return m_model_nr; }
|
||||
|
||||
/// \brief Return a list of all the atoms in this structure
|
||||
const std::vector<atom> &atoms() const { return m_atoms; }
|
||||
[[nodiscard]] const std::vector<atom> &atoms() const { return m_atoms; }
|
||||
|
||||
EntityType get_entity_type_for_entity_id(const std::string entityID) const; ///< Return the entity type for the entity with id @a entity_id
|
||||
EntityType get_entity_type_for_asym_id(const std::string asymID) const; ///< Return the entity type for the asym with id @a asym_id
|
||||
[[nodiscard]] EntityType get_entity_type_for_entity_id(const std::string entityID) const; ///< Return the entity type for the entity with id @a entity_id
|
||||
[[nodiscard]] EntityType get_entity_type_for_asym_id(const std::string asymID) const; ///< Return the entity type for the asym with id @a asym_id
|
||||
|
||||
const std::list<polymer> &polymers() const { return m_polymers; } ///< Return the list of polymers
|
||||
std::list<polymer> &polymers() { return m_polymers; } ///< Return the list of polymers
|
||||
[[nodiscard]] const std::list<polymer> &polymers() const { return m_polymers; } ///< Return the list of polymers
|
||||
[[nodiscard]] std::list<polymer> &polymers() { return m_polymers; } ///< Return the list of polymers
|
||||
|
||||
polymer &get_polymer_by_asym_id(const std::string &asymID); ///< Return the polymer having asym ID @a asymID
|
||||
const polymer &get_polymer_by_asym_id(const std::string &asymID) const ///< Return the polymer having asym ID @a asymID
|
||||
[[nodiscard]] polymer &get_polymer_by_asym_id(const std::string &asymID); ///< Return the polymer having asym ID @a asymID
|
||||
[[nodiscard]] const polymer &get_polymer_by_asym_id(const std::string &asymID) const ///< Return the polymer having asym ID @a asymID
|
||||
{
|
||||
return const_cast<structure *>(this)->get_polymer_by_asym_id(asymID);
|
||||
}
|
||||
|
||||
const std::list<branch> &branches() const { return m_branches; } ///< Return the list of all branches
|
||||
std::list<branch> &branches() { return m_branches; } ///< Return the list of all branches
|
||||
[[nodiscard]] const std::list<branch> &branches() const { return m_branches; } ///< Return the list of all branches
|
||||
[[nodiscard]] std::list<branch> &branches() { return m_branches; } ///< Return the list of all branches
|
||||
|
||||
branch &get_branch_by_asym_id(const std::string &asymID); ///< Return the branch having asym ID @a asymID
|
||||
const branch &get_branch_by_asym_id(const std::string &asymID) const; ///< Return the branch having asym ID @a asymID
|
||||
[[nodiscard]] branch &get_branch_by_asym_id(const std::string &asymID); ///< Return the branch having asym ID @a asymID
|
||||
[[nodiscard]] const branch &get_branch_by_asym_id(const std::string &asymID) const; ///< Return the branch having asym ID @a asymID
|
||||
|
||||
const std::vector<residue> &non_polymers() const { return m_non_polymers; } ///< Return the list of non-polymers, actually the list of ligands
|
||||
[[nodiscard]] const std::vector<residue> &non_polymers() const { return m_non_polymers; } ///< Return the list of non-polymers, actually the list of ligands
|
||||
|
||||
bool has_atom_id(const std::string &id) const; ///< Return true if an atom with ID @a id exists in this structure
|
||||
atom get_atom_by_id(const std::string &id) const; ///< Return the atom with ID @a id
|
||||
[[nodiscard]] bool has_atom_id(const std::string &id) const; ///< Return true if an atom with ID @a id exists in this structure
|
||||
[[nodiscard]] atom get_atom_by_id(const std::string &id) const; ///< Return the atom with ID @a id
|
||||
|
||||
/// \brief Return the atom identified by the label_ values specified
|
||||
atom get_atom_by_label(const std::string &atomID, const std::string &asymID,
|
||||
[[nodiscard]] atom get_atom_by_label(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 get_atom_by_position(point p) const;
|
||||
[[nodiscard]] atom get_atom_by_position(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 get_atom_by_position_and_type(point p, std::string_view type, std::string_view res_type) const;
|
||||
[[nodiscard]] atom get_atom_by_position_and_type(point p, std::string_view type, std::string_view res_type) const;
|
||||
|
||||
/// \brief Create a non-poly residue based on atoms already present in this structure.
|
||||
residue &create_residue(const std::vector<atom> &atoms);
|
||||
|
||||
/// \brief Get a non-poly residue for an asym with id \a asymID
|
||||
residue &get_residue(const std::string &asymID)
|
||||
[[nodiscard]] residue &get_residue(const std::string &asymID)
|
||||
{
|
||||
return get_residue(asymID, 0, "");
|
||||
}
|
||||
|
||||
/// \brief Get a non-poly residue for an asym with id \a asymID
|
||||
const residue &get_residue(const std::string &asymID) const
|
||||
[[nodiscard]] const residue &get_residue(const std::string &asymID) const
|
||||
{
|
||||
return get_residue(asymID, 0, "");
|
||||
}
|
||||
|
||||
/// \brief Get a residue for an asym with id \a asymID seq id \a seqID and authSeqID \a authSeqID
|
||||
residue &get_residue(const std::string &asymID, int seqID, const std::string &authSeqID);
|
||||
[[nodiscard]] residue &get_residue(const std::string &asymID, int seqID, const std::string &authSeqID);
|
||||
|
||||
/// \brief Get a the single residue for an asym with id \a asymID seq id \a seqID and authSeqID \a authSeqID
|
||||
const residue &get_residue(const std::string &asymID, int seqID, const std::string &authSeqID) const
|
||||
[[nodiscard]] const residue &get_residue(const std::string &asymID, int seqID, const std::string &authSeqID) const
|
||||
{
|
||||
return const_cast<structure *>(this)->get_residue(asymID, seqID, authSeqID);
|
||||
}
|
||||
|
||||
/// \brief Get a residue for an asym with id \a asymID, compound id \a compID, seq id \a seqID and authSeqID \a authSeqID
|
||||
residue &get_residue(const std::string &asymID, const std::string &compID, int seqID, const std::string &authSeqID);
|
||||
[[nodiscard]] residue &get_residue(const std::string &asymID, const std::string &compID, int seqID, const std::string &authSeqID);
|
||||
|
||||
/// \brief Get a residue for an asym with id \a asymID, compound id \a compID, seq id \a seqID and authSeqID \a authSeqID
|
||||
const residue &get_residue(const std::string &asymID, const std::string &compID, int seqID, const std::string &authSeqID) const
|
||||
[[nodiscard]] const residue &get_residue(const std::string &asymID, const std::string &compID, int seqID, const std::string &authSeqID) const
|
||||
{
|
||||
return const_cast<structure *>(this)->get_residue(asymID, compID, seqID, authSeqID);
|
||||
}
|
||||
|
||||
/// \brief Get a the residue for atom \a atom
|
||||
residue &get_residue(const atom &atom)
|
||||
[[nodiscard]] residue &get_residue(const atom &atom)
|
||||
{
|
||||
return get_residue(atom.get_label_asym_id(), atom.get_label_comp_id(), atom.get_label_seq_id(), atom.get_auth_seq_id());
|
||||
}
|
||||
|
||||
/// \brief Get a the residue for atom \a atom
|
||||
const residue &get_residue(const atom &atom) const
|
||||
[[nodiscard]] const residue &get_residue(const atom &atom) const
|
||||
{
|
||||
return get_residue(atom.get_label_asym_id(), atom.get_label_comp_id(), atom.get_label_seq_id(), atom.get_auth_seq_id());
|
||||
}
|
||||
@@ -1059,12 +1124,30 @@ class structure
|
||||
/// \return The newly create asym ID
|
||||
std::string create_non_poly(const std::string &entity_id, std::vector<row_initializer> atoms);
|
||||
|
||||
/// \brief Create a new NonPolymer struct_asym for a compound of type \a compound_id, returns asym_id.
|
||||
/// This method creates new atom records filled with info from the CCD compound info.
|
||||
///
|
||||
/// \param compound_id The compound ID of the new nonpoly
|
||||
/// \param skip_hydrogen Do not create hydrogen atoms when true
|
||||
/// \return The newly create asym ID
|
||||
std::string create_non_poly(const std::string &compound_id, bool skip_hydrogen);
|
||||
|
||||
/// \brief Create a new water with atom constructed from info in \a atom_info
|
||||
/// This method creates a new atom record filled with info from the info.
|
||||
///
|
||||
/// \param atom The set of item data containing the data for the atoms.
|
||||
void create_water(row_initializer atom);
|
||||
|
||||
/// \brief Create a link, a struct_conn record for two atoms.
|
||||
///
|
||||
/// \param a1 Atom 1
|
||||
/// \param a2 Atom 2
|
||||
/// \param link_type The struct_conn_type ID for the link
|
||||
/// \param role The pdbx_role field value
|
||||
/// \return The ID of the struct_conn record created
|
||||
|
||||
std::string create_link(atom a1, atom a2, const std::string &link_type, const std::string &role);
|
||||
|
||||
/// \brief Create a new and empty (sugar) branch
|
||||
branch &create_branch();
|
||||
|
||||
@@ -1104,13 +1187,13 @@ class structure
|
||||
void cleanup_empty_categories();
|
||||
|
||||
/// \brief Direct access to underlying data
|
||||
category &get_category(std::string_view name) const
|
||||
[[nodiscard]] category &get_category(std::string_view name) const
|
||||
{
|
||||
return m_db[name];
|
||||
}
|
||||
|
||||
/// \brief Direct access to underlying data
|
||||
datablock &get_datablock() const
|
||||
[[nodiscard]] datablock &get_datablock() const
|
||||
{
|
||||
return m_db;
|
||||
}
|
||||
@@ -1119,14 +1202,13 @@ class structure
|
||||
void validate_atoms() const;
|
||||
|
||||
/// \brief emplace a newly created atom using @a args
|
||||
template <typename... Args>
|
||||
atom &emplace_atom(Args &...args)
|
||||
atom &emplace_atom(datablock &db, const_row_handle rh)
|
||||
{
|
||||
return emplace_atom(atom{ std::forward<Args>(args)... });
|
||||
return emplace_atom(atom{ db, rh });
|
||||
}
|
||||
|
||||
/// \brief emplace the moved atom @a atom
|
||||
atom &emplace_atom(atom &&atom);
|
||||
atom &emplace_atom(atom atom);
|
||||
|
||||
/// \brief Reorder atom_site atoms based on 'natural' ordering
|
||||
void reorder_atoms();
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
@@ -164,7 +167,13 @@ class sac_parser
|
||||
SAVE_NAME,
|
||||
STOP,
|
||||
ITEM_NAME,
|
||||
VALUE
|
||||
|
||||
VALUE_INAPPLICABLE,
|
||||
VALUE_UNKNOWN,
|
||||
VALUE_NUMERIC_INTEGER,
|
||||
VALUE_NUMERIC_FLOAT,
|
||||
VALUE_CHARSTRING,
|
||||
VALUE_TEXTFIELD
|
||||
};
|
||||
|
||||
static constexpr const char *get_token_name(CIFToken token)
|
||||
@@ -180,7 +189,15 @@ class sac_parser
|
||||
case CIFToken::SAVE_NAME: return "SAVE+name";
|
||||
case CIFToken::STOP: return "STOP";
|
||||
case CIFToken::ITEM_NAME: return "Tag";
|
||||
case CIFToken::VALUE: return "Value";
|
||||
// case CIFToken::VALUE: return "Value";
|
||||
|
||||
case CIFToken::VALUE_INAPPLICABLE: return "Inapplicable value";
|
||||
case CIFToken::VALUE_UNKNOWN: return "'Unknown' value (=null)";
|
||||
case CIFToken::VALUE_NUMERIC_INTEGER: return "Integer value";
|
||||
case CIFToken::VALUE_NUMERIC_FLOAT: return "Float value";
|
||||
case CIFToken::VALUE_CHARSTRING: return "Charstring value";
|
||||
case CIFToken::VALUE_TEXTFIELD: return "Textfield value";
|
||||
|
||||
default: return "Invalid token parameter";
|
||||
}
|
||||
}
|
||||
@@ -245,7 +262,7 @@ class sac_parser
|
||||
|
||||
void error(const std::string &msg)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "Error parsing mmCIF: " << msg << '\n';
|
||||
|
||||
throw parse_error(m_line_nr, msg);
|
||||
@@ -253,7 +270,7 @@ class sac_parser
|
||||
|
||||
void warning(const std::string &msg)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "parser warning at line " << m_line_nr << ": " << msg << '\n';
|
||||
}
|
||||
|
||||
@@ -262,7 +279,7 @@ class sac_parser
|
||||
virtual void produce_datablock(std::string_view name) = 0;
|
||||
virtual void produce_category(std::string_view name) = 0;
|
||||
virtual void produce_row() = 0;
|
||||
virtual void produce_item(std::string_view category, std::string_view item, std::string_view value) = 0;
|
||||
virtual void produce_item(std::string_view category, std::string_view item, item_value value) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -281,7 +298,17 @@ class sac_parser
|
||||
TextItem,
|
||||
TextItemNL,
|
||||
Reserved,
|
||||
Value
|
||||
Value,
|
||||
|
||||
TextItemBS,
|
||||
TextItemBS2,
|
||||
TextItemBSNL,
|
||||
|
||||
Numeric_Zero,
|
||||
Numeric_Integer,
|
||||
Numeric_Float,
|
||||
Numeric_Exponent1,
|
||||
Numeric_Exponent2
|
||||
};
|
||||
|
||||
std::streambuf &m_source;
|
||||
@@ -289,11 +316,15 @@ class sac_parser
|
||||
// Parser state
|
||||
uint32_t m_line_nr;
|
||||
bool m_bol;
|
||||
bool m_backslash_strings = false;
|
||||
CIFToken m_lookahead;
|
||||
|
||||
// token buffer
|
||||
std::vector<char> m_token_buffer;
|
||||
std::string_view m_token_value;
|
||||
int64_t m_token_value_int;
|
||||
double m_token_value_float;
|
||||
int m_float_precision;
|
||||
|
||||
/** @endcond */
|
||||
};
|
||||
@@ -331,7 +362,7 @@ class parser : public sac_parser
|
||||
|
||||
void produce_row() override;
|
||||
|
||||
void produce_item(std::string_view category, std::string_view item, std::string_view value) override;
|
||||
void produce_item(std::string_view category, std::string_view item, item_value value) override;
|
||||
|
||||
protected:
|
||||
file &m_file;
|
||||
|
||||
@@ -149,6 +149,23 @@ void fixup_pdbx(file &pdbx_file, const validator &v);
|
||||
|
||||
bool reconstruct_pdbx(file &pdbx_file, const validator &v);
|
||||
|
||||
/** \brief This is an extension to cif::validator, use the logic in common
|
||||
* PDBx files to see if the file is internally consistent.
|
||||
*
|
||||
* This function for now checks if the following categories are consistent:
|
||||
*
|
||||
* atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity
|
||||
*
|
||||
* Use the common \ref cif::VERBOSE flag to turn on diagnostic messages.
|
||||
*
|
||||
* This function throws a std::system_error in case of an error
|
||||
*
|
||||
* \param pdbx_file The input file
|
||||
* \result Returns true if the file was valid and consistent
|
||||
*/
|
||||
|
||||
bool is_valid_pdbx_file(const file &pdbx_file);
|
||||
|
||||
/** \brief This is an extension to cif::validator, use the logic in common
|
||||
* PDBx files to see if the file is internally consistent.
|
||||
*
|
||||
@@ -165,8 +182,7 @@ bool reconstruct_pdbx(file &pdbx_file, const validator &v);
|
||||
* \result Returns true if the file was valid and consistent
|
||||
*/
|
||||
|
||||
bool is_valid_pdbx_file(const file &pdbx_file,
|
||||
const validator &v = validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
bool is_valid_pdbx_file(const file &pdbx_file, const validator &v);
|
||||
|
||||
/** \brief This is an extension to cif::validator, use the logic in common
|
||||
* PDBx files to see if the file is internally consistent.
|
||||
|
||||
@@ -30,15 +30,18 @@
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
#include <valarray>
|
||||
|
||||
#if __has_include(<clipper/core/coords.h>)
|
||||
#define HAVE_LIBCLIPPER 1
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#include <clipper/core/coords.h>
|
||||
#pragma GCC diagnostic pop
|
||||
# define HAVE_LIBCLIPPER 1
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
# include <clipper/core/coords.h>
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/** \file point.hpp
|
||||
@@ -54,7 +57,7 @@ namespace cif
|
||||
|
||||
/// \brief Our value for Pi
|
||||
const double
|
||||
kPI = 3.141592653589793238462643383279502884;
|
||||
kPI = std::numbers::pi;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
@@ -117,13 +120,13 @@ class quaternion_type
|
||||
// accessors
|
||||
|
||||
/// \brief See class description, return the *real* part of the quaternion
|
||||
constexpr value_type real() const
|
||||
[[nodiscard]] constexpr value_type real() const
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
/// \brief See class description, return the *unreal* part of the quaternion
|
||||
constexpr quaternion_type unreal() const
|
||||
[[nodiscard]] constexpr quaternion_type unreal() const
|
||||
{
|
||||
return { 0, b, c, d };
|
||||
}
|
||||
@@ -152,15 +155,7 @@ class quaternion_type
|
||||
}
|
||||
|
||||
/// \brief Assignment operator
|
||||
constexpr quaternion_type &operator=(quaternion_type const &rhs)
|
||||
{
|
||||
a = rhs.a;
|
||||
b = rhs.b;
|
||||
c = rhs.c;
|
||||
d = rhs.d;
|
||||
|
||||
return *this;
|
||||
}
|
||||
constexpr quaternion_type &operator=(quaternion_type const &rhs) = default;
|
||||
|
||||
/// \brief Assignment operator that sets the *real* part to @a rhs and the *unreal* parts to zero
|
||||
constexpr quaternion_type &operator=(value_type const &rhs)
|
||||
@@ -273,10 +268,10 @@ class quaternion_type
|
||||
template <typename X>
|
||||
constexpr quaternion_type &operator*=(quaternion_type<X> const &rhs)
|
||||
{
|
||||
value_type ar = static_cast<value_type>(rhs.a);
|
||||
value_type br = static_cast<value_type>(rhs.b);
|
||||
value_type cr = static_cast<value_type>(rhs.c);
|
||||
value_type dr = static_cast<value_type>(rhs.d);
|
||||
auto ar = static_cast<value_type>(rhs.a);
|
||||
auto br = static_cast<value_type>(rhs.b);
|
||||
auto cr = static_cast<value_type>(rhs.c);
|
||||
auto dr = static_cast<value_type>(rhs.d);
|
||||
|
||||
quaternion_type result(a * ar - b * br - c * cr - d * dr, a * br + b * ar + c * dr - d * cr, a * cr - b * dr + c * ar + d * br, a * dr + b * cr - c * br + d * ar);
|
||||
swap(result);
|
||||
@@ -308,10 +303,10 @@ class quaternion_type
|
||||
template <typename X>
|
||||
constexpr quaternion_type &operator/=(quaternion_type<X> const &rhs)
|
||||
{
|
||||
value_type ar = static_cast<value_type>(rhs.a);
|
||||
value_type br = static_cast<value_type>(rhs.b);
|
||||
value_type cr = static_cast<value_type>(rhs.c);
|
||||
value_type dr = static_cast<value_type>(rhs.d);
|
||||
auto ar = static_cast<value_type>(rhs.a);
|
||||
auto br = static_cast<value_type>(rhs.b);
|
||||
auto cr = static_cast<value_type>(rhs.c);
|
||||
auto dr = static_cast<value_type>(rhs.d);
|
||||
|
||||
value_type denominator = ar * ar + br * br + cr * cr + dr * dr;
|
||||
quaternion_type result((+a * ar + b * br + c * cr + d * dr) / denominator, (-a * br + b * ar - c * dr + d * cr) / denominator, (-a * cr + b * dr + c * ar - d * br) / denominator, (-a * dr - b * cr + c * br + d * ar) / denominator);
|
||||
@@ -347,10 +342,10 @@ class quaternion_type
|
||||
return quaternion_type{ +q.a, -q.b, -q.c, -q.d };
|
||||
}
|
||||
|
||||
constexpr value_type get_a() const { return a; } ///< Return part a
|
||||
constexpr value_type get_b() const { return b; } ///< Return part b
|
||||
constexpr value_type get_c() const { return c; } ///< Return part c
|
||||
constexpr value_type get_d() const { return d; } ///< Return part d
|
||||
[[nodiscard]] constexpr value_type get_a() const { return a; } ///< Return part a
|
||||
[[nodiscard]] constexpr value_type get_b() const { return b; } ///< Return part b
|
||||
[[nodiscard]] constexpr value_type get_c() const { return c; } ///< Return part c
|
||||
[[nodiscard]] constexpr value_type get_d() const { return d; } ///< Return part d
|
||||
|
||||
/// \brief compare with @a rhs
|
||||
constexpr bool operator==(const quaternion_type &rhs) const
|
||||
@@ -365,11 +360,18 @@ class quaternion_type
|
||||
}
|
||||
|
||||
/// \brief test for all zero values
|
||||
constexpr operator bool() const
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return a != 0 or b != 0 or c != 0 or d != 0;
|
||||
}
|
||||
|
||||
/// \brief for debugging e.g.
|
||||
friend std::ostream &operator<<(std::ostream &os, const quaternion_type &rhs)
|
||||
{
|
||||
os << std::format("{{ a: {}, b: {}, c: {}, d: {} }}", rhs.a, rhs.b, rhs.c, rhs.d);
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type a, b, c, d;
|
||||
};
|
||||
@@ -491,17 +493,17 @@ struct point_type
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr value_type &get_x() { return m_x; } ///< Get a reference to x
|
||||
constexpr value_type get_x() const { return m_x; } ///< Get the value of x
|
||||
constexpr void set_x(value_type x) { m_x = x; } ///< Set the value of x to @a x
|
||||
[[nodiscard]] constexpr value_type &get_x() { return m_x; } ///< Get a reference to x
|
||||
[[nodiscard]] constexpr value_type get_x() const { return m_x; } ///< Get the value of x
|
||||
constexpr void set_x(value_type x) { m_x = x; } ///< Set the value of x to @a x
|
||||
|
||||
constexpr value_type &get_y() { return m_y; } ///< Get a reference to y
|
||||
constexpr value_type get_y() const { return m_y; } ///< Get the value of y
|
||||
constexpr void set_y(value_type y) { m_y = y; } ///< Set the value of y to @a y
|
||||
[[nodiscard]] constexpr value_type &get_y() { return m_y; } ///< Get a reference to y
|
||||
[[nodiscard]] constexpr value_type get_y() const { return m_y; } ///< Get the value of y
|
||||
constexpr void set_y(value_type y) { m_y = y; } ///< Set the value of y to @a y
|
||||
|
||||
constexpr value_type &get_z() { return m_z; } ///< Get a reference to z
|
||||
constexpr value_type get_z() const { return m_z; } ///< Get the value of z
|
||||
constexpr void set_z(value_type z) { m_z = z; } ///< Set the value of z to @a z
|
||||
[[nodiscard]] constexpr value_type &get_z() { return m_z; } ///< Get a reference to z
|
||||
[[nodiscard]] constexpr value_type get_z() const { return m_z; } ///< Get the value of z
|
||||
constexpr void set_z(value_type z) { m_z = z; } ///< Set the value of z to @a z
|
||||
|
||||
/// \brief add @a rhs
|
||||
constexpr point_type &operator+=(const point_type &rhs)
|
||||
@@ -682,13 +684,13 @@ struct point_type
|
||||
// consider point as a vector... perhaps I should rename point?
|
||||
|
||||
/// \brief looking at the point as if it is a vector, return the squared length
|
||||
constexpr value_type length_sq() const
|
||||
[[nodiscard]] constexpr value_type length_sq() const
|
||||
{
|
||||
return m_x * m_x + m_y * m_y + m_z * m_z;
|
||||
}
|
||||
|
||||
/// \brief looking at the point as if it is a vector, return the length
|
||||
constexpr value_type length() const
|
||||
[[nodiscard]] constexpr value_type length() const
|
||||
{
|
||||
return std::sqrt(length_sq());
|
||||
}
|
||||
@@ -743,6 +745,55 @@ inline constexpr auto cross_product(const point_type<F1> &a, const point_type<F2
|
||||
a.m_x * b.m_y - b.m_x * a.m_y);
|
||||
}
|
||||
|
||||
/// \brief return the squared norm of point @a p
|
||||
template <typename F>
|
||||
constexpr F norm_squared(const point_type<F> &p)
|
||||
{
|
||||
return p.m_x * p.m_x + p.m_y * p.m_y + p.m_z * p.m_z;
|
||||
}
|
||||
|
||||
/// \brief return the norm of point @a p
|
||||
template <typename F>
|
||||
constexpr point_type<F> norm(const point_type<F> &p)
|
||||
{
|
||||
return std::sqrt(norm_squared(p));
|
||||
}
|
||||
|
||||
/// \brief return the point where two lines intersect, or an empty value if they don't intersect at all
|
||||
template <typename F>
|
||||
std::optional<cif::point> line_line_intersection(const point_type<F> &p1,
|
||||
const point_type<F> &p2, const point_type<F> &p3, const point_type<F> &p4)
|
||||
{
|
||||
auto p13 = p1 - p3;
|
||||
auto p43 = p4 - p3;
|
||||
if (std::abs(p43.m_x) < std::numeric_limits<F>::epsilon() and std::abs(p43.m_y) < std::numeric_limits<F>::epsilon() and std::abs(p43.m_z) < std::numeric_limits<F>::epsilon())
|
||||
return {};
|
||||
|
||||
auto p21 = p2 - p1;
|
||||
if (std::abs(p21.m_x) < std::numeric_limits<F>::epsilon() and std::abs(p21.m_y) < std::numeric_limits<F>::epsilon() and std::abs(p21.m_z) < std::numeric_limits<F>::epsilon())
|
||||
return {};
|
||||
|
||||
auto d1343 = cif::dot_product(p43, p13);
|
||||
auto d4321 = cif::dot_product(p43, p21);
|
||||
auto d1321 = cif::dot_product(p13, p21);
|
||||
auto d4343 = cif::dot_product(p43, p43);
|
||||
auto d2121 = cif::dot_product(p21, p21);
|
||||
|
||||
auto denom = d2121 * d4343 - d4321 * d4321;
|
||||
if (std::abs(denom) < std::numeric_limits<F>::epsilon())
|
||||
return {};
|
||||
|
||||
auto numer = d1343 * d4321 - d1321 * d4343;
|
||||
|
||||
auto mua = numer / denom;
|
||||
auto mub = (d1343 + d4321 * mua) / d4343;
|
||||
|
||||
auto pa = p1 + mua * p21;
|
||||
auto pb = p3 + mub * p43;
|
||||
|
||||
return { (pa + pb) / 2 };
|
||||
}
|
||||
|
||||
/// \brief return the angle in degrees between the vectors from point @a p2 to @a p1 and @a p2 to @a p3
|
||||
template <typename F>
|
||||
constexpr auto angle(const point_type<F> &p1, const point_type<F> &p2, const point_type<F> &p3)
|
||||
@@ -806,6 +857,9 @@ constexpr auto distance_point_to_line(const point_type<F> &l1, const point_type<
|
||||
return cross.length() / line.length();
|
||||
}
|
||||
|
||||
/// \brief return the smallest sphere around the points in @a pts
|
||||
std::tuple<point, float> smallest_sphere_around_points(std::vector<point> pts);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* @brief For e.g. simulated annealing, returns a new point that is moved in
|
||||
@@ -878,7 +932,7 @@ class spherical_dots
|
||||
}
|
||||
|
||||
/// \brief The number of points
|
||||
std::size_t size() const { return P; }
|
||||
[[nodiscard]] std::size_t size() const { return P; }
|
||||
|
||||
/// \brief Access a point by index
|
||||
const point operator[](uint32_t inIx) const { return m_points[inIx]; }
|
||||
@@ -890,12 +944,12 @@ class spherical_dots
|
||||
iterator end() const { return m_points.end(); }
|
||||
|
||||
/// \brief return the *weight*,
|
||||
double weight() const { return W; }
|
||||
[[nodiscard]] double weight() const { return W; }
|
||||
|
||||
spherical_dots()
|
||||
{
|
||||
const double
|
||||
kGoldenRatio = (1 + std::sqrt(5.0)) / 2;
|
||||
kGoldenRatio = std::numbers::phi;
|
||||
|
||||
auto p = m_points.begin();
|
||||
|
||||
|
||||
@@ -29,55 +29,434 @@
|
||||
#include "cif++/item.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @file row.hpp
|
||||
*
|
||||
*
|
||||
* The class cif::row should be an opaque type. It is used to store the
|
||||
* internal data per row in a category. You should use cif::row_handle
|
||||
* to get access to the contents in a row.
|
||||
*
|
||||
*
|
||||
* One could think of rows as vectors of cif::item. But internally
|
||||
* that's not the case.
|
||||
*
|
||||
*
|
||||
* You can access the values of stored items by name or index.
|
||||
* The return value of operator[] is an cif::item_handle object.
|
||||
*
|
||||
* The return value of operator[] is a reference to a cif::item_value object.
|
||||
*
|
||||
* @code {.cpp}
|
||||
* cif::category &atom_site = my_db["atom_site"];
|
||||
* cif::row_handle rh = atom_site.front();
|
||||
*
|
||||
*
|
||||
* // by name:
|
||||
* std::string name = rh["label_atom_id"].as<std::string>();
|
||||
*
|
||||
*
|
||||
* // by index:
|
||||
* uint16_t ix = atom_site.get_item_ix("label_atom_id");
|
||||
* assert(rh[ix].as<std::string() == name);
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* There some template magic here to allow easy extracting of data
|
||||
* from rows. This can be done using cif::tie e.g.:
|
||||
*
|
||||
*
|
||||
* @code {.cpp}
|
||||
* std::string name;
|
||||
* float x, y, z;
|
||||
*
|
||||
*
|
||||
* cif::tie(name, x, y, z) = rh.get("label_atom_id", "cartn_x", "cartn_y", "cartn_z");
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* However, a more modern way uses structured binding:
|
||||
*
|
||||
*
|
||||
* @code {.cpp}
|
||||
* const auto &[name, x, y, z] = rh.get<std::string,float,float,float>("label_atom_id", "cartn_x", "cartn_y", "cartn_z");
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
class category;
|
||||
class row_handle;
|
||||
class const_row_handle;
|
||||
|
||||
namespace cql
|
||||
{
|
||||
struct connection_impl;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename... C>
|
||||
struct get_row_result;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief the row class, this one is not directly accessible from the outside
|
||||
|
||||
class row : public std::vector<item_value>
|
||||
{
|
||||
public:
|
||||
row() = default;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return the item_value pointer for item at index @a ix
|
||||
*/
|
||||
item_value *get(uint16_t ix)
|
||||
{
|
||||
if (ix >= size())
|
||||
resize(ix + 1);
|
||||
return &data()[ix];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the const item_value pointer for item at index @a ix
|
||||
*/
|
||||
[[nodiscard]] const item_value *get(uint16_t ix) const
|
||||
{
|
||||
return ix < size() ? &data()[ix] : nullptr;
|
||||
}
|
||||
|
||||
void set(uint16_t ix, item_value v)
|
||||
{
|
||||
if (ix >= size())
|
||||
resize(ix + 1);
|
||||
operator[](ix) = std::move(v);
|
||||
}
|
||||
|
||||
friend class category;
|
||||
friend class category_index;
|
||||
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
row *m_next = nullptr;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief row_handle is the way to access data stored in rows
|
||||
|
||||
class row_handle
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
template <bool>
|
||||
friend struct item_handle_base;
|
||||
friend class category;
|
||||
friend class category_index;
|
||||
friend class row_initializer;
|
||||
friend class const_row_handle;
|
||||
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
row_handle() = default;
|
||||
virtual ~row_handle() = default;
|
||||
|
||||
row_handle(const row_handle &) = default;
|
||||
row_handle(row_handle &&) = default;
|
||||
row_handle &operator=(const row_handle &) = default;
|
||||
row_handle &operator=(row_handle &&) = default;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/// \brief constructor taking a category @a cat and a row @a r
|
||||
row_handle(category &cat, row &r)
|
||||
: m_category(&cat)
|
||||
, m_row(&r)
|
||||
{
|
||||
}
|
||||
|
||||
/// \brief return the category this row belongs to
|
||||
[[nodiscard]] category &get_category() const
|
||||
{
|
||||
return *m_category;
|
||||
}
|
||||
|
||||
/// \brief return the row ID
|
||||
[[nodiscard]] int64_t row_id() const
|
||||
{
|
||||
return reinterpret_cast<int64_t>(m_row);
|
||||
}
|
||||
|
||||
/// \brief Return true if the row is empty or uninitialised
|
||||
[[nodiscard]] bool empty() const
|
||||
{
|
||||
return m_category == nullptr or m_row == nullptr;
|
||||
}
|
||||
|
||||
/// \brief convenience method to test for empty()
|
||||
explicit operator bool() const
|
||||
{
|
||||
return not empty();
|
||||
}
|
||||
|
||||
/// \brief return the count of the items
|
||||
[[nodiscard]] size_t size() const { return m_row->size(); }
|
||||
|
||||
/// \brief return a cif::item_handle to the item in item @a item_ix
|
||||
item_handle operator[](uint16_t item_ix)
|
||||
{
|
||||
return { *m_category, *m_row, item_ix };
|
||||
}
|
||||
|
||||
/// \brief return a cif::item_handle to the item in item @a item_ix
|
||||
const_item_handle operator[](uint16_t item_ix) const
|
||||
{
|
||||
return { *m_category, *m_row, item_ix };
|
||||
}
|
||||
|
||||
/// \brief return a cif::item_handle to the item in the item named @a item_name
|
||||
item_handle operator[](std::string_view item_name)
|
||||
{
|
||||
return { *m_category, *m_row, add_item(item_name) };
|
||||
}
|
||||
|
||||
/// \brief return a cif::item_handle to the item in the item named @a item_name
|
||||
const_item_handle operator[](std::string_view item_name) const
|
||||
{
|
||||
return { *m_category, *m_row, get_item_ix(item_name) };
|
||||
}
|
||||
|
||||
/// \brief assign each of the items named in @a values to their respective value
|
||||
void assign(const std::vector<item> &values)
|
||||
{
|
||||
for (auto &value : values)
|
||||
assign(value, true);
|
||||
}
|
||||
|
||||
/** \brief assign the value @a value to the item named @a name
|
||||
*
|
||||
* If updateLinked it true, linked records are updated as well.
|
||||
* That means that if item @a name is part of the link definition
|
||||
* and the link results in a linked record in another category
|
||||
* this record in the linked category is updated as well.
|
||||
*
|
||||
* If validate is true, which is default, the assigned value is
|
||||
* checked to see if it conforms to the rules defined in the dictionary
|
||||
*/
|
||||
|
||||
void assign(std::string_view name, item_value value, bool updateLinked, bool validate = true)
|
||||
{
|
||||
assign(add_item(name), std::move(value), updateLinked, validate);
|
||||
}
|
||||
|
||||
/** \brief assign the value @a value to item at index @a item
|
||||
*
|
||||
* If updateLinked it true, linked records are updated as well.
|
||||
* That means that if item @a item is part of the link definition
|
||||
* and the link results in a linked record in another category
|
||||
* this record in the linked category is updated as well.
|
||||
*
|
||||
* If validate is true, which is default, the assigned value is
|
||||
* checked to see if it conforms to the rules defined in the dictionary
|
||||
*/
|
||||
|
||||
void assign(uint16_t item, item_value value, bool updateLinked, bool validate = true);
|
||||
|
||||
/// \brief Return an object that can be used in combination with cif::tie
|
||||
/// to assign the values for the items @a items
|
||||
template <typename... C>
|
||||
[[nodiscard]] auto get(C... items) const
|
||||
{
|
||||
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
|
||||
}
|
||||
|
||||
/// \brief Return a tuple of values of types @a Ts for the items @a items
|
||||
template <typename... Ts, typename... C>
|
||||
std::tuple<Ts...> get(C... items) const
|
||||
requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
|
||||
{
|
||||
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
|
||||
}
|
||||
|
||||
/// \brief Get the value of item @a item cast to type @a T
|
||||
template <typename T>
|
||||
[[nodiscard]] T get(std::string_view item) const
|
||||
{
|
||||
return operator[](get_item_ix(item)).template get<T>();
|
||||
}
|
||||
|
||||
/// \brief compare two rows
|
||||
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
|
||||
|
||||
/// \brief compare two rows
|
||||
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
|
||||
|
||||
protected:
|
||||
[[nodiscard]] uint16_t get_item_ix(std::string_view name) const;
|
||||
[[nodiscard]] std::string_view get_item_name(uint16_t ix) const;
|
||||
|
||||
friend cql::connection_impl;
|
||||
|
||||
[[nodiscard]] auto get_row() const
|
||||
{
|
||||
return m_row;
|
||||
}
|
||||
|
||||
// void swap(uint16_t item, row_handle &r) noexcept(false);
|
||||
// {
|
||||
// if (not m_category)
|
||||
// throw std::runtime_error("uninitialized row");
|
||||
//
|
||||
// m_category->swap_item(item, *this, b);
|
||||
// }
|
||||
|
||||
category *m_category = nullptr;
|
||||
row *m_row = nullptr;
|
||||
|
||||
private:
|
||||
uint16_t add_item(std::string_view name);
|
||||
|
||||
void assign(const item &i, bool updateLinked)
|
||||
{
|
||||
assign(i.name(), i.value(), updateLinked);
|
||||
}
|
||||
};
|
||||
|
||||
class const_row_handle
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
template <bool>
|
||||
friend struct item_handle_base;
|
||||
friend class category;
|
||||
friend class category_index;
|
||||
friend class row_initializer;
|
||||
|
||||
template <bool, typename...>
|
||||
friend class iterator_impl_base;
|
||||
|
||||
const_row_handle() = default;
|
||||
virtual ~const_row_handle() = default;
|
||||
|
||||
const_row_handle(const const_row_handle &) = default;
|
||||
const_row_handle(const_row_handle &&) = default;
|
||||
const_row_handle &operator=(const const_row_handle &) = default;
|
||||
const_row_handle &operator=(const_row_handle &&) = default;
|
||||
|
||||
const_row_handle(row_handle rh)
|
||||
: m_category(rh.m_category)
|
||||
, m_row(rh.m_row)
|
||||
{
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/// \brief constructor taking a category @a cat and a row @a r
|
||||
const_row_handle(const category &cat, const row &r)
|
||||
: m_category(&cat)
|
||||
, m_row(&r)
|
||||
{
|
||||
}
|
||||
|
||||
/// \brief return the category this row belongs to
|
||||
[[nodiscard]] const category &get_category() const
|
||||
{
|
||||
return *m_category;
|
||||
}
|
||||
|
||||
/// \brief return the row ID
|
||||
[[nodiscard]] int64_t row_id() const
|
||||
{
|
||||
return reinterpret_cast<int64_t>(m_row);
|
||||
}
|
||||
|
||||
/// \brief Return true if the row is empty or uninitialised
|
||||
[[nodiscard]] bool empty() const
|
||||
{
|
||||
return m_category == nullptr or m_row == nullptr;
|
||||
}
|
||||
|
||||
/// \brief convenience method to test for empty()
|
||||
explicit operator bool() const
|
||||
{
|
||||
return not empty();
|
||||
}
|
||||
|
||||
/// \brief return the count of the items
|
||||
[[nodiscard]] size_t size() const { return m_row->size(); }
|
||||
|
||||
/// \brief return a cif::item_handle to the item in item @a item_ix
|
||||
const_item_handle operator[](uint16_t item_ix) const
|
||||
{
|
||||
return { *m_category, *m_row, item_ix };
|
||||
}
|
||||
|
||||
/// \brief return a cif::item_handle to the item in the item named @a item_name
|
||||
const_item_handle operator[](std::string_view item_name) const
|
||||
{
|
||||
return operator[](get_item_ix(item_name));
|
||||
}
|
||||
|
||||
/// \brief Return an object that can be used in combination with cif::tie
|
||||
/// to assign the values for the items @a items
|
||||
template <typename... C>
|
||||
[[nodiscard]] auto get(C... items) const
|
||||
{
|
||||
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
|
||||
}
|
||||
|
||||
/// \brief Return a tuple of values of types @a Ts for the items @a items
|
||||
template <typename... Ts, typename... C>
|
||||
std::tuple<Ts...> get(C... items) const
|
||||
requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
|
||||
{
|
||||
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
|
||||
}
|
||||
|
||||
/// \brief Get the value of item @a item cast to type @a T
|
||||
template <typename T>
|
||||
[[nodiscard]] T get(std::string_view item) const
|
||||
{
|
||||
return operator[](get_item_ix(item)).template get<T>();
|
||||
}
|
||||
|
||||
/// \brief compare two rows
|
||||
// bool operator==(const const_row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
|
||||
friend bool operator==(const_row_handle a, const_row_handle b)
|
||||
{
|
||||
return a.m_category == b.m_category and a.m_row == b.m_row;
|
||||
}
|
||||
|
||||
/// \brief compare two rows
|
||||
bool operator!=(const const_row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
|
||||
|
||||
protected:
|
||||
[[nodiscard]] uint16_t get_item_ix(std::string_view name) const;
|
||||
[[nodiscard]] std::string_view get_item_name(uint16_t ix) const;
|
||||
|
||||
friend cql::connection_impl;
|
||||
|
||||
[[nodiscard]] auto get_row() const
|
||||
{
|
||||
return m_row;
|
||||
}
|
||||
|
||||
// void swap(uint16_t item, const_row_handle &r) noexcept(false);
|
||||
// {
|
||||
// if (not m_category)
|
||||
// throw std::runtime_error("uninitialized row");
|
||||
//
|
||||
// m_category->swap_item(item, *this, b);
|
||||
// }
|
||||
|
||||
const category *m_category = nullptr;
|
||||
const row *m_row = nullptr;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
@@ -87,8 +466,8 @@ namespace detail
|
||||
{
|
||||
static constexpr std::size_t N = sizeof...(C);
|
||||
|
||||
get_row_result(const row_handle &r, std::array<uint16_t, N> &&items)
|
||||
: m_row(r)
|
||||
get_row_result(const_row_handle r, std::array<uint16_t, N> &&items)
|
||||
: m_row(std::move(r))
|
||||
, m_items(std::move(items))
|
||||
{
|
||||
}
|
||||
@@ -98,19 +477,20 @@ namespace detail
|
||||
return m_row[m_items[ix]];
|
||||
}
|
||||
|
||||
template <typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0>
|
||||
template <typename... Ts>
|
||||
operator std::tuple<Ts...>() const
|
||||
requires(N == sizeof...(Ts))
|
||||
{
|
||||
return get<Ts...>(std::index_sequence_for<Ts...>{});
|
||||
}
|
||||
|
||||
template <typename... Ts, std::size_t... Is>
|
||||
std::tuple<Ts...> get(std::index_sequence<Is...>) const
|
||||
[[nodiscard]] std::tuple<Ts...> get(std::index_sequence<Is...>) const
|
||||
{
|
||||
return std::tuple<Ts...>{ m_row[m_items[Is]].template as<Ts>()... };
|
||||
return std::tuple<Ts...>{ m_row[m_items[Is]].template get<Ts>()... };
|
||||
}
|
||||
|
||||
const row_handle &m_row;
|
||||
const_row_handle m_row;
|
||||
std::array<uint16_t, N> m_items;
|
||||
};
|
||||
|
||||
@@ -131,7 +511,7 @@ namespace detail
|
||||
// of the row should be equal to the number of items in the tuple
|
||||
// you are trying to tie.
|
||||
|
||||
using RType = std::tuple<typename std::remove_reference<Ts>::type...>;
|
||||
using RType = std::tuple<std::remove_reference_t<Ts>...>;
|
||||
|
||||
m_value = static_cast<RType>(rr);
|
||||
}
|
||||
@@ -141,7 +521,7 @@ namespace detail
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// \brief similar to std::tie, assign values to each element in @a v from the
|
||||
/// \brief similar to std::tie, assign values to each element in @a v from the
|
||||
/// result of a get on a row_handle.
|
||||
template <typename... Ts>
|
||||
auto tie(Ts &...v)
|
||||
@@ -149,229 +529,11 @@ auto tie(Ts &...v)
|
||||
return detail::tie_wrap<Ts &...>(std::forward<Ts &>(v)...);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief the row class, this one is not directly accessible from the outside
|
||||
|
||||
class row : public std::vector<item_value>
|
||||
{
|
||||
public:
|
||||
row() = default;
|
||||
|
||||
/**
|
||||
* @brief Return the item_value pointer for item at index @a ix
|
||||
*/
|
||||
item_value* get(uint16_t ix)
|
||||
{
|
||||
return ix < size() ? &data()[ix] : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the const item_value pointer for item at index @a ix
|
||||
*/
|
||||
const item_value* get(uint16_t ix) const
|
||||
{
|
||||
return ix < size() ? &data()[ix] : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class category;
|
||||
friend class category_index;
|
||||
|
||||
template <typename, typename...>
|
||||
friend class iterator_impl;
|
||||
|
||||
void append(uint16_t ix, item_value &&iv)
|
||||
{
|
||||
if (ix >= size())
|
||||
resize(ix + 1);
|
||||
|
||||
at(ix) = std::move(iv);
|
||||
}
|
||||
|
||||
void remove(uint16_t ix)
|
||||
{
|
||||
if (ix < size())
|
||||
at(ix) = item_value{};
|
||||
}
|
||||
|
||||
row *m_next = nullptr;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief row_handle is the way to access data stored in rows
|
||||
|
||||
class row_handle
|
||||
{
|
||||
public:
|
||||
/** @cond */
|
||||
friend struct item_handle;
|
||||
friend class category;
|
||||
friend class category_index;
|
||||
friend class row_initializer;
|
||||
template <typename, typename...> friend class iterator_impl;
|
||||
|
||||
row_handle() = default;
|
||||
|
||||
row_handle(const row_handle &) = default;
|
||||
row_handle(row_handle &&) = default;
|
||||
|
||||
row_handle &operator=(const row_handle &) = default;
|
||||
row_handle &operator=(row_handle &&) = default;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/// \brief constructor taking a category @a cat and a row @a r
|
||||
row_handle(const category &cat, const row &r)
|
||||
: m_category(const_cast<category *>(&cat))
|
||||
, m_row(const_cast<row *>(&r))
|
||||
{
|
||||
}
|
||||
|
||||
/// \brief return the category this row belongs to
|
||||
const category &get_category() const
|
||||
{
|
||||
return *m_category;
|
||||
}
|
||||
|
||||
/// \brief Return true if the row is empty or uninitialised
|
||||
bool empty() const
|
||||
{
|
||||
return m_category == nullptr or m_row == nullptr;
|
||||
}
|
||||
|
||||
/// \brief convenience method to test for empty()
|
||||
explicit operator bool() const
|
||||
{
|
||||
return not empty();
|
||||
}
|
||||
|
||||
/// \brief return a cif::item_handle to the item in item @a item_ix
|
||||
item_handle operator[](uint16_t item_ix)
|
||||
{
|
||||
return empty() ? item_handle::s_null_item : item_handle(item_ix, *this);
|
||||
}
|
||||
|
||||
/// \brief return a const cif::item_handle to the item in item @a item_ix
|
||||
const item_handle operator[](uint16_t item_ix) const
|
||||
{
|
||||
return empty() ? item_handle::s_null_item : item_handle(item_ix, const_cast<row_handle &>(*this));
|
||||
}
|
||||
|
||||
/// \brief return a cif::item_handle to the item in the item named @a item_name
|
||||
item_handle operator[](std::string_view item_name)
|
||||
{
|
||||
return empty() ? item_handle::s_null_item : item_handle(add_item(item_name), *this);
|
||||
}
|
||||
|
||||
/// \brief return a const cif::item_handle to the item in the item named @a item_name
|
||||
const item_handle operator[](std::string_view item_name) const
|
||||
{
|
||||
return empty() ? item_handle::s_null_item : item_handle(get_item_ix(item_name), const_cast<row_handle &>(*this));
|
||||
}
|
||||
|
||||
/// \brief Return an object that can be used in combination with cif::tie
|
||||
/// to assign the values for the items @a items
|
||||
template <typename... C>
|
||||
auto get(C... items) const
|
||||
{
|
||||
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
|
||||
}
|
||||
|
||||
/// \brief Return a tuple of values of types @a Ts for the items @a items
|
||||
template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1, int> = 0>
|
||||
std::tuple<Ts...> get(C... items) const
|
||||
{
|
||||
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
|
||||
}
|
||||
|
||||
/// \brief Get the value of item @a item cast to type @a T
|
||||
template <typename T>
|
||||
T get(const char *item) const
|
||||
{
|
||||
return operator[](get_item_ix(item)).template as<T>();
|
||||
}
|
||||
|
||||
/// \brief Get the value of item @a item cast to type @a T
|
||||
template <typename T>
|
||||
T get(std::string_view item) const
|
||||
{
|
||||
return operator[](get_item_ix(item)).template as<T>();
|
||||
}
|
||||
|
||||
/// \brief assign each of the items named in @a values to their respective value
|
||||
void assign(const std::vector<item> &values)
|
||||
{
|
||||
for (auto &value : values)
|
||||
assign(value, true);
|
||||
}
|
||||
|
||||
/** \brief assign the value @a value to the item named @a name
|
||||
*
|
||||
* If updateLinked it true, linked records are updated as well.
|
||||
* That means that if item @a name is part of the link definition
|
||||
* and the link results in a linked record in another category
|
||||
* this record in the linked category is updated as well.
|
||||
*
|
||||
* If validate is true, which is default, the assigned value is
|
||||
* checked to see if it conforms to the rules defined in the dictionary
|
||||
*/
|
||||
|
||||
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
|
||||
{
|
||||
assign(add_item(name), value, updateLinked, validate);
|
||||
}
|
||||
|
||||
/** \brief assign the value @a value to item at index @a item
|
||||
*
|
||||
* If updateLinked it true, linked records are updated as well.
|
||||
* That means that if item @a item is part of the link definition
|
||||
* and the link results in a linked record in another category
|
||||
* this record in the linked category is updated as well.
|
||||
*
|
||||
* If validate is true, which is default, the assigned value is
|
||||
* checked to see if it conforms to the rules defined in the dictionary
|
||||
*/
|
||||
|
||||
void assign(uint16_t item, std::string_view value, bool updateLinked, bool validate = true);
|
||||
|
||||
/// \brief compare two rows
|
||||
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
|
||||
|
||||
/// \brief compare two rows
|
||||
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
|
||||
|
||||
private:
|
||||
uint16_t get_item_ix(std::string_view name) const;
|
||||
std::string_view get_item_name(uint16_t ix) const;
|
||||
|
||||
uint16_t add_item(std::string_view name);
|
||||
|
||||
row *get_row()
|
||||
{
|
||||
return m_row;
|
||||
}
|
||||
|
||||
const row *get_row() const
|
||||
{
|
||||
return m_row;
|
||||
}
|
||||
|
||||
void assign(const item &i, bool updateLinked)
|
||||
{
|
||||
assign(i.name(), i.value(), updateLinked);
|
||||
}
|
||||
|
||||
void swap(uint16_t item, row_handle &r);
|
||||
|
||||
category *m_category = nullptr;
|
||||
row *m_row = nullptr;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief The class row_initializer is a list of cif::item's.
|
||||
*
|
||||
*
|
||||
* This class is used to construct new rows, it allows to
|
||||
* group a list of item name and value pairs and pass it
|
||||
* in one go to the constructing function.
|
||||
@@ -397,18 +559,23 @@ class row_initializer : public std::vector<item>
|
||||
}
|
||||
|
||||
/// \brief constructor taking a range of items
|
||||
template <typename ItemIter, std::enable_if_t<std::is_same_v<typename ItemIter::value_type, item>, int> = 0>
|
||||
template <typename ItemIter>
|
||||
row_initializer(ItemIter b, ItemIter e)
|
||||
requires(std::is_same_v<typename ItemIter::value_type, item>)
|
||||
: std::vector<item>(b, e)
|
||||
{
|
||||
}
|
||||
|
||||
/// \brief constructor taking the values of an existing row
|
||||
row_initializer(row_handle rh);
|
||||
row_initializer(row_handle rh)
|
||||
: cif::row_initializer(const_row_handle{ rh })
|
||||
{
|
||||
}
|
||||
|
||||
row_initializer(const_row_handle rh);
|
||||
|
||||
/// \brief set the value for item name @a name to @a value
|
||||
void set_value(std::string_view name, std::string_view value);
|
||||
void set_value(std::string name, item_value value);
|
||||
|
||||
/// \brief set the value for item based on @a i
|
||||
void set_value(const item &i)
|
||||
@@ -417,7 +584,7 @@ class row_initializer : public std::vector<item>
|
||||
}
|
||||
|
||||
/// \brief set the value for item name @a name to @a value, but only if the item did not have a value already
|
||||
void set_value_if_empty(std::string_view name, std::string_view value);
|
||||
void set_value_if_empty(std::string name, item_value value);
|
||||
|
||||
/// \brief set the value for item @a i, but only if the item did not have a value already
|
||||
void set_value_if_empty(const item &i)
|
||||
@@ -426,4 +593,4 @@ class row_initializer : public std::vector<item>
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cif
|
||||
} // namespace cif
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <string>
|
||||
|
||||
#if defined(__cpp_impl_three_way_comparison)
|
||||
#include <compare>
|
||||
# include <utility>
|
||||
#endif
|
||||
|
||||
/** \file cif++/symmetry.hpp
|
||||
@@ -127,20 +127,20 @@ struct symop_data
|
||||
}
|
||||
|
||||
/// \brief return an int representing the value stored in the two bits at offset @a offset
|
||||
inline constexpr int unpack3(int offset) const
|
||||
[[nodiscard]] inline constexpr int unpack3(int offset) const
|
||||
{
|
||||
int result = (m_packed >> offset) bitand 0x03;
|
||||
int result = static_cast<int>((m_packed >> offset) bitand 0x03);
|
||||
return result == 3 ? -1 : result;
|
||||
}
|
||||
|
||||
/// \brief return an int representing the value stored in the three bits at offset @a offset
|
||||
inline constexpr int unpack7(int offset) const
|
||||
[[nodiscard]] inline constexpr int unpack7(int offset) const
|
||||
{
|
||||
return (m_packed >> offset) bitand 0x07;
|
||||
return static_cast<int>((m_packed >> offset) bitand 0x07);
|
||||
}
|
||||
|
||||
/// \brief return an array of 15 ints representing the values stored
|
||||
constexpr std::array<int, 15> data() const
|
||||
[[nodiscard]] constexpr std::array<int, 15> data() const
|
||||
{
|
||||
return {
|
||||
unpack3(34),
|
||||
@@ -189,9 +189,9 @@ struct symop_datablock
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t spacegroup() const { return m_v >> 48; } ///< Return the spacegroup
|
||||
symop_data symop() const { return symop_data(m_v); } ///< Return the symmetry operation
|
||||
uint8_t rotational_number() const { return (m_v >> 40) bitand 0xff; } ///< Return the rotational_number
|
||||
[[nodiscard]] int spacegroup() const { return m_v >> 48; } ///< Return the spacegroup
|
||||
[[nodiscard]] symop_data symop() const { return { m_v }; } ///< Return the symmetry operation
|
||||
[[nodiscard]] uint8_t rotational_number() const { return (m_v >> 40) bitand 0xff; } ///< Return the rotational_number
|
||||
|
||||
private:
|
||||
uint64_t m_v;
|
||||
@@ -249,7 +249,7 @@ struct sym_op
|
||||
/** @endcond */
|
||||
|
||||
/// \brief return true if this sym_op is the identity operator
|
||||
constexpr bool is_identity() const
|
||||
[[nodiscard]] constexpr bool is_identity() const
|
||||
{
|
||||
return m_nr == 1 and m_ta == 5 and m_tb == 5 and m_tc == 5;
|
||||
}
|
||||
@@ -261,7 +261,7 @@ struct sym_op
|
||||
}
|
||||
|
||||
/// \brief return the content encoded in a string
|
||||
std::string string() const;
|
||||
[[nodiscard]] std::string string() const;
|
||||
|
||||
#if defined(__cpp_impl_three_way_comparison)
|
||||
/// \brief a default spaceship operator
|
||||
@@ -389,18 +389,18 @@ class cell
|
||||
/// \brief constructor that takes the appropriate values from the *cell* category in datablock @a db
|
||||
cell(const datablock &db);
|
||||
|
||||
float get_a() const { return m_a; } ///< return dimension a
|
||||
float get_b() const { return m_b; } ///< return dimension b
|
||||
float get_c() const { return m_c; } ///< return dimension c
|
||||
[[nodiscard]] float get_a() const { return m_a; } ///< return dimension a
|
||||
[[nodiscard]] float get_b() const { return m_b; } ///< return dimension b
|
||||
[[nodiscard]] float get_c() const { return m_c; } ///< return dimension c
|
||||
|
||||
float get_alpha() const { return m_alpha; } ///< return angle alpha
|
||||
float get_beta() const { return m_beta; } ///< return angle beta
|
||||
float get_gamma() const { return m_gamma; } ///< return angle gamma
|
||||
[[nodiscard]] float get_alpha() const { return m_alpha; } ///< return angle alpha
|
||||
[[nodiscard]] float get_beta() const { return m_beta; } ///< return angle beta
|
||||
[[nodiscard]] float get_gamma() const { return m_gamma; } ///< return angle gamma
|
||||
|
||||
float get_volume() const; ///< return the calculated volume for this cell
|
||||
[[nodiscard]] float get_volume() const; ///< return the calculated volume for this cell
|
||||
|
||||
matrix3x3<float> get_orthogonal_matrix() const { return m_orthogonal; } ///< return the matrix to use to transform coordinates from fractional to orthogonal
|
||||
matrix3x3<float> get_fractional_matrix() const { return m_fractional; } ///< return the matrix to use to transform coordinates from orthogonal to fractional
|
||||
[[nodiscard]] matrix3x3<float> get_orthogonal_matrix() const { return m_orthogonal; } ///< return the matrix to use to transform coordinates from fractional to orthogonal
|
||||
[[nodiscard]] matrix3x3<float> get_fractional_matrix() const { return m_fractional; } ///< return the matrix to use to transform coordinates from orthogonal to fractional
|
||||
|
||||
private:
|
||||
void init();
|
||||
@@ -448,8 +448,8 @@ class spacegroup : public std::vector<transformation>
|
||||
/// \brief constructor using the spacegroup number @a nr
|
||||
spacegroup(int nr);
|
||||
|
||||
int get_nr() const { return m_nr; } ///< Return the nr
|
||||
std::string get_name() const; ///< Return the name
|
||||
[[nodiscard]] int get_nr() const { return m_nr; } ///< Return the nr
|
||||
[[nodiscard]] std::string get_name() const; ///< Return the name
|
||||
|
||||
/** \brief perform a spacegroup operation on point @a pt using
|
||||
* cell @a c and sym_op @a symop
|
||||
@@ -460,7 +460,7 @@ class spacegroup : public std::vector<transformation>
|
||||
/** \brief perform an inverse spacegroup operation on point @a pt using
|
||||
* cell @a c and sym_op @a symop
|
||||
*/
|
||||
point inverse(const point &pt, const cell &c, sym_op symop) const;
|
||||
[[nodiscard]] point inverse(const point &pt, const cell &c, sym_op symop) const;
|
||||
|
||||
private:
|
||||
int m_nr;
|
||||
@@ -486,9 +486,9 @@ class crystal
|
||||
}
|
||||
|
||||
/// \brief constructor using cell @a c and spacegroup @a sg
|
||||
crystal(const cell &c, const spacegroup &sg)
|
||||
crystal(const cell &c, spacegroup sg)
|
||||
: m_cell(c)
|
||||
, m_spacegroup(sg)
|
||||
, m_spacegroup(std::move(sg))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -499,24 +499,24 @@ class crystal
|
||||
crystal &operator=(crystal &&) = default;
|
||||
/** @endcond */
|
||||
|
||||
const cell &get_cell() const { return m_cell; } ///< Return the cell
|
||||
const spacegroup &get_spacegroup() const { return m_spacegroup; } ///< Return the spacegroup
|
||||
[[nodiscard]] const cell &get_cell() const { return m_cell; } ///< Return the cell
|
||||
[[nodiscard]] const spacegroup &get_spacegroup() const { return m_spacegroup; } ///< Return the spacegroup
|
||||
|
||||
/// \brief Return the symmetry copy of point @a pt using symmetry operation @a symop
|
||||
point symmetry_copy(const point &pt, sym_op symop) const
|
||||
[[nodiscard]] point symmetry_copy(const point &pt, sym_op symop) const
|
||||
{
|
||||
return m_spacegroup(pt, m_cell, symop);
|
||||
}
|
||||
|
||||
/// \brief Return the symmetry copy of point @a pt using the inverse of symmetry operation @a symop
|
||||
point inverse_symmetry_copy(const point &pt, sym_op symop) const
|
||||
[[nodiscard]] point inverse_symmetry_copy(const point &pt, sym_op symop) const
|
||||
{
|
||||
return m_spacegroup.inverse(pt, m_cell, symop);
|
||||
}
|
||||
|
||||
/// \brief Return a tuple consisting of distance, new location and symmetry operation
|
||||
/// for the point @a b with respect to point @a a.
|
||||
std::tuple<float, point, sym_op> closest_symmetry_copy(point a, point b) const;
|
||||
[[nodiscard]] std::tuple<float, point, sym_op> closest_symmetry_copy(point a, point b) const;
|
||||
|
||||
private:
|
||||
cell m_cell;
|
||||
|
||||
@@ -29,17 +29,21 @@
|
||||
#include "cif++/exports.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if __has_include(<experimental/type_traits>)
|
||||
|
||||
#include <experimental/type_traits>
|
||||
# include <experimental/type_traits>
|
||||
namespace std_experimental = std::experimental;
|
||||
|
||||
#else
|
||||
@@ -88,16 +92,16 @@ namespace cif
|
||||
// our own case conversion routines.
|
||||
|
||||
/// \brief return whether string @a is equal to string @a b ignoring changes in character case
|
||||
bool iequals(std::string_view a, std::string_view b);
|
||||
bool iequals(std::string_view a, std::string_view b) noexcept;
|
||||
|
||||
/// \brief compare string @a is to string @a b ignoring changes in character case
|
||||
int icompare(std::string_view a, std::string_view b);
|
||||
int icompare(std::string_view a, std::string_view b) noexcept;
|
||||
|
||||
/// \brief return whether string @a is equal to string @a b ignoring changes in character case
|
||||
bool iequals(const char *a, const char *b);
|
||||
bool iequals(const char *a, const char *b) noexcept;
|
||||
|
||||
/// \brief compare string @a is to string @a b ignoring changes in character case
|
||||
int icompare(const char *a, const char *b);
|
||||
int icompare(const char *a, const char *b) noexcept;
|
||||
|
||||
/// \brief convert the string @a s to lower case in situ
|
||||
void to_lower(std::string &s);
|
||||
@@ -328,7 +332,6 @@ inline char tolower(int ch)
|
||||
[[deprecated("use split_item_name instead")]]
|
||||
std::tuple<std::string, std::string> split_tag_name(std::string_view item_name);
|
||||
|
||||
|
||||
/** \brief return a tuple consisting of the category and item name for @a item_name
|
||||
*
|
||||
* The category name is stripped of its leading underscore character.
|
||||
@@ -355,279 +358,35 @@ std::string cif_id_for_number(int number);
|
||||
std::vector<std::string> word_wrap(const std::string &text, std::size_t width);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief std::from_chars for floating point types.
|
||||
///
|
||||
/// These are optional, there's a selected_charconv class below that selects
|
||||
/// the best option to use based on support by the stl library.
|
||||
///
|
||||
/// I.e. that in case of GNU < 12 (or something) the cif implementation will
|
||||
/// be used, all other cases will use the stl version.
|
||||
|
||||
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
|
||||
std::from_chars_result from_chars(const char *first, const char *last, FloatType &value)
|
||||
{
|
||||
std::from_chars_result result{ first, {} };
|
||||
|
||||
enum State
|
||||
{
|
||||
IntegerSign,
|
||||
Integer,
|
||||
Fraction,
|
||||
ExponentSign,
|
||||
Exponent
|
||||
} state = IntegerSign;
|
||||
int sign = 1;
|
||||
unsigned long long vi = 0;
|
||||
int fl = 0, tz = 0;
|
||||
int exponent_sign = 1;
|
||||
int exponent = 0;
|
||||
bool done = false;
|
||||
|
||||
while (not done and not (bool)result.ec)
|
||||
{
|
||||
char ch = result.ptr != last ? *result.ptr : 0;
|
||||
++result.ptr;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case IntegerSign:
|
||||
if (ch == '-')
|
||||
{
|
||||
sign = -1;
|
||||
state = Integer;
|
||||
}
|
||||
else if (ch == '+')
|
||||
state = Integer;
|
||||
else if (ch >= '0' and ch <= '9')
|
||||
{
|
||||
vi = ch - '0';
|
||||
state = Integer;
|
||||
}
|
||||
else if (ch == '.')
|
||||
state = Fraction;
|
||||
else
|
||||
result.ec = std::errc::invalid_argument;
|
||||
break;
|
||||
|
||||
case Integer:
|
||||
if (ch >= '0' and ch <= '9')
|
||||
vi = 10 * vi + (ch - '0');
|
||||
else if (ch == 'e' or ch == 'E')
|
||||
state = ExponentSign;
|
||||
else if (ch == '.')
|
||||
state = Fraction;
|
||||
else
|
||||
{
|
||||
done = true;
|
||||
--result.ptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case Fraction:
|
||||
if (ch >= '0' and ch <= '9')
|
||||
{
|
||||
vi = 10 * vi + (ch - '0');
|
||||
|
||||
if (ch == '0')
|
||||
tz += 1;
|
||||
else
|
||||
{
|
||||
fl += tz + 1;
|
||||
tz = 0;
|
||||
}
|
||||
}
|
||||
else if (ch == 'e' or ch == 'E')
|
||||
state = ExponentSign;
|
||||
else
|
||||
{
|
||||
done = true;
|
||||
--result.ptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExponentSign:
|
||||
if (ch == '-')
|
||||
{
|
||||
exponent_sign = -1;
|
||||
state = Exponent;
|
||||
}
|
||||
else if (ch == '+')
|
||||
state = Exponent;
|
||||
else if (ch >= '0' and ch <= '9')
|
||||
{
|
||||
exponent = ch - '0';
|
||||
state = Exponent;
|
||||
}
|
||||
else
|
||||
result.ec = std::errc::invalid_argument;
|
||||
break;
|
||||
|
||||
case Exponent:
|
||||
if (ch >= '0' and ch <= '9')
|
||||
exponent = 10 * exponent + (ch - '0');
|
||||
else
|
||||
{
|
||||
done = true;
|
||||
--result.ptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (not (bool)result.ec)
|
||||
{
|
||||
while (tz-- > 0)
|
||||
vi /= 10;
|
||||
|
||||
long double v = std::pow(10, -fl) * vi * sign;
|
||||
if (exponent != 0)
|
||||
v *= std::pow(10, exponent * exponent_sign);
|
||||
|
||||
if (std::isnan(v))
|
||||
result.ec = std::errc::invalid_argument;
|
||||
else if (std::abs(v) > std::numeric_limits<FloatType>::max())
|
||||
result.ec = std::errc::result_out_of_range;
|
||||
|
||||
value = static_cast<FloatType>(v);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief duplication of std::chars_format for deficient STL implementations
|
||||
enum class chars_format
|
||||
{
|
||||
scientific = 1,
|
||||
fixed = 2,
|
||||
// hex,
|
||||
general = fixed | scientific
|
||||
};
|
||||
|
||||
/// \brief a simplistic implementation of std::to_chars for old STL implementations
|
||||
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
|
||||
std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_format fmt)
|
||||
{
|
||||
int size = static_cast<int>(last - first);
|
||||
int r = 0;
|
||||
|
||||
switch (fmt)
|
||||
{
|
||||
case chars_format::scientific:
|
||||
if constexpr (std::is_same_v<FloatType, long double>)
|
||||
r = snprintf(first, last - first, "%le", value);
|
||||
else
|
||||
r = snprintf(first, last - first, "%e", value);
|
||||
break;
|
||||
|
||||
case chars_format::fixed:
|
||||
if constexpr (std::is_same_v<FloatType, long double>)
|
||||
r = snprintf(first, last - first, "%lf", value);
|
||||
else
|
||||
r = snprintf(first, last - first, "%f", value);
|
||||
break;
|
||||
|
||||
case chars_format::general:
|
||||
if constexpr (std::is_same_v<FloatType, long double>)
|
||||
r = snprintf(first, last - first, "%lg", value);
|
||||
else
|
||||
r = snprintf(first, last - first, "%g", value);
|
||||
break;
|
||||
}
|
||||
|
||||
std::to_chars_result result;
|
||||
if (r < 0 or r >= size)
|
||||
result = { first, std::errc::value_too_large };
|
||||
else
|
||||
result = { first + r, std::errc() };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief a simplistic implementation of std::to_chars for old STL implementations
|
||||
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
|
||||
std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_format fmt, int precision)
|
||||
{
|
||||
int size = static_cast<int>(last - first);
|
||||
int r = 0;
|
||||
|
||||
switch (fmt)
|
||||
{
|
||||
case chars_format::scientific:
|
||||
if constexpr (std::is_same_v<FloatType, long double>)
|
||||
r = snprintf(first, last - first, "%.*le", precision, value);
|
||||
else
|
||||
r = snprintf(first, last - first, "%.*e", precision, value);
|
||||
break;
|
||||
|
||||
case chars_format::fixed:
|
||||
if constexpr (std::is_same_v<FloatType, long double>)
|
||||
r = snprintf(first, last - first, "%.*lf", precision, value);
|
||||
else
|
||||
r = snprintf(first, last - first, "%.*f", precision, value);
|
||||
break;
|
||||
|
||||
case chars_format::general:
|
||||
if constexpr (std::is_same_v<FloatType, long double>)
|
||||
r = snprintf(first, last - first, "%.*lg", precision, value);
|
||||
else
|
||||
r = snprintf(first, last - first, "%.*g", precision, value);
|
||||
break;
|
||||
}
|
||||
|
||||
std::to_chars_result result;
|
||||
if (r < 0 or r >= size)
|
||||
result = { first, std::errc::value_too_large };
|
||||
else
|
||||
result = { first + r, std::errc() };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// \brief class that uses our implementation of std::from_chars and std::to_chars
|
||||
template <typename T>
|
||||
struct my_charconv
|
||||
{
|
||||
/// @brief Simply call our version of std::from_chars
|
||||
static std::from_chars_result from_chars(const char *a, const char *b, T &d)
|
||||
{
|
||||
return cif::from_chars(a, b, d);
|
||||
}
|
||||
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
|
||||
|
||||
/// @brief Simply call our version of std::to_chars
|
||||
static std::to_chars_result to_chars(char *first, char *last, T &value, chars_format fmt)
|
||||
{
|
||||
return cif::to_chars(first, last, value, fmt);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief class that uses the STL implementation of std::from_chars and std::to_chars
|
||||
template <typename T>
|
||||
struct std_charconv
|
||||
{
|
||||
/// @brief Simply call std::from_chars
|
||||
static std::from_chars_result from_chars(const char *a, const char *b, T &d)
|
||||
{
|
||||
return std::from_chars(a, b, d);
|
||||
}
|
||||
|
||||
/// @brief Simply call std::to_chars
|
||||
static std::to_chars_result to_chars(char *first, char *last, T &value, chars_format fmt)
|
||||
{
|
||||
return std::to_chars(first, last, value, fmt);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief helper to find a from_chars function
|
||||
template <typename T>
|
||||
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
|
||||
template <typename T, typename = void>
|
||||
struct ff_charconv;
|
||||
|
||||
/**
|
||||
* @brief Helper to select the best implementation of charconv based on availability of the
|
||||
* function in the std:: namespace
|
||||
*
|
||||
* @tparam T The type for which we want to find a from_chars/to_chars function
|
||||
*/
|
||||
template <typename T>
|
||||
using selected_charconv = typename std::conditional_t<std_experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, my_charconv<T>>;
|
||||
struct ff_charconv<T, typename std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
{
|
||||
static std::from_chars_result from_chars(const char *a, const char *b, T &v);
|
||||
};
|
||||
|
||||
} // namespace cif
|
||||
template <typename T>
|
||||
using charconv = typename std::conditional_t<std_experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, ff_charconv<T>>;
|
||||
|
||||
template <typename T>
|
||||
constexpr auto from_chars(const char *s, const char *e, T &v)
|
||||
{
|
||||
return charconv<T>::from_chars(s, e, v);
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -28,33 +28,44 @@
|
||||
|
||||
#include "cif++/exports.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef STDOUT_FILENO
|
||||
/// @brief For systems that lack this value
|
||||
#define STDOUT_FILENO 1
|
||||
# define STDOUT_FILENO 1
|
||||
#endif
|
||||
|
||||
#ifndef STDERR_FILENO
|
||||
/// @brief For systems that lack this value
|
||||
#define STDERR_FILENO 2
|
||||
# define STDERR_FILENO 2
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
#include <io.h>
|
||||
#define isatty _isatty
|
||||
# include <io.h>
|
||||
# define isatty _isatty
|
||||
#else
|
||||
#include <unistd.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if _MSC_VER
|
||||
#pragma warning(disable : 4996) // unsafe function or variable (strcpy e.g.)
|
||||
#pragma warning(disable : 4068) // unknown pragma
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#pragma warning(disable : 4101) // unreferenced local variable
|
||||
#pragma warning(disable : 4702) // unreachable code (too bad, this one. Happens in for loops)
|
||||
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1
|
||||
# pragma warning(disable : 4996) // unsafe function or variable (strcpy e.g.)
|
||||
# pragma warning(disable : 4068) // unknown pragma
|
||||
# pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
# pragma warning(disable : 4101) // unreferenced local variable
|
||||
# pragma warning(disable : 4702) // unreachable code (too bad, this one. Happens in for loops)
|
||||
|
||||
// Truncation warnings: yes, perhaps, but I think they are okay
|
||||
# pragma warning(disable : 4244)
|
||||
# pragma warning(disable : 4267)
|
||||
# pragma warning(disable : 4305)
|
||||
|
||||
# define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1
|
||||
#endif
|
||||
|
||||
/** \file utilities.hpp
|
||||
@@ -76,10 +87,10 @@ namespace cif
|
||||
extern CIFPP_EXPORT int VERBOSE;
|
||||
|
||||
/// return the git 'build' number
|
||||
std::string get_version_nr();
|
||||
[[nodiscard]] std::string get_version_nr();
|
||||
|
||||
/// return the width of the current output terminal, or 80 if it cannot be determined
|
||||
uint32_t get_terminal_width();
|
||||
[[nodiscard]] uint32_t get_terminal_width();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -88,7 +99,7 @@ namespace colour
|
||||
/// @brief The defined colours
|
||||
enum colour_type
|
||||
{
|
||||
black = 0,
|
||||
black,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
@@ -96,7 +107,8 @@ namespace colour
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
none = 9
|
||||
_unused,
|
||||
none
|
||||
};
|
||||
|
||||
/// @brief The defined styles
|
||||
@@ -127,6 +139,7 @@ namespace colour
|
||||
{
|
||||
}
|
||||
|
||||
coloured_string_t(coloured_string_t &) = delete;
|
||||
coloured_string_t &operator=(coloured_string_t &) = delete;
|
||||
|
||||
/**
|
||||
@@ -136,14 +149,7 @@ namespace colour
|
||||
friend std::basic_ostream<char_type, traits_type> &operator<<(
|
||||
std::basic_ostream<char_type, traits_type> &os, const coloured_string_t &cs)
|
||||
{
|
||||
bool use_colour = false;
|
||||
|
||||
if (os.rdbuf() == std::cout.rdbuf() and isatty(STDOUT_FILENO))
|
||||
use_colour = true;
|
||||
else if (os.rdbuf() == std::cerr.rdbuf() and isatty(STDERR_FILENO))
|
||||
use_colour = true;
|
||||
|
||||
if (use_colour)
|
||||
if ((os.rdbuf() == std::cout.rdbuf() and isatty(STDOUT_FILENO)) or (os.rdbuf() == std::cerr.rdbuf() and isatty(STDERR_FILENO)))
|
||||
{
|
||||
os << "\033[" << cs.m_fore_colour << ';' << cs.m_style << ';' << cs.m_back_colour << 'm'
|
||||
<< cs.m_str
|
||||
@@ -167,7 +173,7 @@ namespace colour
|
||||
|
||||
/**
|
||||
* @brief Manipulator for coloured strings.
|
||||
*
|
||||
*
|
||||
* When writing out text to the terminal it is often useful to have
|
||||
* some of the text colourised. But only if the output is really a
|
||||
* terminal since colouring text is done using escape sequences
|
||||
@@ -204,51 +210,51 @@ inline auto coloured(T str,
|
||||
|
||||
/**
|
||||
* @brief A simple progress bar class for terminal based output
|
||||
*
|
||||
*
|
||||
* Using a progress bar is very convenient for the end user when
|
||||
* you have long running code. It gives feed back on how fast an
|
||||
* operation is performed and may give an indication how long it
|
||||
* will take before it is finished.
|
||||
*
|
||||
*
|
||||
* Using this cif::progress_bar implementation is straightforward:
|
||||
*
|
||||
*
|
||||
* @code {.cpp}
|
||||
* using namespace std::chrono_literals;
|
||||
*
|
||||
*
|
||||
* cif::progress_bar pb(10, "counting to ten");
|
||||
*
|
||||
*
|
||||
* for (int i = 1; i <= 10; ++i)
|
||||
* {
|
||||
* pb.consumed(1);
|
||||
* std::this_thread::sleep_for(1s);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* When the progress_bar is created, it first checks
|
||||
* to see if stdout is to a real TTY and if the VERBOSE
|
||||
* flag is not less than zero (quiet mode). If this passes
|
||||
* a thread is started that waits for updates.
|
||||
*
|
||||
*
|
||||
* The first two seconds, nothing is written to the screen
|
||||
* so if the work is finished within those two seconds
|
||||
* the screen stays clean.
|
||||
*
|
||||
*
|
||||
* After this time, a progress bar is printed that may look
|
||||
* like this:
|
||||
*
|
||||
*
|
||||
* @code
|
||||
* step 3 ========================-------------------------------- 40% ⢁
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* The first characters contain the initial action name or
|
||||
* the message text if it was used afterwards.
|
||||
*
|
||||
*
|
||||
* The thermometer is made up with '=' and '-' characters.
|
||||
*
|
||||
*
|
||||
* A percentage is also shown and at the end there is a spinner
|
||||
* that gives feedback that the program is really still working.
|
||||
*
|
||||
*
|
||||
* The progress bar is removed if the max has been reached
|
||||
* or if the progress bar is destructed. If any output has
|
||||
* been generated, the initial action is printed out along
|
||||
@@ -258,13 +264,16 @@ inline auto coloured(T str,
|
||||
class progress_bar
|
||||
{
|
||||
public:
|
||||
progress_bar(const progress_bar &) = delete;
|
||||
progress_bar &operator=(const progress_bar &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Construct a new progress bar object
|
||||
*
|
||||
*
|
||||
* Progress ranges from 0 (zero) to @a inMax
|
||||
*
|
||||
*
|
||||
* The action in @a inAction is used for display
|
||||
*
|
||||
*
|
||||
* @param inMax The maximum value
|
||||
* @param inAction The description of what is
|
||||
* going on
|
||||
@@ -274,7 +283,7 @@ class progress_bar
|
||||
|
||||
/**
|
||||
* @brief Destroy the progress bar object
|
||||
*
|
||||
*
|
||||
*/
|
||||
~progress_bar();
|
||||
|
||||
@@ -296,11 +305,13 @@ class progress_bar
|
||||
*/
|
||||
void message(const std::string &inMessage);
|
||||
|
||||
private:
|
||||
progress_bar(const progress_bar &) = delete;
|
||||
progress_bar &operator=(const progress_bar &) = delete;
|
||||
/**
|
||||
* @brief Flush the progress bar to the output stream
|
||||
*/
|
||||
void flush();
|
||||
|
||||
struct progress_bar_impl *m_impl;
|
||||
private:
|
||||
struct progress_bar_impl *m_impl = nullptr;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -308,14 +319,14 @@ class progress_bar
|
||||
|
||||
/**
|
||||
* @brief Load a resource from disk or the compiled in resources
|
||||
*
|
||||
*
|
||||
* @verbatim embed:rst
|
||||
.. note::
|
||||
|
||||
See the :doc:`documentation on resources </resources>` for more information.
|
||||
|
||||
@endverbatim
|
||||
*
|
||||
*
|
||||
* @param name The named resource to load
|
||||
* @return std::unique_ptr<std::istream> A pointer to the std::istream or empty if not found
|
||||
*/
|
||||
@@ -324,14 +335,14 @@ std::unique_ptr<std::istream> load_resource(std::filesystem::path name);
|
||||
|
||||
/**
|
||||
* @brief Add a file specified by @a dataFile as the data for resource @a name
|
||||
*
|
||||
*
|
||||
* @verbatim embed:rst
|
||||
.. note::
|
||||
|
||||
See the :doc:`documentation on resources </resources>` for more information.
|
||||
|
||||
@endverbatim
|
||||
*
|
||||
*
|
||||
* @param name The name of the resource to specify
|
||||
* @param dataFile Path to a file containing the data
|
||||
*/
|
||||
@@ -340,7 +351,7 @@ void add_file_resource(const std::string &name, std::filesystem::path dataFile);
|
||||
|
||||
/**
|
||||
* @brief List all the file resources added with cif::add_file_resource.
|
||||
*
|
||||
*
|
||||
* @param os The std::ostream to write the directories to
|
||||
*/
|
||||
|
||||
@@ -349,7 +360,7 @@ void list_file_resources(std::ostream &os);
|
||||
/**
|
||||
* @brief Add a directory to the list of search directories. This list is
|
||||
* searched in a last-in-first-out order.
|
||||
*
|
||||
*
|
||||
* @verbatim embed:rst
|
||||
.. note::
|
||||
|
||||
@@ -362,7 +373,7 @@ void add_data_directory(std::filesystem::path dataDir);
|
||||
|
||||
/**
|
||||
* @brief List all the data directories, for error reporting on missing resources.
|
||||
*
|
||||
*
|
||||
* @param os The std::ostream to write the directories to
|
||||
*/
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "cif++/text.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -65,6 +64,8 @@ enum class validation_error
|
||||
{
|
||||
value_does_not_match_rx = 1, /**< The value of an item does not conform to the regular expression specified for it */
|
||||
value_is_not_in_enumeration_list, /**< The value of an item is not in the list of values allowed */
|
||||
value_is_not_a_number, /**< The value is not a number */
|
||||
value_is_not_a_char_string, /**< The value is not a character string */
|
||||
not_a_known_primitive_type, /**< The type is not a known primitive type */
|
||||
undefined_category, /**< Category has no definition in the dictionary */
|
||||
unknown_item, /**< The item is not defined to be part of the category */
|
||||
@@ -90,7 +91,7 @@ class validation_category_impl : public std::error_category
|
||||
* @return const char*
|
||||
*/
|
||||
|
||||
const char *name() const noexcept override
|
||||
[[nodiscard]] const char *name() const noexcept override
|
||||
{
|
||||
return "cif::validation";
|
||||
}
|
||||
@@ -102,7 +103,7 @@ class validation_category_impl : public std::error_category
|
||||
* @return std::string
|
||||
*/
|
||||
|
||||
std::string message(int ev) const override
|
||||
[[nodiscard]] std::string message(int ev) const override
|
||||
{
|
||||
switch (static_cast<validation_error>(ev))
|
||||
{
|
||||
@@ -110,6 +111,10 @@ class validation_category_impl : public std::error_category
|
||||
return "Value in item does not match regular expression";
|
||||
case validation_error::value_is_not_in_enumeration_list:
|
||||
return "Value is not in the enumerated list of valid values";
|
||||
case validation_error::value_is_not_a_number:
|
||||
return "Value is not a number";
|
||||
case validation_error::value_is_not_a_char_string:
|
||||
return "Value is not a character string";
|
||||
case validation_error::not_a_known_primitive_type:
|
||||
return "The type is not a known primitive type";
|
||||
case validation_error::undefined_category:
|
||||
@@ -144,7 +149,7 @@ class validation_category_impl : public std::error_category
|
||||
*
|
||||
*/
|
||||
|
||||
bool equivalent(const std::error_code & /*code*/, int /*condition*/) const noexcept override
|
||||
[[nodiscard]] bool equivalent(const std::error_code & /*code*/, int /*condition*/) const noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -163,12 +168,12 @@ inline std::error_category &validation_category()
|
||||
|
||||
inline std::error_code make_error_code(validation_error e)
|
||||
{
|
||||
return std::error_code(static_cast<int>(e), validation_category());
|
||||
return { static_cast<int>(e), validation_category() };
|
||||
}
|
||||
|
||||
inline std::error_condition make_error_condition(validation_error e)
|
||||
{
|
||||
return std::error_condition(static_cast<int>(e), validation_category());
|
||||
return { static_cast<int>(e), validation_category() };
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -238,7 +243,7 @@ struct type_validator
|
||||
type_validator(std::string_view name, DDL_PrimitiveType type, std::string_view rx);
|
||||
|
||||
/// @brief Copy constructor
|
||||
type_validator(const type_validator &tv);
|
||||
type_validator(const type_validator &tv) = default;
|
||||
|
||||
/// @brief Move constructor
|
||||
type_validator(type_validator &&rhs)
|
||||
@@ -254,7 +259,7 @@ struct type_validator
|
||||
}
|
||||
|
||||
/// @brief Destructor
|
||||
~type_validator();
|
||||
~type_validator() = default;
|
||||
|
||||
friend void swap(type_validator &a, type_validator &b)
|
||||
{
|
||||
@@ -273,7 +278,7 @@ struct type_validator
|
||||
/// primitive type of this type. A value of zero indicates the
|
||||
/// values are equal. Less than zero means @a a sorts before @a b
|
||||
/// and a value larger than zero likewise means the opposite
|
||||
int compare(std::string_view a, std::string_view b) const;
|
||||
[[nodiscard]] int compare(const item_value &a, const item_value &b) const;
|
||||
};
|
||||
|
||||
/** @brief Item alias, items can be renamed over time
|
||||
@@ -281,10 +286,10 @@ struct type_validator
|
||||
|
||||
struct item_alias
|
||||
{
|
||||
item_alias(const std::string &alias_name, const std::string &dictionary, const std::string &version)
|
||||
: m_name(alias_name)
|
||||
, m_dict(dictionary)
|
||||
, m_vers(version)
|
||||
item_alias(std::string alias_name, std::string dictionary, std::string version)
|
||||
: m_name(std::move(alias_name))
|
||||
, m_dict(std::move(dictionary))
|
||||
, m_vers(std::move(version))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -327,11 +332,13 @@ struct item_validator
|
||||
return iequals(m_item_name, rhs.m_item_name);
|
||||
}
|
||||
|
||||
/// @brief Validate the value in @a value for this item
|
||||
/// Will throw a std::system_error exception if it fails
|
||||
void operator()(std::string_view value) const;
|
||||
/// @brief Validate value @a value, throws if invalid
|
||||
void validate_value(const item_value &value) const;
|
||||
|
||||
/// @brief A more gentle version of value validation
|
||||
/// @brief Validate value @a value and return potential error in @a ec
|
||||
bool validate_value(const item_value &value, std::error_code &ec) const noexcept;
|
||||
|
||||
/// @brief Validate value @a value and return potential error in @a ec
|
||||
bool validate_value(std::string_view value, std::error_code &ec) const noexcept;
|
||||
};
|
||||
|
||||
@@ -343,11 +350,11 @@ struct item_validator
|
||||
*/
|
||||
struct category_validator
|
||||
{
|
||||
std::string m_name; ///< The name of the category
|
||||
std::vector<std::string> m_keys; ///< The list of items that make up the key
|
||||
cif::iset m_groups; ///< The category groups this category belongs to
|
||||
cif::iset m_mandatory_items; ///< The mandatory items for this category
|
||||
std::set<item_validator> m_item_validators; ///< The item validators for the items in this category
|
||||
std::string m_name; ///< The name of the category
|
||||
std::vector<std::string> m_keys; ///< The list of items that make up the key
|
||||
cif::iset m_groups; ///< The category groups this category belongs to
|
||||
cif::iset m_mandatory_items; ///< The mandatory items for this category
|
||||
std::vector<item_validator> m_item_validators; ///< The item validators for the items in this category
|
||||
|
||||
/// @brief return true if this category sorts before @a rhs
|
||||
bool operator<(const category_validator &rhs) const
|
||||
@@ -359,10 +366,10 @@ struct category_validator
|
||||
void add_item_validator(item_validator &&v);
|
||||
|
||||
/// @brief Return the item_validator for item @a item_name, may return nullptr
|
||||
const item_validator *get_validator_for_item(std::string_view item_name) const;
|
||||
[[nodiscard]] const item_validator *get_validator_for_item(std::string_view item_name) const;
|
||||
|
||||
/// @brief Return the item_validator for an item that has as alias name @a item_name, may return nullptr
|
||||
const item_validator *get_validator_for_aliased_item(std::string_view item_name) const;
|
||||
[[nodiscard]] const item_validator *get_validator_for_aliased_item(std::string_view item_name) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -420,7 +427,7 @@ class validator
|
||||
/// @brief destructor
|
||||
~validator() = default;
|
||||
|
||||
validator(const validator &rhs);
|
||||
validator(const validator &rhs) = default;
|
||||
|
||||
/// @brief move constructor
|
||||
validator(validator &&rhs)
|
||||
@@ -447,22 +454,22 @@ class validator
|
||||
void add_type_validator(type_validator &&v);
|
||||
|
||||
/// @brief Return the type validator for @a type_code, may return nullptr
|
||||
const type_validator *get_validator_for_type(std::string_view type_code) const;
|
||||
[[nodiscard]] const type_validator *get_validator_for_type(std::string_view type_code) const;
|
||||
|
||||
/// @brief Add category_validator @a v to the list of category validators
|
||||
void add_category_validator(category_validator &&v);
|
||||
|
||||
/// @brief Return the category validator for @a category, may return nullptr
|
||||
const category_validator *get_validator_for_category(std::string_view category) const;
|
||||
[[nodiscard]] const category_validator *get_validator_for_category(std::string_view category) const;
|
||||
|
||||
/// @brief Add link_validator @a v to the list of link validators
|
||||
void add_link_validator(link_validator &&v);
|
||||
|
||||
/// @brief Return the list of link validators for which the parent is @a category
|
||||
std::vector<const link_validator *> get_links_for_parent(std::string_view category) const;
|
||||
[[nodiscard]] std::vector<const link_validator *> get_links_for_parent(std::string_view category) const;
|
||||
|
||||
/// @brief Return the list of link validators for which the child is @a category
|
||||
std::vector<const link_validator *> get_links_for_child(std::string_view category) const;
|
||||
[[nodiscard]] std::vector<const link_validator *> get_links_for_child(std::string_view category) const;
|
||||
|
||||
/// @brief Bottleneck function to report an error in validation
|
||||
void report_error(validation_error err, bool fatal = true) const
|
||||
@@ -489,14 +496,14 @@ class validator
|
||||
void fill_audit_conform(category &audit_conform) const;
|
||||
|
||||
/// @brief Return true if this validator matches @a audit_conform
|
||||
bool matches_audit_conform(const category &audit_conform) const;
|
||||
[[nodiscard]] bool matches_audit_conform(const category &audit_conform) const;
|
||||
|
||||
/// @brief Add info
|
||||
void append_audit_conform(const std::string &name, const std::optional<std::string> &version);
|
||||
|
||||
private:
|
||||
// name is fully qualified here:
|
||||
item_validator *get_validator_for_item(std::string_view name) const;
|
||||
[[nodiscard]] item_validator *get_validator_for_item(std::string_view name) const;
|
||||
|
||||
category m_audit_conform;
|
||||
|
||||
@@ -520,10 +527,18 @@ class validator_factory
|
||||
static validator_factory &instance();
|
||||
|
||||
/// @brief Return validator with info recorded in @a audit_conform
|
||||
const validator &get(const category &audit_conform);
|
||||
const validator *get(const category &audit_conform);
|
||||
|
||||
/// @brief Return the single-file validator with name @a dictionary_name
|
||||
const validator &get(std::string_view dictionary_name);
|
||||
/// and the dictionary name may be a set of dictionaries separated by comma
|
||||
const validator *get(std::string_view dictionary_name);
|
||||
|
||||
/// @brief Return validator with info recorded in @a audit_conform
|
||||
const validator &operator[](const category &audit_conform);
|
||||
|
||||
/// @brief Return the single-file validator with name @a dictionary_name
|
||||
/// and the dictionary name may be a set of dictionaries separated by comma
|
||||
const validator &operator[](std::string_view dictionary_name);
|
||||
|
||||
/// @brief Return true if the version @a found is equal or higher than @a expected for dictionary @a name
|
||||
static bool check_version(std::string_view name, std::string_view expected, std::string_view found);
|
||||
@@ -535,6 +550,21 @@ class validator_factory
|
||||
return m_validators.emplace_back(std::move(v));
|
||||
}
|
||||
|
||||
// #if __cplusplus >= 202302L
|
||||
// /// @brief Return validator with info recorded in @a audit_conform
|
||||
// static validator &operator[](const category &audit_conform)
|
||||
// {
|
||||
// return instance()[audit_conform];
|
||||
// }
|
||||
|
||||
// /// @brief Return the single-file validator with name @a dictionary_name
|
||||
// /// and the dictionary name may be a set of dictionaries separated by comma
|
||||
// static validator &operator[](std::string_view dict)
|
||||
// {
|
||||
// return instance()[dict];
|
||||
// }
|
||||
// #endif
|
||||
|
||||
private:
|
||||
validator_factory() = default;
|
||||
|
||||
|
||||
316
pcre2-simple/CMakeLists.txt
Normal file
316
pcre2-simple/CMakeLists.txt
Normal file
@@ -0,0 +1,316 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2025 Maarten L. Hekkelman
|
||||
#
|
||||
# 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.
|
||||
|
||||
# A simplified wrapper CMakeLists.txt file for PCRE2
|
||||
#
|
||||
# This will generate an OBJECT library so it can be linked into another library
|
||||
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
project(pcre2s VERSION 1.0.0 LANGUAGES C CXX)
|
||||
|
||||
# The original code:
|
||||
|
||||
file(DOWNLOAD https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.46/pcre2-10.46.tar.gz
|
||||
${CMAKE_CURRENT_BINARY_DIR}/pcre2-code.tgz
|
||||
EXPECTED_HASH SHA256=8d28d7f2c3b970c3a4bf3776bcbb5adfc923183ce74bc8df1ebaad8c1985bd07)
|
||||
file(ARCHIVE_EXTRACT INPUT ${CMAKE_CURRENT_BINARY_DIR}/pcre2-code.tgz
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(PCRE2_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/pcre2-10.46)
|
||||
set(PCRE2_MAJOR 10)
|
||||
set(PCRE2_MINOR 46)
|
||||
set(PCRE2_VERSION "${PCRE2_MAJOR}.${PCRE2_MINOR}")
|
||||
set(PCRE2_DATE "2024-06-09")
|
||||
|
||||
# Some needed configuration options
|
||||
|
||||
# option(PCRE2_BUILD_PCRE2_8 "Build 8 bit PCRE2 library" ON)
|
||||
# option(PCRE2_BUILD_PCRE2_16 "Build 16 bit PCRE2 library" OFF)
|
||||
# option(PCRE2_BUILD_PCRE2_32 "Build 32 bit PCRE2 library" OFF)
|
||||
|
||||
option(PCRE2_STATIC_PIC "Build the static library with the option position independent code enabled." OFF)
|
||||
|
||||
set(PCRE2_NEWLINE "LF" CACHE STRING "What to recognize as a newline (one of CR, LF, CRLF, ANY, ANYCRLF, NUL)." FORCE)
|
||||
set_property(CACHE PCRE2_NEWLINE PROPERTY STRINGS "CR" "LF" "CRLF" "ANY" "ANYCRLF" "NUL")
|
||||
|
||||
set(PCRE2_LINK_SIZE "2" CACHE STRING "Internal link size (2, 3 or 4 allowed). See LINK_SIZE in config.h.in for details.")
|
||||
set_property(CACHE PCRE2_LINK_SIZE PROPERTY STRINGS "2" "3" "4")
|
||||
|
||||
set(PCRE2_PARENS_NEST_LIMIT "250" CACHE STRING "Default nested parentheses limit. See PARENS_NEST_LIMIT in config.h.in for details.")
|
||||
set(PCRE2_HEAP_LIMIT "20000000" CACHE STRING "Default limit on heap memory (kibibytes). See HEAP_LIMIT in config.h.in for details.")
|
||||
set(PCRE2_MAX_VARLOOKBEHIND "255" CACHE STRING "Default limit on variable lookbehinds.")
|
||||
set(PCRE2_MATCH_LIMIT "10000000" CACHE STRING "Default limit on internal looping. See MATCH_LIMIT in config.h.in for details.")
|
||||
set(PCRE2_MATCH_LIMIT_DEPTH "MATCH_LIMIT" CACHE STRING "Default limit on internal depth of search. See MATCH_LIMIT_DEPTH in config.h.in for details.")
|
||||
set(PCRE2GREP_BUFSIZE "20480" CACHE STRING "Buffer starting size parameter for pcre2grep. See PCRE2GREP_BUFSIZE in config.h.in for details.")
|
||||
set(PCRE2GREP_MAX_BUFSIZE "1048576" CACHE STRING "Buffer maximum size parameter for pcre2grep. See PCRE2GREP_MAX_BUFSIZE in config.h.in for details.")
|
||||
set(PCRE2_SUPPORT_JIT OFF CACHE BOOL "Enable support for Just-in-time compiling.")
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES Linux|NetBSD)
|
||||
set(PCRE2_SUPPORT_JIT_SEALLOC OFF CACHE BOOL "Enable SELinux compatible execmem allocator in JIT (experimental).")
|
||||
else()
|
||||
set(PCRE2_SUPPORT_JIT_SEALLOC IGNORE)
|
||||
endif()
|
||||
|
||||
set(PCRE2GREP_SUPPORT_JIT ON CACHE BOOL "Enable use of Just-in-time compiling in pcre2grep.")
|
||||
set(PCRE2GREP_SUPPORT_CALLOUT ON CACHE BOOL "Enable callout string support in pcre2grep.")
|
||||
set(PCRE2GREP_SUPPORT_CALLOUT_FORK ON CACHE BOOL "Enable callout string fork support in pcre2grep.")
|
||||
set(PCRE2_SUPPORT_UNICODE ON CACHE BOOL "Enable support for Unicode and UTF-8/UTF-16/UTF-32 encoding.")
|
||||
set(PCRE2_SUPPORT_BSR_ANYCRLF OFF CACHE BOOL "ON=Backslash-R matches only LF CR and CRLF, OFF=Backslash-R matches all Unicode Linebreaks")
|
||||
set(PCRE2_NEVER_BACKSLASH_C OFF CACHE BOOL "If ON, backslash-C (upper case C) is locked out.")
|
||||
set(PCRE2_SUPPORT_VALGRIND OFF CACHE BOOL "Enable Valgrind support.")
|
||||
|
||||
if(MINGW)
|
||||
option(NON_STANDARD_LIB_PREFIX "ON=Shared libraries built in mingw will be named pcre2.dll, etc., instead of libpcre2.dll, etc." OFF)
|
||||
option(NON_STANDARD_LIB_SUFFIX "ON=Shared libraries built in mingw will be named libpcre2-0.dll, etc., instead of libpcre2.dll, etc." OFF)
|
||||
endif()
|
||||
|
||||
#
|
||||
|
||||
set(NEWLINE_DEFAULT "")
|
||||
|
||||
if(PCRE2_NEWLINE STREQUAL "CR")
|
||||
set(NEWLINE_DEFAULT "1")
|
||||
elseif(PCRE2_NEWLINE STREQUAL "LF")
|
||||
set(NEWLINE_DEFAULT "2")
|
||||
elseif(PCRE2_NEWLINE STREQUAL "CRLF")
|
||||
set(NEWLINE_DEFAULT "3")
|
||||
elseif(PCRE2_NEWLINE STREQUAL "ANY")
|
||||
set(NEWLINE_DEFAULT "4")
|
||||
elseif(PCRE2_NEWLINE STREQUAL "ANYCRLF")
|
||||
set(NEWLINE_DEFAULT "5")
|
||||
elseif(PCRE2_NEWLINE STREQUAL "NUL")
|
||||
set(NEWLINE_DEFAULT "6")
|
||||
else()
|
||||
message(FATAL_ERROR "The PCRE2_NEWLINE variable must be set to one of the following values: \"LF\", \"CR\", \"CRLF\", \"ANY\", \"ANYCRLF\".")
|
||||
endif()
|
||||
|
||||
# Some tests
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckSymbolExists)
|
||||
include(CheckIncludeFile)
|
||||
|
||||
check_include_file(assert.h HAVE_ASSERT_H)
|
||||
check_include_file(dirent.h HAVE_DIRENT_H)
|
||||
check_include_file(sys/stat.h HAVE_SYS_STAT_H)
|
||||
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
check_include_file(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
check_symbol_exists(bcopy "strings.h" HAVE_BCOPY)
|
||||
check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE)
|
||||
check_symbol_exists(memmove "string.h" HAVE_MEMMOVE)
|
||||
check_symbol_exists(secure_getenv "stdlib.h" HAVE_SECURE_GETENV)
|
||||
check_symbol_exists(strerror "string.h" HAVE_STRERROR)
|
||||
|
||||
check_c_source_compiles(
|
||||
"int main(void) { char buf[128] __attribute__((uninitialized)); (void)buf; return 0; }"
|
||||
HAVE_ATTRIBUTE_UNINITIALIZED
|
||||
)
|
||||
|
||||
check_c_source_compiles(
|
||||
[=[
|
||||
extern __attribute__ ((visibility ("default"))) int f(void);
|
||||
int main(void) { return f(); }
|
||||
int f(void) { return 42; }
|
||||
]=]
|
||||
HAVE_VISIBILITY
|
||||
)
|
||||
|
||||
if(HAVE_VISIBILITY)
|
||||
set(PCRE2_EXPORT [=[__attribute__ ((visibility ("default")))]=])
|
||||
else()
|
||||
set(PCRE2_EXPORT)
|
||||
endif()
|
||||
|
||||
check_c_source_compiles("int main(void) { __assume(1); return 0; }" HAVE_BUILTIN_ASSUME)
|
||||
|
||||
check_c_source_compiles(
|
||||
[=[
|
||||
#include <stddef.h>
|
||||
int main(void) { int a,b; size_t m; __builtin_mul_overflow(a,b,&m); return 0; }
|
||||
]=]
|
||||
HAVE_BUILTIN_MUL_OVERFLOW
|
||||
)
|
||||
|
||||
check_c_source_compiles(
|
||||
"int main(int c, char *v[]) { if (c) __builtin_unreachable(); return (int)(*v[0]); }"
|
||||
HAVE_BUILTIN_UNREACHABLE
|
||||
)
|
||||
|
||||
# # Check whether Intel CET is enabled, and if so, adjust compiler flags. This
|
||||
# # code was written by PH, trying to imitate the logic from the autotools
|
||||
# # configuration.
|
||||
|
||||
# check_c_source_compiles(
|
||||
# [=[
|
||||
# #ifndef __CET__
|
||||
# #error CET is not enabled
|
||||
# #endif
|
||||
# int main() { return 0; }
|
||||
# ]=]
|
||||
# INTEL_CET_ENABLED
|
||||
# )
|
||||
|
||||
# if(INTEL_CET_ENABLED)
|
||||
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mshstk")
|
||||
# endif()
|
||||
|
||||
# Set up some dependencies first
|
||||
|
||||
configure_file(
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_chartables.c.dist
|
||||
${CMAKE_CURRENT_BINARY_DIR}/pcre2_chartables.c
|
||||
COPYONLY
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${PCRE2_SOURCE_DIR}/config-cmake.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/interface/config.h
|
||||
@ONLY
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/interface/pcre2.h
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Define our library
|
||||
|
||||
list(APPEND PCRE2_HEADERS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/interface/pcre2.h)
|
||||
|
||||
list(APPEND PCRE2_SOURCES
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_auto_possess.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/pcre2_chartables.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_chkdint.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_compile.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_compile_class.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_config.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_context.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_convert.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_dfa_match.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_error.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_extuni.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_find_bracket.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_jit_compile.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_maketables.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_match.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_match_data.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_newline.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_ord2utf.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_pattern_info.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_script_run.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_serialize.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_string_utils.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_study.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_substitute.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_substring.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_tables.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_ucd.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_valid_utf.c
|
||||
${PCRE2_SOURCE_DIR}/src/pcre2_xclass.c
|
||||
)
|
||||
|
||||
add_library(pcre2s OBJECT)
|
||||
|
||||
target_sources(pcre2s
|
||||
PRIVATE ${PCRE2_SOURCES}
|
||||
PUBLIC
|
||||
FILE_SET pcre2_headers TYPE HEADERS
|
||||
BASE_DIRS ${PCRE2_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/interface
|
||||
FILES ${PCRE2_HEADERS}
|
||||
)
|
||||
|
||||
target_compile_definitions(pcre2s PUBLIC PCRE2_CODE_UNIT_WIDTH=8 HAVE_CONFIG_H)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(pcre2s PUBLIC PCRE2_STATIC)
|
||||
endif()
|
||||
|
||||
target_include_directories(pcre2s PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/interface ${PCRE2_SOURCE_DIR}/src)
|
||||
|
||||
if(PCRE2_STATIC_PIC)
|
||||
set_target_properties(pcre2s PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
endif()
|
||||
|
||||
# # Installation and config files
|
||||
|
||||
# include(CMakePackageConfigHelpers)
|
||||
# include(GenerateExportHeader)
|
||||
|
||||
# # Install rules
|
||||
# install(TARGETS pcre2s
|
||||
# EXPORT pcre2s
|
||||
# FILE_SET pcre2_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
# if(MSVC AND BUILD_SHARED_LIBS)
|
||||
# install(
|
||||
# FILES $<TARGET_PDB_FILE:pcre2s>
|
||||
# DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
# OPTIONAL)
|
||||
# endif()
|
||||
|
||||
# install(EXPORT pcre2s
|
||||
# NAMESPACE pcre2s::
|
||||
# FILE "pcre2s-targets.cmake"
|
||||
# DESTINATION lib/cmake/pcre2s)
|
||||
|
||||
# configure_package_config_file(
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/pcre2s-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config.cmake
|
||||
# INSTALL_DESTINATION lib/cmake/pcre2s)
|
||||
|
||||
# install(
|
||||
# FILES "${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config.cmake"
|
||||
# "${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config-version.cmake"
|
||||
# DESTINATION lib/cmake/pcre2s)
|
||||
|
||||
# set_target_properties(
|
||||
# pcre2s
|
||||
# PROPERTIES VERSION ${PCRE2_VERSION}
|
||||
# SOVERSION ${PCRE2_VERSION}
|
||||
# INTERFACE_pcre2s_MAJOR_VERSION ${PCRE2_MAJOR})
|
||||
|
||||
# set_property(
|
||||
# TARGET pcre2s
|
||||
# APPEND
|
||||
# PROPERTY COMPATIBLE_INTERFACE_STRING pcre2s_MAJOR_VERSION)
|
||||
|
||||
# write_basic_package_version_file(
|
||||
# "${CMAKE_CURRENT_BINARY_DIR}/pcre2s/pcre2s-config-version.cmake"
|
||||
# VERSION "${PCRE2_VERSION}"
|
||||
# COMPATIBILITY AnyNewerVersion)
|
||||
|
||||
# # Testing
|
||||
|
||||
# if(PROJECT_IS_TOP_LEVEL)
|
||||
# include(CTest)
|
||||
|
||||
# if(BUILD_TESTING)
|
||||
# add_subdirectory(test)
|
||||
# endif()
|
||||
# endif()
|
||||
@@ -24,6 +24,8 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN(modernize-use-std-numbers)
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "cif++.hpp"
|
||||
@@ -34,7 +36,7 @@ namespace cif
|
||||
namespace data
|
||||
{
|
||||
|
||||
const atom_type_info kKnownAtoms[] =
|
||||
const atom_type_info kKnownAtoms[] = // NOLINT(bugprone-throwing-static-initialization,cert-err58-cpp)
|
||||
{
|
||||
{ Nn, "Unknown", "Nn", 0, false, { kNA, kNA, kNA, kNA, kNA, kNA, kNA } }, // 0 Nn Unknown
|
||||
{ H, "Hydrogen", "H", 1.008f, false, { 53, 25, 37, 32, kNA, kNA, 120 } }, // 1 H Hydrogen
|
||||
@@ -1111,7 +1113,7 @@ auto atom_type_traits::wksf(int charge) const -> const SFData&
|
||||
{
|
||||
// Oops, not found. Fall back to zero charge and see if we can use that
|
||||
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "No scattering factor found for " << name() << " with charge " << charge << " will try to fall back to zero charge...\n";
|
||||
|
||||
for (auto& sf: data::kWKSFData)
|
||||
@@ -1180,3 +1182,4 @@ float atom_type_traits::effective_ionic_radius(int charge) const
|
||||
}
|
||||
|
||||
}
|
||||
// NOLINTEND(modernize-use-std-numbers)
|
||||
|
||||
977
src/category.cpp
977
src/category.cpp
File diff suppressed because it is too large
Load Diff
187
src/compound.cpp
187
src/compound.cpp
@@ -24,19 +24,41 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/compound.hpp" // for compound_atom, compound_bond, compoun...
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <shared_mutex>
|
||||
#include "cif++/atom_type.hpp" // for atom_type_traits
|
||||
#include "cif++/category.hpp" // for category
|
||||
#include "cif++/datablock.hpp" // for datablock
|
||||
#include "cif++/file.hpp" // for file
|
||||
#include "cif++/parser.hpp" // for parser
|
||||
#include "cif++/point.hpp" // for distance, point
|
||||
#include "cif++/row.hpp" // for tie, row_initializer, tie_wrap
|
||||
#include "cif++/text.hpp" // for iequals, replace_all, iset
|
||||
#include "cif++/utilities.hpp" // for load_resource, VERBOSE, colour_type
|
||||
|
||||
#include <algorithm> // for find_if
|
||||
#include <cstddef> // for size_t
|
||||
#include <exception> // for exception, throw_with_nested
|
||||
#include <filesystem> // for path, exists
|
||||
#include <fstream> // for char_traits, basic_ostream, operator<<
|
||||
#include <iomanip> // for operator<<, quoted
|
||||
#include <iostream> // for clog, cout, cerr
|
||||
#include <limits> // for numeric_limits
|
||||
#include <map> // for allocator, map, _Rb_tree_iterator
|
||||
#include <memory> // for shared_ptr, unique_ptr, __shared_ptr_...
|
||||
#include <optional> // for optional
|
||||
#include <shared_mutex> // for shared_lock, shared_timed_mutex
|
||||
#include <stdexcept> // for runtime_error, invalid_argument, out_...
|
||||
#include <string> // for basic_string, string, operator==, ope...
|
||||
#include <string_view> // for string_view, basic_string_view
|
||||
#include <utility> // for pair, exchange, move
|
||||
#include <vector> // for vector
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace cif
|
||||
{
|
||||
using std::shared_ptr;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -140,7 +162,7 @@ compound::compound(cif::datablock &db)
|
||||
|
||||
cif::tie(m_id, m_name, m_type, m_formula, m_formula_weight, m_formal_charge, one_letter_code, m_parent_id) =
|
||||
chemComp.front().get("id", "name", "type", "formula", "formula_weight", "pdbx_formal_charge", "one_letter_code", "mon_nstd_parent_comp_id");
|
||||
|
||||
|
||||
if (one_letter_code.length() == 1)
|
||||
m_one_letter_code = one_letter_code.front();
|
||||
|
||||
@@ -152,14 +174,21 @@ compound::compound(cif::datablock &db)
|
||||
{
|
||||
compound_atom atom;
|
||||
std::string type_symbol, stereo_config;
|
||||
cif::tie(atom.id, type_symbol, atom.charge, atom.aromatic, atom.leaving_atom, stereo_config, atom.x, atom.y, atom.z) =
|
||||
|
||||
std::string aromaticFlag, leavingAtomFlag;
|
||||
|
||||
cif::tie(atom.id, type_symbol, atom.charge, aromaticFlag, leavingAtomFlag, stereo_config, atom.x, atom.y, atom.z) =
|
||||
row.get("atom_id", "type_symbol", "charge", "pdbx_aromatic_flag", "pdbx_leaving_atom_flag", "pdbx_stereo_config",
|
||||
"model_Cartn_x", "model_Cartn_y", "model_Cartn_z");
|
||||
|
||||
atom.aromatic = iequals(aromaticFlag, "Y");
|
||||
atom.leaving_atom = iequals(leavingAtomFlag, "Y");
|
||||
|
||||
atom.type_symbol = atom_type_traits(type_symbol).type();
|
||||
if (stereo_config.empty())
|
||||
atom.stereo_config = stereo_config_type::N;
|
||||
else
|
||||
atom.stereo_config = parse_stereo_config_from_string(stereo_config);
|
||||
atom.stereo_config = parse_stereo_config_from_string(stereo_config);
|
||||
m_atoms.push_back(std::move(atom));
|
||||
}
|
||||
|
||||
@@ -167,12 +196,17 @@ compound::compound(cif::datablock &db)
|
||||
for (auto row : chemCompBond)
|
||||
{
|
||||
compound_bond bond;
|
||||
std::string valueOrder;
|
||||
cif::tie(bond.atom_id[0], bond.atom_id[1], valueOrder, bond.aromatic, bond.stereo_config) = row.get("atom_id_1", "atom_id_2", "value_order", "pdbx_aromatic_flag", "pdbx_stereo_config");
|
||||
std::string valueOrder, aromaticFlag, stereoConfigFlag;
|
||||
|
||||
cif::tie(bond.atom_id[0], bond.atom_id[1], valueOrder, aromaticFlag, stereoConfigFlag) = row.get("atom_id_1", "atom_id_2", "value_order", "pdbx_aromatic_flag", "pdbx_stereo_config");
|
||||
|
||||
bond.aromatic = iequals(aromaticFlag, "Y");
|
||||
bond.stereo_config = iequals(stereoConfigFlag, "Y");
|
||||
|
||||
if (valueOrder.empty())
|
||||
bond.type = bond_type::sing;
|
||||
else
|
||||
bond.type = parse_bond_type_from_string(valueOrder);
|
||||
bond.type = parse_bond_type_from_string(valueOrder);
|
||||
m_bonds.push_back(std::move(bond));
|
||||
}
|
||||
}
|
||||
@@ -197,7 +231,7 @@ compound_atom compound::get_atom_by_atom_id(const std::string &atom_id) const
|
||||
|
||||
bool compound::atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const
|
||||
{
|
||||
auto i = find_if(m_bonds.begin(), m_bonds.end(),
|
||||
auto i = std::ranges::find_if(m_bonds,
|
||||
[&](const compound_bond &b)
|
||||
{
|
||||
return (b.atom_id[0] == atomId_1 and b.atom_id[1] == atomId_2) or (b.atom_id[0] == atomId_2 and b.atom_id[1] == atomId_1);
|
||||
@@ -208,7 +242,7 @@ bool compound::atoms_bonded(const std::string &atomId_1, const std::string &atom
|
||||
|
||||
float compound::bond_length(const std::string &atomId_1, const std::string &atomId_2) const
|
||||
{
|
||||
auto i = find_if(m_bonds.begin(), m_bonds.end(),
|
||||
auto i = std::ranges::find_if(m_bonds,
|
||||
[&](const compound_bond &b)
|
||||
{
|
||||
return (b.atom_id[0] == atomId_1 and b.atom_id[1] == atomId_2) or (b.atom_id[0] == atomId_2 and b.atom_id[1] == atomId_1);
|
||||
@@ -231,18 +265,18 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom
|
||||
|
||||
bool compound::is_peptide() const
|
||||
{
|
||||
return iequals(m_type, "l-peptide linking") or iequals(m_type, "peptide linking");
|
||||
return iequals(m_type, "l-peptide linking") or iequals(m_type, "peptide linking");
|
||||
}
|
||||
|
||||
bool compound::is_base() const
|
||||
{
|
||||
return iequals(m_type, "dna linking") or iequals(m_type, "rna linking");
|
||||
return iequals(m_type, "dna linking") or iequals(m_type, "rna linking");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// known amino acids and bases
|
||||
|
||||
const std::map<std::string, char> compound_factory::kAAMap{
|
||||
const std::map<std::string, char> compound_factory::kAAMap{ // NOLINT(bugprone-throwing-static-initialization,cert-err58-cpp)
|
||||
{ "ALA", 'A' },
|
||||
{ "ARG", 'R' },
|
||||
{ "ASN", 'N' },
|
||||
@@ -267,7 +301,7 @@ const std::map<std::string, char> compound_factory::kAAMap{
|
||||
{ "ASX", 'B' }
|
||||
};
|
||||
|
||||
const std::map<std::string, char> compound_factory::kBaseMap{
|
||||
const std::map<std::string, char> compound_factory::kBaseMap{ // NOLINT(bugprone-throwing-static-initialization,cert-err58-cpp)
|
||||
{ "A", 'A' },
|
||||
{ "C", 'C' },
|
||||
{ "G", 'G' },
|
||||
@@ -285,21 +319,40 @@ const std::map<std::string, char> compound_factory::kBaseMap{
|
||||
class compound_factory_impl : public std::enable_shared_from_this<compound_factory_impl>
|
||||
{
|
||||
public:
|
||||
compound_factory_impl();
|
||||
compound_factory_impl() = default;
|
||||
compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next);
|
||||
|
||||
virtual ~compound_factory_impl()
|
||||
virtual ~compound_factory_impl() // NOLINT(modernize-use-equals-default)
|
||||
{
|
||||
for (auto c : m_compounds)
|
||||
delete c;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool exists_self(const std::string &id) const
|
||||
{
|
||||
if (m_missing.contains(id))
|
||||
return false;
|
||||
|
||||
if (std::ranges::find_if(m_compounds, [id](compound *c)
|
||||
{ return c->id() == id; }) != m_compounds.end())
|
||||
return true;
|
||||
|
||||
return m_next and m_next->exists_self(id);
|
||||
}
|
||||
|
||||
bool exists(std::string_view id)
|
||||
{
|
||||
std::shared_lock lock(mMutex);
|
||||
|
||||
return exists_self(std::string{ id });
|
||||
}
|
||||
|
||||
compound *get(std::string id)
|
||||
{
|
||||
std::shared_lock lock(mMutex);
|
||||
|
||||
compound *result = nullptr;
|
||||
|
||||
|
||||
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
|
||||
{
|
||||
result = impl->create(id);
|
||||
@@ -341,12 +394,8 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
|
||||
std::shared_ptr<compound_factory_impl> m_next;
|
||||
};
|
||||
|
||||
compound_factory_impl::compound_factory_impl()
|
||||
{
|
||||
}
|
||||
|
||||
compound_factory_impl::compound_factory_impl(std::shared_ptr<compound_factory_impl> next)
|
||||
: m_next(next)
|
||||
: m_next(std::move(next))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -363,7 +412,9 @@ compound *compound_factory_impl::create(const std::string &id)
|
||||
if (m_missing.contains(id))
|
||||
return nullptr;
|
||||
|
||||
if (auto i = find_if(m_compounds.begin(), m_compounds.end(), [id](compound *c) { return c->id() == id; }); i != m_compounds.end())
|
||||
if (auto i = std::ranges::find_if(m_compounds, [id](compound *c)
|
||||
{ return c->id() == id; });
|
||||
i != m_compounds.end())
|
||||
return *i;
|
||||
|
||||
compound *result = nullptr;
|
||||
@@ -380,13 +431,13 @@ compound *compound_factory_impl::create(const std::string &id)
|
||||
}
|
||||
}
|
||||
else
|
||||
ccd.reset(new std::ifstream(m_file));
|
||||
ccd = std::make_unique<std::ifstream>(m_file);
|
||||
|
||||
cif::file file;
|
||||
|
||||
if (m_index.empty())
|
||||
{
|
||||
if (cif::VERBOSE > 1)
|
||||
if (VERBOSE > 1)
|
||||
{
|
||||
std::cout << "Creating component index "
|
||||
<< "...";
|
||||
@@ -396,7 +447,7 @@ compound *compound_factory_impl::create(const std::string &id)
|
||||
cif::parser parser(*ccd, file);
|
||||
m_index = parser.index_datablocks();
|
||||
|
||||
if (cif::VERBOSE > 1)
|
||||
if (VERBOSE > 1)
|
||||
std::cout << " done\n";
|
||||
|
||||
// reload the resource, perhaps this should be improved...
|
||||
@@ -407,10 +458,10 @@ compound *compound_factory_impl::create(const std::string &id)
|
||||
throw std::runtime_error("Could not locate the CCD components.cif file, please make sure the software is installed properly and/or use the update-libcifpp-data to fetch the data.");
|
||||
}
|
||||
else
|
||||
ccd.reset(new std::ifstream(m_file));
|
||||
ccd = std::make_unique<std::ifstream>(m_file);
|
||||
}
|
||||
|
||||
if (cif::VERBOSE > 1)
|
||||
if (VERBOSE > 1)
|
||||
{
|
||||
std::cout << "Loading component " << id << "...";
|
||||
std::cout.flush();
|
||||
@@ -419,7 +470,7 @@ compound *compound_factory_impl::create(const std::string &id)
|
||||
cif::parser parser(*ccd, file);
|
||||
parser.parse_single_datablock(id, m_index);
|
||||
|
||||
if (cif::VERBOSE > 1)
|
||||
if (VERBOSE > 1)
|
||||
std::cout << " done\n";
|
||||
|
||||
if (not file.empty())
|
||||
@@ -445,16 +496,15 @@ compound *compound_factory_impl::create(const std::string &id)
|
||||
class local_compound_factory_impl : public compound_factory_impl
|
||||
{
|
||||
public:
|
||||
local_compound_factory_impl(const cif::file &file, std::shared_ptr<compound_factory_impl> next)
|
||||
local_compound_factory_impl(cif::file file, shared_ptr<compound_factory_impl> next)
|
||||
: compound_factory_impl(next)
|
||||
, m_local_file(file)
|
||||
, m_local_file(std::move(file))
|
||||
{
|
||||
}
|
||||
|
||||
compound *create(const std::string &id) override;
|
||||
|
||||
private:
|
||||
|
||||
compound *construct_compound(const datablock &db, const std::string &id, const std::string &name, const std::string &three_letter_code, const std::string &group);
|
||||
|
||||
cif::file m_local_file;
|
||||
@@ -465,7 +515,9 @@ compound *local_compound_factory_impl::create(const std::string &id)
|
||||
if (m_missing.contains(id))
|
||||
return nullptr;
|
||||
|
||||
if (auto i = find_if(m_compounds.begin(), m_compounds.end(), [id](compound *c) { return c->id() == id; }); i != m_compounds.end())
|
||||
if (auto i = std::ranges::find_if(m_compounds, [id](compound *c)
|
||||
{ return c->id() == id; });
|
||||
i != m_compounds.end())
|
||||
return *i;
|
||||
|
||||
compound *result = nullptr;
|
||||
@@ -510,12 +562,10 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
|
||||
float formula_weight = 0;
|
||||
int formal_charge = 0;
|
||||
std::map<std::string,std::size_t> formula_data;
|
||||
std::map<std::string, std::size_t> formula_data;
|
||||
|
||||
for (std::size_t ord = 1; const auto &[atom_id, type_symbol, type, charge, x, y, z, xi, yi, zi] :
|
||||
rdb["chem_comp_atom"].rows<std::string, std::string, std::string, int,
|
||||
std::optional<float>, std::optional<float>, std::optional<float>,
|
||||
std::optional<float>, std::optional<float>, std::optional<float>>(
|
||||
rdb["chem_comp_atom"].rows<std::string, std::string, std::string, int, std::optional<float>, std::optional<float>, std::optional<float>, std::optional<float>, std::optional<float>, std::optional<float>>(
|
||||
"atom_id", "type_symbol", "type", "charge",
|
||||
"model_Cartn_x", "model_Cartn_y", "model_Cartn_z",
|
||||
"pdbx_model_Cartn_x_ideal", "pdbx_model_Cartn_y_ideal", "pdbx_model_Cartn_z_ideal"))
|
||||
@@ -525,22 +575,20 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
|
||||
formula_data[type_symbol] += 1;
|
||||
|
||||
db["chem_comp_atom"].emplace({
|
||||
{ "comp_id", id },
|
||||
db["chem_comp_atom"].emplace({ { "comp_id", id },
|
||||
{ "atom_id", atom_id },
|
||||
{ "type_symbol", type_symbol },
|
||||
{ "charge", charge },
|
||||
{ "model_Cartn_x", x.has_value() ? x : xi, 3 },
|
||||
{ "model_Cartn_y", y.has_value() ? y : yi, 3 },
|
||||
{ "model_Cartn_z", z.has_value() ? z : zi, 3 },
|
||||
{ "pdbx_ordinal", ord++ }
|
||||
});
|
||||
{ "model_Cartn_x", x.has_value() ? x : xi/* , 3 */ },
|
||||
{ "model_Cartn_y", y.has_value() ? y : yi/* , 3 */ },
|
||||
{ "model_Cartn_z", z.has_value() ? z : zi/* , 3 */ },
|
||||
{ "pdbx_ordinal", ord++ } });
|
||||
|
||||
formal_charge += charge;
|
||||
}
|
||||
|
||||
for (std::size_t ord = 1; const auto &[atom_id_1, atom_id_2, type, aromatic] :
|
||||
rdb["chem_comp_bond"].rows<std::string, std::string, std::string, bool>("atom_id_1", "atom_id_2", "type", "aromatic"))
|
||||
rdb["chem_comp_bond"].rows<std::string, std::string, std::string, std::string>("atom_id_1", "atom_id_2", "type", "aromatic"))
|
||||
{
|
||||
std::string value_order("SING");
|
||||
|
||||
@@ -551,21 +599,19 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
else if (cif::iequals(type, "triple") or cif::iequals(type, "trip"))
|
||||
value_order = "TRIP";
|
||||
|
||||
db["chem_comp_bond"].emplace({
|
||||
{ "comp_id", id },
|
||||
db["chem_comp_bond"].emplace({ { "comp_id", id },
|
||||
{ "atom_id_1", atom_id_1 },
|
||||
{ "atom_id_2", atom_id_2 },
|
||||
{ "value_order", value_order },
|
||||
{ "pdbx_aromatic_flag", aromatic },
|
||||
// TODO: fetch stereo_config info from chem_comp_chir
|
||||
{ "pdbx_ordinal", ord++ }
|
||||
});
|
||||
{ "pdbx_ordinal", ord++ } });
|
||||
}
|
||||
|
||||
db.emplace_back(rdb["pdbx_chem_comp_descriptor"]);
|
||||
|
||||
std::string formula;
|
||||
for (bool first = true; const auto &[symbol, count]: formula_data)
|
||||
for (bool first = true; const auto &[symbol, count] : formula_data)
|
||||
{
|
||||
if (std::exchange(first, false))
|
||||
formula += ' ';
|
||||
@@ -584,15 +630,13 @@ compound *local_compound_factory_impl::construct_compound(const datablock &rdb,
|
||||
else
|
||||
type = "NON-POLYMER";
|
||||
|
||||
db["chem_comp"].emplace({
|
||||
{ "id", id },
|
||||
db["chem_comp"].emplace({ { "id", id },
|
||||
{ "name", name },
|
||||
{ "type", type },
|
||||
{ "formula", formula },
|
||||
{ "pdbx_formal_charge", formal_charge },
|
||||
{ "formula_weight", formula_weight },
|
||||
{ "three_letter_code", three_letter_code }
|
||||
});
|
||||
{ "formula_weight", { formula_weight, 3 } },
|
||||
{ "three_letter_code", three_letter_code } });
|
||||
|
||||
std::shared_lock lock(mMutex);
|
||||
|
||||
@@ -618,14 +662,10 @@ compound_factory::compound_factory()
|
||||
auto ccd = cif::load_resource("components.cif");
|
||||
if (ccd)
|
||||
m_impl = std::make_shared<compound_factory_impl>();
|
||||
else if (cif::VERBOSE > 0)
|
||||
else if (VERBOSE > 0)
|
||||
std::cerr << "CCD components.cif resource was not found\n";
|
||||
}
|
||||
|
||||
compound_factory::~compound_factory()
|
||||
{
|
||||
}
|
||||
|
||||
compound_factory &compound_factory::instance()
|
||||
{
|
||||
if (s_use_thread_local_instance)
|
||||
@@ -657,7 +697,7 @@ void compound_factory::set_default_dictionary(const fs::path &inDictFile)
|
||||
|
||||
try
|
||||
{
|
||||
m_impl.reset(new compound_factory_impl(inDictFile, m_impl));
|
||||
m_impl = std::make_shared<compound_factory_impl>(inDictFile, m_impl);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
@@ -672,7 +712,7 @@ void compound_factory::push_dictionary(const fs::path &inDictFile)
|
||||
|
||||
try
|
||||
{
|
||||
m_impl.reset(new compound_factory_impl(inDictFile, m_impl));
|
||||
m_impl = std::make_shared<compound_factory_impl>(inDictFile, m_impl);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
@@ -684,7 +724,7 @@ void compound_factory::push_dictionary(const cif::file &inDictFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_impl.reset(new local_compound_factory_impl(inDictFile, m_impl));
|
||||
m_impl = std::make_shared<local_compound_factory_impl>(inDictFile, m_impl);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
@@ -698,6 +738,11 @@ void compound_factory::pop_dictionary()
|
||||
m_impl = m_impl->next();
|
||||
}
|
||||
|
||||
bool compound_factory::exists(std::string_view id) const
|
||||
{
|
||||
return m_impl and m_impl->exists(id);
|
||||
}
|
||||
|
||||
const compound *compound_factory::create(std::string_view id)
|
||||
{
|
||||
auto result = m_impl ? m_impl->get(std::string{ id }) : nullptr;
|
||||
@@ -722,7 +767,7 @@ bool compound_factory::is_peptide(std::string_view res_name) const
|
||||
bool result = is_std_peptide(res_name);
|
||||
if (not result and m_impl)
|
||||
{
|
||||
auto compound = const_cast<compound_factory&>(*this).create(res_name);
|
||||
auto compound = const_cast<compound_factory &>(*this).create(res_name);
|
||||
result = compound != nullptr and compound->is_peptide();
|
||||
}
|
||||
return result;
|
||||
@@ -734,7 +779,7 @@ bool compound_factory::is_base(std::string_view res_name) const
|
||||
bool result = is_std_base(res_name);
|
||||
if (not result and m_impl)
|
||||
{
|
||||
auto compound = const_cast<compound_factory&>(*this).create(res_name);
|
||||
auto compound = const_cast<compound_factory &>(*this).create(res_name);
|
||||
result = compound != nullptr and compound->is_base();
|
||||
}
|
||||
return result;
|
||||
@@ -784,7 +829,7 @@ void compound_factory::report_missing_compound(std::string_view compound_id)
|
||||
<< "in /var/cache/libcifpp using the following commands:\n\n"
|
||||
<< "curl -o " << CACHE_DIR << "/components.cif https://files.wwpdb.org/pub/pdb/data/monomers/components.cif\n"
|
||||
<< "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic\n"
|
||||
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n";
|
||||
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic\n\n";
|
||||
#endif
|
||||
|
||||
if (m_impl)
|
||||
|
||||
@@ -25,9 +25,12 @@
|
||||
*/
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
@@ -36,9 +39,13 @@ iset get_category_items(const category &cat)
|
||||
return cat.key_items();
|
||||
}
|
||||
|
||||
uint16_t get_item_ix(const category &cat, std::string_view col)
|
||||
std::optional<uint16_t> get_item_ix(const category &cat, std::string_view col)
|
||||
{
|
||||
return cat.get_item_ix(col);
|
||||
auto ix = cat.get_item_ix(col);
|
||||
std::optional<uint16_t> result;
|
||||
if (ix < cat.get_item_count())
|
||||
result = ix;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_item_type_uchar(const category &cat, std::string_view col)
|
||||
@@ -75,7 +82,7 @@ namespace detail
|
||||
// return this;
|
||||
// }
|
||||
//
|
||||
// bool test(row_handle r) const override
|
||||
// bool test(const_row_handle r) const override
|
||||
// {
|
||||
// return m_single_hit == r;
|
||||
// }
|
||||
@@ -110,32 +117,24 @@ namespace detail
|
||||
|
||||
condition_impl *key_equals_condition_impl::prepare(const category &c)
|
||||
{
|
||||
m_item_ix = c.get_item_ix(m_item_name);
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
|
||||
if (c.get_cat_validator() != nullptr and
|
||||
c.key_item_indices().contains(m_item_ix) and
|
||||
c.key_item_indices().size() == 1)
|
||||
condition_impl *result = nullptr;
|
||||
|
||||
if (auto ix = get_item_ix(c, m_item_name); ix.has_value())
|
||||
{
|
||||
m_single_hit = c[{ { m_item_name, m_value } }];
|
||||
m_item_ix = *ix;
|
||||
m_icase = is_item_type_uchar(c, m_item_name);
|
||||
|
||||
if (c.get_cat_validator() != nullptr and
|
||||
c.key_item_indices().contains(m_item_ix) and
|
||||
c.key_item_indices().size() == 1)
|
||||
{
|
||||
m_single_hit = c[{ { m_item_name, m_value } }];
|
||||
}
|
||||
|
||||
result = this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
condition_impl *key_equals_number_condition_impl::prepare(const category &c)
|
||||
{
|
||||
m_item_ix = c.get_item_ix(m_item_name);
|
||||
|
||||
if (c.get_cat_validator() != nullptr and
|
||||
c.key_item_indices().contains(m_item_ix) and
|
||||
c.key_item_indices().size() == 1)
|
||||
{
|
||||
item v(m_item_name, m_value);
|
||||
m_single_hit = c[{ { m_item_name, std::string{ v.value() }, false } }];
|
||||
}
|
||||
|
||||
return this;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool found_in_range(condition_impl *c, std::vector<and_condition_impl *>::iterator b, std::vector<and_condition_impl *>::iterator e)
|
||||
@@ -146,7 +145,7 @@ namespace detail
|
||||
{
|
||||
auto &cs = (*s)->m_sub;
|
||||
|
||||
if (find_if(cs.begin(), cs.end(), [c](const condition_impl *i)
|
||||
if (std::ranges::find_if(cs, [c](const condition_impl *i)
|
||||
{ return i->equals(c); }) == cs.end())
|
||||
{
|
||||
result = false;
|
||||
@@ -177,7 +176,7 @@ namespace detail
|
||||
and_result = new and_condition_impl();
|
||||
|
||||
and_result->m_sub.push_back(c);
|
||||
fc.erase(fc.begin() + fc_i);
|
||||
fc.erase(fc.begin() + static_cast<std::string::difference_type>(fc_i));
|
||||
|
||||
for (auto sub : subs)
|
||||
{
|
||||
@@ -192,7 +191,7 @@ namespace detail
|
||||
continue;
|
||||
}
|
||||
|
||||
ssub.erase(ssub.begin() + ssub_i);
|
||||
ssub.erase(ssub.begin() + static_cast<std::string::difference_type>(ssub_i));
|
||||
delete sc;
|
||||
break;
|
||||
}
|
||||
@@ -211,7 +210,10 @@ namespace detail
|
||||
condition_impl *and_condition_impl::prepare(const category &c)
|
||||
{
|
||||
for (auto &sub : m_sub)
|
||||
sub = sub->prepare(c);
|
||||
{
|
||||
if (sub->prepare(c) == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto cv = c.get_cat_validator(); cv != nullptr)
|
||||
{
|
||||
@@ -234,17 +236,6 @@ namespace detail
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto s = dynamic_cast<const key_equals_number_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
{
|
||||
item v{ s->m_item_name, s->m_value };
|
||||
lookup.emplace_back(s->m_item_name, std::string{ v.value() } );
|
||||
subs.emplace_back(sub);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto s = dynamic_cast<const key_equals_or_empty_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
@@ -255,17 +246,6 @@ namespace detail
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto s = dynamic_cast<const key_equals_number_or_empty_condition_impl *>(sub); s != nullptr)
|
||||
{
|
||||
if (keys.contains(s->m_item_name))
|
||||
{
|
||||
item v{ s->m_item_name, s->m_value };
|
||||
lookup.emplace_back(s->m_item_name, std::string{ v.value() }, true );
|
||||
subs.emplace_back(sub);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (lookup.size() == keys.size())
|
||||
@@ -273,14 +253,14 @@ namespace detail
|
||||
m_single = c[lookup];
|
||||
|
||||
for (auto s : subs)
|
||||
m_sub.erase(std::remove(m_sub.begin(), m_sub.end(), s), m_sub.end());
|
||||
std::erase(m_sub, s);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool and_condition_impl::test(row_handle r) const
|
||||
bool and_condition_impl::test(const_row_handle r) const
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
@@ -307,25 +287,30 @@ namespace detail
|
||||
|
||||
for (auto &sub : m_sub)
|
||||
{
|
||||
sub = sub->prepare(c);
|
||||
if (sub->prepare(c) == nullptr)
|
||||
{
|
||||
delete sub;
|
||||
sub = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeid(*sub) == typeid(and_condition_impl))
|
||||
and_conditions.push_back(static_cast<and_condition_impl *>(sub));
|
||||
}
|
||||
|
||||
if (and_conditions.size() == m_sub.size())
|
||||
std::erase(m_sub, nullptr);
|
||||
|
||||
if (not m_sub.empty() and and_conditions.size() == m_sub.size())
|
||||
return and_condition_impl::combine_equal(and_conditions, this);
|
||||
|
||||
return this;
|
||||
return m_sub.empty() ? nullptr : this;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
void condition::prepare(const category &c)
|
||||
bool condition::prepare(const category &c)
|
||||
{
|
||||
if (m_impl)
|
||||
m_impl = m_impl->prepare(c);
|
||||
|
||||
m_prepared = true;
|
||||
return m_impl and m_impl->prepare(c) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -25,7 +25,13 @@
|
||||
*/
|
||||
|
||||
#include "cif++/datablock.hpp"
|
||||
|
||||
#include "cif++/validate.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <ranges>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -42,7 +48,28 @@ datablock::datablock(const datablock &db)
|
||||
void datablock::load_dictionary()
|
||||
{
|
||||
if (auto *audit_conform = get("audit_conform"); audit_conform and not audit_conform->empty())
|
||||
set_validator(&validator_factory::instance().get(*audit_conform));
|
||||
{
|
||||
try
|
||||
{
|
||||
set_validator(validator_factory::instance().get(*audit_conform));
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::clog << ex.what() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void datablock::load_dictionary(std::string_view dict)
|
||||
{
|
||||
try
|
||||
{
|
||||
set_validator(validator_factory::instance().get(dict));
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::clog << ex.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void datablock::set_validator(const validator *v)
|
||||
@@ -96,16 +123,16 @@ bool datablock::strip()
|
||||
bool result = true;
|
||||
|
||||
// remove all categories that have no validator
|
||||
erase(std::remove_if(begin(), end(), [](category &c) {
|
||||
std::erase_if(*this, [](category &c)
|
||||
{
|
||||
bool result = false;
|
||||
if (c.get_cat_validator() == nullptr)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::clog << "Dropping category " << c.name() << '\n';
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}), end());
|
||||
return result; });
|
||||
|
||||
// then strip the remaining categories
|
||||
for (auto &cat : *this)
|
||||
@@ -134,7 +161,7 @@ bool datablock::strip()
|
||||
|
||||
category &datablock::operator[](std::string_view name)
|
||||
{
|
||||
auto i = std::find_if(begin(), end(), [name](const category &c)
|
||||
auto i = std::ranges::find_if(*this, [name](const category &c)
|
||||
{ return iequals(c.name(), name); });
|
||||
|
||||
if (i != end())
|
||||
@@ -151,14 +178,14 @@ category &datablock::operator[](std::string_view name)
|
||||
const category &datablock::operator[](std::string_view name) const
|
||||
{
|
||||
static const category s_empty;
|
||||
auto i = std::find_if(begin(), end(), [name](const category &c)
|
||||
auto i = std::ranges::find_if(*this, [name](const category &c)
|
||||
{ return iequals(c.name(), name); });
|
||||
return i == end() ? s_empty : *i;
|
||||
}
|
||||
|
||||
category *datablock::get(std::string_view name)
|
||||
{
|
||||
auto i = std::find_if(begin(), end(), [name](const category &c)
|
||||
auto i = std::ranges::find_if(*this, [name](const category &c)
|
||||
{ return iequals(c.name(), name); });
|
||||
return i == end() ? nullptr : &*i;
|
||||
}
|
||||
@@ -204,7 +231,7 @@ std::vector<std::string> datablock::get_item_order() const
|
||||
std::vector<std::string> result;
|
||||
|
||||
// for entry and audit_conform on top
|
||||
auto ci = find_if(begin(), end(), [](const category &cat)
|
||||
auto ci = std::ranges::find_if(*this, [](const category &cat)
|
||||
{ return cat.name() == "entry"; });
|
||||
if (ci != end())
|
||||
{
|
||||
@@ -212,7 +239,7 @@ std::vector<std::string> datablock::get_item_order() const
|
||||
result.insert(result.end(), cto.begin(), cto.end());
|
||||
}
|
||||
|
||||
ci = find_if(begin(), end(), [](const category &cat)
|
||||
ci = std::ranges::find_if(*this, [](const category &cat)
|
||||
{ return cat.name() == "audit_conform"; });
|
||||
if (ci != end())
|
||||
{
|
||||
@@ -260,7 +287,7 @@ namespace
|
||||
|
||||
for (auto link : validator.get_links_for_child(cat))
|
||||
{
|
||||
auto ei = std::find_if(cat_order.begin(), cat_order.end(), [parent = link->m_parent_category](elem_t &a)
|
||||
auto ei = std::ranges::find_if(cat_order, [parent = link->m_parent_category](elem_t &a)
|
||||
{ return std::get<0>(a) == parent; });
|
||||
|
||||
if (ei == cat_order.end())
|
||||
@@ -297,7 +324,7 @@ void datablock::write(std::ostream &os) const
|
||||
for (auto i = cat_order.begin(); i != cat_order.end(); ++i)
|
||||
calculate_cat_order(cat_order, i, *m_validator);
|
||||
|
||||
std::sort(cat_order.begin(), cat_order.end(), [](const elem_t &a, const elem_t &b)
|
||||
std::ranges::sort(cat_order, [](const elem_t &a, const elem_t &b)
|
||||
{
|
||||
const auto &[cat_a, count_a, on_stack_a] = a;
|
||||
const auto &[cat_b, count_b, on_stack_b] = b;
|
||||
@@ -349,7 +376,7 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &item_nam
|
||||
{
|
||||
std::string cat_name, item_name;
|
||||
std::tie(cat_name, item_name) = split_item_name(o);
|
||||
if (find_if(cat_order.rbegin(), cat_order.rend(), [cat_name](const std::string &s) -> bool
|
||||
if (std::ranges::find_if(std::ranges::reverse_view(cat_order), [cat_name](const std::string &s) -> bool
|
||||
{ return iequals(cat_name, s); }) == cat_order.rend())
|
||||
cat_order.push_back(cat_name);
|
||||
}
|
||||
@@ -376,7 +403,7 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &item_nam
|
||||
// for any Category we missed in the catOrder
|
||||
for (auto &cat : *this)
|
||||
{
|
||||
if (find_if(cat_order.begin(), cat_order.end(), [&](const std::string &s) -> bool
|
||||
if (std::ranges::find_if(cat_order, [&](const std::string &s) -> bool
|
||||
{ return iequals(cat.name(), s); }) != cat_order.end())
|
||||
continue;
|
||||
|
||||
@@ -400,14 +427,14 @@ bool datablock::operator==(const datablock &rhs) const
|
||||
if (not cat.empty())
|
||||
catA.push_back(cat.name());
|
||||
}
|
||||
std::sort(catA.begin(), catA.end());
|
||||
std::ranges::sort(catA);
|
||||
|
||||
for (auto &cat : dbB)
|
||||
{
|
||||
if (not cat.empty())
|
||||
catB.push_back(cat.name());
|
||||
}
|
||||
std::sort(catB.begin(), catB.end());
|
||||
std::ranges::sort(catB);
|
||||
|
||||
// loop over categories twice, to group output
|
||||
// First iteration is to list missing categories.
|
||||
|
||||
@@ -24,10 +24,17 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/dictionary_parser.hpp"
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -46,7 +53,7 @@ class dictionary_parser : public parser
|
||||
void load_dictionary()
|
||||
{
|
||||
std::unique_ptr<datablock> dict;
|
||||
auto savedDatablock = m_datablock;
|
||||
auto savedDatablock = std::exchange(m_datablock, nullptr);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -60,7 +67,7 @@ class dictionary_parser : public parser
|
||||
|
||||
default:
|
||||
{
|
||||
dict.reset(new datablock(m_token_value)); // dummy datablock, for constructing the validator only
|
||||
dict = std::make_unique<datablock>(m_token_value); // dummy datablock, for constructing the validator only
|
||||
m_datablock = dict.get();
|
||||
|
||||
match(CIFToken::DATA);
|
||||
@@ -75,6 +82,9 @@ class dictionary_parser : public parser
|
||||
error(ex.what());
|
||||
}
|
||||
|
||||
if (m_datablock == nullptr)
|
||||
throw std::runtime_error("Dictionary file is empty?");
|
||||
|
||||
// store all validators
|
||||
for (auto &ic : mCategoryValidators)
|
||||
m_validator.add_category_validator(std::move(ic));
|
||||
@@ -98,7 +108,7 @@ class dictionary_parser : public parser
|
||||
// store meta information
|
||||
if (auto dictionary = m_datablock->get("dictionary"); dictionary != nullptr and not dictionary->empty())
|
||||
{
|
||||
const auto &[name, version] = dictionary->front().get<std::string,std::optional<std::string>>("title", "version");
|
||||
const auto &[name, version] = dictionary->front().get<std::string, std::optional<std::string>>("title", "version");
|
||||
m_validator.append_audit_conform(name, version);
|
||||
}
|
||||
|
||||
@@ -113,7 +123,7 @@ class dictionary_parser : public parser
|
||||
if (not m_collected_item_types)
|
||||
m_collected_item_types = collect_item_types();
|
||||
|
||||
std::string saveFrameName { m_token_value };
|
||||
std::string saveFrameName{ m_token_value };
|
||||
|
||||
if (saveFrameName.empty())
|
||||
error("Invalid save frame, should contain more than just 'save_' here");
|
||||
@@ -121,7 +131,7 @@ class dictionary_parser : public parser
|
||||
bool isCategorySaveFrame = m_token_value[0] != '_';
|
||||
|
||||
datablock dict(m_token_value);
|
||||
datablock::iterator cat = dict.end();
|
||||
auto cat = dict.end();
|
||||
|
||||
match(CIFToken::SAVE_NAME);
|
||||
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME)
|
||||
@@ -147,15 +157,39 @@ class dictionary_parser : public parser
|
||||
match(CIFToken::ITEM_NAME);
|
||||
}
|
||||
|
||||
while (m_lookahead == CIFToken::VALUE)
|
||||
while (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
|
||||
{
|
||||
cat->emplace({});
|
||||
auto row = cat->back();
|
||||
|
||||
for (auto item_name : item_names)
|
||||
{
|
||||
row[item_name] = m_token_value;
|
||||
match(CIFToken::VALUE);
|
||||
switch (m_lookahead)
|
||||
{
|
||||
case CIFToken::VALUE_INAPPLICABLE:
|
||||
row[item_name] = nullptr;
|
||||
match(CIFToken::VALUE_INAPPLICABLE);
|
||||
break;
|
||||
case CIFToken::VALUE_UNKNOWN:
|
||||
row[item_name] = item_value{ std::optional<std::string>{} };
|
||||
match(CIFToken::VALUE_UNKNOWN);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_INTEGER:
|
||||
row[item_name] = m_token_value_int;
|
||||
match(CIFToken::VALUE_NUMERIC_INTEGER);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_FLOAT:
|
||||
row[item_name] = m_token_value_float;
|
||||
match(CIFToken::VALUE_NUMERIC_FLOAT);
|
||||
break;
|
||||
case CIFToken::VALUE_CHARSTRING:
|
||||
case CIFToken::VALUE_TEXTFIELD:
|
||||
row[item_name] = m_token_value;
|
||||
match(m_lookahead);
|
||||
break;
|
||||
default:
|
||||
match(CIFToken::VALUE_CHARSTRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,9 +207,33 @@ class dictionary_parser : public parser
|
||||
|
||||
if (cat->empty())
|
||||
cat->emplace({});
|
||||
cat->back()[item_name] = m_token_value;
|
||||
|
||||
match(CIFToken::VALUE);
|
||||
switch (m_lookahead)
|
||||
{
|
||||
case CIFToken::VALUE_INAPPLICABLE:
|
||||
cat->back()[item_name] = nullptr;
|
||||
match(CIFToken::VALUE_INAPPLICABLE);
|
||||
break;
|
||||
case CIFToken::VALUE_UNKNOWN:
|
||||
cat->back()[item_name] = item_value{ std::optional<std::string>{} };
|
||||
match(CIFToken::VALUE_UNKNOWN);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_INTEGER:
|
||||
cat->back()[item_name] = m_token_value_int;
|
||||
match(CIFToken::VALUE_NUMERIC_INTEGER);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_FLOAT:
|
||||
cat->back()[item_name] = m_token_value_float;
|
||||
match(CIFToken::VALUE_NUMERIC_FLOAT);
|
||||
break;
|
||||
case CIFToken::VALUE_CHARSTRING:
|
||||
case CIFToken::VALUE_TEXTFIELD:
|
||||
cat->back()[item_name] = m_token_value;
|
||||
match(m_lookahead);
|
||||
break;
|
||||
default:
|
||||
match(CIFToken::VALUE_CHARSTRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +241,7 @@ class dictionary_parser : public parser
|
||||
|
||||
if (isCategorySaveFrame)
|
||||
{
|
||||
std::string category = dict["category"].front().get<std::string>("id");
|
||||
auto category = dict["category"].front().get<std::string>("id");
|
||||
|
||||
std::vector<std::string> keys;
|
||||
for (auto k : dict["category_key"])
|
||||
@@ -198,17 +256,22 @@ class dictionary_parser : public parser
|
||||
else
|
||||
{
|
||||
// if the type code is missing, this must be a pointer, just skip it
|
||||
std::string typeCode = dict["item_type"].front().get<std::string>("code");
|
||||
std::optional<std::string> typeCode;
|
||||
|
||||
if (not dict["item_type"].empty())
|
||||
typeCode = dict["item_type"].front().get<std::optional<std::string>>("code");
|
||||
|
||||
const type_validator *tv = nullptr;
|
||||
if (not(typeCode.empty() or typeCode == "?"))
|
||||
tv = m_validator.get_validator_for_type(typeCode);
|
||||
if (typeCode.has_value())
|
||||
tv = m_validator.get_validator_for_type(*typeCode);
|
||||
|
||||
iset ess;
|
||||
for (auto e : dict["item_enumeration"])
|
||||
ess.insert(e["value"].as<std::string>());
|
||||
|
||||
std::string defaultValue = dict["item_default"].front().get<std::string>("value");
|
||||
std::string defaultValue;
|
||||
if (auto &cat = dict["item_default"]; not cat.empty())
|
||||
defaultValue = cat.front().get<std::string>("value");
|
||||
// bool defaultIsNull = false;
|
||||
// if (defaultValue.empty())
|
||||
// {
|
||||
@@ -222,7 +285,7 @@ class dictionary_parser : public parser
|
||||
|
||||
std::vector<item_alias> aliases;
|
||||
for (const auto &[alias_name, dictionary, version] :
|
||||
dict["item_aliases"].rows<std::string,std::string,std::string>("alias_name", "dictionary", "version"))
|
||||
dict["item_aliases"].rows<std::string, std::string, std::string>("alias_name", "dictionary", "version"))
|
||||
{
|
||||
aliases.emplace_back(alias_name, dictionary, version);
|
||||
}
|
||||
@@ -230,8 +293,7 @@ class dictionary_parser : public parser
|
||||
// collect the dict from our dataBlock and construct validators
|
||||
for (auto i : dict["item"])
|
||||
{
|
||||
std::string item, category, mandatory;
|
||||
cif::tie(item, category, mandatory) = i.get("name", "category_id", "mandatory_code");
|
||||
auto &&[item, category, mandatory] = i.get<std::string, std::string, std::string>("name", "category_id", "mandatory_code");
|
||||
|
||||
std::string cat_name, item_name;
|
||||
std::tie(cat_name, item_name) = split_item_name(item);
|
||||
@@ -246,9 +308,9 @@ class dictionary_parser : public parser
|
||||
|
||||
auto &ivs = mItemValidators[category];
|
||||
|
||||
auto vi = find(ivs.begin(), ivs.end(), item_validator{ item_name });
|
||||
auto vi = std::ranges::find(ivs, item_validator{ item_name });
|
||||
if (vi == ivs.end())
|
||||
ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, cat_name, std::move(aliases) });
|
||||
ivs.push_back(item_validator{ item_name, iequals(mandatory, "yes"), tv, ess, defaultValue, cat_name, aliases });
|
||||
else
|
||||
{
|
||||
// need to update the itemValidator?
|
||||
@@ -287,9 +349,7 @@ class dictionary_parser : public parser
|
||||
|
||||
// collect the dict from our dataBlock and construct validators
|
||||
for (auto i : dict["item_linked"])
|
||||
{
|
||||
mLinkedItems.emplace(i.get<std::string,std::string>("child_name", "parent_name"));
|
||||
}
|
||||
mLinkedItems.emplace(i.get<std::string, std::string>("child_name", "parent_name"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,9 +394,7 @@ class dictionary_parser : public parser
|
||||
|
||||
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 &&[child, parent, link_group_id] = gl.get<std::string, std::string, int>("child_name", "parent_name", "link_group_id");
|
||||
|
||||
auto civ = m_validator.get_validator_for_item(child);
|
||||
if (civ == nullptr)
|
||||
@@ -350,7 +408,7 @@ class dictionary_parser : public parser
|
||||
if (not linkIndex.count(key))
|
||||
{
|
||||
linkIndex[key] = linkKeys.size();
|
||||
linkKeys.push_back({});
|
||||
linkKeys.emplace_back();
|
||||
}
|
||||
|
||||
std::size_t ix = linkIndex.at(key);
|
||||
@@ -378,7 +436,7 @@ class dictionary_parser : public parser
|
||||
if (not linkIndex.count(key))
|
||||
{
|
||||
linkIndex[key] = linkKeys.size();
|
||||
linkKeys.push_back({});
|
||||
linkKeys.emplace_back();
|
||||
}
|
||||
|
||||
std::size_t ix = linkIndex.at(key);
|
||||
@@ -403,6 +461,35 @@ class dictionary_parser : public parser
|
||||
break;
|
||||
}
|
||||
|
||||
// A last validation, link ends should both point to the same time
|
||||
|
||||
auto childCatValidator = m_validator.get_validator_for_category(link.m_child_category);
|
||||
auto parentCatValidator = m_validator.get_validator_for_category(link.m_parent_category);
|
||||
|
||||
if (childCatValidator == nullptr)
|
||||
throw std::runtime_error(std::format("Invalid dictionary, undefined category {} in link {}", link.m_child_category, link.m_link_group_id));
|
||||
if (parentCatValidator == nullptr)
|
||||
throw std::runtime_error(std::format("Invalid dictionary, undefined category {} in link {}", link.m_parent_category, link.m_link_group_id));
|
||||
|
||||
for (size_t ix = 0; ix < link.m_child_keys.size(); ++ix)
|
||||
{
|
||||
auto childItemValidator = childCatValidator->get_validator_for_item(link.m_child_keys[ix]);
|
||||
auto parentItemValidator = parentCatValidator->get_validator_for_item(link.m_parent_keys[ix]);
|
||||
|
||||
if (childItemValidator == nullptr)
|
||||
throw std::runtime_error(std::format("Invalid dictionary, in link group {} the item {} is not know in category {}",
|
||||
link.m_link_group_id, link.m_child_keys[ix], link.m_child_category));
|
||||
if (parentItemValidator == nullptr)
|
||||
throw std::runtime_error(std::format("Invalid dictionary, in link group {} the item {} is not know in category {}",
|
||||
link.m_link_group_id, link.m_parent_keys[ix], link.m_parent_category));
|
||||
|
||||
if (childItemValidator->m_type == nullptr)
|
||||
const_cast<item_validator *>(childItemValidator)->m_type = parentItemValidator->m_type;
|
||||
else if (childItemValidator->m_type != parentItemValidator->m_type)
|
||||
throw std::runtime_error(std::format("Invalid dictionary, in link group {} the items _{}.{}/_{}.{} do not have the same type",
|
||||
link.m_link_group_id, link.m_parent_category, link.m_parent_keys[ix], link.m_child_category, link.m_child_keys[ix]));
|
||||
}
|
||||
|
||||
m_validator.add_link_validator(std::move(link));
|
||||
}
|
||||
|
||||
@@ -412,7 +499,7 @@ class dictionary_parser : public parser
|
||||
{
|
||||
for (auto &iv : cv.m_item_validators)
|
||||
{
|
||||
if (iv.m_type == nullptr and cif::VERBOSE >= 0)
|
||||
if (iv.m_type == nullptr and VERBOSE >= 0)
|
||||
std::cerr << "Missing item_type for " << iv.m_item_name << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
37
src/file.cpp
37
src/file.cpp
@@ -25,8 +25,13 @@
|
||||
*/
|
||||
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/condition.hpp"
|
||||
|
||||
#include "cif++/gzio.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -76,13 +81,13 @@ bool file::validate_links() const
|
||||
|
||||
bool file::contains(std::string_view name) const
|
||||
{
|
||||
return std::find_if(begin(), end(), [name](const datablock &db)
|
||||
return std::ranges::find_if(*this, [name](const datablock &db)
|
||||
{ return iequals(db.name(), name); }) != end();
|
||||
}
|
||||
|
||||
datablock &file::operator[](std::string_view name)
|
||||
{
|
||||
auto i = std::find_if(begin(), end(), [name](const datablock &c)
|
||||
auto i = std::ranges::find_if(*this, [name](const datablock &c)
|
||||
{ return iequals(c.name(), name); });
|
||||
|
||||
if (i != end())
|
||||
@@ -95,7 +100,7 @@ datablock &file::operator[](std::string_view name)
|
||||
const datablock &file::operator[](std::string_view name) const
|
||||
{
|
||||
static const datablock s_empty;
|
||||
auto i = std::find_if(begin(), end(), [name](const datablock &c)
|
||||
auto i = std::ranges::find_if(*this, [name](const datablock &c)
|
||||
{ return iequals(c.name(), name); });
|
||||
return i == end() ? s_empty : *i;
|
||||
}
|
||||
@@ -139,30 +144,6 @@ void file::load(const std::filesystem::path &p)
|
||||
}
|
||||
}
|
||||
|
||||
void file::load(const std::filesystem::path &p, const validator &v)
|
||||
{
|
||||
gzio::ifstream in(p);
|
||||
if (not in.is_open())
|
||||
throw std::runtime_error("Could not open file '" + p.string() + '\'');
|
||||
|
||||
try
|
||||
{
|
||||
load(in, v);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
throw_with_nested(std::runtime_error("Error reading file '" + p.string() + '\''));
|
||||
}
|
||||
}
|
||||
|
||||
void file::load(std::istream &is, const validator &v)
|
||||
{
|
||||
parser p(is, *this);
|
||||
p.parse_file();
|
||||
for (auto &db : *this)
|
||||
db.set_validator(&v);
|
||||
}
|
||||
|
||||
void file::load(std::istream &is)
|
||||
{
|
||||
parser p(is, *this);
|
||||
|
||||
200
src/item.cpp
200
src/item.cpp
@@ -24,45 +24,193 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/item.hpp"
|
||||
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <compare>
|
||||
#include <string_view>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
const item_handle item_handle::s_null_item;
|
||||
row_handle s_null_row_handle;
|
||||
|
||||
item_handle::item_handle()
|
||||
: m_item_ix(std::numeric_limits<uint16_t>::max())
|
||||
, m_row_handle(s_null_row_handle)
|
||||
bool item_handle::empty() const
|
||||
{
|
||||
return m_item_ix >= m_row.size() or m_row[m_item_ix].empty();
|
||||
}
|
||||
|
||||
std::string_view item_handle::text() const
|
||||
item_value &item_handle::value()
|
||||
{
|
||||
if (not m_row_handle.empty())
|
||||
assert(m_item_ix < m_row.size());
|
||||
return m_row.operator[](m_item_ix);
|
||||
}
|
||||
|
||||
const item_value &item_handle::value() const
|
||||
{
|
||||
assert(m_item_ix < m_row.size());
|
||||
return m_row.operator[](m_item_ix);
|
||||
}
|
||||
|
||||
void item_handle::set(item_value value, bool updateLinked)
|
||||
{
|
||||
row_handle rh{ m_category, m_row };
|
||||
rh.assign(m_item_ix, std::move(value), updateLinked);
|
||||
}
|
||||
|
||||
bool const_item_handle::empty() const
|
||||
{
|
||||
return m_item_ix >= m_row.size() or m_row[m_item_ix].empty();
|
||||
}
|
||||
|
||||
const item_value &const_item_handle::value() const
|
||||
{
|
||||
assert(m_item_ix < m_row.size());
|
||||
return m_row.operator[](m_item_ix);
|
||||
}
|
||||
|
||||
int item_value::compare(const item_value &b, bool ignore_case) const noexcept
|
||||
{
|
||||
int d = static_cast<int>(m_data.m_type) - static_cast<int>(b.m_data.m_type);
|
||||
|
||||
if (d == 0)
|
||||
{
|
||||
auto iv = m_row_handle.m_row->get(m_item_ix);
|
||||
if (iv != nullptr)
|
||||
return iv->text();
|
||||
switch (m_data.m_type)
|
||||
{
|
||||
case cif::item_value_type::INT:
|
||||
d = m_data.m_value.m_integer - b.m_data.m_value.m_integer;
|
||||
break;
|
||||
case cif::item_value_type::FLOAT:
|
||||
// stupid comparison based on chopped textual representation
|
||||
if (m_data.m_len > 0 or b.m_data.m_len > 0)
|
||||
{
|
||||
double fa = m_data.m_value.m_float;
|
||||
double fb = b.m_data.m_value.m_float;
|
||||
|
||||
auto delta = std::abs(fa - fb);
|
||||
if (delta == 0 or std::isnan(delta))
|
||||
d = 0;
|
||||
else if (m_data.m_len and b.m_data.m_len)
|
||||
{
|
||||
auto epsilon = std::pow(10.0f, -1.0f * std::min(m_data.m_len, b.m_data.m_len));
|
||||
if (delta > epsilon)
|
||||
d = fa < fb ? -1 : 1;
|
||||
else
|
||||
d = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dp = (m_data.m_value.m_float <=> b.m_data.m_value.m_float);
|
||||
if (dp == std::partial_ordering::less)
|
||||
d = -1;
|
||||
else if (dp == std::partial_ordering::greater)
|
||||
d = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dp = (m_data.m_value.m_float <=> b.m_data.m_value.m_float);
|
||||
if (dp == std::partial_ordering::less)
|
||||
d = -1;
|
||||
else if (dp == std::partial_ordering::greater)
|
||||
d = 1;
|
||||
}
|
||||
break;
|
||||
case cif::item_value_type::TEXT:
|
||||
d = m_data.sv().compare(b.m_data.sv());
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
else if (is_number() and b.is_number())
|
||||
{
|
||||
std::partial_ordering dp = std::partial_ordering::equivalent;
|
||||
|
||||
if (is_number_float())
|
||||
dp = m_data.m_value.m_float <=> b.m_data.m_value.m_integer;
|
||||
else /* if (is_number_int()) */
|
||||
dp = m_data.m_value.m_integer <=> b.m_data.m_value.m_float;
|
||||
|
||||
if (dp == std::partial_ordering::less)
|
||||
d = -1;
|
||||
else if (dp == std::partial_ordering::greater)
|
||||
d = 1;
|
||||
else
|
||||
d = 0;
|
||||
}
|
||||
else if (is_number_int() and b.is_string())
|
||||
d = str().compare(b.m_data.sv());
|
||||
else if (is_string() and b.is_number_int())
|
||||
d = m_data.sv().compare(b.str());
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
std::string item_value::str() const
|
||||
{
|
||||
switch (m_data.m_type)
|
||||
{
|
||||
case item_value_type::MISSING:
|
||||
return "?";
|
||||
|
||||
case item_value_type::INAPPLICABLE:
|
||||
return ".";
|
||||
|
||||
case item_value_type::TEXT:
|
||||
return std::string{ m_data.sv() };
|
||||
|
||||
case cif::item_value_type::INT:
|
||||
return std::format("{}", m_data.m_value.m_integer);
|
||||
|
||||
case cif::item_value_type::FLOAT:
|
||||
{
|
||||
char s[32];
|
||||
|
||||
std::to_chars_result r;
|
||||
|
||||
if (m_data.m_len)
|
||||
{
|
||||
r = std::to_chars(s, s + sizeof(s), m_data.m_value.m_float, std::chars_format::fixed, m_data.m_len);
|
||||
if (r.ec != std::errc{})
|
||||
r = std::to_chars(s, s + sizeof(s), m_data.m_value.m_float);
|
||||
}
|
||||
else
|
||||
r = std::to_chars(s, s + sizeof(s), m_data.m_value.m_float);
|
||||
|
||||
return r.ec == std::errc{} ? std::string{ s, r.ptr } : "*****";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// void const_item_handle::assign_value(const item_value &value)
|
||||
// {
|
||||
// assert(not m_row_handle.empty());
|
||||
// m_row_handle.assign(m_item_ix, value, true);
|
||||
// }
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const item_value &v)
|
||||
{
|
||||
switch (v.type())
|
||||
{
|
||||
case cif::item_value_type::INT:
|
||||
os << v.m_data.m_value.m_integer;
|
||||
break;
|
||||
case cif::item_value_type::FLOAT:
|
||||
os << v.m_data.m_value.m_float;
|
||||
break;
|
||||
case cif::item_value_type::TEXT:
|
||||
os << v.m_data.sv();
|
||||
break;
|
||||
case cif::item_value_type::MISSING:
|
||||
os << '?';
|
||||
break;
|
||||
case cif::item_value_type::INAPPLICABLE:
|
||||
os << '.';
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
return os;
|
||||
}
|
||||
|
||||
void item_handle::assign_value(std::string_view value)
|
||||
{
|
||||
assert(not m_row_handle.empty());
|
||||
m_row_handle.assign(m_item_ix, value, true);
|
||||
}
|
||||
|
||||
void item_handle::swap(item_handle &b)
|
||||
{
|
||||
assert(m_item_ix == b.m_item_ix);
|
||||
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
|
||||
m_row_handle.swap(m_item_ix, b.m_row_handle);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cif
|
||||
|
||||
447
src/model.cpp
447
src/model.cpp
@@ -24,13 +24,19 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/model.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <initializer_list>
|
||||
#include <numeric>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -47,95 +53,50 @@ void atom::atom_impl::moveTo(const point &p)
|
||||
|
||||
auto r = row();
|
||||
|
||||
r.assign("Cartn_x", cif::format("{:.3f}", p.m_x), false, false);
|
||||
r.assign("Cartn_y", cif::format("{:.3f}", p.m_y), false, false);
|
||||
r.assign("Cartn_z", cif::format("{:.3f}", p.m_z), false, false);
|
||||
r.assign("Cartn_x", std::format("{:.3f}", p.m_x), false, false);
|
||||
r.assign("Cartn_y", std::format("{:.3f}", p.m_y), false, false);
|
||||
r.assign("Cartn_z", std::format("{:.3f}", p.m_z), false, false);
|
||||
|
||||
m_location = p;
|
||||
}
|
||||
|
||||
// const compound *compound() const;
|
||||
|
||||
std::string atom::atom_impl::get_property(std::string_view name) const
|
||||
const item_value &atom::atom_impl::get_property(std::string_view name) const
|
||||
{
|
||||
return row()[name].as<std::string>();
|
||||
if (auto rh = row(); rh)
|
||||
return rh[name].value();
|
||||
throw std::runtime_error(std::format("Missing property {} for atom", name));
|
||||
}
|
||||
|
||||
int atom::atom_impl::get_property_int(std::string_view name) const
|
||||
{
|
||||
int result = 0;
|
||||
if (not row()[name].empty())
|
||||
{
|
||||
auto s = get_property(name);
|
||||
|
||||
std::from_chars_result r = std::from_chars(s.data(), s.data() + s.length(), result);
|
||||
if ((bool)r.ec and VERBOSE > 0)
|
||||
std::cerr << "Error converting " << s << " to number for property " << name << '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float atom::atom_impl::get_property_float(std::string_view name) const
|
||||
{
|
||||
float result = 0;
|
||||
if (not row()[name].empty())
|
||||
{
|
||||
auto s = get_property(name);
|
||||
|
||||
std::from_chars_result r = cif::from_chars(s.data(), s.data() + s.length(), result);
|
||||
if ((bool)r.ec and VERBOSE > 0)
|
||||
std::cerr << "Error converting " << s << " to number for property " << name << '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void atom::atom_impl::set_property(const std::string_view name, const std::string &value)
|
||||
void atom::atom_impl::set_property(const std::string_view name, item_value value)
|
||||
{
|
||||
auto r = row();
|
||||
if (not r)
|
||||
throw std::runtime_error("Trying to modify a row that does not exist");
|
||||
r.assign(name, value, true, true);
|
||||
r.assign(name, std::move(value), true, true);
|
||||
}
|
||||
|
||||
int atom::atom_impl::compare(const atom_impl &b) const
|
||||
{
|
||||
int d = get_property("label_asym_id").compare(b.get_property("label_asym_id"));
|
||||
if (d == 0)
|
||||
d = get_property_int("label_seq_id") - b.get_property_int("label_seq_id");
|
||||
d = get_property("label_seq_id").compare(b.get_property("label_seq_id"));
|
||||
if (d == 0)
|
||||
d = get_property_int("auth_seq_id") - b.get_property_int("auth_seq_id");
|
||||
d = get_property("auth_seq_id").compare(b.get_property("auth_seq_id"));
|
||||
if (d == 0)
|
||||
d = get_property("label_atom_id").compare(b.get_property("label_atom_id"));
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
// bool atom::atom_impl::getAnisoU(float anisou[6]) const
|
||||
// {
|
||||
// bool result = false;
|
||||
|
||||
// auto cat = m_db.get("atom_site_anisotrop");
|
||||
// if (cat)
|
||||
// {
|
||||
// for (auto r : cat->find(key("id") == m_id))
|
||||
// {
|
||||
// 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;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
int atom::atom_impl::get_charge() const
|
||||
{
|
||||
auto formalCharge = row()["pdbx_formal_charge"].as<std::optional<int>>();
|
||||
|
||||
if (not formalCharge.has_value())
|
||||
{
|
||||
auto c = cif::compound_factory::instance().create(get_property("label_comp_id"));
|
||||
auto c = cif::compound_factory::instance().create(get_property("label_comp_id").get<std::string>());
|
||||
|
||||
if (c != nullptr and c->atoms().size() == 1)
|
||||
formalCharge = c->atoms().front().charge;
|
||||
@@ -224,14 +185,14 @@ atom residue::create_new_atom(atom_type inType, const std::string &inAtomID, poi
|
||||
{ "label_entity_id", get_entity_id() },
|
||||
{ "label_atom_id", inAtomID },
|
||||
{ "label_asym_id", m_asym_id },
|
||||
{ "label_alt_id", "." },
|
||||
{ "label_alt_id", nullptr },
|
||||
{ "label_comp_id", m_compound_id },
|
||||
{ "label_seq_id", m_seq_id },
|
||||
{ "auth_asym_id", m_pdb_strand_id },
|
||||
{ "auth_atom_id", inAtomID },
|
||||
{ "auth_comp_id", m_compound_id },
|
||||
{ "auth_seq_id", m_pdb_seq_num },
|
||||
{ "occupancy", 1.0f, 2 },
|
||||
{ "occupancy", { 1.0f, 2 } },
|
||||
{ "B_iso_or_equiv", 20.0f },
|
||||
{ "pdbx_PDB_model_num", m_structure->get_model_nr() },
|
||||
});
|
||||
@@ -348,28 +309,18 @@ std::tuple<point, float> residue::center_and_radius() const
|
||||
for (auto &a : m_atoms)
|
||||
pts.push_back(a.get_location());
|
||||
|
||||
auto center = centroid(pts);
|
||||
float radius = 0;
|
||||
|
||||
for (auto &pt : pts)
|
||||
{
|
||||
float d = static_cast<float>(distance(pt, center));
|
||||
if (radius < d)
|
||||
radius = d;
|
||||
}
|
||||
|
||||
return std::make_tuple(center, radius);
|
||||
return smallest_sphere_around_points(pts);
|
||||
}
|
||||
|
||||
bool residue::has_alternate_atoms() const
|
||||
{
|
||||
return std::find_if(m_atoms.begin(), m_atoms.end(), [](const atom &atom)
|
||||
return std::ranges::find_if(m_atoms, [](const atom &atom)
|
||||
{ return atom.is_alternate(); }) != m_atoms.end();
|
||||
}
|
||||
|
||||
bool residue::has_alternate_atoms_for(const std::string &atomID) const
|
||||
{
|
||||
return std::find_if(m_atoms.begin(), m_atoms.end(), [atomID](const atom &atom)
|
||||
return std::ranges::find_if(m_atoms, [atomID](const atom &atom)
|
||||
{ return atom.get_label_atom_id() == atomID and atom.is_alternate(); }) != m_atoms.end();
|
||||
}
|
||||
|
||||
@@ -413,24 +364,6 @@ monomer::monomer(const polymer &polymer, std::size_t index, int seqID, const std
|
||||
{
|
||||
}
|
||||
|
||||
monomer::monomer(monomer &&rhs)
|
||||
: residue(std::move(rhs))
|
||||
, m_polymer(rhs.m_polymer)
|
||||
, m_index(rhs.m_index)
|
||||
{
|
||||
rhs.m_polymer = nullptr;
|
||||
}
|
||||
|
||||
monomer &monomer::operator=(monomer &&rhs)
|
||||
{
|
||||
residue::operator=(std::move(rhs));
|
||||
m_polymer = rhs.m_polymer;
|
||||
rhs.m_polymer = nullptr;
|
||||
m_index = rhs.m_index;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool monomer::is_first_in_chain() const
|
||||
{
|
||||
return m_index == 0;
|
||||
@@ -600,7 +533,7 @@ float monomer::omega() const
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::map<std::string, std::vector<std::string>> kChiAtomsMap = {
|
||||
const std::map<std::string, std::vector<std::string>> kChiAtomsMap = { // NOLINT(bugprone-throwing-static-initialization,cert-err58-cpp)
|
||||
{ "ASP", { "CG", "OD1" } },
|
||||
{ "ASN", { "CG", "OD1" } },
|
||||
{ "ARG", { "CG", "CD", "NE", "CZ" } },
|
||||
@@ -779,8 +712,10 @@ bool monomer::are_bonded(const monomer &a, const monomer &b, float errorMargin)
|
||||
|
||||
result = std::abs(distanceCACA - maxCACADistance) < errorMargin;
|
||||
}
|
||||
catch (...)
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
if (VERBOSE > 2)
|
||||
std::cerr << "missing atoms in monomer::are_bonded: " << ex.what() << '\n';
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -822,11 +757,11 @@ atom monomer::create_new_atom(atom_type inType, const std::string &inAtomID, poi
|
||||
// --------------------------------------------------------------------
|
||||
// polymer
|
||||
|
||||
polymer::polymer(structure &s, const std::string &entityID, const std::string &asym_id, const std::string &auth_asym_id)
|
||||
polymer::polymer(structure &s, std::string entityID, std::string asym_id, std::string auth_asym_id)
|
||||
: m_structure(const_cast<structure *>(&s))
|
||||
, m_entity_id(entityID)
|
||||
, m_asym_id(asym_id)
|
||||
, m_pdb_strand_id(auth_asym_id)
|
||||
, m_entity_id(std::move(entityID))
|
||||
, m_asym_id(std::move(asym_id))
|
||||
, m_pdb_strand_id(std::move(auth_asym_id))
|
||||
{
|
||||
using namespace cif::literals;
|
||||
|
||||
@@ -835,7 +770,7 @@ polymer::polymer(structure &s, const std::string &entityID, const std::string &a
|
||||
auto &poly_seq_scheme = s.get_datablock()["pdbx_poly_seq_scheme"];
|
||||
reserve(poly_seq_scheme.size());
|
||||
|
||||
for (auto r : poly_seq_scheme.find("asym_id"_key == asym_id))
|
||||
for (auto r : poly_seq_scheme.find("asym_id"_key == m_asym_id))
|
||||
{
|
||||
int seqID;
|
||||
std::string compoundID, pdbSeqNum, pdbInsCode;
|
||||
@@ -915,23 +850,6 @@ sugar::sugar(branch &branch, const std::string &compoundID,
|
||||
{
|
||||
}
|
||||
|
||||
sugar::sugar(sugar &&rhs)
|
||||
: residue(std::forward<residue>(rhs))
|
||||
, m_branch(rhs.m_branch)
|
||||
{
|
||||
}
|
||||
|
||||
sugar &sugar::operator=(sugar &&rhs)
|
||||
{
|
||||
if (this != &rhs)
|
||||
{
|
||||
residue::operator=(std::forward<residue>(rhs));
|
||||
m_branch = rhs.m_branch;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// bool sugar::hasLinkedSugarAtLeavingO(int leavingO) const
|
||||
// {
|
||||
// return false;
|
||||
@@ -987,27 +905,28 @@ cif::mm::atom sugar::add_atom(row_initializer atom_info)
|
||||
atom_info.set_value({ "label_entity_id", m_branch->get_entity_id() });
|
||||
atom_info.set_value({ "label_asym_id", m_branch->get_asym_id() });
|
||||
atom_info.set_value({ "label_comp_id", m_compound_id });
|
||||
atom_info.set_value({ "label_seq_id", "." });
|
||||
atom_info.set_value({ "label_alt_id", "." });
|
||||
atom_info.set_value({ "label_seq_id", nullptr });
|
||||
atom_info.set_value({ "label_alt_id", nullptr });
|
||||
atom_info.set_value({ "auth_asym_id", m_branch->get_asym_id() });
|
||||
atom_info.set_value({ "auth_comp_id", m_compound_id });
|
||||
atom_info.set_value({ "auth_seq_id", m_pdb_seq_num });
|
||||
atom_info.set_value({ "occupancy", 1.0, 2 });
|
||||
atom_info.set_value({ "B_iso_or_equiv", 30.0, 2 });
|
||||
atom_info.set_value({ "occupancy", { 1.0, 2 } });
|
||||
atom_info.set_value({ "B_iso_or_equiv", { 30.0, 2 } });
|
||||
atom_info.set_value({ "pdbx_PDB_model_num", 1 });
|
||||
|
||||
auto row = atom_site.emplace(std::move(atom_info));
|
||||
auto result = m_structure->emplace_atom(db, row);
|
||||
const_row_handle rh = *row;
|
||||
auto result = m_structure->emplace_atom(db, rh);
|
||||
|
||||
residue::add_atom(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
branch::branch(structure &structure, const std::string &asym_id, const std::string &entity_id)
|
||||
branch::branch(structure &structure, std::string asym_id, std::string entity_id)
|
||||
: m_structure(&structure)
|
||||
, m_asym_id(asym_id)
|
||||
, m_entity_id(entity_id)
|
||||
, m_asym_id(std::move(asym_id))
|
||||
, m_entity_id(std::move(entity_id))
|
||||
{
|
||||
using namespace literals;
|
||||
|
||||
@@ -1016,12 +935,12 @@ branch::branch(structure &structure, const std::string &asym_id, const std::stri
|
||||
auto &branch_scheme = db["pdbx_branch_scheme"];
|
||||
auto &branch_link = db["pdbx_entity_branch_link"];
|
||||
|
||||
for (const auto &asym_entity_id : struct_asym.find<std::string>("id"_key == asym_id, "entity_id"))
|
||||
for (const auto &asym_entity_id : struct_asym.find<std::string>("id"_key == m_asym_id, "entity_id"))
|
||||
{
|
||||
for (const auto &[comp_id, num] : branch_scheme.find<std::string, int>(
|
||||
"asym_id"_key == asym_id, "mon_id", "pdb_seq_num"))
|
||||
"asym_id"_key == m_asym_id, "mon_id", "pdb_seq_num"))
|
||||
{
|
||||
emplace_back(*this, comp_id, asym_id, num);
|
||||
emplace_back(*this, comp_id, m_asym_id, num);
|
||||
}
|
||||
|
||||
for (const auto &[num1, num2, atom1, atom2] : branch_link.find<std::size_t, std::size_t, std::string, std::string>(
|
||||
@@ -1067,7 +986,7 @@ void branch::link_atoms()
|
||||
|
||||
sugar &branch::get_sugar_by_num(int nr)
|
||||
{
|
||||
auto i = find_if(begin(), end(), [nr](const sugar &s)
|
||||
auto i = std::ranges::find_if(*this, [nr](const sugar &s)
|
||||
{ return s.num() == nr; });
|
||||
if (i == end())
|
||||
throw std::out_of_range("Sugar with num " + std::to_string(nr) + " not found in branch " + m_asym_id);
|
||||
@@ -1095,7 +1014,7 @@ sugar &branch::construct_sugar(const std::string &compound_id)
|
||||
chemComp.emplace({ { "id", compound_id },
|
||||
{ "name", compound->name() },
|
||||
{ "formula", compound->formula() },
|
||||
{ "formula_weight", compound->formula_weight() },
|
||||
{ "formula_weight", { compound->formula_weight(), 3 } },
|
||||
{ "type", compound->type() } });
|
||||
}
|
||||
|
||||
@@ -1141,7 +1060,7 @@ sugar &branch::construct_sugar(const std::string &compound_id, const std::string
|
||||
{ "entity_branch_list_num_2", linked.num() },
|
||||
{ "comp_id_2", linked.get_compound_id() },
|
||||
{ "atom_id_2", linked_atom_id },
|
||||
{ "leaving_atom_id_2", "." },
|
||||
{ "leaving_atom_id_2", nullptr },
|
||||
{ "value_order", "sing" } });
|
||||
|
||||
return result;
|
||||
@@ -1199,10 +1118,10 @@ structure::structure(datablock &db, std::size_t modelNr, structure_open_options
|
||||
load_atoms_for_model(options);
|
||||
|
||||
// Check to see if we should actually load another model?
|
||||
if (m_atoms.empty() and m_model_nr == 1)
|
||||
if (m_atoms.empty() and m_model_nr == 1 and not atom_site.empty())
|
||||
{
|
||||
std::optional<std::size_t> model_nr;
|
||||
cif::tie(model_nr) = atom_site.front().get("pdbx_PDB_model_num");
|
||||
auto model_nr =
|
||||
atom_site.front().get<std::optional<std::size_t>>("pdbx_PDB_model_num");
|
||||
if (model_nr and *model_nr != m_model_nr)
|
||||
{
|
||||
if (VERBOSE > 0)
|
||||
@@ -1267,7 +1186,7 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
for (auto id : atom_site.find<std::string>(std::move(c), "id"))
|
||||
{
|
||||
auto a = std::make_shared<atom::atom_impl>(m_db, id);
|
||||
if (a->get_property_float("occupancy") > 0)
|
||||
if (a->get_property("occupancy").get<float>() > 0)
|
||||
continue;
|
||||
emplace_atom(a);
|
||||
}
|
||||
@@ -1275,28 +1194,28 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
else
|
||||
{
|
||||
std::vector<cif::mm::atom> atoms;
|
||||
std::map<std::tuple<std::string,int>, std::map<std::string, float>> alts;
|
||||
|
||||
std::map<std::tuple<std::string, int>, std::map<std::string, float>> alts;
|
||||
|
||||
for (auto id : atom_site.find<std::string>(std::move(c), "id"))
|
||||
{
|
||||
auto a = atoms.emplace_back(std::make_shared<atom::atom_impl>(m_db, id));
|
||||
|
||||
|
||||
if (a.is_alternate())
|
||||
{
|
||||
auto key = std::make_tuple(a.get_label_asym_id(), a.get_label_seq_id());
|
||||
auto alt_id = a.get_label_alt_id();
|
||||
|
||||
|
||||
if (auto i = alts.find(key); i != alts.end())
|
||||
i->second[alt_id] += a.get_occupancy();
|
||||
else
|
||||
alts[key][alt_id] = a.get_occupancy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto &&[key, value] : alts)
|
||||
{
|
||||
// const auto &[asym_id, seq_id] = key;
|
||||
|
||||
|
||||
// select highest occupancy for this residue's alternates
|
||||
std::string alt_id;
|
||||
float occupancy = options.occupancy_mode == occupancy_policy::MAX ? 0.f : std::numeric_limits<float>::max();
|
||||
@@ -1319,11 +1238,11 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value.clear();
|
||||
value.emplace(alt_id, occupancy);
|
||||
}
|
||||
|
||||
|
||||
for (auto a : atoms)
|
||||
{
|
||||
if (a.is_alternate())
|
||||
@@ -1335,10 +1254,8 @@ void structure::load_atoms_for_model(structure_open_options options)
|
||||
}
|
||||
else
|
||||
emplace_atom(a);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void structure::load_data()
|
||||
@@ -1416,9 +1333,8 @@ void structure::load_data()
|
||||
}
|
||||
|
||||
// what the ...
|
||||
m_branches.erase(std::remove_if(m_branches.begin(), m_branches.end(), [](const branch &b)
|
||||
{ return b.empty(); }),
|
||||
m_branches.end());
|
||||
std::erase_if(m_branches, [](const branch &b)
|
||||
{ return b.empty(); });
|
||||
|
||||
for (auto &branch : m_branches)
|
||||
branch.link_atoms();
|
||||
@@ -1719,7 +1635,7 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
|
||||
chemComp.emplace({ { "id", compoundID },
|
||||
{ "name", compound->name() },
|
||||
{ "formula", compound->formula() },
|
||||
{ "formula_weight", compound->formula_weight() },
|
||||
{ "formula_weight", { compound->formula_weight(), 3 } },
|
||||
{ "type", compound->type() } });
|
||||
}
|
||||
|
||||
@@ -1739,7 +1655,7 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
|
||||
entity.emplace({ { "id", entity_id },
|
||||
{ "type", "non-polymer" },
|
||||
{ "pdbx_description", compound->name() },
|
||||
{ "formula_weight", compound->formula_weight() } });
|
||||
{ "formula_weight", { compound->formula_weight(), 3 } } });
|
||||
|
||||
pdbxEntityNonpoly.emplace({ { "entity_id", entity_id },
|
||||
{ "name", compound->name() },
|
||||
@@ -1752,7 +1668,7 @@ std::string structure::insert_compound(const std::string &compoundID, bool is_en
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
atom &structure::emplace_atom(atom &&atom)
|
||||
atom &structure::emplace_atom(atom atom)
|
||||
{
|
||||
int L = 0, R = static_cast<int>(m_atom_index.size() - 1);
|
||||
while (L <= R)
|
||||
@@ -1794,22 +1710,12 @@ void structure::remove_atom(atom &a, bool removeFromResidue)
|
||||
|
||||
auto &atomSite = m_db["atom_site"];
|
||||
|
||||
if (a.is_water())
|
||||
{
|
||||
auto ra = atomSite.find1("id"_key == a.id());
|
||||
if (ra)
|
||||
{
|
||||
auto &nps = m_db["pdbx_nonpoly_scheme"];
|
||||
for (auto rnp : atomSite.get_children(ra, nps))
|
||||
nps.erase(rnp);
|
||||
}
|
||||
}
|
||||
else if (removeFromResidue)
|
||||
if (removeFromResidue)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto &res = get_residue(a);
|
||||
res.m_atoms.erase(std::remove(res.m_atoms.begin(), res.m_atoms.end(), a), res.m_atoms.end());
|
||||
std::erase(res.m_atoms, a);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -1820,6 +1726,13 @@ void structure::remove_atom(atom &a, bool removeFromResidue)
|
||||
|
||||
for (auto ri : atomSite.find("id"_key == a.id()))
|
||||
{
|
||||
if (a.is_water())
|
||||
{
|
||||
auto &nps = m_db["pdbx_nonpoly_scheme"];
|
||||
for (auto rnp : atomSite.get_children(ri, nps))
|
||||
nps.erase(rnp);
|
||||
}
|
||||
|
||||
// also remove struct_conn records for this atom
|
||||
auto &structConn = m_db["struct_conn"];
|
||||
|
||||
@@ -1898,13 +1811,8 @@ void structure::swap_atoms(atom a1, atom a2)
|
||||
auto r1 = atomSites.find1(key("id") == a1.id());
|
||||
auto r2 = atomSites.find1(key("id") == a2.id());
|
||||
|
||||
auto l1 = r1["label_atom_id"];
|
||||
auto l2 = r2["label_atom_id"];
|
||||
l1.swap(l2);
|
||||
|
||||
auto l3 = r1["auth_atom_id"];
|
||||
auto l4 = r2["auth_atom_id"];
|
||||
l3.swap(l4);
|
||||
for (std::string fld : std::initializer_list<std::string>{ "label_atom_id", "auth_atom_id", "type_symbol" })
|
||||
swap(r1[fld].value(), r2[fld].value());
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -1945,7 +1853,7 @@ void structure::change_residue(residue &res, const std::string &newCompound,
|
||||
entity.emplace({ { "id", entityID },
|
||||
{ "type", "non-polymer" },
|
||||
{ "pdbx_description", compound->name() },
|
||||
{ "formula_weight", compound->formula_weight() } });
|
||||
{ "formula_weight", { compound->formula_weight(), 3 } } });
|
||||
|
||||
auto &pdbxEntityNonpoly = m_db["pdbx_entity_nonpoly"];
|
||||
pdbxEntityNonpoly.emplace({ { "entity_id", entityID },
|
||||
@@ -1969,7 +1877,7 @@ void structure::change_residue(residue &res, const std::string &newCompound,
|
||||
chemComp.emplace({ { "id", newCompound },
|
||||
{ "name", compound->name() },
|
||||
{ "formula", compound->formula() },
|
||||
{ "formula_weight", compound->formula_weight() },
|
||||
{ "formula_weight", { compound->formula_weight(), 3 } },
|
||||
{ "type", compound->type() } });
|
||||
}
|
||||
|
||||
@@ -1986,7 +1894,7 @@ void structure::change_residue(residue &res, const std::string &newCompound,
|
||||
|
||||
for (const auto &[a1, a2] : remappedAtoms)
|
||||
{
|
||||
auto i = find_if(atoms.begin(), atoms.end(), [id = a1](const atom &a)
|
||||
auto i = std::ranges::find_if(atoms, [id = a1](const atom &a)
|
||||
{ return a.get_label_atom_id() == id; });
|
||||
if (i == atoms.end())
|
||||
{
|
||||
@@ -2000,7 +1908,7 @@ void structure::change_residue(residue &res, const std::string &newCompound,
|
||||
if (r.size() != 1)
|
||||
continue;
|
||||
|
||||
if (a2.empty() or a2 == ".")
|
||||
if (a2.empty())
|
||||
{
|
||||
i->set_property("label_comp_id", newCompound);
|
||||
remove_atom(*i);
|
||||
@@ -2083,19 +1991,19 @@ void structure::remove_residue(residue &res)
|
||||
"seq_id"_key == res.get_seq_id());
|
||||
|
||||
for (auto &poly : m_polymers)
|
||||
poly.erase(std::remove(poly.begin(), poly.end(), m), poly.end());
|
||||
std::erase(poly, m);
|
||||
break;
|
||||
}
|
||||
|
||||
case EntityType::NonPolymer:
|
||||
m_db["pdbx_nonpoly_scheme"].erase("asym_id"_key == res.get_asym_id());
|
||||
m_db["struct_asym"].erase("id"_key == res.get_asym_id());
|
||||
m_non_polymers.erase(std::remove(m_non_polymers.begin(), m_non_polymers.end(), res), m_non_polymers.end());
|
||||
std::erase(m_non_polymers, res);
|
||||
break;
|
||||
|
||||
case EntityType::Water:
|
||||
m_db["pdbx_nonpoly_scheme"].erase("asym_id"_key == res.get_asym_id());
|
||||
m_non_polymers.erase(std::remove(m_non_polymers.begin(), m_non_polymers.end(), res), m_non_polymers.end());
|
||||
std::erase(m_non_polymers, res);
|
||||
break;
|
||||
|
||||
case EntityType::Branched:
|
||||
@@ -2123,7 +2031,7 @@ void structure::remove_sugar(sugar &s)
|
||||
|
||||
std::string asym_id = s.get_asym_id();
|
||||
branch &branch = get_branch_by_asym_id(asym_id);
|
||||
auto si = std::find(branch.begin(), branch.end(), s);
|
||||
auto si = std::ranges::find(branch, s);
|
||||
if (si == branch.end())
|
||||
throw std::runtime_error("sugar not part of branch");
|
||||
std::size_t six = si - branch.begin();
|
||||
@@ -2135,6 +2043,7 @@ void structure::remove_sugar(sugar &s)
|
||||
std::set<std::size_t> dix;
|
||||
std::stack<std::size_t> test;
|
||||
test.push(s.num());
|
||||
std::vector<atom> da;
|
||||
|
||||
while (not test.empty())
|
||||
{
|
||||
@@ -2153,12 +2062,14 @@ void structure::remove_sugar(sugar &s)
|
||||
}
|
||||
|
||||
for (auto atom : branch[tix - 1].atoms())
|
||||
remove_atom(atom, false);
|
||||
da.emplace_back(atom);
|
||||
}
|
||||
|
||||
branch.erase(remove_if(branch.begin(), branch.end(), [dix](const sugar &s)
|
||||
{ return dix.count(s.num()); }),
|
||||
branch.end());
|
||||
std::erase_if(branch, [dix](const sugar &s)
|
||||
{ return dix.count(s.num()); });
|
||||
|
||||
for (auto atom : da)
|
||||
remove_atom(atom, false);
|
||||
|
||||
auto entity_id = create_entity_for_branch(branch);
|
||||
|
||||
@@ -2184,7 +2095,7 @@ void structure::remove_sugar(sugar &s)
|
||||
{ "mon_id", sugar.get_compound_id() },
|
||||
|
||||
{ "pdb_asym_id", asym_id },
|
||||
{ "pdb_seq_num", sugar.num() },
|
||||
{ "pdb_seq_num", std::to_string(sugar.num()) },
|
||||
{ "pdb_mon_id", sugar.get_compound_id() },
|
||||
|
||||
// TODO: need fix, collect from nag_atoms?
|
||||
@@ -2212,7 +2123,7 @@ void structure::remove_branch(branch &branch)
|
||||
m_db["struct_asym"].erase("id"_key == branch.get_asym_id());
|
||||
m_db["struct_conn"].erase("ptnr1_label_asym_id"_key == branch.get_asym_id() or "ptnr2_label_asym_id"_key == branch.get_asym_id());
|
||||
|
||||
m_branches.erase(remove(m_branches.begin(), m_branches.end(), branch), m_branches.end());
|
||||
std::erase(m_branches, branch);
|
||||
}
|
||||
|
||||
std::string structure::create_non_poly_entity(const std::string &comp_id)
|
||||
@@ -2233,7 +2144,7 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
|
||||
{ "entity_id", entity_id },
|
||||
{ "details", "?" } });
|
||||
|
||||
std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
|
||||
auto comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
|
||||
|
||||
auto &atom_site = m_db["atom_site"];
|
||||
|
||||
@@ -2245,24 +2156,24 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
|
||||
|
||||
auto row = atom_site.emplace({ { "group_PDB", atom.get_property("group_PDB") },
|
||||
{ "id", atom_id },
|
||||
{ "type_symbol", atom.get_property("type_symbol") },
|
||||
{ "label_atom_id", atom.get_property("label_atom_id") },
|
||||
{ "label_alt_id", atom.get_property("label_alt_id") },
|
||||
{ "type_symbol", atom.get_property_value("type_symbol") },
|
||||
{ "label_atom_id", atom.get_property_value("label_atom_id") },
|
||||
{ "label_alt_id", atom.get_property_value("label_alt_id") },
|
||||
{ "label_comp_id", comp_id },
|
||||
{ "label_asym_id", asym_id },
|
||||
{ "label_entity_id", entity_id },
|
||||
{ "label_seq_id", "." },
|
||||
{ "label_seq_id", nullptr },
|
||||
{ "pdbx_PDB_ins_code", "" },
|
||||
{ "Cartn_x", atom.get_property("Cartn_x") },
|
||||
{ "Cartn_y", atom.get_property("Cartn_y") },
|
||||
{ "Cartn_z", atom.get_property("Cartn_z") },
|
||||
{ "occupancy", atom.get_property("occupancy") },
|
||||
{ "B_iso_or_equiv", atom.get_property("B_iso_or_equiv") },
|
||||
{ "pdbx_formal_charge", atom.get_property("pdbx_formal_charge") },
|
||||
{ "auth_seq_id", 1 },
|
||||
{ "Cartn_x", atom.get_property_value("Cartn_x") },
|
||||
{ "Cartn_y", atom.get_property_value("Cartn_y") },
|
||||
{ "Cartn_z", atom.get_property_value("Cartn_z") },
|
||||
{ "occupancy", atom.get_property_value("occupancy") },
|
||||
{ "B_iso_or_equiv", atom.get_property_value("B_iso_or_equiv") },
|
||||
{ "pdbx_formal_charge", atom.get_property_value("pdbx_formal_charge") },
|
||||
{ "auth_seq_id", "1" },
|
||||
{ "auth_comp_id", comp_id },
|
||||
{ "auth_asym_id", asym_id },
|
||||
{ "auth_atom_id", atom.get_property("label_atom_id") },
|
||||
{ "auth_atom_id", atom.get_property_value("label_atom_id") },
|
||||
{ "pdbx_PDB_model_num", 1 } });
|
||||
|
||||
auto &newAtom = emplace_atom(std::make_shared<atom::atom_impl>(m_db, atom_id));
|
||||
@@ -2275,13 +2186,13 @@ std::string structure::create_non_poly(const std::string &entity_id, const std::
|
||||
{ "asym_id", asym_id },
|
||||
{ "entity_id", entity_id },
|
||||
{ "mon_id", comp_id },
|
||||
{ "ndb_seq_num", ndb_nr },
|
||||
{ "ndb_seq_num", std::to_string(ndb_nr) },
|
||||
{ "pdb_seq_num", res.get_pdb_seq_num() },
|
||||
{ "auth_seq_num", res.get_pdb_seq_num() },
|
||||
{ "pdb_mon_id", comp_id },
|
||||
{ "auth_mon_id", comp_id },
|
||||
{ "pdb_strand_id", asym_id },
|
||||
{ "pdb_ins_code", "." },
|
||||
{ "pdb_ins_code", nullptr },
|
||||
});
|
||||
|
||||
return asym_id;
|
||||
@@ -2300,7 +2211,7 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
|
||||
{ "entity_id", entity_id },
|
||||
{ "details", "?" } });
|
||||
|
||||
std::string comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
|
||||
auto comp_id = m_db["pdbx_entity_nonpoly"].find1<std::string>("entity_id"_key == entity_id, "comp_id");
|
||||
|
||||
auto &atom_site = m_db["atom_site"];
|
||||
|
||||
@@ -2317,12 +2228,12 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
|
||||
|
||||
atom.set_value_if_empty({ "group_PDB", "HETATM" });
|
||||
atom.set_value_if_empty({ "label_comp_id", comp_id });
|
||||
atom.set_value_if_empty({ "label_seq_id", "." });
|
||||
atom.set_value_if_empty({ "label_seq_id", nullptr });
|
||||
atom.set_value_if_empty({ "auth_comp_id", comp_id });
|
||||
atom.set_value_if_empty({ "auth_seq_id", 1 });
|
||||
atom.set_value_if_empty({ "auth_seq_id", "1" });
|
||||
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
|
||||
atom.set_value_if_empty({ "label_alt_id", "" });
|
||||
atom.set_value_if_empty({ "occupancy", 1.0, 2 });
|
||||
atom.set_value_if_empty({ "occupancy", { 1.0, 2 } });
|
||||
|
||||
auto row = atom_site.emplace(atom.begin(), atom.end());
|
||||
|
||||
@@ -2336,18 +2247,48 @@ std::string structure::create_non_poly(const std::string &entity_id, std::vector
|
||||
{ "asym_id", asym_id },
|
||||
{ "entity_id", entity_id },
|
||||
{ "mon_id", comp_id },
|
||||
{ "ndb_seq_num", ndb_nr },
|
||||
{ "ndb_seq_num", std::to_string(ndb_nr) },
|
||||
{ "pdb_seq_num", res.get_pdb_seq_num() },
|
||||
{ "auth_seq_num", res.get_pdb_seq_num() },
|
||||
{ "pdb_mon_id", comp_id },
|
||||
{ "auth_mon_id", comp_id },
|
||||
{ "pdb_strand_id", asym_id },
|
||||
{ "pdb_ins_code", "." },
|
||||
{ "pdb_ins_code", nullptr },
|
||||
});
|
||||
|
||||
return asym_id;
|
||||
}
|
||||
|
||||
std::string structure::create_non_poly(const std::string &compound_id, bool skip_hydrogen)
|
||||
{
|
||||
auto compound = cif::compound_factory::instance().create(compound_id);
|
||||
if (compound == nullptr)
|
||||
throw std::runtime_error(std::format("{} is not a known compound", compound_id));
|
||||
|
||||
std::vector<cif::row_initializer> atoms;
|
||||
for (auto a : compound->atoms())
|
||||
{
|
||||
// We skip H-atoms, as fitting without H-atoms works better and we avoid conflicts in protonation states between CCD and MONLIB
|
||||
if (skip_hydrogen and cif::atom_type_traits(a.type_symbol).symbol() == "H")
|
||||
continue;
|
||||
|
||||
auto ax = a.get_location().get_x();
|
||||
auto ay = a.get_location().get_y();
|
||||
auto az = a.get_location().get_z();
|
||||
|
||||
atoms.emplace_back(cif::row_initializer{
|
||||
{ "type_symbol", cif::atom_type_traits(a.type_symbol).symbol() },
|
||||
{ "label_atom_id", a.id },
|
||||
{ "auth_atom_id", a.id },
|
||||
{ "Cartn_x", ax },
|
||||
{ "Cartn_y", ay },
|
||||
{ "Cartn_z", az },
|
||||
{ "B_iso_or_equiv", 30.00 } });
|
||||
}
|
||||
|
||||
return create_non_poly(create_non_poly_entity(compound_id), atoms);
|
||||
}
|
||||
|
||||
void structure::create_water(row_initializer atom)
|
||||
{
|
||||
using namespace literals;
|
||||
@@ -2386,11 +2327,11 @@ void structure::create_water(row_initializer atom)
|
||||
|
||||
atom.set_value_if_empty({ "group_PDB", "HETATM" });
|
||||
atom.set_value_if_empty({ "label_comp_id", "HOH" });
|
||||
atom.set_value_if_empty({ "label_seq_id", "." });
|
||||
atom.set_value_if_empty({ "label_seq_id", nullptr });
|
||||
atom.set_value_if_empty({ "auth_comp_id", "HOH" });
|
||||
atom.set_value_if_empty({ "pdbx_PDB_model_num", 1 });
|
||||
atom.set_value_if_empty({ "label_alt_id", "" });
|
||||
atom.set_value_if_empty({ "occupancy", 1.0, 2 });
|
||||
atom.set_value_if_empty({ "occupancy", { 1.0, 2 } });
|
||||
|
||||
auto row = atom_site.emplace(atom.begin(), atom.end());
|
||||
|
||||
@@ -2408,10 +2349,65 @@ void structure::create_water(row_initializer atom)
|
||||
{ "pdb_mon_id", "HOH" },
|
||||
{ "auth_mon_id", "HOH" },
|
||||
{ "pdb_strand_id", asym_id },
|
||||
{ "pdb_ins_code", "." },
|
||||
{ "pdb_ins_code", nullptr },
|
||||
});
|
||||
}
|
||||
|
||||
std::string structure::create_link(atom a1, atom a2, const std::string &link_type, const std::string &role)
|
||||
{
|
||||
using namespace literals;
|
||||
|
||||
auto &struct_conn = m_db["struct_conn"];
|
||||
auto &struct_conn_type = m_db["struct_conn_type"];
|
||||
|
||||
// This will validate link_type :-)
|
||||
if (not struct_conn_type.contains("id"_key == link_type))
|
||||
struct_conn_type.emplace({ { "id", link_type } });
|
||||
|
||||
std::string link_id = struct_conn.get_unique_id(link_type + '_');
|
||||
|
||||
item label_seq_id_1("ptnr1_label_seq_id");
|
||||
if (int nr = a1.get_label_seq_id(); nr != 0)
|
||||
label_seq_id_1.value(std::to_string(nr));
|
||||
|
||||
item label_seq_id_2("ptnr2_label_seq_id");
|
||||
if (int nr = a2.get_label_seq_id(); nr != 0)
|
||||
label_seq_id_2.value(std::to_string(nr));
|
||||
|
||||
struct_conn.emplace(
|
||||
{ //
|
||||
{ "id", link_id },
|
||||
{ "conn_type_id", link_type },
|
||||
{ "pdbx_leaving_atom_flag", "one" },
|
||||
|
||||
{ "ptnr1_label_asym_id", a1.get_label_asym_id() },
|
||||
{ "ptnr1_label_comp_id", a1.get_label_comp_id() },
|
||||
label_seq_id_1,
|
||||
{ "ptnr1_label_atom_id", a1.get_label_atom_id() },
|
||||
{ "pdbx_ptnr1_label_alt_id", a1.get_label_alt_id() },
|
||||
{ "pdbx_ptnr1_PDB_ins_code", a1.get_pdb_ins_code() },
|
||||
{ "ptnr1_auth_asym_id", a1.get_auth_asym_id() },
|
||||
{ "ptnr1_auth_comp_id", a1.get_auth_comp_id() },
|
||||
{ "ptnr1_auth_seq_id", a1.get_auth_seq_id() },
|
||||
{ "ptnr1_symmetry", a1.symmetry() },
|
||||
|
||||
{ "ptnr2_label_asym_id", a2.get_label_asym_id() },
|
||||
{ "ptnr2_label_comp_id", a2.get_label_comp_id() },
|
||||
label_seq_id_2,
|
||||
{ "ptnr2_label_atom_id", a2.get_label_atom_id() },
|
||||
{ "pdbx_ptnr2_label_alt_id", a2.get_label_alt_id() },
|
||||
{ "pdbx_ptnr2_PDB_ins_code", a2.get_pdb_ins_code() },
|
||||
{ "ptnr2_auth_asym_id", a2.get_auth_asym_id() },
|
||||
{ "ptnr2_auth_comp_id", a2.get_auth_comp_id() },
|
||||
{ "ptnr2_auth_seq_id", a2.get_auth_seq_id() },
|
||||
{ "ptnr2_symmetry", a2.symmetry() },
|
||||
|
||||
{ "pdbx_dist_value", { distance(a1.get_location(), a2.get_location()), 3 } },
|
||||
{ "pdbx_role", role } });
|
||||
|
||||
return link_id;
|
||||
}
|
||||
|
||||
branch &structure::create_branch()
|
||||
{
|
||||
auto &entity = m_db["entity"];
|
||||
@@ -2466,7 +2462,7 @@ branch &structure::create_branch()
|
||||
|
||||
// // atom.set_value_if_empty({"group_PDB", "HETATM"});
|
||||
// // atom.set_value_if_empty({"label_comp_id", "NAG"});
|
||||
// // atom.set_value_if_empty({"label_seq_id", "."});
|
||||
// // atom.set_value_if_empty({"label_seq_id", nullptr});
|
||||
// // atom.set_value_if_empty({"auth_comp_id", "NAG"});
|
||||
// // atom.set_value_if_empty({"pdbx_PDB_model_num", 1});
|
||||
// // atom.set_value_if_empty({"label_alt_id", ""});
|
||||
@@ -2615,7 +2611,7 @@ std::string structure::create_entity_for_branch(branch &branch)
|
||||
|
||||
auto &entity = m_db["entity"];
|
||||
|
||||
std::string entityID = entity.find_first<std::string>("type"_key == "branched" and "pdbx_description"_key == entityName, "id");
|
||||
auto entityID = entity.find_first<std::string>("type"_key == "branched" and "pdbx_description"_key == entityName, "id");
|
||||
|
||||
if (entityID.empty())
|
||||
{
|
||||
@@ -2628,7 +2624,7 @@ std::string structure::create_entity_for_branch(branch &branch)
|
||||
{ "type", "branched" },
|
||||
{ "src_method", "man" },
|
||||
{ "pdbx_description", entityName },
|
||||
{ "formula_weight", branch.weight() } });
|
||||
{ "formula_weight", { branch.weight(), 3 } } });
|
||||
|
||||
auto &pdbx_entity_branch_list = m_db["pdbx_entity_branch_list"];
|
||||
for (auto &sugar : branch)
|
||||
@@ -2682,7 +2678,7 @@ void structure::cleanup_empty_categories()
|
||||
|
||||
for (auto chemComp : chem_comp)
|
||||
{
|
||||
std::string compID = chemComp["id"].as<std::string>();
|
||||
auto compID = chemComp["id"].as<std::string>();
|
||||
if (atomSite.contains("label_comp_id"_key == compID or "auth_comp_id"_key == compID) or
|
||||
pdbxPolySeqScheme.contains("mon_id"_key == compID or "auth_mon_id"_key == compID or "pdb_mon_id"_key == compID) or
|
||||
entityPolySeq.contains("mon_id"_key == compID))
|
||||
@@ -2703,7 +2699,7 @@ void structure::cleanup_empty_categories()
|
||||
|
||||
for (auto entity : entities)
|
||||
{
|
||||
std::string entityID = entity["id"].as<std::string>();
|
||||
auto entityID = entity["id"].as<std::string>();
|
||||
if (atomSite.contains("label_entity_id"_key == entityID))
|
||||
continue;
|
||||
|
||||
@@ -2714,7 +2710,7 @@ void structure::cleanup_empty_categories()
|
||||
|
||||
for (auto entity : obsoleteEntities)
|
||||
{
|
||||
std::string entityID = entity["id"].as<std::string>();
|
||||
auto entityID = entity["id"].as<std::string>();
|
||||
if (validator)
|
||||
{
|
||||
for (auto linked : validator->get_links_for_parent("entity"))
|
||||
@@ -2801,19 +2797,12 @@ void structure::validate_atoms() const
|
||||
|
||||
std::vector<atom> atoms = m_atoms;
|
||||
|
||||
auto removeAtomFromList = [&atoms](const atom &a)
|
||||
{
|
||||
auto i = std::find(atoms.begin(), atoms.end(), a);
|
||||
assert(i != atoms.end());
|
||||
atoms.erase(i);
|
||||
};
|
||||
|
||||
for (auto &poly : m_polymers)
|
||||
{
|
||||
for (auto &monomer : poly)
|
||||
{
|
||||
for (auto &atom : monomer.atoms())
|
||||
removeAtomFromList(atom);
|
||||
std::erase(atoms, atom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2822,14 +2811,14 @@ void structure::validate_atoms() const
|
||||
for (auto &sugar : branch)
|
||||
{
|
||||
for (auto &atom : sugar.atoms())
|
||||
removeAtomFromList(atom);
|
||||
std::erase(atoms, atom);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &res : m_non_polymers)
|
||||
{
|
||||
for (auto &atom : res.atoms())
|
||||
removeAtomFromList(atom);
|
||||
std::erase(atoms, atom);
|
||||
}
|
||||
|
||||
assert(atoms.empty());
|
||||
@@ -2845,10 +2834,10 @@ static int compare_numbers(std::string_view a, std::string_view b)
|
||||
|
||||
std::from_chars_result ra, rb;
|
||||
|
||||
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
|
||||
ra = from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = from_chars(b.data(), b.data() + b.length(), db);
|
||||
|
||||
if (not(bool) ra.ec and not(bool) rb.ec)
|
||||
if (ra.ec == std::errc{} and rb.ec == std::errc{})
|
||||
{
|
||||
auto d = da - db;
|
||||
if (std::abs(d) > std::numeric_limits<double>::epsilon())
|
||||
@@ -2859,7 +2848,7 @@ static int compare_numbers(std::string_view a, std::string_view b)
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
else if ((bool)ra.ec)
|
||||
else if (ra.ec != std::errc{})
|
||||
result = 1;
|
||||
else
|
||||
result = -1;
|
||||
|
||||
326
src/parser.cpp
326
src/parser.cpp
@@ -24,15 +24,16 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/utilities.hpp"
|
||||
#include "cif++/forward_decl.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
|
||||
#include "cif++/file.hpp"
|
||||
#include "cif++/forward_decl.hpp"
|
||||
#include "cif++/item.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace cif
|
||||
class reserved_words_automaton
|
||||
{
|
||||
public:
|
||||
reserved_words_automaton() {}
|
||||
reserved_words_automaton() = default;
|
||||
|
||||
enum move_result
|
||||
{
|
||||
@@ -56,14 +57,14 @@ class reserved_words_automaton
|
||||
stop
|
||||
};
|
||||
|
||||
constexpr bool finished() const
|
||||
[[nodiscard]] constexpr bool finished() const
|
||||
{
|
||||
return m_state <= 0;
|
||||
return m_state <= 0;
|
||||
}
|
||||
|
||||
constexpr bool matched() const
|
||||
[[nodiscard]] constexpr bool matched() const
|
||||
{
|
||||
return m_state < 0;
|
||||
return m_state < 0;
|
||||
}
|
||||
|
||||
constexpr move_result move(int ch)
|
||||
@@ -75,7 +76,7 @@ class reserved_words_automaton
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -1: // data_
|
||||
case -1: // data_
|
||||
if (sac_parser::is_non_blank(ch))
|
||||
m_seen_trailing_chars = true;
|
||||
else if (m_seen_trailing_chars)
|
||||
@@ -84,15 +85,15 @@ class reserved_words_automaton
|
||||
result = no_keyword;
|
||||
break;
|
||||
|
||||
case -2: // global_
|
||||
case -2: // global_
|
||||
result = sac_parser::is_non_blank(ch) ? no_keyword : global;
|
||||
break;
|
||||
|
||||
case -3: // loop_
|
||||
case -3: // loop_
|
||||
result = sac_parser::is_non_blank(ch) ? no_keyword : loop;
|
||||
break;
|
||||
|
||||
case -4: // save_
|
||||
case -4: // save_
|
||||
if (sac_parser::is_non_blank(ch))
|
||||
m_seen_trailing_chars = true;
|
||||
else if (m_seen_trailing_chars)
|
||||
@@ -101,10 +102,10 @@ class reserved_words_automaton
|
||||
result = save;
|
||||
break;
|
||||
|
||||
case -5: // stop_
|
||||
case -5: // stop_
|
||||
result = sac_parser::is_non_blank(ch) ? no_keyword : stop;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
assert(m_state > 0 and m_state < NODE_COUNT);
|
||||
|
||||
@@ -137,17 +138,17 @@ class reserved_words_automaton
|
||||
static constexpr struct node
|
||||
{
|
||||
int16_t ch;
|
||||
int8_t next_match;
|
||||
int8_t next_nomatch;
|
||||
int next_match;
|
||||
int next_nomatch;
|
||||
} s_dag[] = {
|
||||
{ 0 },
|
||||
{ 'D', 5, 2 },
|
||||
{ 'G', 9, 3 },
|
||||
{ 'D', 5, 2 },
|
||||
{ 'G', 9, 3 },
|
||||
{ 'L', 15, 4 },
|
||||
{ 'S', 19, 0 },
|
||||
{ 'A', 6, 0 },
|
||||
{ 'T', 7, 0 },
|
||||
{ 'A', 8, 0 },
|
||||
{ 'A', 6, 0 },
|
||||
{ 'T', 7, 0 },
|
||||
{ 'A', 8, 0 },
|
||||
{ '_', -1, 0 },
|
||||
{ 'L', 10, 0 },
|
||||
{ 'O', 11, 0 },
|
||||
@@ -155,7 +156,7 @@ class reserved_words_automaton
|
||||
{ 'A', 13, 0 },
|
||||
{ 'L', 14, 0 },
|
||||
{ '_', -2, 0 },
|
||||
{ 'O', 16, 0},
|
||||
{ 'O', 16, 0 },
|
||||
{ 'O', 17, 0 },
|
||||
{ 'P', 18, 0 },
|
||||
{ '_', -3, 0 },
|
||||
@@ -238,7 +239,7 @@ int sac_parser::get_next_char()
|
||||
}
|
||||
else if (result == '\n')
|
||||
++m_line_nr;
|
||||
|
||||
|
||||
m_token_buffer.push_back(std::char_traits<char>::to_char_type(result));
|
||||
}
|
||||
|
||||
@@ -277,6 +278,9 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
m_token_buffer.clear();
|
||||
m_token_value = {};
|
||||
|
||||
bool negative = false;
|
||||
m_float_precision = 0;
|
||||
|
||||
reserved_words_automaton dag;
|
||||
|
||||
while (result == CIFToken::UNKNOWN)
|
||||
@@ -300,7 +304,12 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
else if (ch == '_')
|
||||
state = State::ItemName;
|
||||
else if (ch == ';' and m_bol)
|
||||
state = State::TextItem;
|
||||
{
|
||||
if (m_backslash_strings)
|
||||
state = State::TextItemBS;
|
||||
else
|
||||
state = State::TextItem;
|
||||
}
|
||||
else if (ch == '?')
|
||||
state = State::QuestionMark;
|
||||
else if (ch == '\'' or ch == '"')
|
||||
@@ -310,6 +319,17 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
}
|
||||
else if (dag.move(ch) == reserved_words_automaton::undefined)
|
||||
state = State::Reserved;
|
||||
else if (ch == '+' or ch == '-')
|
||||
{
|
||||
negative = true;
|
||||
state = State::Numeric_Integer;
|
||||
}
|
||||
else if (ch == '0')
|
||||
state = State::Numeric_Zero;
|
||||
else if (ch >= '1' and ch <= '9')
|
||||
state = State::Numeric_Integer;
|
||||
else if (ch == '.')
|
||||
state = State::Numeric_Float;
|
||||
else
|
||||
state = State::Value;
|
||||
break;
|
||||
@@ -326,12 +346,14 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
else
|
||||
m_bol = (ch == '\n');
|
||||
break;
|
||||
|
||||
|
||||
case State::Comment:
|
||||
if (ch == '\n')
|
||||
{
|
||||
state = State::Start;
|
||||
m_bol = true;
|
||||
if (m_token_buffer.size() == 3 and m_token_buffer == std::vector{ '#', '\\', '\n' })
|
||||
m_backslash_strings = true;
|
||||
m_token_buffer.clear();
|
||||
}
|
||||
else if (ch == kEOF)
|
||||
@@ -339,24 +361,63 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
else if (not is_any_print(ch))
|
||||
error("invalid character in comment");
|
||||
break;
|
||||
|
||||
|
||||
case State::QuestionMark:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE;
|
||||
result = CIFToken::VALUE_UNKNOWN;
|
||||
}
|
||||
else
|
||||
state = State::Value;
|
||||
break;
|
||||
|
||||
case State::TextItemBS:
|
||||
if (ch == '\\')
|
||||
{
|
||||
state = State::TextItemBS2;
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
case State::TextItem:
|
||||
if (ch == '\n')
|
||||
state = State::TextItemNL;
|
||||
else if (ch == kEOF)
|
||||
error("unterminated textfield");
|
||||
else if (not is_any_print(ch) and cif::VERBOSE > 2)
|
||||
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
|
||||
else if (not is_any_print(ch) and VERBOSE > 2)
|
||||
warning("invalid character in text field '" + std::string({ static_cast<char>(ch) }) + "' (" + std::to_string(ch) + ")");
|
||||
break;
|
||||
|
||||
case State::TextItemBS2:
|
||||
if (ch == '\n')
|
||||
{
|
||||
if (m_token_buffer[m_token_buffer.size() - 2] == '\\')
|
||||
{
|
||||
m_token_buffer.pop_back();
|
||||
m_token_buffer.pop_back();
|
||||
}
|
||||
state = State::TextItemBSNL;
|
||||
}
|
||||
else if (ch == kEOF)
|
||||
error("unterminated textfield");
|
||||
else if (not is_any_print(ch) and VERBOSE > 2)
|
||||
warning("invalid character in text field '" + std::string({ static_cast<char>(ch) }) + "' (" + std::to_string(ch) + ")");
|
||||
break;
|
||||
|
||||
case State::TextItemBSNL:
|
||||
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
|
||||
state = State::TextItemBS;
|
||||
else if (ch == ';')
|
||||
{
|
||||
assert(m_token_buffer.size() >= 2);
|
||||
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
|
||||
result = CIFToken::VALUE_CHARSTRING;
|
||||
}
|
||||
else if (ch == kEOF)
|
||||
error("unterminated textfield");
|
||||
else if (ch != '\n')
|
||||
error("invalid character in text field");
|
||||
break;
|
||||
|
||||
case State::TextItemNL:
|
||||
@@ -366,7 +427,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
{
|
||||
assert(m_token_buffer.size() >= 2);
|
||||
m_token_value = std::string_view(m_token_buffer.data() + 1, m_token_buffer.size() - 3);
|
||||
result = CIFToken::VALUE;
|
||||
result = CIFToken::VALUE_TEXTFIELD;
|
||||
}
|
||||
else if (ch == kEOF)
|
||||
error("unterminated textfield");
|
||||
@@ -379,15 +440,15 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
error("unterminated quoted string");
|
||||
else if (ch == quoteChar)
|
||||
state = State::QuotedStringQuote;
|
||||
else if (not is_any_print(ch) and cif::VERBOSE > 2)
|
||||
warning("invalid character in quoted string: '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
|
||||
else if (not is_any_print(ch) and VERBOSE > 2)
|
||||
warning("invalid character in quoted string: '" + std::string({ static_cast<char>(ch) }) + "' (" + std::to_string(ch) + ")");
|
||||
break;
|
||||
|
||||
case State::QuotedStringQuote:
|
||||
if (is_white(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE;
|
||||
result = CIFToken::VALUE_CHARSTRING;
|
||||
if (m_token_buffer.size() < 2)
|
||||
error("Invalid quoted string token");
|
||||
|
||||
@@ -422,7 +483,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE;
|
||||
result = CIFToken::VALUE_CHARSTRING;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
}
|
||||
else
|
||||
@@ -463,11 +524,84 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Numeric_Zero:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE_NUMERIC_INTEGER;
|
||||
}
|
||||
else if (ch == '.')
|
||||
state = State::Numeric_Float;
|
||||
else
|
||||
state = State::Value;
|
||||
break;
|
||||
|
||||
case State::Numeric_Integer:
|
||||
if (ch == '.')
|
||||
state = State::Numeric_Float;
|
||||
else if (ch == 'e' or ch == 'E')
|
||||
state = State::Numeric_Exponent1;
|
||||
else if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
if (m_token_buffer.size() == 1 and negative)
|
||||
{
|
||||
result = CIFToken::VALUE_CHARSTRING; // A single hyphen...
|
||||
m_token_value = std::string_view{ m_token_buffer.data(), m_token_buffer.data() + 1 };
|
||||
}
|
||||
else
|
||||
result = CIFToken::VALUE_NUMERIC_INTEGER;
|
||||
}
|
||||
else if (ch < '0' or ch > '9')
|
||||
state = State::Value;
|
||||
break;
|
||||
|
||||
case State::Numeric_Float:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
if (m_token_buffer.size() == 1)
|
||||
result = CIFToken::VALUE_INAPPLICABLE;
|
||||
else
|
||||
result = CIFToken::VALUE_NUMERIC_FLOAT;
|
||||
}
|
||||
else if (ch == 'e' or ch == 'E')
|
||||
state = State::Numeric_Exponent1;
|
||||
else if (ch < '0' or ch > '9')
|
||||
state = State::Value;
|
||||
else
|
||||
++m_float_precision;
|
||||
break;
|
||||
|
||||
case State::Numeric_Exponent1:
|
||||
if (ch == '+' or ch == '-' or (ch >= '0' and ch <= '9'))
|
||||
state = State::Numeric_Exponent2;
|
||||
else
|
||||
{
|
||||
// warning(std::format("parsing {}: Invalid floating point value, expected digit or sign character", std::string_view{ m_token_buffer.data(), m_token_buffer.size() }));
|
||||
state = State::Value;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Numeric_Exponent2:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE_NUMERIC_FLOAT;
|
||||
}
|
||||
else if (ch < '0' or ch > '9')
|
||||
{
|
||||
if (VERBOSE > 0)
|
||||
// warning(std::format("parsing {}: Invalid floating point value, expected digit or sign character", std::string_view{ m_token_buffer.data(), m_token_buffer.size() }));
|
||||
state = State::Value;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Value:
|
||||
if (not is_non_blank(ch))
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE;
|
||||
result = CIFToken::VALUE_CHARSTRING;
|
||||
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
|
||||
break;
|
||||
}
|
||||
@@ -480,12 +614,25 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
}
|
||||
}
|
||||
|
||||
if (VERBOSE >= 5)
|
||||
// if (VERBOSE >= 5)
|
||||
// {
|
||||
// std::cerr << get_token_name(result);
|
||||
// if (result != CIFToken::END_OF_FILE)
|
||||
// std::cerr << " " << std::quoted(m_token_value);
|
||||
// std::cerr << '\n';
|
||||
// }
|
||||
|
||||
if (result == CIFToken::VALUE_NUMERIC_INTEGER)
|
||||
{
|
||||
std::cerr << get_token_name(result);
|
||||
if (result != CIFToken::END_OF_FILE)
|
||||
std::cerr << " " << std::quoted(m_token_value);
|
||||
std::cerr << '\n';
|
||||
auto [ptr, ec] = std::from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_int);
|
||||
if (ec != std::errc{})
|
||||
error("Invalid integer value: " + std::make_error_code(ec).message());
|
||||
}
|
||||
else if (result == CIFToken::VALUE_NUMERIC_FLOAT)
|
||||
{
|
||||
auto [ptr, ec] = std::from_chars(m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size(), m_token_value_float);
|
||||
if (ec != std::errc{})
|
||||
error("Invalid integer value: " + std::make_error_code(ec).message());
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -540,6 +687,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock)
|
||||
if (bol)
|
||||
state = qstring;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -633,6 +781,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
|
||||
if (bol)
|
||||
state = qstring;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -661,7 +810,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
|
||||
case data:
|
||||
if (dblk[si] == 0 and is_non_blank(ch))
|
||||
{
|
||||
datablock = {static_cast<char>(ch)};
|
||||
datablock = { static_cast<char>(ch) };
|
||||
state = data_name;
|
||||
}
|
||||
else if (dblk[si++] != ch)
|
||||
@@ -670,7 +819,7 @@ sac_parser::datablock_index sac_parser::index_datablocks()
|
||||
|
||||
case data_name:
|
||||
if (is_non_blank(ch))
|
||||
datablock.insert(datablock.end(), (char)std::toupper(ch));
|
||||
datablock.insert(datablock.end(), static_cast<char>(std::toupper(ch)));
|
||||
else if (is_space(ch))
|
||||
{
|
||||
if (not datablock.empty())
|
||||
@@ -738,14 +887,17 @@ void sac_parser::parse_global()
|
||||
while (m_lookahead == CIFToken::ITEM_NAME)
|
||||
{
|
||||
match(CIFToken::ITEM_NAME);
|
||||
match(CIFToken::VALUE);
|
||||
if (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
|
||||
match(m_lookahead);
|
||||
else
|
||||
match(CIFToken::VALUE_CHARSTRING);
|
||||
}
|
||||
}
|
||||
|
||||
void sac_parser::parse_datablock()
|
||||
{
|
||||
static const std::string kUnitializedCategory("<invalid>");
|
||||
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
|
||||
std::string cat = kUnitializedCategory; // intial value acts as a guard for empty category names
|
||||
|
||||
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME or m_lookahead == CIFToken::SAVE_NAME)
|
||||
{
|
||||
@@ -777,14 +929,38 @@ void sac_parser::parse_datablock()
|
||||
match(CIFToken::ITEM_NAME);
|
||||
}
|
||||
|
||||
while (m_lookahead == CIFToken::VALUE)
|
||||
while (m_lookahead >= CIFToken::VALUE_INAPPLICABLE)
|
||||
{
|
||||
produce_row();
|
||||
|
||||
for (auto item_name : item_names)
|
||||
{
|
||||
produce_item(cat, item_name, m_token_value);
|
||||
match(CIFToken::VALUE);
|
||||
switch (m_lookahead)
|
||||
{
|
||||
case CIFToken::VALUE_INAPPLICABLE:
|
||||
produce_item(cat, item_name, item_value_type::INAPPLICABLE);
|
||||
match(m_lookahead);
|
||||
break;
|
||||
case CIFToken::VALUE_UNKNOWN:
|
||||
produce_item(cat, item_name, item_value_type::MISSING);
|
||||
match(m_lookahead);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_INTEGER:
|
||||
produce_item(cat, item_name, m_token_value_int);
|
||||
match(m_lookahead);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_FLOAT:
|
||||
produce_item(cat, item_name, { m_token_value_float, m_float_precision });
|
||||
match(m_lookahead);
|
||||
break;
|
||||
case CIFToken::VALUE_CHARSTRING:
|
||||
case CIFToken::VALUE_TEXTFIELD:
|
||||
produce_item(cat, item_name, m_token_value);
|
||||
match(m_lookahead);
|
||||
break;
|
||||
default:;
|
||||
match(CIFToken::VALUE_CHARSTRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,9 +982,33 @@ void sac_parser::parse_datablock()
|
||||
|
||||
match(CIFToken::ITEM_NAME);
|
||||
|
||||
produce_item(cat, itemName, m_token_value);
|
||||
switch (m_lookahead)
|
||||
{
|
||||
case CIFToken::VALUE_INAPPLICABLE:
|
||||
produce_item(cat, itemName, item_value_type::INAPPLICABLE);
|
||||
match(CIFToken::VALUE_INAPPLICABLE);
|
||||
break;
|
||||
case CIFToken::VALUE_UNKNOWN:
|
||||
produce_item(cat, itemName, item_value_type::MISSING);
|
||||
match(CIFToken::VALUE_UNKNOWN);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_INTEGER:
|
||||
produce_item(cat, itemName, m_token_value_int);
|
||||
match(CIFToken::VALUE_NUMERIC_INTEGER);
|
||||
break;
|
||||
case CIFToken::VALUE_NUMERIC_FLOAT:
|
||||
produce_item(cat, itemName, { m_token_value_float, m_float_precision });
|
||||
match(CIFToken::VALUE_NUMERIC_FLOAT);
|
||||
break;
|
||||
case CIFToken::VALUE_CHARSTRING:
|
||||
case CIFToken::VALUE_TEXTFIELD:
|
||||
produce_item(cat, itemName, m_token_value);
|
||||
match(m_lookahead);
|
||||
break;
|
||||
default:
|
||||
match(CIFToken::VALUE_CHARSTRING);
|
||||
}
|
||||
|
||||
match(CIFToken::VALUE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -847,7 +1047,9 @@ void parser::produce_category(std::string_view name)
|
||||
if (VERBOSE >= 4)
|
||||
std::cerr << "producing category " << name << '\n';
|
||||
|
||||
const auto &[cat, ignore] = m_datablock->emplace(name);
|
||||
const auto &[cat, is_new] = m_datablock->emplace(name);
|
||||
if (is_new and m_validator)
|
||||
cat->set_validator(m_validator, *m_datablock);
|
||||
m_category = &*cat;
|
||||
}
|
||||
|
||||
@@ -859,12 +1061,11 @@ void parser::produce_row()
|
||||
if (m_category == nullptr)
|
||||
error("inconsistent categories in loop_");
|
||||
|
||||
m_category->emplace({});
|
||||
m_row = m_category->back();
|
||||
// m_row.lineNr(m_line_nr);
|
||||
auto i = m_category->emplace({});
|
||||
m_row = *i;
|
||||
}
|
||||
|
||||
void parser::produce_item(std::string_view category, std::string_view item, std::string_view value)
|
||||
void parser::produce_item(std::string_view category, std::string_view item, item_value value)
|
||||
{
|
||||
if (VERBOSE >= 4)
|
||||
std::cerr << "producing _" << category << '.' << item << " -> " << value << '\n';
|
||||
@@ -872,7 +1073,20 @@ void parser::produce_item(std::string_view category, std::string_view item, std:
|
||||
if (m_category == nullptr or not iequals(category, m_category->name()))
|
||||
error("inconsistent categories in loop_");
|
||||
|
||||
m_row[item] = m_token_value;
|
||||
if (value.is_number())
|
||||
{
|
||||
auto cv = m_category->get_cat_validator();
|
||||
if (cv != nullptr)
|
||||
{
|
||||
if (auto iv = cv->get_validator_for_item(item))
|
||||
{
|
||||
if (auto tv = iv->m_type; tv and tv->m_primitive_type != DDL_PrimitiveType::Numb)
|
||||
value = std::string_view{ m_token_buffer.data(), m_token_buffer.data() + m_token_buffer.size() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_row[item].set(value, false);
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -26,10 +26,13 @@
|
||||
|
||||
#include "pdb2cif_remark_3.hpp"
|
||||
|
||||
#include <cif++.hpp>
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <cif++.hpp>
|
||||
#include <utility>
|
||||
|
||||
// NOLINTBEGIN(bugprone-empty-catch)
|
||||
|
||||
namespace cif::pdb
|
||||
{
|
||||
@@ -153,7 +156,7 @@ const TemplateLine kBusterTNT_Template[] = {
|
||||
class BUSTER_TNT_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
BUSTER_TNT_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
BUSTER_TNT_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db,
|
||||
kBusterTNT_Template, sizeof(kBusterTNT_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((BUSTER(?:-TNT)?)(?: (\d+(?:\..+)?))?)"))
|
||||
@@ -237,18 +240,28 @@ const TemplateLine kCNS_Template[] = {
|
||||
/* 72 */ { R"(METHOD USED\s*:\s*(.+))", 1, "refine", { "solvent_model_details" } },
|
||||
/* 73 */ { R"(KSOL\s*:\s*(.+))", 1, "refine", { "solvent_model_param_ksol" } },
|
||||
/* 74 */ { R"(BSOL\s*:\s*(.+))", 1, "refine", { "solvent_model_param_bsol" } },
|
||||
/* 75 */ { R"(NCS MODEL\s*:\s*(.+))", 1, /* "refine_ls_restr_ncs", { "ncs_model_details" } */ },
|
||||
/* 75 */ { R"(NCS MODEL\s*:\s*(.+))",
|
||||
1,
|
||||
/* "refine_ls_restr_ncs", { "ncs_model_details" } */ },
|
||||
/* 76 */ { R"(NCS RESTRAINTS\. RMS SIGMA/WEIGHT)", 1 },
|
||||
/* 77 */ { R"(GROUP (\d+) POSITIONAL \(A\)\s*:\s*(.+))", 1, /* "refine_ls_restr_ncs", { "dom_id", "rms_dev_position", "weight_position" } */ },
|
||||
/* 78 */ { R"(GROUP (\d+) B-FACTOR \(A\*\*2\)\s*:\s*(.+))", 1, /* "refine_ls_restr_ncs", { "dom_id", "rms_dev_B_iso", "weight_B_iso" } */ },
|
||||
/* 79 */ { R"(PARAMETER FILE (\d+) :\s+(.+))", 1, /* "pdbx_xplor_file", { "serial_no", "param_file" } */ },
|
||||
/* 80 */ { R"(TOPOLOGY FILE (\d+) :\s+(.+))", 1, /* "pdbx_xplor_file", { "serial_no", "topol_file" } */ },
|
||||
/* 77 */ { R"(GROUP (\d+) POSITIONAL \(A\)\s*:\s*(.+))",
|
||||
1,
|
||||
/* "refine_ls_restr_ncs", { "dom_id", "rms_dev_position", "weight_position" } */ },
|
||||
/* 78 */ { R"(GROUP (\d+) B-FACTOR \(A\*\*2\)\s*:\s*(.+))",
|
||||
1,
|
||||
/* "refine_ls_restr_ncs", { "dom_id", "rms_dev_B_iso", "weight_B_iso" } */ },
|
||||
/* 79 */ { R"(PARAMETER FILE (\d+) :\s+(.+))",
|
||||
1,
|
||||
/* "pdbx_xplor_file", { "serial_no", "param_file" } */ },
|
||||
/* 80 */ { R"(TOPOLOGY FILE (\d+) :\s+(.+))",
|
||||
1,
|
||||
/* "pdbx_xplor_file", { "serial_no", "topol_file" } */ },
|
||||
};
|
||||
|
||||
class CNS_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
CNS_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
CNS_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kCNS_Template,
|
||||
sizeof(kCNS_Template) / sizeof(TemplateLine), std::regex(R"((CN[SX])(?: (\d+(?:\.\d+)?))?)"))
|
||||
{
|
||||
@@ -332,13 +345,13 @@ const TemplateLine kPHENIX_Template[] = {
|
||||
class PHENIX_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
PHENIX_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
PHENIX_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kPHENIX_Template, sizeof(kPHENIX_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((PHENIX)(?: \(PHENIX\.REFINE:) (\d+(?:\.[^)]+)?)\)?)"))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void fixup();
|
||||
void fixup() override;
|
||||
};
|
||||
|
||||
void PHENIX_Remark3Parser::fixup()
|
||||
@@ -347,7 +360,7 @@ void PHENIX_Remark3Parser::fixup()
|
||||
{
|
||||
try
|
||||
{
|
||||
float val = r["percent_reflns_obs"].as<float>();
|
||||
auto val = r["percent_reflns_obs"].as<float>();
|
||||
int perc = static_cast<int>(val * 100);
|
||||
r["percent_reflns_obs"] = perc;
|
||||
}
|
||||
@@ -420,13 +433,13 @@ const TemplateLine kNUCLSQ_Template[] = {
|
||||
class NUCLSQ_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
NUCLSQ_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
NUCLSQ_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kNUCLSQ_Template, sizeof(kNUCLSQ_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((NUCLSQ)(?: (\d+(?:\.\d+)?))?)"))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void fixup()
|
||||
void fixup() override
|
||||
{
|
||||
for (auto r : mDb["refine_hist"])
|
||||
{
|
||||
@@ -513,13 +526,13 @@ const TemplateLine kPROLSQ_Template[] = {
|
||||
class PROLSQ_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
PROLSQ_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
PROLSQ_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kPROLSQ_Template, sizeof(kPROLSQ_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((PROLSQ)(?: (\d+(?:\.\d+)?))?)"))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void fixup()
|
||||
void fixup() override
|
||||
{
|
||||
for (auto r : mDb["refine_hist"])
|
||||
{
|
||||
@@ -556,7 +569,9 @@ const TemplateLine kREFMAC_Template[] = {
|
||||
/* 17 */ { R"(NUCLEIC ACID ATOMS\s*:\s*(.+))", 1, "refine_hist", { "pdbx_number_atoms_nucleic_acid" } },
|
||||
/* 18 */ { R"(HETEROGEN ATOMS\s*:\s*(.+))", 1, "refine_hist", { "pdbx_number_atoms_ligand" } },
|
||||
/* 19 */ { R"(SOLVENT ATOMS\s*:\s*(.+))", 1, "refine_hist", { "number_atoms_solvent" } },
|
||||
/* 20 */ { R"(ALL ATOMS\s*:\s*(.+))", 1, /* "refine_hist", "pdbx_number_atoms_protein" */ },
|
||||
/* 20 */ { R"(ALL ATOMS\s*:\s*(.+))",
|
||||
1,
|
||||
/* "refine_hist", "pdbx_number_atoms_protein" */ },
|
||||
/* 21 */ { R"(B VALUES\..*)", 1 },
|
||||
/* 22 */ { R"(B VALUE TYPE\s*:\s*(.+))", 1, "refine", { "pdbx_TLS_residual_ADP_flag" } },
|
||||
/* 23 */ { R"(FROM WILSON PLOT \(A\*\*2\)\s*:\s*(.+))", 1, "reflns", { "B_iso_Wilson_estimate" } },
|
||||
@@ -601,14 +616,14 @@ const TemplateLine kREFMAC_Template[] = {
|
||||
class REFMAC_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
REFMAC_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
REFMAC_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kREFMAC_Template, sizeof(kREFMAC_Template) / sizeof(TemplateLine),
|
||||
std::regex(".+"))
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string program() { return "REFMAC"; }
|
||||
virtual std::string version() { return ""; }
|
||||
std::string program() override { return "REFMAC"; }
|
||||
std::string version() override { return ""; }
|
||||
};
|
||||
|
||||
const TemplateLine kREFMAC5_Template[] = {
|
||||
@@ -641,7 +656,9 @@ const TemplateLine kREFMAC5_Template[] = {
|
||||
/* 26 */ { R"(NUCLEIC ACID ATOMS\s*:\s*(.+))", 1, "refine_hist", { "pdbx_number_atoms_nucleic_acid" } },
|
||||
/* 27 */ { R"(HETEROGEN ATOMS\s*:\s*(.+))", 1, "refine_hist", { "pdbx_number_atoms_ligand" } },
|
||||
/* 28 */ { R"(SOLVENT ATOMS\s*:\s*(.+))", 1, "refine_hist", { "number_atoms_solvent" } },
|
||||
/* 29 */ { R"(ALL ATOMS\s*:\s*(.+))", 1, /* "refine_hist", { "pdbx_number_atoms_protein" } */ },
|
||||
/* 29 */ { R"(ALL ATOMS\s*:\s*(.+))",
|
||||
1,
|
||||
/* "refine_hist", { "pdbx_number_atoms_protein" } */ },
|
||||
/* 30 */ { R"(B VALUES\..*)", 1 },
|
||||
/* 31 */ { R"(B VALUE TYPE\s*:\s*(.+))", 1, "refine", { "pdbx_TLS_residual_ADP_flag" } },
|
||||
/* 32 */ { R"(FROM WILSON PLOT \(A\*\*2\)\s*:\s*(.+))", 1, "reflns", { "B_iso_Wilson_estimate" } },
|
||||
@@ -705,8 +722,12 @@ const TemplateLine kREFMAC5_Template[] = {
|
||||
// Simply ignore NCS, you can ask Robbie why
|
||||
/* 90 */ { R"(NCS RESTRAINTS STATISTICS)", 1 },
|
||||
/* 91 */ { R"(NUMBER OF DIFFERENT NCS GROUPS\s*:\s*(.+))", 1 },
|
||||
/* 92 */ { R"(NCS GROUP NUMBER\s*:\s*(\d+))", 1, /*"struct_ncs_dom", { "pdbx_ens_id" }*/ },
|
||||
/* 93 */ { R"(CHAIN NAMES\s*:\s*(.+))", 1, /*"struct_ncs_dom", { "details" }*/ },
|
||||
/* 92 */ { R"(NCS GROUP NUMBER\s*:\s*(\d+))",
|
||||
1,
|
||||
/*"struct_ncs_dom", { "pdbx_ens_id" }*/ },
|
||||
/* 93 */ { R"(CHAIN NAMES\s*:\s*(.+))",
|
||||
1,
|
||||
/*"struct_ncs_dom", { "details" }*/ },
|
||||
/* 94 */ { R"(NUMBER OF COMPONENTS NCS GROUP\s*:\s*(\d+))", 1 },
|
||||
/* 95 */ { R"(COMPONENT C SSSEQI TO C SSSEQI CODE)", 1 },
|
||||
//// This sucks.... The following line is fixed format
|
||||
@@ -719,7 +740,9 @@ const TemplateLine kREFMAC5_Template[] = {
|
||||
/* 102 */ { R"(TIGHT THERMAL\s+\d+\s+(.)\s+\(A\*\*2\):\s+(\d+)\s*;\s*(\d+(?:\.\d*)?)\s*;\s*(\d+(?:\.\d*)?))", 0 }, // , "refine_ls_restr_ncs", {"pdbx_auth_asym_id", "pdbx_number", "rms_dev_position", "weight_position"}, { "pdbx_type", "tight thermal", }, 1 },
|
||||
/* 103 */ { R"(MEDIUM THERMAL\s+\d+\s+(.)\s+\(A\*\*2\):\s+(\d+)\s*;\s*(\d+(?:\.\d*)?)\s*;\s*(\d+(?:\.\d*)?))", 0 }, // , "refine_ls_restr_ncs", {"pdbx_auth_asym_id", "pdbx_number", "rms_dev_position", "weight_position"}, { "pdbx_type", "medium thermal", }, 1 },
|
||||
/* 104 */ { R"(LOOSE THERMAL\s+\d+\s+(.)\s+\(A\*\*2\):\s+(\d+)\s*;\s*(\d+(?:\.\d*)?)\s*;\s*(\d+(?:\.\d*)?))", 0 }, // , "refine_ls_restr_ncs", {"pdbx_auth_asym_id", "pdbx_number", "rms_dev_position", "weight_position"}, { "pdbx_type", "loose thermal", }, 10 },
|
||||
/* 105 */ { R"(NCS GROUP NUMBER\s*:\s*(\d+))", 93 - 105, /*"struct_ncs_dom", { "pdbx_ens_id" }*/ },
|
||||
/* 105 */ { R"(NCS GROUP NUMBER\s*:\s*(\d+))",
|
||||
93 - 105,
|
||||
/*"struct_ncs_dom", { "pdbx_ens_id" }*/ },
|
||||
/* 106 */ { R"(TWIN DETAILS)", 1 },
|
||||
/* 107 */ { R"(NUMBER OF TWIN DOMAINS\s*:\s*(\d*))", 1 },
|
||||
/* 108 */ { R"(TWIN DOMAIN\s*:\s*(.+))", 1, "pdbx_reflns_twin", { "domain_id" }, nullptr, true },
|
||||
@@ -755,7 +778,7 @@ const TemplateLine kREFMAC5_Template[] = {
|
||||
class REFMAC5_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
REFMAC5_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
REFMAC5_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kREFMAC5_Template, sizeof(kREFMAC5_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((REFMAC)(?: (\d+(?:\..+)?))?)"))
|
||||
{
|
||||
@@ -815,7 +838,7 @@ const TemplateLine kSHELXL_Template[] = {
|
||||
class SHELXL_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
SHELXL_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
SHELXL_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kSHELXL_Template, sizeof(kSHELXL_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((SHELXL)(?:-(\d+(?:\..+)?)))"))
|
||||
{
|
||||
@@ -872,7 +895,7 @@ const TemplateLine kTNT_Template[] = {
|
||||
class TNT_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
TNT_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
TNT_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kTNT_Template, sizeof(kTNT_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((TNT)(?: V. (\d+.+)?)?)"))
|
||||
{
|
||||
@@ -941,18 +964,28 @@ const TemplateLine kXPLOR_Template[] = {
|
||||
/* 58 */ { R"(MAIN-CHAIN ANGLE \(A\*\*2\) :\s+(.+?);\s+(.+))", 1, "refine_ls_restr", { "dev_ideal", "dev_ideal_target" }, "x_mcangle_it", false },
|
||||
/* 59 */ { R"(SIDE-CHAIN BOND \(A\*\*2\) :\s+(.+?);\s+(.+))", 1, "refine_ls_restr", { "dev_ideal", "dev_ideal_target" }, "x_scbond_it", false },
|
||||
/* 60 */ { R"(SIDE-CHAIN ANGLE \(A\*\*2\) :\s+(.+?);\s+(.+))", 1, "refine_ls_restr", { "dev_ideal", "dev_ideal_target" }, "x_scangle_it", false },
|
||||
/* 61 */ { R"(NCS MODEL :\s+(.+))", 1, /* "refine_ls_restr_ncs", { "ncs_model_details" } */ },
|
||||
/* 61 */ { R"(NCS MODEL :\s+(.+))",
|
||||
1,
|
||||
/* "refine_ls_restr_ncs", { "ncs_model_details" } */ },
|
||||
/* 62 */ { R"(NCS RESTRAINTS\. RMS SIGMA/WEIGHT)", 1 },
|
||||
/* 63 */ { R"(GROUP (\d+) POSITIONAL \(A\) :\s+(.+?);\s+(.+))", 1, /* "refine_ls_restr_ncs", { ":dom_id", "rms_dev_position", "weight_position" } */ },
|
||||
/* 64 */ { R"(GROUP (\d+) B-FACTOR \(A\*\*2\) :\s+(.+?);\s+(.+))", 63 - 64, /* "refine_ls_restr_ncs", { ":dom_id", "rms_dev_B_iso", "weight_B_iso" } */ },
|
||||
/* 65 */ { R"(PARAMETER FILE (\d+) :\s+(.+))", 0, /* "pdbx_xplor_file", { "serial_no", "param_file" } */ },
|
||||
/* 66 */ { R"(TOPOLOGY FILE (\d+) :\s+(.+))", 0, /* "pdbx_xplor_file", { "serial_no", "topol_file" } */ },
|
||||
/* 63 */ { R"(GROUP (\d+) POSITIONAL \(A\) :\s+(.+?);\s+(.+))",
|
||||
1,
|
||||
/* "refine_ls_restr_ncs", { ":dom_id", "rms_dev_position", "weight_position" } */ },
|
||||
/* 64 */ { R"(GROUP (\d+) B-FACTOR \(A\*\*2\) :\s+(.+?);\s+(.+))",
|
||||
63 - 64,
|
||||
/* "refine_ls_restr_ncs", { ":dom_id", "rms_dev_B_iso", "weight_B_iso" } */ },
|
||||
/* 65 */ { R"(PARAMETER FILE (\d+) :\s+(.+))",
|
||||
0,
|
||||
/* "pdbx_xplor_file", { "serial_no", "param_file" } */ },
|
||||
/* 66 */ { R"(TOPOLOGY FILE (\d+) :\s+(.+))",
|
||||
0,
|
||||
/* "pdbx_xplor_file", { "serial_no", "topol_file" } */ },
|
||||
};
|
||||
|
||||
class XPLOR_Remark3Parser : public Remark3Parser
|
||||
{
|
||||
public:
|
||||
XPLOR_Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db)
|
||||
XPLOR_Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db)
|
||||
: Remark3Parser(name, expMethod, r, db, kXPLOR_Template, sizeof(kXPLOR_Template) / sizeof(TemplateLine),
|
||||
std::regex(R"((X-PLOR)(?: (\d+(?:\.\d+)?))?)"))
|
||||
{
|
||||
@@ -961,15 +994,15 @@ class XPLOR_Remark3Parser : public Remark3Parser
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
Remark3Parser::Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db,
|
||||
Remark3Parser::Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db,
|
||||
const TemplateLine templatelines[], uint32_t templateLineCount, std::regex programversion)
|
||||
: mName(name)
|
||||
, mExpMethod(expMethod)
|
||||
: mName(std::move(name))
|
||||
, mExpMethod(std::move(expMethod))
|
||||
, mRec(r)
|
||||
, mDb(db.name())
|
||||
, mTemplate(templatelines)
|
||||
, mTemplateCount(templateLineCount)
|
||||
, mProgramVersion(programversion)
|
||||
, mProgramVersion(std::move(programversion))
|
||||
{
|
||||
mDb.set_validator(db.get_validator());
|
||||
}
|
||||
@@ -1042,13 +1075,13 @@ std::string Remark3Parser::nextLine()
|
||||
break;
|
||||
}
|
||||
|
||||
if (cif::VERBOSE >= 2)
|
||||
if (VERBOSE >= 2)
|
||||
std::cerr << "RM3: " << mLine << '\n';
|
||||
|
||||
return mLine;
|
||||
}
|
||||
|
||||
bool Remark3Parser::match(const char *expr, int nextState)
|
||||
bool Remark3Parser::match(const char *expr, uint32_t nextState)
|
||||
{
|
||||
std::regex rx(expr);
|
||||
|
||||
@@ -1056,7 +1089,7 @@ bool Remark3Parser::match(const char *expr, int nextState)
|
||||
|
||||
if (result)
|
||||
mState = nextState;
|
||||
else if (cif::VERBOSE >= 3)
|
||||
else if (VERBOSE >= 3)
|
||||
{
|
||||
using namespace colour;
|
||||
|
||||
@@ -1085,7 +1118,7 @@ float Remark3Parser::parse()
|
||||
if (mState == 0 and match(R"(AUTHORS\s*:.+)", 0))
|
||||
continue;
|
||||
|
||||
auto state = mState;
|
||||
uint32_t state;
|
||||
for (state = mState; state < mTemplateCount; ++state)
|
||||
{
|
||||
const TemplateLine &tmpl = mTemplate[state];
|
||||
@@ -1120,7 +1153,7 @@ float Remark3Parser::parse()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cif::VERBOSE >= 2)
|
||||
if (VERBOSE >= 2)
|
||||
{
|
||||
using namespace colour;
|
||||
|
||||
@@ -1136,7 +1169,7 @@ float Remark3Parser::parse()
|
||||
mDb["refine"].front()["details"] = remarks;
|
||||
}
|
||||
|
||||
float score = float(lineCount - dropped) / lineCount;
|
||||
float score = static_cast<float>(lineCount - dropped) / static_cast<float>(lineCount);
|
||||
|
||||
return score;
|
||||
}
|
||||
@@ -1176,7 +1209,7 @@ void Remark3Parser::storeCapture(const char *category, std::initializer_list<con
|
||||
if (iequals(value, "NULL") or iequals(value, "NONE") or iequals(value, "Inf") or iequals(value, "+Inf") or iequals(value, std::string(value.length(), '*')))
|
||||
continue;
|
||||
|
||||
if (cif::VERBOSE >= 3)
|
||||
if (VERBOSE >= 3)
|
||||
std::cerr << "storing: '" << value << "' in _" << category << '.' << item << '\n';
|
||||
|
||||
auto &cat = mDb[category];
|
||||
@@ -1185,7 +1218,7 @@ void Remark3Parser::storeCapture(const char *category, std::initializer_list<con
|
||||
if (iequals(category, "refine"))
|
||||
cat.emplace({ { "pdbx_refine_id", mExpMethod },
|
||||
{ "entry_id", mDb.name() },
|
||||
//#warning("this diffrn-id is probably not correct?")
|
||||
// #warning("this diffrn-id is probably not correct?")
|
||||
{ "pdbx_diffrn_id", 1 } });
|
||||
else if (iequals(category, "refine_analyze") or iequals(category, "pdbx_refine"))
|
||||
cat.emplace({
|
||||
@@ -1220,8 +1253,7 @@ void Remark3Parser::storeCapture(const char *category, std::initializer_list<con
|
||||
tlsID = mDb["pdbx_refine_tls"].back()["id"].as<std::string>();
|
||||
std::string tlsGroupID = cat.get_unique_id("");
|
||||
|
||||
cat.emplace({
|
||||
{ "pdbx_refine_id", mExpMethod },
|
||||
cat.emplace({ { "pdbx_refine_id", mExpMethod },
|
||||
{ "id", tlsGroupID },
|
||||
{ "refine_tls_id", tlsID } });
|
||||
}
|
||||
@@ -1276,10 +1308,8 @@ void Remark3Parser::storeRefineLsRestr(const char *type, std::initializer_list<c
|
||||
|
||||
if (r.empty())
|
||||
{
|
||||
r = mDb["refine_ls_restr"].emplace({
|
||||
{"pdbx_refine_id", mExpMethod},
|
||||
{"type", type}
|
||||
});
|
||||
r = mDb["refine_ls_restr"].emplace({ { "pdbx_refine_id", mExpMethod },
|
||||
{ "type", type } });
|
||||
}
|
||||
|
||||
r[item] = value;
|
||||
@@ -1337,7 +1367,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
|
||||
if (line != "REFINEMENT.")
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "Unexpected data in REMARK 3\n";
|
||||
return false;
|
||||
}
|
||||
@@ -1349,7 +1379,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
|
||||
if (not std::regex_match(line, m, rxp))
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "Expected valid PROGRAM line in REMARK 3\n";
|
||||
return false;
|
||||
}
|
||||
@@ -1358,8 +1388,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
|
||||
struct programScore
|
||||
{
|
||||
programScore(const std::string &program, Remark3Parser *parser, float score)
|
||||
: program(program)
|
||||
programScore(std::string program, Remark3Parser *parser, float score)
|
||||
: program(std::move(program))
|
||||
, parser(parser)
|
||||
, score(score)
|
||||
{
|
||||
@@ -1388,20 +1418,18 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
if (cif::VERBOSE >= 0)
|
||||
if (VERBOSE >= 0)
|
||||
std::cerr << "Error parsing REMARK 3 with " << parser->program() << '\n'
|
||||
<< e.what() << '\n';
|
||||
score = 0;
|
||||
}
|
||||
|
||||
if (cif::VERBOSE >= 2)
|
||||
if (VERBOSE >= 2)
|
||||
std::cerr << "Score for " << parser->program() << ": " << score << '\n';
|
||||
|
||||
if (score > 0)
|
||||
{
|
||||
std::string program = parser->program();
|
||||
std::string version = parser->version();
|
||||
|
||||
scores.emplace_back(program, parser.release(), score);
|
||||
}
|
||||
};
|
||||
@@ -1430,16 +1458,16 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
tryParser(new TNT_Remark3Parser(program, expMethod, r, db));
|
||||
else if (cif::starts_with(program, "X-PLOR"))
|
||||
tryParser(new XPLOR_Remark3Parser(program, expMethod, r, db));
|
||||
else if (cif::VERBOSE > 0)
|
||||
else if (VERBOSE > 0)
|
||||
std::cerr << "Skipping unknown program (" << program << ") in REMARK 3\n";
|
||||
}
|
||||
|
||||
sort(scores.begin(), scores.end());
|
||||
std::sort(scores.begin(), scores.end()); // NOLINT(modernize-use-ranges)
|
||||
|
||||
bool guessProgram = scores.empty() or scores.front().score < 0.9f;
|
||||
if (guessProgram)
|
||||
{
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "Unknown or untrusted program in REMARK 3, trying all parsers to see if there is a match\n";
|
||||
|
||||
tryParser(new BUSTER_TNT_Remark3Parser("BUSTER-TNT", expMethod, r, db));
|
||||
@@ -1460,11 +1488,11 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
{
|
||||
result = true;
|
||||
|
||||
sort(scores.begin(), scores.end());
|
||||
sort(scores.begin(), scores.end()); // NOLINT(modernize-use-ranges)
|
||||
|
||||
auto &best = scores.front();
|
||||
|
||||
if (cif::VERBOSE > 0)
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << "Choosing " << best.parser->program() << " version '" << best.parser->version() << "' as refinement program. Score = " << best.score << '\n';
|
||||
|
||||
auto &software = db["software"];
|
||||
@@ -1478,7 +1506,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
|
||||
best.parser->fixup();
|
||||
|
||||
auto &validator = cif::validator_factory::instance().get("mmcif_pdbx.dic");
|
||||
auto &validator = cif::validator_factory::instance()["mmcif_pdbx.dic"];
|
||||
|
||||
for (auto &cat1 : best.parser->mDb)
|
||||
{
|
||||
@@ -1494,19 +1522,19 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
cat2.emplace(cat1.front());
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
auto r1 = cat1.front();
|
||||
auto r2 = cat2.front();
|
||||
|
||||
auto cv = cat1.get_cat_validator();
|
||||
if (cv == nullptr)
|
||||
cv = validator.get_validator_for_category(cat1.name());
|
||||
|
||||
|
||||
if (cv == nullptr)
|
||||
continue;
|
||||
|
||||
for (auto &iv : cv->m_item_validators)
|
||||
r2[iv.m_item_name] = r1[iv.m_item_name].text();
|
||||
r2[iv.m_item_name] = r1[iv.m_item_name].str();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1520,4 +1548,6 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace pdbx
|
||||
} // namespace cif::pdb
|
||||
|
||||
// NOLINTEND(bugprone-empty-catch)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "pdb_record.hpp"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -38,7 +39,7 @@ struct TemplateLine;
|
||||
class Remark3Parser
|
||||
{
|
||||
public:
|
||||
virtual ~Remark3Parser() {}
|
||||
virtual ~Remark3Parser() = default;
|
||||
|
||||
static bool parse(const std::string &expMethod, PDBRecord *r, cif::datablock &db);
|
||||
|
||||
@@ -46,13 +47,13 @@ class Remark3Parser
|
||||
virtual std::string version();
|
||||
|
||||
protected:
|
||||
Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db,
|
||||
Remark3Parser(std::string name, std::string expMethod, PDBRecord *r, cif::datablock &db,
|
||||
const TemplateLine templatelines[], uint32_t templateLineCount, std::regex programVersion);
|
||||
|
||||
virtual float parse();
|
||||
std::string nextLine();
|
||||
|
||||
bool match(const char *expr, int nextState);
|
||||
bool match(const char *expr, uint32_t nextState);
|
||||
void storeCapture(const char *category, std::initializer_list<const char *> items, bool createNew = false);
|
||||
void storeRefineLsRestr(const char *type, std::initializer_list<const char *> values);
|
||||
void updateRefineLsRestr(const char *type, std::initializer_list<const char *> values);
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/file.hpp"
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
/// \file pdb_record.hpp
|
||||
|
||||
@@ -37,14 +39,14 @@ namespace cif::pdb
|
||||
|
||||
struct PDBRecord
|
||||
{
|
||||
PDBRecord *mNext;
|
||||
PDBRecord *mNext = nullptr;
|
||||
uint32_t mLineNr;
|
||||
char mName[11];
|
||||
std::size_t mVlen;
|
||||
char mValue[1];
|
||||
|
||||
PDBRecord(uint32_t lineNr, const std::string &name, const std::string &value);
|
||||
~PDBRecord();
|
||||
~PDBRecord() = default;
|
||||
|
||||
void *operator new(std::size_t);
|
||||
void *operator new(std::size_t size, std::size_t vLen);
|
||||
@@ -60,4 +62,4 @@ struct PDBRecord
|
||||
std::string vF(std::size_t columnFirst, std::size_t columnLast);
|
||||
};
|
||||
|
||||
} // namespace pdbx
|
||||
} // namespace cif::pdb
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,11 +25,12 @@
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
namespace cif::pdb
|
||||
{
|
||||
|
||||
condition get_parents_condition(const validator &validator, row_handle rh, const category &parentCat)
|
||||
condition get_parents_condition(const validator &validator, const_row_handle rh, const category &parentCat)
|
||||
{
|
||||
condition result;
|
||||
|
||||
@@ -38,9 +39,8 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
|
||||
auto parentName = parentCat.name();
|
||||
|
||||
auto links = validator.get_links_for_child(childName);
|
||||
links.erase(remove_if(links.begin(), links.end(), [n = parentName](auto &l)
|
||||
{ return l->m_parent_category != n; }),
|
||||
links.end());
|
||||
std::erase_if(links, [n = parentName](auto &l)
|
||||
{ return l->m_parent_category != n; });
|
||||
|
||||
if (not links.empty())
|
||||
{
|
||||
@@ -50,12 +50,10 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
|
||||
|
||||
for (std::size_t ix = 0; ix < link->m_child_keys.size(); ++ix)
|
||||
{
|
||||
auto childValue = rh[link->m_child_keys[ix]];
|
||||
|
||||
if (childValue.empty())
|
||||
if (rh[link->m_child_keys[ix]].empty())
|
||||
continue;
|
||||
|
||||
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.text();
|
||||
cond = std::move(cond) and key(link->m_parent_keys[ix]) == rh[link->m_child_keys[ix]].value();
|
||||
}
|
||||
|
||||
result = std::move(result) or std::move(cond);
|
||||
@@ -65,11 +63,18 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_valid_pdbx_file(const file &file)
|
||||
{
|
||||
std::error_code ec;
|
||||
bool result = is_valid_pdbx_file(file, validator_factory::instance()["mmcif_pdbx.dic"], ec);
|
||||
return result and ec == std::errc{};
|
||||
}
|
||||
|
||||
bool is_valid_pdbx_file(const file &file, const validator &v)
|
||||
{
|
||||
std::error_code ec;
|
||||
bool result = is_valid_pdbx_file(file, v, ec);
|
||||
return result and not (bool)ec;
|
||||
return result and ec == std::errc{};
|
||||
}
|
||||
|
||||
bool is_valid_pdbx_file(const file &file, std::error_code &ec)
|
||||
@@ -79,10 +84,10 @@ bool is_valid_pdbx_file(const file &file, std::error_code &ec)
|
||||
if (file.empty())
|
||||
ec = make_error_code(validation_error::empty_file);
|
||||
else if (auto ac = file.front().get("audit_conform"); ac != nullptr)
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance().get(*ac), ec);
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance()[*ac], ec);
|
||||
else
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance().get("mmcif_pdbx.dic"), ec);
|
||||
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance()["mmcif_pdbx.dic"], ec);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -110,7 +115,6 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
|
||||
auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
|
||||
|
||||
std::string last_asym_id;
|
||||
int last_seq_id = -1;
|
||||
for (auto r : atom_site)
|
||||
{
|
||||
@@ -139,7 +143,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
if (p.size() != 1)
|
||||
{
|
||||
if (VERBOSE > 0)
|
||||
std::clog << "In atom_site record: " << r["id"].text() << '\n';
|
||||
std::clog << "In atom_site record: " << r["id"].str() << '\n';
|
||||
throw std::runtime_error("For each monomer in atom_site there should be exactly one pdbx_poly_seq_scheme record");
|
||||
}
|
||||
}
|
||||
@@ -165,11 +169,11 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
if (entity_poly.count("entity_id"_key == entity_id) != 1)
|
||||
throw std::runtime_error("There should be exactly one entity_poly record per polymer entity");
|
||||
|
||||
const auto entity_poly_type = entity_poly.find1<std::string>("entity_id"_key == entity_id, "type");
|
||||
// const auto entity_poly_type = entity_poly.find1<std::string>("entity_id"_key == entity_id, "type");
|
||||
|
||||
std::map<int,std::set<std::string>> mon_per_seq_id;
|
||||
std::map<int, std::set<std::string>> mon_per_seq_id;
|
||||
|
||||
for (const auto &[num, mon_id, hetero] : entity_poly_seq.find<int, std::string, bool>("entity_id"_key == entity_id, "num", "mon_id", "hetero"))
|
||||
for (const auto &[num, mon_id, hetero] : entity_poly_seq.find<int, std::string, std::string>("entity_id"_key == entity_id, "num", "mon_id", "hetero"))
|
||||
{
|
||||
mon_per_seq_id[num].emplace(mon_id);
|
||||
|
||||
@@ -187,7 +191,7 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[seq_id, mon_id, hetero] : pdbx_poly_seq_scheme.find<int, std::string, bool>("entity_id"_key == entity_id, "seq_id", "mon_id", "hetero"))
|
||||
for (const auto &[seq_id, mon_id, hetero] : pdbx_poly_seq_scheme.find<int, std::string, std::string>("entity_id"_key == entity_id, "seq_id", "mon_id", "hetero"))
|
||||
{
|
||||
if (entity_poly_seq.count(
|
||||
"entity_id"_key == entity_id and
|
||||
@@ -198,32 +202,41 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
throw std::runtime_error("For each pdbx_poly_seq/struct_asym record there should be exactly one entity_poly_seq record");
|
||||
}
|
||||
|
||||
if ((mon_per_seq_id[seq_id].size() > 1) != hetero)
|
||||
if ((mon_per_seq_id[seq_id].size() > 1) != iequals(hetero, "Y"))
|
||||
throw std::runtime_error("Mismatch between the hetero flag in the poly seq schemes and the number residues per seq_id");
|
||||
}
|
||||
|
||||
for (const auto &[seq_id, mon_ids] : mon_per_seq_id)
|
||||
{
|
||||
for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
|
||||
{
|
||||
condition cond;
|
||||
|
||||
for (auto mon_id : mon_ids)
|
||||
cond = std::move(cond) or "label_comp_id"_key == mon_id;
|
||||
// This code proved to take too much time ...
|
||||
|
||||
cond = "label_entity_id"_key == entity_id and
|
||||
"label_asym_id"_key == asym_id and
|
||||
"label_seq_id"_key == seq_id and not std::move(cond);
|
||||
|
||||
if (atom_site.contains(std::move(cond)))
|
||||
throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
|
||||
}
|
||||
// for (const auto &[seq_id, mon_ids] : mon_per_seq_id)
|
||||
// {
|
||||
// for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
|
||||
// {
|
||||
// condition cond;
|
||||
|
||||
// for (auto mon_id : mon_ids)
|
||||
// cond = std::move(cond) or "label_comp_id"_key == mon_id;
|
||||
|
||||
// cond = "label_entity_id"_key == entity_id and
|
||||
// "label_asym_id"_key == asym_id and
|
||||
// "label_seq_id"_key == seq_id and not std::move(cond);
|
||||
|
||||
// if (atom_site.contains(std::move(cond)))
|
||||
// throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
|
||||
// }
|
||||
// }
|
||||
|
||||
// ... so we're using this instead, should be almost the same...
|
||||
|
||||
for (const auto &[comp_id, seq_id] :
|
||||
atom_site.find<std::string, int>("label_entity_id"_key == entity_id, "label_comp_id", "label_seq_id"))
|
||||
{
|
||||
if (not mon_per_seq_id[seq_id].contains(comp_id))
|
||||
throw std::runtime_error("An atom_site record exists that has no parent in the poly seq scheme categories");
|
||||
}
|
||||
|
||||
auto &&[seq, seq_can] = entity_poly.find1<std::optional<std::string>, std::optional<std::string>>("entity_id"_key == entity_id,
|
||||
"pdbx_seq_one_letter_code", "pdbx_seq_one_letter_code_can");
|
||||
|
||||
std::string::const_iterator si, sci, se, sce;
|
||||
|
||||
auto seq_match = [&](bool can, std::string::const_iterator si, std::string::const_iterator se)
|
||||
{
|
||||
@@ -260,11 +273,11 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
else
|
||||
letter = '(' + comp_id + ')';
|
||||
}
|
||||
|
||||
if (iequals(std::string{si, si + letter.length()}, letter))
|
||||
|
||||
if (iequals(std::string{ si, si + static_cast<int>(letter.length()) }, letter))
|
||||
{
|
||||
match = true;
|
||||
si += letter.length();
|
||||
si += static_cast<int>(letter.length());
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -285,7 +298,9 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
}
|
||||
else
|
||||
{
|
||||
seq->erase(std::remove_if(seq->begin(), seq->end(), [](char ch) { return std::isspace(ch); }), seq->end());
|
||||
seq->erase(std::remove_if(seq->begin(), seq->end(), [](char ch)
|
||||
{ return std::isspace(ch); }),
|
||||
seq->end());
|
||||
|
||||
if (not seq_match(false, seq->begin(), seq->end()))
|
||||
throw std::runtime_error("Sequences do not match for entity " + entity_id);
|
||||
@@ -298,7 +313,9 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
}
|
||||
else
|
||||
{
|
||||
seq_can->erase(std::remove_if(seq_can->begin(), seq_can->end(), [](char ch) { return std::isspace(ch); }), seq_can->end());
|
||||
seq_can->erase(std::remove_if(seq_can->begin(), seq_can->end(), [](char ch)
|
||||
{ return std::isspace(ch); }),
|
||||
seq_can->end());
|
||||
|
||||
if (not seq_match(true, seq_can->begin(), seq_can->end()))
|
||||
throw std::runtime_error("Canonical sequences do not match for entity " + entity_id);
|
||||
@@ -315,11 +332,10 @@ bool is_valid_pdbx_file(const file &file, const validator &validator, std::error
|
||||
ec = make_error_code(validation_error::not_valid_pdbx);
|
||||
}
|
||||
|
||||
if (not result and (bool)ec)
|
||||
if (not result and ec == std::errc{})
|
||||
ec = make_error_code(validation_error::not_valid_pdbx);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace cif::pdb
|
||||
|
||||
|
||||
282
src/point.cpp
282
src/point.cpp
@@ -25,17 +25,20 @@
|
||||
*/
|
||||
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/matrix.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
#include "cif++/matrix.hpp" // for matrix_subtraction, matrix_cofactors
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <random> // for uniform_real_distribution, normal_distri...
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
quaternion_type<T> normalize(quaternion_type<T> q)
|
||||
{
|
||||
std::valarray<double> t(4);
|
||||
@@ -104,9 +107,9 @@ point center_points(std::vector<point> &Points)
|
||||
t.m_z += pt.m_z;
|
||||
}
|
||||
|
||||
t.m_x /= Points.size();
|
||||
t.m_y /= Points.size();
|
||||
t.m_z /= Points.size();
|
||||
t.m_x /= static_cast<float>(Points.size());
|
||||
t.m_y /= static_cast<float>(Points.size());
|
||||
t.m_z /= static_cast<float>(Points.size());
|
||||
|
||||
for (point &pt : Points)
|
||||
{
|
||||
@@ -126,10 +129,9 @@ quaternion construct_for_dihedral_angle(point p1, point p2, point p3, point p4,
|
||||
p4 -= p3;
|
||||
p3 -= p3;
|
||||
|
||||
quaternion q;
|
||||
auto axis = -p2;
|
||||
|
||||
float dh = dihedral_angle(p1, p2, p3, p4);
|
||||
|
||||
return construct_from_angle_axis(angle - dh, axis);
|
||||
}
|
||||
|
||||
@@ -161,7 +163,7 @@ double RMSd(const std::vector<point> &a, const std::vector<point> &b)
|
||||
sum += d.sum();
|
||||
}
|
||||
|
||||
return std::sqrt(sum / a.size());
|
||||
return std::sqrt(sum / static_cast<double>(a.size()));
|
||||
}
|
||||
|
||||
// The next function returns the largest solution for a quartic equation
|
||||
@@ -293,9 +295,9 @@ quaternion align_points(const std::vector<point> &pa, const std::vector<point> &
|
||||
}
|
||||
|
||||
quaternion q(
|
||||
static_cast<float>(cf(maxR, 0)),
|
||||
static_cast<float>(cf(maxR, 1)),
|
||||
static_cast<float>(cf(maxR, 2)),
|
||||
static_cast<float>(cf(maxR, 0)),
|
||||
static_cast<float>(cf(maxR, 1)),
|
||||
static_cast<float>(cf(maxR, 2)),
|
||||
static_cast<float>(cf(maxR, 3)));
|
||||
q = normalize(q);
|
||||
|
||||
@@ -306,17 +308,15 @@ quaternion align_points(const std::vector<point> &pa, const std::vector<point> &
|
||||
|
||||
point nudge(point p, float offset)
|
||||
{
|
||||
static const float kPI_f = static_cast<float>(kPI);
|
||||
|
||||
static std::random_device rd;
|
||||
static std::mt19937_64 rng(rd());
|
||||
|
||||
std::uniform_real_distribution<float> randomAngle(0, 2 * kPI_f);
|
||||
std::uniform_real_distribution<float> randomAngle(0, 2 * std::numbers::pi);
|
||||
std::normal_distribution<float> randomOffset(0, offset);
|
||||
|
||||
float theta = randomAngle(rng);
|
||||
float phi1 = randomAngle(rng) - kPI_f;
|
||||
float phi2 = randomAngle(rng) - kPI_f;
|
||||
float phi1 = randomAngle(rng) - static_cast<float>(std::numbers::pi);
|
||||
float phi2 = randomAngle(rng) - static_cast<float>(std::numbers::pi);
|
||||
|
||||
quaternion q = spherical(1.0f, theta, phi1, phi2);
|
||||
|
||||
@@ -327,4 +327,250 @@ point nudge(point p, float offset)
|
||||
return p + r;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_2_points(std::array<cif::point, 2> pts)
|
||||
{
|
||||
return { (pts[0] + pts[1]) / 2, distance(pts[0], pts[1]) / 2 };
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_3_points(std::array<cif::point, 3> pts)
|
||||
{
|
||||
// Find two bisectors
|
||||
auto vz = cross_product(pts[1] - pts[0], pts[2] - pts[0]);
|
||||
|
||||
auto bs1 = cross_product(vz, pts[1] - pts[0]);
|
||||
bs1.normalize();
|
||||
|
||||
auto v1 = (pts[1] - pts[0]);
|
||||
v1.normalize();
|
||||
|
||||
auto s1 = pts[0] + (distance(pts[1], pts[0]) / 2) * v1;
|
||||
|
||||
auto bs2 = cross_product(vz, pts[2] - pts[0]);
|
||||
bs2.normalize();
|
||||
|
||||
auto v2 = (pts[2] - pts[0]);
|
||||
v2.normalize();
|
||||
|
||||
auto s2 = pts[0] + (distance(pts[2], pts[0]) / 2) * v2;
|
||||
|
||||
auto c = line_line_intersection(s1, s1 + bs1, s2, s2 + bs2);
|
||||
if (c)
|
||||
return { *c, distance(*c, pts[0]) };
|
||||
|
||||
// Colinear points I guess, try something else
|
||||
auto l1 = distance_squared(pts[0], pts[1]);
|
||||
auto l2 = distance_squared(pts[0], pts[2]);
|
||||
auto l3 = distance_squared(pts[1], pts[2]);
|
||||
|
||||
if (l1 > l2 and l1 > l3)
|
||||
return smallest_sphere_around_2_points({ pts[0], pts[1] });
|
||||
else if (l2 > l1 and l2 > l3)
|
||||
return smallest_sphere_around_2_points({ pts[0], pts[2] });
|
||||
else
|
||||
return smallest_sphere_around_2_points({ pts[1], pts[2] });
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_4_points(std::array<cif::point, 4> pts)
|
||||
{
|
||||
auto t0 = -norm_squared(pts[0]);
|
||||
auto t1 = -norm_squared(pts[1]);
|
||||
auto t2 = -norm_squared(pts[2]);
|
||||
auto t3 = -norm_squared(pts[3]);
|
||||
|
||||
// clang-format off
|
||||
matrix4x4<float> Tm({
|
||||
pts[0].m_x, pts[0].m_y, pts[0].m_z, 1,
|
||||
pts[1].m_x, pts[1].m_y, pts[1].m_z, 1,
|
||||
pts[2].m_x, pts[2].m_y, pts[2].m_z, 1,
|
||||
pts[3].m_x, pts[3].m_y, pts[3].m_z, 1
|
||||
});
|
||||
auto T = determinant(Tm);
|
||||
|
||||
if (T != 0)
|
||||
{
|
||||
matrix4x4<float> Dm({
|
||||
t0, pts[0].m_y, pts[0].m_z, 1,
|
||||
t1, pts[1].m_y, pts[1].m_z, 1,
|
||||
t2, pts[2].m_y, pts[2].m_z, 1,
|
||||
t3, pts[3].m_y, pts[3].m_z, 1
|
||||
});
|
||||
auto D = determinant(Dm) / T;
|
||||
|
||||
matrix4x4<float> Em({
|
||||
pts[0].m_x, t0, pts[0].m_z, 1,
|
||||
pts[1].m_x, t1, pts[1].m_z, 1,
|
||||
pts[2].m_x, t2, pts[2].m_z, 1,
|
||||
pts[3].m_x, t3, pts[3].m_z, 1
|
||||
});
|
||||
auto E = determinant(Em) / T;
|
||||
|
||||
matrix4x4<float> Fm({
|
||||
pts[0].m_x, pts[0].m_y, t0, 1,
|
||||
pts[1].m_x, pts[1].m_y, t1, 1,
|
||||
pts[2].m_x, pts[2].m_y, t2, 1,
|
||||
pts[3].m_x, pts[3].m_y, t3, 1
|
||||
});
|
||||
|
||||
auto F = determinant(Fm) / T;
|
||||
|
||||
matrix4x4<float> Gm({
|
||||
pts[0].m_x, pts[0].m_y, pts[0].m_z, t0,
|
||||
pts[1].m_x, pts[1].m_y, pts[1].m_z, t1,
|
||||
pts[2].m_x, pts[2].m_y, pts[2].m_z, t2,
|
||||
pts[3].m_x, pts[3].m_y, pts[3].m_z, t3
|
||||
});
|
||||
auto G = determinant(Gm) / T;
|
||||
|
||||
point center{ -D / 2, -E / 2, -F / 2 };
|
||||
float radius = std::sqrt(D * D + E * E + F * F - 4 * G) / 2;
|
||||
|
||||
// clang-format on
|
||||
|
||||
return { center, radius };
|
||||
}
|
||||
|
||||
// Perhaps some colinear points, try something else:
|
||||
|
||||
for (auto ix : std::initializer_list<std::array<size_t, 4>>{
|
||||
{ 1, 2, 3, 0 },
|
||||
{ 0, 2, 3, 1 },
|
||||
{ 0, 1, 3, 2 },
|
||||
{ 0, 1, 2, 3 },
|
||||
})
|
||||
{
|
||||
auto [center, radius] =
|
||||
smallest_sphere_around_3_points({ pts[ix[0]], pts[ix[1]], pts[ix[2]] });
|
||||
|
||||
if (distance(pts[ix[3]], center) <= radius)
|
||||
return { center, radius };
|
||||
}
|
||||
|
||||
assert(false);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_all_points(std::vector<point> P, std::vector<point> R)
|
||||
{
|
||||
if (P.empty() or R.size() == 4)
|
||||
{
|
||||
switch (R.size())
|
||||
{
|
||||
case 1:
|
||||
return { R[0], 0 };
|
||||
|
||||
case 2:
|
||||
return smallest_sphere_around_2_points({ R[0], R[1] });
|
||||
|
||||
case 3:
|
||||
return smallest_sphere_around_3_points({ R[0], R[1], R[2] });
|
||||
|
||||
case 4:
|
||||
return smallest_sphere_around_4_points({ R[0], R[1], R[2], R[3] });
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
auto p = P.back();
|
||||
P.pop_back();
|
||||
|
||||
auto [c, r] = smallest_sphere_around_all_points(P, R);
|
||||
assert(not std::isnan(r));
|
||||
if (distance(c, p) <= r)
|
||||
return { c, r };
|
||||
|
||||
R.emplace_back(p);
|
||||
return smallest_sphere_around_all_points(P, R);
|
||||
}
|
||||
|
||||
bool point_in_circle(point p, std::vector<point> c)
|
||||
{
|
||||
switch (c.size())
|
||||
{
|
||||
case 0:
|
||||
return false;
|
||||
|
||||
case 1:
|
||||
return p == c.front();
|
||||
|
||||
case 2:
|
||||
{
|
||||
auto [center, radius] = smallest_sphere_around_2_points({ c[0], c[1] });
|
||||
return cif::distance_squared(p, center) <= radius * radius;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
auto [center, radius] = smallest_sphere_around_3_points({ c[0], c[1], c[2] });
|
||||
return cif::distance_squared(p, center) <= radius * radius;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
auto [center, radius] = smallest_sphere_around_4_points({ c[0], c[1], c[2], c[3] });
|
||||
return cif::distance_squared(p, center) <= radius * radius;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
throw std::runtime_error("Error finding smallest sphere");
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<point, float> smallest_sphere_around_points(std::vector<point> pts)
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
|
||||
std::shuffle(pts.begin(), pts.end(), g);
|
||||
|
||||
std::vector<size_t> cix;
|
||||
|
||||
auto cirle_points = [&]()
|
||||
{
|
||||
std::vector<point> result;
|
||||
for (auto ix : cix)
|
||||
result.emplace_back(pts[ix]);
|
||||
return result;
|
||||
};
|
||||
|
||||
size_t i = 0;
|
||||
while (i < pts.size())
|
||||
{
|
||||
if (std::ranges::find(cix, i) != cix.end() or
|
||||
point_in_circle(pts[i], cirle_points()))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::erase_if(cix, [i](size_t j)
|
||||
{ return j < i; });
|
||||
cix.push_back(i);
|
||||
if (cix.size() < 4)
|
||||
i = 0;
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cix.size())
|
||||
{
|
||||
case 1:
|
||||
return { pts[cix[0]], 0 };
|
||||
case 2:
|
||||
return smallest_sphere_around_2_points({ pts[cix[0]], pts[cix[1]] });
|
||||
case 3:
|
||||
return smallest_sphere_around_3_points({ pts[cix[0]], pts[cix[1]], pts[cix[2]] });
|
||||
case 4:
|
||||
return smallest_sphere_around_4_points({ pts[cix[0]], pts[cix[1]], pts[cix[2]], pts[cix[3]] });
|
||||
default:
|
||||
assert(false);
|
||||
throw std::runtime_error("Error finding smallest sphere");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
|
||||
92
src/row.cpp
92
src/row.cpp
@@ -24,18 +24,41 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/item.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
void row_handle::assign(uint16_t item, std::string_view value, bool updateLinked, bool validate)
|
||||
{
|
||||
if (not m_category)
|
||||
throw std::runtime_error("uninitialized row");
|
||||
// item_value &row_handle::operator[](uint16_t item_ix)
|
||||
// {
|
||||
// return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
|
||||
// }
|
||||
|
||||
m_category->update_value(m_row, item, value, updateLinked, validate);
|
||||
}
|
||||
// const item_value &row_handle::operator[](uint16_t item_ix) const
|
||||
// {
|
||||
// return empty() or item_ix >= m_row->size() ? s_null_item : m_row->operator[](item_ix);
|
||||
// }
|
||||
|
||||
// item_value &row_handle::operator[](std::string_view item_name)
|
||||
// {
|
||||
// return operator[](get_item_ix(item_name));
|
||||
// }
|
||||
|
||||
// const item_value &row_handle::operator[](std::string_view item_name) const
|
||||
// {
|
||||
// return operator[](get_item_ix(item_name));
|
||||
// }
|
||||
|
||||
uint16_t row_handle::get_item_ix(std::string_view name) const
|
||||
{
|
||||
@@ -53,6 +76,32 @@ std::string_view row_handle::get_item_name(uint16_t ix) const
|
||||
return m_category->get_item_name(ix);
|
||||
}
|
||||
|
||||
uint16_t const_row_handle::get_item_ix(std::string_view name) const
|
||||
{
|
||||
if (not m_category)
|
||||
throw std::runtime_error("uninitialized row");
|
||||
|
||||
return m_category->get_item_ix(name);
|
||||
}
|
||||
|
||||
std::string_view const_row_handle::get_item_name(uint16_t ix) const
|
||||
{
|
||||
if (not m_category)
|
||||
throw std::runtime_error("uninitialized row");
|
||||
|
||||
return m_category->get_item_name(ix);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void row_handle::assign(uint16_t item, item_value value, bool updateLinked, bool validate)
|
||||
{
|
||||
if (not m_category)
|
||||
throw std::runtime_error("uninitialized row");
|
||||
|
||||
m_category->update_value(m_row, item, std::move(value), updateLinked, validate);
|
||||
}
|
||||
|
||||
uint16_t row_handle::add_item(std::string_view name)
|
||||
{
|
||||
if (not m_category)
|
||||
@@ -61,53 +110,46 @@ uint16_t row_handle::add_item(std::string_view name)
|
||||
return m_category->add_item(name);
|
||||
}
|
||||
|
||||
void row_handle::swap(uint16_t item, row_handle &b)
|
||||
{
|
||||
if (not m_category)
|
||||
throw std::runtime_error("uninitialized row");
|
||||
|
||||
m_category->swap_item(item, *this, b);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
row_initializer::row_initializer(row_handle rh)
|
||||
row_initializer::row_initializer(const_row_handle rh)
|
||||
{
|
||||
if (not rh.m_category)
|
||||
throw std::runtime_error("uninitialized row");
|
||||
|
||||
assert(rh.m_row);
|
||||
|
||||
row *r = rh.get_row();
|
||||
auto r = rh.get_row();
|
||||
auto &cat = *rh.m_category;
|
||||
|
||||
for (uint16_t ix = 0; ix < r->size(); ++ix)
|
||||
for (uint16_t ix = 0; std::cmp_less(ix, r->size()); ++ix)
|
||||
{
|
||||
auto &i = r->operator[](ix);
|
||||
if (not i)
|
||||
continue;
|
||||
emplace_back(cat.get_item_name(ix), i.text());
|
||||
emplace_back(cat.get_item_name(ix), i);
|
||||
}
|
||||
}
|
||||
|
||||
void row_initializer::set_value(std::string_view name, std::string_view value)
|
||||
void row_initializer::set_value(std::string name, item_value value)
|
||||
{
|
||||
for (auto &i : *this)
|
||||
{
|
||||
if (i.name() == name)
|
||||
{
|
||||
i.value(value);
|
||||
i.value(std::move(value));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emplace_back(name, value);
|
||||
emplace_back(std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
void row_initializer::set_value_if_empty(std::string_view name, std::string_view value)
|
||||
void row_initializer::set_value_if_empty(std::string name, item_value value)
|
||||
{
|
||||
if (find_if(begin(), end(), [name](auto &i) { return i.name() == name; }) == end())
|
||||
emplace_back(name, value);
|
||||
if (std::ranges::find_if(*this, [name](auto &i)
|
||||
{ return i.name() == name; }) == end())
|
||||
emplace_back(std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
} // namespace cif
|
||||
|
||||
@@ -25,16 +25,16 @@
|
||||
*/
|
||||
|
||||
#include "cif++/symmetry.hpp"
|
||||
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/point.hpp"
|
||||
#include "symop_table_data.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "symop_table_data.hpp"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
#pragma warning (disable : 4127) // conditional expression is constant
|
||||
# pragma warning(disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
# pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
@@ -116,7 +116,7 @@ sym_op::sym_op(std::string_view s)
|
||||
m_tb = r.ptr[2] - '0';
|
||||
m_tc = r.ptr[3] - '0';
|
||||
|
||||
if ((bool)r.ec or rnri > 192 or r.ptr[0] != '_' or m_ta > 9 or m_tb > 9 or m_tc > 9)
|
||||
if (r.ec != std::errc{} or rnri > 192 or r.ptr[0] != '_' or m_ta > 9 or m_tb > 9 or m_tc > 9)
|
||||
throw std::invalid_argument("Could not convert string into sym_op");
|
||||
}
|
||||
|
||||
@@ -124,13 +124,13 @@ std::string sym_op::string() const
|
||||
{
|
||||
char b[9];
|
||||
auto r = std::to_chars(b, b + sizeof(b), m_nr);
|
||||
if ((bool)r.ec or r.ptr > b + 4)
|
||||
if (r.ec != std::errc{} or r.ptr > b + 4)
|
||||
throw std::runtime_error("Could not write out symmetry operation to string");
|
||||
|
||||
*r.ptr++ = '_';
|
||||
*r.ptr++ = '0' + m_ta;
|
||||
*r.ptr++ = '0' + m_tb;
|
||||
*r.ptr++ = '0' + m_tc;
|
||||
*r.ptr++ = static_cast<char>('0' + m_ta);
|
||||
*r.ptr++ = static_cast<char>('0' + m_tb);
|
||||
*r.ptr++ = static_cast<char>('0' + m_tc);
|
||||
*r.ptr = 0;
|
||||
|
||||
return { b, static_cast<std::size_t>(r.ptr - b) };
|
||||
@@ -187,7 +187,7 @@ transformation operator*(const transformation &lhs, const transformation &rhs)
|
||||
auto t = lhs.m_rotation * rhs.m_translation;
|
||||
t = t + lhs.m_translation;
|
||||
|
||||
return transformation(r, t);
|
||||
return { r, t };
|
||||
}
|
||||
|
||||
transformation inverse(const transformation &t)
|
||||
@@ -345,9 +345,8 @@ int get_space_group_number(std::string_view spacegroup)
|
||||
// not found, see if we can find a match based on xHM name
|
||||
if (result == 0)
|
||||
{
|
||||
for (std::size_t i = 0; i < kNrOfSpaceGroups; ++i)
|
||||
for (const auto &sp : kSpaceGroups)
|
||||
{
|
||||
auto &sp = kSpaceGroups[i];
|
||||
if (sp.xHM == spacegroup)
|
||||
{
|
||||
result = sp.nr;
|
||||
|
||||
@@ -280,7 +280,7 @@ int main(int argc, char* const argv[])
|
||||
if (std::isdigit(line[0])) // start of new spacegroup
|
||||
{
|
||||
auto r = std::from_chars(line.data(), line.data() + line.length(), sgnr);
|
||||
if ((bool)r.ec)
|
||||
if (r.ec != std::errc{})
|
||||
throw std::runtime_error("Error parsing symop.lib file");
|
||||
rnr = 1;
|
||||
continue;
|
||||
|
||||
@@ -363,7 +363,7 @@ const space_group kSpaceGroups[] =
|
||||
|
||||
const std::size_t kNrOfSpaceGroups = sizeof(kSpaceGroups) / sizeof(space_group);
|
||||
|
||||
const symop_datablock kSymopNrTable[] = {
|
||||
const symop_datablock kSymopNrTable[] = { // NOLINT(bugprone-throwing-static-initialization,cert-err58-cpp)
|
||||
// P 1
|
||||
{ 1, 1, { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, } },
|
||||
// P -1
|
||||
|
||||
66
src/text.cpp
66
src/text.cpp
@@ -28,6 +28,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <stdexcept>
|
||||
|
||||
#if __has_include("fast_float/fast_float.h")
|
||||
# include "fast_float/fast_float.h"
|
||||
#endif
|
||||
|
||||
namespace cif
|
||||
{
|
||||
@@ -56,31 +62,29 @@ const uint8_t kCharToLowerMap[256] = {
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
bool iequals(std::string_view a, std::string_view b)
|
||||
bool iequals(std::string_view a, std::string_view b) noexcept
|
||||
{
|
||||
bool result = a.length() == b.length();
|
||||
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);
|
||||
result = kCharToLowerMap[static_cast<uint8_t>(*ai)] == kCharToLowerMap[static_cast<uint8_t>(*bi)];
|
||||
return result;
|
||||
}
|
||||
|
||||
bool iequals(const char *a, const char *b)
|
||||
bool iequals(const char *a, const char *b) noexcept
|
||||
{
|
||||
bool result = true;
|
||||
for (; result and *a and *b; ++a, ++b)
|
||||
result = kCharToLowerMap[uint8_t(*a)] == kCharToLowerMap[uint8_t(*b)];
|
||||
|
||||
result = kCharToLowerMap[static_cast<uint8_t>(*a)] == kCharToLowerMap[static_cast<uint8_t>(*b)];
|
||||
return result and *a == *b;
|
||||
}
|
||||
|
||||
int icompare(std::string_view a, std::string_view b)
|
||||
int icompare(std::string_view a, std::string_view b) noexcept
|
||||
{
|
||||
int d = 0;
|
||||
auto ai = a.begin(), bi = b.begin();
|
||||
|
||||
for (; d == 0 and ai != a.end() and bi != b.end(); ++ai, ++bi)
|
||||
d = (int)kCharToLowerMap[uint8_t(*ai)] - (int)kCharToLowerMap[uint8_t(*bi)];
|
||||
d = static_cast<int>(kCharToLowerMap[static_cast<uint8_t>(*ai)]) - static_cast<int>(kCharToLowerMap[static_cast<uint8_t>(*bi)]);
|
||||
|
||||
if (d == 0)
|
||||
{
|
||||
@@ -93,12 +97,12 @@ int icompare(std::string_view a, std::string_view b)
|
||||
return d;
|
||||
}
|
||||
|
||||
int icompare(const char *a, const char *b)
|
||||
int icompare(const char *a, const char *b) noexcept
|
||||
{
|
||||
int d = 0;
|
||||
|
||||
for (; d == 0 and *a != 0 and *b != 0; ++a, ++b)
|
||||
d = (int)kCharToLowerMap[uint8_t(*a)] - (int)kCharToLowerMap[uint8_t(*b)];
|
||||
d = static_cast<int>(kCharToLowerMap[static_cast<uint8_t>(*a)]) - static_cast<int>(kCharToLowerMap[static_cast<uint8_t>(*b)]);
|
||||
|
||||
if (d == 0)
|
||||
{
|
||||
@@ -194,7 +198,7 @@ void trim_left(std::string &s)
|
||||
|
||||
while (in != s.end() and std::isspace(*in))
|
||||
++in;
|
||||
|
||||
|
||||
if (in == s.end())
|
||||
s.clear();
|
||||
else if (in != out)
|
||||
@@ -214,7 +218,7 @@ void trim(std::string &s)
|
||||
|
||||
while (in != end and std::isspace(*in))
|
||||
++in;
|
||||
|
||||
|
||||
if (in == end)
|
||||
s.clear();
|
||||
else if (in != out)
|
||||
@@ -263,7 +267,7 @@ std::string cif_id_for_number(int number)
|
||||
number = (number - r) / 26 - 1;
|
||||
} while (number >= 0);
|
||||
|
||||
std::reverse(result.begin(), result.end());
|
||||
std::ranges::reverse(result);
|
||||
|
||||
assert(not result.empty());
|
||||
|
||||
@@ -385,7 +389,7 @@ std::string::const_iterator nextLineBreak(std::string::const_iterator text, std:
|
||||
/* JT */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, IBK },
|
||||
};
|
||||
|
||||
uint8_t ch = static_cast<uint8_t>(*text);
|
||||
auto ch = static_cast<uint8_t>(*text);
|
||||
|
||||
LineBreakClass cls;
|
||||
|
||||
@@ -489,7 +493,7 @@ std::vector<std::string> wrapLine(const std::string &text, std::size_t width)
|
||||
j = i;
|
||||
}
|
||||
|
||||
reverse(result.begin(), result.end());
|
||||
std::ranges::reverse(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -501,7 +505,7 @@ std::vector<std::string> word_wrap(const std::string &text, std::size_t width)
|
||||
{
|
||||
if (p.empty())
|
||||
{
|
||||
result.push_back("");
|
||||
result.emplace_back("");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -512,4 +516,32 @@ std::vector<std::string> word_wrap(const std::string &text, std::size_t width)
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace cif
|
||||
#if __has_include("fast_float/fast_float.h")
|
||||
|
||||
template <typename T>
|
||||
std::from_chars_result ff_charconv<T, typename std::enable_if_t<std::is_floating_point_v<T>>>::from_chars(const char *a, const char *b, T &v)
|
||||
{
|
||||
auto r = fast_float::from_chars(a, b, v);
|
||||
return { r.ptr, r.ec };
|
||||
}
|
||||
|
||||
template struct ff_charconv<float>;
|
||||
template struct ff_charconv<double>;
|
||||
// template struct ff_charconv<long double>;
|
||||
|
||||
# ifdef __STDCPP_FLOAT64_T__
|
||||
template struct ff_charconv<std::float64_t>;
|
||||
# endif
|
||||
# ifdef __STDCPP_FLOAT32_T__
|
||||
template struct ff_charconv<std::float32_t>;
|
||||
# endif
|
||||
# ifdef __STDCPP_FLOAT16_T__
|
||||
template struct ff_charconv<std::float16_t>;
|
||||
# endif
|
||||
# ifdef __STDCPP_BFLOAT16_T__
|
||||
template struct ff_charconv<std::bfloat16_t>;
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace cif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
321
src/validate.cpp
321
src/validate.cpp
@@ -25,14 +25,20 @@
|
||||
*/
|
||||
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/dictionary_parser.hpp"
|
||||
#include "cif++/gzio.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <compare>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
// The validator depends on regular expressions. Unfortunately,
|
||||
// the implementation of std::regex in g++ is buggy and crashes
|
||||
@@ -62,14 +68,6 @@ validation_exception::validation_exception(std::error_code ec, std::string_view
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// struct regex_impl : public regex
|
||||
// {
|
||||
// regex_impl(std::string_view rx)
|
||||
// : regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
|
||||
// {
|
||||
// }
|
||||
// };
|
||||
|
||||
struct regex_impl
|
||||
{
|
||||
regex_impl(std::string_view rx);
|
||||
@@ -81,22 +79,22 @@ struct regex_impl
|
||||
bool match(std::string_view v) const;
|
||||
|
||||
private:
|
||||
|
||||
pcre2_code *m_rx = nullptr;
|
||||
pcre2_code_8 *m_rx = nullptr;
|
||||
pcre2_match_data *m_data = nullptr;
|
||||
mutable std::mutex m_mutex;
|
||||
};
|
||||
|
||||
regex_impl::regex_impl(std::string_view rx)
|
||||
{
|
||||
int err_code;
|
||||
size_t err_offset;
|
||||
m_rx = pcre2_compile((PCRE2_SPTR)rx.data(), rx.length(), 0, &err_code, &err_offset, nullptr);
|
||||
m_rx = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(rx.data()), rx.length(), 0, &err_code, &err_offset, nullptr);
|
||||
if (m_rx == nullptr)
|
||||
{
|
||||
PCRE2_UCHAR buffer[256];
|
||||
int n = pcre2_get_error_message(err_code, buffer, sizeof(buffer));
|
||||
char buffer[256];
|
||||
int n = pcre2_get_error_message(err_code, reinterpret_cast<unsigned char *>(&buffer[0]), sizeof(buffer));
|
||||
|
||||
throw std::runtime_error(std::string("PCRE2 compilation failed: ") + std::string{ (char *)buffer, (char *)buffer + n });
|
||||
throw std::runtime_error(std::string("PCRE2 compilation failed: ") + std::string{ buffer, buffer + n });
|
||||
}
|
||||
|
||||
m_data = pcre2_match_data_create_from_pattern(m_rx, nullptr);
|
||||
@@ -104,6 +102,8 @@ regex_impl::regex_impl(std::string_view rx)
|
||||
|
||||
regex_impl::~regex_impl()
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
if (m_data)
|
||||
pcre2_match_data_free(m_data);
|
||||
|
||||
@@ -113,9 +113,11 @@ regex_impl::~regex_impl()
|
||||
|
||||
bool regex_impl::match(std::string_view v) const
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (int rc = pcre2_match(m_rx, (PCRE2_SPTR)v.data(), v.length(), 0, 0, m_data, nullptr); rc >= 0)
|
||||
if (int rc = pcre2_match(m_rx, reinterpret_cast<PCRE2_SPTR>(v.data()), v.length(), 0, 0, m_data, nullptr); rc >= 0)
|
||||
result = true;
|
||||
else if (rc != PCRE2_ERROR_NOMATCH)
|
||||
std::cerr << "Error matching with pcre\n";
|
||||
@@ -158,136 +160,88 @@ type_validator::type_validator(std::string_view name, DDL_PrimitiveType type, st
|
||||
{
|
||||
}
|
||||
|
||||
type_validator::type_validator(const type_validator &tv)
|
||||
: m_name(tv.m_name)
|
||||
, m_primitive_type(tv.m_primitive_type)
|
||||
, m_rx(tv.m_rx)
|
||||
{
|
||||
}
|
||||
|
||||
type_validator::~type_validator()
|
||||
{
|
||||
}
|
||||
|
||||
int type_validator::compare(std::string_view a, std::string_view b) const
|
||||
int type_validator::compare(const item_value &a, const item_value &b) const
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (a.empty())
|
||||
result = b.empty() ? 0 : -1;
|
||||
else if (b.empty())
|
||||
result = a.empty() ? 0 : +1;
|
||||
else
|
||||
switch (m_primitive_type)
|
||||
{
|
||||
switch (m_primitive_type)
|
||||
case DDL_PrimitiveType::Numb:
|
||||
{
|
||||
case DDL_PrimitiveType::Numb:
|
||||
{
|
||||
double da, db;
|
||||
if (a.is_number() and b.is_number())
|
||||
return a.compare(b);
|
||||
|
||||
using namespace cif;
|
||||
using namespace std;
|
||||
auto da = a.get<double>();
|
||||
auto db = b.get<double>();
|
||||
|
||||
std::from_chars_result ra, rb;
|
||||
|
||||
ra = selected_charconv<double>::from_chars(a.data(), a.data() + a.length(), da);
|
||||
rb = selected_charconv<double>::from_chars(b.data(), b.data() + b.length(), db);
|
||||
|
||||
if (not(bool) ra.ec and not(bool) rb.ec)
|
||||
{
|
||||
auto d = da - db;
|
||||
if (std::abs(d) > std::numeric_limits<double>::epsilon())
|
||||
{
|
||||
if (d > 0)
|
||||
result = 1;
|
||||
else if (d < 0)
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
else if ((bool)ra.ec)
|
||||
result = 1;
|
||||
else
|
||||
result = -1;
|
||||
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.begin(), bi = b.begin();
|
||||
for (;;)
|
||||
{
|
||||
if (ai == a.end())
|
||||
{
|
||||
if (bi != b.end())
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
else if (bi == b.end())
|
||||
{
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
char ca = *ai;
|
||||
char cb = *bi;
|
||||
|
||||
if (m_primitive_type == DDL_PrimitiveType::UChar)
|
||||
{
|
||||
ca = tolower(ca);
|
||||
cb = tolower(cb);
|
||||
}
|
||||
|
||||
result = ca - cb;
|
||||
|
||||
if (result != 0)
|
||||
break;
|
||||
|
||||
if (ca == ' ')
|
||||
{
|
||||
while (ai[1] == ' ')
|
||||
++ai;
|
||||
while (bi[1] == ' ')
|
||||
++bi;
|
||||
}
|
||||
|
||||
++ai;
|
||||
++bi;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return da < db
|
||||
? -1
|
||||
: da > db
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
case DDL_PrimitiveType::UChar:
|
||||
if (a.is_string() and b.is_string())
|
||||
return a.compare(b, true);
|
||||
|
||||
return icompare(a.str(), b.str());
|
||||
|
||||
case DDL_PrimitiveType::Char:
|
||||
if (a.is_string() and b.is_string())
|
||||
return a.compare(b, false);
|
||||
|
||||
return a.str().compare(b.str());
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void item_validator::operator()(std::string_view value) const
|
||||
void item_validator::validate_value(const item_value &value) const
|
||||
{
|
||||
std::error_code ec;
|
||||
if (not validate_value(value, ec))
|
||||
throw std::system_error(ec, std::string{ value } + " does not match rx for " + m_item_name);
|
||||
throw std::system_error(ec, std::format("'{}' is not a valid value for {}", value.str(), m_item_name));
|
||||
}
|
||||
|
||||
bool item_validator::validate_value(std::string_view value, std::error_code &ec) const noexcept
|
||||
bool item_validator::validate_value(const item_value &value, std::error_code &ec) const noexcept
|
||||
{
|
||||
ec.clear();
|
||||
|
||||
if (not value.empty() and value != "?" and value != ".")
|
||||
if (not value.empty())
|
||||
{
|
||||
if (m_type != nullptr and not m_type->m_rx->match(value))
|
||||
ec = make_error_code(validation_error::value_does_not_match_rx);
|
||||
else if (not m_enums.empty() and m_enums.count(std::string{ value }) == 0)
|
||||
if (m_type != nullptr)
|
||||
{
|
||||
if (m_type->m_primitive_type == DDL_PrimitiveType::Numb)
|
||||
{
|
||||
if (not value.is_number())
|
||||
ec = make_error_code(validation_error::value_is_not_a_number);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.is_number())
|
||||
ec = make_error_code(validation_error::value_is_not_a_char_string);
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
auto s = value.str();
|
||||
if (not m_type->m_rx->match(s))
|
||||
ec = make_error_code(validation_error::value_does_not_match_rx);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ec = make_error_code(validation_error::value_does_not_match_rx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ec == std::errc{} and not m_enums.empty() and m_enums.count(value.str()) == 0)
|
||||
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
|
||||
}
|
||||
|
||||
return not(bool) ec;
|
||||
return ec == std::errc{};
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -299,15 +253,20 @@ void category_validator::add_item_validator(item_validator &&v)
|
||||
|
||||
v.m_category = m_name;
|
||||
|
||||
auto r = m_item_validators.insert(std::move(v));
|
||||
if (not r.second and VERBOSE >= 4)
|
||||
std::cout << "Could not add validator for item " << v.m_item_name << " to category " << m_name << '\n';
|
||||
auto i = std::ranges::find(m_item_validators, v);
|
||||
if (i != m_item_validators.end())
|
||||
{
|
||||
if (VERBOSE >= 4)
|
||||
std::cout << "Could not add validator for item " << v.m_item_name << " to category " << m_name << '\n';
|
||||
}
|
||||
else
|
||||
m_item_validators.emplace_back(std::move(v));
|
||||
}
|
||||
|
||||
const item_validator *category_validator::get_validator_for_item(std::string_view item_name) const
|
||||
{
|
||||
const item_validator *result = nullptr;
|
||||
auto i = m_item_validators.find(item_validator{ std::string(item_name) });
|
||||
auto i = std::ranges::find(m_item_validators, item_validator{ std::string(item_name) });
|
||||
if (i != m_item_validators.end())
|
||||
result = &*i;
|
||||
else if (VERBOSE > 4)
|
||||
@@ -339,15 +298,6 @@ const item_validator *category_validator::get_validator_for_aliased_item(std::st
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
validator::validator(const validator &rhs)
|
||||
: m_audit_conform(rhs.m_audit_conform)
|
||||
, m_strict(rhs.m_strict)
|
||||
, m_type_validators(rhs.m_type_validators)
|
||||
, m_category_validators(rhs.m_category_validators)
|
||||
, m_link_validators(rhs.m_link_validators)
|
||||
{
|
||||
}
|
||||
|
||||
void swap(validator &a, validator &b) noexcept
|
||||
{
|
||||
std::swap(a.m_audit_conform, b.m_audit_conform);
|
||||
@@ -364,9 +314,7 @@ void validator::parse(std::istream &is)
|
||||
|
||||
void validator::add_type_validator(type_validator &&v)
|
||||
{
|
||||
auto r = m_type_validators.insert(std::move(v));
|
||||
if (not r.second and VERBOSE > 4)
|
||||
std::cout << "Could not add validator for type " << v.m_name << '\n';
|
||||
m_type_validators.emplace(v);
|
||||
}
|
||||
|
||||
const type_validator *validator::get_validator_for_type(std::string_view typeCode) const
|
||||
@@ -376,16 +324,12 @@ const type_validator *validator::get_validator_for_type(std::string_view typeCod
|
||||
auto i = m_type_validators.find(type_validator{ std::string(typeCode), DDL_PrimitiveType::Char, {} });
|
||||
if (i != m_type_validators.end())
|
||||
result = &*i;
|
||||
else if (VERBOSE > 4)
|
||||
std::cout << "No validator for type " << typeCode << '\n';
|
||||
return result;
|
||||
}
|
||||
|
||||
void validator::add_category_validator(category_validator &&v)
|
||||
{
|
||||
auto r = m_category_validators.insert(std::move(v));
|
||||
if (not r.second and VERBOSE > 4)
|
||||
std::cout << "Could not add validator for category " << v.m_name << '\n';
|
||||
m_category_validators.emplace(v);
|
||||
}
|
||||
|
||||
const category_validator *validator::get_validator_for_category(std::string_view category) const
|
||||
@@ -394,8 +338,6 @@ const category_validator *validator::get_validator_for_category(std::string_view
|
||||
auto i = m_category_validators.find(category_validator{ std::string(category) });
|
||||
if (i != m_category_validators.end())
|
||||
result = &*i;
|
||||
else if (VERBOSE > 4)
|
||||
std::cout << "No validator for category " << category << '\n';
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -486,12 +428,19 @@ void validator::report_error(std::error_code ec, bool fatal) const
|
||||
void validator::report_error(std::error_code ec, std::string_view category,
|
||||
std::string_view item, bool fatal) const
|
||||
{
|
||||
auto ex = item.empty() ? validation_exception(ec, category) : validation_exception(ec, category, item);
|
||||
|
||||
if (m_strict or fatal)
|
||||
throw ex;
|
||||
else if (VERBOSE > 0)
|
||||
std::cerr << ex.what() << '\n';
|
||||
{
|
||||
if (item.empty())
|
||||
throw validation_exception(ec, category);
|
||||
else
|
||||
throw validation_exception(ec, category, item);
|
||||
}
|
||||
|
||||
if (VERBOSE > 0)
|
||||
std::cerr << ec.message()
|
||||
<< "; category: " << std::quoted(category)
|
||||
<< " item: " << std::quoted(item)
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
void validator::fill_audit_conform(category &audit_conform) const
|
||||
@@ -544,57 +493,79 @@ validator_factory &validator_factory::instance()
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
const validator &validator_factory::get(std::string_view dictionary_name)
|
||||
const validator *validator_factory::get(std::string_view dictionary_name)
|
||||
{
|
||||
category audit_conform("audit_conform");
|
||||
for (auto part : cif::split(dictionary_name, ";", true))
|
||||
for (auto part : cif::split(dictionary_name, ";,", true))
|
||||
audit_conform.emplace({ { "dict_name", part } });
|
||||
|
||||
return get(audit_conform);
|
||||
}
|
||||
|
||||
const validator &validator_factory::get(const category &audit_conform)
|
||||
const validator *validator_factory::get(const category &audit_conform)
|
||||
{
|
||||
if (audit_conform.empty())
|
||||
throw std::runtime_error("Empty audit_conform category, cannot create a validator");
|
||||
const validator *result = nullptr;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
// Check existing first
|
||||
for (auto &v : m_validators)
|
||||
{
|
||||
if (v.matches_audit_conform(audit_conform))
|
||||
return v;
|
||||
result = &v;
|
||||
}
|
||||
|
||||
// If the audit conform contains only one record, this is easy
|
||||
if (audit_conform.size() == 1)
|
||||
if (result == nullptr and audit_conform.size() == 1)
|
||||
{
|
||||
const auto &[name, version] = audit_conform.front().get<std::string, std::optional<std::string>>("dict_name", "dict_version");
|
||||
return m_validators.emplace_back(construct_validator(name, version));
|
||||
const auto &[name, version] =
|
||||
audit_conform.front().get<std::string, std::optional<std::string>>("dict_name", "dict_version");
|
||||
if (not name.empty())
|
||||
result = &m_validators.emplace_back(construct_validator(name, version));
|
||||
}
|
||||
|
||||
// A new, merged dictionary
|
||||
|
||||
std::optional<validator> v;
|
||||
for (const auto &[name, version] : audit_conform.rows<std::string, std::optional<std::string>>("dict_name", "dict_version"))
|
||||
if (result == nullptr)
|
||||
{
|
||||
if (not v)
|
||||
v = construct_validator(name, version);
|
||||
else
|
||||
// A new, merged dictionary
|
||||
std::optional<validator> v;
|
||||
for (const auto &[name, version] : audit_conform.rows<std::string, std::optional<std::string>>("dict_name", "dict_version"))
|
||||
{
|
||||
auto data = load_resource(name);
|
||||
if (not data)
|
||||
throw std::runtime_error("Could not load dictionary " + std::string{ name });
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
v->parse(*data);
|
||||
if (not v) // first dict
|
||||
v = construct_validator(name, version);
|
||||
else // additional/extending dict
|
||||
{
|
||||
auto data = load_resource(name);
|
||||
if (not data)
|
||||
throw std::runtime_error("Could not load dictionary " + std::string{ name });
|
||||
|
||||
v->parse(*data);
|
||||
}
|
||||
}
|
||||
|
||||
if (v)
|
||||
result = &m_validators.emplace_back(std::move(*v));
|
||||
}
|
||||
|
||||
if (not v)
|
||||
throw std::runtime_error("Missing dictionary information?");
|
||||
return result;
|
||||
}
|
||||
|
||||
return m_validators.emplace_back(std::move(*v));
|
||||
const validator &validator_factory::operator[](const category &audit_conform)
|
||||
{
|
||||
auto v = get(audit_conform);
|
||||
if (v == nullptr)
|
||||
throw std::runtime_error("Could not load dictionary for audit_conform");
|
||||
return *v;
|
||||
}
|
||||
|
||||
const validator &validator_factory::operator[](std::string_view dictionary_name)
|
||||
{
|
||||
auto v = get(dictionary_name);
|
||||
if (v == nullptr)
|
||||
throw std::runtime_error("Could not load dictionary for " + std::string{ dictionary_name });
|
||||
return *v;
|
||||
}
|
||||
|
||||
validator validator_factory::construct_validator(std::string_view name, std::optional<std::string> version)
|
||||
@@ -613,7 +584,7 @@ validator validator_factory::construct_validator(std::string_view name, std::opt
|
||||
not v.matches_audit_conform(category{ "audit_conform", //
|
||||
{ { "dict_name", name }, { "dict_version", version } } }))
|
||||
{
|
||||
std::clog << "Invalid dictionary?\n";
|
||||
std::clog << "Loaded dictionary does not match name=" << name << " and version=" << version.value_or("''") << "\n";
|
||||
}
|
||||
|
||||
return v;
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
# We're using the older version 2 of Catch2
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2025 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.
|
||||
|
||||
if(NOT(Catch2_FOUND OR TARGET Catch2))
|
||||
find_package(Catch2 QUIET)
|
||||
if(NOT (Catch2_FOUND OR TARGET Catch2))
|
||||
find_package(Catch2 3 QUIET)
|
||||
|
||||
if(NOT Catch2_FOUND)
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
Catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v2.13.9)
|
||||
GIT_TAG v3.4.0)
|
||||
|
||||
FetchContent_MakeAvailable(Catch2)
|
||||
|
||||
set(Catch2_VERSION "2.13.9")
|
||||
target_compile_features(Catch2 PRIVATE cxx_std_20)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -23,24 +43,19 @@ list(
|
||||
unit-v2
|
||||
unit-3d
|
||||
model
|
||||
query
|
||||
# query
|
||||
rename-compound
|
||||
sugar
|
||||
spinner
|
||||
# reconstruction
|
||||
reconstruction
|
||||
validate-pdbx
|
||||
)
|
||||
matrix
|
||||
)
|
||||
|
||||
add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")
|
||||
|
||||
target_link_libraries(test-main cifpp::cifpp Catch2::Catch2)
|
||||
|
||||
if("${Catch2_VERSION}" VERSION_LESS 3.0.0)
|
||||
target_compile_definitions(test-main PUBLIC CATCH22=1)
|
||||
else()
|
||||
target_compile_definitions(test-main PUBLIC CATCH22=0)
|
||||
endif()
|
||||
|
||||
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
|
||||
set(CIFPP_TEST "${CIFPP_TEST}-test")
|
||||
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${CIFPP_TEST}.cpp")
|
||||
@@ -48,29 +63,17 @@ foreach(CIFPP_TEST IN LISTS CIFPP_tests)
|
||||
add_executable(
|
||||
${CIFPP_TEST} ${CIFPP_TEST_SOURCE} $<TARGET_OBJECTS:test-main>)
|
||||
|
||||
if(${Catch2_VERSION} VERSION_GREATER_EQUAL 3.0.0)
|
||||
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=0)
|
||||
else()
|
||||
target_compile_definitions(${CIFPP_TEST} PUBLIC CATCH22=1)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${CIFPP_TEST} PRIVATE cifpp::cifpp Catch2::Catch2)
|
||||
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
|
||||
target_compile_features(${CIFPP_TEST} PUBLIC cxx_std_23)
|
||||
|
||||
if(MSVC)
|
||||
# Specify unwind semantics so that MSVC knowns how to handle exceptions
|
||||
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
"run-${CIFPP_TEST}"
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch ${CIFPP_TEST})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Run${CIFPP_TEST}.touch
|
||||
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_test(NAME ${CIFPP_TEST} COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endforeach()
|
||||
if(NOT (CIFPP_TEST STREQUAL "spinner-test"))
|
||||
add_test(NAME ${CIFPP_TEST}
|
||||
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
103
test/matrix-test.cpp
Normal file
103
test/matrix-test.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 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.
|
||||
*/
|
||||
|
||||
#include "cif++/matrix.hpp"
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cif++.hpp>
|
||||
|
||||
TEST_CASE("m1")
|
||||
{
|
||||
cif::matrix3x3<int> m = cif::identity_matrix<int>(3);
|
||||
|
||||
CHECK(cif::determinant(m) == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("m2")
|
||||
{
|
||||
cif::matrix4x4<int> m = cif::identity_matrix<int>(4);
|
||||
|
||||
cif::sub_matrix<cif::matrix4x4<int>> ms(m, 1, 1);
|
||||
CHECK(ms == cif::identity_matrix<int>(3));
|
||||
}
|
||||
|
||||
TEST_CASE("m3")
|
||||
{
|
||||
cif::matrix4x4<int> m{
|
||||
{ 1, 2, 3, 4, //
|
||||
5, 6, 7, 8, //
|
||||
9, 10, 11, 12, //
|
||||
13, 14, 15, 16 }
|
||||
};
|
||||
cif::sub_matrix<cif::matrix4x4<int>> ms(m, 1, 1);
|
||||
|
||||
cif::matrix3x3<int> t{
|
||||
{ 1, 3, 4, 9, 11, 12, 13, 15, 16 }
|
||||
};
|
||||
|
||||
CHECK(ms == t);
|
||||
}
|
||||
|
||||
TEST_CASE("m4")
|
||||
{
|
||||
cif::matrix4x4<int> m{
|
||||
{
|
||||
-2,
|
||||
3,
|
||||
1,
|
||||
0,
|
||||
4,
|
||||
1,
|
||||
-3,
|
||||
2,
|
||||
0,
|
||||
-1,
|
||||
2,
|
||||
5,
|
||||
3,
|
||||
2,
|
||||
0,
|
||||
-4,
|
||||
}
|
||||
};
|
||||
|
||||
// std::cout << m << "\n\n";
|
||||
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 0)) << "\n\n";
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 1)) << "\n\n";
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 2)) << "\n\n";
|
||||
// std::cout << cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 3)) << "\n\n";
|
||||
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 0))) << "\n\n";
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 1))) << "\n\n";
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 2))) << "\n\n";
|
||||
// std::cout << cif::determinant(cif::matrix3x3<int>(cif::sub_matrix<decltype(m)>(m, 0, 3))) << "\n\n";
|
||||
|
||||
CHECK(cif::determinant(m) == 332);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <spanstream>
|
||||
#include <cif++.hpp>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -54,7 +53,7 @@ TEST_CASE("create_nonpoly_1")
|
||||
|
||||
cif::file file;
|
||||
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
|
||||
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
dbi->set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
cif::mm::structure structure(file);
|
||||
|
||||
@@ -82,7 +81,7 @@ _atom_site.pdbx_formal_charge
|
||||
# that's enough to test with
|
||||
)"_cf;
|
||||
|
||||
atoms.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
atoms.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &hem_data = atoms["HEM"];
|
||||
auto &atom_site = hem_data["atom_site"];
|
||||
@@ -94,8 +93,7 @@ _atom_site.pdbx_formal_charge
|
||||
|
||||
structure.create_non_poly(entity_id, atom_data);
|
||||
|
||||
auto expected = R"(
|
||||
data_TEST
|
||||
auto expected = R"(data_TEST
|
||||
#
|
||||
_pdbx_nonpoly_scheme.asym_id A
|
||||
_pdbx_nonpoly_scheme.ndb_seq_num 1
|
||||
@@ -139,7 +137,7 @@ _chem_comp.id HEM
|
||||
_chem_comp.type NON-POLYMER
|
||||
_chem_comp.name 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_chem_comp.formula 'C34 H32 Fe N4 O4'
|
||||
_chem_comp.formula_weight 616.487000
|
||||
_chem_comp.formula_weight 616.487
|
||||
#
|
||||
_pdbx_entity_nonpoly.entity_id 1
|
||||
_pdbx_entity_nonpoly.name 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
@@ -148,7 +146,7 @@ _pdbx_entity_nonpoly.comp_id HEM
|
||||
_entity.id 1
|
||||
_entity.type non-polymer
|
||||
_entity.pdbx_description 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_entity.formula_weight 616.487000
|
||||
_entity.formula_weight 616.487
|
||||
#
|
||||
_struct_asym.id A
|
||||
_struct_asym.entity_id 1
|
||||
@@ -157,16 +155,35 @@ _struct_asym.pdbx_modified N
|
||||
_struct_asym.details ?
|
||||
#
|
||||
_atom_type.symbol C
|
||||
)"_cf;
|
||||
)";
|
||||
|
||||
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
cif::file expected_file;
|
||||
|
||||
if (not(expected.front() == structure.get_datablock()))
|
||||
struct membuf : public std::streambuf
|
||||
{
|
||||
std::cerr << expected.front() << '\n'
|
||||
membuf(char *text, std::size_t length)
|
||||
{
|
||||
this->setg(text, text, text + length);
|
||||
}
|
||||
} buffer(const_cast<char *>(expected), std::strlen(expected));
|
||||
|
||||
std::istream is(&buffer);
|
||||
|
||||
expected_file.load(is);
|
||||
expected_file.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
if (not(expected_file.front() == structure.get_datablock()))
|
||||
{
|
||||
CHECK(false);
|
||||
std::cout << expected << '\n'
|
||||
<< '\n'
|
||||
<< structure.get_datablock() << '\n';
|
||||
REQUIRE(false);
|
||||
|
||||
|
||||
std::ofstream of("/tmp/a");
|
||||
of << expected;
|
||||
|
||||
file.save("/tmp/b");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +195,7 @@ TEST_CASE("create_nonpoly_2")
|
||||
|
||||
cif::file file;
|
||||
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
|
||||
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
dbi->set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
cif::mm::structure structure(file);
|
||||
|
||||
@@ -195,9 +212,9 @@ TEST_CASE("create_nonpoly_2")
|
||||
{ "type_symbol", type_symbol },
|
||||
{ "label_atom_id", label_atom_id },
|
||||
{ "auth_atom_id", label_atom_id },
|
||||
{ "Cartn_x", Cartn_x },
|
||||
{ "Cartn_y", Cartn_y },
|
||||
{ "Cartn_z", Cartn_z } });
|
||||
{ "Cartn_x", { Cartn_x, 3 } },
|
||||
{ "Cartn_y", { Cartn_y, 3 } },
|
||||
{ "Cartn_z", { Cartn_z, 3 } } });
|
||||
|
||||
if (atoms.size() == 4)
|
||||
break;
|
||||
@@ -243,14 +260,14 @@ _atom_site.auth_atom_id
|
||||
_atom_site.pdbx_PDB_model_num
|
||||
1 A ? A CHA HEM 1 . C HETATM ? 2.748 -19.531 39.896 1.00 ? 1 HEM CHA 1
|
||||
2 A ? A CHB HEM 1 . C HETATM ? 3.258 -17.744 35.477 1.00 ? 1 HEM CHB 1
|
||||
3 A ? A CHC HEM 1 . C HETATM ? 1.703 -21.9 33.637 1.00 ? 1 HEM CHC 1
|
||||
3 A ? A CHC HEM 1 . C HETATM ? 1.703 -21.900 33.637 1.00 ? 1 HEM CHC 1
|
||||
4 A ? A CHD HEM 1 . C HETATM ? 1.149 -23.677 38.059 1.00 ? 1 HEM CHD 1
|
||||
#
|
||||
_chem_comp.id HEM
|
||||
_chem_comp.type NON-POLYMER
|
||||
_chem_comp.name 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_chem_comp.formula 'C34 H32 Fe N4 O4'
|
||||
_chem_comp.formula_weight 616.487000
|
||||
_chem_comp.formula_weight 616.487
|
||||
#
|
||||
_pdbx_entity_nonpoly.entity_id 1
|
||||
_pdbx_entity_nonpoly.name 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
@@ -259,7 +276,7 @@ _pdbx_entity_nonpoly.comp_id HEM
|
||||
_entity.id 1
|
||||
_entity.type non-polymer
|
||||
_entity.pdbx_description 'PROTOPORPHYRIN IX CONTAINING FE'
|
||||
_entity.formula_weight 616.487000
|
||||
_entity.formula_weight 616.487
|
||||
#
|
||||
_struct_asym.id A
|
||||
_struct_asym.entity_id 1
|
||||
@@ -270,13 +287,11 @@ _struct_asym.details ?
|
||||
_atom_type.symbol C
|
||||
)"_cf;
|
||||
|
||||
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
REQUIRE(expected.front() == structure.get_datablock());
|
||||
expected.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
if (not(expected.front() == structure.get_datablock()))
|
||||
{
|
||||
// REQUIRE(false);
|
||||
CHECK(false);
|
||||
std::cout << expected.front() << '\n'
|
||||
<< '\n'
|
||||
<< structure.get_datablock() << '\n';
|
||||
@@ -354,14 +369,14 @@ _struct_asym.details ?
|
||||
#
|
||||
)"_cf;
|
||||
|
||||
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
data.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
cif::mm::structure s(data);
|
||||
|
||||
REQUIRE(s.get_atom_by_id("1").get_label_atom_id() == "CHA");
|
||||
REQUIRE(s.get_atom_by_id("2").get_label_atom_id() == "CHC");
|
||||
REQUIRE(s.get_atom_by_id("3").get_label_atom_id() == "CHB");
|
||||
REQUIRE(s.get_atom_by_id("4").get_label_atom_id() == "CHD");
|
||||
CHECK(s.get_atom_by_id("1").get_label_atom_id() == "CHA");
|
||||
CHECK(s.get_atom_by_id("2").get_label_atom_id() == "CHC");
|
||||
CHECK(s.get_atom_by_id("3").get_label_atom_id() == "CHB");
|
||||
CHECK(s.get_atom_by_id("4").get_label_atom_id() == "CHD");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -382,19 +397,19 @@ TEST_CASE("atom_numbers_1")
|
||||
{
|
||||
auto atom = structure.get_atom_by_id(id);
|
||||
|
||||
REQUIRE(atom.get_label_asym_id() == label_asym_id);
|
||||
REQUIRE(atom.get_label_seq_id() == label_seq_id);
|
||||
REQUIRE(atom.get_label_atom_id() == label_atom_id);
|
||||
REQUIRE(atom.get_auth_seq_id() == auth_seq_id);
|
||||
REQUIRE(atom.get_label_comp_id() == label_comp_id);
|
||||
CHECK(atom.get_label_asym_id() == label_asym_id);
|
||||
CHECK(atom.get_label_seq_id() == label_seq_id);
|
||||
CHECK(atom.get_label_atom_id() == label_atom_id);
|
||||
CHECK(atom.get_auth_seq_id() == auth_seq_id);
|
||||
CHECK(atom.get_label_comp_id() == label_comp_id);
|
||||
|
||||
REQUIRE(ai != atoms.end());
|
||||
CHECK(ai != atoms.end());
|
||||
|
||||
REQUIRE(ai->id() == id);
|
||||
CHECK(ai->id() == id);
|
||||
++ai;
|
||||
}
|
||||
|
||||
REQUIRE(ai == atoms.end());
|
||||
CHECK(ai == atoms.end());
|
||||
}
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -414,9 +429,7 @@ TEST_CASE("test_load_2")
|
||||
auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
|
||||
|
||||
for (auto &poly : s.polymers())
|
||||
{
|
||||
REQUIRE(poly.size() == pdbx_poly_seq_scheme.find("asym_id"_key == poly.get_asym_id()).size());
|
||||
}
|
||||
CHECK(poly.size() == pdbx_poly_seq_scheme.find("asym_id"_key == poly.get_asym_id()).size());
|
||||
}
|
||||
|
||||
TEST_CASE("remove_residue_1")
|
||||
@@ -429,7 +442,7 @@ TEST_CASE("remove_residue_1")
|
||||
cif::mm::structure s(file);
|
||||
s.remove_residue(s.get_residue("B"));
|
||||
|
||||
REQUIRE_NOTHROW(s.validate_atoms());
|
||||
CHECK_NOTHROW(s.validate_atoms());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -566,7 +579,7 @@ _struct_asym.details ?
|
||||
#
|
||||
)"_cf;
|
||||
|
||||
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
data.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
SECTION("max")
|
||||
{
|
||||
|
||||
@@ -49,6 +49,4 @@ TEST_CASE("q-1")
|
||||
|
||||
CHECK(pdbx_poly_seq_scheme.count("asym_id"_key == "A" and "entity_id"_key == 1 and "seq_id"_key == 1 and "mon_id"_key == "PRO" and "hetero"_key == false) == 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -24,15 +24,19 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/utilities.hpp"
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <cif++.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
TEST_CASE("reconstruct")
|
||||
{
|
||||
cif::VERBOSE = 1;
|
||||
|
||||
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
|
||||
|
||||
for (std::filesystem::directory_iterator i(gTestDir / "reconstruct"); i != std::filesystem::directory_iterator{}; ++i)
|
||||
@@ -57,6 +61,8 @@ TEST_CASE("reconstruct")
|
||||
CHECK((bool)ec);
|
||||
|
||||
CHECK(cif::pdb::reconstruct_pdbx(f));
|
||||
|
||||
f.save(std::filesystem::temp_directory_path() / i->path().filename());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,4 +183,6 @@ TEST_CASE("delete_sugar_1")
|
||||
// file.save(gTestDir / "test-create_sugar_3.cif");
|
||||
|
||||
cif::mm::structure s2(file);
|
||||
|
||||
file.save("/tmp/min-s.cif");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <cif++.hpp>
|
||||
#include <cif++/utilities.hpp>
|
||||
#include <cif++/compound.hpp>
|
||||
|
||||
std::filesystem::path gTestDir = std::filesystem::current_path();
|
||||
|
||||
@@ -11,12 +12,7 @@ int main(int argc, char *argv[])
|
||||
Catch::Session session; // There must be exactly one instance
|
||||
|
||||
// Build a new parser on top of Catch2's
|
||||
#if CATCH22
|
||||
using namespace Catch::clara;
|
||||
#else
|
||||
// Build a new parser on top of Catch2's
|
||||
using namespace Catch::Clara;
|
||||
#endif
|
||||
|
||||
auto cli = session.cli() // Get Catch2's command line parser
|
||||
| Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2024 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,11 +26,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CATCH22
|
||||
#include <catch2/catch.hpp>
|
||||
#else
|
||||
#include <catch2/catch_all.hpp>
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
@@ -24,15 +24,17 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cif++/point.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <cif++.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
#pragma warning (disable : 4127) // conditional expression is constant
|
||||
# pragma warning(disable : 5054) // warning C5054: operator '&': deprecated between enumerations of different types
|
||||
# pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Eigenvalues>
|
||||
@@ -315,8 +317,7 @@ TEST_CASE("m2q_0a")
|
||||
cif::point p2 = p1;
|
||||
p2.rotate(q);
|
||||
|
||||
cif::matrix3x3<float> rot_c({
|
||||
static_cast<float>(d[0]),
|
||||
cif::matrix3x3<float> rot_c({ static_cast<float>(d[0]),
|
||||
static_cast<float>(d[1]),
|
||||
static_cast<float>(d[2]),
|
||||
static_cast<float>(d[3]),
|
||||
@@ -324,8 +325,7 @@ TEST_CASE("m2q_0a")
|
||||
static_cast<float>(d[5]),
|
||||
static_cast<float>(d[6]),
|
||||
static_cast<float>(d[7]),
|
||||
static_cast<float>(d[8])
|
||||
});
|
||||
static_cast<float>(d[8]) });
|
||||
|
||||
cif::point p3 = rot_c * p1;
|
||||
|
||||
@@ -465,6 +465,7 @@ TEST_CASE("symm_4wvp_1")
|
||||
using namespace cif::literals;
|
||||
|
||||
cif::file f(gTestDir / "4wvp.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &db = f.front();
|
||||
cif::mm::structure s(db);
|
||||
@@ -491,6 +492,7 @@ TEST_CASE("symm_4wvp_1")
|
||||
TEST_CASE("symm_2bi3_1")
|
||||
{
|
||||
cif::file f(gTestDir / "2bi3.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &db = f.front();
|
||||
cif::mm::structure s(db);
|
||||
@@ -537,6 +539,7 @@ TEST_CASE("symm_2bi3_1a")
|
||||
using namespace cif::literals;
|
||||
|
||||
cif::file f(gTestDir / "2bi3.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &db = f.front();
|
||||
|
||||
@@ -580,6 +583,7 @@ TEST_CASE("symm_2bi3_1a")
|
||||
TEST_CASE("symm_3bwh_1")
|
||||
{
|
||||
cif::file f(gTestDir / "3bwh.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &db = f.front();
|
||||
|
||||
@@ -603,6 +607,7 @@ TEST_CASE("symm_3bwh_1")
|
||||
TEST_CASE("volume_3bwh_1")
|
||||
{
|
||||
cif::file f(gTestDir / "1juh.cif.gz");
|
||||
f.front().set_validator(cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
auto &db = f.front();
|
||||
|
||||
@@ -610,3 +615,40 @@ TEST_CASE("volume_3bwh_1")
|
||||
|
||||
CHECK_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("smallest_sphere-1")
|
||||
{
|
||||
std::vector<cif::point> pts{
|
||||
{ 0.9295, 4.9006, 46.9706 },
|
||||
{ -0.1215, 5.5936, 46.0726 },
|
||||
{ -0.7975, 4.7046, 45.0796 },
|
||||
{ -1.4875, 3.5486, 45.7196 },
|
||||
{ -0.6535, 2.8816, 46.8186 },
|
||||
{ 0.3825, 3.5156, 47.4496 },
|
||||
{ 1.1995, 2.9206, 48.5286 },
|
||||
{ 0.8255, 2.0466, 49.4716 },
|
||||
{ 1.6625, 1.5036, 50.5176 },
|
||||
{ 1.1165, 0.6056, 51.3626 },
|
||||
{ 1.8325, -0.0064, 52.4656 },
|
||||
{ 1.1945, -0.9044, 53.2216 },
|
||||
{ 1.8135, -1.5534, 54.3566 },
|
||||
{ 1.0925, -2.4574, 55.0656 },
|
||||
{ 1.5205, -3.2204, 56.2476 },
|
||||
{ 1.1955, 5.8066, 48.1796 },
|
||||
{ 2.2495, 4.6896, 46.1796 },
|
||||
{ -1.2515, 1.5186, 47.1786 },
|
||||
{ 3.1385, 1.9106, 50.6166 },
|
||||
{ 3.2605, -1.1834, 54.7206 },
|
||||
{ 2.5975, -3.8554, 56.2096 },
|
||||
{ 0.7975, -3.2184, 57.2686 }
|
||||
};
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
auto [c, r] = cif::smallest_sphere_around_points(pts);
|
||||
CHECK_THAT(cif::distance(c, cif::point{ 0, 0.743099928, 51.1741028 }), Catch::Matchers::WithinAbs(0.f, 0.01f));
|
||||
CHECK_THAT(r, Catch::Matchers::WithinAbs(7.31248331f, 0.01f));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET ZLIB_VERSION=1.3
|
||||
SET PCRE2_VERSION=10.45
|
||||
|
||||
IF NOT EXIST build_ci\libs (
|
||||
MKDIR build_ci\libs
|
||||
@@ -22,19 +21,4 @@ cmake --build build --target ALL_BUILD --config Release || EXIT /b 1
|
||||
cmake --build build --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build build --target INSTALL --config Release || EXIT /b 1
|
||||
|
||||
@REM Install PCRE2
|
||||
IF NOT EXIST pcre2-%PCRE2_VERSION%.zip (
|
||||
ECHO Downloading https://github.com/PCRE2Project/pcre2/releases/download/pcre2-%PCRE2_VERSION%/pcre2-%PCRE2_VERSION%.zip
|
||||
curl -L -o pcre2-%PCRE2_VERSION%.zip https://github.com/PCRE2Project/pcre2/releases/download/pcre2-%PCRE2_VERSION%/pcre2-%PCRE2_VERSION%.zip || EXIT /b 1
|
||||
)
|
||||
IF NOT EXIST pcre2-%PCRE2_VERSION% (
|
||||
ECHO Unpacking pcre2-%PCRE2_VERSION%.zip
|
||||
C:\windows\system32\tar.exe -x -f pcre2-%PCRE2_VERSION%.zip || EXIT /b 1
|
||||
)
|
||||
CD pcre2-%PCRE2_VERSION%
|
||||
cmake -B build -DPCRE2_BUILD_PCRE2GREP=OFF || EXIT /b 1
|
||||
cmake --build build --target ALL_BUILD --config Release || EXIT /b 1
|
||||
@REM cmake --build build --target RUN_TESTS --config Release || EXIT /b 1
|
||||
cmake --build build --target INSTALL --config Release || EXIT /b 1
|
||||
|
||||
@EXIT /b 0
|
||||
|
||||
@@ -63,7 +63,7 @@ update_dictionary() {
|
||||
|
||||
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz"
|
||||
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_pdbx.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz"
|
||||
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic"
|
||||
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic"
|
||||
|
||||
# notify subscribers, using find instead of run-parts to make it work on FreeBSD as well
|
||||
|
||||
|
||||
Reference in New Issue
Block a user