Compare commits

...

90 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
7efa8970cc why? 2026-02-13 19:03:14 +01:00
Maarten L. Hekkelman
0da03af25f Fix condition for testing null 2026-02-13 13:17:20 +01:00
Maarten L. Hekkelman
70a6076242 Fix memory corruption 2026-02-13 11:33:11 +01:00
Maarten L. Hekkelman
24386adfd6 Fix conditions and a memory leak 2026-02-13 10:35:10 +01:00
Maarten L. Hekkelman
79674aef8f Assertions added 2026-02-13 09:34:40 +01:00
Maarten L. Hekkelman
6348f59f15 three tests passing 2026-02-12 20:12:50 +01:00
Maarten L. Hekkelman
276ce57d5e model-test passing 2026-02-12 19:56:05 +01:00
Maarten L. Hekkelman
cb81411d72 more work 2026-02-12 16:35:10 +01:00
Maarten L. Hekkelman
6ec7033769 Merge branch 'new-item-storage-plus' of github.com:PDB-REDO/libcifpp into new-item-storage-plus 2026-02-12 14:43:39 +01:00
Maarten L. Hekkelman
341b248175 There's still a lot of work to do 2026-02-12 10:58:08 +01:00
Maarten L. Hekkelman
da521fd2d1 attempting next test 2026-02-12 08:53:39 +01:00
Maarten L. Hekkelman
25e167cfd3 no updateLinked when parsing 2026-02-12 08:44:07 +01:00
Maarten L. Hekkelman
83a2def09c Remove boolean as basic type 2026-02-11 16:41:13 +01:00
Maarten L. Hekkelman
a6a27aaf00 Some cleaning up 2026-02-11 16:18:14 +01:00
Maarten L. Hekkelman
7d31880fce Start work on unit-3d-test 2026-02-11 15:58:36 +01:00
Maarten L. Hekkelman
81c02d1f77 unit-test-2 passes 2026-02-11 15:27:26 +01:00
Maarten L. Hekkelman
ce21d26705 Compiling... 2026-02-11 10:22:12 +01:00
Maarten L. Hekkelman
25a73d288d backup 2026-02-10 16:53:18 +01:00
Maarten L. Hekkelman
abc61afe7d some merging 2026-02-10 10:00:51 +01:00
Maarten L. Hekkelman
a0f4eada6f a start 2026-02-04 16:39:39 +01:00
Maarten L. Hekkelman
64e6b3cd2d pfft 2025-12-27 13:37:21 +01:00
Maarten L. Hekkelman
f19c6d078e Merge branch 'trunk' into develop 2025-12-20 08:40:04 +01:00
Maarten L. Hekkelman
73f18a4da2 PCRE2 is not thread safe, the way it is used in libcifpp type validator 2025-12-20 08:38:59 +01:00
Maarten L. Hekkelman
7a9d94bc57 private linking to fast_float 2025-11-27 15:34:20 +01:00
Maarten L. Hekkelman
a3ba760ab5 Merge branch 'develop' into trunk 2025-11-27 15:29:46 +01:00
Maarten L. Hekkelman
913abcd1b3 Fixes in makefile 2025-11-27 11:26:47 +01:00
Maarten L. Hekkelman
510e336306 exclude from all for fast_float 2025-11-27 09:06:22 +01:00
Maarten L. Hekkelman
ffff2479d2 revert version-string code 2025-11-26 13:17:14 +01:00
Maarten L. Hekkelman
b550e9b027 re-enable tests 2025-11-19 13:28:47 +01:00
Maarten L. Hekkelman
452bb83ce7 Remove revision.hpp file when making clean 2025-11-19 11:39:27 +01:00
Maarten L. Hekkelman
6eda9aaf36 better center_and_radius for residue 2025-11-18 16:44:53 +01:00
Maarten L. Hekkelman
251fb55d6a fixing smallest sphere 2025-11-05 13:18:58 +01:00
Maarten L. Hekkelman
f94e9aece9 create_non_poly, another 2025-11-05 11:03:56 +01:00
Maarten L. Hekkelman
c565bb96be Do not run the spinner test 2025-10-30 09:19:50 +01:00
Maarten L. Hekkelman
e51f31dc4c Remove libfmt, fix instantiating templates for fast_float usage 2025-10-30 09:08:44 +01:00
Maarten L. Hekkelman
4e128885d6 Added missing include 2025-10-29 18:21:47 +01:00
Maarten L. Hekkelman
b37054228d Added smalles sphere function 2025-10-29 17:09:13 +01:00
Maarten L. Hekkelman
815b33fee0 Matrix determinant for 4x4 2025-10-28 15:55:05 +01:00
Maarten L. Hekkelman
97f55c1639 Version bump 2025-10-22 10:05:55 +02:00
Maarten L. Hekkelman
89de73eb6f Added exists to compound_factory 2025-10-21 13:06:09 +02:00
Maarten L. Hekkelman
75f2ec3792 Remove warning 2025-10-13 14:22:57 +02:00
Maarten L. Hekkelman
f4d29e8da9 re-enable test to see if fast_float is required 2025-10-01 17:09:06 +02:00
Maarten L. Hekkelman
b97b2638b8 More supported float types 2025-10-01 17:08:33 +02:00
Maarten L. Hekkelman
bc0222dc0e attempt two 2025-10-01 16:42:05 +02:00
Maarten L. Hekkelman
10a6b5649b Using fast float instead of home baked version 2025-10-01 16:14:07 +02:00
Maarten L. Hekkelman
743e2800f8 update changelog 2025-09-30 11:21:03 +02:00
Maarten L. Hekkelman
32ac884127 Do not stop on empty audit_conform fields 2025-09-29 10:34:29 +02:00
Maarten L. Hekkelman
bec69f7d07 Fix reconstruction when entity ID's are missing 2025-09-29 09:59:04 +02:00
Maarten L. Hekkelman
a99215ad6a version bump 2025-09-24 16:45:05 +02:00
Maarten L. Hekkelman
e3d2cbd044 Lower required catch2 version 2025-09-24 16:42:45 +02:00
Maarten L. Hekkelman
5fc965789d messages updated 2025-09-24 15:11:33 +02:00
Maarten L. Hekkelman
b4596902aa Add compile features for Catch2, required on Windows 2025-09-24 14:15:19 +02:00
Maarten L. Hekkelman
cbf8b52f62 Update catch2 usage 2025-09-24 13:25:56 +02:00
Maarten L. Hekkelman
4e0fa1c916 No complete jthread on macOS/CLang 2025-09-24 13:00:50 +02:00
Maarten L. Hekkelman
95b007d38f Merge branch 'trunk' into develop 2025-09-24 11:38:01 +02:00
Maarten L. Hekkelman
b66f7a30ce Progress bar using WriteConsole on Windows 2025-09-24 11:36:37 +02:00
Maarten L. Hekkelman
ec7287c503 remove warning 2025-09-24 11:32:12 +02:00
Maarten L. Hekkelman
a41c591f0c Restore order of imports, avoid reordering by clang-format 2025-09-24 10:51:53 +02:00
Maarten L. Hekkelman
3a6527cdd5 yet another update on progress bar 2025-09-24 10:23:58 +02:00
Maarten L. Hekkelman
5f21a094c0 added flush to progress bar 2025-09-24 10:14:03 +02:00
Maarten L. Hekkelman
2203a1855d improved progress bar 2025-09-24 09:49:28 +02:00
Maarten L. Hekkelman
7edd2ecc18 new progress bar 2025-09-23 15:53:55 +02:00
Maarten L. Hekkelman
1d2953c850 Fix reconstruction, version bump 2025-09-22 13:51:18 +02:00
Maarten L. Hekkelman
dbf59ce622 reconstruct better when entity ID's are missing 2025-09-22 12:59:16 +02:00
Maarten L. Hekkelman
1596db8499 Merge branch 'develop' of github.com:pdb-redo/libcifpp into develop 2025-09-16 13:29:57 +02:00
Maarten L. Hekkelman
bd1fb5c5cd Added model::create_link 2025-09-16 13:29:51 +02:00
Maarten L. Hekkelman
da500025c3 swap atoms should swap type_symbol as well 2025-09-10 17:16:22 +02:00
Maarten L. Hekkelman
60eeea9a93 more resilient loading of dictionary data 2025-09-10 15:06:06 +02:00
Maarten L. Hekkelman
1220f01f1e change location of mmcif_ma.dic 2025-09-10 14:22:52 +02:00
Maarten L. Hekkelman
ad0a34fe98 Update changelog 2025-09-10 12:49:43 +02:00
Maarten L. Hekkelman
a7425ff1a0 Optimise validation code 2025-09-10 12:40:01 +02:00
Maarten L. Hekkelman
1ce25f86ae better check anisotrop atoms 2025-09-10 12:19:56 +02:00
Maarten L. Hekkelman
cd93f72b96 Merge branch 'develop' into better-create-entity-ids 2025-09-10 09:28:50 +02:00
Maarten L. Hekkelman
23500bd303 Fix reconstruction of really bare files 2025-09-10 09:25:49 +02:00
Maarten L. Hekkelman
14b4753b4f test for null 2025-09-09 19:55:13 +02:00
Maarten L. Hekkelman
4c37d5db5f use rowhandles 2025-09-09 19:52:39 +02:00
Maarten L. Hekkelman
fc2c4b4172 fix map::at in reconstruct sequences 2025-09-09 10:51:31 +02:00
Maarten L. Hekkelman
3ac64de16b Version bump, update changelog 2025-09-03 14:55:20 +02:00
Maarten L. Hekkelman
45eecd72b0 using pkg-config, when available 2025-09-03 14:24:29 +02:00
Maarten L. Hekkelman
d1dd558cda as object lib 2025-09-03 14:00:22 +02:00
Maarten L. Hekkelman
d19e2c2196 Update pcre2s 2025-09-03 13:11:45 +02:00
Maarten L. Hekkelman
72c7aca074 revert to catch2 version 2, due to linker errors on Windows? 2025-09-03 13:04:13 +02:00
Maarten L. Hekkelman
683a1087d0 Install catch2 before testing 2025-09-03 12:28:06 +02:00
Maarten L. Hekkelman
35bc139deb Update pcre2s 2025-09-03 11:39:00 +02:00
Maarten L. Hekkelman
45ece2fa0d pcre2 is no longer a depends on Windows 2025-09-03 11:18:04 +02:00
Maarten L. Hekkelman
11c98f553f Update Catch2 to version 3
Updated pcre2s
2025-09-03 11:15:31 +02:00
Maarten L. Hekkelman
28aa9b1036 Using pcre2s 2025-09-03 11:07:02 +02:00
Maarten L. Hekkelman
d7b5c0a748 remove message 2025-09-02 15:21:04 +02:00
Maarten L. Hekkelman
065e7f5f18 added clang format file 2025-09-02 14:59:28 +02:00
Maarten L. Hekkelman
4b1623cfdc pcre2 again 2025-09-02 14:57:07 +02:00
69 changed files with 7478 additions and 4861 deletions

22
.clang-format Normal file
View 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
View 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

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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

View File

@@ -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})

View File

@@ -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")

View File

@@ -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
View 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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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();
}
};

View File

@@ -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)));
}
/**

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 },

View File

@@ -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();

View File

@@ -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;

View 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.

View File

@@ -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();

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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
View 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()

View File

@@ -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 Hydro­gen
@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -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';
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View 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);
}

View File

@@ -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")
{

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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