Compare commits

..

2 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
92a9a067c6 for write_data_files=off 2024-01-31 10:55:08 +01:00
Maarten L. Hekkelman
9acc343145 added option to not write data files 2024-01-31 10:37:02 +01:00
87 changed files with 7891 additions and 45201 deletions

View File

@@ -1,22 +0,0 @@
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

View File

@@ -1,65 +0,0 @@
# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
name: publish docs
on:
push:
branches: [ "trunk" ]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
- name: Install dependencies Ubuntu
run: sudo apt-get update && sudo apt-get install cmake doxygen
- uses: actions/setup-python@v4
with:
python-version: '3.9'
cache: 'pip' # caching pip dependencies
- run: pip install -r docs/requirements.txt
- name: Configure CMake
run: cmake -S . -B ${{ steps.strings.outputs.build-output-dir }} -DBUILD_DOCUMENTATION=ON -DBUILD_TESTING=OFF
- name: Run Sphinx
run: |
cmake --build ${{ steps.strings.outputs.build-output-dir }} --target Sphinx-libcifpp
ls -l ${{ steps.strings.outputs.build-output-dir }}
ls -l ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: docs
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -33,32 +33,25 @@ jobs:
- name: Install dependencies Ubuntu
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install mrc catch2
run: sudo apt-get update && sudo apt-get install mrc
- 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 }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_BUILD_TYPE=Release
-DCIFPP_DOWNLOAD_CCD=OFF
-S ${{ github.workspace }}
- name: Build
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release
- name: Test
working-directory: ${{ steps.strings.outputs.build-output-dir }}
run: ctest --build-config Release --output-on-failure
- name: Install
if: matrix.os != 'windows-latest'
run: sudo cmake --install ${{ steps.strings.outputs.build-output-dir }} --config Release

8
.gitignore vendored
View File

@@ -2,7 +2,7 @@ build/
.vscode/
.vs/
tools/update-libcifpp-data
rsrc/components.cif*
data/components.cif*
CMakeSettings.json
msvc/
src/revision.hpp
@@ -11,8 +11,4 @@ Testing/
include/cif++/exports.hpp
docs/api
docs/conf.py
build_ci/
data/components.cif
perf.data*
.cache/
build_ci/

View File

@@ -11,38 +11,37 @@
# 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.
# 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.
cmake_minimum_required(VERSION 3.23)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
cmake_minimum_required(VERSION 3.16)
# set the project name
project(
libcifpp
VERSION 9.0.4
LANGUAGES CXX C)
project(libcifpp VERSION 6.0.0 LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(FindAtomic)
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckLibraryExists)
include(CMakePackageConfigHelpers)
include(CheckCXXSourceCompiles)
include(GenerateExportHeader)
include(CTest)
include(ExternalProject)
include(FetchContent)
include(ExternalProject)
set(CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# When building with ninja-multiconfig, build both debug and release by default
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
@@ -51,47 +50,50 @@ if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers"
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
elseif(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
# Build documentation?
set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the documentation")
option(BUILD_DOCUMENTATION "Build the documentation" OFF)
# Optionally build a version to be installed inside CCP4
set(BUILD_FOR_CCP4 OFF CACHE BOOL "Build a version to be installed in CCP4")
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4")
# Building shared libraries?
if(NOT(BUILD_FOR_CCP4 AND WIN32))
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build a shared library instead of a static one")
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
endif()
if(PROJECT_IS_TOP_LEVEL AND NOT BUILD_FOR_CCP4)
# Lots of code depend on the availability of the components.cif file
set(CIFPP_DOWNLOAD_CCD ON CACHE BOOL "Download the CCD file components.cif during installation")
# Lots of code depend on the availability of the components.cif file
option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" ON)
# An optional cron script can be installed to keep the data files up-to-date
if(UNIX AND NOT APPLE)
set(CIFPP_INSTALL_UPDATE_SCRIPT ON CACHE BOOL "Install the script to update CCD and dictionary files")
# An optional cron script can be installed to keep the data files up-to-date
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT BUILD_FOR_CCP4)
option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" ON)
endif()
# Optionally avoid installing data files (requires privileges at inconvenient moments)
option(WRITE_DATA_FILES "Write data files during installation" ON)
# When CCP4 is sourced in the environment, we can recreate the symmetry operations table
if(EXISTS "$ENV{CCP4}")
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
option(CIFPP_RECREATE_SYMOP_DATA "Recreate SymOp data table in case it is out of date" ON)
else()
set(CIFPP_RECREATE_SYMOP_DATA OFF)
message(WARNING "Symop data table recreation requested, but file syminfo.lib was not found in $ENV{CLIBD}")
endif()
else()
unset(CIFPP_DOWNLOAD_CCD)
unset(CIFPP_INSTALL_UPDATE_SCRIPT)
endif()
# When CCP4 is sourced in the environment, we can recreate the symmetry
# operations table
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
set(CIFPP_RECREATE_SYMOP_DATA ON CACHE BOOL "Recreate SymOp data table in case it is out of date")
set(CIFPP_RECREATE_SYMOP_DATA OFF)
message("Not trying to recreate symop_table_data.hpp since CCP4 is not defined")
endif()
# CCP4 build
if(BUILD_FOR_CCP4)
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
message(FATAL_ERROR "cifpp: A CCP4 built was requested but CCP4 was not sourced")
message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced")
else()
list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
@@ -121,6 +123,8 @@ if(WIN32)
add_definitions(-D _WIN32_WINNT=0x0501)
endif()
add_definitions(-DNOMINMAX)
# We do not want to write an export file for all our symbols...
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
@@ -130,7 +134,6 @@ if(MSVC)
add_compile_options(/permissive- /bigobj)
add_link_options(/NODEFAULTLIB:library)
# This is dubious...
if(BUILD_SHARED_LIBS)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
@@ -140,9 +143,50 @@ endif()
# Libraries
# Start by finding out if std:regex is usable. Note that the current
# implementation in GCC is not acceptable, it crashes on long lines.
# The implementation in libc++ (clang) and MSVC seem to be OK.
check_cxx_source_compiles("
#include <iostream>
#ifndef __GLIBCXX__
#error
#endif
int main(int argc, char *argv[]) { return 0; }" GXX_LIBSTDCPP)
if(GXX_LIBSTDCPP)
message(STATUS "Testing for known regex bug, since you're using GNU libstdc++")
try_run(STD_REGEX_RUNNING STD_REGEX_COMPILING
${CMAKE_CURRENT_BINARY_DIR}/test ${PROJECT_SOURCE_DIR}/cmake/test-rx.cpp)
if(STD_REGEX_RUNNING STREQUAL FAILED_TO_RUN)
message(STATUS "You are probably trying to compile using the g++ standard library which contains a crashing std::regex implementation. Will use boost::regex instead")
find_package(Boost 1.80 QUIET COMPONENTS regex)
if(NOT Boost_FOUND)
set(BOOST_REGEX_STANDALONE ON)
FetchContent_Declare(
boost-rx
GIT_REPOSITORY https://github.com/boostorg/regex
GIT_TAG boost-1.83.0
)
FetchContent_MakeAvailable(boost-rx)
endif()
set(BOOST_REGEX ON)
endif()
endif()
set(CMAKE_THREAD_PREFER_PTHREAD)
set(THREADS_PREFER_PTHREAD_FLAG)
find_package(Threads)
if(MSVC)
# Avoid linking the shared library of zlib. Search ZLIB_ROOT first if it is
# set.
# Avoid linking the shared library of zlib
# Search ZLIB_ROOT first if it is set.
if(ZLIB_ROOT)
set(_ZLIB_SEARCH_ROOT PATHS ${ZLIB_ROOT} NO_DEFAULT_PATH)
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_ROOT)
@@ -152,7 +196,8 @@ if(MSVC)
set(_ZLIB_x86 "(x86)")
set(_ZLIB_SEARCH_NORMAL
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]"
"$ENV{ProgramFiles}/zlib" "$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
"$ENV{ProgramFiles}/zlib"
"$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
unset(_ZLIB_x86)
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
@@ -161,206 +206,144 @@ if(MSVC)
endif()
foreach(search ${_ZLIB_SEARCHES})
find_library(
ZLIB_LIBRARY
NAMES zlibstatic NAMES_PER_DIR ${${search}}
PATH_SUFFIXES lib)
find_library(ZLIB_LIBRARY NAMES zlibstatic NAMES_PER_DIR ${${search}} PATH_SUFFIXES lib)
endforeach()
endif()
# First check if <format> is available
find_file(FMT NAME format)
find_package(ZLIB REQUIRED)
if(FMT EQUAL "FMT-NOTFOUND")
if(NOT (fmt_FOUND OR TARGET fmt))
find_package(fmt REQUIRED)
message(FATAL_ERROR "cifpp: <format> not found and neither was libfmt, compiler too old, you're out of luck")
endif()
endif()
find_package(Eigen3 QUIET)
# 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)
FetchContent_MakeAvailable(fastfloat)
endif()
find_package(Threads)
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.
find_package(Eigen3 3.4 QUIET)
if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen
INTERFACE_INCLUDE_DIRECTORIES)
if(Eigen3_FOUND)
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
else()
# Use ExternalProject since FetchContent always tries to install the result...
ExternalProject_Add(my-eigen3
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
ExternalProject_Add(
local_Eigen3
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/external
)
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
set(EIGEN3_D local_Eigen3)
set(EIGEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/include/eigen3)
endif()
include(FindFilesystem)
list(APPEND CIFPP_REQUIRED_LIBRARIES ${STDCPPFS_LIBRARY})
include(FindAtomic)
list(APPEND CIFPP_REQUIRED_LIBRARIES ${STDCPPATOMIC_LIBRARY})
# Create a revision file, containing the current git version info
include(VersionString)
write_version_header(${CMAKE_CURRENT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP")
write_version_header(${PROJECT_SOURCE_DIR}/src/ LIB_NAME "LibCIFPP")
# SymOp data table
if(CIFPP_RECREATE_SYMOP_DATA)
# The tool to create the table
add_executable(symop-map-generator
"${CMAKE_CURRENT_SOURCE_DIR}/src/symop-map-generator.cpp")
target_compile_features(symop-map-generator PUBLIC cxx_std_20)
add_executable(symop-map-generator "${PROJECT_SOURCE_DIR}/src/symop-map-generator.cpp")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND
$<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib
$ENV{CLIBD}/symop.lib ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp)
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND $<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib $ENV{CLIBD}/symop.lib ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
)
add_custom_target(
OUTPUT
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
"$ENV{CLIBD}/symop.lib")
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib" "$ENV{CLIBD}/symop.lib"
)
endif()
# Sources
set(project_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/condition.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/datablock.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/dictionary_parser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/item.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/parser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/row.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/validate.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/text.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
${PROJECT_SOURCE_DIR}/src/category.cpp
${PROJECT_SOURCE_DIR}/src/condition.cpp
${PROJECT_SOURCE_DIR}/src/datablock.cpp
${PROJECT_SOURCE_DIR}/src/dictionary_parser.cpp
${PROJECT_SOURCE_DIR}/src/file.cpp
${PROJECT_SOURCE_DIR}/src/item.cpp
${PROJECT_SOURCE_DIR}/src/parser.cpp
${PROJECT_SOURCE_DIR}/src/row.cpp
${PROJECT_SOURCE_DIR}/src/validate.cpp
${PROJECT_SOURCE_DIR}/src/text.cpp
${PROJECT_SOURCE_DIR}/src/utilities.cpp
${PROJECT_SOURCE_DIR}/src/atom_type.cpp
${PROJECT_SOURCE_DIR}/src/compound.cpp
${PROJECT_SOURCE_DIR}/src/point.cpp
${PROJECT_SOURCE_DIR}/src/symmetry.cpp
${PROJECT_SOURCE_DIR}/src/model.cpp
${PROJECT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb_record.hpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
${PROJECT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
)
set(project_headers
include/cif++.hpp
include/cif++/atom_type.hpp
include/cif++/category.hpp
include/cif++/compound.hpp
include/cif++/condition.hpp
include/cif++/datablock.hpp
include/cif++/dictionary_parser.hpp
include/cif++/exports.hpp
include/cif++/file.hpp
include/cif++/format.hpp
include/cif++/forward_decl.hpp
include/cif++/gzio.hpp
include/cif++/item.hpp
include/cif++/iterator.hpp
include/cif++/matrix.hpp
include/cif++/model.hpp
include/cif++/parser.hpp
include/cif++/pdb/cif2pdb.hpp
include/cif++/pdb.hpp
include/cif++/pdb/io.hpp
include/cif++/pdb/pdb2cif.hpp
include/cif++/pdb/tls.hpp
include/cif++/point.hpp
include/cif++/row.hpp
include/cif++/symmetry.hpp
include/cif++/text.hpp
include/cif++/utilities.hpp
include/cif++/validate.hpp
${PROJECT_SOURCE_DIR}/include/cif++.hpp
${PROJECT_SOURCE_DIR}/include/cif++/utilities.hpp
${PROJECT_SOURCE_DIR}/include/cif++/item.hpp
${PROJECT_SOURCE_DIR}/include/cif++/datablock.hpp
${PROJECT_SOURCE_DIR}/include/cif++/file.hpp
${PROJECT_SOURCE_DIR}/include/cif++/validate.hpp
${PROJECT_SOURCE_DIR}/include/cif++/iterator.hpp
${PROJECT_SOURCE_DIR}/include/cif++/parser.hpp
${PROJECT_SOURCE_DIR}/include/cif++/forward_decl.hpp
${PROJECT_SOURCE_DIR}/include/cif++/dictionary_parser.hpp
${PROJECT_SOURCE_DIR}/include/cif++/condition.hpp
${PROJECT_SOURCE_DIR}/include/cif++/category.hpp
${PROJECT_SOURCE_DIR}/include/cif++/row.hpp
${PROJECT_SOURCE_DIR}/include/cif++/atom_type.hpp
${PROJECT_SOURCE_DIR}/include/cif++/compound.hpp
${PROJECT_SOURCE_DIR}/include/cif++/point.hpp
${PROJECT_SOURCE_DIR}/include/cif++/symmetry.hpp
${PROJECT_SOURCE_DIR}/include/cif++/model.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/cif2pdb.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/io.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/pdb2cif.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/tls.hpp
)
add_library(cifpp)
add_library(cifpp ${project_sources} ${project_headers} ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
add_library(cifpp::cifpp ALIAS cifpp)
if(TARGET my-eigen3)
add_dependencies(cifpp my-eigen3)
set(CMAKE_DEBUG_POSTFIX d)
set_target_properties(cifpp PROPERTIES DEBUG_POSTFIX "d")
generate_export_header(cifpp EXPORT_FILE_NAME ${PROJECT_SOURCE_DIR}/include/cif++/exports.hpp)
if(BOOST_REGEX)
target_compile_definitions(cifpp PRIVATE USE_BOOST_REGEX=1 BOOST_REGEX_STANDALONE=1)
get_target_property(BOOST_REGEX_INCLUDE_DIR Boost::regex INTERFACE_INCLUDE_DIRECTORIES)
endif()
target_sources(cifpp
PRIVATE ${project_sources}
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
PUBLIC
FILE_SET cifpp_headers TYPE HEADERS
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
FILES ${project_headers}
)
# The code now really requires C++20
target_compile_features(cifpp PUBLIC cxx_std_20)
generate_export_header(cifpp EXPORT_FILE_NAME
${CMAKE_CURRENT_SOURCE_DIR}/include/cif++/exports.hpp)
if(MSVC)
target_compile_definitions(cifpp PUBLIC NOMINMAX=1)
endif()
set_target_properties(cifpp PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(
cifpp
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
target_include_directories(cifpp
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE "${EIGEN_INCLUDE_DIR}")
PRIVATE
"${EIGEN_INCLUDE_DIR}"
"${BOOST_REGEX_INCLUDE_DIR}"
)
target_link_libraries(cifpp
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES})
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 PUBLIC FastFloat::fast_float)
endif()
if(fmt_FOUND)
target_link_libraries(cifpp PUBLIC fmt)
if(${EIGEN3_D})
add_dependencies(cifpp ${EIGEN3_D})
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
@@ -369,100 +352,82 @@ endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CIFPP_DOWNLOAD_CCD)
# download the components.cif file from CCD
set(COMPONENTS_CIF ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/components.cif)
set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/data/components.cif)
if(EXISTS ${COMPONENTS_CIF})
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
if(CCD_FILE_SIZE EQUAL 0)
message(STATUS "cifpp: Removing empty ${COMPONENTS_CIF} file")
message(STATUS "Removing empty ${COMPONENTS_CIF} file")
file(REMOVE "${COMPONENTS_CIF}")
endif()
endif()
if(NOT EXISTS ${COMPONENTS_CIF})
# Since the file(DOWNLOAD) command in cmake does not use compression, we try
# to download the gzipped version and decompress it ourselves.
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/data)
file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
endif()
# Since the file(DOWNLOAD) command in cmake does not use
# compression, we try to download the gzipped version and
# decompress it ourselves.
find_program(GUNZIP gunzip)
if(WIN32 OR GUNZIP STREQUAL "GUNZIP-NOTFOUND")
file(
DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif
${COMPONENTS_CIF}
SHOW_PROGRESS
STATUS CCD_FETCH_STATUS)
else()
if(GUNZIP)
if(NOT EXISTS "${COMPONENTS_CIF}.gz")
file(
DOWNLOAD
https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
${COMPONENTS_CIF}.gz
SHOW_PROGRESS
STATUS CCD_FETCH_STATUS)
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz
SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
endif()
add_custom_command(
OUTPUT ${COMPONENTS_CIF}
COMMAND "${GUNZIP}" ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/)
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
add_custom_command(OUTPUT ${COMPONENTS_CIF}
COMMAND ${GUNZIP} ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
else()
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF}
SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
endif()
# Do not continue if downloading went wrong
list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE)
if(CCD_FETCH_STATUS_CODE)
message(
FATAL_ERROR "cifpp: Error trying to download CCD file: ${CCD_FETCH_STATUS}")
message(FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}")
endif()
endif()
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
endif()
# Installation directories
if(BUILD_FOR_CCP4)
set(CIFPP_DATA_DIR
"$ENV{CCP4}/share/libcifpp"
CACHE PATH "Directory where dictionary and other static data is stored")
set(CIFPP_DATA_DIR "$ENV{CCP4}/share/libcifpp" CACHE PATH "Directory where dictionary and other static data is stored")
else()
set(CIFPP_DATA_DIR
"${CMAKE_INSTALL_FULL_DATADIR}/libcifpp"
CACHE PATH "Directory where dictionary and other static data is stored")
set(CIFPP_DATA_DIR "${CMAKE_INSTALL_FULL_DATADIR}/libcifpp" CACHE PATH "Directory where dictionary and other static data is stored")
endif()
if(CIFPP_DATA_DIR)
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
set_target_properties(cifpp PROPERTIES CIFPP_DATA_DIR ${CIFPP_DATA_DIR})
endif()
if(NOT PROJECT_IS_TOP_LEVEL)
set(CIFPP_SHARE_DIR ${CIFPP_DATA_DIR} PARENT_SCOPE)
endif()
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
if(UNIX AND NOT BUILD_FOR_CCP4)
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
set(CIFPP_CACHE_DIR
"/var/cache/libcifpp"
CACHE PATH "The directory where downloaded data files are stored")
set(CIFPP_CACHE_DIR "/var/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored")
else()
set(CIFPP_CACHE_DIR
"${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp"
CACHE PATH "The directory where downloaded data files are stored")
set(CIFPP_CACHE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp" CACHE PATH "The directory where downloaded data files are stored")
endif()
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
set(CIFPP_ETC_DIR
"${CMAKE_INSTALL_FULL_SYSCONFDIR}"
CACHE PATH "The directory where the update configuration file is stored")
set(CIFPP_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}" CACHE PATH "The directory where the update configuration file is stored")
else()
unset(CIFPP_CACHE_DIR)
endif()
# Install rules
install(TARGETS cifpp
EXPORT cifpp
FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
EXPORT cifpp-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if(MSVC AND BUILD_SHARED_LIBS)
install(
@@ -477,92 +442,157 @@ file(GLOB OLD_CONFIG_FILES
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
if(OLD_CONFIG_FILES)
message(
STATUS "cifpp: Installation will remove old config files: ${OLD_CONFIG_FILES}")
message(STATUS "Installation will remove old config files: ${OLD_CONFIG_FILES}")
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
endif()
install(EXPORT cifpp
NAMESPACE cifpp::
install(EXPORT cifpp-targets
FILE "cifpp-targets.cmake"
DESTINATION lib/cmake/cifpp)
NAMESPACE cifpp::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
)
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
DIRECTORY include/cif++
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT Devel
)
if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
install(FILES ${COMPONENTS_CIF}
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
install(
FILES include/cif++.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT Devel
)
if(WRITE_DATA_FILES)
install(FILES
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic
${COMPONENTS_CIF}
DESTINATION ${CIFPP_DATA_DIR}
)
if(CIFPP_CACHE_DIR)
install(FILES
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
${PROJECT_SOURCE_DIR}/rsrc/mmcif_ma.dic
${COMPONENTS_CIF}
DESTINATION ${CIFPP_CACHE_DIR}
)
endif()
endif()
set(CONFIG_TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
set(REQUIRE_FMT ${fmt_FOUND})
set(CONFIG_TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
configure_package_config_file(
${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
INSTALL_DESTINATION lib/cmake/cifpp
PATH_VARS CIFPP_DATA_DIR)
${CONFIG_TEMPLATE_FILE}
${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
PATH_VARS CIFPP_DATA_DIR
)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
DESTINATION lib/cmake/cifpp)
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
COMPONENT Devel
)
set_target_properties(
cifpp
PROPERTIES VERSION ${PROJECT_VERSION}
set_target_properties(cifpp PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set_property(
TARGET cifpp
APPEND
PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
set_property(TARGET cifpp APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
COMPATIBILITY AnyNewerVersion
)
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_subdirectory(test)
if(BUILD_TESTING)
# We're using the older version 2 of Catch2
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.13.9
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CIFPP_tests
unit-v2
unit-3d
format
model
rename-compound
sugar
spinner
)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/test/${CIFPP_TEST}.cpp")
add_executable(${CIFPP_TEST} ${CIFPP_TEST_SOURCE} "${CMAKE_CURRENT_SOURCE_DIR}/test/test-main.cpp")
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE ${EIGEN_INCLUDE_DIR})
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}/test)
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR}/test)
endforeach()
endif()
# Optionally install the update scripts for CCD and dictionary files
if(CIFPP_INSTALL_UPDATE_SCRIPT)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tools/update-libcifpp-data.in
update-libcifpp-data @ONLY)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
${CMAKE_SYSTEM_NAME} STREQUAL "GNU" OR
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/update-libcifpp-data
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cron.weekly
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
WORLD_READ)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
set(CIFPP_CRON_DIR "/etc/cron.weekly" CACHE PATH "The cron directory, for the update script")
else()
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly" CACHE PATH "The cron directory, for the update script")
endif()
elseif(UNIX) # assume all others are like FreeBSD...
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/periodic/weekly" CACHE PATH "The cron directory, for the update script")
else()
message(FATAL_ERROR "cifpp: Don't know where to install the update script")
message(FATAL_ERROR "Don't know where to install the update script")
endif()
configure_file(${PROJECT_SOURCE_DIR}/tools/update-libcifpp-data.in update-libcifpp-data @ONLY)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/update-libcifpp-data
DESTINATION ${CIFPP_CRON_DIR}
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
)
install(DIRECTORY DESTINATION ${CIFPP_CACHE_DIR})
# a config file, to make it complete
# install(DIRECTORY DESTINATION "${CMAKE_INSTALL_LOCALSTATEDIR}/libcifpp")
if(NOT EXISTS "${CMAKE_INSTALL_SYSCONFDIR}/libcifpp.conf")
file(
WRITE ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
[[# Uncomment the next line to enable automatic updates
if(NOT EXISTS "${CIFPP_ETC_DIR}/libcifpp.conf")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf [[# Uncomment the next line to enable automatic updates
# update=true
]])
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
install(
CODE "message(\"cifpp: A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf DESTINATION "${CIFPP_ETC_DIR}")
install(CODE "message(\"A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")")
install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/libcifpp/cache-update.d)
install(DIRECTORY DESTINATION "${CIFPP_ETC_DIR}/libcifpp/cache-update.d")
endif()
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
@@ -571,3 +601,13 @@ endif()
if(BUILD_DOCUMENTATION)
add_subdirectory(docs)
endif()
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_SOURCE_TGZ ON)
set(CPACK_SOURCE_TBZ2 OFF)
set(CPACK_SOURCE_TXZ OFF)
set(CPACK_SOURCE_TZ OFF)
set(CPACK_SOURCE_IGNORE_FILES "/data/components.cif;/build;/.vscode;/.git")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME})
include(CPack)

168
README.md
View File

@@ -1,38 +1,14 @@
[![github CI](https://github.com/pdb-redo/libcifpp/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/pdb-redo/libcifpp/actions)
[![GitHub License](https://img.shields.io/github/license/pdb-redo/libcifpp)](https://github.com/pdb-redo/libcifpp/LICENSE)
# libcifpp
As the name implies, this library was originally written to work with mmCIF files
using C++ as programming language. The design of this library leanes heavily on
the structure of CIF files. These files can be thought of as a text dump of a
relational databank with, often but not always, a very strict schema describing
the data. These schema's are called dictionaries.
Using information from the content of a mmCIF file and an optional schema,
libcifpp allows you to access the data in the file as a collection of datablock
each containing a collection of categories with rows of data. The categories can
be searched for data using queries written in regular C++ syntax. When a dictionary
was specified, inserted data is checked for validity. Likewise removal of data
may result in cascaded removal of linked data in other categories using
parent/child relationship information.
Since there were still many programs using the legacy PDB format at the time
development started, a layer was added that converts data to and from PDB format
into mmCIF format. This means you can manipulate PDB files as if they were
normal mmCIF files.
Apart from this basic functionality, libcifpp also offers code to help with
symmetry calculations, 3d manipulations and obtaining information from the CCD
[Chemical Component Dictionary](https://www.wwpdb.org/data/ccd).
This library contains code to work with mmCIF and legacy PDB files.
## Documentation
The documentation can be found at [github.io](https://pdb-redo.github.io/libcifpp/)
The documentation can be found at https://www.hekkelman.com/libcifpp-doc/
## Synopsis
```cpp
```c++
// A simple program counting residues with an OXT atom
#include <filesystem>
@@ -52,7 +28,7 @@ int main(int argc, char *argv[])
if (file.empty())
{
std::cerr << "Empty file\n";
std::cerr << "Empty file" << std::endl;
exit(1);
}
@@ -66,8 +42,8 @@ int main(int argc, char *argv[])
auto n = atom_site.count(cif::key("label_atom_id") == "OXT");
std::cout << "File contains " << atom_site.size() << " atoms of which "
<< n << (n == 1 ? " is" : " are") << " OXT\n"
<< "residues with an OXT are:\n";
<< n << (n == 1 ? " is" : " are") << " OXT" << std::endl
<< "residues with an OXT are:" << std::endl;
// Loop over all atoms with atom-id "OXT" and print out some info.
// That info is extracted using structured binding in C++
@@ -76,7 +52,7 @@ int main(int argc, char *argv[])
cif::key("label_atom_id") == "OXT",
"label_asym_id", "label_comp_id", "label_seq_id"))
{
std::cout << asym << ' ' << comp << ' ' << seqnr << '\n';
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
}
return 0;
@@ -88,134 +64,54 @@ int main(int argc, char *argv[])
You might be able to use libcifpp from a package manager used by your
OS distribution. But most likely this package will be out-of-date.
Therefore it is recommended to build *libcifpp* from code. It is not
hard to do. But it is recommended to read the following instructions
carefully.
hard to do.
### Requirements
The code for this library was written in C++17. You therefore need a
recent compiler to build it. For the development gcc >= 9.4 and clang >= 9.0
recent compiler to build it. For the development gcc 9.4 and clang 9.0
have been used as well as MSVC version 2019.
The other requirement you really need to have installed on your computer
is a version of [CMake](https://cmake.org). For now the minimum version
is 3.16 but that may soon change into a higher version. You should also
install the gui version of CMake to set build options easily, on Debian
I prefer to use the curses version installed with `cmake-curses-gui`.
It is very useful to have [mrc](https://github.com/mhekkel/mrc) available.
However, this is only an option if you use Windows or an operating system
using the ELF executable format (i.e. Linux or FreeBSD). MRC is a resource
compiler that allows including data files into the executable making them
easier to install.
Other libraries you might want to install beforehand are:
Other requirements are:
- [cmake](https://cmake.org) A build tool.
- [mrc](https://github.com/mhekkel/mrc), a resource compiler that
allows including data files into the executable making them easier to
install. Strictly speaking this is optional, but at the expense of
functionality.
- [libeigen](https://eigen.tuxfamily.org/index.php?title=Main_Page), a
library to do amongst others matrix calculations. This usually can be
installed using your package manager, in Debian/Ubuntu it is called
`libeigen3-dev`
- [zlib](https://github.com/madler/zlib), the development version of this
library. On Debian/Ubuntu this is the package `zlib1g-dev`.
- [pcre2](https://www.pcre.org/), the Perl Compatible Regular Expression
library. On Debian/Ubuntu this is the package `libpcre2-dev`.
- [boost](https://www.boost.org).
The Boost libraries are only needed in case you want to build the test
code or if you are using GCC. That last condition is due to a long
standing bug in the implementation of std::regex. It simply crashes
on the regular expressions used in the mmcif_pdbx dictionary and so
we use the boost regex implementation instead.
### Building
First you need to download the code:
Building the code is as simple as typing:
```console
git clone https://github.com/PDB-REDO/libcifpp.git
git clone https://github.com/PDB-REDO/libcifpp.git --recurse-submodules
cd libcifpp
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build
```
You should start by considering where to install libcifpp. If you have
sufficient permissions on your computer you perhaps should use the
default but libcifpp can be configured to be installed anywhere
including e.g. *$HOME/.local*.
Next step is to configure, for this use the CMake gui application. If you
installed the curses version of cmake you can type `ccmake`. On Windows
you can use `cmake-gui.exe`.
To install in the default location:
```console
ccmake -S . -B build
```
To install elsewhere, e.g. *$HOME/.local*:
```console
ccmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local
```
In the cmake window, start the configure command (use button or press 'c').
After the first configure step you will see a list of settable options.
Alter these to match your preferences. Most options are self explaining
and contain a description. Some may need a bit more explanation:
- CIFPP_DATA_DIR, this directory will be used to store initial versions
of the mmcif_pdbx dictionary as well as the optional CCD file.
- CIFPP_DOWNLOAD_CCD
The CCD file is huge and perhaps you think you don't
need it. In that case you can leave this OFF. But that will limit the
use cases.
- CIFPP_INSTALL_UPDATE_SCRIPT
The files in CIFPP_DATA_DIR are quickly becoming out of date. On
FreeBSD and Linux you can install a script that updates these files
on a weekly basis.
- CIFPP_CRON_DIR
The directory where the update script is to be installed.
- CIFPP_ETC_DIR
The update script will only work if the file called *libcifpp.conf*
in this *etc* directory will contain an uncommented line with
```console
update=true
```
- CIFPP_CACHE_DIR
When you installed and enabled the update script, new files are
written to this directory.
- CIFPP_RECREATE_SYMOP_DATA
If you had CCP4 sourced into your environment, this option allows
you to recreate the symop data file.
- BUILD_FOR_CCP4
Build a special version of libcifpp to be installed in the CCP4
environment.
After setting these options you can run the configure step again and
then use generate to create the makefiles.
Building and installing is then as simple as:
```console
cmake --build build
cmake --install build
```
If this fails due to lack of permissions, you can try:
```console
sudo cmake --install build
```
This checks out the source code from github, creates a new directory
where cmake stores its files. Run a configure, build the code and then
it installs the library and auxiliary files.
Tests are created by default, and to test the code you can run:
```console
ctest --test-dir build
cmake --build build
ctest --test-dir build
```

View File

@@ -1,98 +1,3 @@
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
using the correct mapping between atom_site and residues.
- _atom_site.auth_alt_id does not exist, it should be
_atom_site.pdbx_auth_alt_id of course.
- Added a more lightweight fixup for mmcif_pdbx files
that lack certain categories.
Version 8.0.1
- Fix cif::mm::structure::cleanup_empty_categories, removed too much
- Add default value for B_iso_or_equiv in residue::create_new_atom
- Reconstruct some branch records in bare pdbx files
- Fix parsing PDB files (bug due to missing validator in dest. cat.)
- Do not fail conversion of PDB files when compound info is missing
Version 8.0.0
- A dictionary is for a datablock and a file can have
datablocks with differing dictionaries.
Version 7.0.10
- Deal with missing _entity.type in reconstructing mmCIF files
- Replace code creating quaternions from rotation matrices
that might sometimes give incorrect results. Or at least,
the test code failed on this particular kind of code. Sometimes.
- Fix reconstruction to build pdbx_nonpoly_scheme
Version 7.0.9
- Using cif::file::load_dictionary it is now possible to
load a dictionary along with its extensions in one go.
E.g. file.load_dictionary("mmcif_pdbx;dssp-extension")
- Fix in compound factory to avoid errors with lower case
compound id's
- Fix sac_parser's index to be case insensitive
Version 7.0.8
- Fix PDB Remark 3 parser
- Added three way comparison for point
Version 7.0.7
- Set CIFPP_DATA_DIR on target cifpp for use in projects that include
libcifpp directly
Version 7.0.6
- Fix linking to std::atomic
Version 7.0.5
- Fix case where category index was not updated for updated value
Version 7.0.4
- Do not install headers and library in case we're not the top project
Version 7.0.3
- Fix installation, write exports.hpp again
Version 7.0.2
- Fix in testing error_code results.
Version 7.0.1
- Various reconstruction fixes
- category order in output fixed
- better implementation of constructors for file, datablock and category
- small optimisation in iterator
Version 7.0.0
- Renaming many methods and parameters to be more
consistent with the mmCIF dictionaries.
(Most notably, item used to be called column or
tag sometimes).
- validation_error is now a std::system_error error
value. The exception is gone.
- Added repairSequenceInfo to repair invalid files
Version 6.1.0
- Add formula weight to entity in pdb2cif
- Change order of categories inside a datablock to match order in file
- Change default order to write out categories in a file based on
parent/child relationship
- Added validate_pdbx and recover_pdbx
- Fixed a serious bug in category_index when moving categories
Version 6.0.0
- Drop the use of CCP4's monomer library for compound information

View File

@@ -0,0 +1,75 @@
# Simplistic reimplementation of https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake
if(TARGET std::filesystem)
return()
endif()
cmake_minimum_required(VERSION 3.10)
include(CMakePushCheckState)
include(CheckIncludeFileCXX)
include(CheckCXXSourceCompiles)
cmake_push_check_state()
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
set(code [[
#include <cstdlib>
#include <filesystem>
int main() {
auto cwd = std::filesystem::current_path();
return EXIT_SUCCESS;
}
]])
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 8.4.0)
# >> https://stackoverflow.com/questions/63902528/program-crashes-when-filesystempath-is-destroyed
set(CXX_FILESYSTEM_NO_LINK_NEEDED 0)
else()
# Check a simple filesystem program without any linker flags
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
endif()
if(CXX_FILESYSTEM_NO_LINK_NEEDED)
set(_found 1)
else()
set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})
# Add the libstdc++ flag
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
set(_found ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)
# Try the libc++ flag
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
set(_found ${CXX_FILESYSTEM_CPPFS_NEEDED})
endif()
endif()
if(_found)
add_library(std::filesystem INTERFACE IMPORTED)
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17)
if(CXX_FILESYSTEM_NO_LINK_NEEDED)
# Nothing to add...
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
set_target_properties(std::filesystem PROPERTIES IMPORTED_LIBNAME stdc++fs)
set(STDCPPFS_LIBRARY stdc++fs)
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
set_target_properties(std::filesystem PROPERTIES IMPORTED_LIBNAME c++fs)
set(STDCPPFS_LIBRARY c++fs)
endif()
endif()
cmake_pop_check_state()
set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
mark_as_advanced(Filesystem_FOUND)
if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
message(FATAL_ERROR "Cannot run simple program using std::filesystem")
endif()

View File

@@ -1,12 +0,0 @@
# 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
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)
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

@@ -2,14 +2,11 @@
include("${CMAKE_CURRENT_LIST_DIR}/cifpp-targets.cmake")
set_and_check(CIFPP_SHARE_DIR "@PACKAGE_CIFPP_DATA_DIR@")
# set_and_check(CIFPP_SHARE_DIR "@PACKAGE_CIFPP_DATA_DIR@")
include(CMakeFindDependencyMacro)
find_dependency(Threads)
find_dependency(ZLIB REQUIRED)
if(@REQUIRE_FMT@)
find_dependency(fmt REQUIRED)
endif()
check_required_components(cifpp)

View File

@@ -1,17 +0,0 @@
#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;
}

18
cmake/test-rx.cpp Normal file
View File

@@ -0,0 +1,18 @@
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
#include <iostream>
#include <regex>
int main()
{
std::string s(100'000, '*');
std::smatch m;
std::regex r("^(.*?)$");
std::regex_search(s, m, r);
std::cout << s.substr(0, 10) << '\n';
std::cout << m.str(1).substr(0, 10) << '\n';
return 0;
}

View File

@@ -2712,96 +2712,4 @@ _pdbx_chem_comp_audit.processing_site
HIS "Create component" 1999-07-08 EBI
HIS "Modify descriptor" 2011-06-04 RCSB
#
data_HOH
#
_chem_comp.id HOH
_chem_comp.name WATER
_chem_comp.type NON-POLYMER
_chem_comp.pdbx_type HETAS
_chem_comp.formula "H2 O"
_chem_comp.mon_nstd_parent_comp_id ?
_chem_comp.pdbx_synonyms ?
_chem_comp.pdbx_formal_charge 0
_chem_comp.pdbx_initial_date 1999-07-08
_chem_comp.pdbx_modified_date 2011-06-04
_chem_comp.pdbx_ambiguous_flag N
_chem_comp.pdbx_release_status REL
_chem_comp.pdbx_replaced_by ?
_chem_comp.pdbx_replaces MTO
_chem_comp.formula_weight 18.015
_chem_comp.one_letter_code ?
_chem_comp.three_letter_code HOH
_chem_comp.pdbx_model_coordinates_details ?
_chem_comp.pdbx_model_coordinates_missing_flag N
_chem_comp.pdbx_ideal_coordinates_details ?
_chem_comp.pdbx_ideal_coordinates_missing_flag N
_chem_comp.pdbx_model_coordinates_db_code 1NHE
_chem_comp.pdbx_subcomponent_list ?
_chem_comp.pdbx_processing_site RCSB
# #
loop_
_chem_comp_atom.comp_id
_chem_comp_atom.atom_id
_chem_comp_atom.alt_atom_id
_chem_comp_atom.type_symbol
_chem_comp_atom.charge
_chem_comp_atom.pdbx_align
_chem_comp_atom.pdbx_aromatic_flag
_chem_comp_atom.pdbx_leaving_atom_flag
_chem_comp_atom.pdbx_stereo_config
_chem_comp_atom.model_Cartn_x
_chem_comp_atom.model_Cartn_y
_chem_comp_atom.model_Cartn_z
_chem_comp_atom.pdbx_model_Cartn_x_ideal
_chem_comp_atom.pdbx_model_Cartn_y_ideal
_chem_comp_atom.pdbx_model_Cartn_z_ideal
_chem_comp_atom.pdbx_component_atom_id
_chem_comp_atom.pdbx_component_comp_id
_chem_comp_atom.pdbx_ordinal
HOH O O O 0 1 N N N -23.107 18.401 -21.626 -0.064 0.000 0.000 O HOH 1
HOH H1 1H H 0 1 N N N -22.157 18.401 -21.626 0.512 0.000 -0.776 H1 HOH 2
HOH H2 2H H 0 1 N N N -23.424 18.401 -20.730 0.512 0.000 0.776 H2 HOH 3
# #
loop_
_chem_comp_bond.comp_id
_chem_comp_bond.atom_id_1
_chem_comp_bond.atom_id_2
_chem_comp_bond.value_order
_chem_comp_bond.pdbx_aromatic_flag
_chem_comp_bond.pdbx_stereo_config
_chem_comp_bond.pdbx_ordinal
HOH O H1 SING N N 1
HOH O H2 SING N N 2
# #
loop_
_pdbx_chem_comp_descriptor.comp_id
_pdbx_chem_comp_descriptor.type
_pdbx_chem_comp_descriptor.program
_pdbx_chem_comp_descriptor.program_version
_pdbx_chem_comp_descriptor.descriptor
HOH SMILES ACDLabs 10.04 O
HOH SMILES_CANONICAL CACTVS 3.341 O
HOH SMILES CACTVS 3.341 O
HOH SMILES_CANONICAL "OpenEye OEToolkits" 1.5.0 O
HOH SMILES "OpenEye OEToolkits" 1.5.0 O
HOH InChI InChI 1.03 InChI=1S/H2O/h1H2
HOH InChIKey InChI 1.03 XLYOFNOQVPJJNP-UHFFFAOYSA-N
# #
loop_
_pdbx_chem_comp_identifier.comp_id
_pdbx_chem_comp_identifier.type
_pdbx_chem_comp_identifier.program
_pdbx_chem_comp_identifier.program_version
_pdbx_chem_comp_identifier.identifier
HOH "SYSTEMATIC NAME" ACDLabs 10.04 water
HOH "SYSTEMATIC NAME" "OpenEye OEToolkits" 1.5.0 oxidane
# #
loop_
_pdbx_chem_comp_audit.comp_id
_pdbx_chem_comp_audit.action_type
_pdbx_chem_comp_audit.date
_pdbx_chem_comp_audit.processing_site
HOH "Create component" 1999-07-08 RCSB
HOH "Modify descriptor" 2011-06-04 RCSB
##

View File

@@ -217,7 +217,7 @@ A simple case:
#include <cif++.hpp>
cif::file f("1cbs.cif.gz");
f.load_dictionary("mmcif_pdbx.dic");
f.load_dictionary("mmcif_pdbx");
if (not f.is_valid())
std::cout << "This file is not valid\n";

View File

@@ -27,7 +27,7 @@ Using *libcifpp* is easy, if you are familiar with modern C++:
.. literalinclude:: ../README.md
:language: c++
:start-after: ```cpp
:start-after: ```c++
:end-before: ```
.. toctree::

View File

@@ -18,7 +18,7 @@ Loading Resources
No matter where the resource is located, you should always use the single libcifpp API call :cpp:func:`cif::load_resource` to load them. This function returns a *std::istream* wrapped inside a *std::unique_ptr*.
The order in which resources are searched for is:
The order in which resources are search for is:
* Use the resource that was defined by calling :cpp:func:`cif::add_file_resource`
for this name.

View File

@@ -13,7 +13,7 @@ int main(int argc, char *argv[])
exit(1);
}
cif::file file(argv[1]);
cif::file file = cif::pdb::read(argv[1]);
if (file.empty())
{

View File

@@ -200,7 +200,7 @@ enum class radius_type
};
/// @brief The number of radii per element which can be requested from atom_type_info
constexpr std::size_t kRadiusTypeCount = static_cast<std::size_t>(radius_type::type_count);
constexpr size_t kRadiusTypeCount = static_cast<size_t>(radius_type::type_count);
/// An enum used to select either the effective or the crystal radius of an ion.
/// See explanation on Wikipedia: https://en.wikipedia.org/wiki/Ionic_radius
@@ -276,7 +276,7 @@ class atom_type_traits
{
if (type >= radius_type::type_count)
throw std::invalid_argument("invalid radius requested");
return m_info->radii[static_cast<std::size_t>(type)] / 100.f;
return m_info->radii[static_cast<size_t>(type)] / 100.f;
}
/// \brief Return the radius for a charged version of this atom in a solid crystal

View File

@@ -31,30 +31,26 @@
#include "cif++/condition.hpp"
#include "cif++/iterator.hpp"
#include "cif++/row.hpp"
#include "cif++/validate.hpp"
#include "cif++/text.hpp"
#include <array>
/** \file category.hpp
* Documentation for the cif::category class
*
* The category class should meet the requirements of Container and
* SequenceContainer.
*
* TODO: implement all of:
* https://en.cppreference.com/w/cpp/named_req/Container
* https://en.cppreference.com/w/cpp/named_req/SequenceContainer
* and more?
*/
* Documentation for the cif::category class
*
* The category class should meet the requirements of Container and
* SequenceContainer.
*
* TODO: implement all of:
* https://en.cppreference.com/w/cpp/named_req/Container
* https://en.cppreference.com/w/cpp/named_req/SequenceContainer
* and more?
*/
namespace cif
{
class validator;
struct category_validator;
struct item_validator;
struct link_validator;
// --------------------------------------------------------------------
// special exceptions
@@ -65,43 +61,23 @@ struct link_validator;
class duplicate_key_error : public std::runtime_error
{
public:
/**
* @brief Construct a new duplicate key error object
*/
/**
* @brief Construct a new duplicate key error object
*/
duplicate_key_error(const std::string &msg)
: std::runtime_error(msg)
{
}
};
/// @brief A missing_key_error is thrown when an attempt is made
/// to create an index when one of the key items is missing.
class missing_key_error : public std::runtime_error
{
public:
/**
* @brief Construct a new duplicate key error object
*/
missing_key_error(const std::string &msg, const std::string &key)
: std::runtime_error(msg)
, m_key(key)
{
}
const std::string &get_key() const noexcept { return m_key; }
private:
std::string m_key;
};
/// @brief A multiple_results_error is throw when you request a single
/// row using a query but the query contains more than exactly one row.
class multiple_results_error : public std::runtime_error
{
public:
/**
* @brief Construct a new multiple results error object
*/
/**
* @brief Construct a new multiple results error object
*/
multiple_results_error()
: std::runtime_error("query should have returned exactly one row")
{
@@ -145,49 +121,23 @@ class category
/// \endcond
category() = default; ///< Default constructor
category(std::string_view name); ///< Constructor taking a \a name
/// @brief Constructor creating a category named @a name and filled with @a rows
/// @param name Name for the new category
/// @param rows The data stored in the category
category(std::string_view name, row_initializer &&rows)
: category(name)
{
emplace(std::forward<row_initializer>(rows));
}
category(const category &rhs); ///< Copy constructor
category(category &&rhs) noexcept ///< Move constructor
{
swap(*this, rhs);
}
category &operator=(category rhs) ///< assignement operator
{
swap(*this, rhs);
return *this;
}
category() = default; ///< Default constructor
category(std::string_view name); ///< Constructor taking a \a name
category(const category &rhs); ///< Copy constructor
category(category &&rhs); ///< Move constructor
category &operator=(const category &rhs); ///< Copy assignement operator
category &operator=(category &&rhs); ///< Move assignement operator
/// @brief Destructor
/// @note Please note that the destructor is not virtual. It is assumed that
/// you will not derive from this class.
~category();
friend void swap(category &a, category &b) noexcept;
// --------------------------------------------------------------------
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
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")]] std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key items.
std::set<uint16_t> key_item_indices() const; ///< Returns a set of indices for the key items.
iset key_fields() const; ///< Returns the cif::iset of key field names. Retrieved from the @ref category_validator for this category
std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key fields.
/// @brief Set the validator for this category to @a v
/// @param v The category_validator to assign. A nullptr value is allowed.
@@ -196,7 +146,7 @@ class category
/// @brief Update the links in this category
/// @param db The enclosing @ref datablock
void update_links(const datablock &db);
void update_links(datablock &db);
/// @brief Return the global @ref validator for the data
/// @return The @ref validator or nullptr if not assigned
@@ -212,7 +162,7 @@ class category
/// @brief Validate links, that means, values in this category should have an
/// accompanying value in parent categories.
///
///
/// @note
/// The code makes one exception when validating missing links and that's between
/// *atom_site* and a parent *pdbx_poly_seq_scheme* or *entity_poly_seq*.
@@ -223,11 +173,6 @@ class category
/// @return Returns true is all validations pass
bool validate_links() const;
/**
* @brief Strip removes items from this category that are invalid according to the assigned validator
*/
void strip();
/// @brief Equality operator, returns true if @a rhs is equal to this
/// @param rhs The object to compare with
/// @return True if the data contained is equal
@@ -312,15 +257,15 @@ class category
}
/// Return a count of the rows in this container
std::size_t size() const
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
size_t max_size() const
{
return std::numeric_limits<std::size_t>::max(); // this is a bit optimistic, I guess
return std::numeric_limits<size_t>::max(); // this is a bit optimistic, I guess
}
/// Return true if the category is empty
@@ -332,24 +277,16 @@ class category
// --------------------------------------------------------------------
// A category can have a key, as defined by the validator/dictionary
/// @brief The type of an element of the key_type
struct key_element_type
{
std::string name; ///< Name of the item
std::string value; ///< Value to be found
bool may_be_null = false; ///< If true, value should be same or empty
};
/// @brief The key type
using key_type = std::vector<key_element_type>;
using key_type = row_initializer;
/// @brief Return a 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
/// @param key The value for the key, fields specified in the dictionary should have a value
/// @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
/// @param key The value for the key, items specified in the dictionary should have a value
/// @param key The value for the key, fields 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
{
@@ -364,15 +301,15 @@ class category
/// @code{.cpp}
/// for (const auto &[name, value] : cat.rows<std::string,int>("item_name", "item_value"))
/// std::cout << name << ": " << value << '\n';
/// @endcode
/// @endcode
///
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
template <typename... Ts, typename... Ns>
iterator_proxy<const category, 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");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<const category, Ts...>(*this, begin(), { names... });
}
@@ -383,19 +320,19 @@ class category
/// for (const auto &[name, value] : cat.rows<std::string,int>("item_name", "item_value"))
/// std::cout << name << ": " << value << '\n';
///
/// // or in case we only need one item:
/// // or in case we only need one column:
///
/// for (int id : cat.rows<int>("id"))
/// std::cout << id << '\n';
/// @endcode
/// @endcode
///
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
template <typename... Ts, typename... Ns>
iterator_proxy<category, 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");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<category, Ts...>(*this, begin(), { names... });
}
@@ -406,7 +343,7 @@ class category
/// @code{.cpp}
/// for (row_handle rh : cat.find(cif::key("first_name") == "John" and cif::key("last_name") == "Doe"))
/// .. // do something with rh
/// @endcode
/// @endcode
///
/// @param cond The condition for the query
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
@@ -460,17 +397,17 @@ class category
/// @code{.cpp}
/// for (const auto &[name, value] : cat.find<std::string,int>(cif::key("item_value") > 10, "item_name", "item_value"))
/// std::cout << name << ": " << value << '\n';
/// @endcode
/// @endcode
///
/// @param cond The condition for the query
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @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)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
@@ -478,14 +415,14 @@ class category
/// iterator can be used in a structured binding context.
///
/// @param cond The condition for the query
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @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
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
@@ -494,14 +431,14 @@ class category
///
/// @param pos Iterator pointing to the location where to start
/// @param cond The condition for the query
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @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)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
@@ -510,14 +447,14 @@ class category
///
/// @param pos Iterator pointing to the location where to start
/// @param cond The condition for the query
/// @tparam Ts The types for the items requested
/// @param names The names for the items requested
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @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
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of item names should be equal to the number of types to return");
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
@@ -572,30 +509,30 @@ class category
return *h.begin();
}
/// @brief Return value for the item named @a item for the single row that
/// @brief Return value for the column named @a column for the single row that
/// matches @a cond. Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The type to use for the result
/// @param cond The condition to search for
/// @param item The name of the item to return the value for
/// @param column The name of the column to return the value for
/// @return The value found
template <typename T>
T find1(condition &&cond, std::string_view item) const
T find1(condition &&cond, const char *column) const
{
return find1<T>(cbegin(), std::move(cond), item);
return find1<T>(cbegin(), std::move(cond), column);
}
/// @brief Return value for the item named @a item for the single row that
/// @brief Return value for the column named @a column for the single row that
/// matches @a cond when starting to search at @a pos.
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The type to use for the result
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param item The name of the item to return the value for
/// @param column The name of the column to return the value for
/// @return The value found
template <typename T, std::enable_if_t<not is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, std::string_view item) const
T find1(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<T>(pos, std::move(cond), item);
auto h = find<T>(pos, std::move(cond), column);
if (h.size() != 1)
throw multiple_results_error();
@@ -603,18 +540,18 @@ class category
return *h.begin();
}
/// @brief Return a value of type std::optional<T> for the item named @a item for the single row that
/// @brief Return a value of type std::optional<T> for the column named @a column for the single row that
/// matches @a cond when starting to search at @a pos.
/// If the row was not found, an empty value is returned.
/// @tparam The type to use for the result
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param item The name of the item to return the value for
/// @param column The name of the column 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>
T find1(const_iterator pos, condition &&cond, std::string_view item) const
T find1(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<typename T::value_type>(pos, std::move(cond), item);
auto h = find<typename T::value_type>(pos, std::move(cond), column);
if (h.size() > 1)
throw multiple_results_error();
@@ -625,34 +562,34 @@ class category
return *h.begin();
}
/// @brief Return a std::tuple for the values for the items named in @a items
/// @brief Return a std::tuple for the values for the columns named in @a columns
/// for the single row that matches @a cond
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The types to use for the resulting tuple
/// @param cond The condition to search for
/// @param items The names of the items to return the value for
/// @param columns The names of the columns to return the value for
/// @return The values found as a single tuple of type std::tuple<Ts...>
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(condition &&cond, Cs... items) const
std::tuple<Ts...> find1(condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The item names should be const char");
return find1<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(items)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find1<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
}
/// @brief Return a std::tuple for the values for the items named in @a items
/// @brief Return a std::tuple for the values for the columns named in @a columns
/// for the single row that matches @a cond when starting to search at @a pos
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The types to use for the resulting tuple
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param items The names of the items to return the value for
/// @param columns The names of the columns to return the value for
/// @return The values found as a single tuple of type std::tuple<Ts...>
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... items) const
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(items)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(columns)...);
if (h.size() != 1)
throw multiple_results_error();
@@ -701,74 +638,74 @@ class category
return h.empty() ? row_handle{} : *h.begin();
}
/// @brief Return the value for item @a item for the first row that matches condition @a cond
/// @brief Return the value for column @a column for the first row that matches condition @a cond
/// @tparam The type of the value to return
/// @param cond The condition to search for
/// @param item The item for which the value should be returned
/// @param column The column for which the value should be returned
/// @return The value found or a default constructed value if not found
template <typename T>
T find_first(condition &&cond, std::string_view item) const
T find_first(condition &&cond, const char *column) const
{
return find_first<T>(cbegin(), std::move(cond), item);
return find_first<T>(cbegin(), std::move(cond), column);
}
/// @brief Return the value for item @a item for the first row that matches condition @a cond
/// @brief Return the value for column @a column for the first row that matches condition @a cond
/// when starting the search at @a pos
/// @tparam The type of the value to return
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @param item The item for which the value should be returned
/// @param column The column for which the value should be returned
/// @return The value found or a default constructed value if not found
template <typename T>
T find_first(const_iterator pos, condition &&cond, std::string_view item) const
T find_first(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<T>(pos, std::move(cond), item);
auto h = find<T>(pos, std::move(cond), column);
return h.empty() ? T{} : *h.begin();
}
/// @brief Return a tuple containing the values for the items @a items for the first row that matches condition @a cond
/// @brief Return a tuple containing the values for the columns @a columns for the first row that matches condition @a cond
/// @tparam The types of the values to return
/// @param cond The condition to search for
/// @param items The items for which the values should be returned
/// @param columns The columns for which the values should be returned
/// @return The values found or default constructed values if not found
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(condition &&cond, Cs... items) const
std::tuple<Ts...> find_first(condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The item names should be const char");
return find_first<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(items)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find_first<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
}
/// @brief Return a tuple containing the values for the items @a items for the first row that matches condition @a cond
/// @brief Return a tuple containing the values for the columns @a columns for the first row that matches condition @a cond
/// when starting the search at @a pos
/// @tparam The types of the values to return
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @param items The items for which the values should be returned
/// @param columns The columns for which the values should be returned
/// @return The values found or default constructed values if not found
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(const_iterator pos, condition &&cond, Cs... items) const
std::tuple<Ts...> find_first(const_iterator pos, condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of item names should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(items)...);
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::move(cond), std::forward<Cs>(columns)...);
return h.empty() ? std::tuple<Ts...>{} : *h.begin();
}
// --------------------------------------------------------------------
/// @brief Return the maximum value for item @a item for all rows that match condition @a cond
/// @brief Return the maximum value for column @a column for all rows that match condition @a cond
/// @tparam The type of the value to return
/// @param item The item to use for the value
/// @param column The column 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>
T find_max(std::string_view item, condition &&cond) const
T find_max(const char *column, condition &&cond) const
{
T result = std::numeric_limits<T>::min();
for (auto v : find<T>(std::move(cond), item))
for (auto v : find<T>(std::move(cond), column))
{
if (result < v)
result = v;
@@ -777,27 +714,27 @@ class category
return result;
}
/// @brief Return the maximum value for item @a item for all rows
/// @brief Return the maximum value for column @a column for all rows
/// @tparam The type of the value to return
/// @param item The item to use for the value
/// @param column The column 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
T find_max(const char *column) const
{
return find_max<T>(item, all());
return find_max<T>(column, all());
}
/// @brief Return the minimum value for item @a item for all rows that match condition @a cond
/// @brief Return the minimum value for column @a column for all rows that match condition @a cond
/// @tparam The type of the value to return
/// @param item The item to use for the value
/// @param column The column 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
T find_min(const char *column, condition &&cond) const
{
T result = std::numeric_limits<T>::max();
for (auto v : find<T>(std::move(cond), item))
for (auto v : find<T>(std::move(cond), column))
{
if (result > v)
result = v;
@@ -806,28 +743,20 @@ class category
return result;
}
/// @brief Return the maximum value for item @a item for all rows
/// @brief Return the maximum value for column @a column for all rows
/// @tparam The type of the value to return
/// @param item The item to use for the value
/// @param column The column 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
T find_min(const char *column) const
{
return find_min<T>(item, all());
return find_min<T>(column, all());
}
/// @brief Return whether a row exists that matches condition @a cond
/// @param cond The condition to match
/// @return True if a row exists
[[deprecated("Use contains instead")]] bool exists(condition &&cond) const
{
return contains(std::move(cond));
}
/// @brief Return whether a row exists that matches condition @a cond
/// @param cond The condition to match
/// @return True if a row exists
bool contains(condition &&cond) const
bool exists(condition &&cond) const
{
bool result = false;
@@ -858,9 +787,9 @@ class category
/// @brief Return the total number of rows that match condition @a cond
/// @param cond The condition to match
/// @return The count
std::size_t count(condition &&cond) const
size_t count(condition &&cond) const
{
std::size_t result = 0;
size_t result = 0;
if (cond)
{
@@ -917,7 +846,7 @@ class category
// insert_impl(pos, std::move(row));
// }
/// Erase the row pointed to by @a pos and return the iterator to the
/// Erase the row pointed to by @a pos and return the iterator to the
/// row following pos.
iterator erase(iterator pos);
@@ -930,14 +859,14 @@ class category
/// @brief Erase all rows that match condition @a cond
/// @param cond The condition
/// @return The number of rows that have been erased
std::size_t erase(condition &&cond);
size_t erase(condition &&cond);
/// @brief Erase all rows that match condition @a cond calling
/// the visitor function @a visit for each before actually erasing it.
/// @param cond The condition
/// @param visit The visitor function
/// @return The number of rows that have been erased
std::size_t erase(condition &&cond, std::function<void(row_handle)> &&visit);
size_t erase(condition &&cond, std::function<void(row_handle)> &&visit);
/// @brief Emplace the values in @a ri in a new row
/// @param ri An object containing the values to insert
@@ -961,7 +890,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->append(add_column(i->name()), { i->value() });
}
}
catch (...)
@@ -974,12 +903,6 @@ class category
return insert_impl(cend(), r);
}
void emplace(const_iterator b, const_iterator e)
{
while (b != e)
emplace(*b++);
}
/// @brief Completely erase all rows contained in this category
void clear();
@@ -989,6 +912,7 @@ class category
/// result is unique in the context of this category
std::string get_unique_id(std::function<std::string(int)> generator = cif::cif_id_for_number);
/// @brief Generate a new, unique ID based on a string prefix followed by a number
/// @param prefix The string prefix
/// @return a new unique ID
@@ -998,156 +922,98 @@ class category
{ return prefix + std::to_string(nr + 1); });
}
/// @brief Generate a new, unique value for a item named @a item_name
/// @param item_name The name of the item
/// @return a new unique value
std::string get_unique_value(std::string_view item_name);
// --------------------------------------------------------------------
using value_provider_type = std::function<std::string_view(std::string_view)>;
/// \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
/// \brief Update a single column named @a tag in the rows that match \a cond to value \a value
/// making sure the linked categories are updated according to the link.
/// 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,
value_provider_type &&value_provider)
void update_value(condition &&cond, std::string_view tag, std::string_view value)
{
auto rs = find(std::move(cond));
std::vector<row_handle> rows;
std::copy(rs.begin(), rs.end(), std::back_inserter(rows));
update_value(rows, item_name, std::move(value_provider));
update_value(rows, tag, value);
}
/// \brief Update a single item named @a item_name in the rows \a rows
/// to values provided by a callback function \a value_provider
/// \brief Update a single column named @a tag in @a rows to value \a value
/// making sure the linked categories are updated according to the link.
/// 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,
value_provider_type &&value_provider);
/// \brief Update a single item named @a item_name in the rows that match \a cond to value \a value
/// making sure the linked categories are updated according to the link.
/// 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)
{
auto rs = find(std::move(cond));
std::vector<row_handle> rows;
std::copy(rs.begin(), rs.end(), std::back_inserter(rows));
update_value(rows, item_name, value);
}
/// \brief Update a single item named @a item_name in @a rows to value \a value
/// making sure the linked categories are updated according to the link.
/// 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)
{
update_value(rows, item_name, [value](std::string_view)
{ return value; });
}
void update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view 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
uint16_t get_column_ix(std::string_view column_name) const
{
return get_item_ix(column_name);
uint16_t result;
for (result = 0; result < m_columns.size(); ++result)
{
if (iequals(column_name, m_columns[result].m_name))
break;
}
if (VERBOSE > 0 and result == m_columns.size() and m_cat_validator != nullptr) // validate the name, if it is known at all (since it was not found)
{
auto iv = m_cat_validator->get_validator_for_item(column_name);
if (iv == nullptr)
std::cerr << "Invalid name used '" << column_name << "' is not a known column in " + m_name << '\n';
}
return result;
}
/// @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
std::string_view get_column_name(uint16_t ix) const
{
return get_item_name(ix);
if (ix >= m_columns.size())
throw std::out_of_range("column index is out of range");
return m_columns[ix].m_name;
}
/// @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)
/// @brief Make sure a column with name @a column_name is known and return its index number
/// @param column_name The name of the column
/// @return The index number of the column
uint16_t add_column(std::string_view column_name)
{
return add_item(item_name);
}
using namespace std::literals;
/** @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);
}
uint16_t result = get_column_ix(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);
if (result == m_columns.size())
{
const item_validator *item_validator = nullptr;
if (m_cat_validator != nullptr)
{
item_validator = m_cat_validator->get_validator_for_item(column_name);
if (item_validator == nullptr)
m_validator->report_error("tag " + std::string(column_name) + " not allowed in category " + m_name, false);
}
m_columns.emplace_back(column_name, item_validator);
}
return result;
}
/// @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
bool has_column(std::string_view name) const
{
return has_item(name);
return get_column_ix(name) < m_columns.size();
}
/// @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;
/// @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
{
if (ix >= m_items.size())
throw std::out_of_range("item index is out of range");
return m_items[ix].m_name;
}
/// @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
uint16_t add_item(std::string_view item_name);
/** @brief Remove item name @a colum_name
* @param item_name The item to be removed
*/
void remove_item(std::string_view item_name);
/** @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
{
return get_item_ix(name) < m_items.size();
}
/// @brief Return the cif::iset of items in this category
iset get_items() const;
iset get_columns() const;
// --------------------------------------------------------------------
@@ -1163,36 +1029,30 @@ class category
// --------------------------------------------------------------------
/// 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;
/// This function returns effectively the list of fully qualified column
/// names, that is category_name + '.' + column_name for each column
std::vector<std::string> get_tag_order() const;
/// Write the contents of the category to the std::ostream @a os
void write(std::ostream &os) const;
/// @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 @a order as the order of the columns. If @a addMissingColumns is
/// false, columns 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);
/// @param order The order in which the columns should appear
/// @param addMissingColumns When false, empty columns are suppressed from the output
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingColumns = true);
private:
void write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
void write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyColumns) const;
public:
/// friend function to make it possible to do:
/// @code {.cpp}
/// std::cout << my_category;
/// @endcode
/// @endcode
friend std::ostream &operator<<(std::ostream &os, const category &cat)
{
cat.write(os);
@@ -1200,7 +1060,7 @@ 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 column, std::string_view value, bool updateLinked, bool validate = true);
void erase_orphans(condition &&cond, category &parent);
@@ -1237,12 +1097,12 @@ class category
row_handle create_copy(row_handle r);
struct item_entry
struct item_column
{
std::string m_name;
const item_validator *m_validator;
item_entry(std::string_view name, const item_validator *validator)
item_column(std::string_view name, const item_validator *validator)
: m_name(name)
, m_validator(validator)
{
@@ -1257,7 +1117,6 @@ class category
{
}
// TODO: NEED TO FIX THIS!
category *linked;
const link_validator *v;
};
@@ -1273,12 +1132,12 @@ class category
// --------------------------------------------------------------------
void swap_item(uint16_t item_ix, row_handle &a, row_handle &b);
void swap_item(uint16_t column_ix, row_handle &a, row_handle &b);
// --------------------------------------------------------------------
std::string m_name;
std::vector<item_entry> m_items;
std::vector<item_column> m_columns;
const validator *m_validator = nullptr;
const category_validator *m_cat_validator = nullptr;
std::vector<link> m_parent_links, m_child_links;

View File

@@ -138,7 +138,7 @@ struct compound_bond
/// This information is derived from the CDD by default.
///
/// To create compounds, you use the factory method. You can add your own
/// compound definitions by calling the push_dictionary function and
/// compound definitions by calling the addExtraComponents function and
/// pass it a valid CCD formatted file.
class compound
@@ -166,27 +166,17 @@ class compound
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;
/** \brief Return whether this compound has a type of either 'DNA linking' or 'RNA linking' */
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)
private:
friend class compound_factory_impl;
friend class local_compound_factory_impl;
compound(cif::datablock &db);
compound(cif::datablock &db, const std::string &id, const std::string &name, const std::string &type, const std::string &group);
std::string m_id;
std::string m_name;
std::string m_type;
std::string m_group;
std::string m_formula;
char m_one_letter_code = 0;
std::string m_parent_id;
float m_formula_weight = 0;
int m_formal_charge = 0;
std::vector<compound_atom> m_atoms;
@@ -224,78 +214,29 @@ class compound_factory
/// Override any previously loaded dictionary with @a inDictFile
void push_dictionary(const std::filesystem::path &inDictFile);
/** @brief Override any previously loaded dictionary with the data in @a file
*
* @note experimental feature
*
* Load the file @a file as a source for compound information. This may
* be e.g. a regular mmCIF file with extra files containing compound
* information.
*
* Be carefull to remove the block again, best use @ref cif::compound_source
* as a stack based object.
*/
void push_dictionary(const file &file);
/// Remove the last pushed dictionary
void pop_dictionary();
/// 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;
/// 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;
/// Return whether @a res_name is a peptide
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;
/// Return whether @a res_name is one of the standard peptides
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;
/// Return whether @a res_name is a monomer (either base or peptide)
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
{
return is_std_base(res_name) or is_std_peptide(res_name);
}
bool is_water(std::string_view res_name) const
{
return res_name == "HOH" or res_name == "H2O" or res_name == "WAT";
}
/// \brief Create the compound object for \a id
///
/// This will create the compound instance for \a id if it doesn't exist already.
/// The result is owned by this factory and should not be deleted by the user.
/// \param id The compound ID, a three letter code usually
/// \result The compound, or nullptr if it could not be created (missing info)
const compound *create(std::string_view id);
const compound *create(std::string id);
~compound_factory();
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; }
void set_report_missing(bool report)
{
m_report_missing = report;
}
void report_missing_compound(const std::string &compound_id);
private:
compound_factory();
@@ -308,38 +249,6 @@ class compound_factory
static bool s_use_thread_local_instance;
std::shared_ptr<compound_factory_impl> m_impl;
bool m_report_missing = true;
};
// --------------------------------------------------------------------
/**
* @brief Stack based source for compound info.
*
* Use this class to temporarily add a compound source to the
* compound_factory.
*
* @code{.cpp}
* cif::file f("1cbs-with-custom-rea.cif");
* cif::compound_source cs(f);
*
* auto &cf = cif::compound_factory::instance();
* auto rea_compound = cf.create("REA");
* @endcode
*/
class compound_source
{
public:
compound_source(const cif::file &file)
{
cif::compound_factory::instance().push_dictionary(file);
}
~compound_source()
{
cif::compound_factory::instance().pop_dictionary();
}
};
} // namespace cif

View File

@@ -27,10 +27,8 @@
#pragma once
#include "cif++/row.hpp"
#include "cif++/format.hpp"
#include <cassert>
#include <concepts>
#include <functional>
#include <iostream>
#include <regex>
@@ -41,58 +39,58 @@
* query you can use to find rows in a @ref cif::category
*
* Conditions are created as standard C++ expressions. That means
* you can use the standard comparison operators to compare item
* you can use the standard comparison operators to compare field
* contents with a value and boolean operators to chain everything
* together.
*
* To create a query that simply compares one item with one value:
* To create a query that simply compares one field with one value:
*
* @code {.cpp}
* cif::condition c = cif::key("id") == 1;
* @endcode
*
* That will find rows where the ID item contains the number 1. If
*
* That will find rows where the ID field contains the number 1. If
* using cif::key is a bit too much typing, you can also write:
*
*
* @code{.cpp}
* using namespace cif::literals;
*
*
* cif::condition c2 = "id"_key == 1;
* @endcode
*
*
* Now if you want both ID = 1 and ID = 2 in the result:
*
*
* @code{.cpp}
* auto c3 = "id"_key == 1 or "id"_key == 2;
* @endcode
*
* There are some special values you can use. To find rows with item that
*
* There are some special values you can use. To find rows with field that
* do not have a value:
*
*
* @code{.cpp}
* auto c4 = "type"_key == cif::null;
* @endcode
*
* @endcode
*
* Of if it should not be NULL:
*
*
* @code{.cpp}
* auto c5 = "type"_key != cif::null;
* @endcode
*
* @endcode
*
* There's even a way to find all records:
*
*
* @code{.cpp}
* auto c6 = cif::all;
* @endcode
*
* And when you want to search for any item containing the value 'foo':
*
*
* And when you want to search for any column containing the value 'foo':
*
* @code{.cpp}
* auto c7 = cif::any == "foo";
* @endcode
*
* @endcode
*
* All these conditions can be chained together again:
*
*
* @code{.cpp}
* auto c8 = std::move(c3) and std::move(c5);
* @endcode
@@ -106,40 +104,31 @@ namespace cif
/// we declare a function to access its contents
/**
* @brief Get the items that can be used as key in conditions for a category
*
* @param cat The category whose items to return
* @return iset The set of key item names
* @brief Get the fields that can be used as key in conditions for a category
*
* @param cat The category whose fields to return
* @return iset The set of key field names
*/
[[deprecated("use get_category_items instead")]]
iset get_category_fields(const category &cat);
/**
* @brief Get the items that can be used as key in conditions for a category
*
* @param cat The category whose items to return
* @return iset The set of key field names
*/
iset get_category_items(const category &cat);
/**
* @brief Get the item index for item @a col in category @a cat
*
* @brief Get the column index for column @a col in category @a cat
*
* @param cat The category
* @param col The name of the item
* @param col The name of the column
* @return uint16_t The index
*/
uint16_t get_item_ix(const category &cat, std::string_view col);
uint16_t get_column_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*
*
* @brief Return whether the column @a col in category @a cat has a primitive type of *uchar*
*
* @param cat The category
* @param col The item name
* @param col The column name
* @return true If the primitive type is of type *uchar*
* @return false If the primitive type is not of type *uchar*
*/
bool is_item_type_uchar(const category &cat, std::string_view col);
bool is_column_type_uchar(const category &cat, std::string_view col);
// --------------------------------------------------------------------
// some more templates to be able to do querying
@@ -176,13 +165,14 @@ namespace detail
class condition
{
public:
/** @cond */
using condition_impl = detail::condition_impl;
/** @endcond */
/**
* @brief Construct a new, empty condition object
*
*
*/
condition()
: m_impl(nullptr)
@@ -191,7 +181,7 @@ class condition
/**
* @brief Construct a new condition object with implementation @a impl
*
*
* @param impl The implementation to use
*/
explicit condition(condition_impl *impl)
@@ -229,16 +219,16 @@ class condition
/**
* @brief Prepare the condition to be used on category @a c. This will
* take care of setting the correct indices for items e.g.
*
* take care of setting the correct indices for fields e.g.
*
* @param c The category this query should act upon
*/
void prepare(const category &c);
/**
* @brief This operator returns true if the row referenced by @a r is
* @brief This operator returns true if the row referenced by @a r is
* a match for this condition.
*
*
* @param r The reference to a row.
* @return true If there is a match
* @return false If there is no match
@@ -263,7 +253,7 @@ class condition
/**
* @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
* a row reference if there is a single hit, it will be empty otherwise
*/
@@ -292,7 +282,7 @@ class condition
/**
* @brief Operator to use to write out a condition to @a os, for debugging purposes
*
*
* @param os The std::ostream to write to
* @param cond The condition to write
* @return std::ostream& The same as @a os
@@ -315,14 +305,14 @@ 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(const std::string &item_tag)
: m_item_tag(item_tag)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_item_ix(c, m_item_name);
m_item_ix = get_column_ix(c, m_item_tag);
return this;
}
@@ -333,23 +323,23 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_name << " IS NULL";
os << m_item_tag << " IS NULL";
}
std::string m_item_name;
std::string m_item_tag;
uint16_t m_item_ix = 0;
};
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(const std::string &item_tag)
: m_item_tag(item_tag)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_item_ix(c, m_item_name);
m_item_ix = get_column_ix(c, m_item_tag);
return this;
}
@@ -360,18 +350,18 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_name << " IS NOT NULL";
os << m_item_tag << " IS NOT NULL";
}
std::string m_item_name;
std::string m_item_tag;
uint16_t m_item_ix = 0;
};
struct key_equals_condition_impl : public condition_impl
{
key_equals_condition_impl(item &&i)
: m_item_name(i.name())
, m_value(std::forward<item>(i).value())
: m_item_tag(i.name())
, m_value(i.value())
{
}
@@ -384,7 +374,7 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value;
os << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value;
}
virtual std::optional<row_handle> single() const override
@@ -400,13 +390,13 @@ namespace detail
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;
// watch out, both m_item_ix might be the same while tags 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_tag == ri->m_item_tag;
}
return this == rhs;
}
std::string m_item_name;
std::string m_item_tag;
uint16_t m_item_ix = 0;
bool m_icase = false;
std::string m_value;
@@ -416,7 +406,7 @@ namespace detail
struct key_equals_or_empty_condition_impl : public condition_impl
{
key_equals_or_empty_condition_impl(key_equals_condition_impl *equals)
: m_item_name(equals->m_item_name)
: m_item_tag(equals->m_item_tag)
, m_value(equals->m_value)
, m_icase(equals->m_icase)
, m_single_hit(equals->m_single_hit)
@@ -425,8 +415,8 @@ 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);
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
return this;
}
@@ -442,7 +432,7 @@ namespace detail
void str(std::ostream &os) const override
{
os << '(' << m_item_name << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_name << " IS NULL)";
os << '(' << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_tag << " IS NULL)";
}
virtual std::optional<row_handle> single() const override
@@ -458,124 +448,24 @@ namespace detail
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;
// watch out, both m_item_ix might be the same while tags 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_tag == ri->m_item_tag;
}
return this == rhs;
}
std::string m_item_name;
std::string m_item_tag;
uint16_t m_item_ix = 0;
std::string 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;
};
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)
key_compare_condition_impl(const std::string &item_tag, COMP &&comp, const std::string &s)
: m_item_tag(item_tag)
, m_compare(std::move(comp))
, m_str(s)
{
@@ -583,8 +473,8 @@ 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);
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
return this;
}
@@ -595,10 +485,10 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_name << (m_icase ? "^ " : " ") << m_str;
os << m_item_tag << (m_icase ? "^ " : " ") << m_str;
}
std::string m_item_name;
std::string m_item_tag;
uint16_t m_item_ix = 0;
bool m_icase = false;
std::function<bool(row_handle, bool)> m_compare;
@@ -607,8 +497,8 @@ namespace detail
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)
key_matches_condition_impl(const std::string &item_tag, const std::regex &rx)
: m_item_tag(item_tag)
, m_item_ix(0)
, mRx(rx)
{
@@ -616,7 +506,7 @@ namespace detail
condition_impl *prepare(const category &c) override
{
m_item_ix = get_item_ix(c, m_item_name);
m_item_ix = get_column_ix(c, m_item_tag);
return this;
}
@@ -628,10 +518,10 @@ namespace detail
void str(std::ostream &os) const override
{
os << m_item_name << " =~ expression";
os << m_item_tag << " =~ expression";
}
std::string m_item_name;
std::string m_item_tag;
uint16_t m_item_ix;
std::regex mRx;
};
@@ -651,7 +541,7 @@ namespace detail
auto &c = r.get_category();
bool result = false;
for (auto &f : get_category_items(c))
for (auto &f : get_category_fields(c))
{
try
{
@@ -689,7 +579,7 @@ namespace detail
auto &c = r.get_category();
bool result = false;
for (auto &f : get_category_items(c))
for (auto &f : get_category_fields(c))
{
try
{
@@ -752,9 +642,28 @@ namespace detail
delete sub;
}
condition_impl *prepare(const category &c) override;
condition_impl *prepare(const category &c) override
{
for (auto &sub : m_sub)
sub = sub->prepare(c);
return this;
}
bool test(row_handle r) const override;
bool test(row_handle r) const override
{
bool result = true;
for (auto sub : m_sub)
{
if (sub->test(r))
continue;
result = false;
break;
}
return result;
}
void str(std::ostream &os) const override
{
@@ -801,7 +710,6 @@ 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
};
struct or_condition_impl : public condition_impl
@@ -956,40 +864,19 @@ inline condition operator or(condition &&a, condition &&b)
auto ci = static_cast<detail::key_equals_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)
if (ci->m_item_tag == ce->m_item_tag)
return condition(new detail::key_equals_or_empty_condition_impl(ci));
}
if (typeid(*b.m_impl) == typeid(detail::key_equals_condition_impl) and
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
else if (typeid(*b.m_impl) == typeid(detail::key_equals_condition_impl) and
typeid(*a.m_impl) == typeid(detail::key_is_empty_condition_impl))
{
auto ci = static_cast<detail::key_equals_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)
if (ci->m_item_tag == ce->m_item_tag)
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)));
}
@@ -1000,8 +887,8 @@ inline condition operator or(condition &&a, condition &&b)
}
/**
* @brief A helper class to make it possible to search for empty items (NULL)
*
* @brief A helper class to make it possible to search for empty fields (NULL)
*
* @code{.cpp}
* "id"_key == cif::empty_type();
* @endcode
@@ -1013,7 +900,7 @@ struct empty_type
/**
* @brief A helper to make it possible to have conditions like
*
*
* @code{.cpp}
* "id"_key == cif::null;
* @endcode
@@ -1022,58 +909,44 @@ struct empty_type
inline constexpr empty_type null = empty_type();
/**
* @brief Class to use in creating conditions, creates a reference to a item or item
*
* @brief Class to use in creating conditions, creates a reference to a field or column
*
*/
struct key
{
/**
* @brief Construct a new key object using @a item_name as name
*
* @param item_name
* @brief Construct a new key object using @a itemTag as name
*
* @param itemTag
*/
explicit key(const std::string &item_name)
: m_item_name(item_name)
explicit key(const std::string &itemTag)
: m_item_tag(itemTag)
{
}
/**
* @brief Construct a new key object using @a item_name as name
*
* @param item_name
* @brief Construct a new key object using @a itemTag as name
*
* @param itemTag
*/
explicit key(const char *item_name)
: m_item_name(item_name)
{
}
/**
* @brief Construct a new key object using @a item_name as name
*
* @param item_name
*/
explicit key(std::string_view item_name)
: m_item_name(item_name)
explicit key(const char *itemTag)
: m_item_tag(itemTag)
{
}
key(const key &) = delete;
key &operator=(const key &) = delete;
std::string m_item_name; ///< The item name
std::string m_item_tag; ///< The column 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
* @brief Operator to create an equals condition based on a key @a key and a value @a v
*/
template <Numeric T>
template <typename 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)));
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, v }));
}
/**
@@ -1082,19 +955,9 @@ condition operator==(const key &key, const T &v)
inline condition operator==(const key &key, std::string_view value)
{
if (not value.empty())
return condition(new detail::key_equals_condition_impl({ key.m_item_name, value }));
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, value }));
else
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" }));
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
}
/**
@@ -1117,93 +980,61 @@ inline condition operator!=(const key &key, std::string_view value)
/**
* @brief Operator to create a greater than condition based on a key @a key and a value @a v
*/
template <Numeric T>
template <typename T>
condition operator>(const key &key, const T &v)
{
std::ostringstream s;
s << " > " << 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)
{ return r[item_name].compare(v) > 0; },
cif::format(" > {}", v)));
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; },
s.str()));
}
/**
* @brief Operator to create a greater than or equals condition based on a key @a key and a value @a v
*/
template <Numeric T>
template <typename T>
condition operator>=(const key &key, const T &v)
{
std::ostringstream s;
s << " >= " << 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)
{ return r[item_name].compare(v) >= 0; },
cif::format(" >= {}", v)));
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; },
s.str()));
}
/**
* @brief Operator to create a less than condition based on a key @a key and a value @a v
*/
template <Numeric T>
template <typename T>
condition operator<(const key &key, const T &v)
{
std::ostringstream s;
s << " < " << 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)
{ return r[item_name].compare(v) < 0; },
cif::format(" < {}", v)));
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; },
s.str()));
}
/**
* @brief Operator to create a less than or equals condition based on a key @a key and a value @a v
*/
template <Numeric T>
template <typename 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)
{ return r[item_name].compare(v) <= 0; },
cif::format(" <= {}", v)));
}
std::ostringstream s;
s << " <= " << v;
/**
* @brief Operator to create a greater than condition based on a key @a key and a value @a 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)
{ return r[item_name].compare(v, icase) > 0; },
cif::format(" > {}", v)));
}
/**
* @brief Operator to create a greater than or equals condition based on a key @a key and a value @a 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)
{ return r[item_name].compare(v, icase) >= 0; },
cif::format(" >= {}", v)));
}
/**
* @brief Operator to create a less than condition based on a key @a key and a value @a 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)
{ return r[item_name].compare(v, icase) < 0; },
cif::format(" < {}", v)));
}
/**
* @brief Operator to create a less than or equals condition based on a key @a key and a value @a 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)
{ return r[item_name].compare(v, icase) <= 0; },
cif::format(" <= {}", v)));
key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; },
s.str()));
}
/**
@@ -1211,7 +1042,7 @@ inline condition operator<=(const key &key, std::string_view v)
*/
inline condition operator==(const key &key, const std::regex &rx)
{
return condition(new detail::key_matches_condition_impl(key.m_item_name, rx));
return condition(new detail::key_matches_condition_impl(key.m_item_tag, rx));
}
/**
@@ -1219,7 +1050,7 @@ inline condition operator==(const key &key, const std::regex &rx)
*/
inline condition operator==(const key &key, const empty_type &)
{
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
}
/**
@@ -1227,33 +1058,20 @@ inline condition operator==(const key &key, const empty_type &)
*/
inline condition operator!=(const key &key, const empty_type &)
{
return condition(new detail::key_is_not_empty_condition_impl(key.m_item_name));
return condition(new detail::key_is_not_empty_condition_impl(key.m_item_tag));
}
/**
* @brief Create a condition to search any item for a value @a v if @a v contains a value
* @brief Create a condition to search any column for a value @a v if @a v contains a value
* compare to null if not.
*/
template <typename T>
condition operator==(const key &key, const std::optional<T> &v)
{
if (v.has_value())
return condition(new detail::key_equals_condition_impl({ key.m_item_name, *v }));
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, *v }));
else
return condition(new detail::key_is_empty_condition_impl(key.m_item_name));
}
/**
* @brief Create a condition to search any item for a value @a v if @a v contains a value
* compare to null if not.
*/
template <typename T>
condition operator!=(const key &key, const std::optional<T> &v)
{
if (v.has_value())
return condition(new detail::not_condition_impl(condition(new detail::key_equals_condition_impl({ key.m_item_name, *v }))));
else
return condition(new detail::not_condition_impl(condition(new detail::key_is_empty_condition_impl(key.m_item_name))));
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
}
/**
@@ -1271,12 +1089,12 @@ struct any_type
/** @endcond */
/**
* @brief A helper for any item constructs
* @brief A helper for any field constructs
*/
inline constexpr any_type any = any_type{};
/**
* @brief Create a condition to search any item for a value @a v
* @brief Create a condition to search any column for a value @a v
*/
template <typename T>
condition operator==(const any_type &, const T &v)
@@ -1285,7 +1103,7 @@ condition operator==(const any_type &, const T &v)
}
/**
* @brief Create a condition to search any item for a regular expression @a rx
* @brief Create a condition to search any column for a regular expression @a rx
*/
inline condition operator==(const any_type &, const std::regex &rx)
{
@@ -1303,13 +1121,13 @@ inline condition all()
namespace literals
{
/**
* @brief Return a cif::key for the item name @a text
*
* @param text The name of the item
* @brief Return a cif::key for the column name @a text
*
* @param text The name of the column
* @param length The length of @a text
* @return key The cif::key created
*/
inline key operator""_key(const char *text, std::size_t length)
inline key operator""_key(const char *text, size_t length)
{
return key(std::string(text, length));
}

View File

@@ -29,8 +29,6 @@
#include "cif++/category.hpp"
#include "cif++/forward_decl.hpp"
#include <list>
/** \file datablock.hpp
* Each valid mmCIF file contains at least one @ref cif::datablock.
* A datablock has a name and can contain one or more @ref cif::category "categories"
@@ -63,26 +61,12 @@ class datablock : public std::list<category>
/** @cond */
datablock(const datablock &);
datablock(datablock &&) = default;
datablock(datablock &&db) noexcept
{
swap_(*this, db);
}
datablock &operator=(datablock db)
{
swap_(*this, db);
return *this;
}
datablock &operator=(const datablock &);
datablock &operator=(datablock &&) = default;
/** @endcond */
friend void swap_(datablock &a, datablock &b) noexcept
{
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));
}
// --------------------------------------------------------------------
/**
@@ -100,12 +84,6 @@ class datablock : public std::list<category>
m_name = name;
}
/**
* @brief Attempt to load the dictionary specified in audit_conform category
*
*/
void load_dictionary();
/**
* @brief Set the validator object to @a v
*
@@ -137,14 +115,6 @@ class datablock : public std::list<category>
*/
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();
// --------------------------------------------------------------------
/**
@@ -183,15 +153,6 @@ class datablock : public std::list<category>
*/
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
{
return get(name) != nullptr;
}
/**
* @brief Tries to find a category with name @a name and will create a
* new one if it is not found. The result is a tuple of an iterator
@@ -208,16 +169,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;
std::vector<std::string> get_tag_order() const;
/**
* @brief Write out the contents to @a os
@@ -225,9 +177,9 @@ class datablock : public std::list<category>
void write(std::ostream &os) const;
/**
* @brief Write out the contents to @a os using the order defined in @a item_name_order
* @brief Write out the contents to @a os using the order defined in @a tag_order
*/
void write(std::ostream &os, const std::vector<std::string> &item_name_order);
void write(std::ostream &os, const std::vector<std::string> &tag_order);
/**
* @brief Friend operator<< to write datablock @a db to std::ostream @a os
@@ -250,4 +202,4 @@ class datablock : public std::list<category>
const validator *m_validator = nullptr;
};
} // namespace cif
} // namespace cif

View File

@@ -38,8 +38,13 @@ namespace cif
{
/**
* @brief Parse the contents of @a is and place content in validator @a v
* @brief Parse the contents of @a is and create a new validator object with name @a name
*/
void parse_dictionary(validator &v, std::istream &is);
validator parse_dictionary(std::string_view name, std::istream &is);
/**
* @brief Extend the definitions in validator @a v with the contents of stream @a is
*/
void extend_dictionary(validator &v, std::istream &is);
} // namespace cif

View File

@@ -85,11 +85,11 @@ class file : public std::list<datablock>
* @param data The pointer to the character string with data to load
* @param length The length of the data
*/
explicit file(const char *data, std::size_t length)
explicit file(const char *data, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
@@ -100,24 +100,25 @@ class file : public std::list<datablock>
}
/** @cond */
file(const file &rhs)
: std::list<datablock>(rhs)
{
}
file(file &&rhs)
{
this->swap(rhs);
}
file &operator=(file f)
{
this->swap(f);
return *this;
}
file(const file &) = default;
file(file &&) = default;
file &operator=(const file &) = default;
file &operator=(file &&) = default;
/** @endcond */
/**
* @brief Set the validator object to @a v
*/
void set_validator(const validator *v);
/**
* @brief Get the validator object
*/
const validator *get_validator() const
{
return m_validator;
}
/**
* @brief Validate the content and return true if everything was valid.
*
@@ -152,6 +153,21 @@ class file : public std::list<datablock>
*/
bool validate_links() const;
/**
* @brief Attempt to load a dictionary (validator) based on
* the contents of the *audit_conform* category, if available.
*/
void load_dictionary();
/**
* @brief Attempt to load the named dictionary @a name and
* create a validator based on it.
*
* @param name The name of the dictionary to load
*/
void load_dictionary(std::string_view name);
/**
* @brief Return true if a datablock with the name @a name is part of this file
*/
@@ -204,12 +220,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;
@@ -224,6 +234,9 @@ class file : public std::list<datablock>
f.save(os);
return os;
}
private:
const validator *m_validator = nullptr;
};
} // namespace cif
} // namespace cif

View File

@@ -26,28 +26,138 @@
#pragma once
#if __has_include(<format>)
#include <format>
#define USE_STD_FORMAT 1
#else
#include <fmt/format.h>
#endif
#include <string>
/** \file format.hpp
*
* Now using cif::format instead of a home grown rip off
* File containing a basic reimplementation of boost::format
* but then a bit more simplistic. Still this allowed me to move my code
* from using boost::format to something without external dependency easily.
*/
namespace cif
{
#if USE_STD_FORMAT
using std::format;
#else
using fmt::format;
#endif
namespace detail
{
template <typename T>
struct to_varg
{
using type = T;
to_varg(const T &v)
: m_value(v)
{
}
type operator*() { return m_value; }
T m_value;
};
template <>
struct to_varg<const char *>
{
using type = const char *;
to_varg(const char *v)
: m_value(v)
{
}
type operator*() { return m_value.c_str(); }
std::string m_value;
};
template <>
struct to_varg<std::string>
{
using type = const char *;
to_varg(const std::string &v)
: m_value(v)
{
}
type operator*() { return m_value.c_str(); }
std::string m_value;
};
} // namespace
/** @cond */
template <typename... Args>
class format_plus_arg
{
public:
using args_vector_type = std::tuple<detail::to_varg<Args>...>;
using vargs_vector_type = std::tuple<typename detail::to_varg<Args>::type...>;
format_plus_arg(const format_plus_arg &) = delete;
format_plus_arg &operator=(const format_plus_arg &) = delete;
format_plus_arg(std::string_view fmt, Args... args)
: m_fmt(fmt)
, m_args(std::forward<Args>(args)...)
{
auto ix = std::make_index_sequence<sizeof...(Args)>();
copy_vargs(ix);
}
std::string str()
{
char buffer[1024];
std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), m_fmt.c_str()), m_vargs));
return { buffer, r };
}
friend std::ostream &operator<<(std::ostream &os, const format_plus_arg &f)
{
char buffer[1024];
std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), f.m_fmt.c_str()), f.m_vargs));
os.write(buffer, r);
return os;
}
private:
template <size_t... I>
void copy_vargs(std::index_sequence<I...>)
{
((std::get<I>(m_vargs) = *std::get<I>(m_args)), ...);
}
std::string m_fmt;
args_vector_type m_args;
vargs_vector_type m_vargs;
};
/** @endcond */
/**
* @brief A simplistic reimplementation of boost::format, in fact it is
* actually a way to call the C function snprintf to format the arguments
* in @a args into the format string @a fmt
*
* The string in @a fmt should thus be a C style format string.
*
* TODO: Move to C++23 style of printing.
*
* @tparam Args The types of the arguments
* @param fmt The format string
* @param args The arguments
* @return An object that can be written out to a std::ostream using operator<<
*/
template <typename... Args>
constexpr auto format(std::string_view fmt, Args... args)
{
return format_plus_arg(fmt, std::forward<Args>(args)...);
}
// --------------------------------------------------------------------
/// A streambuf that fills out lines with spaces up until a specified width

View File

@@ -1,33 +1,7 @@
/*-
* 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.
*/
/*
Original code comes from libgxrio at https://github.com/mhekkel/gxrio
This is a stripped down version.
*/
// Copyright Maarten L. Hekkelman, 2022
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
@@ -62,7 +36,7 @@ namespace cif::gzio
{
/** The default buffer size to use */
const std::size_t kDefaultBufferSize = 256;
const size_t kDefaultBufferSize = 256;
// --------------------------------------------------------------------
@@ -138,7 +112,7 @@ class basic_streambuf : public std::basic_streambuf<CharT, Traits>
/// This implementation of streambuf can decompress (inflate) data compressed
/// using zlib.
template <typename CharT, typename Traits, std::size_t BufferSize = kDefaultBufferSize>
template <typename CharT, typename Traits, size_t BufferSize = kDefaultBufferSize>
class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
{
public:
@@ -343,7 +317,7 @@ class basic_igzip_streambuf : public basic_streambuf<CharT, Traits>
///
/// This implementation of streambuf can compress (deflate) data using zlib.
template <typename CharT, typename Traits, std::size_t BufferSize = kDefaultBufferSize>
template <typename CharT, typename Traits, size_t BufferSize = kDefaultBufferSize>
class basic_ogzip_streambuf : public basic_streambuf<CharT, Traits>
{
public:

View File

@@ -44,7 +44,7 @@
/** \file item.hpp
*
* This file contains the declaration of item but also the item_value and item_handle
* These handle the storage of and access to the data for a single data item.
* These handle the storage of and access to the data for a single data field.
*/
namespace cif
@@ -53,12 +53,12 @@ namespace cif
// --------------------------------------------------------------------
/** @brief item is a transient class that is used to pass data into rows
* but it also takes care of formatting data.
*
*
*
*
*
*
* The class cif::item is often used implicitly when creating a row in a category
* using the emplace function.
*
*
* @code{.cpp}
* cif::category cat("my-cat");
* cat.emplace({
@@ -68,12 +68,12 @@ namespace cif
* { "item-4", std::make_optional<int>(42) }, // <- stores an item with value 42
* { "item-5" } // <- stores an item with value .
* });
*
*
* std::cout << cat << '\n';
* @endcode
*
*
* Will result in:
*
*
* @code{.txt}
* _my-cat.item-1 1
* _my-cat.item-2 1.00
@@ -117,9 +117,11 @@ class item
char buffer[32];
auto r = to_chars(buffer, buffer + sizeof(buffer) - 1, value, chars_format::fixed, precision);
if ((bool)r.ec)
if (r.ec != std::errc())
throw std::runtime_error("Could not format number");
assert(r.ptr >= buffer and r.ptr < buffer + sizeof(buffer));
*r.ptr = 0;
m_value.assign(buffer, r.ptr - buffer);
}
@@ -136,9 +138,11 @@ class item
char buffer[32];
auto r = to_chars(buffer, buffer + sizeof(buffer) - 1, value, chars_format::general);
if ((bool)r.ec)
if (r.ec != std::errc())
throw std::runtime_error("Could not format number");
assert(r.ptr >= buffer and r.ptr < buffer + sizeof(buffer));
*r.ptr = 0;
m_value.assign(buffer, r.ptr - buffer);
}
@@ -151,9 +155,11 @@ class item
char buffer[32];
auto r = std::to_chars(buffer, buffer + sizeof(buffer) - 1, value);
if ((bool)r.ec)
if (r.ec != std::errc())
throw std::runtime_error("Could not format number");
assert(r.ptr >= buffer and r.ptr < buffer + sizeof(buffer));
*r.ptr = 0;
m_value.assign(buffer, r.ptr - buffer);
}
@@ -168,21 +174,12 @@ class item
/// \brief constructor for an item with name \a name and as
/// content value \a value
item(const std::string_view name, std::string_view value)
item(const std::string_view name, const std::string_view value)
: m_name(name)
, m_value(value)
{
}
/// \brief constructor for an item with name \a name and as
/// content value \a value
template <typename T, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0>
item(const std::string_view name, T &&value)
: m_name(name)
, m_value(std::move(value))
{
}
/// \brief constructor for an item with name \a name and as
/// content the optional value \a value
template <typename T>
@@ -221,9 +218,8 @@ class item
item &operator=(item &&rhs) noexcept = default;
/** @endcond */
std::string_view name() const { return m_name; } ///< Return the name of the item
std::string_view value() const & { return m_value; } ///< Return the value of the item
std::string value() const && { return std::move(m_value); } ///< Return the value of the item
std::string_view name() const { return m_name; } ///< Return the name of the item
std::string_view value() const { return m_value; } ///< Return the value of the item
/// \brief replace the content of the stored value with \a v
void value(std::string_view v) { m_value = v; }
@@ -231,17 +227,17 @@ class item
/// \brief empty means either null or unknown
bool empty() const { return m_value.empty(); }
/// \brief returns true if the item contains '.'
/// \brief returns true if the field contains '.'
bool is_null() const { return m_value == "."; }
/// \brief returns true if the item contains '?'
/// \brief returns true if the field contains '?'
bool is_unknown() const { return m_value == "?"; }
/// \brief the length of the value string
std::size_t length() const { return m_value.length(); }
size_t length() const { return m_value.length(); }
/// \brief support for structured binding
template <std::size_t N>
template <size_t N>
decltype(auto) get() const
{
if constexpr (N == 0)
@@ -250,8 +246,6 @@ class item
return value();
}
auto operator<=>(const item &rhs) const = default;
private:
std::string_view m_name;
std::string m_value;
@@ -290,16 +284,19 @@ struct item_value
}
/** @cond */
item_value(item_value &&rhs) noexcept
item_value(item_value &&rhs)
: m_length(std::exchange(rhs.m_length, 0))
, m_storage(std::exchange(rhs.m_storage, 0))
{
}
item_value &operator=(item_value &&rhs) noexcept
item_value &operator=(item_value &&rhs)
{
std::swap(m_length, rhs.m_length);
std::swap(m_storage, rhs.m_storage);
if (this != &rhs)
{
m_length = std::exchange(rhs.m_length, m_length);
m_storage = std::exchange(rhs.m_storage, m_storage);
}
return *this;
}
@@ -321,7 +318,7 @@ struct item_value
return m_length != 0;
}
std::size_t m_length = 0; ///< Length of the data
size_t m_length = 0; ///< Length of the data
union
{
char m_local_data[8]; ///< Storage area for small strings (strings smaller than kBufferSize)
@@ -330,7 +327,7 @@ struct item_value
};
/** The maximum length of locally stored strings */
static constexpr std::size_t kBufferSize = sizeof(m_local_data);
static constexpr size_t kBufferSize = sizeof(m_local_data);
// By using std::string_view instead of c_str we obain a
// nice performance gain since we avoid many calls to strlen.
@@ -366,35 +363,8 @@ struct item_handle
template <typename T>
item_handle &operator=(const T &value)
{
assign_value(item{ "", value }.value());
return *this;
}
/**
* @brief Assign value @a value to the item referenced
*
* @tparam T Type of the value
* @param value The value
* @return reference to this item_handle
*/
template <typename T>
item_handle &operator=(T &&value)
{
assign_value(item{ "", std::forward<T>(value) }.value());
return *this;
}
/**
* @brief Assign value @a value to the item referenced
*
* @tparam T Type of the value
* @param value The value
* @return reference to this item_handle
*/
template <std::size_t N>
item_handle &operator=(const char (&value)[N])
{
assign_value(item{ "", std::move(value) }.value());
item v{ "", value };
assign_value(v);
return *this;
}
@@ -494,14 +464,14 @@ struct item_handle
/** Easy way to test for an empty item */
explicit operator bool() const { return not empty(); }
/// is_null return true if the item contains '.'
/// is_null return true if the field contains '.'
bool is_null() const
{
auto txt = text();
return txt.length() == 1 and txt.front() == '.';
}
/// is_unknown returns true if the item contains '?'
/// is_unknown returns true if the field contains '?'
bool is_unknown() const
{
auto txt = text();
@@ -514,11 +484,11 @@ struct item_handle
/**
* @brief Construct a new item handle object
*
* @param item Item index
* @param column Column index
* @param row Reference to the row
*/
item_handle(uint16_t item, row_handle &row)
: m_item_ix(item)
item_handle(uint16_t column, row_handle &row)
: m_column(column)
, m_row_handle(row)
{
}
@@ -535,10 +505,10 @@ struct item_handle
private:
item_handle();
uint16_t m_item_ix;
uint16_t m_column;
row_handle &m_row_handle;
void assign_value(std::string_view value);
void assign_value(const item &value);
};
// So sad that older gcc implementations of from_chars did not support floats yet...
@@ -560,11 +530,9 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto b = txt.data();
auto e = txt.data() + txt.size();
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) //
? from_chars(b + 1, e, result)
: from_chars(b, e, result);
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) ? selected_charconv<value_type>::from_chars(b + 1, e, result) : selected_charconv<value_type>::from_chars(b, e, result);
if ((bool)r.ec or r.ptr != e)
if (r.ec != std::errc() or r.ptr != e)
{
result = {};
if (cif::VERBOSE)
@@ -588,7 +556,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto txt = ref.text();
if (ref.empty())
if (txt.empty())
result = 1;
else
{
@@ -597,11 +565,9 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
auto b = txt.data();
auto e = txt.data() + txt.size();
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1]))
? from_chars(b + 1, e, v)
: from_chars(b, e, v);
std::from_chars_result r = (b + 1 < e and *b == '+' and std::isdigit(b[1])) ? selected_charconv<value_type>::from_chars(b + 1, e, v) : selected_charconv<value_type>::from_chars(b, e, v);
if ((bool)r.ec or r.ptr != e)
if (r.ec != std::errc() or r.ptr != e)
{
if (cif::VERBOSE)
{
@@ -614,8 +580,6 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> an
}
result = 1;
}
else if (std::abs(v - value) <= std::numeric_limits<value_type>::epsilon())
result = 0;
else if (v < value)
result = -1;
else if (v > value)
@@ -670,7 +634,7 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>>
}
};
template <std::size_t N>
template <size_t N>
struct item_handle::item_value_as<char[N]>
{
static std::string convert(const item_handle &ref)

View File

@@ -49,7 +49,7 @@ namespace cif
/**
* @brief Implementation of an iterator that can return
* multiple values in a tuple. Of course, that tuple can
* then be used in structured binding to receive the values
* then used in structured binding to receive the values
* in a for loop e.g.
*
* @tparam Category The category for this iterator
@@ -67,7 +67,7 @@ class iterator_impl
/** @endcond */
/** variable that contains the number of elements in the tuple */
static constexpr std::size_t N = sizeof...(Ts);
static constexpr size_t N = sizeof...(Ts);
/** @cond */
using category_type = std::remove_cv_t<Category>;
@@ -84,38 +84,41 @@ class iterator_impl
iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default;
iterator_impl(iterator_impl &&rhs) = default;
template <typename C2, typename... T2s>
iterator_impl(const iterator_impl<C2, T2s...> &rhs)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
, m_column_ix(rhs.m_column_ix)
{
}
template <typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
, m_column_ix(rhs.m_column_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))
, m_item_ix(cix)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(cix)
{
m_value = get(std::make_index_sequence<N>());
}
iterator_impl &operator=(iterator_impl i)
iterator_impl &operator=(const iterator_impl &i)
{
std::swap(m_current, i.m_current);
std::swap(m_item_ix, i.m_item_ix);
std::swap(m_value, i.m_value);
m_category = i.m_category;
m_current = i.m_current;
m_column_ix = i.m_column_ix;
m_value = i.m_value;
return *this;
}
@@ -133,18 +136,18 @@ class iterator_impl
operator const row_handle() const
{
return m_current;
return { *m_category, *m_current };
}
operator row_handle()
{
return m_current;
return { *m_category, *m_current };
}
iterator_impl &operator++()
{
if (m_current)
m_current.m_row = m_current.m_row->m_next;
if (m_current != nullptr)
m_current = m_current->m_next;
m_value = get(std::make_index_sequence<N>());
@@ -176,15 +179,22 @@ class iterator_impl
/** @endcond */
private:
template <std::size_t... Is>
template <size_t... Is>
tuple_type get(std::index_sequence<Is...>) const
{
return m_current ? tuple_type{ m_current[m_item_ix[Is]].template as<Ts>()... } : tuple_type{};
if (m_current != nullptr)
{
row_handle rh{ *m_category, *m_current };
return tuple_type{ rh[m_column_ix[Is]].template as<Ts>()... };
}
return {};
}
row_handle m_current;
category_type *m_category = nullptr;
row_type *m_current = nullptr;
value_type m_value;
std::array<uint16_t, N> m_item_ix;
std::array<uint16_t, N> m_column_ix;
};
/**
@@ -209,34 +219,37 @@ class iterator_impl<Category>
using iterator_category = std::forward_iterator_tag;
using value_type = row_handle;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
using pointer = row_handle;
using reference = row_handle;
iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default;
iterator_impl(iterator_impl &&rhs) = default;
template <typename C2>
iterator_impl(const iterator_impl<C2> &rhs)
: m_current(const_cast<row_handle &>(rhs.m_current))
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
{
}
iterator_impl(Category &cat, row *current)
: m_current(cat, *current)
: m_category(const_cast<category_type *>(&cat))
, m_current(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))
: m_category(rhs.m_category)
, m_current(rhs.m_current)
{
}
iterator_impl &operator=(iterator_impl i)
iterator_impl &operator=(const iterator_impl &i)
{
std::swap(m_current, i.m_current);
m_category = i.m_category;
m_current = i.m_current;
return *this;
}
@@ -244,7 +257,7 @@ class iterator_impl<Category>
reference operator*()
{
return m_current;
return { *m_category, *m_current };
}
pointer operator->()
@@ -254,18 +267,18 @@ class iterator_impl<Category>
operator const row_handle() const
{
return m_current;
return { *m_category, *m_current };
}
operator row_handle()
{
return m_current;
return { *m_category, *m_current };
}
iterator_impl &operator++()
{
if (m_current)
m_current.m_row = m_current.m_row->m_next;
if (m_current != nullptr)
m_current = m_current->m_next;
return *this;
}
@@ -295,7 +308,8 @@ class iterator_impl<Category>
/** @endcond */
private:
row_handle m_current;
category_type *m_category = nullptr;
row_type *m_current = nullptr;
};
/**
@@ -328,38 +342,41 @@ class iterator_impl<Category, T>
iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default;
iterator_impl(iterator_impl &&rhs) = default;
template <typename C2, typename T2>
iterator_impl(const iterator_impl<C2, T2> &rhs)
: m_current(rhs.m_current)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
, m_column_ix(rhs.m_column_ix)
{
}
template <typename IRowType>
iterator_impl(iterator_impl<IRowType, T> &rhs)
: m_current(const_cast<row_handle&>(rhs.m_current))
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value)
, m_item_ix(rhs.m_item_ix)
, m_column_ix(rhs.m_column_ix)
{
m_value = get();
m_value = get(m_current);
}
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))
, m_item_ix(cix[0])
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(cix[0])
{
m_value = get();
}
iterator_impl &operator=(iterator_impl i)
iterator_impl &operator=(const iterator_impl &i)
{
std::swap(m_current, i.m_current);
std::swap(m_item_ix, i.m_item_ix);
std::swap(m_value, i.m_value);
m_category = i.m_category;
m_current = i.m_current;
m_column_ix = i.m_column_ix;
m_value = i.m_value;
return *this;
}
@@ -377,18 +394,18 @@ class iterator_impl<Category, T>
operator const row_handle() const
{
return m_current;
return { *m_category, *m_current };
}
operator row_handle()
{
return m_current;
return { *m_category, *m_current };
}
iterator_impl &operator++()
{
if (m_current)
m_current.m_row = m_current.m_row->m_next;
if (m_current != nullptr)
m_current = m_current->m_next;
m_value = get();
@@ -422,12 +439,19 @@ class iterator_impl<Category, T>
private:
value_type get() const
{
return m_current ? m_current[m_item_ix].template as<value_type>() : value_type{};
if (m_current != nullptr)
{
row_handle rh{ *m_category, *m_current };
return rh[m_column_ix].template as<T>();
}
return {};
}
row_handle m_current;
category_type *m_category = nullptr;
row_type *m_current = nullptr;
value_type m_value;
uint16_t m_item_ix;
uint16_t m_column_ix;
};
// --------------------------------------------------------------------
@@ -450,7 +474,7 @@ class iterator_proxy
{
public:
/** @cond */
static constexpr const std::size_t N = sizeof...(Ts);
static constexpr const size_t N = sizeof...(Ts);
using category_type = Category;
using row_type = std::conditional_t<std::is_const_v<category_type>, const row, row>;
@@ -458,8 +482,8 @@ class iterator_proxy
using iterator = iterator_impl<category_type, Ts...>;
using row_iterator = iterator_impl<category_type>;
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(category_type &cat, row_iterator pos, char const *const columns[N]);
iterator_proxy(category_type &cat, row_iterator pos, std::initializer_list<char const *> columns);
iterator_proxy(iterator_proxy &&p);
iterator_proxy &operator=(iterator_proxy &&p);
@@ -468,12 +492,12 @@ class iterator_proxy
iterator_proxy &operator=(const iterator_proxy &) = 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
iterator begin() const { return iterator(m_begin, m_column_ix); } ///< Return the iterator pointing to the first row
iterator end() const { return iterator(m_end, m_column_ix); } ///< Return the iterator pointing past the last row
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
size_t size() const { return std::distance(begin(), end()); } ///< Return size of the range
// row front() { return *begin(); }
// row back() { return *(std::prev(end())); }
@@ -486,13 +510,13 @@ class iterator_proxy
std::swap(m_category, rhs.m_category);
std::swap(m_begin, rhs.m_begin);
std::swap(m_end, rhs.m_end);
std::swap(m_item_ix, rhs.m_item_ix);
std::swap(m_column_ix, rhs.m_column_ix);
}
private:
category_type *m_category;
row_iterator m_begin, m_end;
std::array<uint16_t, N> m_item_ix;
std::array<uint16_t, N> m_column_ix;
};
// --------------------------------------------------------------------
@@ -512,7 +536,7 @@ class conditional_iterator_proxy
{
public:
/** @cond */
static constexpr const std::size_t N = sizeof...(Ts);
static constexpr const size_t N = sizeof...(Ts);
using category_type = std::remove_cv_t<CategoryType>;
@@ -538,23 +562,22 @@ class conditional_iterator_proxy
reference operator*()
{
return *m_begin;
return *mBegin;
}
pointer operator->()
{
m_current = *m_begin;
return &m_current;
return &*mBegin;
}
conditional_iterator_impl &operator++()
{
while (m_begin != m_end)
while (mBegin != mEnd)
{
if (++m_begin == m_end)
if (++mBegin == mEnd)
break;
if (m_condition->operator()(m_begin))
if (m_condition->operator()(mBegin))
break;
}
@@ -568,22 +591,18 @@ class conditional_iterator_proxy
return result;
}
bool operator==(const conditional_iterator_impl &rhs) const { return m_begin == rhs.m_begin; }
bool operator!=(const conditional_iterator_impl &rhs) const { return m_begin != rhs.m_begin; }
bool operator==(const row_iterator &rhs) const { return m_begin == rhs; }
bool operator!=(const row_iterator &rhs) const { return m_begin != rhs; }
bool operator==(const conditional_iterator_impl &rhs) const { return mBegin == rhs.mBegin; }
bool operator!=(const conditional_iterator_impl &rhs) const { return mBegin != rhs.mBegin; }
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return m_begin == rhs; }
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin == rhs; }
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return m_begin != rhs; }
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; }
private:
CategoryType *m_cat;
base_iterator m_begin, m_end;
value_type m_current;
CategoryType *mCat;
base_iterator mBegin, mEnd;
const condition *m_condition;
};
@@ -606,7 +625,7 @@ class conditional_iterator_proxy
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
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
// row_handle back() { return *begin(); }
@@ -627,26 +646,26 @@ class conditional_iterator_proxy
/** @cond */
template <typename Category, typename... Ts>
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const items[N])
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, char const *const columns[N])
: m_category(&cat)
, m_begin(pos)
, m_end(cat.end())
{
for (uint16_t i = 0; i < N; ++i)
m_item_ix[i] = m_category->get_item_ix(items[i]);
m_column_ix[i] = m_category->get_column_ix(columns[i]);
}
template <typename Category, typename... Ts>
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> items)
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, std::initializer_list<char const *> columns)
: m_category(&cat)
, m_begin(pos)
, m_end(cat.end())
{
// static_assert(items.size() == N, "The list of item names should be exactly the same as the list of requested items");
// static_assert(columns.size() == N, "The list of column names should be exactly the same as the list of requested columns");
std::uint16_t i = 0;
for (auto item : items)
m_item_ix[i++] = m_category->get_item_ix(item);
for (auto column : columns)
m_column_ix[i++] = m_category->get_column_ix(column);
}
// --------------------------------------------------------------------
@@ -654,13 +673,13 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos,
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)
: m_cat(&cat)
, m_begin(pos, cix)
, m_end(cat.end(), cix)
: mCat(&cat)
, mBegin(pos, cix)
, mEnd(cat.end(), cix)
, m_condition(&cond)
{
if (m_condition == nullptr or m_condition->empty())
m_begin = m_end;
mBegin = mEnd;
}
template <typename Category, typename... Ts>
@@ -683,7 +702,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
, mCBegin(pos)
, mCEnd(cat.end())
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of item names should be equal to number of requested value types");
static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of column names should be equal to number of requested value types");
if (m_condition)
{
@@ -696,7 +715,7 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category
mCBegin = mCEnd;
uint16_t i = 0;
((mCix[i++] = m_cat->get_item_ix(names)), ...);
((mCix[i++] = m_cat->get_column_ix(names)), ...);
}
template <typename Category, typename... Ts>

View File

@@ -59,27 +59,27 @@ template <typename M>
class matrix_expression
{
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
constexpr size_t dim_m() const { return static_cast<const M &>(*this).dim_m(); } ///< Return the size (dimension) in direction m
constexpr 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
/** Return a reference to element [ @a i, @a j ] */
constexpr auto &operator()(std::size_t i, std::size_t j)
constexpr auto &operator()(size_t i, 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
constexpr auto operator()(size_t i, size_t j) const
{
return static_cast<const M &>(*this).operator()(i, j);
}
/** Swap the contents of rows @a r1 and @a r2 */
void swap_row(std::size_t r1, std::size_t r2)
void swap_row(size_t r1, size_t r2)
{
for (std::size_t c = 0; c < dim_m(); ++c)
for (size_t c = 0; c < dim_m(); ++c)
{
auto v = operator()(r1, c);
operator()(r1, c) = operator()(r2, c);
@@ -88,9 +88,9 @@ class matrix_expression
}
/** Swap the contents of columns @a c1 and @a c2 */
void swap_col(std::size_t c1, std::size_t c2)
void swap_col(size_t c1, size_t c2)
{
for (std::size_t r = 0; r < dim_n(); ++r)
for (size_t r = 0; r < dim_n(); ++r)
{
auto &a = operator()(r, c1);
auto &b = operator()(r, c2);
@@ -103,11 +103,11 @@ class matrix_expression
{
os << '[';
for (std::size_t i = 0; i < m.dim_m(); ++i)
for (size_t i = 0; i < m.dim_m(); ++i)
{
os << '[';
for (std::size_t j = 0; j < m.dim_n(); ++j)
for (size_t j = 0; j < m.dim_n(); ++j)
{
os << m(i, j);
if (j + 1 < m.dim_n())
@@ -156,9 +156,9 @@ class matrix : public matrix_expression<matrix<F>>
, m_n(m.dim_n())
, m_data(m_m * m_n)
{
for (std::size_t i = 0; i < m_m; ++i)
for (size_t i = 0; i < m_m; ++i)
{
for (std::size_t j = 0; j < m_n; ++j)
for (size_t j = 0; j < m_n; ++j)
operator()(i, j) = m(i, j);
}
}
@@ -171,7 +171,7 @@ class matrix : public matrix_expression<matrix<F>>
* @param n Requested dimension N
* @param v Value to store in each element
*/
matrix(std::size_t m, std::size_t n, value_type v = 0)
matrix(size_t m, size_t n, value_type v = 0)
: m_m(m)
, m_n(n)
, m_data(m_m * m_n)
@@ -187,11 +187,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
constexpr size_t dim_m() const { return m_m; } ///< Return dimension m
constexpr 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
constexpr value_type operator()(size_t i, size_t j) const
{
assert(i < m_m);
assert(j < m_n);
@@ -199,7 +199,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)
constexpr value_type &operator()(size_t i, size_t j)
{
assert(i < m_m);
assert(j < m_n);
@@ -207,7 +207,7 @@ class matrix : public matrix_expression<matrix<F>>
}
private:
std::size_t m_m = 0, m_n = 0;
size_t m_m = 0, m_n = 0;
std::vector<value_type> m_data;
};
@@ -224,7 +224,7 @@ class matrix : public matrix_expression<matrix<F>>
* element m i,j is mapped to [i * n + j] and thus storage is row major
*/
template <typename F, std::size_t M, std::size_t N>
template <typename F, size_t M, size_t N>
class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
{
public:
@@ -232,16 +232,16 @@ class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
using value_type = F;
/** The storage size */
static constexpr std::size_t kSize = M * N;
static constexpr size_t kSize = M * N;
/** Copy constructor */
template <typename M2>
matrix_fixed(const M2 &m)
{
assert(M == m.dim_m() and N == m.dim_n());
for (std::size_t i = 0; i < M; ++i)
for (size_t i = 0; i < M; ++i)
{
for (std::size_t j = 0; j < N; ++j)
for (size_t j = 0; j < N; ++j)
operator()(i, j) = m(i, j);
}
}
@@ -266,18 +266,18 @@ class matrix_fixed : public matrix_expression<matrix_fixed<F, M, N>>
/** @endcond */
/** Store the values in @a a in the matrix */
template<std::size_t... Ixs>
template<size_t... Ixs>
matrix_fixed& fill(const F (&a)[kSize], std::index_sequence<Ixs...>)
{
m_data = { a[Ixs]... };
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
constexpr size_t dim_m() const { return M; } ///< Return dimension m
constexpr 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
constexpr value_type operator()(size_t i, size_t j) const
{
assert(i < M);
assert(j < N);
@@ -285,7 +285,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)
constexpr value_type &operator()(size_t i, size_t j)
{
assert(i < M);
assert(j < N);
@@ -322,7 +322,7 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
using value_type = F;
/** constructor for a matrix of size @a n x @a n elements with value @a v */
symmetric_matrix(std::size_t n, value_type v = 0)
symmetric_matrix(size_t n, value_type v = 0)
: m_n(n)
, m_data((m_n * (m_n + 1)) / 2)
{
@@ -337,11 +337,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
constexpr size_t dim_m() const { return m_n; } ///< Return dimension m
constexpr 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
constexpr value_type operator()(size_t i, size_t j) const
{
return i < j
? m_data[(j * (j + 1)) / 2 + i]
@@ -349,7 +349,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)
constexpr value_type &operator()(size_t i, size_t j)
{
if (i > j)
std::swap(i, j);
@@ -358,7 +358,7 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
}
private:
std::size_t m_n;
size_t m_n;
std::vector<value_type> m_data;
};
@@ -373,7 +373,7 @@ class symmetric_matrix : public matrix_expression<symmetric_matrix<F>>
* matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
* element m i,j is mapped to [i * n + j] and thus storage is row major
*/
template <typename F, std::size_t M>
template <typename F, size_t M>
class symmetric_matrix_fixed : public matrix_expression<symmetric_matrix_fixed<F, M>>
{
public:
@@ -393,11 +393,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
constexpr size_t dim_m() const { return M; } ///< Return dimension m
constexpr 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
constexpr value_type operator()(size_t i, size_t j) const
{
return i < j
? m_data[(j * (j + 1)) / 2 + i]
@@ -405,7 +405,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)
constexpr value_type &operator()(size_t i, size_t j)
{
if (i > j)
std::swap(i, j);
@@ -444,22 +444,22 @@ class identity_matrix : public matrix_expression<identity_matrix<F>>
using value_type = F;
/** constructor taking a dimension @a n */
identity_matrix(std::size_t n)
identity_matrix(size_t n)
: m_n(n)
{
}
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
constexpr size_t dim_m() const { return m_n; } ///< Return dimension m
constexpr 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
constexpr value_type operator()(size_t i, size_t j) const
{
return static_cast<value_type>(i == j ? 1 : 0);
}
private:
std::size_t m_n;
size_t m_n;
};
// --------------------------------------------------------------------
@@ -484,11 +484,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
constexpr size_t dim_m() const { return m_m1.dim_m(); } ///< Return dimension m
constexpr 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
constexpr auto operator()(size_t i, size_t j) const
{
return m_m1(i, j) - m_m2(i, j);
}
@@ -523,17 +523,17 @@ 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
constexpr size_t dim_m() const { return m_m1.dim_m(); } ///< Return dimension m
constexpr 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
constexpr auto operator()(size_t i, size_t j) const
{
using value_type = decltype(m_m1(0, 0));
value_type result = {};
for (std::size_t k = 0; k < m_m1.dim_m(); ++k)
for (size_t k = 0; k < m_m1.dim_m(); ++k)
result += m_m1(i, k) * m_m2(k, j);
return result;
@@ -564,11 +564,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
constexpr size_t dim_m() const { return m_m.dim_m(); } ///< Return dimension m
constexpr 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
constexpr auto operator()(size_t i, size_t j) const
{
return m_m(i, j) * m_v;
}
@@ -655,21 +655,21 @@ 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
constexpr size_t dim_m() const { return m_m.dim_m(); } ///< Return dimension m
constexpr 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
constexpr auto operator()(size_t i, size_t j) const
{
const std::size_t ixs[4][3] = {
const size_t ixs[4][3] = {
{ 1, 2, 3 },
{ 0, 2, 3 },
{ 0, 1, 3 },
{ 0, 1, 2 }
};
const std::size_t *ix = ixs[i];
const std::size_t *iy = ixs[j];
const size_t *ix = ixs[i];
const size_t *iy = ixs[j];
auto result =
m_m(ix[0], iy[0]) * m_m(ix[1], iy[1]) * m_m(ix[2], iy[2]) +

View File

@@ -34,7 +34,7 @@
#include <numeric>
#if __cpp_lib_format
# include <format>
#include <format>
#endif
/** @file model.hpp
@@ -72,7 +72,7 @@ class structure;
*
* The class atom is a kind of flyweight class. It can be copied
* with low overhead. All data is stored in the underlying mmCIF
* categories but some very often used items are cached in the
* categories but some very often used fields are cached in the
* impl.
*
* It is also possible to have symmetry copies of atoms. They
@@ -106,6 +106,8 @@ class atom
atom_impl(const atom_impl &i) = default;
void prefetch();
int compare(const atom_impl &b) const;
// bool getAnisoU(float anisou[6]) const;
@@ -205,7 +207,7 @@ class atom
/// \brief Copy assignement operator
atom &operator=(const atom &rhs) = default;
/// \brief Return the item named @a name in the _atom_site category for this atom
/// \brief Return the field named @a name in the _atom_site category for this atom
std::string get_property(std::string_view name) const
{
if (not m_impl)
@@ -213,7 +215,7 @@ class 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
/// \brief Return the field named @a name in the _atom_site category for this atom cast to an int
int get_property_int(std::string_view name) const
{
if (not m_impl)
@@ -221,7 +223,7 @@ class atom
return m_impl->get_property_int(name);
}
/// \brief Return the item named @a name in the _atom_site category for this atom cast to a float
/// \brief Return the field named @a name in the _atom_site category for this atom cast to a float
float get_property_float(std::string_view name) const
{
if (not m_impl)
@@ -229,7 +231,7 @@ class atom
return m_impl->get_property_float(name);
}
/// \brief Set value for the item named @a name in the _atom_site category to @a value
/// \brief Set value for the field named @a name in the _atom_site category to @a value
void set_property(const std::string_view name, const std::string &value)
{
if (not m_impl)
@@ -237,7 +239,7 @@ class atom
m_impl->set_property(name, value);
}
/// \brief Set value for the item named @a name in the _atom_site category to @a value
/// \brief Set value for the field 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>
void set_property(const std::string_view name, const T &value)
{
@@ -343,17 +345,12 @@ class atom
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_alt_id() const { return get_property("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
/// Return true if this atom is an alternate
bool is_alternate() const
{
if (auto alt_id = get_label_alt_id(); alt_id.empty() or alt_id == ".")
return false;
return true;
}
bool is_alternate() const { return not get_label_alt_id().empty(); }
/// Convenience method to return a string that might be ID in PDB space
std::string pdb_id() const
@@ -479,8 +476,8 @@ class residue
, m_compound_id(compoundID)
, m_asym_id(asymID)
, m_seq_id(seqID)
, m_pdb_strand_id(authAsymID)
, m_pdb_seq_num(authSeqID)
, m_auth_asym_id(authAsymID)
, m_auth_seq_id(authSeqID)
, m_pdb_ins_code(pdbInsCode)
{
}
@@ -507,9 +504,9 @@ class residue
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
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
const std::string get_auth_asym_id() const { return m_auth_asym_id; } ///< Return the auth_asym_id
const std::string get_auth_seq_id() const { return m_auth_seq_id; } ///< Return the auth_seq_id
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
@@ -538,9 +535,6 @@ class residue
/// \brief Return the atom with atom_id @a atomID
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;
/// \brief Return the list of atoms having ID \a atomID
///
/// This includes all alternate atoms with this ID
@@ -556,9 +550,6 @@ class residue
/// \brief Return true if this residue has alternate atoms
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;
/// \brief Return the list of unique alt ID's present in this residue
std::set<std::string> get_alternate_ids() const;
@@ -578,13 +569,9 @@ class residue
m_seq_id == rhs.m_seq_id and
m_asym_id == rhs.m_asym_id and
m_compound_id == rhs.m_compound_id and
m_pdb_seq_num == rhs.m_pdb_seq_num);
m_auth_seq_id == rhs.m_auth_seq_id);
}
/// @brief Create a new atom and add it to the list
/// @return newly created atom
virtual atom create_new_atom(atom_type inType, const std::string &inAtomID, point inLocation);
protected:
/** @cond */
residue() {}
@@ -592,7 +579,7 @@ class residue
structure *m_structure = nullptr;
std::string m_compound_id, m_asym_id;
int m_seq_id = 0;
std::string m_pdb_strand_id, m_pdb_seq_num, m_pdb_ins_code;
std::string m_auth_asym_id, m_auth_seq_id, m_pdb_ins_code;
std::vector<atom> m_atoms;
/** @endcond */
};
@@ -617,15 +604,12 @@ class monomer : public residue
monomer &operator=(monomer &&rhs);
/// \brief constructor with actual values
monomer(const polymer &polymer, std::size_t index, int seqID, const std::string &authSeqID,
monomer(const polymer &polymer, 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
const monomer &prev() const; // Return previous monomer in polymer
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)
@@ -640,8 +624,8 @@ class monomer : public residue
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
size_t nr_of_chis() const; ///< Return how many torsion angles can be calculated
float chi(size_t i) const; ///< Return torsion angle @a i
bool is_cis() const; ///< Return true if this residue is in a cis conformation
@@ -688,11 +672,9 @@ class monomer : public residue
return m_polymer == rhs.m_polymer and m_index == rhs.m_index;
}
atom create_new_atom(atom_type inType, const std::string &inAtomID, point inLocation) override;
private:
const polymer *m_polymer;
std::size_t m_index;
size_t m_index;
};
// --------------------------------------------------------------------
@@ -712,15 +694,15 @@ class polymer : public std::vector<monomer>
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
std::string get_asym_id() const { return m_asym_id; } ///< Return the asym_id
std::string get_auth_asym_id() const { return m_auth_asym_id; } ///< Return the PDB chain ID, actually
std::string get_entity_id() const { return m_entity_id; } ///< Return the entity_id
private:
structure *m_structure;
std::string m_entity_id;
std::string m_asym_id;
std::string m_pdb_strand_id;
std::string m_auth_asym_id;
};
// --------------------------------------------------------------------
@@ -748,7 +730,7 @@ class sugar : public residue
/**
* @brief Return the sugar number in the glycosylation tree
*
* To store the sugar number, the auth_seq_id item has been overloaded
* To store the sugar number, the auth_seq_id field has been overloaded
* in the specification. But since a sugar number should be, ehm, a number
* and auth_seq_id is specified to contain a string, we do a check here
* to see if it really is a number.
@@ -758,8 +740,8 @@ class sugar : public residue
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)
auto r = std::from_chars(m_auth_seq_id.data(), m_auth_seq_id.data() + m_auth_seq_id.length(), result);
if (r.ec != std::errc())
throw std::runtime_error("The auth_seq_id should be a number for a sugar");
return result;
}
@@ -774,9 +756,9 @@ class sugar : public residue
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
size_t get_link_nr() const
{
std::size_t result = 0;
size_t result = 0;
if (m_link)
result = m_link.get_property_int("auth_seq_id");
return result;
@@ -857,38 +839,19 @@ class branch : public std::vector<sugar>
std::string m_asym_id, m_entity_id;
};
/** @brief Enumeration for controlling atom selection based on occupancy. */
enum class occupancy_policy
// --------------------------------------------------------------------
/// \brief A still very limited set of options for reading structures
enum class StructureOpenOptions
{
/** @brief Include all atoms regardless of their occupancy factor. */
ALL = 0,
/** @brief Select only alternate atoms with the maximum occupancy factor.
* If multiple atoms have the same maximum occupancy, choose the one with the minimum B-factor.
* If multiple atoms share both the maximum occupancy and the minimum B-factor, select the first encountered atom.
*/
MAX = 1,
/** @brief Select only alternate atoms with the minimum occupancy factor.
* Similar to MAX, if multiple atoms have the same minimum occupancy, choose the one with the minimum B-factor.
* If multiple atoms share both the minimum occupancy and the minimum B-factor, select the first encountered atom.
*/
MIN = 2,
/** @brief Exclude all atoms with an occupancy factor greater than zero. */
UNOCCUPIED = 3
SkipHydrogen = 1 << 0 ///< Do not include hydrogen atoms in the structure object
};
struct structure_open_options
/// \brief A way to combine two options. Not very useful as there is only one...
constexpr inline bool operator&(StructureOpenOptions a, StructureOpenOptions b)
{
bool skip_hydrogen = false; ///< Do not include hydrogen atoms in the structure object
bool skip_hetatom = false; ///< Do not include HET atoms in the structure object
bool skip_water = false; ///< Do not include water atoms in the structure object
occupancy_policy occupancy_mode = occupancy_policy::ALL; ///< By default, the occupancy policy is set to occupancy_policy::ALL
std::vector<std::string> asyms; ///< The asyms to load, if empty load all
std::optional<float> min_b_factor; ///< Only load atoms with at least this b_factor
std::optional<float> max_b_factor; ///< Only load atoms with at most this b_factor
};
return static_cast<int>(a) bitand static_cast<int>(b);
}
// --------------------------------------------------------------------
@@ -902,10 +865,10 @@ class structure
{
public:
/// \brief Read the structure from cif::file @a p
structure(file &p, std::size_t modelNr = 1, structure_open_options options = {});
structure(file &p, size_t modelNr = 1, StructureOpenOptions options = {});
/// \brief Load the structure from already parsed mmCIF data in @a db
structure(datablock &db, std::size_t modelNr = 1, structure_open_options options = {});
structure(datablock &db, size_t modelNr = 1, StructureOpenOptions options = {});
/** @cond */
structure(structure &&s) = default;
@@ -918,7 +881,7 @@ class structure
~structure() = default;
/// \brief Return the model number
std::size_t get_model_nr() const { return m_model_nr; }
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; }
@@ -1015,18 +978,18 @@ class structure
/**
* @brief Change residue @a res to a new compound ID optionally
* remapping atoms.
*
*
* A new chem_comp entry as well as an entity is created if needed and
* if the list of @a remappedAtoms is not empty it is used to remap.
*
*
* The array in @a remappedAtoms contains tuples of strings, both
* strings contain an atom_id. The first is the one in the current
* residue and the second is the atom_id that should be used instead.
* If the second string is empty, the atom is removed from the residue.
*
* @param res
* @param newcompound
* @param remappedAtoms
*
* @param res
* @param newcompound
* @param remappedAtoms
*/
void change_residue(residue &res, const std::string &newcompound,
const std::vector<std::tuple<std::string, std::string>> &remappedAtoms);
@@ -1065,16 +1028,6 @@ class structure
/// \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();
@@ -1138,14 +1091,11 @@ class structure
/// \brief emplace the moved atom @a atom
atom &emplace_atom(atom &&atom);
/// \brief Reorder atom_site atoms based on 'natural' ordering
void reorder_atoms();
private:
friend polymer;
friend residue;
void load_atoms_for_model(structure_open_options options);
void load_atoms_for_model(StructureOpenOptions options);
std::string insert_compound(const std::string &compoundID, bool is_entity);
@@ -1157,12 +1107,19 @@ class structure
void remove_sugar(sugar &sugar);
datablock &m_db;
std::size_t m_model_nr;
size_t m_model_nr;
std::vector<atom> m_atoms;
std::vector<std::size_t> m_atom_index;
std::vector<size_t> m_atom_index;
std::list<polymer> m_polymers;
std::list<branch> m_branches;
std::vector<residue> m_non_polymers;
};
// --------------------------------------------------------------------
/// \brief Reconstruct all missing categories for an assumed PDBx file.
/// Some people believe that simply dumping some atom records is enough.
/// \param db The cif::datablock that hopefully contains some valid data
void reconstruct_pdbx(datablock &db);
} // namespace cif::mm

View File

@@ -39,8 +39,6 @@
namespace cif
{
class validator;
// --------------------------------------------------------------------
/** Exception that is thrown when the mmCIF file contains a parsing error */
@@ -75,15 +73,7 @@ class sac_parser
{
public:
/** @cond */
struct iless_op
{
bool operator()(std::string_view a, std::string_view b) const
{
return icompare(a, b) < 0;
}
};
using datablock_index = std::map<std::string, std::size_t, iless_op>;
using datablock_index = std::map<std::string, std::size_t>;
virtual ~sac_parser() = default;
/** @endcond */
@@ -153,9 +143,9 @@ class sac_parser
enum class CIFToken
{
UNKNOWN,
Unknown,
END_OF_FILE,
Eof,
DATA,
LOOP,
@@ -163,24 +153,24 @@ class sac_parser
SAVE_,
SAVE_NAME,
STOP,
ITEM_NAME,
VALUE
Tag,
Value
};
static constexpr const char *get_token_name(CIFToken token)
{
switch (token)
{
case CIFToken::UNKNOWN: return "Unknown";
case CIFToken::END_OF_FILE: return "Eof";
case CIFToken::Unknown: return "Unknown";
case CIFToken::Eof: return "Eof";
case CIFToken::DATA: return "DATA";
case CIFToken::LOOP: return "LOOP";
case CIFToken::GLOBAL: return "GLOBAL";
case CIFToken::SAVE_: return "SAVE";
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::Tag: return "Tag";
case CIFToken::Value: return "Value";
default: return "Invalid token parameter";
}
}
@@ -277,9 +267,9 @@ class sac_parser
QuotedString,
QuotedStringQuote,
UnquotedString,
ItemName,
TextItem,
TextItemNL,
Tag,
TextField,
TextFieldNL,
Reserved,
Value
};
@@ -309,14 +299,6 @@ class sac_parser
class parser : public sac_parser
{
public:
/// \brief constructor, generates data into @a file from @a is using validator @a v
parser(std::istream &is, file &file, const validator *v)
: sac_parser(is)
, m_file(file)
, m_validator(v)
{
}
/// \brief constructor, generates data into @a file from @a is
parser(std::istream &is, file &file)
: sac_parser(is)
@@ -337,7 +319,6 @@ class parser : public sac_parser
file &m_file;
datablock *m_datablock = nullptr;
category *m_category = nullptr;
const validator *m_validator = nullptr;
row_handle m_row;
/** @endcond */

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2023 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
@@ -27,19 +27,16 @@
#pragma once
#include "cif++/file.hpp"
#include "cif++/validate.hpp"
#include <system_error>
/**
* @file pdb.hpp
*
*
* This file presents the API to read and write files in the
* legacy and ancient PDB format.
*
*
* The code works on the basis of best effort since it is
* impossible to have correct round trip fidelity.
*
*
*/
namespace cif::pdb
@@ -84,7 +81,7 @@ inline void write(std::ostream &os, const file &f)
/** @brief Write out the data in @a db to file @a file
* in legacy PDB format or mmCIF format, depending on the
* filename extension.
*
*
* If extension of @a file is *.gz* the resulting file will
* be written in gzip compressed format.
*/
@@ -93,7 +90,7 @@ void write(const std::filesystem::path &file, const datablock &db);
/** @brief Write out the data in @a f to file @a file
* in legacy PDB format or mmCIF format, depending on the
* filename extension.
*
*
* If extension of @a file is *.gz* the resulting file will
* be written in gzip compressed format.
*/
@@ -102,108 +99,6 @@ inline void write(const std::filesystem::path &p, const file &f)
write(p, f.front());
}
// --------------------------------------------------------------------
/**
* @brief Quickly fix a PDB file that lacks some often needed categories
*
* This differs from reconstruct_pdbx which does a much more thorough job
*
* \param pdbx_file The cif::file that hopefully contains some valid data
*/
void fixup_pdbx(file &pdbx_file);
/**
* @brief Quickly fix a PDB file that lacks some often needed categories
*
* This differs from reconstruct_pdbx which does a much more thorough job
*
* \param pdbx_file The cif::file that hopefully contains some valid data
* \param v The validator to use
*/
void fixup_pdbx(file &pdbx_file, const validator &v);
/** \brief Reconstruct all missing categories for an assumed PDBx file.
*
* Some people believe that simply dumping some atom records is enough.
*
* This version uses the audit_conform information and falls back to
* using mmcif_pdbx.dic if not specified.
*
* \param pdbx_file The cif::file that hopefully contains some valid data
* \result Returns true if the resulting file is valid
*/
bool reconstruct_pdbx(file &pdbx_file);
/** \brief Reconstruct all missing categories for an assumed PDBx file.
*
* Some people believe that simply dumping some atom records is enough.
*
* \param pdbx_file The cif::file that hopefully contains some valid data
* \param v The validator to use
* \result Returns true if the resulting file is valid
*/
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
* \param v The validator to use
* \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"));
/** \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.
*
* The dictionary is assumed to be specified in the file or to be the
* default mmcif_pdbx.dic dictionary.
*
* \param file The input file
* \result Returns true if the file was valid and consistent
*/
bool is_valid_pdbx_file(const file &pdbx_file, std::error_code &ec);
/** \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.
*
* \param file The input file
* \param v The validator to use
* \param ec The error_code in case something was wrong
* \result Returns true if the file was valid and consistent
*/
bool is_valid_pdbx_file(const file &pdbx_file, const validator &v,
std::error_code &ec);
// --------------------------------------------------------------------
// Other I/O related routines
@@ -211,7 +106,7 @@ bool is_valid_pdbx_file(const file &pdbx_file, const validator &v,
*
* The line returned should be compatible with the legacy PDB
* format and is e.g. used in the DSSP program.
*
*
* @param data The datablock to use as source for the requested data
* @param truncate_at The maximum length of the line returned
*/
@@ -221,7 +116,7 @@ std::string get_HEADER_line(const datablock &data, std::string::size_type trunca
*
* The line returned should be compatible with the legacy PDB
* format and is e.g. used in the DSSP program.
*
*
* @param data The datablock to use as source for the requested data
* @param truncate_at The maximum length of the line returned
*/
@@ -231,7 +126,7 @@ std::string get_COMPND_line(const datablock &data, std::string::size_type trunca
*
* The line returned should be compatible with the legacy PDB
* format and is e.g. used in the DSSP program.
*
*
* @param data The datablock to use as source for the requested data
* @param truncate_at The maximum length of the line returned
*/
@@ -241,11 +136,12 @@ std::string get_SOURCE_line(const datablock &data, std::string::size_type trunca
*
* The line returned should be compatible with the legacy PDB
* format and is e.g. used in the DSSP program.
*
*
* @param data The datablock to use as source for the requested data
* @param truncate_at The maximum length of the line returned
*/
std::string get_AUTHOR_line(const datablock &data, std::string::size_type truncate_at = 127);
} // namespace cif::pdb
} // namespace pdbx

View File

@@ -35,10 +35,7 @@
#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
#endif
/** \file point.hpp
@@ -662,23 +659,12 @@ struct point_type
return std::make_tuple(std::ref(m_x), std::ref(m_y), std::ref(m_z));
}
#if defined(__cpp_impl_three_way_comparison)
/// \brief a default spaceship operator
constexpr auto operator<=>(const point_type &rhs) const = default;
#else
/// \brief a default equals operator
/// \brief Compare with @a rhs
constexpr bool operator==(const point_type &rhs) const
{
return m_x == rhs.m_x and m_y == rhs.m_y and m_z == rhs.m_z;
}
/// \brief a default not-equals operator
constexpr bool operator!=(const point_type &rhs) const
{
return not operator==(rhs);
}
#endif
// 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
@@ -878,7 +864,7 @@ class spherical_dots
}
/// \brief The number of points
std::size_t size() const { return P; }
size_t size() const { return P; }
/// \brief Access a point by index
const point operator[](uint32_t inIx) const { return m_points[inIx]; }

View File

@@ -51,7 +51,7 @@
* std::string name = rh["label_atom_id"].as<std::string>();
*
* // by index:
* uint16_t ix = atom_site.get_item_ix("label_atom_id");
* uint16_t ix = atom_site.get_column_ix("label_atom_id");
* assert(rh[ix].as<std::string() == name);
* @endcode
*
@@ -85,17 +85,17 @@ namespace detail
template <typename... C>
struct get_row_result
{
static constexpr std::size_t N = sizeof...(C);
static constexpr size_t N = sizeof...(C);
get_row_result(const row_handle &r, std::array<uint16_t, N> &&items)
get_row_result(const row_handle &r, std::array<uint16_t, N> &&columns)
: m_row(r)
, m_items(std::move(items))
, m_columns(std::move(columns))
{
}
const item_handle operator[](uint16_t ix) const
{
return m_row[m_items[ix]];
return m_row[m_columns[ix]];
}
template <typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0>
@@ -104,14 +104,14 @@ namespace detail
return get<Ts...>(std::index_sequence_for<Ts...>{});
}
template <typename... Ts, std::size_t... Is>
template <typename... Ts, size_t... Is>
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_columns[Is]].template as<Ts>()... };
}
const row_handle &m_row;
std::array<uint16_t, N> m_items;
std::array<uint16_t, N> m_columns;
};
// we want to be able to tie some variables to a get_row_result, for this we use tiewraps
@@ -208,7 +208,6 @@ class row_handle
friend class category;
friend class category_index;
friend class row_initializer;
template <typename, typename...> friend class iterator_impl;
row_handle() = default;
@@ -245,70 +244,63 @@ class row_handle
return not empty();
}
/// \brief return a cif::item_handle to the item in item @a item_ix
item_handle operator[](uint16_t item_ix)
/// \brief return a cif::item_handle to the item in column @a column_ix
item_handle operator[](uint16_t column_ix)
{
return empty() ? item_handle::s_null_item : item_handle(item_ix, *this);
return empty() ? item_handle::s_null_item : item_handle(column_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
/// \brief return a const cif::item_handle to the item in column @a column_ix
const item_handle operator[](uint16_t column_ix) const
{
return empty() ? item_handle::s_null_item : item_handle(item_ix, const_cast<row_handle &>(*this));
return empty() ? item_handle::s_null_item : item_handle(column_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)
/// \brief return a cif::item_handle to the item in the column named @a column_name
item_handle operator[](std::string_view column_name)
{
return empty() ? item_handle::s_null_item : item_handle(add_item(item_name), *this);
return empty() ? item_handle::s_null_item : item_handle(add_column(column_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
/// \brief return a const cif::item_handle to the item in the column named @a column_name
const item_handle operator[](std::string_view column_name) const
{
return empty() ? item_handle::s_null_item : item_handle(get_item_ix(item_name), const_cast<row_handle &>(*this));
return empty() ? item_handle::s_null_item : item_handle(get_column_ix(column_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
/// to assign the values for the columns @a columns
template <typename... C>
auto get(C... items) const
auto get(C... columns) const
{
return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
return detail::get_row_result<C...>(*this, { get_column_ix(columns)... });
}
/// \brief Return a tuple of values of types @a Ts for the items @a items
/// \brief Return a tuple of values of types @a Ts for the columns @a columns
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
std::tuple<Ts...> get(C... columns) const
{
return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
return detail::get_row_result<Ts...>(*this, { get_column_ix(columns)... });
}
/// \brief Get the value of item @a item cast to type @a T
/// \brief Get the value of column @a column cast to type @a T
template <typename T>
T get(const char *item) const
T get(const char *column) const
{
return operator[](get_item_ix(item)).template as<T>();
return operator[](get_column_ix(column)).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
/// \brief assign each of the columns 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
/** \brief assign the value @a value to the column 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
* That means that if column @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.
*
@@ -318,13 +310,13 @@ class row_handle
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
{
assign(add_item(name), value, updateLinked, validate);
assign(add_column(name), value, updateLinked, validate);
}
/** \brief assign the value @a value to item at index @a item
/** \brief assign the value @a value to column at index @a column
*
* If updateLinked it true, linked records are updated as well.
* That means that if item @a item is part of the link definition
* That means that if column @a column 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.
*
@@ -332,7 +324,7 @@ class row_handle
* 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);
void assign(uint16_t column, 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; }
@@ -341,10 +333,10 @@ class row_handle
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 get_column_ix(std::string_view name) const;
std::string_view get_column_name(uint16_t ix) const;
uint16_t add_item(std::string_view name);
uint16_t add_column(std::string_view name);
row *get_row()
{
@@ -361,7 +353,7 @@ class row_handle
assign(i.name(), i.value(), updateLinked);
}
void swap(uint16_t item, row_handle &r);
void swap(uint16_t column, row_handle &r);
category *m_category = nullptr;
row *m_row = nullptr;

View File

@@ -300,7 +300,7 @@ namespace literals
* @endcode
*
*/
inline sym_op operator""_symop(const char *text, std::size_t length)
inline sym_op operator""_symop(const char *text, size_t length)
{
return sym_op({ text, length });
}
@@ -464,7 +464,7 @@ class spacegroup : public std::vector<transformation>
private:
int m_nr;
std::size_t m_index;
size_t m_index;
};
// --------------------------------------------------------------------

View File

@@ -317,7 +317,7 @@ inline char tolower(int ch)
// --------------------------------------------------------------------
/** \brief return a tuple consisting of the category and item name for @a item_name
/** \brief return a tuple consisting of the category and item name for @a tag
*
* The category name is stripped of its leading underscore character.
*
@@ -325,19 +325,7 @@ inline char tolower(int ch)
* cif 1.0 formatted data.
*/
[[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.
*
* If no dot character was found, the category name is empty. That's for
* cif 1.0 formatted data.
*/
std::tuple<std::string, std::string> split_item_name(std::string_view item_name);
std::tuple<std::string, std::string> split_tag_name(std::string_view tag);
// --------------------------------------------------------------------
@@ -352,38 +340,272 @@ std::string cif_id_for_number(int number);
* a dynamic programming approach to get the most efficient filling of
* the space.
*/
std::vector<std::string> word_wrap(const std::string &text, std::size_t width);
std::vector<std::string> word_wrap(const std::string &text, 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;
long double f = 1;
int exponent_sign = 1;
int exponent = 0;
bool done = false;
while (not done and result.ec == std::errc())
{
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');
f /= 10;
}
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 (result.ec == std::errc())
{
long double v = f * 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>
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<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);
}
/// @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);
}
};
template <typename T, typename = void>
struct ff_charconv;
/// \brief helper to find a from_chars function
template <typename 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);
};
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
/**
* @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 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);
}
using selected_charconv = typename std::conditional_t<std_experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, my_charconv<T>>;
} // namespace cif

View File

@@ -53,7 +53,6 @@
#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
#endif
@@ -114,12 +113,16 @@ namespace colour
/**
* @brief Struct for delimited strings.
*/
template <typename StringType>
struct coloured_string_t
{
static_assert(std::is_reference_v<StringType> or std::is_pointer_v<StringType>,
"String type must be pointer or reference");
/**
* @brief Construct a new coloured string t object
*/
coloured_string_t(std::string_view s, colour_type fc, colour_type bc, style_type st)
coloured_string_t(StringType s, colour_type fc, colour_type bc, style_type st)
: m_str(s)
, m_fore_colour(static_cast<int>(fc) + 30)
, m_back_colour(static_cast<int>(bc) + 40)
@@ -149,14 +152,12 @@ namespace colour
<< cs.m_str
<< "\033[0m";
}
else
os << cs.m_str;
return os;
}
/// @cond
std::string_view m_str;
StringType m_str;
int m_fore_colour, m_back_colour;
int m_style;
/// @endcond
@@ -190,13 +191,39 @@ namespace colour
* @param st Text style to use
*/
template <typename T>
requires std::is_assignable_v<std::string_view, T>
inline auto coloured(T str,
template <typename char_type>
inline auto coloured(const char_type *str,
colour::colour_type fg, colour::colour_type bg = colour::colour_type::none,
colour::style_type st = colour::style_type::regular)
{
return colour::detail::coloured_string_t(str, fg, bg, st);
return colour::detail::coloured_string_t<const char_type *>(str, fg, bg, st);
}
/// @brief Manipulator for coloured strings.
template <typename char_type, typename traits_type, typename allocator_type>
inline auto coloured(const std::basic_string<char_type, traits_type, allocator_type> &str,
colour::colour_type fg, colour::colour_type bg = colour::colour_type::none,
colour::style_type st = colour::style_type::regular)
{
return colour::detail::coloured_string_t<const std::basic_string<char_type, traits_type, allocator_type> &>(str, fg, bg, st);
}
/// @brief Manipulator for coloured strings.
template <typename char_type, typename traits_type, typename allocator_type>
inline auto coloured(std::basic_string<char_type, traits_type, allocator_type> &str,
colour::colour_type fg, colour::colour_type bg = colour::colour_type::none,
colour::style_type st = colour::style_type::regular)
{
return colour::detail::coloured_string_t<std::basic_string<char_type, traits_type, allocator_type> &>(str, fg, bg, st);
}
/// @brief Manipulator for coloured strings.
template <typename char_type, typename traits_type>
inline auto coloured(std::basic_string_view<char_type, traits_type> &str,
colour::colour_type fg, colour::colour_type bg = colour::colour_type::none,
colour::style_type st = colour::style_type::regular)
{
return colour::detail::coloured_string_t<std::basic_string_view<char_type, traits_type> &>(str, fg, bg, st);
}
// --------------------------------------------------------------------
@@ -296,11 +323,6 @@ class progress_bar
*/
void message(const std::string &inMessage);
/**
* @brief Flush the progress bar to the output stream
*/
void flush();
private:
progress_bar(const progress_bar &) = delete;
progress_bar &operator=(const progress_bar &) = delete;

View File

@@ -26,15 +26,11 @@
#pragma once
#include "cif++/category.hpp"
#include "cif++/text.hpp"
#include <cassert>
#include <filesystem>
#include <list>
#include <mutex>
#include <optional>
#include <system_error>
#include <utility>
/**
@@ -50,152 +46,30 @@
namespace cif
{
class category;
struct category_validator;
// --------------------------------------------------------------------
// New: error_code
/**
* @enum validation_error
*
* @brief A stronly typed class containing the error codes reported by @ref cif::validator and friends
*/
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 */
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 */
incorrect_item_validator, /**< Incorrectly specified validator for item */
missing_mandatory_items, /**< Missing mandatory items */
missing_key_items, /**< An index could not be constructed due to missing key items */
item_not_allowed_in_category, /**< Requested item allowed in category according to dictionary */
empty_file, /**< The file contains no datablocks */
empty_datablock, /**< The datablock contains no categories */
empty_category, /**< The category is empty */
not_valid_pdbx, /**< The file is not a valid PDBx file */
};
/**
* @brief The implementation for @ref validation_category error messages
* @brief The exception thrown when a validation error occurs
*
*/
class validation_category_impl : public std::error_category
class validation_error : public std::exception
{
public:
/**
* @brief User friendly name
*
* @return const char*
*/
/// @brief Constructor
validation_error(const std::string &msg);
const char *name() const noexcept override
{
return "cif::validation";
}
/// @brief Constructor
validation_error(const std::string &cat, const std::string &item,
const std::string &msg);
/**
* @brief Provide the error message as a string for the error code @a ev
*
* @param ev The error code
* @return std::string
*/
/// @brief The description of the error
const char *what() const noexcept { return m_msg.c_str(); }
std::string message(int ev) const override
{
switch (static_cast<validation_error>(ev))
{
case validation_error::value_does_not_match_rx:
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::not_a_known_primitive_type:
return "The type is not a known primitive type";
case validation_error::undefined_category:
return "Category has no definition in the dictionary";
case validation_error::unknown_item:
return "Item is not defined to be part of the category";
case validation_error::incorrect_item_validator:
return "Incorrectly specified validator for item";
case validation_error::missing_mandatory_items:
return "Missing mandatory items";
case validation_error::missing_key_items:
return "An index could not be constructed due to missing key items";
case validation_error::item_not_allowed_in_category:
return "Requested item not allowed in category according to dictionary";
case validation_error::empty_file:
return "The file contains no datablocks";
case validation_error::empty_datablock:
return "The datablock contains no categories";
case validation_error::empty_category:
return "The category is empty";
case validation_error::not_valid_pdbx:
return "The file is not a valid PDBx file";
default:
assert(false);
return "unknown error code";
}
}
/**
* @brief Return whether two error codes are equivalent, always false in this case
*
*/
bool equivalent(const std::error_code & /*code*/, int /*condition*/) const noexcept override
{
return false;
}
};
/**
* @brief Return the implementation for the validation_category
*
* @return std::error_category&
*/
inline std::error_category &validation_category()
{
static validation_category_impl instance;
return instance;
}
inline std::error_code make_error_code(validation_error e)
{
return std::error_code(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());
}
// --------------------------------------------------------------------
class validation_exception : public std::runtime_error
{
public:
validation_exception(validation_error err)
: validation_exception(make_error_code(err))
{
}
validation_exception(validation_error err, std::string_view category)
: validation_exception(make_error_code(err), category)
{
}
validation_exception(validation_error err, std::string_view category, std::string_view item)
: validation_exception(make_error_code(err), category, item)
{
}
validation_exception(std::error_code ec);
validation_exception(std::error_code ec, std::string_view category);
validation_exception(std::error_code ec, std::string_view category, std::string_view item);
/// @cond
std::string m_msg;
/// @endcond
};
// --------------------------------------------------------------------
@@ -211,9 +85,6 @@ enum class DDL_PrimitiveType
/// @brief Return the DDL_PrimitiveType encoded in @a s
DDL_PrimitiveType map_to_primitive_type(std::string_view s);
/// @brief Return the DDL_PrimitiveType encoded in @a s, error reporting variant
DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec) noexcept;
struct regex_impl;
/**
@@ -230,39 +101,38 @@ struct type_validator
{
std::string m_name; ///< The name of the type
DDL_PrimitiveType m_primitive_type; ///< The primitive_type of the type
std::shared_ptr<regex_impl> m_rx; ///< The regular expression for the type
regex_impl *m_rx; ///< The regular expression for the type
type_validator() = delete;
/// @brief Constructor
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 &) = delete;
/// @brief Move constructor
/// @brief Copy constructor
type_validator(type_validator &&rhs)
: m_name(std::move(rhs.m_name))
, m_primitive_type(rhs.m_primitive_type)
{
swap(*this, rhs);
m_rx = std::exchange(rhs.m_rx, nullptr);
}
type_validator &operator=(const type_validator &) = delete;
/// @brief Move constructor
type_validator &operator=(type_validator rhs)
type_validator &operator=(type_validator &&rhs)
{
swap(*this, rhs);
m_name = std::move(rhs.m_name);
m_primitive_type = rhs.m_primitive_type;
m_rx = std::exchange(rhs.m_rx, nullptr);
return *this;
}
/// @brief Destructor
~type_validator();
friend void swap(type_validator &a, type_validator &b)
{
std::swap(a.m_name, b.m_name);
std::swap(a.m_primitive_type, b.m_primitive_type);
std::swap(a.m_rx, b.m_rx);
}
/// @brief Return the sorting order
bool operator<(const type_validator &rhs) const
{
@@ -276,26 +146,6 @@ struct type_validator
int compare(std::string_view a, std::string_view b) const;
};
/** @brief Item alias, items can be renamed over time
*/
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(const item_alias &) = default;
item_alias &operator=(const item_alias &) = default;
std::string m_name; ///< The alias_name
std::string m_dict; ///< The dictionary in which it was known
std::string m_vers; ///< The version of the dictionary
};
/**
* @brief An item_validator binds a type_validator to an item in
* a category along with other information found in the dictionary.
@@ -307,32 +157,28 @@ struct item_alias
*/
struct item_validator
{
std::string m_item_name; ///< The item name
bool m_mandatory; ///< Flag indicating this item is mandatory
const type_validator *m_type; ///< The type for this item
cif::iset m_enums; ///< If filled, the set of allowed values
std::string m_default; ///< If filled, a default value for this item
std::string m_category; ///< The category this item_validator belongs to
std::vector<item_alias> m_aliases; ///< The aliases for this item
std::string m_tag; ///< The item name
bool m_mandatory; ///< Flag indicating this item is mandatory
const type_validator *m_type; ///< The type for this item
cif::iset m_enums; ///< If filled, the set of allowed values
std::string m_default; ///< If filled, a default value for this item
category_validator *m_category = nullptr; ///< The category_validator this item_validator belongs to
/// @brief Compare based on the name
bool operator<(const item_validator &rhs) const
{
return icompare(m_item_name, rhs.m_item_name) < 0;
return icompare(m_tag, rhs.m_tag) < 0;
}
/// @brief Compare based on the name
bool operator==(const item_validator &rhs) const
{
return iequals(m_item_name, rhs.m_item_name);
return iequals(m_tag, rhs.m_tag);
}
/// @brief Validate the value in @a value for this item
/// Will throw a std::system_error exception if it fails
/// Will throw a validation_error exception if it fails
void operator()(std::string_view value) const;
/// @brief A more gentle version of value validation
bool validate_value(std::string_view value, std::error_code &ec) const noexcept;
};
/**
@@ -345,8 +191,8 @@ 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
cif::iset m_groups; ///< The category groups this category belongs to
cif::iset m_mandatory_fields; ///< The mandatory fields for this category
std::set<item_validator> m_item_validators; ///< The item validators for the items in this category
/// @brief return true if this category sorts before @a rhs
@@ -356,13 +202,10 @@ struct category_validator
}
/// @brief Add item_validator @a v to the list of item validators
void add_item_validator(item_validator &&v);
void addItemValidator(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;
/// @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;
/// @brief Return the item_validator for item @a tag, may return nullptr
const item_validator *get_validator_for_item(std::string_view tag) const;
};
/**
@@ -400,48 +243,24 @@ class validator
*
* @param name The name of the underlying dictionary
*/
validator()
: m_audit_conform("audit_conform")
validator(std::string_view name)
: m_name(name)
{
}
/**
* @brief Construct a new validator object
*
* @param name The name of the underlying dictionary
* @param is The data to parse
*/
validator(std::istream &is)
: m_audit_conform("audit_conform")
{
parse(is);
}
/// @brief destructor
~validator() = default;
validator(const validator &rhs);
validator(const validator &rhs) = delete;
validator &operator=(const validator &rhs) = delete;
/// @brief move constructor
validator(validator &&rhs)
{
swap(*this, rhs);
}
validator(validator &&rhs) = default;
validator &operator=(validator rhs)
{
swap(*this, rhs);
return *this;
}
friend void swap(validator &a, validator &b) noexcept;
/// @brief move assignment operator
validator &operator=(validator &&rhs) = default;
friend class dictionary_parser;
friend class validator_factory;
/// @brief Parse dictionary in @a is and put content in this validator, optionally extending it
/// @param is The stream containing a valid cif dictionary
void parse(std::istream &is);
/// @brief Add type_validator @a v to the list of type validators
void add_type_validator(type_validator &&v);
@@ -465,41 +284,20 @@ class validator
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
{
report_error(make_error_code(err), fatal);
}
void report_error(const std::string &msg, bool fatal) const;
/// @brief Bottleneck function to report an error in validation
void report_error(std::error_code ec, bool fatal = true) const;
const std::string &name() const { return m_name; } ///< Get the name of this validator
void set_name(const std::string &name) { m_name = name; } ///< Set the name of this validator
/// @brief Bottleneck function to report an error in validation
void report_error(validation_error err, std::string_view category,
std::string_view item, bool fatal = true) const
{
report_error(make_error_code(err), category, item, fatal);
}
/// @brief Bottleneck function to report an error in validation
void report_error(std::error_code ec, std::string_view category,
std::string_view item, bool fatal = true) const;
/// @brief Write out the audit_conform data for this validator
/// @param audit_conform
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;
/// @brief Add info
void append_audit_conform(const std::string &name, const std::optional<std::string> &version);
const std::string &version() const { return m_version; } ///< Get the version of this validator
void set_version(const std::string &version) { m_version = version; } ///< Set the version of this validator
private:
// name is fully qualified here:
item_validator *get_validator_for_item(std::string_view name) const;
category m_audit_conform;
std::string m_name;
std::string m_version;
bool m_strict = false;
std::set<type_validator> m_type_validators;
std::set<category_validator> m_category_validators;
@@ -519,27 +317,16 @@ class validator_factory
/// @brief Return the singleton instance
static validator_factory &instance();
/// @brief Return validator with info recorded in @a audit_conform
const validator &get(const category &audit_conform);
/// @brief Return the validator with name @a dictionary_name
const validator &operator[](std::string_view dictionary_name);
/// @brief Return the single-file validator with name @a dictionary_name
const validator &get(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);
/// @brief Add validator @a v to the list of known validators
const validator &add(validator &&v)
{
std::unique_lock lock(m_mutex);
return m_validators.emplace_back(std::move(v));
}
/// @brief Construct a new validator with name @a name from the data in @a is
const validator &construct_validator(std::string_view name, std::istream &is);
private:
validator_factory() = default;
// --------------------------------------------------------------------
/// @brief Construct a new validator with name @a name from the data in @a is with at least version @a version if specified
validator construct_validator(std::string_view name, std::optional<std::string> version);
validator_factory() = default;
std::mutex m_mutex;
std::list<validator> m_validators;

View File

@@ -1,316 +0,0 @@
# 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()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -136,17 +136,14 @@ compound::compound(cif::datablock &db)
if (chemComp.size() != 1)
throw std::runtime_error("Invalid compound file, chem_comp should contain a single row");
std::string one_letter_code;
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();
cif::tie(m_id, m_name, m_type, m_formula, m_formula_weight, m_formal_charge) =
chemComp.front().get("id", "name", "type", "formula", "formula_weight", "pdbx_formal_charge");
// The name should not contain newline characters since that triggers validation errors later on
cif::replace_all(m_name, "\n", "");
m_group = "non-polymer";
auto &chemCompAtom = db["chem_comp_atom"];
for (auto row : chemCompAtom)
{
@@ -156,9 +153,6 @@ compound::compound(cif::datablock &db)
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.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);
m_atoms.push_back(std::move(atom));
}
@@ -169,14 +163,59 @@ compound::compound(cif::datablock &db)
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");
if (valueOrder.empty())
bond.type = bond_type::sing;
else
bond.type = parse_bond_type_from_string(valueOrder);
m_bonds.push_back(std::move(bond));
}
}
compound::compound(cif::datablock &db, const std::string &id, const std::string &name, const std::string &type, const std::string &group)
: m_id(id)
, m_name(name)
, m_type(type)
, m_group(group)
{
auto &chemCompAtom = db["chem_comp_atom"];
for (auto row : chemCompAtom)
{
compound_atom atom;
std::string type_symbol;
cif::tie(atom.id, type_symbol, atom.charge, atom.x, atom.y, atom.z) =
row.get("atom_id", "type_symbol", "charge", "x", "y", "z");
atom.type_symbol = atom_type_traits(type_symbol).type();
m_formal_charge += atom.charge;
m_formula_weight += atom_type_traits(atom.type_symbol).weight();
m_atoms.push_back(std::move(atom));
}
auto &chemCompBond = db["chem_comp_bond"];
for (auto row : chemCompBond)
{
compound_bond bond;
std::string btype;
cif::tie(bond.atom_id[0], bond.atom_id[1], btype, bond.aromatic) = row.get("atom_id_1", "atom_id_2", "type", "aromatic");
using cif::iequals;
if (iequals(btype, "single"))
bond.type = bond_type::sing;
else if (iequals(btype, "double"))
bond.type = bond_type::doub;
else if (iequals(btype, "triple"))
bond.type = bond_type::trip;
else if (iequals(btype, "deloc") or iequals(btype, "aromat") or iequals(btype, "aromatic"))
bond.type = bond_type::delo;
else
{
if (cif::VERBOSE > 0)
std::cerr << "Unimplemented chem_comp_bond.type " << btype << " in " << id << '\n';
bond.type = bond_type::sing;
}
m_bonds.push_back(std::move(bond));
}
}
compound_atom compound::get_atom_by_atom_id(const std::string &atom_id) const
{
compound_atom result = {};
@@ -221,23 +260,12 @@ float compound::bond_length(const std::string &atomId_1, const std::string &atom
auto a = get_atom_by_atom_id(atomId_1);
auto b = get_atom_by_atom_id(atomId_2);
result = distance(point{ a.x, a.y, a.z }, point{ b.x, b.y, b.z });
result = distance(point{a.x, a.y, a.z}, point{b.x, b.y, b.z});
}
return result;
}
// --------------------------------------------------------------------
bool compound::is_peptide() const
{
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");
}
// --------------------------------------------------------------------
// known amino acids and bases
@@ -288,7 +316,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
compound_factory_impl();
compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next);
virtual ~compound_factory_impl()
~compound_factory_impl()
{
for (auto c : m_compounds)
delete c;
@@ -296,17 +324,41 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
compound *get(std::string id)
{
cif::to_upper(id);
std::shared_lock lock(mMutex);
compound *result = nullptr;
// walk the list, see if any of the implementations has the compound already
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
{
result = impl->create(id);
if (result != nullptr)
for (auto cmp : impl->m_compounds)
{
if (iequals(cmp->id(), id))
{
result = cmp;
break;
}
}
if (result)
break;
}
if (result == nullptr and m_missing.count(id) == 0)
{
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
{
result = impl->create(id);
if (result != nullptr)
break;
}
if (result == nullptr)
m_missing.insert(id);
}
return result;
}
@@ -321,15 +373,13 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
os << "CCD components.cif resource\n";
else
os << "CCD components file: " << std::quoted(m_file.string()) << '\n';
if (m_next)
m_next->describe(os);
}
protected:
compound_factory_impl(std::shared_ptr<compound_factory_impl> next);
virtual compound *create(const std::string &id);
private:
compound *create(const std::string &id);
std::shared_timed_mutex mMutex;
@@ -337,7 +387,7 @@ class compound_factory_impl : public std::enable_shared_from_this<compound_facto
cif::parser::datablock_index m_index;
std::vector<compound *> m_compounds;
cif::iset m_missing;
std::set<std::string> m_missing;
std::shared_ptr<compound_factory_impl> m_next;
};
@@ -345,27 +395,14 @@ compound_factory_impl::compound_factory_impl()
{
}
compound_factory_impl::compound_factory_impl(std::shared_ptr<compound_factory_impl> next)
: m_next(next)
{
}
compound_factory_impl::compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next)
: compound_factory_impl(next)
: m_file(file)
, m_next(next)
{
m_file = file;
}
compound *compound_factory_impl::create(const std::string &id)
{
// shortcut
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())
return *i;
compound *result = nullptr;
std::unique_ptr<std::istream> ccd;
@@ -397,7 +434,7 @@ compound *compound_factory_impl::create(const std::string &id)
m_index = parser.index_datablocks();
if (cif::VERBOSE > 1)
std::cout << " done\n";
std::cout << " done" << std::endl;
// reload the resource, perhaps this should be improved...
if (m_file.empty())
@@ -420,7 +457,7 @@ compound *compound_factory_impl::create(const std::string &id)
parser.parse_single_datablock(id, m_index);
if (cif::VERBOSE > 1)
std::cout << " done\n";
std::cout << " done" << std::endl;
if (not file.empty())
{
@@ -434,170 +471,6 @@ compound *compound_factory_impl::create(const std::string &id)
}
}
if (result == nullptr)
m_missing.insert(id);
return result;
}
// --------------------------------------------------------------------
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)
: compound_factory_impl(next)
, m_local_file(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;
};
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())
return *i;
compound *result = nullptr;
for (auto &db : m_local_file)
{
if (db.name() == id)
{
auto chem_comp = db.get("chem_comp");
if (not chem_comp)
break;
try
{
const auto &[id2, name, threeLetterCode, group] =
chem_comp->front().get<std::string, std::string, std::string, std::string>("id", "name", "three_letter_code", "group");
if (id == id2)
result = construct_compound(db, id, name, threeLetterCode, group);
else
throw std::runtime_error("Compound ID's don't match: id 1=" + id + ", id 2=" + id2);
}
catch (const std::exception &ex)
{
std::throw_with_nested(std::runtime_error("Error loading compound " + id));
}
break;
}
}
if (result == nullptr)
m_missing.insert(id);
return result;
}
compound *local_compound_factory_impl::construct_compound(const datablock &rdb, const std::string &id,
const std::string &name, const std::string &three_letter_code, const std::string &group)
{
cif::datablock db(id);
float formula_weight = 0;
int formal_charge = 0;
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>>(
"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"))
{
auto atom = cif::atom_type_traits(type_symbol);
formula_weight += atom.weight();
formula_data[type_symbol] += 1;
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++ }
});
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"))
{
std::string value_order("SING");
if (cif::iequals(type, "single") or cif::iequals(type, "sing"))
value_order = "SING";
else if (cif::iequals(type, "double") or cif::iequals(type, "doub"))
value_order = "DOUB";
else if (cif::iequals(type, "triple") or cif::iequals(type, "trip"))
value_order = "TRIP";
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++ }
});
}
db.emplace_back(rdb["pdbx_chem_comp_descriptor"]);
std::string formula;
for (bool first = true; const auto &[symbol, count]: formula_data)
{
if (std::exchange(first, false))
formula += ' ';
formula += symbol;
if (count > 1)
formula += std::to_string(count);
}
std::string type;
if (cif::iequals(group, "peptide") or cif::iequals(group, "l-peptide") or cif::iequals(group, "l-peptide linking"))
type = "L-PEPTIDE LINKING";
else if (cif::iequals(group, "dna"))
type = "DNA LINKING";
else if (cif::iequals(group, "rna"))
type = "RNA LINKING";
else
type = "NON-POLYMER";
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 }
});
std::shared_lock lock(mMutex);
auto result = new compound(db);
m_compounds.push_back(result);
return result;
}
@@ -680,111 +553,57 @@ void compound_factory::push_dictionary(const fs::path &inDictFile)
}
}
void compound_factory::push_dictionary(const cif::file &inDictFile)
{
try
{
m_impl.reset(new local_compound_factory_impl(inDictFile, m_impl));
}
catch (const std::exception &)
{
std::throw_with_nested(std::runtime_error("Error loading dictionary from local mmCIF file"));
}
}
void compound_factory::pop_dictionary()
{
if (m_impl)
m_impl = m_impl->next();
}
const compound *compound_factory::create(std::string_view id)
const compound *compound_factory::create(std::string id)
{
auto result = m_impl ? m_impl->get(std::string{ id }) : nullptr;
auto result = m_impl ? m_impl->get(id) : nullptr;
if (not result)
report_missing_compound(id);
return result;
}
bool compound_factory::is_known_peptide(const std::string &res_name) const
bool compound_factory::is_known_peptide(const std::string &resName) const
{
return kAAMap.count(res_name) > 0;
return kAAMap.count(resName) > 0;
}
bool compound_factory::is_known_base(const std::string &res_name) const
bool compound_factory::is_known_base(const std::string &resName) const
{
return kBaseMap.count(res_name) > 0;
return kBaseMap.count(resName) > 0;
}
/// Return whether @a res_name is a peptide
bool compound_factory::is_peptide(std::string_view res_name) const
void compound_factory::report_missing_compound(const std::string &compound_id)
{
bool result = is_std_peptide(res_name);
if (not result and m_impl)
{
auto compound = const_cast<compound_factory&>(*this).create(res_name);
result = compound != nullptr and compound->is_peptide();
}
return result;
}
/// Return whether @a res_name is a base
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);
result = compound != nullptr and compound->is_base();
}
return result;
}
/// Return whether @a res_name is one of the standard peptides
bool compound_factory::is_std_peptide(std::string_view res_name) const
{
return kAAMap.count(std::string{ res_name }) > 0;
}
/// Return whether @a res_name is one of the standard bases
bool compound_factory::is_std_base(std::string_view res_name) const
{
return kBaseMap.count(std::string{ res_name }) > 0;
}
/// Return whether @a res_name is a monomer (either base or peptide)
bool compound_factory::is_monomer(std::string_view res_name) const
{
return is_peptide(res_name) or is_base(res_name);
}
void compound_factory::report_missing_compound(std::string_view compound_id)
{
if (std::exchange(m_report_missing, false))
static bool s_reported = false;
if (std::exchange(s_reported, true) == false)
{
using namespace cif::colour;
std::clog << "\n"
<< cif::coloured("Configuration error:", white, red) << "\n\n"
std::clog << "\n" << cif::coloured("Configuration error:", white, red) << "\n\n"
<< "The attempt to retrieve compound information for " << std::quoted(compound_id) << " failed.\n\n"
<< "This information is searched for in a CCD file called components.cif or\n"
<< "components.cif.gz which should be located in one of the following directories:\n\n";
cif::list_data_directories(std::clog);
std::clog << "\n(Note that you can add a directory to the search paths by setting the \n"
<< "LIBCIFPP_DATA_DIR environmental variable)\n\n";
#if defined(CACHE_DIR)
#if defined(CACHE_DIR)
std::clog << "On Linux an optional cron script might have been installed that automatically updates\n"
<< "components.cif and mmCIF dictionary files. This script only works when the file\n"
<< "libcifpp.conf contains an uncommented line with the text:\n\n"
<< "update=true\n\n"
<< "If you do not have a working cron script, you can manually update the files\n"
<< "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://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic\n\n";
<< "curl -o " << CACHE_DIR << "/components.cif https://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz\n"
<< "curl -o " << CACHE_DIR << "/mmcif_pdbx.dic https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz\n"
<< "curl -o " << CACHE_DIR << "/mmcif_ma.dic https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic\n\n";
#endif
if (m_impl)
@@ -794,9 +613,9 @@ void compound_factory::report_missing_compound(std::string_view compound_id)
}
else
std::clog << "No compound factory objects are created since none of the data sources is found.\n";
cif::list_file_resources(std::clog);
std::clog.flush();
}
}

View File

@@ -24,24 +24,23 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cif++/condition.hpp"
#include "cif++/category.hpp"
#include "cif++/validate.hpp"
#include "cif++/condition.hpp"
namespace cif
{
iset get_category_items(const category &cat)
iset get_category_fields(const category &cat)
{
return cat.key_items();
return cat.key_fields();
}
uint16_t get_item_ix(const category &cat, std::string_view col)
uint16_t get_column_ix(const category &cat, std::string_view col)
{
return cat.get_item_ix(col);
return cat.get_column_ix(col);
}
bool is_item_type_uchar(const category &cat, std::string_view col)
bool is_column_type_uchar(const category &cat, std::string_view col)
{
bool result = false;
@@ -61,78 +60,17 @@ bool is_item_type_uchar(const category &cat, std::string_view col)
namespace detail
{
// // index lookup
// struct index_lookup_condition_impl : public condition_impl
// {
// index_lookup_condition_impl(row_initializer &&key_values)
// : m_key_values(std::move(key_values))
// {
// }
//
// condition_impl *prepare(const category &c) override
// {
// m_single_hit = c[m_key_values];
// return this;
// }
//
// bool test(row_handle r) const override
// {
// return m_single_hit == r;
// }
//
// void str(std::ostream &os) const override
// {
// os << "index scan";
// }
//
// 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(index_lookup_condition_impl))
// {
// auto ri = static_cast<const index_lookup_condition_impl *>(rhs);
// if (m_single_hit or ri->m_single_hit)
// 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_key_values == ri->m_key_values;
// }
// return this == rhs;
// }
//
// row_initializer m_key_values;
// row_handle m_single_hit;
// };
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);
m_item_ix = c.get_column_ix(m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
if (c.get_cat_validator() != nullptr and
c.key_item_indices().contains(m_item_ix) and
c.key_item_indices().size() == 1)
c.key_field_indices().contains(m_item_ix) and
c.key_field_indices().size() == 1)
{
m_single_hit = c[{ { m_item_name, m_value } }];
}
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 } }];
m_single_hit = c[{ { m_item_tag, m_value } }];
}
return this;
@@ -146,8 +84,7 @@ namespace detail
{
auto &cs = (*s)->m_sub;
if (find_if(cs.begin(), cs.end(), [c](const condition_impl *i)
{ return i->equals(c); }) == cs.end())
if (find_if(cs.begin(), cs.end(), [c](const condition_impl *i) { return i->equals(c); }) == cs.end())
{
result = false;
break;
@@ -164,35 +101,27 @@ namespace detail
auto first = subs.front();
auto &fc = first->m_sub;
for (size_t fc_i = 0; fc_i < fc.size();)
for (auto c : fc)
{
auto c = fc[fc_i];
if (not found_in_range(c, subs.begin() + 1, subs.end()))
{
++fc_i;
continue;
}
if (and_result == nullptr)
and_result = new and_condition_impl();
and_result->m_sub.push_back(c);
fc.erase(fc.begin() + fc_i);
fc.erase(remove(fc.begin(), fc.end(), c), fc.end());
for (auto sub : subs)
{
auto &ssub = sub->m_sub;
for (size_t ssub_i = 0; ssub_i < ssub.size();)
for (auto sc : ssub)
{
auto sc = ssub[ssub_i];
if (not sc->equals(c))
{
++ssub_i;
continue;
}
ssub.erase(ssub.begin() + ssub_i);
ssub.erase(remove(ssub.begin(), ssub.end(), sc), ssub.end());
delete sc;
break;
}
@@ -208,99 +137,6 @@ namespace detail
return oc;
}
condition_impl *and_condition_impl::prepare(const category &c)
{
for (auto &sub : m_sub)
sub = sub->prepare(c);
if (auto cv = c.get_cat_validator(); cv != nullptr)
{
// See if we can collapse a search part of this and_condition into a single index lookup
cif::iset keys{ cv->m_keys.begin(), cv->m_keys.end() };
category::key_type lookup;
std::vector<condition_impl *> subs;
std::vector<std::string> may_be_empty;
for (auto &sub : m_sub)
{
if (auto s = dynamic_cast<const key_equals_condition_impl *>(sub); s != nullptr)
{
if (keys.contains(s->m_item_name))
{
lookup.emplace_back(s->m_item_name, s->m_value);
subs.emplace_back(sub);
}
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))
{
lookup.emplace_back(s->m_item_name, s->m_value, true);
subs.emplace_back(sub);
may_be_empty.emplace_back(s->m_item_name);
}
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())
{
m_single = c[lookup];
for (auto s : subs)
m_sub.erase(std::remove(m_sub.begin(), m_sub.end(), s), m_sub.end());
}
}
return this;
}
bool and_condition_impl::test(row_handle r) const
{
bool result = true;
if (m_single.has_value() and *m_single != r)
result = false;
else
{
for (auto sub : m_sub)
{
if (sub->test(r))
continue;
result = false;
break;
}
}
return result;
}
condition_impl *or_condition_impl::prepare(const category &c)
{
std::vector<and_condition_impl *> and_conditions;
@@ -324,7 +160,7 @@ void condition::prepare(const category &c)
{
if (m_impl)
m_impl = m_impl->prepare(c);
m_prepared = true;
}

View File

@@ -26,10 +26,6 @@
#include "cif++/datablock.hpp"
#include "cif++/validate.hpp"
#include <exception>
namespace cif
{
@@ -42,19 +38,19 @@ datablock::datablock(const datablock &db)
cat.update_links(*this);
}
void datablock::load_dictionary()
datablock &datablock::operator=(const datablock &db)
{
if (auto *audit_conform = get("audit_conform"); audit_conform and not audit_conform->empty())
if (this != &db)
{
try
{
set_validator(&validator_factory::instance().get(*audit_conform));
}
catch (const std::exception &ex)
{
std::clog << ex.what() << '\n';
}
std::list<category>::operator=(db);
m_name = db.m_name;
m_validator = db.m_validator;
for (auto &cat : *this)
cat.update_links(*this);
}
return *this;
}
void datablock::set_validator(const validator *v)
@@ -68,7 +64,6 @@ void datablock::set_validator(const validator *v)
}
catch (const std::exception &)
{
m_validator = nullptr;
throw_with_nested(std::runtime_error("Error while setting validator in datablock " + m_name));
}
}
@@ -81,7 +76,7 @@ const validator *datablock::get_validator() const
bool datablock::is_valid() const
{
if (m_validator == nullptr)
throw std::runtime_error("Validator not specified for datablock data_" + name());
throw std::runtime_error("Validator not specified");
bool result = true;
for (auto &cat : *this)
@@ -94,52 +89,9 @@ bool datablock::validate_links() const
{
bool result = true;
for (auto &cat : *this)
const_cast<category &>(cat).update_links(*this);
for (auto &cat : *this)
result = cat.validate_links() and result;
return result;
}
bool datablock::strip()
{
bool result = true;
// remove all categories that have no validator
erase(std::remove_if(begin(), end(), [](category &c)
{
bool result = false;
if (c.get_cat_validator() == nullptr)
{
if (cif::VERBOSE > 0)
std::clog << "Dropping category " << c.name() << '\n';
result = true;
}
return result; }),
end());
// then strip the remaining categories
for (auto &cat : *this)
cat.strip();
// Add or remove the audit_conform block here.
if (is_valid())
{
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (auto audit_conform = get("audit_conform");
audit_conform != nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
{
audit_conform->clear();
m_validator->fill_audit_conform(*audit_conform);
}
}
else
result = false;
return result;
}
@@ -191,6 +143,13 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (iequals(name, i->name()))
{
is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break;
}
@@ -199,37 +158,30 @@ std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
if (is_new)
{
i = insert(end(), { name });
i->set_validator(m_validator, *this);
auto &c = emplace_front(name);
c.set_validator(m_validator, *this);
}
assert(i != end());
// links may have changed...
for (auto &cat : *this)
cat.update_links(*this);
return std::make_tuple(i, is_new);
return std::make_tuple(begin(), is_new);
}
std::vector<std::string> datablock::get_item_order() const
std::vector<std::string> datablock::get_tag_order() const
{
std::vector<std::string> result;
// for entry and audit_conform on top
auto ci = find_if(begin(), end(), [](const category &cat)
{ return cat.name() == "entry"; });
auto ci = find_if(begin(), end(), [](const category &cat) { return cat.name() == "entry"; });
if (ci != end())
{
auto cto = ci->get_item_order();
auto cto = ci->get_tag_order();
result.insert(result.end(), cto.begin(), cto.end());
}
ci = find_if(begin(), end(), [](const category &cat)
{ return cat.name() == "audit_conform"; });
ci = find_if(begin(), end(), [](const category &cat) { return cat.name() == "audit_conform"; });
if (ci != end())
{
auto cto = ci->get_item_order();
auto cto = ci->get_tag_order();
result.insert(result.end(), cto.begin(), cto.end());
}
@@ -237,131 +189,62 @@ std::vector<std::string> datablock::get_item_order() const
{
if (cat.name() == "entry" or cat.name() == "audit_conform")
continue;
auto cto = cat.get_item_order();
auto cto = cat.get_tag_order();
result.insert(result.end(), cto.begin(), cto.end());
}
return result;
}
namespace
{
using elem_t = std::tuple<std::string, int, bool>;
using cat_order_t = std::vector<elem_t>;
using iter_t = cat_order_t::iterator;
inline int get_count(iter_t i)
{
return std::get<1>(*i);
}
inline bool is_on_stack(iter_t i)
{
return std::get<2>(*i);
}
void calculate_cat_order(cat_order_t &cat_order, iter_t i, const validator &validator)
{
if (i == cat_order.end() or get_count(i) >= 0)
return;
auto &&[cat, count, on_stack] = *i;
on_stack = true;
int parent_count = 0;
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)
{ return std::get<0>(a) == parent; });
if (ei == cat_order.end())
continue;
if (not is_on_stack(ei))
calculate_cat_order(cat_order, ei, validator);
parent_count += get_count(ei);
}
count = parent_count + 1;
}
} // namespace
void datablock::write(std::ostream &os) const
{
os << "data_" << m_name << '\n'
<< "# \n";
if (m_validator and size() > 0)
// mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
for (auto &cat : *this)
{
// base order on parent child relationships, parents first
if (cat.name() != "entry")
continue;
cat_order_t cat_order;
cat.write(os);
for (auto &cat : *this)
{
if (cat.name() == "entry" or cat.name() == "audit_conform")
continue;
cat_order.emplace_back(cat.name(), -1, false);
}
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)
{
const auto &[cat_a, count_a, on_stack_a] = a;
const auto &[cat_b, count_b, on_stack_b] = b;
int d = std::get<1>(a) - std::get<1>(b);
if (d == 0)
d = cat_b.compare(cat_a);
return d < 0; });
if (auto entry = get("entry"); entry != nullptr)
entry->write(os);
if (auto audit_conform = get("audit_conform"); audit_conform != nullptr)
audit_conform->write(os);
for (auto &&[cat, count, on_stack] : cat_order)
get(cat)->write(os);
break;
}
else
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (get("audit_conform"))
get("audit_conform")->write(os);
else if (m_validator != nullptr and m_validator->get_validator_for_category("audit_conform") != nullptr)
{
// mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
category auditConform("audit_conform");
auditConform.emplace({
{"dict_name", m_validator->name()},
{"dict_version", m_validator->version()}});
auditConform.write(os);
}
if (auto entry = get("entry"); entry != nullptr)
entry->write(os);
// If the dictionary declares an audit_conform category, put it in,
// but only if it does not exist already!
if (auto audit_conform = get("audit_conform"); audit_conform != nullptr)
audit_conform->write(os);
for (auto &cat : *this)
{
if (cat.name() != "entry" and cat.name() != "audit_conform")
cat.write(os);
}
for (auto &cat : *this)
{
if (cat.name() != "entry" and cat.name() != "audit_conform")
cat.write(os);
}
}
void datablock::write(std::ostream &os, const std::vector<std::string> &item_name_order)
void datablock::write(std::ostream &os, const std::vector<std::string> &tag_order)
{
os << "data_" << m_name << '\n'
<< "# \n";
std::vector<std::string> cat_order{ "entry", "audit_conform" };
for (auto &o : item_name_order)
std::vector<std::string> cat_order;
for (auto &o : tag_order)
{
std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_item_name(o);
std::tie(cat_name, item_name) = split_tag_name(o);
if (find_if(cat_order.rbegin(), cat_order.rend(), [cat_name](const std::string &s) -> bool
{ return iequals(cat_name, s); }) == cat_order.rend())
cat_order.push_back(cat_name);
@@ -374,10 +257,10 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &item_nam
continue;
std::vector<std::string> items;
for (auto &o : item_name_order)
for (auto &o : tag_order)
{
std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_item_name(o);
std::tie(cat_name, item_name) = split_tag_name(o);
if (cat_name == c)
items.push_back(item_name);
@@ -399,10 +282,6 @@ void datablock::write(std::ostream &os, const std::vector<std::string> &item_nam
bool datablock::operator==(const datablock &rhs) const
{
// shortcut
if (this == &rhs)
return true;
auto &dbA = *this;
auto &dbB = rhs;
@@ -458,7 +337,7 @@ bool datablock::operator==(const datablock &rhs) const
++catA_i;
else
{
if (not(*dbA.get(*catA_i) == *dbB.get(*catB_i)))
if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
return false;
++catA_i;
++catB_i;
@@ -468,4 +347,4 @@ bool datablock::operator==(const datablock &rhs) const
return true;
}
} // namespace cif
} // namespace cif::cif

View File

@@ -28,9 +28,6 @@
#include "cif++/dictionary_parser.hpp"
#include "cif++/file.hpp"
#include "cif++/parser.hpp"
#include <exception>
#include <iomanip>
#include <stdexcept>
namespace cif
{
@@ -49,11 +46,11 @@ class dictionary_parser : public parser
void load_dictionary()
{
std::unique_ptr<datablock> dict;
auto savedDatablock = std::exchange(m_datablock, nullptr);
auto savedDatablock = m_datablock;
try
{
while (m_lookahead != CIFToken::END_OF_FILE)
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
@@ -78,9 +75,6 @@ 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));
@@ -93,7 +87,7 @@ class dictionary_parser : public parser
error("Undefined category '" + iv.first);
for (auto &v : iv.second)
const_cast<category_validator *>(cv)->add_item_validator(std::move(v));
const_cast<category_validator *>(cv)->addItemValidator(std::move(v));
}
// check all item validators for having a typeValidator
@@ -102,10 +96,14 @@ class dictionary_parser : public parser
link_items();
// store meta information
if (auto dictionary = m_datablock->get("dictionary"); dictionary != nullptr and not dictionary->empty())
datablock::iterator info;
bool is_new;
std::tie(info, is_new) = m_datablock->emplace("dictionary");
if (not is_new and not info->empty())
{
const auto &[name, version] = dictionary->front().get<std::string,std::optional<std::string>>("title", "version");
m_validator.append_audit_conform(name, version);
auto r = info->front();
m_validator.set_name(r["title"].as<std::string>());
m_validator.set_version(r["version"].as<std::string>());
}
m_datablock = savedDatablock;
@@ -130,7 +128,7 @@ class dictionary_parser : public parser
datablock::iterator cat = dict.end();
match(CIFToken::SAVE_NAME);
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME)
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag)
{
if (m_lookahead == CIFToken::LOOP)
{
@@ -138,30 +136,30 @@ class dictionary_parser : public parser
match(CIFToken::LOOP);
std::vector<std::string> item_names;
while (m_lookahead == CIFToken::ITEM_NAME)
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
{
std::string catName, item_name;
std::tie(catName, item_name) = split_item_name(m_token_value);
std::tie(catName, item_name) = split_tag_name(m_token_value);
if (cat == dict.end())
std::tie(cat, std::ignore) = dict.emplace(catName);
else if (not iequals(cat->name(), catName))
error("inconsistent categories in loop_");
item_names.push_back(item_name);
match(CIFToken::ITEM_NAME);
tags.push_back(item_name);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::VALUE)
while (m_lookahead == CIFToken::Value)
{
cat->emplace({});
auto row = cat->back();
for (auto item_name : item_names)
for (auto tag : tags)
{
row[item_name] = m_token_value;
match(CIFToken::VALUE);
row[tag] = m_token_value;
match(CIFToken::Value);
}
}
@@ -170,18 +168,18 @@ class dictionary_parser : public parser
else
{
std::string catName, item_name;
std::tie(catName, item_name) = split_item_name(m_token_value);
std::tie(catName, item_name) = split_tag_name(m_token_value);
if (cat == dict.end() or not iequals(cat->name(), catName))
std::tie(cat, std::ignore) = dict.emplace(catName);
match(CIFToken::ITEM_NAME);
match(CIFToken::Tag);
if (cat->empty())
cat->emplace({});
cat->back()[item_name] = m_token_value;
match(CIFToken::VALUE);
match(CIFToken::Value);
}
}
@@ -193,7 +191,7 @@ class dictionary_parser : public parser
std::vector<std::string> keys;
for (auto k : dict["category_key"])
keys.push_back(std::get<1>(split_item_name(k["name"].as<std::string>())));
keys.push_back(std::get<1>(split_tag_name(k["name"].as<std::string>())));
iset groups;
for (auto g : dict["category_group"])
@@ -226,27 +224,20 @@ 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"))
{
aliases.emplace_back(alias_name, dictionary, version);
}
// 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");
std::string tagName, category, mandatory;
cif::tie(tagName, category, mandatory) = i.get("name", "category_id", "mandatory_code");
std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_item_name(item);
std::tie(cat_name, item_name) = split_tag_name(tagName);
if (cat_name.empty() or item_name.empty())
error("Invalid item name in _item.name " + item);
error("Invalid tag name in _item.name " + tagName);
if (not iequals(category, cat_name) and not(category.empty() or category == "?"))
error("specified category id does match the implicit category name for item '" + item + '\'');
error("specified category id does match the implicit category name for tag '" + tagName + '\'');
else
category = cat_name;
@@ -254,7 +245,7 @@ class dictionary_parser : public parser
auto vi = find(ivs.begin(), ivs.end(), 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 /*, defaultIsNull*/ });
else
{
// need to update the itemValidator?
@@ -262,22 +253,22 @@ class dictionary_parser : public parser
{
if (VERBOSE > 2)
{
std::cerr << "inconsistent mandatory value for " << item << " in dictionary\n";
std::cerr << "inconsistent mandatory value for " << tagName << " in dictionary\n";
if (iequals(item, saveFrameName))
if (iequals(tagName, saveFrameName))
std::cerr << "choosing " << mandatory << '\n';
else
std::cerr << "choosing " << (vi->m_mandatory ? "Y" : "N") << '\n';
}
if (iequals(item, saveFrameName))
if (iequals(tagName, saveFrameName))
vi->m_mandatory = (iequals(mandatory, "yes"));
}
if (vi->m_type != nullptr and tv != nullptr and vi->m_type != tv)
{
if (VERBOSE > 1)
std::cerr << "inconsistent type for " << item << " in dictionary\n";
std::cerr << "inconsistent type for " << tagName << " in dictionary\n";
}
// vi->mMandatory = (iequals(mandatory, "yes"));
@@ -310,17 +301,17 @@ class dictionary_parser : public parser
using key_type = std::tuple<std::string, std::string, int>;
std::map<key_type, std::size_t> linkIndex;
std::map<key_type, size_t> linkIndex;
// Each link group consists of a set of keys
std::vector<std::tuple<std::vector<std::string>, std::vector<std::string>>> linkKeys;
auto addLink = [&](std::size_t ix, const std::string &pk, const std::string &ck)
auto addLink = [&](size_t ix, const std::string &pk, const std::string &ck)
{
auto &&[pkeys, ckeys] = linkKeys.at(ix);
bool found = false;
for (std::size_t i = 0; i < pkeys.size(); ++i)
for (size_t i = 0; i < pkeys.size(); ++i)
{
if (pkeys[i] == pk and ckeys[i] == ck)
{
@@ -352,15 +343,15 @@ class dictionary_parser : public parser
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
key_type key{ piv->m_category, civ->m_category, link_group_id };
key_type key{ piv->m_category->m_name, civ->m_category->m_name, link_group_id };
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
}
std::size_t ix = linkIndex.at(key);
addLink(ix, piv->m_item_name, civ->m_item_name);
size_t ix = linkIndex.at(key);
addLink(ix, piv->m_tag, civ->m_tag);
}
// Only process inline linked items if the linked group list is absent
@@ -380,15 +371,15 @@ class dictionary_parser : public parser
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
key_type key{ piv->m_category, civ->m_category, 0 };
key_type key{ piv->m_category->m_name, civ->m_category->m_name, 0 };
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
}
std::size_t ix = linkIndex.at(key);
addLink(ix, piv->m_item_name, civ->m_item_name);
size_t ix = linkIndex.at(key);
addLink(ix, piv->m_tag, civ->m_tag);
}
}
@@ -419,7 +410,7 @@ class dictionary_parser : public parser
for (auto &iv : cv.m_item_validators)
{
if (iv.m_type == nullptr and cif::VERBOSE >= 0)
std::cerr << "Missing item_type for " << iv.m_item_name << '\n';
std::cerr << "Missing item_type for " << iv.m_tag << '\n';
}
}
}
@@ -479,7 +470,18 @@ class dictionary_parser : public parser
// --------------------------------------------------------------------
void parse_dictionary(validator &v, std::istream &is)
validator parse_dictionary(std::string_view name, std::istream &is)
{
validator result(name);
file f;
dictionary_parser p(result, is, f);
p.load_dictionary();
return result;
}
void extend_dictionary(validator &v, std::istream &is)
{
file f;
dictionary_parser p(v, is, f);

View File

@@ -25,14 +25,24 @@
*/
#include "cif++/file.hpp"
#include "cif++/condition.hpp"
#include "cif++/gzio.hpp"
namespace cif
{
// --------------------------------------------------------------------
void file::set_validator(const validator *v)
{
m_validator = v;
for (auto &db : *this)
db.set_validator(v);
}
bool file::is_valid() const
{
if (m_validator == nullptr)
std::runtime_error("No validator loaded explicitly, cannot continue");
bool result = true;
for (auto &d : *this)
result = d.is_valid() and result;
@@ -45,18 +55,18 @@ bool file::is_valid() const
bool file::is_valid()
{
if (m_validator == nullptr)
{
if (VERBOSE > 0)
std::cerr << "No dictionary loaded explicitly, loading default\n";
load_dictionary();
}
bool result = not empty();
for (bool first = true; auto &d : *this)
{
if (first)
{
result = d.is_valid() and result;
first = false;
}
else if (d.get_validator() != nullptr)
result = d.is_valid() and result;
}
for (auto &d : *this)
result = d.is_valid() and result;
if (result)
result = validate_links();
@@ -66,18 +76,56 @@ bool file::is_valid()
bool file::validate_links() const
{
if (m_validator == nullptr)
std::runtime_error("No validator loaded explicitly, cannot continue");
bool result = true;
for (auto &db : *this)
result = db.validate_links() and result;
return result;
}
void file::load_dictionary()
{
if (not empty())
{
auto *audit_conform = front().get("audit_conform");
if (audit_conform and not audit_conform->empty())
{
std::string name = audit_conform->front().get<std::string>("dict_name");
if (name == "mmcif_pdbx_v50")
name = "mmcif_pdbx.dic"; // we had a bug here in libcifpp...
if (not name.empty())
{
try
{
load_dictionary(name);
}
catch (const std::exception &ex)
{
if (VERBOSE)
std::cerr << "Failed to load dictionary " << std::quoted(name) << ": " << ex.what() << '\n';
}
}
}
}
// if (not m_validator)
// load_dictionary("mmcif_pdbx.dic"); // TODO: maybe incorrect? Perhaps improve?
}
void file::load_dictionary(std::string_view name)
{
set_validator(&validator_factory::instance()[name]);
}
bool file::contains(std::string_view name) const
{
return std::find_if(begin(), end(), [name](const datablock &db)
{ return iequals(db.name(), name); }) != end();
return std::find_if(begin(), end(), [name](const datablock &db) { return iequals(db.name(), name); }) != end();
}
datablock &file::operator[](std::string_view name)
@@ -110,6 +158,13 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
if (iequals(name, i->name()))
{
is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break;
}
@@ -117,10 +172,12 @@ std::tuple<file::iterator, bool> file::emplace(std::string_view name)
}
if (is_new)
i = insert(end(), { name });
{
auto &db = emplace_front(name);
db.set_validator(m_validator);
}
assert(i != end());
return std::make_tuple(i, is_new);
return std::make_tuple(begin(), is_new);
}
void file::load(const std::filesystem::path &p)
@@ -139,34 +196,18 @@ 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)
{
auto saved = m_validator;
set_validator(nullptr);
parser p(is, *this);
p.parse_file();
if (saved != nullptr)
set_validator(saved);
else
load_dictionary();
}
void file::save(const std::filesystem::path &p) const
@@ -184,4 +225,4 @@ void file::save(std::ostream &os) const
db.write(os);
}
} // namespace cif
} // namespace cif

View File

@@ -35,7 +35,7 @@ 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_column(std::numeric_limits<uint16_t>::max())
, m_row_handle(s_null_row_handle)
{
}
@@ -44,7 +44,7 @@ std::string_view item_handle::text() const
{
if (not m_row_handle.empty())
{
auto iv = m_row_handle.m_row->get(m_item_ix);
auto iv = m_row_handle.m_row->get(m_column);
if (iv != nullptr)
return iv->text();
}
@@ -52,17 +52,17 @@ std::string_view item_handle::text() const
return {};
}
void item_handle::assign_value(std::string_view value)
void item_handle::assign_value(const item &v)
{
assert(not m_row_handle.empty());
m_row_handle.assign(m_item_ix, value, true);
m_row_handle.assign(m_column, v.value(), true);
}
void item_handle::swap(item_handle &b)
{
assert(m_item_ix == b.m_item_ix);
assert(m_column == b.m_column);
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
m_row_handle.swap(m_item_ix, b.m_row_handle);
m_row_handle.swap(m_column, b.m_row_handle);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -269,7 +269,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
const auto kEOF = std::char_traits<char>::eof();
CIFToken result = CIFToken::UNKNOWN;
CIFToken result = CIFToken::Unknown;
int quoteChar = 0;
State state = State::Start;
m_bol = false;
@@ -279,7 +279,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
reserved_words_automaton dag;
while (result == CIFToken::UNKNOWN)
while (result == CIFToken::Unknown)
{
auto ch = get_next_char();
@@ -287,7 +287,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
{
case State::Start:
if (ch == kEOF)
result = CIFToken::END_OF_FILE;
result = CIFToken::Eof;
else if (ch == '\n')
{
m_bol = true;
@@ -298,9 +298,9 @@ sac_parser::CIFToken sac_parser::get_next_token()
else if (ch == '#')
state = State::Comment;
else if (ch == '_')
state = State::ItemName;
state = State::Tag;
else if (ch == ';' and m_bol)
state = State::TextItem;
state = State::TextField;
else if (ch == '?')
state = State::QuestionMark;
else if (ch == '\'' or ch == '"')
@@ -316,7 +316,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
case State::White:
if (ch == kEOF)
result = CIFToken::END_OF_FILE;
result = CIFToken::Eof;
else if (not is_space(ch))
{
state = State::Start;
@@ -335,7 +335,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
m_token_buffer.clear();
}
else if (ch == kEOF)
result = CIFToken::END_OF_FILE;
result = CIFToken::Eof;
else if (not is_any_print(ch))
error("invalid character in comment");
break;
@@ -344,29 +344,29 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::Value;
}
else
state = State::Value;
break;
case State::TextItem:
case State::TextField:
if (ch == '\n')
state = State::TextItemNL;
state = State::TextFieldNL;
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) + ")");
break;
case State::TextItemNL:
case State::TextFieldNL:
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextItem;
state = State::TextField;
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;
result = CIFToken::Value;
}
else if (ch == kEOF)
error("unterminated textfield");
@@ -387,7 +387,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (is_white(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::Value;
if (m_token_buffer.size() < 2)
error("Invalid quoted string token");
@@ -403,11 +403,11 @@ sac_parser::CIFToken sac_parser::get_next_token()
error("invalid character in quoted string");
break;
case State::ItemName:
case State::Tag:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::ITEM_NAME;
result = CIFToken::Tag;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
break;
@@ -422,7 +422,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::Value;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
}
else
@@ -467,7 +467,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (not is_non_blank(ch))
{
retract();
result = CIFToken::VALUE;
result = CIFToken::Value;
m_token_value = std::string_view(m_token_buffer.data(), m_token_buffer.size());
break;
}
@@ -483,7 +483,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
if (VERBOSE >= 5)
{
std::cerr << get_token_name(result);
if (result != CIFToken::END_OF_FILE)
if (result != CIFToken::Eof)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << '\n';
}
@@ -710,7 +710,7 @@ bool sac_parser::parse_single_datablock(const std::string &datablock, const data
void sac_parser::parse_file()
{
while (m_lookahead != CIFToken::END_OF_FILE)
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
@@ -735,10 +735,10 @@ void sac_parser::parse_file()
void sac_parser::parse_global()
{
match(CIFToken::GLOBAL);
while (m_lookahead == CIFToken::ITEM_NAME)
while (m_lookahead == CIFToken::Tag)
{
match(CIFToken::ITEM_NAME);
match(CIFToken::VALUE);
match(CIFToken::Tag);
match(CIFToken::Value);
}
}
@@ -747,7 +747,7 @@ 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
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::ITEM_NAME or m_lookahead == CIFToken::SAVE_NAME)
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE_NAME)
{
switch (m_lookahead)
{
@@ -757,12 +757,12 @@ void sac_parser::parse_datablock()
match(CIFToken::LOOP);
std::vector<std::string> item_names;
std::vector<std::string> tags;
while (m_lookahead == CIFToken::ITEM_NAME)
while (m_lookahead == CIFToken::Tag)
{
std::string catName, itemName;
std::tie(catName, itemName) = split_item_name(m_token_value);
std::tie(catName, itemName) = split_tag_name(m_token_value);
if (cat == kUnitializedCategory)
{
@@ -772,19 +772,19 @@ void sac_parser::parse_datablock()
else if (not iequals(cat, catName))
error("inconsistent categories in loop_");
item_names.push_back(itemName);
tags.push_back(itemName);
match(CIFToken::ITEM_NAME);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::VALUE)
while (m_lookahead == CIFToken::Value)
{
produce_row();
for (auto item_name : item_names)
for (auto tag : tags)
{
produce_item(cat, item_name, m_token_value);
match(CIFToken::VALUE);
produce_item(cat, tag, m_token_value);
match(CIFToken::Value);
}
}
@@ -792,10 +792,10 @@ void sac_parser::parse_datablock()
break;
}
case CIFToken::ITEM_NAME:
case CIFToken::Tag:
{
std::string catName, itemName;
std::tie(catName, itemName) = split_item_name(m_token_value);
std::tie(catName, itemName) = split_tag_name(m_token_value);
if (not iequals(cat, catName))
{
@@ -804,11 +804,11 @@ void sac_parser::parse_datablock()
produce_row();
}
match(CIFToken::ITEM_NAME);
match(CIFToken::Tag);
produce_item(cat, itemName, m_token_value);
match(CIFToken::VALUE);
match(CIFToken::Value);
break;
}
@@ -837,9 +837,6 @@ void parser::produce_datablock(std::string_view name)
const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
if (m_validator)
m_datablock->set_validator(m_validator);
}
void parser::produce_category(std::string_view name)

View File

@@ -33,6 +33,7 @@
#include <regex>
#include <set>
namespace cif::pdb
{
@@ -57,9 +58,9 @@ std::string cif2pdbDate(const std::string &d)
int month = std::stoi(m[2].str());
if (m[3].matched)
result = cif::format("{:02}-{:3.3}-{:02}", stoi(m[3].str()), kMonths[month - 1], (year % 100));
result = cif::format("%02.2d-%3.3s-%02.2d", stoi(m[3].str()), kMonths[month - 1], (year % 100)).str();
else
result = cif::format("{:3.3}-{:02}", kMonths[month - 1], (year % 100));
result = cif::format("%3.3s-%02.2d", kMonths[month - 1], (year % 100)).str();
}
return result;
@@ -181,7 +182,7 @@ std::vector<std::string> MapAsymIDs2ChainIDs(const std::vector<std::string> &asy
}
// support for wrapping text using a 'continuation marker'
std::size_t WriteContinuedLine(std::ostream &pdbFile, std::string header, int &count, int cLen, std::string text, std::string::size_type lStart = 0)
size_t WriteContinuedLine(std::ostream &pdbFile, std::string header, int &count, int cLen, std::string text, std::string::size_type lStart = 0)
{
if (lStart == 0)
{
@@ -216,15 +217,15 @@ std::size_t WriteContinuedLine(std::ostream &pdbFile, std::string header, int &c
return lines.size();
}
std::size_t WriteOneContinuedLine(std::ostream &pdbFile, std::string header, int cLen, std::string line, int lStart = 0)
size_t WriteOneContinuedLine(std::ostream &pdbFile, std::string header, int cLen, std::string line, int lStart = 0)
{
int count = 0;
return WriteContinuedLine(pdbFile, header, count, cLen, line, lStart);
}
std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle r, int reference)
size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle r, int reference)
{
std::size_t result = 0;
size_t result = 0;
std::string s1;
@@ -257,14 +258,16 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
{
to_upper(pubname);
pdbFile << s1 << cif::format("REF {:2.2s} {:<28.28s} {:2.2s}{:>4.4s} {:>5.5s} {:4.4s}", "" /* continuation */, pubname, (volume.empty() ? "" : "V."), volume, pageFirst, year)
const std::string kRefHeader = s1 + "REF %2.2s %-28.28s %2.2s%4.4s %5.5s %4.4s";
pdbFile << cif::format(kRefHeader, "" /* continuation */, pubname, (volume.empty() ? "" : "V."), volume, pageFirst, year)
<< '\n';
++result;
}
if (not issn.empty())
{
pdbFile << s1 << cif::format("REFN ISSN {:<25.25s}", issn) << '\n';
const std::string kRefHeader = s1 + "REFN ISSN %-25.25s";
pdbFile << cif::format(kRefHeader, issn) << '\n';
++result;
}
@@ -273,25 +276,27 @@ std::size_t WriteCitation(std::ostream &pdbFile, const datablock &db, row_handle
//// 0 1 2 3 4 5 6 7 8
//// HEADER xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDDDDDDDDD IIII
// const char kRefHeader[] =
// "REMARK 1 REFN {:4.4s} {:<6.6s} {:2.2s} {:<25.25s}";
// "REMARK 1 REFN %4.4s %-6.6s %2.2s %-25.25s";
//
// pdbFile << (boost::cif::format(kRefHeader)
// % (astm.empty() ? "" : "ASTN")
// % astm
// % country
// % issn)
// % issn).str()
// << '\n';
// }
if (not pmid.empty())
{
pdbFile << s1 << cif::format("PMID {:<60.60s} ", pmid) << '\n';
const std::string kPMID = s1 + "PMID %-60.60s ";
pdbFile << cif::format(kPMID, pmid) << '\n';
++result;
}
if (not doi.empty())
{
pdbFile << s1 << cif::format("DOI {:<60.60s} ", doi) << '\n';
const std::string kDOI = s1 + "DOI %-60.60s ";
pdbFile << cif::format(kDOI, doi) << '\n';
++result;
}
@@ -302,10 +307,10 @@ void write_header_lines(std::ostream &pdbFile, const datablock &db)
{
// 0 1 2 3 4 5 6 7 8
// HEADER xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDDDDDDDDD IIII
// const char kHeader[] =
// "HEADER {:<40.40s}"
// "{:<9.9s}"
// " {:<4.4s}";
const char kHeader[] =
"HEADER %-40.40s"
"%-9.9s"
" %-4.4s";
// HEADER
@@ -340,12 +345,7 @@ void write_header_lines(std::ostream &pdbFile, const datablock &db)
}
}
pdbFile << cif::format(/* kHeader */
"HEADER {:<40.40s}"
"{:<9.9s}"
" {:<4.4s}"
, keywords, date, db.name()) << '\n';
pdbFile << cif::format(kHeader, keywords, date, db.name()) << '\n';
// TODO: implement
// OBSLTE (skip for now)
@@ -535,6 +535,7 @@ void WriteTitle(std::ostream &pdbFile, const datablock &db)
write_header_lines(pdbFile, db);
// REVDAT
const char kRevDatFmt[] = "REVDAT %3d%2.2s %9.9s %4.4s %1d ";
auto &cat2 = db["database_PDB_rev"];
std::vector<row_handle> rev(cat2.begin(), cat2.end());
sort(rev.begin(), rev.end(), [](row_handle a, row_handle b) -> bool
@@ -558,9 +559,9 @@ void WriteTitle(std::ostream &pdbFile, const datablock &db)
{
std::string cs = ++continuation > 1 ? std::to_string(continuation) : std::string();
pdbFile << cif::format("REVDAT {:3}{:2.2s} {:9.9s} {:4.4s} {:1} ", revNum, cs, date, db.name(), modType);
for (std::size_t i = 0; i < 4; ++i)
pdbFile << cif::format(" {:<6.6s}", (i < types.size() ? types[i] : std::string()));
pdbFile << cif::format(kRevDatFmt, revNum, cs, date, db.name(), modType);
for (size_t i = 0; i < 4; ++i)
pdbFile << cif::format(" %-6.6s", (i < types.size() ? types[i] : std::string()));
pdbFile << '\n';
if (types.size() > 4)
@@ -613,7 +614,7 @@ void WriteRemark2(std::ostream &pdbFile, const datablock &db)
{
float resHigh = refine.front()["ls_d_res_high"].as<float>();
pdbFile << "REMARK 2\n"
<< cif::format("REMARK 2 RESOLUTION. {:7.2f} ANGSTROMS.", resHigh) << '\n';
<< cif::format("REMARK 2 RESOLUTION. %7.2f ANGSTROMS.", resHigh) << '\n';
}
catch (...)
{ /* skip it */
@@ -680,7 +681,7 @@ class Fi : public FBase
{
long l = 0;
auto r = std::from_chars(s.data(), s.data() + s.length(), l);
if ((bool)r.ec)
if (r.ec != std::errc())
{
if (VERBOSE > 0)
std::cerr << "Failed to write '" << s << "' as a long from field " << mField << ", this indicates an error in the code for writing PDB files\n";
@@ -718,7 +719,7 @@ class Ff : public FBase
double d = 0;
auto r = cif::from_chars(s.data(), s.data() + s.length(), d);
if ((bool)r.ec)
if (r.ec != std::errc())
{
if (VERBOSE > 0)
std::cerr << "Failed to write '" << s << "' as a double from field " << mField << ", this indicates an error in the code for writing PDB files\n";
@@ -747,7 +748,7 @@ class Fs : public FBase
virtual void out(std::ostream &os)
{
std::string s{ text() };
std::size_t width = os.width();
size_t width = os.width();
if (s.empty())
{
@@ -760,7 +761,10 @@ class Fs : public FBase
else
{
os << '\n';
WriteOneContinuedLine(os, cif::format("REMARK {:3} ", mNr), 0, s);
std::stringstream ss;
ss << "REMARK " << std::setw(3) << std::right << mNr << ' ';
WriteOneContinuedLine(os, ss.str(), 0, s);
}
}
@@ -1613,7 +1617,7 @@ void WriteRemark3Phenix(std::ostream &pdbFile, const datablock &db)
percent_reflns_obs /= 100;
pdbFile << RM3(" ") << cif::format("{:3} {:7.4f} - {:7.4f} {:4.2f} {:8} {:5} {:6.4f} {:6.4f}", bin++, d_res_low, d_res_high, percent_reflns_obs, number_reflns_R_work, number_reflns_R_free, R_factor_R_work, R_factor_R_free) << '\n';
pdbFile << RM3(" ") << cif::format("%3d %7.4f - %7.4f %4.2f %8d %5d %6.4f %6.4f", bin++, d_res_low, d_res_high, percent_reflns_obs, number_reflns_R_work, number_reflns_R_free, R_factor_R_work, R_factor_R_free) << '\n';
}
pdbFile << RM3("") << '\n'
@@ -2364,7 +2368,7 @@ void WriteRemark280(std::ostream &pdbFile, const datablock &db)
const char *keys[] = { "pdbx_details", "ph", "method", "temp" };
for (std::size_t i = 0; i < (sizeof(keys) / sizeof(const char *)); ++i)
for (size_t i = 0; i < (sizeof(keys) / sizeof(const char *)); ++i)
{
const char *c = keys[i];
@@ -2581,7 +2585,7 @@ void WriteRemark465(std::ostream &pdbFile, const datablock &db)
cif::tie(modelNr, resName, chainID, iCode, seqNr) =
r.get("PDB_model_num", "auth_comp_id", "auth_asym_id", "PDB_ins_code", "auth_seq_id");
pdbFile << cif::format("REMARK 465 {:3.3s} {:3.3s} {:1.1s} {:5}{:1.1s}", modelNr, resName, chainID, seqNr, iCode) << '\n';
pdbFile << cif::format("REMARK 465 %3.3s %3.3s %1.1s %5d%1.1s", modelNr, resName, chainID, seqNr, iCode) << '\n';
}
}
@@ -2628,9 +2632,9 @@ void WriteRemark470(std::ostream &pdbFile, const datablock &db)
while (not a.second.empty())
{
pdbFile << cif::format("REMARK 470 {:>3.3s} {:3.3s} {:1.1s}{:4}{:1.1s} ", modelNr, resName, chainID, seqNr, iCode) << " ";
pdbFile << cif::format("REMARK 470 %3.3s %3.3s %1.1s%4d%1.1s ", modelNr, resName, chainID, seqNr, iCode) << " ";
for (std::size_t i = 0; i < 6 and not a.second.empty(); ++i)
for (size_t i = 0; i < 6 and not a.second.empty(); ++i)
{
pdbFile << cif2pdbAtomName(a.second.front(), resName, db) << ' ';
a.second.pop_front();
@@ -2726,16 +2730,16 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
if (dbAccession.length() > 8 or db_code.length() > 12 or atoi(dbseqEnd.c_str()) >= 100000)
pdbFile << cif::format(
"DBREF1 {:>4.4s} {:1.1s} {:>4.4s}{:1.1s} {:>4.4s}{:1.1s} {:<6.6s} {:<20.20s}",
"DBREF1 %4.4s %1.1s %4.4s%1.1s %4.4s%1.1s %-6.6s %-20.20s",
idCode, chainID, seqBegin, insertBegin, seqEnd, insertEnd, db_name, db_code)
<< '\n'
<< cif::format(
"DBREF2 {:>4.4s} {:1.1s} {:<22.22s} {:10.10s} {:10.10s}",
"DBREF2 %4.4s %1.1s %-22.22s %10.10s %10.10s",
idCode, chainID, dbAccession, dbseqBegin, dbseqEnd)
<< '\n';
else
pdbFile << cif::format(
"DBREF {:>4.4s} {:1.1s} {:>4.4s}{:1.1s} {:>4.4s}{:1.1s} {:<6.6s} {:<8.8s} {:<12.12s} {:>5.5s}{:1.1s} {:>5.5s}{:1.1s}",
"DBREF %4.4s %1.1s %4.4s%1.1s %4.4s%1.1s %-6.6s %-8.8s %-12.12s %5.5s%1.1s %5.5s%1.1s",
idCode, chainID, seqBegin, insertBegin, seqEnd, insertEnd, db_name, dbAccession, db_code, dbseqBegin, dbinsBeg, dbseqEnd, dbinsEnd)
<< '\n';
}
@@ -2754,8 +2758,9 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
to_upper(conflict);
pdbFile << cif::format(
"SEQADV {:4.4s} {:3.3s} {:1.1s} {:>4.4s}{:1.1s} {:<4.4s} {:<9.9s} {:3.3s} {:>5.5s} {:<21.21s}",
"SEQADV %4.4s %3.3s %1.1s %4.4s%1.1s %-4.4s %-9.9s %3.3s %5.5s %-21.21s",
idCode, resName, chainID, seqNum, iCode, database, dbAccession, dbRes, dbSeq, conflict)
.str()
<< '\n';
}
@@ -2783,7 +2788,7 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
t = 13;
pdbFile << cif::format(
"SEQRES {:3} {:1.1s} {:4} {:<51.51s} ",
"SEQRES %3d %1.1s %4d %-51.51s ",
n++, std::string{ chainID }, seqresl[chainID], join(seq.begin(), seq.begin() + t, " "))
<< '\n';
@@ -2803,8 +2808,9 @@ int WritePrimaryStructure(std::ostream &pdbFile, const datablock &db)
r.get("auth_asym_id", "auth_seq_id", "auth_comp_id", "PDB_ins_code", "parent_comp_id", "details");
pdbFile << cif::format(
"MODRES {:4.4s} {:3.3s} {:1.1s} {:4.4s}{:1.1s} {:3.3s} {:<41.41s}",
"MODRES %4.4s %3.3s %1.1s %4.4s%1.1s %3.3s %-41.41s",
db.name(), resName, chainID, seqNum, iCode, stdRes, comment)
.str()
<< '\n';
}
@@ -2919,7 +2925,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
{
if (h.water)
continue;
pdbFile << cif::format("HET {:3.3s} {:1c}{:4}{:1c} {:5}", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
pdbFile << cif::format("HET %3.3s %c%4d%c %5d", h.hetID, h.chainID, h.seqNum, h.iCode, h.numHetAtoms) << '\n';
++numHet;
}
@@ -2934,7 +2940,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
for (;;)
{
pdbFile << cif::format("HETNAM {:2.2s} {:3.3s} ", (c > 1 ? std::to_string(c) : std::string()), id);
pdbFile << cif::format("HETNAM %2.2s %3.3s ", (c > 1 ? std::to_string(c) : std::string()), id);
++c;
if (name.length() > 55)
@@ -3026,7 +3032,7 @@ int WriteHeterogen(std::ostream &pdbFile, const datablock &db)
{
std::stringstream fs;
fs << cif::format("FORMUL {:2} {:3.3s} {:2.2s}{:1c}", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
fs << cif::format("FORMUL %2d %3.3s %2.2s%c", componentNr, hetID, (c > 1 ? std::to_string(c) : std::string()), (hetID == water_comp_id ? '*' : ' '));
++c;
if (formula.length() > 51)
@@ -3093,7 +3099,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
"pdbx_PDB_helix_class", "pdbx_PDB_helix_length", "beg_auth_seq_id", "end_auth_seq_id");
++numHelix;
pdbFile << cif::format("HELIX {:3} {:>3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s}{:2}{:<30.30s} {:5}",
pdbFile << cif::format("HELIX %3d %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s%2d%-30.30s %5d",
numHelix, pdbx_PDB_helix_id, beg_label_comp_id, beg_auth_asym_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code, end_label_comp_id, end_auth_asym_id, end_auth_seq_id, pdbx_end_PDB_ins_code, pdbx_PDB_helix_class, details, pdbx_PDB_helix_length)
<< '\n';
}
@@ -3130,7 +3136,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
"pdbx_end_PDB_ins_code", "beg_auth_comp_id", "beg_auth_asym_id", "beg_auth_seq_id",
"end_auth_comp_id", "end_auth_asym_id", "end_auth_seq_id");
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2}", rangeID1, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, 0) << '\n';
pdbFile << cif::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d", rangeID1, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, 0) << '\n';
first = false;
}
@@ -3149,7 +3155,7 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
if (h.empty())
{
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2}", rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense) << '\n';
pdbFile << cif::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d", rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense) << '\n';
}
else
{
@@ -3162,8 +3168,8 @@ std::tuple<int, int> WriteSecondaryStructure(std::ostream &pdbFile, const databl
curAtom = cif2pdbAtomName(curAtom, compID[0], db);
prevAtom = cif2pdbAtomName(prevAtom, compID[1], db);
pdbFile << cif::format("SHEET {:>3.3s} {:>3.3s}{:2} {:3.3s} {:1.1s}{:4}{:1.1s} {:3.3s} {:1.1s}{:4}{:1.1s}{:2} "
"{:<4.4s}{:3.3s} {:1.1s}{:4}{:1.1s} {:<4.4s}{:3.3s} {:1.1s}{:4}{:1.1s}",
pdbFile << cif::format("SHEET %3.3s %3.3s%2d %3.3s %1.1s%4d%1.1s %3.3s %1.1s%4d%1.1s%2d "
"%-4.4s%3.3s %1.1s%4d%1.1s %-4.4s%3.3s %1.1s%4d%1.1s",
rangeID2, sheetID, numStrands, initResName, initChainID, initSeqNum, initICode, endResName, endChainID, endSeqNum, endICode, sense, curAtom, curResName, curChainID, curResSeq, curICode, prevAtom, prevResName, prevChainID, prevResSeq, prevICode)
<< '\n';
}
@@ -3201,7 +3207,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
sym1 = cif2pdbSymmetry(sym1);
sym2 = cif2pdbSymmetry(sym2);
pdbFile << cif::format("SSBOND {:3} CYS {:1.1s} {:4}{:1.1s} CYS {:1.1s} {:4}{:1.1s} {:6.6s} {:6.6s} {:5.2f}", nr, chainID1, seqNum1, icode1, chainID2, seqNum2, icode2, sym1, sym2, Length) << '\n';
pdbFile << cif::format("SSBOND %3d CYS %1.1s %4d%1.1s CYS %1.1s %4d%1.1s %6.6s %6.6s %5.2f", nr, chainID1, seqNum1, icode1, chainID2, seqNum2, icode2, sym1, sym2, Length) << '\n';
++nr;
}
@@ -3228,10 +3234,10 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
sym1 = cif2pdbSymmetry(sym1);
sym2 = cif2pdbSymmetry(sym2);
pdbFile << cif::format("LINK {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:>6.6s} {:>6.6s}", name1, altLoc1, resName1, chainID1, resSeq1, iCode1, name2, altLoc2, resName2, chainID2, resSeq2, iCode2, sym1, sym2);
pdbFile << cif::format("LINK %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %6.6s %6.6s", name1, altLoc1, resName1, chainID1, resSeq1, iCode1, name2, altLoc2, resName2, chainID2, resSeq2, iCode2, sym1, sym2);
if (not Length.empty())
pdbFile << cif::format(" {:5.2f}", stod(Length));
pdbFile << cif::format(" %5.2f", stod(Length));
pdbFile << '\n';
}
@@ -3249,7 +3255,7 @@ void WriteConnectivity(std::ostream &pdbFile, const datablock &db)
"pdbx_label_comp_id_2", "pdbx_auth_asym_id_2", "pdbx_auth_seq_id_2", "pdbx_PDB_ins_code_2",
"pdbx_PDB_model_num", "pdbx_omega_angle");
pdbFile << cif::format("CISPEP {:3.3s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:1.1s} {:4}{:1.1s} {:3.3s} {:6.2f}",
pdbFile << cif::format("CISPEP %3.3s %3.3s %1.1s %4d%1.1s %3.3s %1.1s %4d%1.1s %3.3s %6.2f",
serNum, pep1, chainID1, seqNum1, icode1, pep2, chainID2, seqNum2, icode2, modNum, measure) << '\n';
}
}
@@ -3270,7 +3276,7 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
cif::tie(siteID, resName, chainID, seq, iCode) =
r.get("site_id", "auth_comp_id", "auth_asym_id", "auth_seq_id", "pdbx_auth_ins_code");
sites[siteID].push_back(cif::format("{:3.3s} {:1.1s}{:4}{:1.1s} ", resName, chainID, seq, iCode));
sites[siteID].push_back(cif::format("%3.3s %1.1s%4d%1.1s ", resName, chainID, seq, iCode).str());
}
for (auto s : sites)
@@ -3278,12 +3284,12 @@ int WriteMiscellaneousFeatures(std::ostream &pdbFile, const datablock &db)
std::string siteID = std::get<0>(s);
std::deque<std::string> &res = std::get<1>(s);
std::size_t numRes = res.size();
size_t numRes = res.size();
int nr = 1;
while (res.empty() == false)
{
pdbFile << cif::format("SITE {:3} {:3.3s} {:2} ", nr, siteID, numRes);
pdbFile << cif::format("SITE %3d %3.3s %2d ", nr, siteID, numRes);
for (int i = 0; i < 4; ++i)
{
@@ -3312,7 +3318,7 @@ void WriteCrystallographic(std::ostream &pdbFile, const datablock &db)
r = db["cell"].find_first(key("entry_id") == db.name());
pdbFile << cif::format("CRYST1{:9.3f}{:9.3f}{:9.3f}{:7.2f}{:7.2f}{:7.2f} {:<11.11s}{:4}", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << '\n';
pdbFile << cif::format("CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11.11s%4d", r["length_a"].as<double>(), r["length_b"].as<double>(), r["length_c"].as<double>(), r["angle_alpha"].as<double>(), r["angle_beta"].as<double>(), r["angle_gamma"].as<double>(), symmetry, r["Z_PDB"].as<int>()) << '\n';
}
int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
@@ -3321,18 +3327,18 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
for (auto r : db["database_PDB_matrix"])
{
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 1, r["origx[1][1]"].as<float>(), r["origx[1][2]"].as<float>(), r["origx[1][3]"].as<float>(), r["origx_vector[1]"].as<float>()) << '\n';
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 2, r["origx[2][1]"].as<float>(), r["origx[2][2]"].as<float>(), r["origx[2][3]"].as<float>(), r["origx_vector[2]"].as<float>()) << '\n';
pdbFile << cif::format("ORIGX{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 3, r["origx[3][1]"].as<float>(), r["origx[3][2]"].as<float>(), r["origx[3][3]"].as<float>(), r["origx_vector[3]"].as<float>()) << '\n';
pdbFile << cif::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 1, r["origx[1][1]"].as<float>(), r["origx[1][2]"].as<float>(), r["origx[1][3]"].as<float>(), r["origx_vector[1]"].as<float>()) << '\n';
pdbFile << cif::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 2, r["origx[2][1]"].as<float>(), r["origx[2][2]"].as<float>(), r["origx[2][3]"].as<float>(), r["origx_vector[2]"].as<float>()) << '\n';
pdbFile << cif::format("ORIGX%1d %10.6f%10.6f%10.6f %10.5f", 3, r["origx[3][1]"].as<float>(), r["origx[3][2]"].as<float>(), r["origx[3][3]"].as<float>(), r["origx_vector[3]"].as<float>()) << '\n';
result += 3;
break;
}
for (auto r : db["atom_sites"])
{
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 1, r["fract_transf_matrix[1][1]"].as<float>(), r["fract_transf_matrix[1][2]"].as<float>(), r["fract_transf_matrix[1][3]"].as<float>(), r["fract_transf_vector[1]"].as<float>()) << '\n';
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 2, r["fract_transf_matrix[2][1]"].as<float>(), r["fract_transf_matrix[2][2]"].as<float>(), r["fract_transf_matrix[2][3]"].as<float>(), r["fract_transf_vector[2]"].as<float>()) << '\n';
pdbFile << cif::format("SCALE{:1} {:10.6f}{:10.6f}{:10.6f} {:10.5f}", 3, r["fract_transf_matrix[3][1]"].as<float>(), r["fract_transf_matrix[3][2]"].as<float>(), r["fract_transf_matrix[3][3]"].as<float>(), r["fract_transf_vector[3]"].as<float>()) << '\n';
pdbFile << cif::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 1, r["fract_transf_matrix[1][1]"].as<float>(), r["fract_transf_matrix[1][2]"].as<float>(), r["fract_transf_matrix[1][3]"].as<float>(), r["fract_transf_vector[1]"].as<float>()) << '\n';
pdbFile << cif::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 2, r["fract_transf_matrix[2][1]"].as<float>(), r["fract_transf_matrix[2][2]"].as<float>(), r["fract_transf_matrix[2][3]"].as<float>(), r["fract_transf_vector[2]"].as<float>()) << '\n';
pdbFile << cif::format("SCALE%1d %10.6f%10.6f%10.6f %10.5f", 3, r["fract_transf_matrix[3][1]"].as<float>(), r["fract_transf_matrix[3][2]"].as<float>(), r["fract_transf_matrix[3][3]"].as<float>(), r["fract_transf_vector[3]"].as<float>()) << '\n';
result += 3;
break;
}
@@ -3342,9 +3348,9 @@ int WriteCoordinateTransformation(std::ostream &pdbFile, const datablock &db)
{
std::string given = r["code"] == "given" ? "1" : "";
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 1, nr, r["matrix[1][1]"].as<float>(), r["matrix[1][2]"].as<float>(), r["matrix[1][3]"].as<float>(), r["vector[1]"].as<float>(), given) << '\n';
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 2, nr, r["matrix[2][1]"].as<float>(), r["matrix[2][2]"].as<float>(), r["matrix[2][3]"].as<float>(), r["vector[2]"].as<float>(), given) << '\n';
pdbFile << cif::format("MTRIX{:1} {:3}{:10.6f}{:10.6f}{:10.6f} {:10.5f} {:1.1s}", 3, nr, r["matrix[3][1]"].as<float>(), r["matrix[3][2]"].as<float>(), r["matrix[3][3]"].as<float>(), r["vector[3]"].as<float>(), given) << '\n';
pdbFile << cif::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 1, nr, r["matrix[1][1]"].as<float>(), r["matrix[1][2]"].as<float>(), r["matrix[1][3]"].as<float>(), r["vector[1]"].as<float>(), given) << '\n';
pdbFile << cif::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 2, nr, r["matrix[2][1]"].as<float>(), r["matrix[2][2]"].as<float>(), r["matrix[2][3]"].as<float>(), r["vector[2]"].as<float>(), given) << '\n';
pdbFile << cif::format("MTRIX%1d %3d%10.6f%10.6f%10.6f %10.5f %1.1s", 3, nr, r["matrix[3][1]"].as<float>(), r["matrix[3][2]"].as<float>(), r["matrix[3][3]"].as<float>(), r["vector[3]"].as<float>(), given) << '\n';
++nr;
result += 3;
@@ -3363,6 +3369,10 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
auto &atom_site = db["atom_site"];
auto &atom_site_anisotrop = db["atom_site_anisotrop"];
auto &entity = db["entity"];
// auto &pdbx_poly_seq_scheme = db["pdbx_poly_seq_scheme"];
// auto &pdbx_nonpoly_scheme = db["pdbx_nonpoly_scheme"];
auto &pdbx_branch_scheme = db["pdbx_branch_scheme"];
int serial = 1;
auto ri = atom_site.begin();
@@ -3383,7 +3393,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
{
int nr = 0;
auto r = std::from_chars(modelNum.data(), modelNum.data() + modelNum.length(), nr);
if ((bool)r.ec)
if (r.ec != std::errc())
{
if (VERBOSE > 0)
std::cerr << "Model number '" << modelNum << "' is not a valid integer\n";
@@ -3407,7 +3417,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
if (terminate)
{
pdbFile << cif::format("TER {:5} {:3.3s} {:1.1s}{:4}{:1.1s}", serial, resName, chainID, resSeq, iCode) << '\n';
pdbFile << cif::format("TER %5d %3.3s %1.1s%4d%1.1s", serial, resName, chainID, resSeq, iCode) << '\n';
++serial;
terminatedChains.insert(chainID);
@@ -3436,6 +3446,26 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
r.get("id", "group_PDB", "label_atom_id", "label_alt_id", "auth_comp_id", "auth_asym_id", "auth_seq_id",
"pdbx_PDB_ins_code", "Cartn_x", "Cartn_y", "Cartn_z", "occupancy", "B_iso_or_equiv", "type_symbol", "pdbx_formal_charge");
if (resName != "HOH")
{
int entity_id = r.get<int>("label_entity_id");
try
{
auto type = entity.find1<std::string>("id"_key == entity_id, "type");
if (type == "branched") // find the real auth_seq_num, since sugars have their auth_seq_num reused as sugar number... sigh.
resSeq = pdbx_branch_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
// else if (type == "non-polymer") // same for non-polymers
// resSeq = pdbx_nonpoly_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
// else if (type == "polymer")
// resSeq = pdbx_poly_seq_scheme.find1<int>("asym_id"_key == r.get<std::string>("label_asym_id") and "pdb_seq_num"_key == resSeq, "auth_seq_num");
}
catch (const std::exception &ex)
{
std::cerr << "Oops, there was not exactly one entity with id " << entity_id << '\n';
}
}
if (chainID.length() > 1)
throw std::runtime_error("Chain ID " + chainID + " won't fit into a PDB file");
@@ -3446,8 +3476,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
if (charge != 0)
sCharge = std::to_string(charge) + (charge > 0 ? '+' : '-');
pdbFile << cif::format("{:<6.6s}{:5} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:8.3f}{:8.3f}{:8.3f}{:6.2f}{:6.2f} {:>2.2s}{:2.2s}",
group, serial, name, altLoc, resName, chainID, resSeq, iCode, x, y, z, occupancy, tempFactor, element, sCharge) << '\n';
pdbFile << cif::format("%-6.6s%5d %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %8.3f%8.3f%8.3f%6.2f%6.2f %2.2s%2.2s", group, serial, name, altLoc, resName, chainID, resSeq, iCode, x, y, z, occupancy, tempFactor, element, sCharge) << '\n';
++numCoord;
@@ -3462,7 +3491,7 @@ std::tuple<int, int> WriteCoordinatesForModel(std::ostream &pdbFile, const datab
tie(u11, u22, u33, u12, u13, u23) =
ai.get("U[1][1]", "U[2][2]", "U[3][3]", "U[1][2]", "U[1][3]", "U[2][3]");
pdbFile << cif::format("ANISOU{:5} {:<4.4s}{:1.1s}{:3.3s} {:1.1s}{:4}{:1.1s} {:7}{:7}{:7}{:7}{:7}{:7} {:2.2s}{:2.2s}", serial, name, altLoc, resName, chainID, resSeq, iCode, std::lrintf(u11 * 10000), std::lrintf(u22 * 10000), std::lrintf(u33 * 10000), std::lrintf(u12 * 10000), std::lrintf(u13 * 10000), std::lrintf(u23 * 10000), element, sCharge) << '\n';
pdbFile << cif::format("ANISOU%5d %-4.4s%1.1s%3.3s %1.1s%4d%1.1s %7d%7d%7d%7d%7d%7d %2.2s%2.2s", serial, name, altLoc, resName, chainID, resSeq, iCode, std::lrintf(u11 * 10000), std::lrintf(u22 * 10000), std::lrintf(u33 * 10000), std::lrintf(u12 * 10000), std::lrintf(u13 * 10000), std::lrintf(u23 * 10000), element, sCharge) << '\n';
}
++serial;
@@ -3514,7 +3543,7 @@ std::tuple<int, int> WriteCoordinate(std::ostream &pdbFile, const datablock &db)
for (int model_nr : models)
{
if (models.size() > 1)
pdbFile << cif::format("MODEL {:4}", model_nr) << '\n';
pdbFile << cif::format("MODEL %4d", model_nr) << '\n';
std::set<std::string> TERminatedChains;
auto n = WriteCoordinatesForModel(pdbFile, db, last_resseq_for_chain_map, TERminatedChains, model_nr);
@@ -3586,7 +3615,7 @@ std::string get_HEADER_line(const datablock &db, std::string::size_type truncate
}
}
return FixStringLength(cif::format("HEADER {:<40.40s}{:<9.9s} {:<4.4s}", keywords, date, db.name()), truncate_at);
return FixStringLength(cif::format("HEADER %-40.40s%-9.9s %-4.4s", keywords, date, db.name()).str(), truncate_at);
}
std::string get_COMPND_line(const datablock &db, std::string::size_type truncate_at)
@@ -3759,7 +3788,7 @@ void write(std::ostream &os, const datablock &db)
numXform = WriteCoordinateTransformation(os, db);
std::tie(numCoord, numTer) = WriteCoordinate(os, db);
os << cif::format("MASTER {:5} 0{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}{:5}", numRemark, numHet, numHelix, numSheet, numTurn, numSite, numXform, numCoord, numTer, numConect, numSeq) << '\n'
os << cif::format("MASTER %5d 0%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d", numRemark, numHet, numHelix, numSheet, numTurn, numSite, numXform, numCoord, numTer, numConect, numSeq) << '\n'
<< "END\n";
}

File diff suppressed because it is too large Load Diff

View File

@@ -980,8 +980,8 @@ std::string Remark3Parser::nextLine()
while (mRec != nullptr and mRec->is("REMARK 3"))
{
std::size_t valueIndent = 0;
for (std::size_t i = 4; i < mRec->mVlen; ++i)
size_t valueIndent = 0;
for (size_t i = 4; i < mRec->mVlen; ++i)
{
if (mRec->mValue[i] == ' ')
continue;
@@ -1232,7 +1232,7 @@ void Remark3Parser::storeCapture(const char *category, std::initializer_list<con
}
// else if (iequals(category, "struct_ncs_dom"))
// {
// std::size_t id = cat.size() + 1;
// size_t id = cat.size() + 1;
//
// cat.emplace({
// { "id", id }
@@ -1478,13 +1478,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
best.parser->fixup();
auto &validator = cif::validator_factory::instance().get("mmcif_pdbx.dic");
for (auto &cat1 : best.parser->mDb)
{
if (cat1.empty())
continue;
auto &cat2 = db[cat1.name()];
// copy only the values in the first row for the following categories
@@ -1498,15 +1493,8 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
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();
for (auto column : cat1.key_fields())
r2[column] = r1[column].text();
}
}
else

View File

@@ -40,24 +40,24 @@ struct PDBRecord
PDBRecord *mNext;
uint32_t mLineNr;
char mName[11];
std::size_t mVlen;
size_t mVlen;
char mValue[1];
PDBRecord(uint32_t lineNr, const std::string &name, const std::string &value);
~PDBRecord();
void *operator new(std::size_t);
void *operator new(std::size_t size, std::size_t vLen);
void *operator new(size_t);
void *operator new(size_t size, size_t vLen);
void operator delete(void *p);
void operator delete(void *p, std::size_t vLen);
void operator delete(void *p, size_t vLen);
bool is(const char *name) const;
char vC(std::size_t column);
std::string vS(std::size_t columnFirst, std::size_t columnLast = std::numeric_limits<std::size_t>::max());
char vC(size_t column);
std::string vS(size_t columnFirst, size_t columnLast = std::numeric_limits<size_t>::max());
int vI(int columnFirst, int columnLast);
std::string vF(std::size_t columnFirst, std::size_t columnLast);
std::string vF(size_t columnFirst, size_t columnLast);
};
} // namespace pdbx

File diff suppressed because it is too large Load Diff

View File

@@ -1,337 +0,0 @@
/*-
* 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
* 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++.hpp"
namespace cif::pdb
{
condition get_parents_condition(const validator &validator, row_handle rh, const category &parentCat)
{
condition result;
auto &childCat = rh.get_category();
auto childName = childCat.name();
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());
if (not links.empty())
{
for (auto &link : links)
{
condition cond;
for (std::size_t ix = 0; ix < link->m_child_keys.size(); ++ix)
{
auto childValue = rh[link->m_child_keys[ix]];
if (childValue.empty())
continue;
cond = std::move(cond) and key(link->m_parent_keys[ix]) == childValue.text();
}
result = std::move(result) or std::move(cond);
}
}
return result;
}
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;
}
bool is_valid_pdbx_file(const file &file, std::error_code &ec)
{
bool result = false;
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);
else
result = is_valid_pdbx_file(file, validator_factory::instance().get("mmcif_pdbx.dic"), ec);
return result;
}
bool is_valid_pdbx_file(const file &file, const validator &validator, std::error_code &ec)
{
using namespace cif::literals;
bool result = true, warned_missing_parents = false;
try
{
auto &cf = cif::compound_factory::instance();
if (file.empty())
throw std::runtime_error("Empty file");
auto &db = file.front();
if (db.empty())
throw std::runtime_error("Empty datablock");
auto &atom_site = db["atom_site"];
if (atom_site.empty())
throw std::runtime_error("Empty or missing atom_site category");
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)
{
auto seq_id = r.get<std::optional<int>>("label_seq_id");
if (not seq_id.has_value()) // not a residue in a polymer
continue;
if (*seq_id == last_seq_id)
continue;
last_seq_id = *seq_id;
auto comp_id = r.get<std::string>("label_comp_id");
if (not cf.is_monomer(comp_id))
continue;
auto cond = get_parents_condition(validator, r, pdbx_poly_seq_scheme);
if (not cond)
{
if (VERBOSE > 0 and std::exchange(warned_missing_parents, true) == false)
std::cerr << "warning: missing links for atom_site/pdbx_poly_seq_scheme\n";
continue;
}
auto p = pdbx_poly_seq_scheme.find(std::move(cond));
if (p.size() != 1)
{
if (VERBOSE > 0)
std::clog << "In atom_site record: " << r["id"].text() << '\n';
throw std::runtime_error("For each monomer in atom_site there should be exactly one pdbx_poly_seq_scheme record");
}
}
auto &entity = db["entity"];
if (entity.empty())
throw std::runtime_error("Entity category is missing or empty");
auto &entity_poly = db["entity_poly"];
if (entity_poly.empty())
throw std::runtime_error("Entity_poly category is missing or empty");
auto &entity_poly_seq = db["entity_poly_seq"];
if (entity_poly_seq.empty())
throw std::runtime_error("Entity_poly_seq category is missing or empty");
auto &struct_asym = db["struct_asym"];
if (struct_asym.empty())
throw std::runtime_error("struct_asym category is missing or empty");
for (auto entity_id : entity.find<std::string>("type"_key == "polymer", "id"))
{
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");
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"))
{
mon_per_seq_id[num].emplace(mon_id);
for (auto asym_id : struct_asym.find<std::string>("entity_id"_key == entity_id, "id"))
{
if (pdbx_poly_seq_scheme.count(
"entity_id"_key == entity_id and
"asym_id"_key == asym_id and
"mon_id"_key == mon_id and
"seq_id"_key == num and
"hetero"_key == hetero) != 1)
{
throw std::runtime_error("For each entity_poly_seq record there should be exactly one pdbx_poly_seq record");
}
}
}
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"))
{
if (entity_poly_seq.count(
"entity_id"_key == entity_id and
"mon_id"_key == mon_id and
"num"_key == seq_id and
"hetero"_key == hetero) != 1)
{
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)
throw std::runtime_error("Mismatch between the hetero flag in the poly seq schemes and the number residues per seq_id");
}
// This code proved to take too much time ...
// 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");
auto seq_match = [&](bool can, std::string::const_iterator si, std::string::const_iterator se)
{
for (const auto &[seq_id, comp_ids] : mon_per_seq_id)
{
if (si == se)
return false;
bool match = false;
for (auto comp_id : comp_ids)
{
std::string letter;
if (can)
{
if (compound_factory::kBaseMap.contains(comp_id))
letter = compound_factory::kBaseMap.at(comp_id);
else
{
auto c = cf.create(comp_id);
if (c and c->one_letter_code())
letter = c->one_letter_code();
else
letter = "X";
}
}
else
{
if (compound_factory::kAAMap.contains(comp_id))
letter = compound_factory::kAAMap.at(comp_id);
else if (comp_id.length() == 1 and compound_factory::kBaseMap.contains(comp_id))
letter = compound_factory::kBaseMap.at(comp_id);
else
letter = '(' + comp_id + ')';
}
if (iequals(std::string{ si, si + letter.length() }, letter))
{
match = true;
si += letter.length();
break;
}
else
return false;
}
if (not match)
break;
}
return si == se;
};
if (not seq.has_value())
{
if (VERBOSE > 0)
std::clog << "Warning: entity_poly has no sequence for entity_id " << entity_id << '\n';
}
else
{
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);
}
if (not seq_can.has_value())
{
if (VERBOSE > 1)
std::clog << "Warning: entity_poly has no canonical sequence for entity_id " << entity_id << '\n';
}
else
{
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);
}
}
result = true;
}
catch (const std::exception &ex)
{
result = false;
if (VERBOSE > 0)
std::clog << ex.what() << '\n';
ec = make_error_code(validation_error::not_valid_pdbx);
}
if (not result and (bool) ec)
ec = make_error_code(validation_error::not_valid_pdbx);
return result;
}
} // namespace cif::pdb

View File

@@ -29,44 +29,44 @@
namespace cif
{
void row_handle::assign(uint16_t item, std::string_view value, bool updateLinked, bool validate)
void row_handle::assign(uint16_t column, std::string_view value, bool updateLinked, bool validate)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
m_category->update_value(m_row, item, value, updateLinked, validate);
m_category->update_value(m_row, column, value, updateLinked, validate);
}
uint16_t row_handle::get_item_ix(std::string_view name) const
uint16_t row_handle::get_column_ix(std::string_view name) const
{
if (not m_category)
throw std::runtime_error("uninitialized row");
return m_category->get_item_ix(name);
return m_category->get_column_ix(name);
}
std::string_view row_handle::get_item_name(uint16_t ix) const
std::string_view row_handle::get_column_name(uint16_t ix) const
{
if (not m_category)
throw std::runtime_error("uninitialized row");
return m_category->get_item_name(ix);
return m_category->get_column_name(ix);
}
uint16_t row_handle::add_item(std::string_view name)
uint16_t row_handle::add_column(std::string_view name)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
return m_category->add_item(name);
return m_category->add_column(name);
}
void row_handle::swap(uint16_t item, row_handle &b)
void row_handle::swap(uint16_t column, row_handle &b)
{
if (not m_category)
throw std::runtime_error("uninitialized row");
m_category->swap_item(item, *this, b);
m_category->swap_item(column, *this, b);
}
// --------------------------------------------------------------------
@@ -86,7 +86,7 @@ row_initializer::row_initializer(row_handle rh)
auto &i = r->operator[](ix);
if (not i)
continue;
emplace_back(cat.get_item_name(ix), i.text());
emplace_back(cat.get_column_name(ix), i.text());
}
}

View File

@@ -32,12 +32,7 @@
#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
#endif
#include <Eigen/Eigen>
#include <Eigen/Eigenvalues>
namespace cif
{
@@ -95,10 +90,10 @@ float cell::get_volume() const
auto cos_beta = std::cos(beta);
auto cos_gamma = std::cos(gamma);
double vol = m_a * m_b * m_c;
auto vol = m_a * m_b * m_c;
vol *= std::sqrt(1.0f - cos_alpha * cos_alpha - cos_beta * cos_beta - cos_gamma * cos_gamma + 2.0f * cos_alpha * cos_beta * cos_gamma);
return static_cast<float>(vol);
return vol;
}
// --------------------------------------------------------------------
@@ -108,15 +103,15 @@ sym_op::sym_op(std::string_view s)
auto b = s.data();
auto e = b + s.length();
int rnri = 256; // default to unexisting number
int rnri = 256; // default to unexisting number
auto r = std::from_chars(b, e, rnri);
m_nr = static_cast<uint8_t>(rnri);
m_ta = r.ptr[1] - '0';
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,16 +119,16 @@ 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 = 0;
return { b, static_cast<std::size_t>(r.ptr - b) };
return { b, static_cast<size_t>(r.ptr - b) };
}
// --------------------------------------------------------------------
@@ -168,16 +163,41 @@ transformation::transformation(const matrix3x3<float> &r, const cif::point &t)
void transformation::try_create_quaternion()
{
Eigen::Matrix3f rot;
float Qxx = m_rotation(0, 0);
float Qxy = m_rotation(0, 1);
float Qxz = m_rotation(0, 2);
float Qyx = m_rotation(1, 0);
float Qyy = m_rotation(1, 1);
float Qyz = m_rotation(1, 2);
float Qzx = m_rotation(2, 0);
float Qzy = m_rotation(2, 1);
float Qzz = m_rotation(2, 2);
rot << m_rotation(0, 0), m_rotation(0, 1), m_rotation(0, 2),
m_rotation(1, 0), m_rotation(1, 1), m_rotation(1, 2),
m_rotation(2, 0), m_rotation(2, 1), m_rotation(2, 2);
Eigen::Matrix4f em;
if (rot * rot.transpose() == Eigen::Matrix3f::Identity() and rot.determinant() == 1)
em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
auto ev = es.eigenvalues();
for (size_t j = 0; j < 4; ++j)
{
Eigen::Quaternionf qe(rot);
m_q = normalize(cif::quaternion{ qe.w(), qe.x(), qe.y(), qe.z() });
if (std::abs(ev[j].real() - 1) > 0.01)
continue;
auto col = es.eigenvectors().col(j);
m_q = normalize(cif::quaternion{
static_cast<float>(col(3).real()),
static_cast<float>(col(0).real()),
static_cast<float>(col(1).real()),
static_cast<float>(col(2).real()) });
break;
}
}
@@ -201,7 +221,7 @@ transformation inverse(const transformation &t)
spacegroup::spacegroup(int nr)
: m_nr(nr)
{
const std::size_t N = kSymopNrTableSize;
const size_t N = kSymopNrTableSize;
int32_t L = 0, R = static_cast<int32_t>(N - 1);
while (L <= R)
{
@@ -214,7 +234,7 @@ spacegroup::spacegroup(int nr)
m_index = L;
for (std::size_t i = L; i < N and kSymopNrTable[i].spacegroup() == m_nr; ++i)
for (size_t i = L; i < N and kSymopNrTable[i].spacegroup() == m_nr; ++i)
emplace_back(kSymopNrTable[i].symop().data());
}
@@ -277,7 +297,7 @@ point spacegroup::operator()(const point &pt, const cell &c, sym_op symop) const
{
if (symop.m_nr < 1 or symop.m_nr > size())
throw std::out_of_range("symmetry operator number out of range");
transformation t = at(symop.m_nr - 1);
t.m_translation.m_x += symop.m_ta - 5;
@@ -296,7 +316,7 @@ point spacegroup::inverse(const point &pt, const cell &c, sym_op symop) const
{
if (symop.m_nr < 1 or symop.m_nr > size())
throw std::out_of_range("symmetry operator number out of range");
transformation t = at(symop.m_nr - 1);
t.m_translation.m_x += symop.m_ta - 5;
@@ -323,7 +343,7 @@ int get_space_group_number(std::string_view spacegroup)
int result = 0;
const std::size_t N = kNrOfSpaceGroups;
const size_t N = kNrOfSpaceGroups;
int32_t L = 0, R = static_cast<int32_t>(N - 1);
while (L <= R)
{
@@ -345,7 +365,7 @@ 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 (size_t i = 0; i < kNrOfSpaceGroups; ++i)
{
auto &sp = kSpaceGroups[i];
if (sp.xHM == spacegroup)
@@ -375,7 +395,7 @@ int get_space_group_number(std::string_view spacegroup, space_group_name type)
if (type == space_group_name::full)
{
const std::size_t N = kNrOfSpaceGroups;
const size_t N = kNrOfSpaceGroups;
int32_t L = 0, R = static_cast<int32_t>(N - 1);
while (L <= R)
{
@@ -430,13 +450,13 @@ int get_space_group_number(const datablock &db)
if (_symmetry.size() != 1)
throw std::runtime_error("Could not find a unique symmetry in this mmCIF file");
return _symmetry.front().get<int>("Int_Tables_number");
}
// --------------------------------------------------------------------
std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b) const
std::tuple<float,point,sym_op> crystal::closest_symmetry_copy(point a, point b) const
{
if (m_cell.get_a() == 0 or m_cell.get_b() == 0 or m_cell.get_c() == 0)
throw std::runtime_error("Invalid cell, contains a dimension that is zero");
@@ -455,7 +475,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
a = orthogonal(fa, m_cell);
for (std::size_t i = 0; i < m_spacegroup.size(); ++i)
for (size_t i = 0; i < m_spacegroup.size(); ++i)
{
sym_op s(static_cast<uint8_t>(i + 1));
auto &t = m_spacegroup[i];
@@ -471,7 +491,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
while (fsb.m_x + 0.5f < fa.m_x)
{
fsb.m_x += 1;
s.m_ta += 1;
s.m_ta += 1;
}
while (fsb.m_y - 0.5f > fa.m_y)
@@ -483,7 +503,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
while (fsb.m_y + 0.5f < fa.m_y)
{
fsb.m_y += 1;
s.m_tb += 1;
s.m_tb += 1;
}
while (fsb.m_z - 0.5f > fa.m_z)
@@ -495,7 +515,7 @@ std::tuple<float, point, sym_op> crystal::closest_symmetry_copy(point a, point b
while (fsb.m_z + 0.5f < fa.m_z)
{
fsb.m_z += 1;
s.m_tc += 1;
s.m_tc += 1;
}
auto p = orthogonal(fsb, m_cell);

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;
@@ -426,7 +426,7 @@ const space_group kSpaceGroups[] =
out << R"(
};
const std::size_t kNrOfSpaceGroups = sizeof(kSpaceGroups) / sizeof(space_group);
const size_t kNrOfSpaceGroups = sizeof(kSpaceGroups) / sizeof(space_group);
const symop_datablock kSymopNrTable[] = {
)";
@@ -450,7 +450,7 @@ const symop_datablock kSymopNrTable[] = {
out << R"(};
const std::size_t kSymopNrTableSize = sizeof(kSymopNrTable) / sizeof(symop_datablock);
const size_t kSymopNrTableSize = sizeof(kSymopNrTable) / sizeof(symop_datablock);
} // namespace mmcif
)";

View File

@@ -361,7 +361,7 @@ const space_group kSpaceGroups[] =
};
const std::size_t kNrOfSpaceGroups = sizeof(kSpaceGroups) / sizeof(space_group);
const size_t kNrOfSpaceGroups = sizeof(kSpaceGroups) / sizeof(space_group);
const symop_datablock kSymopNrTable[] = {
// P 1
@@ -5286,6 +5286,6 @@ const symop_datablock kSymopNrTable[] = {
{ 5005, 4, { -1, 0, 0, 0, 1, 0, 0, 0,-1, 1, 2, 0, 0, 1, 2, } },
};
const std::size_t kSymopNrTableSize = sizeof(kSymopNrTable) / sizeof(symop_datablock);
const size_t kSymopNrTableSize = sizeof(kSymopNrTable) / sizeof(symop_datablock);
} // namespace mmcif

View File

@@ -29,35 +29,30 @@
#include <algorithm>
#include <cassert>
#if __has_include("fast_float/fast_float.h")
#include "fast_float/fast_float.h"
#endif
namespace cif
{
// --------------------------------------------------------------------
// This really makes a difference, having our own tolower routines
const uint8_t kCharToLowerMap[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
const uint8_t kCharToLowerMap[256] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
// --------------------------------------------------------------------
@@ -74,7 +69,7 @@ bool iequals(const char *a, const char *b)
{
bool result = true;
for (; result and *a and *b; ++a, ++b)
result = kCharToLowerMap[uint8_t(*a)] == kCharToLowerMap[uint8_t(*b)];
result = tolower(*a) == tolower(*b);
return result and *a == *b;
}
@@ -85,7 +80,7 @@ int icompare(std::string_view a, std::string_view b)
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 = tolower(*ai) - tolower(*bi);
if (d == 0)
{
@@ -103,7 +98,7 @@ int icompare(const char *a, const char *b)
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 = tolower(*a) - tolower(*b);
if (d == 0)
{
@@ -176,7 +171,7 @@ std::string trim_right_copy(std::string_view s)
e = pe;
}
return { s.begin(), e };
return {s.begin(), e};
}
std::string trim_left_copy(std::string_view s)
@@ -190,46 +185,27 @@ std::string trim_left_copy(std::string_view s)
b = std::next(b);
}
return { b, s.end() };
return {b, s.end()};
}
void trim_left(std::string &s)
{
auto in = s.begin(), out = s.begin();
while (in != s.end() and std::isspace(*in))
++in;
if (in == s.end())
s.clear();
else if (in != out)
auto b = s.begin();
while (b != s.end())
{
while (in != s.end())
*out++ = *in++;
s.erase(out, s.end());
if (not std::isspace(*b))
break;
b = std::next(b);
}
s.erase(s.begin(), b);
}
void trim(std::string &s)
{
auto in = s.begin(), out = s.begin(), end = s.end();
while (end != s.begin() and std::isspace(*(end - 1)))
--end;
while (in != end and std::isspace(*in))
++in;
if (in == end)
s.clear();
else if (in != out)
{
while (in != end)
*out++ = *in++;
s.erase(out, s.end());
}
else if (end != s.end())
s.erase(end, s.end());
trim_right(s);
trim_left(s);
}
std::string trim_copy(std::string_view s)
@@ -239,19 +215,19 @@ std::string trim_copy(std::string_view s)
// --------------------------------------------------------------------
std::tuple<std::string, std::string> split_item_name(std::string_view item_name)
std::tuple<std::string, std::string> split_tag_name(std::string_view tag)
{
if (item_name.empty())
throw std::runtime_error("empty item_name");
if (item_name[0] != '_')
throw std::runtime_error("item_name '" + std::string{ item_name } + "' does not start with underscore");
if (tag.empty())
throw std::runtime_error("empty tag");
if (tag[0] != '_')
throw std::runtime_error("tag '" + std::string { tag } + "' does not start with underscore");
auto s = item_name.find('.');
auto s = tag.find('.');
if (s == std::string::npos)
// throw std::runtime_error("item_name does not contain dot (" + std::string{ item_name } + ')');
return std::tuple<std::string, std::string>{ "", item_name.substr(1) };
// throw std::runtime_error("tag does not contain dot (" + std::string{ tag } + ')');
return std::tuple<std::string, std::string>{ "", tag.substr(1) };
else
return std::tuple<std::string, std::string>{ item_name.substr(1, s - 1), item_name.substr(s + 1) };
return std::tuple<std::string, std::string>{tag.substr(1, s - 1), tag.substr(s + 1)};
}
// --------------------------------------------------------------------
@@ -266,7 +242,8 @@ std::string cif_id_for_number(int number)
result += static_cast<char>('A' + r);
number = (number - r) / 26 - 1;
} while (number >= 0);
}
while (number >= 0);
std::reverse(result.begin(), result.end());
@@ -321,29 +298,29 @@ enum LineBreakClass
kLBC_Unknown
};
const LineBreakClass kASCII_LBTable[128] = {
kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_CombiningMark, kLBC_BreakAfter, kLBC_LineFeed, kLBC_MandatoryBreak, kLBC_MandatoryBreak, kLBC_CarriageReturn, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_Space, kLBC_Exlamation, kLBC_Quotation, kLBC_Alphabetic, kLBC_PrefixNumeric, kLBC_PostfixNumeric, kLBC_Alphabetic, kLBC_Quotation,
kLBC_OpenPunctuation, kLBC_CloseParenthesis, kLBC_Alphabetic, kLBC_PrefixNumeric,
const LineBreakClass kASCII_LBTable[128] =
{
kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_CombiningMark, kLBC_BreakAfter, kLBC_LineFeed, kLBC_MandatoryBreak, kLBC_MandatoryBreak, kLBC_CarriageReturn, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark, kLBC_CombiningMark,
kLBC_Space, kLBC_Exlamation, kLBC_Quotation, kLBC_Alphabetic, kLBC_PrefixNumeric, kLBC_PostfixNumeric, kLBC_Alphabetic, kLBC_Quotation,
kLBC_OpenPunctuation, kLBC_CloseParenthesis, kLBC_Alphabetic, kLBC_PrefixNumeric,
// comma treated differently here, it is not a numeric separator in PDB
kLBC_SymbolAllowingBreakAfter /* kLBC_InfixNumericSeparator */,
// comma treated differently here, it is not a numeric separator in PDB
kLBC_SymbolAllowingBreakAfter /* kLBC_InfixNumericSeparator */,
kLBC_Hyphen, kLBC_InfixNumericSeparator, kLBC_SymbolAllowingBreakAfter,
kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric,
kLBC_Numeric, kLBC_Numeric, kLBC_InfixNumericSeparator, kLBC_InfixNumericSeparator, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Exlamation,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_OpenPunctuation, kLBC_PrefixNumeric, kLBC_CloseParenthesis, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_OpenPunctuation, kLBC_BreakAfter, kLBC_ClosePunctuation, kLBC_Alphabetic, kLBC_CombiningMark
};
kLBC_Hyphen, kLBC_InfixNumericSeparator, kLBC_SymbolAllowingBreakAfter,
kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric, kLBC_Numeric,
kLBC_Numeric, kLBC_Numeric, kLBC_InfixNumericSeparator, kLBC_InfixNumericSeparator, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Exlamation,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_OpenPunctuation, kLBC_PrefixNumeric, kLBC_CloseParenthesis, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic,
kLBC_Alphabetic, kLBC_Alphabetic, kLBC_Alphabetic, kLBC_OpenPunctuation, kLBC_BreakAfter, kLBC_ClosePunctuation, kLBC_Alphabetic, kLBC_CombiningMark};
std::string::const_iterator nextLineBreak(std::string::const_iterator text, std::string::const_iterator end)
{
@@ -361,33 +338,33 @@ std::string::const_iterator nextLineBreak(std::string::const_iterator text, std:
const breakAction brkTable[27][27] = {
// OP CL CP QU GL NS EX SY IS PR PO NU AL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT
/* OP */ { PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, CPB, PBK, PBK, PBK, PBK, PBK, PBK },
/* CL */ { DBK, PBK, PBK, IBK, IBK, PBK, PBK, PBK, PBK, IBK, IBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* CP */ { DBK, PBK, PBK, IBK, IBK, PBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* QU */ { PBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK },
/* GL */ { IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK },
/* NS */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* EX */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* SY */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* IS */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* PR */ { IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK },
/* PO */ { IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* NU */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* AL */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* ID */ { 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, DBK },
/* IN */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* HY */ { DBK, PBK, PBK, IBK, DBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* BA */ { DBK, PBK, PBK, IBK, DBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* BB */ { IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK },
/* B2 */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, PBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* ZW */ { DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK },
/* CM */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK },
/* WJ */ { IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK },
/* H2 */ { 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, IBK, IBK },
/* H3 */ { 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 },
/* JL */ { DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, DBK },
/* JV */ { 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, IBK, IBK },
/* 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 },
/* OP */ {PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, PBK, CPB, PBK, PBK, PBK, PBK, PBK, PBK},
/* CL */ {DBK, PBK, PBK, IBK, IBK, PBK, PBK, PBK, PBK, IBK, IBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* CP */ {DBK, PBK, PBK, IBK, IBK, PBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* QU */ {PBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK},
/* GL */ {IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK},
/* NS */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* EX */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* SY */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* IS */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* PR */ {IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, IBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK},
/* PO */ {IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* NU */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* AL */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* ID */ {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, DBK},
/* IN */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* HY */ {DBK, PBK, PBK, IBK, DBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* BA */ {DBK, PBK, PBK, IBK, DBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* BB */ {IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK},
/* B2 */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, IBK, IBK, DBK, PBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* ZW */ {DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK, PBK, DBK, DBK, DBK, DBK, DBK, DBK, DBK},
/* CM */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, DBK, IBK, IBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, DBK, DBK, DBK, DBK, DBK},
/* WJ */ {IBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, IBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, IBK},
/* H2 */ {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, IBK, IBK},
/* H3 */ {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},
/* JL */ {DBK, PBK, PBK, IBK, IBK, IBK, PBK, PBK, PBK, DBK, IBK, DBK, DBK, DBK, IBK, IBK, IBK, DBK, DBK, PBK, CIB, PBK, IBK, IBK, IBK, IBK, DBK},
/* JV */ {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, IBK, IBK},
/* 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);
@@ -438,10 +415,10 @@ std::string::const_iterator nextLineBreak(std::string::const_iterator text, std:
return text;
}
std::vector<std::string> wrapLine(const std::string &text, std::size_t width)
std::vector<std::string> wrapLine(const std::string &text, size_t width)
{
std::vector<std::string> result;
std::vector<std::size_t> offsets = { 0 };
std::vector<size_t> offsets = {0};
auto b = text.begin();
while (b != text.end())
@@ -453,18 +430,18 @@ std::vector<std::string> wrapLine(const std::string &text, std::size_t width)
b = e;
}
std::size_t count = offsets.size() - 1;
size_t count = offsets.size() - 1;
std::vector<std::size_t> minima(count + 1, 1000000);
std::vector<size_t> minima(count + 1, 1000000);
minima[0] = 0;
std::vector<std::size_t> breaks(count + 1, 0);
std::vector<size_t> breaks(count + 1, 0);
for (std::size_t i = 0; i < count; ++i)
for (size_t i = 0; i < count; ++i)
{
std::size_t j = i + 1;
size_t j = i + 1;
while (j <= count)
{
std::size_t w = offsets[j] - offsets[i];
size_t w = offsets[j] - offsets[i];
if (w > width)
break;
@@ -472,7 +449,7 @@ std::vector<std::string> wrapLine(const std::string &text, std::size_t width)
while (w > 0 and isspace(text[offsets[i] + w - 1]))
--w;
std::size_t cost = minima[i];
size_t cost = minima[i];
if (j < count) // last line may be shorter
cost += (width - w) * (width - w);
@@ -486,10 +463,10 @@ std::vector<std::string> wrapLine(const std::string &text, std::size_t width)
}
}
std::size_t j = count;
size_t j = count;
while (j > 0)
{
std::size_t i = breaks[j];
size_t i = breaks[j];
result.push_back(text.substr(offsets[i], offsets[j] - offsets[i]));
j = i;
}
@@ -499,7 +476,7 @@ std::vector<std::string> wrapLine(const std::string &text, std::size_t width)
return result;
}
std::vector<std::string> word_wrap(const std::string &text, std::size_t width)
std::vector<std::string> word_wrap(const std::string &text, size_t width)
{
std::vector<std::string> result;
for (auto p : cif::split<std::string>(text, "\n"))
@@ -517,22 +494,4 @@ std::vector<std::string> word_wrap(const std::string &text, std::size_t width)
return result;
}
#if __has_include("fast_float/fast_float.h")
template<>
std::from_chars_result ff_charconv<float>::from_chars(const char *a, const char *b, float &v)
{
auto r = fast_float::from_chars(a, b, v);
return { r.ptr, r.ec };
}
template<>
std::from_chars_result ff_charconv<double>::from_chars(const char *a, const char *b, double &v)
{
auto r = fast_float::from_chars(a, b, v);
return { r.ptr, r.ec };
}
#endif
} // namespace cif

View File

@@ -34,20 +34,14 @@
#include <condition_variable>
#include <cstring>
#include <deque>
#include <format>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#if __cpp_lib_jthread >= 201911L
#include <stop_token>
#endif
namespace fs = std::filesystem;
@@ -69,52 +63,29 @@ std::string get_version_nr()
// --------------------------------------------------------------------
#if defined(_WIN32) or defined(__MINGW32__)
#ifdef _WIN32
}
// clang-format off
# include <windows.h>
# include <libloaderapi.h>
# include <wincon.h>
// clang-format on
#include <Windows.h>
#include <libloaderapi.h>
#include <wincon.h>
#include <codecvt>
namespace cif
{
uint32_t get_terminal_width()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
return ::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
? csbi.srWindow.Right - csbi.srWindow.Left + 1
: 80;
}
void write_to_console(const std::string &s)
{
auto h = ::GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (auto l = ::MultiByteToWideChar(CP_UTF8, 0, s.data(), s.length(), nullptr, 0);
l > 0 and ::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
{
std::u16string ws(l, 0);
::MultiByteToWideChar(CP_UTF8, 0, s.data(), s.length(), (LPWSTR)ws.data(), l);
DWORD w;
::WriteConsoleW(h, ws.data(), ws.length(), &w, nullptr);
}
else
{
std::cout.write(s.data(), s.length());
std::cout.flush();
}
CONSOLE_SCREEN_BUFFER_INFO csbi;
::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#else
# include <limits.h>
# include <sys/ioctl.h>
# include <termios.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <limits.h>
uint32_t get_terminal_width()
{
@@ -129,220 +100,59 @@ uint32_t get_terminal_width()
return result;
}
inline void write_to_console(const std::string &s)
{
std::cout << s << std::flush;
}
#endif
// --------------------------------------------------------------------
struct progress_bar_impl
{
progress_bar_impl(uint64_t max_value, const std::string &message)
: m_max_value(max_value)
progress_bar_impl(int64_t inMax, const std::string &inAction)
: m_max_value(inMax)
, m_consumed(0)
, m_action(message)
, m_message(message)
, m_action(inAction)
, m_message(inAction)
, m_thread(std::bind(&progress_bar_impl::run, this))
{
}
virtual ~progress_bar_impl() {}
progress_bar_impl(const progress_bar_impl&) = delete;
progress_bar_impl &operator=(const progress_bar_impl &) = delete;
virtual void consumed(uint64_t n);
virtual void progress(uint64_t p);
virtual void message(const std::string &msg);
virtual void print_done();
~progress_bar_impl();
void run();
void consumed(int64_t n);
void progress(int64_t p);
void message(const std::string &msg);
void print_progress();
void print_done();
using time_point = std::chrono::time_point<std::chrono::system_clock>;
uint64_t m_max_value;
std::atomic<uint64_t> m_consumed;
int64_t m_max_value;
std::atomic<int64_t> m_consumed;
int64_t m_last_consumed = 0;
int m_spinner_index = 0;
std::string m_action, m_message;
time_point m_start = std::chrono::system_clock::now();
};
void progress_bar_impl::consumed(uint64_t n)
{
m_consumed += n;
}
void progress_bar_impl::progress(uint64_t p)
{
m_consumed = p;
}
void progress_bar_impl::message(const std::string &msg)
{
m_message = msg;
}
void progress_bar_impl::print_done()
{
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - m_start;
std::string days, hours, minutes, seconds;
uint64_t s = static_cast<uint64_t>(std::trunc(elapsed.count()));
if (s > 24 * 60 * 60)
{
days = std::format("{:d}d ", s / (24 * 60 * 60));
s %= 24 * 60 * 60;
}
if (s > 60 * 60)
{
hours = std::format("{:2d}h ", s / (60 * 60));
s %= 60 * 60;
}
if (s > 60)
{
minutes = std::format("{:2d}m ", s / 60);
s %= 60;
}
std::string msg = std::format("{} done in {}{}{}{:.1f}s", m_action, days, hours, minutes, s + 1e-6 * (elapsed.count() - s));
uint32_t width = get_terminal_width();
if (msg.length() < width)
msg += std::string(width - msg.length(), ' ');
write_to_console(msg += '\n');
}
// --------------------------------------------------------------------
struct simple_progress_bar_impl : public progress_bar_impl
{
simple_progress_bar_impl(uint64_t max_value, const std::string &message)
: progress_bar_impl(max_value, message)
{
}
~simple_progress_bar_impl()
{
if (m_printed_any)
print_done();
}
void consumed(uint64_t n) override
{
using namespace std::literals;
progress_bar_impl::consumed(n);
// print at most 10 steps, but only if it took long enough
int percentile = static_cast<int>(std::floor(10.f * m_consumed / m_max_value));
if (percentile > m_last_percentile and (m_printed_any or std::chrono::system_clock::now() - m_start >= 1s))
{
if (not std::exchange(m_printed_any, true))
write_to_console(m_action + ": ");
write_to_console(std::format("...{:d}0%", percentile));
m_last_percentile = percentile;
}
}
void progress(uint64_t p) override
{
consumed(p - m_consumed);
}
void print_done() override
{
if (m_printed_any)
{
write_to_console("\n");
progress_bar_impl::print_done();
}
}
bool m_printed_any = false;
int m_last_percentile = 0;
};
// --------------------------------------------------------------------
struct fancy_progress_bar_impl : public progress_bar_impl
{
fancy_progress_bar_impl(uint64_t max_value, const std::string &message)
: progress_bar_impl(max_value, message)
, m_thread(
#if __cpp_lib_jthread >= 201911L
[this](std::stop_token stoken)
{ this->run(stoken); }
#else
[this]()
{ this->run(); }
#endif
)
{
}
~fancy_progress_bar_impl();
#if __cpp_lib_jthread >= 201911L
void run(std::stop_token stoken);
#else
void run();
#endif
void consumed(uint64_t n) override;
void progress(uint64_t p) override;
void message(const std::string &msg) override;
void print_progress();
std::mutex m_mutex;
std::condition_variable m_cv;
float m_progress;
uint32_t m_width, m_bar_width;
uint32_t m_steps, m_last_steps = 0;
uint64_t m_last_consumed = 0;
#if __cpp_lib_jthread >= 201911L
std::jthread m_thread;
#else
std::thread m_thread;
time_point m_start = std::chrono::system_clock::now();
time_point m_last = std::chrono::system_clock::now();
bool m_stop = false;
#endif
};
const char *kBlocks[] = {
" ",
"",
"",
"",
"",
"",
"",
"",
"",
};
const size_t kBlockCount = sizeof(kBlocks) / sizeof(void *) - 1;
fancy_progress_bar_impl::~fancy_progress_bar_impl()
progress_bar_impl::~progress_bar_impl()
{
using namespace std::literals;
assert(m_thread.joinable());
#if __cpp_lib_jthread >= 201911L
m_thread.request_stop();
#else
m_stop = true;
#endif
m_thread.join();
}
#if __cpp_lib_jthread >= 201911L
void fancy_progress_bar_impl::run(std::stop_token stoken)
#else
void fancy_progress_bar_impl::run()
#endif
void progress_bar_impl::run()
{
using namespace std::literals;
@@ -350,44 +160,25 @@ void fancy_progress_bar_impl::run()
try
{
for (;;)
while (not m_stop)
{
std::unique_lock lock(m_mutex);
m_cv.wait_for(lock, 25ms);
#if __cpp_lib_jthread >= 201911L
if (stoken.stop_requested())
break;
#else
if (m_stop)
break;
#endif
auto now = std::chrono::system_clock::now();
if (m_consumed == m_last_consumed or now - m_start < 1s)
if (now - m_start < 2s or now - m_last < 100ms)
{
std::this_thread::sleep_for(10ms);
continue;
}
m_last_consumed = m_consumed;
std::lock_guard lock(m_mutex);
// See if we need to do work
m_width = get_terminal_width();
m_progress = static_cast<float>(m_consumed) / m_max_value;
m_bar_width = 7 * m_width / 10; // 70% of the width of the terminal
m_steps = static_cast<uint32_t>(std::ceil(m_progress * m_bar_width * kBlockCount));
if (m_steps == m_last_steps)
continue;
m_last_steps = m_steps;
if (not printedAny)
write_to_console("\x1b[?25l");
if (not printedAny and isatty(STDOUT_FILENO))
std::cout << "\x1b[?25l";
print_progress();
printedAny = true;
m_last = std::chrono::system_clock::now();
}
}
catch (...)
@@ -396,94 +187,161 @@ void fancy_progress_bar_impl::run()
if (printedAny)
{
write_to_console("\r\x1b[?25h");
print_done();
if (isatty(STDOUT_FILENO))
std::cout << "\x1b[?25h";
}
}
void fancy_progress_bar_impl::consumed(uint64_t n)
void progress_bar_impl::consumed(int64_t n)
{
progress_bar_impl::consumed(n);
// m_cv.notify_one();
m_consumed += n;
}
void fancy_progress_bar_impl::progress(uint64_t p)
void progress_bar_impl::progress(int64_t p)
{
progress_bar_impl::progress(p);
// m_cv.notify_one();
m_consumed = p;
}
void fancy_progress_bar_impl::message(const std::string &msg)
void progress_bar_impl::message(const std::string &msg)
{
std::unique_lock lock(m_mutex);
progress_bar_impl::message(msg);
// m_cv.notify_one();
m_message = msg;
}
const char* kSpinner[] = {
// ".", "o", "O", "0", "O", "o", ".", " "
// "⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"
".", "o", "O", "0", "@", "*", " "
};
const size_t kSpinnerCount = sizeof(kSpinner) / sizeof(char*);
const int kSpinnerTimeInterval = 100;
const uint32_t kMinBarWidth = 40, kMinMsgWidth = 12;
void fancy_progress_bar_impl::print_progress()
void progress_bar_impl::print_progress()
{
const uint32_t pct_width = 5;
uint32_t msg_width = m_width - m_bar_width - pct_width - 1;
const char *kBlocks[] = {
// "▯", // 0
// "▮", // 1
"=",
"-"
};
if (msg_width < kMinMsgWidth)
uint32_t width = get_terminal_width();
float progress = static_cast<float>(m_consumed) / m_max_value;
if (width < kMinBarWidth)
std::cout << (100 * progress) << '%' << std::endl;
else
{
m_bar_width += kMinMsgWidth - msg_width;
msg_width = kMinMsgWidth;
}
uint32_t bar_width = 7 * width / 10;
uint32_t pct_width = 7;
uint32_t msg_width = width - bar_width - pct_width - 1;
std::string bar;
bar.reserve(m_bar_width * 4);
if (msg_width < kMinMsgWidth)
{
bar_width += kMinMsgWidth - msg_width;
msg_width = kMinMsgWidth;
}
for (uint32_t i = 0; i < m_bar_width; ++i)
{
if (i * kBlockCount <= m_steps)
bar += kBlocks[kBlockCount];
else if (i * kBlockCount > m_steps + kBlockCount)
bar += kBlocks[0];
std::ostringstream msg;
if (m_message.length() <= msg_width)
{
msg << m_message;
if (m_message.length() < msg_width)
msg << std::string(msg_width - m_message.length(), ' ');
}
else
bar += kBlocks[1 + m_steps % kBlockCount];
msg << m_message.substr(0, msg_width - 3) << "...";
msg << ' ';
uint32_t pi = static_cast<uint32_t>(std::ceil(progress * bar_width));
for (uint32_t i = 0; i < bar_width; ++i)
msg << kBlocks[i > pi ? 1 : 0];
msg << ' ';
msg << std::setw(3) << static_cast<int>(std::ceil(progress * 100)) << "% ";
auto now = std::chrono::system_clock::now();
m_spinner_index = (std::chrono::duration_cast<std::chrono::milliseconds>(now - m_start).count() / kSpinnerTimeInterval) % kSpinnerCount;
msg << kSpinner[m_spinner_index];
std::cout << '\r' << msg.str();
std::cout.flush();
}
// make the bar more colorfull
struct color_type
{
uint8_t r, g, b;
} fg{ 0, 3, 5 }, bg{ 0, 1, 2 };
auto esc_1 = std::format("\x1b[38;5;{}m\x1b[48;5;{}m",
16 + (fg.r * 36) + (fg.g * 6) + fg.b,
16 + (bg.r * 36) + (bg.g * 6) + bg.b);
std::string esc_2("\x1b[0m");
bar = esc_1 + bar + esc_2;
std::string msg = m_message.length() <= msg_width
? m_message
: m_message.substr(0, msg_width - 3) + "...";
write_to_console(std::format("{:{}} {} {:3d}%\r", msg, msg_width, bar,
static_cast<int>(std::ceil(m_progress * 100))));
}
// --------------------------------------------------------------------
namespace
{
progress_bar::progress_bar(int64_t max_value, const std::string &message)
std::ostream &operator<<(std::ostream &os, const std::chrono::duration<double> &t)
{
uint64_t s = static_cast<uint64_t>(std::trunc(t.count()));
if (s > 24 * 60 * 60)
{
auto days = s / (24 * 60 * 60);
os << days << "d ";
s %= 24 * 60 * 60;
}
if (s > 60 * 60)
{
auto hours = s / (60 * 60);
os << hours << "h ";
s %= 60 * 60;
}
if (s > 60)
{
auto minutes = s / 60;
os << minutes << "m ";
s %= 60;
}
double ss = s + 1e-6 * (t.count() - s);
os << std::fixed << std::setprecision(1) << ss << 's';
return os;
}
} // namespace
void progress_bar_impl::print_done()
{
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - m_start;
std::ostringstream msgstr;
msgstr << m_action << " done in " << elapsed << " seconds";
auto msg = msgstr.str();
uint32_t width = get_terminal_width();
if (msg.length() < width)
msg += std::string(width - msg.length(), ' ');
std::cout << '\r' << msg << std::endl;
}
progress_bar::progress_bar(int64_t inMax, const std::string &inAction)
: m_impl(nullptr)
{
if (VERBOSE >= 0)
{
if (isatty(STDOUT_FILENO) and get_terminal_width() > kMinBarWidth)
m_impl = new fancy_progress_bar_impl(max_value, message);
else
m_impl = new simple_progress_bar_impl(max_value, message);
}
if (isatty(STDOUT_FILENO) and VERBOSE >= 0)
m_impl = new progress_bar_impl(inMax, inAction);
}
progress_bar::~progress_bar()
{
flush();
delete m_impl;
}
void progress_bar::consumed(int64_t inConsumed)
@@ -492,25 +350,16 @@ void progress_bar::consumed(int64_t inConsumed)
m_impl->consumed(inConsumed);
}
void progress_bar::progress(int64_t value)
void progress_bar::progress(int64_t inProgress)
{
if (m_impl != nullptr)
m_impl->progress(value);
m_impl->progress(inProgress);
}
void progress_bar::message(const std::string &message)
void progress_bar::message(const std::string &inMessage)
{
if (m_impl != nullptr)
m_impl->message(message);
}
void progress_bar::flush()
{
if (m_impl)
{
delete m_impl;
m_impl = nullptr;
}
m_impl->message(inMessage);
}
} // namespace cif
@@ -538,13 +387,13 @@ struct rsrc_imp
#if _WIN32
# if __MINGW32__
#if __MINGW32__
extern "C" __attribute__((weak, alias("gResourceIndexDefault"))) const mrsrc::rsrc_imp gResourceIndex[];
extern "C" __attribute__((weak, alias("gResourceDataDefault"))) const char gResourceData[];
extern "C" __attribute__((weak, alias("gResourceNameDefault"))) const char gResourceName[];
# else
#else
extern "C" const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
extern "C" const char *gResourceDataDefault[1] = {};
@@ -554,11 +403,11 @@ extern "C" const mrsrc::rsrc_imp gResourceIndex[];
extern "C" const char gResourceData[];
extern "C" const char gResourceName[];
# pragma comment(linker, "/alternatename:gResourceIndex=gResourceIndexDefault")
# pragma comment(linker, "/alternatename:gResourceData=gResourceDataDefault")
# pragma comment(linker, "/alternatename:gResourceName=gResourceNameDefault")
#pragma comment(linker, "/alternatename:gResourceIndex=gResourceIndexDefault")
#pragma comment(linker, "/alternatename:gResourceData=gResourceDataDefault")
#pragma comment(linker, "/alternatename:gResourceName=gResourceNameDefault")
# endif
#endif
#else
extern const __attribute__((weak)) mrsrc::rsrc_imp gResourceIndex[];
@@ -1028,21 +877,6 @@ class resource_pool
resource_pool::resource_pool()
{
// directories are searched in reverse order
// As a last resort, try the location that might have been
// used during installation, works only when running on an
// OS with a proc file system.
std::error_code ec;
if (auto exefile = fs::read_symlink("/proc/self/exe", ec); not ec and exefile.parent_path().filename() == "bin")
{
auto install_prefix = exefile.parent_path().parent_path();
auto data_dir = install_prefix / "share" / "libcifpp";
if (fs::exists(data_dir, ec))
pushDir(data_dir);
}
#if defined(DATA_DIR)
pushDir(DATA_DIR);
#endif

View File

@@ -25,7 +25,6 @@
*/
#include "cif++/validate.hpp"
#include "cif++/category.hpp"
#include "cif++/dictionary_parser.hpp"
#include "cif++/gzio.hpp"
#include "cif++/utilities.hpp"
@@ -36,90 +35,43 @@
// The validator depends on regular expressions. Unfortunately,
// the implementation of std::regex in g++ is buggy and crashes
// on reading the pdbx dictionary. We used to use boost regex
// instead but using pcre2 is even easier and faster.
// on reading the pdbx dictionary. Therefore, in case g++ is used
// the code will use boost::regex instead.
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#if USE_BOOST_REGEX
#include <boost/regex.hpp>
using boost::regex;
#else
#include <regex>
using std::regex;
#endif
namespace cif
{
validation_exception::validation_exception(std::error_code ec)
: runtime_error(ec.message())
struct regex_impl : public regex
{
}
validation_exception::validation_exception(std::error_code ec, std::string_view category)
: runtime_error((ec.message() + "; category: ").append(category))
{
}
validation_exception::validation_exception(std::error_code ec, std::string_view category, std::string_view item)
: runtime_error((ec.message() + "; category: ").append(category).append("; item: ").append(item))
{
}
// --------------------------------------------------------------------
struct regex_impl
{
regex_impl(std::string_view rx);
~regex_impl();
regex_impl(const regex_impl &) = delete;
regex_impl &operator=(const regex_impl &) = delete;
bool match(std::string_view v) const;
private:
pcre2_code *m_rx = nullptr;
pcre2_match_data *m_data = nullptr;
regex_impl(std::string_view rx)
: regex(rx.begin(), rx.end(), regex::extended | regex::optimize)
{
}
};
regex_impl::regex_impl(std::string_view rx)
validation_error::validation_error(const std::string &msg)
: m_msg(msg)
{
int err_code;
size_t err_offset;
m_rx = pcre2_compile((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));
throw std::runtime_error(std::string("PCRE2 compilation failed: ") + std::string{ (char *)buffer, (char *)buffer + n });
}
m_data = pcre2_match_data_create_from_pattern(m_rx, nullptr);
}
regex_impl::~regex_impl()
validation_error::validation_error(const std::string &cat, const std::string &item, const std::string &msg)
: m_msg("When validating _" + cat + '.' + item + ": " + msg)
{
if (m_data)
pcre2_match_data_free(m_data);
if (m_rx)
pcre2_code_free(m_rx);
}
bool regex_impl::match(std::string_view v) const
{
bool result = false;
if (int rc = pcre2_match(m_rx, (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";
return result;
}
// --------------------------------------------------------------------
DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec) noexcept
DDL_PrimitiveType map_to_primitive_type(std::string_view s)
{
ec = {};
DDL_PrimitiveType result = DDL_PrimitiveType::Char;
DDL_PrimitiveType result;
if (iequals(s, "char"))
result = DDL_PrimitiveType::Char;
else if (iequals(s, "uchar"))
@@ -127,16 +79,7 @@ DDL_PrimitiveType map_to_primitive_type(std::string_view s, std::error_code &ec)
else if (iequals(s, "numb"))
result = DDL_PrimitiveType::Numb;
else
ec = make_error_code(validation_error::not_a_known_primitive_type);
return result;
}
DDL_PrimitiveType map_to_primitive_type(std::string_view s)
{
std::error_code ec;
auto result = map_to_primitive_type(s, ec);
if (ec)
throw std::system_error(ec, std::string{ s });
throw validation_error("Not a known primitive type");
return result;
}
@@ -149,15 +92,9 @@ 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()
{
delete m_rx;
}
int type_validator::compare(std::string_view a, std::string_view b) const
@@ -181,10 +118,10 @@ int type_validator::compare(std::string_view a, std::string_view b) const
std::from_chars_result ra, rb;
ra = from_chars(a.data(), a.data() + a.length(), da);
rb = from_chars(b.data(), b.data() + b.length(), db);
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)
if (ra.ec == std::errc() and rb.ec == std::errc())
{
auto d = da - db;
if (std::abs(d) > std::numeric_limits<double>::epsilon())
@@ -195,7 +132,7 @@ int type_validator::compare(std::string_view a, std::string_view b) const
result = -1;
}
}
else if ((bool)ra.ec)
else if (ra.ec == std::errc())
result = 1;
else
result = -1;
@@ -259,100 +196,68 @@ int type_validator::compare(std::string_view a, std::string_view b) const
// --------------------------------------------------------------------
// void ValidateItem::addLinked(ValidateItem* parent, const std::string& parentItem, const std::string& childItem)
//{
//// if (mParent != nullptr and VERBOSE)
//// cerr << "replacing parent in " << mCategory->m_name << " from " << mParent->mCategory->m_name << " to " << parent->mCategory->m_name << endl;
//// mParent = parent;
//
// if (m_type == nullptr and parent != nullptr)
// m_type = parent->m_type;
//
// if (parent != nullptr)
// {
// mLinked.push_back({parent, parentItem, childItem});
//
// parent->mChildren.insert(this);
////
//// if (mCategory->mKeys == std::vector<std::string>{mTag})
//// parent->mForeignKeys.insert(this);
// }
//}
void item_validator::operator()(std::string_view 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);
}
bool item_validator::validate_value(std::string_view value, std::error_code &ec) const noexcept
{
ec.clear();
if (not value.empty() and value != "?" and value != ".")
{
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)
ec = make_error_code(validation_error::value_is_not_in_enumeration_list);
}
if (m_type != nullptr and not regex_match(value.begin(), value.end(), *m_type->m_rx))
throw validation_error(m_category->m_name, m_tag, "Value '" + std::string{ value } + "' does not match type expression for type " + m_type->m_name);
return not(bool) ec;
if (not m_enums.empty())
{
if (m_enums.count(std::string{ value }) == 0)
throw validation_error(m_category->m_name, m_tag, "Value '" + std::string{ value } + "' is not in the list of allowed values");
}
}
}
// --------------------------------------------------------------------
void category_validator::add_item_validator(item_validator &&v)
void category_validator::addItemValidator(item_validator &&v)
{
if (v.m_mandatory)
m_mandatory_items.insert(v.m_item_name);
m_mandatory_fields.insert(v.m_tag);
v.m_category = m_name;
v.m_category = this;
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';
std::cout << "Could not add validator for item " << v.m_tag << " to category " << m_name << '\n';
}
const item_validator *category_validator::get_validator_for_item(std::string_view item_name) const
const item_validator *category_validator::get_validator_for_item(std::string_view tag) const
{
const item_validator *result = nullptr;
auto i = m_item_validators.find(item_validator{ std::string(item_name) });
auto i = m_item_validators.find(item_validator{ std::string(tag) });
if (i != m_item_validators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for item " << item_name << '\n';
return result;
}
const item_validator *category_validator::get_validator_for_aliased_item(std::string_view item_name) const
{
const item_validator *result = nullptr;
for (auto &iv : m_item_validators)
{
for (auto &ai : iv.m_aliases)
{
const auto &[cat, name] = split_item_name(ai.m_name);
if (iequals(name, item_name) and iequals(cat, m_name))
{
result = &iv;
break;
}
}
if (result)
break;
}
std::cout << "No validator for tag " << tag << '\n';
return result;
}
// --------------------------------------------------------------------
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);
std::swap(a.m_strict, b.m_strict);
std::swap(a.m_type_validators, b.m_type_validators);
std::swap(a.m_category_validators, b.m_category_validators);
std::swap(a.m_link_validators, b.m_link_validators);
}
void validator::parse(std::istream &is)
{
parse_dictionary(*this, is);
}
void validator::add_type_validator(type_validator &&v)
{
auto r = m_type_validators.insert(std::move(v));
@@ -390,19 +295,19 @@ const category_validator *validator::get_validator_for_category(std::string_view
return result;
}
item_validator *validator::get_validator_for_item(std::string_view item_name) const
item_validator *validator::get_validator_for_item(std::string_view tag) const
{
item_validator *result = nullptr;
std::string cat, item;
std::tie(cat, item) = split_item_name(item_name);
std::tie(cat, item) = split_tag_name(tag);
auto *cv = get_validator_for_category(cat);
if (cv != nullptr)
result = const_cast<item_validator *>(cv->get_validator_for_item(item));
if (result == nullptr and VERBOSE > 4)
std::cout << "No validator for item " << item_name << '\n';
std::cout << "No validator for item " << tag << '\n';
return result;
}
@@ -422,16 +327,16 @@ void validator::add_link_validator(link_validator &&v)
if (ccv == nullptr)
throw std::runtime_error("unknown child category " + v.m_child_category);
for (std::size_t i = 0; i < v.m_parent_keys.size(); ++i)
for (size_t i = 0; i < v.m_parent_keys.size(); ++i)
{
auto piv = pcv->get_validator_for_item(v.m_parent_keys[i]);
if (piv == nullptr)
throw std::runtime_error("unknown parent item _" + v.m_parent_category + '.' + v.m_parent_keys[i]);
throw std::runtime_error("unknown parent tag _" + v.m_parent_category + '.' + v.m_parent_keys[i]);
auto civ = ccv->get_validator_for_item(v.m_child_keys[i]);
if (civ == nullptr)
throw std::runtime_error("unknown child item _" + v.m_child_category + '.' + v.m_child_keys[i]);
throw std::runtime_error("unknown child tag _" + v.m_child_category + '.' + v.m_child_keys[i]);
if (civ->m_type == nullptr and piv->m_type != nullptr)
const_cast<item_validator *>(civ)->m_type = piv->m_type;
@@ -446,7 +351,7 @@ std::vector<const link_validator *> validator::get_links_for_parent(std::string_
for (auto &l : m_link_validators)
{
if (iequals(l.m_parent_category, category))
if (l.m_parent_category == category)
result.push_back(&l);
}
@@ -459,72 +364,19 @@ std::vector<const link_validator *> validator::get_links_for_child(std::string_v
for (auto &l : m_link_validators)
{
if (iequals(l.m_child_category, category))
if (l.m_child_category == category)
result.push_back(&l);
}
return result;
}
void validator::report_error(std::error_code ec, bool fatal) const
void validator::report_error(const std::string &msg, bool fatal) const
{
if (m_strict or fatal)
throw validation_exception(ec);
throw validation_error(msg);
else if (VERBOSE > 0)
std::cerr << ec.message() << '\n';
}
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';
}
void validator::fill_audit_conform(category &audit_conform) const
{
audit_conform.clear();
audit_conform.emplace(m_audit_conform.begin(), m_audit_conform.end());
}
bool validator::matches_audit_conform(const category &audit_conform) const
{
if (audit_conform.empty())
return false;
auto ai = m_audit_conform.begin();
auto bi = audit_conform.begin();
while (ai != m_audit_conform.end() and bi != audit_conform.end())
{
const auto &[name_a, version_a] = ai->get<std::string, std::optional<std::string>>("dict_name", "dict_version");
const auto &[name_b, version_b] = bi->get<std::string, std::optional<std::string>>("dict_name", "dict_version");
++ai;
++bi;
if (name_a != name_b)
return false;
if (not version_b.has_value() or not version_a.has_value())
continue;
if (validator_factory::check_version(name_a, *version_b, *version_a) == false)
return false;
}
return ai == m_audit_conform.end() and bi == audit_conform.end();
}
void validator::append_audit_conform(const std::string &name, const std::optional<std::string> &version)
{
m_audit_conform.emplace({ //
{ "dict_name", name },
{ "dict_version", version } });
std::cerr << msg << '\n';
}
// --------------------------------------------------------------------
@@ -535,127 +387,101 @@ validator_factory &validator_factory::instance()
return s_instance;
}
const validator &validator_factory::get(std::string_view dictionary_name)
const validator &validator_factory::operator[](std::string_view dictionary_name)
{
category audit_conform("audit_conform");
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)
{
if (audit_conform.empty())
throw std::runtime_error("Empty audit_conform category, cannot create a validator");
std::lock_guard lock(m_mutex);
// Check existing first
for (auto &v : m_validators)
try
{
if (v.matches_audit_conform(audit_conform))
return v;
}
std::lock_guard lock(m_mutex);
// If the audit conform contains only one record, this is easy
if (audit_conform.size() == 1)
{
const auto &[name, version] =
audit_conform.front().get<std::string, std::optional<std::string>>("dict_name", "dict_version");
if (not name.empty())
return m_validators.emplace_back(construct_validator(name, version));
}
for (auto &validator : m_validators)
{
if (iequals(validator.name(), dictionary_name))
return validator;
}
// A new, merged dictionary
// not found, try to see if it helps if we tweak the name a little
std::optional<validator> v;
for (const auto &[name, version] : audit_conform.rows<std::string, std::optional<std::string>>("dict_name", "dict_version"))
{
if (name.empty())
continue;
// too bad clang version 10 did not have a constructor for std::filesystem::path that accepts a std::string_view
std::filesystem::path dictionary(dictionary_name.data(), dictionary_name.data() + dictionary_name.length());
if (not v)
v = construct_validator(name, version);
if (dictionary.extension() != ".dic")
{
auto dict_name = dictionary.filename().string() + ".dic";
for (auto &validator : m_validators)
{
if (iequals(validator.name(), dict_name))
return validator;
}
}
// not found, add it
auto data = load_resource(dictionary_name);
if (not data and dictionary.extension().string() != ".dic")
data = load_resource(dictionary.parent_path() / (dictionary.filename().string() + ".dic"));
if (data)
construct_validator(dictionary_name, *data);
else
{
auto data = load_resource(name);
if (not data)
throw std::runtime_error("Could not load dictionary " + std::string{ name });
std::error_code ec;
v->parse(*data);
// might be a compressed dictionary on disk
std::filesystem::path p = dictionary;
if (p.extension() == ".dic")
p = p.parent_path() / (p.filename().string() + ".gz");
else
p = p.parent_path() / (p.filename().string() + ".dic.gz");
#if defined(CACHE_DIR) or defined(DATA_DIR)
if (not std::filesystem::exists(p, ec) or ec)
{
for (const char *dir : {
#if defined(CACHE_DIR)
CACHE_DIR,
#endif
#if defined(DATA_DIR)
DATA_DIR
#endif
})
{
auto p2 = std::filesystem::path(dir) / p;
if (std::filesystem::exists(p2, ec) and not ec)
{
swap(p, p2);
break;
}
}
}
#endif
if (std::filesystem::exists(p, ec) and not ec)
{
gzio::ifstream in(p);
if (not in.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
construct_validator(dictionary_name, in);
}
else
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
}
return m_validators.back();
}
catch (const std::exception &ex)
{
std::string msg = "Error while loading dictionary ";
msg += dictionary_name;
std::throw_with_nested(std::runtime_error(msg));
}
if (not v)
throw std::runtime_error("Missing dictionary information?");
return m_validators.emplace_back(std::move(*v));
}
validator validator_factory::construct_validator(std::string_view name, std::optional<std::string> version)
const validator &validator_factory::construct_validator(std::string_view name, std::istream &is)
{
auto data = load_resource(name);
if (not data and name == "mmcif_pdbx_v50")
data = load_resource("mmcif_pdbx.dic");
if (not data)
throw std::runtime_error("Could not load dictionary " + std::string{ name });
validator v;
v.parse(*data);
if (version.has_value() and VERBOSE >= 0 and
not v.matches_audit_conform(category{ "audit_conform", //
{ { "dict_name", name }, { "dict_version", version } } }))
{
std::clog << "Loaded dictionary does not match name=" << name << " and version=" << version.value_or("''") << "\n";
}
return v;
}
bool validator_factory::check_version(std::string_view name, std::string_view expected, std::string_view found)
{
bool result = true;
auto el = cif::split(expected, ".");
auto fl = cif::split(found, ".");
auto eli = el.begin();
auto fli = fl.begin();
while (eli != el.end() and fli != fl.end())
{
int e_int, f_int;
if (auto [ptr, ec] = std::from_chars(eli->data(), eli->data() + eli->length(), e_int); ec != std::errc{})
{
std::clog << "Could not parse requested version string for dictionary " << std::quoted(expected) << "\n";
result = false;
break;
}
if (auto [ptr, ec] = std::from_chars(fli->data(), fli->data() + fli->length(), f_int); ec != std::errc{})
{
std::clog << "Could not parse version string in dictionary " << name << " " << std::quoted(found) << "\n";
result = false;
break;
}
if (f_int > e_int) // newer version, assume this is ok
break;
if (f_int < e_int)
{
std::clog << "The version in dictionary " << name << " is lower than requested, this may cause validation errors\n";
result = false;
break;
}
++eli;
++fli;
}
return result;
return m_validators.emplace_back(parse_dictionary(name, is));
}
} // namespace cif

BIN
test/.1juh.cif.gz.swp Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +0,0 @@
# 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 3 QUIET)
if(NOT Catch2_FOUND)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0)
FetchContent_MakeAvailable(Catch2)
target_compile_features(Catch2 PRIVATE cxx_std_20)
endif()
endif()
list(
APPEND
CIFPP_tests
unit-v2
unit-3d
model
query
rename-compound
sugar
spinner
reconstruction
validate-pdbx
)
add_library(test-main OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/test-main.cpp")
target_link_libraries(test-main cifpp::cifpp Catch2::Catch2)
foreach(CIFPP_TEST IN LISTS CIFPP_tests)
set(CIFPP_TEST "${CIFPP_TEST}-test")
set(CIFPP_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${CIFPP_TEST}.cpp")
add_executable(
${CIFPP_TEST} ${CIFPP_TEST_SOURCE} $<TARGET_OBJECTS:test-main>)
target_link_libraries(${CIFPP_TEST} PRIVATE cifpp::cifpp Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
if(MSVC)
# Specify unwind semantics so that MSVC knowns how to handle exceptions
target_compile_options(${CIFPP_TEST} PRIVATE /EHsc)
endif()
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> --data-dir ${CMAKE_CURRENT_SOURCE_DIR})
endforeach()

View File

@@ -1,822 +0,0 @@
REMARK 001 design 0 N 3 RMSD 1.605
MODEL 1
ATOM 1 N MET A 1 -24.216 -7.571 -1.155 1.00 31.87 N
ATOM 2 CA MET A 1 -22.971 -7.756 -0.416 1.00 31.87 C
ATOM 3 C MET A 1 -21.814 -7.051 -1.114 1.00 31.87 C
ATOM 4 CB MET A 1 -22.656 -9.245 -0.256 1.00 31.87 C
ATOM 5 O MET A 1 -21.516 -7.338 -2.275 1.00 31.87 O
ATOM 6 CG MET A 1 -22.779 -9.747 1.174 1.00 31.87 C
ATOM 7 SD MET A 1 -22.473 -11.551 1.314 1.00 31.87 S
ATOM 8 CE MET A 1 -22.597 -11.758 3.112 1.00 31.87 C
ATOM 9 N LYS A 2 -21.652 -5.596 -0.909 1.00 33.06 N
ATOM 10 CA LYS A 2 -21.035 -4.320 -0.554 1.00 33.06 C
ATOM 11 C LYS A 2 -19.531 -4.475 -0.351 1.00 33.06 C
ATOM 12 CB LYS A 2 -21.678 -3.747 0.710 1.00 33.06 C
ATOM 13 O LYS A 2 -19.087 -5.326 0.423 1.00 33.06 O
ATOM 14 CG LYS A 2 -22.957 -2.963 0.453 1.00 33.06 C
ATOM 15 CD LYS A 2 -23.454 -2.275 1.717 1.00 33.06 C
ATOM 16 CE LYS A 2 -24.810 -1.615 1.500 1.00 33.06 C
ATOM 17 NZ LYS A 2 -25.284 -0.906 2.725 1.00 33.06 N
ATOM 18 N ILE A 3 -18.889 -4.065 -1.586 1.00 34.22 N
ATOM 19 CA ILE A 3 -17.539 -3.550 -1.381 1.00 34.22 C
ATOM 20 C ILE A 3 -17.331 -2.297 -2.229 1.00 34.22 C
ATOM 21 CB ILE A 3 -16.471 -4.612 -1.721 1.00 34.22 C
ATOM 22 O ILE A 3 -17.508 -2.330 -3.449 1.00 34.22 O
ATOM 23 CG1 ILE A 3 -16.524 -5.766 -0.713 1.00 34.22 C
ATOM 24 CG2 ILE A 3 -15.075 -3.982 -1.759 1.00 34.22 C
ATOM 25 CD1 ILE A 3 -15.743 -7.000 -1.142 1.00 34.22 C
ATOM 26 N ILE A 4 -17.776 -0.985 -1.831 1.00 40.18 N
ATOM 27 CA ILE A 4 -17.255 0.212 -1.181 1.00 40.18 C
ATOM 28 C ILE A 4 -16.631 1.136 -2.224 1.00 40.18 C
ATOM 29 CB ILE A 4 -16.218 -0.142 -0.091 1.00 40.18 C
ATOM 30 O ILE A 4 -15.717 0.736 -2.949 1.00 40.18 O
ATOM 31 CG1 ILE A 4 -16.844 -1.063 0.963 1.00 40.18 C
ATOM 32 CG2 ILE A 4 -15.658 1.128 0.555 1.00 40.18 C
ATOM 33 CD1 ILE A 4 -15.839 -1.662 1.937 1.00 40.18 C
ATOM 34 N GLU A 5 -17.325 2.198 -2.769 1.00 38.95 N
ATOM 35 CA GLU A 5 -17.243 3.640 -2.553 1.00 38.95 C
ATOM 36 C GLU A 5 -15.944 4.019 -1.848 1.00 38.95 C
ATOM 37 CB GLU A 5 -18.445 4.131 -1.741 1.00 38.95 C
ATOM 38 O GLU A 5 -15.602 3.445 -0.812 1.00 38.95 O
ATOM 39 CG GLU A 5 -19.296 5.162 -2.466 1.00 38.95 C
ATOM 40 CD GLU A 5 -20.407 5.738 -1.602 1.00 38.95 C
ATOM 41 OE1 GLU A 5 -20.194 6.791 -0.960 1.00 38.95 O
ATOM 42 OE2 GLU A 5 -21.500 5.129 -1.566 1.00 38.95 O
ATOM 43 N LYS A 6 -14.697 4.048 -2.514 1.00 40.24 N
ATOM 44 CA LYS A 6 -13.793 5.173 -2.294 1.00 40.24 C
ATOM 45 C LYS A 6 -12.348 4.784 -2.593 1.00 40.24 C
ATOM 46 CB LYS A 6 -13.911 5.684 -0.857 1.00 40.24 C
ATOM 47 O LYS A 6 -11.769 3.946 -1.899 1.00 40.24 O
ATOM 48 CG LYS A 6 -14.799 6.911 -0.707 1.00 40.24 C
ATOM 49 CD LYS A 6 -14.696 7.508 0.690 1.00 40.24 C
ATOM 50 CE LYS A 6 -15.689 8.645 0.887 1.00 40.24 C
ATOM 51 NZ LYS A 6 -15.523 9.301 2.219 1.00 40.24 N
ATOM 52 N TYR A 7 -12.096 4.360 -3.877 1.00 55.19 N
ATOM 53 CA TYR A 7 -10.695 4.330 -4.282 1.00 55.19 C
ATOM 54 C TYR A 7 -9.919 5.478 -3.647 1.00 55.19 C
ATOM 55 CB TYR A 7 -10.574 4.398 -5.807 1.00 55.19 C
ATOM 56 O TYR A 7 -10.324 6.639 -3.746 1.00 55.19 O
ATOM 57 CG TYR A 7 -11.069 3.159 -6.511 1.00 55.19 C
ATOM 58 CD1 TYR A 7 -10.245 2.047 -6.669 1.00 55.19 C
ATOM 59 CD2 TYR A 7 -12.361 3.097 -7.021 1.00 55.19 C
ATOM 60 CE1 TYR A 7 -10.696 0.903 -7.319 1.00 55.19 C
ATOM 61 CE2 TYR A 7 -12.823 1.958 -7.673 1.00 55.19 C
ATOM 62 OH TYR A 7 -12.437 -0.262 -8.461 1.00 55.19 O
ATOM 63 CZ TYR A 7 -11.985 0.868 -7.817 1.00 55.19 C
ATOM 64 N VAL A 8 -9.308 5.253 -2.484 1.00 55.50 N
ATOM 65 CA VAL A 8 -8.469 6.226 -1.792 1.00 55.50 C
ATOM 66 C VAL A 8 -7.196 6.476 -2.597 1.00 55.50 C
ATOM 67 CB VAL A 8 -8.112 5.754 -0.364 1.00 55.50 C
ATOM 68 O VAL A 8 -6.488 5.534 -2.960 1.00 55.50 O
ATOM 69 CG1 VAL A 8 -7.215 6.774 0.333 1.00 55.50 C
ATOM 70 CG2 VAL A 8 -9.381 5.509 0.450 1.00 55.50 C
ATOM 71 N PHE A 9 -7.225 7.625 -3.250 1.00 66.91 N
ATOM 72 CA PHE A 9 -6.004 8.094 -3.895 1.00 66.91 C
ATOM 73 C PHE A 9 -4.934 8.414 -2.858 1.00 66.91 C
ATOM 74 CB PHE A 9 -6.289 9.330 -4.754 1.00 66.91 C
ATOM 75 O PHE A 9 -5.089 9.345 -2.065 1.00 66.91 O
ATOM 76 CG PHE A 9 -6.868 9.009 -6.105 1.00 66.91 C
ATOM 77 CD1 PHE A 9 -6.039 8.797 -7.200 1.00 66.91 C
ATOM 78 CD2 PHE A 9 -8.243 8.920 -6.281 1.00 66.91 C
ATOM 79 CE1 PHE A 9 -6.572 8.499 -8.452 1.00 66.91 C
ATOM 80 CE2 PHE A 9 -8.783 8.623 -7.530 1.00 66.91 C
ATOM 81 CZ PHE A 9 -7.946 8.414 -8.614 1.00 66.91 C
ATOM 82 N LEU A 10 -4.065 7.388 -2.607 1.00 72.14 N
ATOM 83 CA LEU A 10 -2.967 7.542 -1.658 1.00 72.14 C
ATOM 84 C LEU A 10 -2.342 8.929 -1.771 1.00 72.14 C
ATOM 85 CB LEU A 10 -1.902 6.469 -1.893 1.00 72.14 C
ATOM 86 O LEU A 10 -1.903 9.499 -0.770 1.00 72.14 O
ATOM 87 CG LEU A 10 -2.222 5.069 -1.365 1.00 72.14 C
ATOM 88 CD1 LEU A 10 -1.258 4.046 -1.957 1.00 72.14 C
ATOM 89 CD2 LEU A 10 -2.165 5.045 0.158 1.00 72.14 C
ATOM 90 N ALA A 11 -2.498 9.659 -2.947 1.00 69.94 N
ATOM 91 CA ALA A 11 -1.847 10.939 -3.211 1.00 69.94 C
ATOM 92 C ALA A 11 -2.571 12.080 -2.501 1.00 69.94 C
ATOM 93 CB ALA A 11 -1.786 11.206 -4.713 1.00 69.94 C
ATOM 94 O ALA A 11 -1.971 13.117 -2.207 1.00 69.94 O
ATOM 95 N GLU A 12 -3.713 11.866 -1.974 1.00 76.23 N
ATOM 96 CA GLU A 12 -4.520 12.921 -1.368 1.00 76.23 C
ATOM 97 C GLU A 12 -4.620 12.741 0.144 1.00 76.23 C
ATOM 98 CB GLU A 12 -5.920 12.951 -1.987 1.00 76.23 C
ATOM 99 O GLU A 12 -5.178 13.590 0.841 1.00 76.23 O
ATOM 100 CG GLU A 12 -5.939 13.391 -3.444 1.00 76.23 C
ATOM 101 CD GLU A 12 -7.343 13.548 -4.005 1.00 76.23 C
ATOM 102 OE1 GLU A 12 -7.904 14.665 -3.935 1.00 76.23 O
ATOM 103 OE2 GLU A 12 -7.888 12.544 -4.518 1.00 76.23 O
ATOM 104 N LEU A 13 -3.986 11.682 0.620 1.00 82.70 N
ATOM 105 CA LEU A 13 -4.106 11.386 2.043 1.00 82.70 C
ATOM 106 C LEU A 13 -2.951 12.001 2.826 1.00 82.70 C
ATOM 107 CB LEU A 13 -4.146 9.873 2.274 1.00 82.70 C
ATOM 108 O LEU A 13 -1.826 12.073 2.327 1.00 82.70 O
ATOM 109 CG LEU A 13 -5.347 9.129 1.688 1.00 82.70 C
ATOM 110 CD1 LEU A 13 -5.171 7.623 1.857 1.00 82.70 C
ATOM 111 CD2 LEU A 13 -6.640 9.598 2.346 1.00 82.70 C
ATOM 112 N SER A 14 -3.339 12.631 3.947 1.00 86.03 N
ATOM 113 CA SER A 14 -2.328 13.121 4.878 1.00 86.03 C
ATOM 114 C SER A 14 -1.680 11.974 5.645 1.00 86.03 C
ATOM 115 CB SER A 14 -2.942 14.120 5.859 1.00 86.03 C
ATOM 116 O SER A 14 -2.161 10.839 5.599 1.00 86.03 O
ATOM 117 OG SER A 14 -3.526 15.211 5.168 1.00 86.03 O
ATOM 118 N ASP A 15 -0.488 12.215 6.359 1.00 86.85 N
ATOM 119 CA ASP A 15 0.221 11.246 7.189 1.00 86.85 C
ATOM 120 C ASP A 15 -0.734 10.542 8.149 1.00 86.85 C
ATOM 121 CB ASP A 15 1.345 11.930 7.971 1.00 86.85 C
ATOM 122 O ASP A 15 -0.647 9.327 8.340 1.00 86.85 O
ATOM 123 CG ASP A 15 2.418 12.521 7.074 1.00 86.85 C
ATOM 124 OD1 ASP A 15 3.279 11.766 6.575 1.00 86.85 O
ATOM 125 OD2 ASP A 15 2.403 13.754 6.867 1.00 86.85 O
ATOM 126 N GLU A 16 -1.606 11.353 8.835 1.00 86.30 N
ATOM 127 CA GLU A 16 -2.590 10.797 9.760 1.00 86.30 C
ATOM 128 C GLU A 16 -3.555 9.859 9.041 1.00 86.30 C
ATOM 129 CB GLU A 16 -3.366 11.918 10.456 1.00 86.30 C
ATOM 130 O GLU A 16 -3.871 8.780 9.545 1.00 86.30 O
ATOM 131 CG GLU A 16 -2.547 12.688 11.481 1.00 86.30 C
ATOM 132 CD GLU A 16 -3.342 13.770 12.194 1.00 86.30 C
ATOM 133 OE1 GLU A 16 -3.925 13.489 13.266 1.00 86.30 O
ATOM 134 OE2 GLU A 16 -3.384 14.908 11.676 1.00 86.30 O
ATOM 135 N GLU A 17 -4.022 10.266 7.880 1.00 87.05 N
ATOM 136 CA GLU A 17 -4.956 9.466 7.093 1.00 87.05 C
ATOM 137 C GLU A 17 -4.305 8.172 6.613 1.00 87.05 C
ATOM 138 CB GLU A 17 -5.477 10.268 5.898 1.00 87.05 C
ATOM 139 O GLU A 17 -4.937 7.114 6.618 1.00 87.05 O
ATOM 140 CG GLU A 17 -6.410 11.407 6.281 1.00 87.05 C
ATOM 141 CD GLU A 17 -7.322 11.844 5.146 1.00 87.05 C
ATOM 142 OE1 GLU A 17 -8.505 11.433 5.125 1.00 87.05 O
ATOM 143 OE2 GLU A 17 -6.850 12.602 4.270 1.00 87.05 O
ATOM 144 N LEU A 18 -3.057 8.282 6.290 1.00 88.16 N
ATOM 145 CA LEU A 18 -2.291 7.120 5.855 1.00 88.16 C
ATOM 146 C LEU A 18 -2.194 6.084 6.970 1.00 88.16 C
ATOM 147 CB LEU A 18 -0.889 7.539 5.407 1.00 88.16 C
ATOM 148 O LEU A 18 -2.374 4.888 6.730 1.00 88.16 O
ATOM 149 CG LEU A 18 -0.785 8.201 4.032 1.00 88.16 C
ATOM 150 CD1 LEU A 18 0.627 8.730 3.803 1.00 88.16 C
ATOM 151 CD2 LEU A 18 -1.179 7.219 2.934 1.00 88.16 C
ATOM 152 N LYS A 19 -1.979 6.558 8.141 1.00 87.30 N
ATOM 153 CA LYS A 19 -1.908 5.682 9.308 1.00 87.30 C
ATOM 154 C LYS A 19 -3.243 4.984 9.553 1.00 87.30 C
ATOM 155 CB LYS A 19 -1.495 6.474 10.549 1.00 87.30 C
ATOM 156 O LYS A 19 -3.280 3.787 9.843 1.00 87.30 O
ATOM 157 CG LYS A 19 -0.007 6.781 10.620 1.00 87.30 C
ATOM 158 CD LYS A 19 0.352 7.515 11.906 1.00 87.30 C
ATOM 159 CE LYS A 19 1.818 7.927 11.926 1.00 87.30 C
ATOM 160 NZ LYS A 19 2.176 8.639 13.189 1.00 87.30 N
ATOM 161 N LYS A 20 -4.246 5.781 9.525 1.00 88.15 N
ATOM 162 CA LYS A 20 -5.586 5.234 9.715 1.00 88.15 C
ATOM 163 C LYS A 20 -5.907 4.185 8.654 1.00 88.15 C
ATOM 164 CB LYS A 20 -6.631 6.350 9.682 1.00 88.15 C
ATOM 165 O LYS A 20 -6.511 3.153 8.956 1.00 88.15 O
ATOM 166 CG LYS A 20 -6.661 7.208 10.937 1.00 88.15 C
ATOM 167 CD LYS A 20 -7.772 8.249 10.877 1.00 88.15 C
ATOM 168 CE LYS A 20 -7.774 9.138 12.113 1.00 88.15 C
ATOM 169 NZ LYS A 20 -8.875 10.146 12.069 1.00 88.15 N
ATOM 170 N LEU A 21 -5.444 4.518 7.462 1.00 86.41 N
ATOM 171 CA LEU A 21 -5.662 3.611 6.341 1.00 86.41 C
ATOM 172 C LEU A 21 -4.974 2.271 6.583 1.00 86.41 C
ATOM 173 CB LEU A 21 -5.148 4.234 5.040 1.00 86.41 C
ATOM 174 O LEU A 21 -5.577 1.214 6.385 1.00 86.41 O
ATOM 175 CG LEU A 21 -5.364 3.417 3.765 1.00 86.41 C
ATOM 176 CD1 LEU A 21 -6.842 3.400 3.391 1.00 86.41 C
ATOM 177 CD2 LEU A 21 -4.524 3.976 2.622 1.00 86.41 C
ATOM 178 N VAL A 22 -3.737 2.304 6.914 1.00 87.19 N
ATOM 179 CA VAL A 22 -2.953 1.109 7.205 1.00 87.19 C
ATOM 180 C VAL A 22 -3.627 0.307 8.316 1.00 87.19 C
ATOM 181 CB VAL A 22 -1.504 1.464 7.609 1.00 87.19 C
ATOM 182 O VAL A 22 -3.719 -0.921 8.236 1.00 87.19 O
ATOM 183 CG1 VAL A 22 -0.752 0.218 8.073 1.00 87.19 C
ATOM 184 CG2 VAL A 22 -0.774 2.130 6.444 1.00 87.19 C
ATOM 185 N GLU A 23 -4.070 0.928 9.305 1.00 88.34 N
ATOM 186 CA GLU A 23 -4.790 0.285 10.400 1.00 88.34 C
ATOM 187 C GLU A 23 -6.050 -0.414 9.897 1.00 88.34 C
ATOM 188 CB GLU A 23 -5.151 1.308 11.480 1.00 88.34 C
ATOM 189 O GLU A 23 -6.364 -1.525 10.328 1.00 88.34 O
ATOM 190 CG GLU A 23 -3.959 1.792 12.292 1.00 88.34 C
ATOM 191 CD GLU A 23 -4.356 2.595 13.521 1.00 88.34 C
ATOM 192 OE1 GLU A 23 -4.486 2.004 14.616 1.00 88.34 O
ATOM 193 OE2 GLU A 23 -4.539 3.826 13.386 1.00 88.34 O
ATOM 194 N GLU A 24 -6.671 0.256 9.129 1.00 87.82 N
ATOM 195 CA GLU A 24 -7.883 -0.315 8.549 1.00 87.82 C
ATOM 196 C GLU A 24 -7.569 -1.568 7.737 1.00 87.82 C
ATOM 197 CB GLU A 24 -8.595 0.717 7.670 1.00 87.82 C
ATOM 198 O GLU A 24 -8.334 -2.535 7.758 1.00 87.82 O
ATOM 199 CG GLU A 24 -9.372 1.763 8.456 1.00 87.82 C
ATOM 200 CD GLU A 24 -10.598 1.200 9.157 1.00 87.82 C
ATOM 201 OE1 GLU A 24 -10.544 0.974 10.387 1.00 87.82 O
ATOM 202 OE2 GLU A 24 -11.621 0.982 8.469 1.00 87.82 O
ATOM 203 N TRP A 25 -6.452 -1.421 6.925 1.00 87.98 N
ATOM 204 CA TRP A 25 -6.008 -2.576 6.152 1.00 87.98 C
ATOM 205 C TRP A 25 -5.804 -3.789 7.054 1.00 87.98 C
ATOM 206 CB TRP A 25 -4.712 -2.256 5.403 1.00 87.98 C
ATOM 207 O TRP A 25 -6.237 -4.895 6.724 1.00 87.98 O
ATOM 208 CG TRP A 25 -4.867 -1.219 4.332 1.00 87.98 C
ATOM 209 CD1 TRP A 25 -6.030 -0.819 3.734 1.00 87.98 C
ATOM 210 CD2 TRP A 25 -3.820 -0.453 3.727 1.00 87.98 C
ATOM 211 CE2 TRP A 25 -4.422 0.394 2.770 1.00 87.98 C
ATOM 212 CE3 TRP A 25 -2.431 -0.399 3.902 1.00 87.98 C
ATOM 213 NE1 TRP A 25 -5.769 0.151 2.794 1.00 87.98 N
ATOM 214 CH2 TRP A 25 -2.324 1.317 2.182 1.00 87.98 C
ATOM 215 CZ2 TRP A 25 -3.681 1.285 1.990 1.00 87.98 C
ATOM 216 CZ3 TRP A 25 -1.695 0.488 3.125 1.00 87.98 C
ATOM 217 N ILE A 26 -5.164 -3.521 8.121 1.00 86.80 N
ATOM 218 CA ILE A 26 -4.842 -4.578 9.074 1.00 86.80 C
ATOM 219 C ILE A 26 -6.123 -5.095 9.724 1.00 86.80 C
ATOM 220 CB ILE A 26 -3.855 -4.082 10.155 1.00 86.80 C
ATOM 221 O ILE A 26 -6.287 -6.302 9.913 1.00 86.80 O
ATOM 222 CG1 ILE A 26 -2.511 -3.706 9.519 1.00 86.80 C
ATOM 223 CG2 ILE A 26 -3.667 -5.144 11.243 1.00 86.80 C
ATOM 224 CD1 ILE A 26 -1.549 -3.010 10.471 1.00 86.80 C
ATOM 225 N LYS A 27 -7.085 -4.130 10.074 1.00 88.23 N
ATOM 226 CA LYS A 27 -8.358 -4.482 10.697 1.00 88.23 C
ATOM 227 C LYS A 27 -9.231 -5.292 9.743 1.00 88.23 C
ATOM 228 CB LYS A 27 -9.101 -3.224 11.147 1.00 88.23 C
ATOM 229 O LYS A 27 -9.857 -6.273 10.148 1.00 88.23 O
ATOM 230 CG LYS A 27 -10.280 -3.498 12.070 1.00 88.23 C
ATOM 231 CD LYS A 27 -10.890 -2.206 12.597 1.00 88.23 C
ATOM 232 CE LYS A 27 -12.096 -2.477 13.486 1.00 88.23 C
ATOM 233 NZ LYS A 27 -12.726 -1.212 13.967 1.00 88.23 N
ATOM 234 N SER A 28 -9.257 -4.866 8.486 1.00 88.60 N
ATOM 235 CA SER A 28 -10.097 -5.462 7.453 1.00 88.60 C
ATOM 236 C SER A 28 -9.499 -6.767 6.939 1.00 88.60 C
ATOM 237 CB SER A 28 -10.292 -4.488 6.290 1.00 88.60 C
ATOM 238 O SER A 28 -10.208 -7.598 6.366 1.00 88.60 O
ATOM 239 OG SER A 28 -11.021 -3.347 6.707 1.00 88.60 O
ATOM 240 N LYS A 29 -8.274 -7.060 7.266 1.00 87.22 N
ATOM 241 CA LYS A 29 -7.552 -8.279 6.911 1.00 87.22 C
ATOM 242 C LYS A 29 -7.546 -8.493 5.400 1.00 87.22 C
ATOM 243 CB LYS A 29 -8.169 -9.491 7.609 1.00 87.22 C
ATOM 244 O LYS A 29 -7.336 -9.612 4.927 1.00 87.22 O
ATOM 245 CG LYS A 29 -7.958 -9.511 9.116 1.00 87.22 C
ATOM 246 CD LYS A 29 -8.508 -10.787 9.741 1.00 87.22 C
ATOM 247 CE LYS A 29 -8.322 -10.795 11.252 1.00 87.22 C
ATOM 248 NZ LYS A 29 -8.836 -12.056 11.866 1.00 87.22 N
ATOM 249 N GLU A 30 -7.995 -7.561 4.711 1.00 90.12 N
ATOM 250 CA GLU A 30 -7.955 -7.620 3.253 1.00 90.12 C
ATOM 251 C GLU A 30 -7.715 -6.239 2.650 1.00 90.12 C
ATOM 252 CB GLU A 30 -9.254 -8.215 2.703 1.00 90.12 C
ATOM 253 O GLU A 30 -8.286 -5.248 3.110 1.00 90.12 O
ATOM 254 CG GLU A 30 -9.273 -8.357 1.188 1.00 90.12 C
ATOM 255 CD GLU A 30 -10.542 -9.008 0.662 1.00 90.12 C
ATOM 256 OE1 GLU A 30 -11.585 -8.321 0.575 1.00 90.12 O
ATOM 257 OE2 GLU A 30 -10.494 -10.215 0.335 1.00 90.12 O
ATOM 258 N VAL A 31 -6.835 -6.090 1.610 1.00 86.60 N
ATOM 259 CA VAL A 31 -6.583 -4.828 0.922 1.00 86.60 C
ATOM 260 C VAL A 31 -6.313 -5.090 -0.558 1.00 86.60 C
ATOM 261 CB VAL A 31 -5.397 -4.064 1.553 1.00 86.60 C
ATOM 262 O VAL A 31 -5.684 -6.090 -0.912 1.00 86.60 O
ATOM 263 CG1 VAL A 31 -4.102 -4.859 1.404 1.00 86.60 C
ATOM 264 CG2 VAL A 31 -5.255 -2.682 0.919 1.00 86.60 C
ATOM 265 N THR A 32 -6.886 -4.281 -1.455 1.00 86.60 N
ATOM 266 CA THR A 32 -6.648 -4.386 -2.890 1.00 86.60 C
ATOM 267 C THR A 32 -5.875 -3.173 -3.400 1.00 86.60 C
ATOM 268 CB THR A 32 -7.971 -4.518 -3.667 1.00 86.60 C
ATOM 269 O THR A 32 -6.311 -2.033 -3.224 1.00 86.60 O
ATOM 270 CG2 THR A 32 -7.715 -4.710 -5.159 1.00 86.60 C
ATOM 271 OG1 THR A 32 -8.700 -5.647 -3.170 1.00 86.60 O
ATOM 272 N PHE A 33 -4.627 -3.379 -3.960 1.00 85.17 N
ATOM 273 CA PHE A 33 -3.825 -2.315 -4.552 1.00 85.17 C
ATOM 274 C PHE A 33 -4.096 -2.201 -6.048 1.00 85.17 C
ATOM 275 CB PHE A 33 -2.333 -2.565 -4.307 1.00 85.17 C
ATOM 276 O PHE A 33 -4.048 -3.198 -6.771 1.00 85.17 O
ATOM 277 CG PHE A 33 -1.918 -2.397 -2.870 1.00 85.17 C
ATOM 278 CD1 PHE A 33 -1.696 -1.132 -2.340 1.00 85.17 C
ATOM 279 CD2 PHE A 33 -1.750 -3.504 -2.050 1.00 85.17 C
ATOM 280 CE1 PHE A 33 -1.312 -0.973 -1.011 1.00 85.17 C
ATOM 281 CE2 PHE A 33 -1.366 -3.354 -0.720 1.00 85.17 C
ATOM 282 CZ PHE A 33 -1.146 -2.087 -0.203 1.00 85.17 C
ATOM 283 N VAL A 34 -4.488 -1.033 -6.377 1.00 79.71 N
ATOM 284 CA VAL A 34 -4.738 -0.771 -7.791 1.00 79.71 C
ATOM 285 C VAL A 34 -3.582 0.033 -8.380 1.00 79.71 C
ATOM 286 CB VAL A 34 -6.072 -0.020 -8.002 1.00 79.71 C
ATOM 287 O VAL A 34 -3.285 1.137 -7.917 1.00 79.71 O
ATOM 288 CG1 VAL A 34 -6.363 0.156 -9.491 1.00 79.71 C
ATOM 289 CG2 VAL A 34 -7.216 -0.762 -7.314 1.00 79.71 C
ATOM 290 N ILE A 35 -2.823 -0.596 -9.337 1.00 77.12 N
ATOM 291 CA ILE A 35 -1.721 0.099 -9.993 1.00 77.12 C
ATOM 292 C ILE A 35 -2.004 0.218 -11.489 1.00 77.12 C
ATOM 293 CB ILE A 35 -0.376 -0.624 -9.757 1.00 77.12 C
ATOM 294 O ILE A 35 -2.768 -0.572 -12.047 1.00 77.12 O
ATOM 295 CG1 ILE A 35 -0.441 -2.059 -10.291 1.00 77.12 C
ATOM 296 CG2 ILE A 35 -0.007 -0.608 -8.271 1.00 77.12 C
ATOM 297 CD1 ILE A 35 0.916 -2.740 -10.402 1.00 77.12 C
ATOM 298 N SER A 36 -1.599 1.325 -11.947 1.00 72.93 N
ATOM 299 CA SER A 36 -1.731 1.517 -13.387 1.00 72.93 C
ATOM 300 C SER A 36 -0.484 1.040 -14.125 1.00 72.93 C
ATOM 301 CB SER A 36 -1.994 2.988 -13.711 1.00 72.93 C
ATOM 302 O SER A 36 0.613 1.040 -13.564 1.00 72.93 O
ATOM 303 OG SER A 36 -1.967 3.208 -15.110 1.00 72.93 O
ATOM 304 N SER A 37 -0.670 0.403 -15.236 1.00 69.39 N
ATOM 305 CA SER A 37 0.426 -0.073 -16.073 1.00 69.39 C
ATOM 306 C SER A 37 1.372 1.064 -16.443 1.00 69.39 C
ATOM 307 CB SER A 37 -0.115 -0.731 -17.343 1.00 69.39 C
ATOM 308 O SER A 37 2.505 0.823 -16.867 1.00 69.39 O
ATOM 309 OG SER A 37 -1.045 0.119 -17.992 1.00 69.39 O
ATOM 310 N ALA A 38 0.862 2.297 -16.248 1.00 67.39 N
ATOM 311 CA ALA A 38 1.656 3.492 -16.525 1.00 67.39 C
ATOM 312 C ALA A 38 2.561 3.834 -15.344 1.00 67.39 C
ATOM 313 CB ALA A 38 0.745 4.672 -16.853 1.00 67.39 C
ATOM 314 O ALA A 38 3.386 4.746 -15.431 1.00 67.39 O
ATOM 315 N ASP A 39 2.279 3.071 -14.251 1.00 72.75 N
ATOM 316 CA ASP A 39 3.057 3.298 -13.037 1.00 72.75 C
ATOM 317 C ASP A 39 4.501 2.833 -13.215 1.00 72.75 C
ATOM 318 CB ASP A 39 2.417 2.580 -11.847 1.00 72.75 C
ATOM 319 O ASP A 39 4.780 1.959 -14.039 1.00 72.75 O
ATOM 320 CG ASP A 39 1.183 3.292 -11.320 1.00 72.75 C
ATOM 321 OD1 ASP A 39 1.293 4.454 -10.874 1.00 72.75 O
ATOM 322 OD2 ASP A 39 0.091 2.684 -11.348 1.00 72.75 O
ATOM 323 N SER A 40 5.475 3.562 -12.768 1.00 72.73 N
ATOM 324 CA SER A 40 6.908 3.286 -12.803 1.00 72.73 C
ATOM 325 C SER A 40 7.256 2.065 -11.958 1.00 72.73 C
ATOM 326 CB SER A 40 7.700 4.499 -12.314 1.00 72.73 C
ATOM 327 O SER A 40 6.491 1.677 -11.073 1.00 72.73 O
ATOM 328 OG SER A 40 7.410 5.640 -13.101 1.00 72.73 O
ATOM 329 N GLU A 41 8.265 1.200 -12.472 1.00 83.34 N
ATOM 330 CA GLU A 41 8.802 0.051 -11.749 1.00 83.34 C
ATOM 331 C GLU A 41 8.950 0.356 -10.261 1.00 83.34 C
ATOM 332 CB GLU A 41 10.152 -0.371 -12.336 1.00 83.34 C
ATOM 333 O GLU A 41 8.722 -0.515 -9.418 1.00 83.34 O
ATOM 334 CG GLU A 41 10.046 -1.058 -13.689 1.00 83.34 C
ATOM 335 CD GLU A 41 11.377 -1.586 -14.200 1.00 83.34 C
ATOM 336 OE1 GLU A 41 11.690 -2.775 -13.962 1.00 83.34 O
ATOM 337 OE2 GLU A 41 12.113 -0.805 -14.842 1.00 83.34 O
ATOM 338 N GLU A 42 9.164 1.541 -9.996 1.00 85.29 N
ATOM 339 CA GLU A 42 9.324 1.965 -8.608 1.00 85.29 C
ATOM 340 C GLU A 42 8.026 1.793 -7.825 1.00 85.29 C
ATOM 341 CB GLU A 42 9.789 3.422 -8.541 1.00 85.29 C
ATOM 342 O GLU A 42 8.031 1.257 -6.715 1.00 85.29 O
ATOM 343 CG GLU A 42 10.241 3.861 -7.156 1.00 85.29 C
ATOM 344 CD GLU A 42 11.177 5.059 -7.182 1.00 85.29 C
ATOM 345 OE1 GLU A 42 10.689 6.208 -7.276 1.00 85.29 O
ATOM 346 OE2 GLU A 42 12.408 4.847 -7.108 1.00 85.29 O
ATOM 347 N ILE A 43 6.916 2.343 -8.302 1.00 82.81 N
ATOM 348 CA ILE A 43 5.610 2.254 -7.658 1.00 82.81 C
ATOM 349 C ILE A 43 5.202 0.789 -7.519 1.00 82.81 C
ATOM 350 CB ILE A 43 4.537 3.036 -8.447 1.00 82.81 C
ATOM 351 O ILE A 43 4.717 0.370 -6.465 1.00 82.81 O
ATOM 352 CG1 ILE A 43 4.822 4.541 -8.387 1.00 82.81 C
ATOM 353 CG2 ILE A 43 3.137 2.722 -7.911 1.00 82.81 C
ATOM 354 CD1 ILE A 43 3.876 5.384 -9.232 1.00 82.81 C
ATOM 355 N LYS A 44 5.549 -0.013 -8.535 1.00 83.23 N
ATOM 356 CA LYS A 44 5.294 -1.450 -8.502 1.00 83.23 C
ATOM 357 C LYS A 44 6.064 -2.118 -7.366 1.00 83.23 C
ATOM 358 CB LYS A 44 5.667 -2.093 -9.838 1.00 83.23 C
ATOM 359 O LYS A 44 5.511 -2.944 -6.636 1.00 83.23 O
ATOM 360 CG LYS A 44 4.624 -1.902 -10.929 1.00 83.23 C
ATOM 361 CD LYS A 44 4.985 -2.679 -12.189 1.00 83.23 C
ATOM 362 CE LYS A 44 4.004 -2.398 -13.320 1.00 83.23 C
ATOM 363 NZ LYS A 44 4.334 -3.183 -14.547 1.00 83.23 N
ATOM 364 N LYS A 45 7.296 -1.791 -7.304 1.00 88.48 N
ATOM 365 CA LYS A 45 8.146 -2.345 -6.255 1.00 88.48 C
ATOM 366 C LYS A 45 7.628 -1.967 -4.870 1.00 88.48 C
ATOM 367 CB LYS A 45 9.589 -1.867 -6.424 1.00 88.48 C
ATOM 368 O LYS A 45 7.601 -2.800 -3.962 1.00 88.48 O
ATOM 369 CG LYS A 45 10.363 -2.605 -7.506 1.00 88.48 C
ATOM 370 CD LYS A 45 11.819 -2.159 -7.554 1.00 88.48 C
ATOM 371 CE LYS A 45 12.582 -2.860 -8.670 1.00 88.48 C
ATOM 372 NZ LYS A 45 14.014 -2.437 -8.712 1.00 88.48 N
ATOM 373 N LEU A 46 7.267 -0.760 -4.707 1.00 90.20 N
ATOM 374 CA LEU A 46 6.764 -0.277 -3.426 1.00 90.20 C
ATOM 375 C LEU A 46 5.478 -1.001 -3.040 1.00 90.20 C
ATOM 376 CB LEU A 46 6.516 1.233 -3.481 1.00 90.20 C
ATOM 377 O LEU A 46 5.308 -1.401 -1.886 1.00 90.20 O
ATOM 378 CG LEU A 46 7.756 2.126 -3.413 1.00 90.20 C
ATOM 379 CD1 LEU A 46 7.391 3.567 -3.755 1.00 90.20 C
ATOM 380 CD2 LEU A 46 8.398 2.045 -2.033 1.00 90.20 C
ATOM 381 N VAL A 47 4.634 -1.055 -3.956 1.00 87.72 N
ATOM 382 CA VAL A 47 3.355 -1.724 -3.743 1.00 87.72 C
ATOM 383 C VAL A 47 3.592 -3.184 -3.363 1.00 87.72 C
ATOM 384 CB VAL A 47 2.454 -1.638 -4.995 1.00 87.72 C
ATOM 385 O VAL A 47 2.953 -3.705 -2.446 1.00 87.72 O
ATOM 386 CG1 VAL A 47 1.201 -2.494 -4.819 1.00 87.72 C
ATOM 387 CG2 VAL A 47 2.077 -0.186 -5.282 1.00 87.72 C
ATOM 388 N GLU A 48 4.453 -3.832 -4.059 1.00 89.12 N
ATOM 389 CA GLU A 48 4.817 -5.214 -3.761 1.00 89.12 C
ATOM 390 C GLU A 48 5.366 -5.347 -2.344 1.00 89.12 C
ATOM 391 CB GLU A 48 5.843 -5.729 -4.774 1.00 89.12 C
ATOM 392 O GLU A 48 5.015 -6.283 -1.622 1.00 89.12 O
ATOM 393 CG GLU A 48 5.260 -6.014 -6.150 1.00 89.12 C
ATOM 394 CD GLU A 48 6.211 -6.777 -7.058 1.00 89.12 C
ATOM 395 OE1 GLU A 48 6.128 -8.025 -7.112 1.00 89.12 O
ATOM 396 OE2 GLU A 48 7.046 -6.122 -7.721 1.00 89.12 O
ATOM 397 N GLU A 49 6.263 -4.525 -2.078 1.00 92.03 N
ATOM 398 CA GLU A 49 6.825 -4.532 -0.731 1.00 92.03 C
ATOM 399 C GLU A 49 5.738 -4.339 0.322 1.00 92.03 C
ATOM 400 CB GLU A 49 7.894 -3.445 -0.589 1.00 92.03 C
ATOM 401 O GLU A 49 5.730 -5.026 1.346 1.00 92.03 O
ATOM 402 CG GLU A 49 9.203 -3.773 -1.292 1.00 92.03 C
ATOM 403 CD GLU A 49 10.340 -2.840 -0.909 1.00 92.03 C
ATOM 404 OE1 GLU A 49 10.970 -3.054 0.152 1.00 92.03 O
ATOM 405 OE2 GLU A 49 10.603 -1.886 -1.675 1.00 92.03 O
ATOM 406 N ASN A 50 4.950 -3.304 0.106 1.00 90.05 N
ATOM 407 CA ASN A 50 3.830 -3.068 1.010 1.00 90.05 C
ATOM 408 C ASN A 50 2.960 -4.313 1.161 1.00 90.05 C
ATOM 409 CB ASN A 50 2.987 -1.887 0.524 1.00 90.05 C
ATOM 410 O ASN A 50 2.539 -4.651 2.269 1.00 90.05 O
ATOM 411 CG ASN A 50 3.539 -0.550 0.978 1.00 90.05 C
ATOM 412 ND2 ASN A 50 4.277 0.116 0.098 1.00 90.05 N
ATOM 413 OD1 ASN A 50 3.305 -0.120 2.110 1.00 90.05 O
ATOM 414 N ALA A 51 2.704 -4.904 0.020 1.00 89.43 N
ATOM 415 CA ALA A 51 1.897 -6.121 -0.004 1.00 89.43 C
ATOM 416 C ALA A 51 2.538 -7.220 0.839 1.00 89.43 C
ATOM 417 CB ALA A 51 1.701 -6.602 -1.439 1.00 89.43 C
ATOM 418 O ALA A 51 1.849 -7.920 1.585 1.00 89.43 O
ATOM 419 N GLU A 52 3.815 -7.414 0.696 1.00 91.12 N
ATOM 420 CA GLU A 52 4.547 -8.437 1.437 1.00 91.12 C
ATOM 421 C GLU A 52 4.436 -8.213 2.942 1.00 91.12 C
ATOM 422 CB GLU A 52 6.018 -8.457 1.015 1.00 91.12 C
ATOM 423 O GLU A 52 4.209 -9.158 3.700 1.00 91.12 O
ATOM 424 CG GLU A 52 6.248 -9.005 -0.387 1.00 91.12 C
ATOM 425 CD GLU A 52 7.720 -9.166 -0.733 1.00 91.12 C
ATOM 426 OE1 GLU A 52 8.313 -10.215 -0.392 1.00 91.12 O
ATOM 427 OE2 GLU A 52 8.285 -8.235 -1.350 1.00 91.12 O
ATOM 428 N ILE A 53 4.609 -7.008 3.402 1.00 91.89 N
ATOM 429 CA ILE A 53 4.531 -6.669 4.819 1.00 91.89 C
ATOM 430 C ILE A 53 3.112 -6.910 5.330 1.00 91.89 C
ATOM 431 CB ILE A 53 4.950 -5.204 5.074 1.00 91.89 C
ATOM 432 O ILE A 53 2.921 -7.486 6.403 1.00 91.89 O
ATOM 433 CG1 ILE A 53 6.419 -4.994 4.686 1.00 91.89 C
ATOM 434 CG2 ILE A 53 4.710 -4.819 6.536 1.00 91.89 C
ATOM 435 CD1 ILE A 53 6.878 -3.544 4.762 1.00 91.89 C
ATOM 436 N LEU A 54 2.220 -6.422 4.598 1.00 90.60 N
ATOM 437 CA LEU A 54 0.817 -6.592 4.960 1.00 90.60 C
ATOM 438 C LEU A 54 0.454 -8.071 5.048 1.00 90.60 C
ATOM 439 CB LEU A 54 -0.087 -5.892 3.942 1.00 90.60 C
ATOM 440 O LEU A 54 -0.274 -8.482 5.955 1.00 90.60 O
ATOM 441 CG LEU A 54 -0.161 -4.367 4.036 1.00 90.60 C
ATOM 442 CD1 LEU A 54 -0.837 -3.792 2.797 1.00 90.60 C
ATOM 443 CD2 LEU A 54 -0.900 -3.944 5.301 1.00 90.60 C
ATOM 444 N GLU A 55 0.909 -8.858 4.086 1.00 90.94 N
ATOM 445 CA GLU A 55 0.715 -10.305 4.103 1.00 90.94 C
ATOM 446 C GLU A 55 1.303 -10.926 5.367 1.00 90.94 C
ATOM 447 CB GLU A 55 1.342 -10.946 2.862 1.00 90.94 C
ATOM 448 O GLU A 55 0.712 -11.837 5.950 1.00 90.94 O
ATOM 449 CG GLU A 55 0.549 -10.716 1.584 1.00 90.94 C
ATOM 450 CD GLU A 55 0.996 -11.603 0.432 1.00 90.94 C
ATOM 451 OE1 GLU A 55 0.436 -12.710 0.266 1.00 90.94 O
ATOM 452 OE2 GLU A 55 1.915 -11.187 -0.308 1.00 90.94 O
ATOM 453 N LYS A 56 2.477 -10.439 5.689 1.00 90.58 N
ATOM 454 CA LYS A 56 3.128 -10.914 6.907 1.00 90.58 C
ATOM 455 C LYS A 56 2.303 -10.564 8.143 1.00 90.58 C
ATOM 456 CB LYS A 56 4.533 -10.324 7.031 1.00 90.58 C
ATOM 457 O LYS A 56 2.287 -11.317 9.119 1.00 90.58 O
ATOM 458 CG LYS A 56 5.551 -10.945 6.085 1.00 90.58 C
ATOM 459 CD LYS A 56 6.949 -10.393 6.327 1.00 90.58 C
ATOM 460 CE LYS A 56 7.967 -11.008 5.376 1.00 90.58 C
ATOM 461 NZ LYS A 56 9.350 -10.516 5.651 1.00 90.58 N
ATOM 462 N LEU A 57 1.666 -9.472 8.081 1.00 89.28 N
ATOM 463 CA LEU A 57 0.843 -8.979 9.180 1.00 89.28 C
ATOM 464 C LEU A 57 -0.522 -9.661 9.185 1.00 89.28 C
ATOM 465 CB LEU A 57 0.668 -7.462 9.080 1.00 89.28 C
ATOM 466 O LEU A 57 -1.332 -9.430 10.085 1.00 89.28 O
ATOM 467 CG LEU A 57 1.906 -6.615 9.381 1.00 89.28 C
ATOM 468 CD1 LEU A 57 1.651 -5.157 9.016 1.00 89.28 C
ATOM 469 CD2 LEU A 57 2.298 -6.744 10.849 1.00 89.28 C
ATOM 470 N GLY A 58 -0.801 -10.562 8.254 1.00 89.24 N
ATOM 471 CA GLY A 58 -2.043 -11.316 8.202 1.00 89.24 C
ATOM 472 C GLY A 58 -3.109 -10.651 7.352 1.00 89.24 C
ATOM 473 O GLY A 58 -4.304 -10.868 7.563 1.00 89.24 O
ATOM 474 N VAL A 59 -2.742 -9.745 6.559 1.00 90.59 N
ATOM 475 CA VAL A 59 -3.658 -9.015 5.689 1.00 90.59 C
ATOM 476 C VAL A 59 -3.626 -9.615 4.285 1.00 90.59 C
ATOM 477 CB VAL A 59 -3.312 -7.511 5.634 1.00 90.59 C
ATOM 478 O VAL A 59 -2.551 -9.854 3.730 1.00 90.59 O
ATOM 479 CG1 VAL A 59 -4.305 -6.760 4.750 1.00 90.59 C
ATOM 480 CG2 VAL A 59 -3.290 -6.917 7.042 1.00 90.59 C
ATOM 481 N LYS A 60 -4.796 -10.038 3.669 1.00 91.31 N
ATOM 482 CA LYS A 60 -4.890 -10.525 2.295 1.00 91.31 C
ATOM 483 C LYS A 60 -4.751 -9.380 1.296 1.00 91.31 C
ATOM 484 CB LYS A 60 -6.215 -11.256 2.074 1.00 91.31 C
ATOM 485 O LYS A 60 -5.554 -8.444 1.300 1.00 91.31 O
ATOM 486 CG LYS A 60 -6.280 -12.039 0.771 1.00 91.31 C
ATOM 487 CD LYS A 60 -7.562 -12.856 0.673 1.00 91.31 C
ATOM 488 CE LYS A 60 -7.618 -13.657 -0.621 1.00 91.31 C
ATOM 489 NZ LYS A 60 -8.859 -14.483 -0.707 1.00 91.31 N
ATOM 490 N VAL A 61 -3.640 -9.388 0.542 1.00 90.50 N
ATOM 491 CA VAL A 61 -3.325 -8.295 -0.373 1.00 90.50 C
ATOM 492 C VAL A 61 -3.627 -8.719 -1.808 1.00 90.50 C
ATOM 493 CB VAL A 61 -1.849 -7.856 -0.246 1.00 90.50 C
ATOM 494 O VAL A 61 -3.177 -9.776 -2.258 1.00 90.50 O
ATOM 495 CG1 VAL A 61 -1.548 -6.696 -1.192 1.00 90.50 C
ATOM 496 CG2 VAL A 61 -1.531 -7.470 1.197 1.00 90.50 C
ATOM 497 N LYS A 62 -4.464 -7.942 -2.568 1.00 89.89 N
ATOM 498 CA LYS A 62 -4.755 -8.135 -3.986 1.00 89.89 C
ATOM 499 C LYS A 62 -4.271 -6.948 -4.813 1.00 89.89 C
ATOM 500 CB LYS A 62 -6.254 -8.348 -4.202 1.00 89.89 C
ATOM 501 O LYS A 62 -4.598 -5.799 -4.510 1.00 89.89 O
ATOM 502 CG LYS A 62 -6.615 -8.835 -5.598 1.00 89.89 C
ATOM 503 CD LYS A 62 -8.100 -9.156 -5.711 1.00 89.89 C
ATOM 504 CE LYS A 62 -8.454 -9.686 -7.094 1.00 89.89 C
ATOM 505 NZ LYS A 62 -9.906 -10.018 -7.205 1.00 89.89 N
ATOM 506 N VAL A 63 -3.369 -7.174 -5.795 1.00 85.66 N
ATOM 507 CA VAL A 63 -2.847 -6.112 -6.649 1.00 85.66 C
ATOM 508 C VAL A 63 -3.490 -6.198 -8.032 1.00 85.66 C
ATOM 509 CB VAL A 63 -1.309 -6.185 -6.772 1.00 85.66 C
ATOM 510 O VAL A 63 -3.359 -7.211 -8.723 1.00 85.66 O
ATOM 511 CG1 VAL A 63 -0.779 -5.034 -7.625 1.00 85.66 C
ATOM 512 CG2 VAL A 63 -0.663 -6.171 -5.388 1.00 85.66 C
ATOM 513 N VAL A 64 -4.304 -5.230 -8.309 1.00 83.17 N
ATOM 514 CA VAL A 64 -5.001 -5.174 -9.589 1.00 83.17 C
ATOM 515 C VAL A 64 -4.339 -4.137 -10.494 1.00 83.17 C
ATOM 516 CB VAL A 64 -6.499 -4.843 -9.406 1.00 83.17 C
ATOM 517 O VAL A 64 -4.094 -3.004 -10.073 1.00 83.17 O
ATOM 518 CG1 VAL A 64 -7.221 -4.843 -10.753 1.00 83.17 C
ATOM 519 CG2 VAL A 64 -7.152 -5.836 -8.447 1.00 83.17 C
ATOM 520 N GLU A 65 -3.914 -4.576 -11.655 1.00 82.38 N
ATOM 521 CA GLU A 65 -3.300 -3.697 -12.646 1.00 82.38 C
ATOM 522 C GLU A 65 -4.341 -3.154 -13.621 1.00 82.38 C
ATOM 523 CB GLU A 65 -2.198 -4.434 -13.410 1.00 82.38 C
ATOM 524 O GLU A 65 -5.007 -3.922 -14.318 1.00 82.38 O
ATOM 525 CG GLU A 65 -1.357 -3.531 -14.300 1.00 82.38 C
ATOM 526 CD GLU A 65 -0.228 -4.266 -15.005 1.00 82.38 C
ATOM 527 OE1 GLU A 65 -0.445 -4.784 -16.124 1.00 82.38 O
ATOM 528 OE2 GLU A 65 0.884 -4.325 -14.433 1.00 82.38 O
ATOM 529 N LEU A 66 -4.731 -1.848 -13.511 1.00 73.61 N
ATOM 530 CA LEU A 66 -5.704 -1.232 -14.407 1.00 73.61 C
ATOM 531 C LEU A 66 -5.031 -0.737 -15.682 1.00 73.61 C
ATOM 532 CB LEU A 66 -6.417 -0.072 -13.708 1.00 73.61 C
ATOM 533 O LEU A 66 -3.927 -0.189 -15.634 1.00 73.61 O
ATOM 534 CG LEU A 66 -7.425 -0.450 -12.622 1.00 73.61 C
ATOM 535 CD1 LEU A 66 -7.847 0.787 -11.836 1.00 73.61 C
ATOM 536 CD2 LEU A 66 -8.638 -1.141 -13.234 1.00 73.61 C
ATOM 537 N GLY A 67 -5.215 -1.540 -16.831 1.00 61.81 N
ATOM 538 CA GLY A 67 -4.761 -1.145 -18.155 1.00 61.81 C
ATOM 539 C GLY A 67 -5.505 0.056 -18.708 1.00 61.81 C
ATOM 540 O GLY A 67 -6.738 0.065 -18.746 1.00 61.81 O
ATOM 541 N GLY A 68 -5.090 1.390 -18.491 1.00 60.96 N
ATOM 542 CA GLY A 68 -5.697 2.625 -18.962 1.00 60.96 C
ATOM 543 C GLY A 68 -4.768 3.820 -18.865 1.00 60.96 C
ATOM 544 O GLY A 68 -3.738 3.760 -18.191 1.00 60.96 O
ATOM 545 N GLU A 69 -4.531 4.607 -19.986 1.00 63.19 N
ATOM 546 CA GLU A 69 -3.769 5.840 -20.158 1.00 63.19 C
ATOM 547 C GLU A 69 -3.863 6.725 -18.918 1.00 63.19 C
ATOM 548 CB GLU A 69 -4.257 6.607 -21.389 1.00 63.19 C
ATOM 549 O GLU A 69 -4.938 6.870 -18.332 1.00 63.19 O
ATOM 550 CG GLU A 69 -3.142 7.015 -22.341 1.00 63.19 C
ATOM 551 CD GLU A 69 -3.653 7.632 -23.634 1.00 63.19 C
ATOM 552 OE1 GLU A 69 -3.936 8.852 -23.654 1.00 63.19 O
ATOM 553 OE2 GLU A 69 -3.771 6.890 -24.634 1.00 63.19 O
ATOM 554 N LEU A 70 -2.763 6.805 -18.127 1.00 62.85 N
ATOM 555 CA LEU A 70 -2.656 7.713 -16.990 1.00 62.85 C
ATOM 556 C LEU A 70 -3.013 9.139 -17.398 1.00 62.85 C
ATOM 557 CB LEU A 70 -1.242 7.676 -16.406 1.00 62.85 C
ATOM 558 O LEU A 70 -2.495 9.653 -18.391 1.00 62.85 O
ATOM 559 CG LEU A 70 -0.896 6.460 -15.544 1.00 62.85 C
ATOM 560 CD1 LEU A 70 0.611 6.230 -15.534 1.00 62.85 C
ATOM 561 CD2 LEU A 70 -1.425 6.642 -14.126 1.00 62.85 C
ATOM 562 N ASP A 71 -4.382 9.504 -17.115 1.00 67.78 N
ATOM 563 CA ASP A 71 -4.779 10.889 -17.347 1.00 67.78 C
ATOM 564 C ASP A 71 -3.796 11.859 -16.695 1.00 67.78 C
ATOM 565 CB ASP A 71 -6.193 11.139 -16.819 1.00 67.78 C
ATOM 566 O ASP A 71 -3.070 11.487 -15.770 1.00 67.78 O
ATOM 567 CG ASP A 71 -6.996 12.083 -17.696 1.00 67.78 C
ATOM 568 OD1 ASP A 71 -7.054 13.295 -17.394 1.00 67.78 O
ATOM 569 OD2 ASP A 71 -7.578 11.611 -18.697 1.00 67.78 O
ATOM 570 N GLU A 72 -3.069 12.841 -17.280 1.00 66.31 N
ATOM 571 CA GLU A 72 -2.192 13.936 -16.876 1.00 66.31 C
ATOM 572 C GLU A 72 -2.299 14.205 -15.378 1.00 66.31 C
ATOM 573 CB GLU A 72 -2.521 15.207 -17.663 1.00 66.31 C
ATOM 574 O GLU A 72 -1.296 14.487 -14.719 1.00 66.31 O
ATOM 575 CG GLU A 72 -1.767 15.327 -18.980 1.00 66.31 C
ATOM 576 CD GLU A 72 -2.109 16.590 -19.754 1.00 66.31 C
ATOM 577 OE1 GLU A 72 -1.474 17.642 -19.511 1.00 66.31 O
ATOM 578 OE2 GLU A 72 -3.020 16.529 -20.609 1.00 66.31 O
ATOM 579 N LEU A 73 -3.365 13.942 -14.773 1.00 66.33 N
ATOM 580 CA LEU A 73 -3.564 14.142 -13.342 1.00 66.33 C
ATOM 581 C LEU A 73 -2.893 13.032 -12.539 1.00 66.33 C
ATOM 582 CB LEU A 73 -5.058 14.196 -13.010 1.00 66.33 C
ATOM 583 O LEU A 73 -2.291 13.292 -11.495 1.00 66.33 O
ATOM 584 CG LEU A 73 -5.727 15.567 -13.115 1.00 66.33 C
ATOM 585 CD1 LEU A 73 -7.205 15.411 -13.456 1.00 66.33 C
ATOM 586 CD2 LEU A 73 -5.551 16.349 -11.818 1.00 66.33 C
ATOM 587 N THR A 74 -2.894 11.771 -13.049 1.00 65.78 N
ATOM 588 CA THR A 74 -2.225 10.632 -12.430 1.00 65.78 C
ATOM 589 C THR A 74 -0.710 10.750 -12.575 1.00 65.78 C
ATOM 590 CB THR A 74 -2.699 9.303 -13.046 1.00 65.78 C
ATOM 591 O THR A 74 0.035 10.429 -11.646 1.00 65.78 O
ATOM 592 CG2 THR A 74 -2.057 8.111 -12.344 1.00 65.78 C
ATOM 593 OG1 THR A 74 -4.124 9.206 -12.920 1.00 65.78 O
ATOM 594 N LEU A 75 -0.311 11.344 -13.746 1.00 68.90 N
ATOM 595 CA LEU A 75 1.107 11.593 -13.982 1.00 68.90 C
ATOM 596 C LEU A 75 1.625 12.698 -13.068 1.00 68.90 C
ATOM 597 CB LEU A 75 1.348 11.970 -15.446 1.00 68.90 C
ATOM 598 O LEU A 75 2.740 12.610 -12.550 1.00 68.90 O
ATOM 599 CG LEU A 75 1.401 10.812 -16.445 1.00 68.90 C
ATOM 600 CD1 LEU A 75 1.017 11.297 -17.839 1.00 68.90 C
ATOM 601 CD2 LEU A 75 2.788 10.179 -16.456 1.00 68.90 C
ATOM 602 N LYS A 76 0.708 13.698 -12.805 1.00 71.32 N
ATOM 603 CA LYS A 76 1.071 14.813 -11.936 1.00 71.32 C
ATOM 604 C LYS A 76 1.146 14.372 -10.477 1.00 71.32 C
ATOM 605 CB LYS A 76 0.068 15.959 -12.086 1.00 71.32 C
ATOM 606 O LYS A 76 1.991 14.854 -9.720 1.00 71.32 O
ATOM 607 CG LYS A 76 0.514 17.260 -11.436 1.00 71.32 C
ATOM 608 CD LYS A 76 -0.488 18.381 -11.684 1.00 71.32 C
ATOM 609 CE LYS A 76 -0.066 19.672 -10.996 1.00 71.32 C
ATOM 610 NZ LYS A 76 -1.069 20.760 -11.197 1.00 71.32 N
ATOM 611 N HIS A 77 0.420 13.365 -10.189 1.00 75.38 N
ATOM 612 CA HIS A 77 0.315 12.913 -8.807 1.00 75.38 C
ATOM 613 C HIS A 77 1.167 11.672 -8.567 1.00 75.38 C
ATOM 614 CB HIS A 77 -1.144 12.626 -8.446 1.00 75.38 C
ATOM 615 O HIS A 77 1.146 11.101 -7.474 1.00 75.38 O
ATOM 616 CG HIS A 77 -1.983 13.857 -8.316 1.00 75.38 C
ATOM 617 CD2 HIS A 77 -1.961 14.855 -7.401 1.00 75.38 C
ATOM 618 ND1 HIS A 77 -2.994 14.166 -9.199 1.00 75.38 N
ATOM 619 CE1 HIS A 77 -3.559 15.304 -8.833 1.00 75.38 C
ATOM 620 NE2 HIS A 77 -2.951 15.743 -7.744 1.00 75.38 N
ATOM 621 N LYS A 78 1.936 11.316 -9.653 1.00 73.17 N
ATOM 622 CA LYS A 78 2.824 10.158 -9.610 1.00 73.17 C
ATOM 623 C LYS A 78 3.862 10.302 -8.500 1.00 73.17 C
ATOM 624 CB LYS A 78 3.521 9.965 -10.957 1.00 73.17 C
ATOM 625 O LYS A 78 4.117 9.353 -7.755 1.00 73.17 O
ATOM 626 CG LYS A 78 4.245 8.633 -11.095 1.00 73.17 C
ATOM 627 CD LYS A 78 4.838 8.461 -12.487 1.00 73.17 C
ATOM 628 CE LYS A 78 5.641 7.172 -12.598 1.00 73.17 C
ATOM 629 NZ LYS A 78 6.222 6.996 -13.963 1.00 73.17 N
ATOM 630 N GLU A 79 4.467 11.425 -8.459 1.00 82.01 N
ATOM 631 CA GLU A 79 5.451 11.676 -7.410 1.00 82.01 C
ATOM 632 C GLU A 79 4.810 11.619 -6.027 1.00 82.01 C
ATOM 633 CB GLU A 79 6.127 13.034 -7.621 1.00 82.01 C
ATOM 634 O GLU A 79 5.388 11.062 -5.091 1.00 82.01 O
ATOM 635 CG GLU A 79 7.125 13.052 -8.769 1.00 82.01 C
ATOM 636 CD GLU A 79 7.967 14.317 -8.812 1.00 82.01 C
ATOM 637 OE1 GLU A 79 9.088 14.320 -8.254 1.00 82.01 O
ATOM 638 OE2 GLU A 79 7.502 15.314 -9.408 1.00 82.01 O
ATOM 639 N LYS A 80 3.748 12.178 -5.930 1.00 81.73 N
ATOM 640 CA LYS A 80 3.031 12.190 -4.659 1.00 81.73 C
ATOM 641 C LYS A 80 2.604 10.781 -4.255 1.00 81.73 C
ATOM 642 CB LYS A 80 1.808 13.105 -4.740 1.00 81.73 C
ATOM 643 O LYS A 80 2.693 10.412 -3.082 1.00 81.73 O
ATOM 644 CG LYS A 80 2.129 14.583 -4.570 1.00 81.73 C
ATOM 645 CD LYS A 80 0.863 15.427 -4.516 1.00 81.73 C
ATOM 646 CE LYS A 80 1.184 16.914 -4.444 1.00 81.73 C
ATOM 647 NZ LYS A 80 -0.055 17.744 -4.351 1.00 81.73 N
ATOM 648 N ILE A 81 2.180 10.044 -5.166 1.00 80.10 N
ATOM 649 CA ILE A 81 1.790 8.655 -4.957 1.00 80.10 C
ATOM 650 C ILE A 81 2.993 7.848 -4.473 1.00 80.10 C
ATOM 651 CB ILE A 81 1.208 8.030 -6.245 1.00 80.10 C
ATOM 652 O ILE A 81 2.875 7.039 -3.550 1.00 80.10 O
ATOM 653 CG1 ILE A 81 -0.130 8.688 -6.600 1.00 80.10 C
ATOM 654 CG2 ILE A 81 1.050 6.515 -6.087 1.00 80.10 C
ATOM 655 CD1 ILE A 81 -0.679 8.279 -7.960 1.00 80.10 C
ATOM 656 N LEU A 82 4.081 8.122 -5.134 1.00 83.52 N
ATOM 657 CA LEU A 82 5.328 7.478 -4.739 1.00 83.52 C
ATOM 658 C LEU A 82 5.679 7.813 -3.294 1.00 83.52 C
ATOM 659 CB LEU A 82 6.470 7.905 -5.666 1.00 83.52 C
ATOM 660 O LEU A 82 5.984 6.919 -2.501 1.00 83.52 O
ATOM 661 CG LEU A 82 7.813 7.204 -5.456 1.00 83.52 C
ATOM 662 CD1 LEU A 82 7.738 5.760 -5.939 1.00 83.52 C
ATOM 663 CD2 LEU A 82 8.927 7.957 -6.174 1.00 83.52 C
ATOM 664 N GLU A 83 5.694 9.021 -2.968 1.00 87.96 N
ATOM 665 CA GLU A 83 6.011 9.477 -1.618 1.00 87.96 C
ATOM 666 C GLU A 83 5.056 8.873 -0.593 1.00 87.96 C
ATOM 667 CB GLU A 83 5.967 11.005 -1.542 1.00 87.96 C
ATOM 668 O GLU A 83 5.488 8.386 0.454 1.00 87.96 O
ATOM 669 CG GLU A 83 6.496 11.571 -0.233 1.00 87.96 C
ATOM 670 CD GLU A 83 6.453 13.090 -0.175 1.00 87.96 C
ATOM 671 OE1 GLU A 83 5.424 13.652 0.264 1.00 87.96 O
ATOM 672 OE2 GLU A 83 7.456 13.723 -0.573 1.00 87.96 O
ATOM 673 N LYS A 84 3.752 8.925 -0.931 1.00 87.17 N
ATOM 674 CA LYS A 84 2.749 8.416 0.000 1.00 87.17 C
ATOM 675 C LYS A 84 2.865 6.903 0.160 1.00 87.17 C
ATOM 676 CB LYS A 84 1.342 8.787 -0.470 1.00 87.17 C
ATOM 677 O LYS A 84 2.708 6.376 1.264 1.00 87.17 O
ATOM 678 CG LYS A 84 1.031 10.274 -0.383 1.00 87.17 C
ATOM 679 CD LYS A 84 0.932 10.739 1.064 1.00 87.17 C
ATOM 680 CE LYS A 84 0.563 12.214 1.154 1.00 87.17 C
ATOM 681 NZ LYS A 84 0.580 12.705 2.564 1.00 87.17 N
ATOM 682 N THR A 85 3.075 6.194 -0.878 1.00 86.48 N
ATOM 683 CA THR A 85 3.277 4.749 -0.859 1.00 86.48 C
ATOM 684 C THR A 85 4.478 4.381 0.007 1.00 86.48 C
ATOM 685 CB THR A 85 3.478 4.195 -2.282 1.00 86.48 C
ATOM 686 O THR A 85 4.438 3.396 0.747 1.00 86.48 O
ATOM 687 CG2 THR A 85 3.571 2.673 -2.271 1.00 86.48 C
ATOM 688 OG1 THR A 85 2.371 4.589 -3.103 1.00 86.48 O
ATOM 689 N LYS A 86 5.560 5.179 -0.120 1.00 89.61 N
ATOM 690 CA LYS A 86 6.738 4.988 0.722 1.00 89.61 C
ATOM 691 C LYS A 86 6.390 5.146 2.199 1.00 89.61 C
ATOM 692 CB LYS A 86 7.840 5.975 0.335 1.00 89.61 C
ATOM 693 O LYS A 86 6.864 4.379 3.040 1.00 89.61 O
ATOM 694 CG LYS A 86 8.596 5.594 -0.930 1.00 89.61 C
ATOM 695 CD LYS A 86 9.745 6.555 -1.205 1.00 89.61 C
ATOM 696 CE LYS A 86 10.475 6.201 -2.494 1.00 89.61 C
ATOM 697 NZ LYS A 86 11.592 7.151 -2.777 1.00 89.61 N
ATOM 698 N LEU A 87 5.725 6.133 2.442 1.00 90.63 N
ATOM 699 CA LEU A 87 5.303 6.398 3.814 1.00 90.63 C
ATOM 700 C LEU A 87 4.520 5.217 4.379 1.00 90.63 C
ATOM 701 CB LEU A 87 4.449 7.667 3.877 1.00 90.63 C
ATOM 702 O LEU A 87 4.736 4.815 5.524 1.00 90.63 O
ATOM 703 CG LEU A 87 4.127 8.198 5.274 1.00 90.63 C
ATOM 704 CD1 LEU A 87 5.400 8.666 5.971 1.00 90.63 C
ATOM 705 CD2 LEU A 87 3.108 9.330 5.195 1.00 90.63 C
ATOM 706 N VAL A 88 3.611 4.770 3.617 1.00 89.50 N
ATOM 707 CA VAL A 88 2.818 3.610 4.012 1.00 89.50 C
ATOM 708 C VAL A 88 3.741 2.436 4.330 1.00 89.50 C
ATOM 709 CB VAL A 88 1.808 3.212 2.913 1.00 89.50 C
ATOM 710 O VAL A 88 3.547 1.739 5.328 1.00 89.50 O
ATOM 711 CG1 VAL A 88 1.118 1.895 3.265 1.00 89.50 C
ATOM 712 CG2 VAL A 88 0.777 4.321 2.708 1.00 89.50 C
ATOM 713 N LEU A 89 4.633 2.181 3.505 1.00 90.95 N
ATOM 714 CA LEU A 89 5.629 1.136 3.718 1.00 90.95 C
ATOM 715 C LEU A 89 6.348 1.334 5.048 1.00 90.95 C
ATOM 716 CB LEU A 89 6.644 1.123 2.572 1.00 90.95 C
ATOM 717 O LEU A 89 6.528 0.380 5.809 1.00 90.95 O
ATOM 718 CG LEU A 89 7.716 0.033 2.629 1.00 90.95 C
ATOM 719 CD1 LEU A 89 7.069 -1.348 2.620 1.00 90.95 C
ATOM 720 CD2 LEU A 89 8.691 0.180 1.466 1.00 90.95 C
ATOM 721 N GLU A 90 6.858 2.505 5.268 1.00 91.77 N
ATOM 722 CA GLU A 90 7.540 2.813 6.521 1.00 91.77 C
ATOM 723 C GLU A 90 6.648 2.516 7.723 1.00 91.77 C
ATOM 724 CB GLU A 90 7.985 4.278 6.545 1.00 91.77 C
ATOM 725 O GLU A 90 7.113 1.982 8.731 1.00 91.77 O
ATOM 726 CG GLU A 90 9.127 4.590 5.589 1.00 91.77 C
ATOM 727 CD GLU A 90 10.319 5.241 6.271 1.00 91.77 C
ATOM 728 OE1 GLU A 90 11.320 4.540 6.544 1.00 91.77 O
ATOM 729 OE2 GLU A 90 10.252 6.462 6.535 1.00 91.77 O
ATOM 730 N ILE A 91 5.411 2.885 7.569 1.00 90.84 N
ATOM 731 CA ILE A 91 4.447 2.638 8.635 1.00 90.84 C
ATOM 732 C ILE A 91 4.287 1.135 8.849 1.00 90.84 C
ATOM 733 CB ILE A 91 3.078 3.283 8.321 1.00 90.84 C
ATOM 734 O ILE A 91 4.286 0.660 9.987 1.00 90.84 O
ATOM 735 CG1 ILE A 91 3.208 4.810 8.263 1.00 90.84 C
ATOM 736 CG2 ILE A 91 2.031 2.861 9.356 1.00 90.84 C
ATOM 737 CD1 ILE A 91 1.989 5.514 7.684 1.00 90.84 C
ATOM 738 N LEU A 92 4.083 0.438 7.825 1.00 90.22 N
ATOM 739 CA LEU A 92 3.929 -1.012 7.890 1.00 90.22 C
ATOM 740 C LEU A 92 5.163 -1.662 8.506 1.00 90.22 C
ATOM 741 CB LEU A 92 3.677 -1.587 6.494 1.00 90.22 C
ATOM 742 O LEU A 92 5.045 -2.579 9.322 1.00 90.22 O
ATOM 743 CG LEU A 92 2.300 -1.314 5.887 1.00 90.22 C
ATOM 744 CD1 LEU A 92 2.262 -1.772 4.432 1.00 90.22 C
ATOM 745 CD2 LEU A 92 1.211 -2.006 6.699 1.00 90.22 C
ATOM 746 N LYS A 93 6.355 -1.270 8.049 1.00 89.96 N
ATOM 747 CA LYS A 93 7.611 -1.791 8.582 1.00 89.96 C
ATOM 748 C LYS A 93 7.703 -1.568 10.089 1.00 89.96 C
ATOM 749 CB LYS A 93 8.803 -1.137 7.881 1.00 89.96 C
ATOM 750 O LYS A 93 8.149 -2.450 10.826 1.00 89.96 O
ATOM 751 CG LYS A 93 9.068 -1.672 6.481 1.00 89.96 C
ATOM 752 CD LYS A 93 10.333 -1.070 5.884 1.00 89.96 C
ATOM 753 CE LYS A 93 10.606 -1.615 4.488 1.00 89.96 C
ATOM 754 NZ LYS A 93 11.842 -1.022 3.894 1.00 89.96 N
ATOM 755 N GLU A 94 7.401 -0.356 10.430 1.00 91.25 N
ATOM 756 CA GLU A 94 7.387 -0.032 11.853 1.00 91.25 C
ATOM 757 C GLU A 94 6.457 -0.965 12.623 1.00 91.25 C
ATOM 758 CB GLU A 94 6.968 1.425 12.069 1.00 91.25 C
ATOM 759 O GLU A 94 6.797 -1.429 13.713 1.00 91.25 O
ATOM 760 CG GLU A 94 8.067 2.432 11.765 1.00 91.25 C
ATOM 761 CD GLU A 94 7.783 3.817 12.324 1.00 91.25 C
ATOM 762 OE1 GLU A 94 8.221 4.116 13.459 1.00 91.25 O
ATOM 763 OE2 GLU A 94 7.116 4.610 11.623 1.00 91.25 O
ATOM 764 N ARG A 95 5.355 -1.225 12.075 1.00 85.31 N
ATOM 765 CA ARG A 95 4.398 -2.127 12.709 1.00 85.31 C
ATOM 766 C ARG A 95 4.938 -3.553 12.756 1.00 85.31 C
ATOM 767 CB ARG A 95 3.059 -2.098 11.969 1.00 85.31 C
ATOM 768 O ARG A 95 4.712 -4.276 13.728 1.00 85.31 O
ATOM 769 CG ARG A 95 2.304 -0.786 12.115 1.00 85.31 C
ATOM 770 CD ARG A 95 1.648 -0.662 13.483 1.00 85.31 C
ATOM 771 NE ARG A 95 0.780 0.509 13.560 1.00 85.31 N
ATOM 772 NH1 ARG A 95 -1.032 -0.634 14.424 1.00 85.31 N
ATOM 773 NH2 ARG A 95 -1.174 1.620 14.033 1.00 85.31 N
ATOM 774 CZ ARG A 95 -0.474 0.496 14.006 1.00 85.31 C
ATOM 775 N LEU A 96 5.431 -3.928 11.737 1.00 87.53 N
ATOM 776 CA LEU A 96 6.039 -5.251 11.646 1.00 87.53 C
ATOM 777 C LEU A 96 7.142 -5.415 12.687 1.00 87.53 C
ATOM 778 CB LEU A 96 6.605 -5.486 10.244 1.00 87.53 C
ATOM 779 O LEU A 96 7.260 -6.471 13.311 1.00 87.53 O
ATOM 780 CG LEU A 96 7.106 -6.899 9.943 1.00 87.53 C
ATOM 781 CD1 LEU A 96 5.938 -7.879 9.903 1.00 87.53 C
ATOM 782 CD2 LEU A 96 7.877 -6.925 8.627 1.00 87.53 C
ATOM 783 N LYS A 97 8.035 -4.458 12.855 1.00 87.68 N
ATOM 784 CA LYS A 97 9.111 -4.470 13.842 1.00 87.68 C
ATOM 785 C LYS A 97 8.555 -4.573 15.260 1.00 87.68 C
ATOM 786 CB LYS A 97 9.976 -3.216 13.709 1.00 87.68 C
ATOM 787 O LYS A 97 9.131 -5.256 16.109 1.00 87.68 O
ATOM 788 CG LYS A 97 10.961 -3.264 12.550 1.00 87.68 C
ATOM 789 CD LYS A 97 11.875 -2.045 12.542 1.00 87.68 C
ATOM 790 CE LYS A 97 12.840 -2.076 11.365 1.00 87.68 C
ATOM 791 NZ LYS A 97 13.742 -0.886 11.356 1.00 87.68 N
ATOM 792 N GLU A 98 7.395 -3.949 15.535 1.00 85.16 N
ATOM 793 CA GLU A 98 6.766 -3.985 16.852 1.00 85.16 C
ATOM 794 C GLU A 98 6.185 -5.364 17.149 1.00 85.16 C
ATOM 795 CB GLU A 98 5.671 -2.920 16.954 1.00 85.16 C
ATOM 796 O GLU A 98 6.174 -5.804 18.300 1.00 85.16 O
ATOM 797 CG GLU A 98 6.201 -1.494 16.993 1.00 85.16 C
ATOM 798 CD GLU A 98 5.199 -0.496 17.550 1.00 85.16 C
ATOM 799 OE1 GLU A 98 5.207 -0.247 18.777 1.00 85.16 O
ATOM 800 OE2 GLU A 98 4.397 0.041 16.753 1.00 85.16 O
ATOM 801 N LYS A 99 5.862 -6.201 16.145 1.00 71.59 N
ATOM 802 CA LYS A 99 5.236 -7.516 16.249 1.00 71.59 C
ATOM 803 C LYS A 99 6.285 -8.620 16.338 1.00 71.59 C
ATOM 804 CB LYS A 99 4.311 -7.766 15.057 1.00 71.59 C
ATOM 805 O LYS A 99 6.031 -9.682 16.910 1.00 71.59 O
ATOM 806 CG LYS A 99 2.886 -7.276 15.266 1.00 71.59 C
ATOM 807 CD LYS A 99 1.932 -7.876 14.240 1.00 71.59 C
ATOM 808 CE LYS A 99 0.499 -7.420 14.474 1.00 71.59 C
ATOM 809 NZ LYS A 99 -0.480 -8.264 13.726 1.00 71.59 N
ATOM 810 N GLU A 100 7.534 -8.377 16.016 1.00 69.93 N
ATOM 811 CA GLU A 100 8.637 -9.325 16.142 1.00 69.93 C
ATOM 812 C GLU A 100 9.233 -9.296 17.546 1.00 69.93 C
ATOM 813 CB GLU A 100 9.723 -9.028 15.104 1.00 69.93 C
ATOM 814 O GLU A 100 9.551 -10.343 18.113 1.00 69.93 O
ATOM 815 CG GLU A 100 9.683 -9.948 13.892 1.00 69.93 C
ATOM 816 CD GLU A 100 10.905 -9.814 12.998 1.00 69.93 C
ATOM 817 OE1 GLU A 100 11.866 -10.601 13.160 1.00 69.93 O
ATOM 818 OE2 GLU A 100 10.903 -8.915 12.128 1.00 69.93 O
ENDMDL
END

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -24,31 +24,37 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept>
#include <cif++.hpp>
#include <iostream>
#include <fstream>
// --------------------------------------------------------------------
TEST_CASE("q-1")
TEST_CASE("fmt_1")
{
using namespace cif::literals;
std::ostringstream os;
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
cif::file a = cif::pdb::read(gTestDir / "pdb1cbs.ent.gz");
auto &pdbx_poly_seq_scheme = a.front()["pdbx_poly_seq_scheme"];
REQUIRE_FALSE(pdbx_poly_seq_scheme.empty());
SECTION("s-11")
{
CHECK(pdbx_poly_seq_scheme.count("asym_id"_key == "A") == 137);
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") == 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);
}
std::string world("world");
os << cif::format("Hello, %-10.10s, the magic number is %d and pi is %g", world, 42, cif::kPI);
REQUIRE(os.str() == "Hello, world , the magic number is 42 and pi is 3.14159");
REQUIRE(cif::format("Hello, %-10.10s, the magic number is %d and pi is %g", world, 42, cif::kPI).str() ==
"Hello, world , the magic number is 42 and pi is 3.14159");
}
// --------------------------------------------------------------------
TEST_CASE("clr_1")
{
using namespace cif::colour;
std::cout << "Hello, " << cif::coloured("world!", white, red, cif::colour::regular) << '\n'
<< "Hello, " << cif::coloured("world!", white, red, bold) << '\n'
<< "Hello, " << cif::coloured("world!", black, red) << '\n'
<< "Hello, " << cif::coloured("world!", white, green) << '\n'
<< "Hello, " << cif::coloured("world!", white, blue) << '\n'
<< "Hello, " << cif::coloured("world!", blue, white) << '\n'
<< "Hello, " << cif::coloured("world!", red, white, bold) << '\n';
}

View File

@@ -26,17 +26,19 @@
#include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept>
#include <cif++.hpp>
// --------------------------------------------------------------------
cif::file operator""_cf(const char *text, std::size_t length)
cif::file operator""_cf(const char *text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
@@ -53,8 +55,8 @@ TEST_CASE("create_nonpoly_1")
cif::VERBOSE = 1;
cif::file file;
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
file.load_dictionary("mmcif_pdbx.dic");
file.emplace("TEST"); // create a datablock
cif::mm::structure structure(file);
@@ -82,7 +84,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.load_dictionary("mmcif_pdbx.dic");
auto &hem_data = atoms["HEM"];
auto &atom_site = hem_data["atom_site"];
@@ -159,14 +161,14 @@ _struct_asym.details ?
_atom_type.symbol C
)"_cf;
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
expected.load_dictionary("mmcif_pdbx.dic");
if (not(expected.front() == structure.get_datablock()))
{
std::cerr << expected.front() << '\n'
REQUIRE(false);
std::cout << expected.front() << '\n'
<< '\n'
<< structure.get_datablock() << '\n';
REQUIRE(false);
}
}
@@ -177,8 +179,8 @@ TEST_CASE("create_nonpoly_2")
cif::VERBOSE = 1;
cif::file file;
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
file.load_dictionary("mmcif_pdbx.dic");
file.emplace("TEST"); // create a datablock
cif::mm::structure structure(file);
@@ -270,7 +272,7 @@ _struct_asym.details ?
_atom_type.symbol C
)"_cf;
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
expected.load_dictionary("mmcif_pdbx.dic");
REQUIRE(expected.front() == structure.get_datablock());
@@ -354,7 +356,7 @@ _struct_asym.details ?
#
)"_cf;
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
data.load_dictionary("mmcif_pdbx.dic");
cif::mm::structure s(data);
@@ -431,169 +433,3 @@ TEST_CASE("remove_residue_1")
REQUIRE_NOTHROW(s.validate_atoms());
}
// --------------------------------------------------------------------
// Tests for structure_open_options
TEST_CASE("options_1")
{
using namespace cif::literals;
const std::filesystem::path example(gTestDir / ".." / "examples" / "1cbs.cif.gz");
cif::file file(example.string());
auto &cf = cif::compound_factory::instance();
SECTION("skip_water")
{
cif::mm::structure s(file, 1, { .skip_water = true });
REQUIRE_NOTHROW(s.validate_atoms());
for (auto a : s.atoms())
CHECK_FALSE(a.is_water());
}
SECTION("skip_hetatom")
{
cif::mm::structure s(file, 1, { .skip_hetatom = true });
REQUIRE_NOTHROW(s.validate_atoms());
for (auto a : s.atoms())
CHECK((a.is_water() or cf.is_peptide(a.get_label_comp_id()) or cf.is_base(a.get_label_comp_id())));
}
SECTION("selected_asyms")
{
cif::mm::structure s(file, 1, { .asyms = { "A" } });
REQUIRE_NOTHROW(s.validate_atoms());
for (auto a : s.atoms())
CHECK(a.get_label_asym_id() == "A");
}
SECTION("min-b-factor")
{
cif::mm::structure s(file, 1, { .min_b_factor = 20.f });
REQUIRE_NOTHROW(s.validate_atoms());
for (auto a : s.atoms())
CHECK(a.get_property_float("B_iso_or_equiv") >= 20.f);
}
SECTION("max-b-factor")
{
cif::mm::structure s(file, 1, { .max_b_factor = 20.f });
REQUIRE_NOTHROW(s.validate_atoms());
for (auto a : s.atoms())
CHECK(a.get_property_float("B_iso_or_equiv") <= 20.f);
}
}
TEST_CASE("options_2")
{
auto data = R"(
data_TEST
#
_pdbx_nonpoly_scheme.asym_id A
_pdbx_nonpoly_scheme.ndb_seq_num 1
_pdbx_nonpoly_scheme.entity_id 1
_pdbx_nonpoly_scheme.mon_id HEM
_pdbx_nonpoly_scheme.pdb_seq_num 1
_pdbx_nonpoly_scheme.auth_seq_num 1
_pdbx_nonpoly_scheme.pdb_mon_id HEM
_pdbx_nonpoly_scheme.auth_mon_id HEM
_pdbx_nonpoly_scheme.pdb_strand_id A
_pdbx_nonpoly_scheme.pdb_ins_code .
#
loop_
_atom_site.id
_atom_site.auth_asym_id
_atom_site.label_alt_id
_atom_site.label_asym_id
_atom_site.label_atom_id
_atom_site.label_comp_id
_atom_site.label_entity_id
_atom_site.label_seq_id
_atom_site.type_symbol
_atom_site.group_PDB
_atom_site.pdbx_PDB_ins_code
_atom_site.Cartn_x
_atom_site.Cartn_y
_atom_site.Cartn_z
_atom_site.occupancy
_atom_site.B_iso_or_equiv
_atom_site.pdbx_formal_charge
_atom_site.auth_seq_id
_atom_site.auth_comp_id
_atom_site.auth_atom_id
_atom_site.pdbx_PDB_model_num
1 A A A CHA HEM 1 . C HETATM ? -5.248 39.769 -0.250 0.75 7.67 ? 1 HEM CHA 1
3 A A A CHB HEM 1 . C HETATM ? -3.774 36.790 3.280 0.75 7.05 ? 1 HEM CHB 1
2 A A A CHC HEM 1 . C HETATM ? -2.879 33.328 0.013 0.75 7.69 ? 1 HEM CHC 1
4 A A A CHD HEM 1 . C HETATM ? -4.342 36.262 -3.536 0.75 8.00 ? 1 HEM CHD 1
5 A B A CHA HEM 1 . C HETATM ? -5.248 39.769 -0.250 0.25 7.67 ? 1 HEM CHA 1
6 A B A CHB HEM 1 . C HETATM ? -3.774 36.790 3.280 0.25 7.05 ? 1 HEM CHB 1
7 A B A CHC HEM 1 . C HETATM ? -2.879 33.328 0.013 0.25 7.69 ? 1 HEM CHC 1
8 A B A CHD HEM 1 . C HETATM ? -4.342 36.262 -3.536 0.25 8.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
#
_pdbx_entity_nonpoly.entity_id 1
_pdbx_entity_nonpoly.name 'PROTOPORPHYRIN IX CONTAINING FE'
_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
#
_struct_asym.id A
_struct_asym.entity_id 1
_struct_asym.pdbx_blank_PDB_chainid_flag N
_struct_asym.pdbx_modified N
_struct_asym.details ?
#
)"_cf;
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
SECTION("max")
{
cif::mm::structure s(data, 1, {
.occupancy_mode = cif::mm::occupancy_policy::MAX
});
REQUIRE(s.atoms().size() == 4);
CHECK(s.atoms().front().get_label_alt_id() == "A");
}
SECTION("min")
{
cif::mm::structure s(data, 1, {
.occupancy_mode = cif::mm::occupancy_policy::MIN
});
REQUIRE(s.atoms().size() == 4);
CHECK(s.atoms().front().get_label_alt_id() == "B");
}
SECTION("unoccupied")
{
cif::mm::structure s(data, 1, {
.occupancy_mode = cif::mm::occupancy_policy::UNOCCUPIED
});
CHECK(s.atoms().empty());
}
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,63 +0,0 @@
/*-
* 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
* 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 "test-main.hpp"
#include <cif++.hpp>
#include <filesystem>
#include <iostream>
#include <fstream>
TEST_CASE("reconstruct")
{
cif::compound_factory::instance().push_dictionary(gTestDir / "REA.cif");
for (std::filesystem::directory_iterator i(gTestDir / "reconstruct"); i != std::filesystem::directory_iterator{}; ++i)
{
std::cout << i->path() << '\n';
if (i->path().extension() == ".pdb")
{
cif::file f = cif::pdb::read(i->path());
std::error_code ec;
if (not cif::pdb::is_valid_pdbx_file(f, ec))
CHECK(cif::pdb::reconstruct_pdbx(f));
}
else
{
cif::file f(i->path());
std::error_code ec;
CHECK_FALSE(cif::pdb::is_valid_pdbx_file(f, ec));
CHECK((bool)ec);
CHECK(cif::pdb::reconstruct_pdbx(f));
}
}
}

View File

@@ -28,6 +28,8 @@
#include <cif++.hpp>
#include <catch2/catch.hpp>
#include <iostream>
#include <fstream>
@@ -35,8 +37,8 @@ TEST_CASE("rename")
{
cif::VERBOSE = 3;
if (std::filesystem::exists(gTestDir / ".." / "rsrc" / "ccd-subset.cif"))
cif::add_file_resource("components.cif", gTestDir / ".." / "rsrc" / "ccd-subset.cif");
if (std::filesystem::exists(gTestDir / ".." / "data" / "ccd-subset.cif"))
cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
if (std::filesystem::exists(gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic"))
cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");

View File

@@ -1,33 +1,7 @@
/*-
* 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
* 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 "test-main.hpp"
#include "cif++/utilities.hpp"
#include <catch2/catch.hpp>
#include <random>
#include <thread>

View File

@@ -26,17 +26,19 @@
#include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept>
#include <cif++.hpp>
// --------------------------------------------------------------------
cif::file operator""_cf(const char* text, std::size_t length)
cif::file operator""_cf(const char* text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char* text, std::size_t length)
membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
@@ -131,7 +133,7 @@ TEST_CASE("sugar_name_1")
// auto &db = s.get_datablock();
// auto &as = db["atom_site"];
// for (std::size_t i = 0; i < 2; ++i)
// for (size_t i = 0; i < 2; ++i)
// {
// for (auto r : as.find("label_asym_id"_key == "H" and "auth_seq_id"_key == i + 1))
// /*auto &ri = */ai[i].emplace_back(r);

View File

@@ -1,9 +1,10 @@
#define CATCH_CONFIG_RUNNER 1
#include "test-main.hpp"
#include <cif++.hpp>
#define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>
std::filesystem::path gTestDir = std::filesystem::current_path();
int main(int argc, char *argv[])
@@ -11,13 +12,11 @@ int main(int argc, char *argv[])
Catch::Session session; // There must be exactly one instance
// Build a new parser on top of Catch2's
using namespace Catch::Clara;
auto cli = session.cli() // Get Catch2's command line parser
| Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string
["-D"]["--data-dir"] // the option names it will respond to
("The directory containing the data files") // description string for the help output
| Opt(cif::VERBOSE, "verbose")["-v"]["--cif-verbose"]("Flag for cif::VERBOSE");
using namespace Catch::clara;
auto cli = session.cli() // Get Catch2's command line parser
| Opt(gTestDir, "data-dir") // bind variable to a new option, with a hint string
["-D"]["--data-dir"] // the option names it will respond to
("The directory containing the data files"); // description string for the help output
// Now pass the new composite back to Catch2 so it uses that
session.cli(cli);
@@ -31,9 +30,10 @@ int main(int argc, char *argv[])
cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");
// initialize CCD location
cif::add_file_resource("components.cif", gTestDir / ".." / "rsrc" / "ccd-subset.cif");
cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
cif::compound_factory::instance().push_dictionary(gTestDir / "HEM.cif");
return session.run();
}

View File

@@ -1,33 +1,3 @@
/*-
* 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
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <catch2/catch_all.hpp>
#include <filesystem>
extern std::filesystem::path gTestDir;

View File

@@ -26,24 +26,21 @@
#include "test-main.hpp"
#include <catch2/catch.hpp>
#include <stdexcept>
#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
#endif
#include <Eigen/Eigenvalues>
// --------------------------------------------------------------------
cif::file operator""_cf(const char *text, std::size_t length)
cif::file operator""_cf(const char *text, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
@@ -95,7 +92,7 @@ TEST_CASE("t1")
const auto &&[angle, axis] = cif::quaternion_to_angle_axis(q2);
CHECK_THAT(std::fmod(360 + angle, 360), Catch::Matchers::WithinRel(std::fmod(360 - angle0, 360), 0.01));
REQUIRE_THAT(std::fmod(360 + angle, 360), Catch::Matchers::WithinRel(std::fmod(360 - angle0, 360), 0.01));
for (auto &p : p1)
p.rotate(q2);
@@ -121,7 +118,7 @@ TEST_CASE("t2")
auto &&[angle, axis] = cif::quaternion_to_angle_axis(q);
CHECK_THAT(angle, Catch::Matchers::WithinRel(45.f, 0.01f));
REQUIRE_THAT(angle, Catch::Matchers::WithinRel(45.f, 0.01f));
}
TEST_CASE("t3")
@@ -145,7 +142,7 @@ TEST_CASE("t3")
double a = cif::angle(v, p[0], p[1]);
CHECK_THAT(a, Catch::Matchers::WithinRel(45.f, 0.01f));
REQUIRE_THAT(a, Catch::Matchers::WithinRel(45.f, 0.01f));
}
TEST_CASE("dh_q_0")
@@ -153,37 +150,39 @@ TEST_CASE("dh_q_0")
cif::point axis(1, 0, 0);
cif::point p(1, 1, 0);
cif::point t[3] = {
cif::point t[3] =
{
{ 0, 1, 0 },
{ 0, 0, 0 },
{ 1, 0, 0 }
};
auto a = cif::dihedral_angle(t[0], t[1], t[2], p);
CHECK_THAT(a, Catch::Matchers::WithinRel(0.f, 0.01f));
REQUIRE_THAT(a, Catch::Matchers::WithinRel(0, 0.01f));
auto q = cif::construct_from_angle_axis(90, axis);
p.rotate(q);
REQUIRE(std::abs(p.m_x - 1.f) < 0.01f);
REQUIRE(std::abs(p.m_y - 0.f) < 0.01f);
REQUIRE(std::abs(p.m_z - 1.f) < 0.01f);
REQUIRE_THAT(p.m_x, Catch::Matchers::WithinRel(1, 0.01f));
REQUIRE_THAT(p.m_y, Catch::Matchers::WithinRel(0, 0.01f));
REQUIRE_THAT(p.m_z, Catch::Matchers::WithinRel(1, 0.01f));
a = cif::dihedral_angle(t[0], t[1], t[2], p);
REQUIRE(std::abs(a - 90.f) < 0.01f);
REQUIRE_THAT(a, Catch::Matchers::WithinRel(90, 0.01f));
q = cif::construct_from_angle_axis(-90, axis);
p.rotate(q);
REQUIRE(std::abs(p.m_x - 1.f) < 0.01f);
REQUIRE(std::abs(p.m_y - 1.f) < 0.01f);
REQUIRE(std::abs(p.m_z - 0.f) < 0.01f);
REQUIRE_THAT(p.m_x, Catch::Matchers::WithinRel(1, 0.01f));
REQUIRE_THAT(p.m_y, Catch::Matchers::WithinRel(1, 0.01f));
REQUIRE_THAT(p.m_z, Catch::Matchers::WithinRel(0, 0.01f));
a = cif::dihedral_angle(t[0], t[1], t[2], p);
REQUIRE(std::abs(a - 0.f) < 0.01f);
REQUIRE_THAT(a, Catch::Matchers::WithinRel(0, 0.01f));
}
TEST_CASE("dh_q_1")
@@ -225,119 +224,78 @@ TEST_CASE("dh_q_1")
pts[3].rotate(q, pts[2]);
auto dh = cif::dihedral_angle(pts[0], pts[1], pts[2], pts[3]);
CHECK_THAT(dh, Catch::Matchers::WithinRel(angle, 0.1f));
REQUIRE_THAT(dh, Catch::Matchers::WithinRel(angle, 0.1f));
}
}
// --------------------------------------------------------------------
// TEST_CASE("m2q_0")
// {
// for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
// {
// auto d = cif::kSymopNrTable[i].symop().data();
// cif::matrix3x3<float> rot;
// float Qxx = rot(0, 0) = d[0];
// float Qxy = rot(0, 1) = d[1];
// float Qxz = rot(0, 2) = d[2];
// float Qyx = rot(1, 0) = d[3];
// float Qyy = rot(1, 1) = d[4];
// float Qyz = rot(1, 2) = d[5];
// float Qzx = rot(2, 0) = d[6];
// float Qzy = rot(2, 1) = d[7];
// float Qzz = rot(2, 2) = d[8];
// Eigen::Matrix4f em;
// em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
// Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
// Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
// Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
// Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
// auto ev = es.eigenvalues();
// std::size_t bestJ = 0;
// float bestEV = -1;
// for (std::size_t j = 0; j < 4; ++j)
// {
// if (bestEV < ev[j].real())
// {
// bestEV = ev[j].real();
// bestJ = j;
// }
// }
// if (std::abs(bestEV - 1) > 0.01)
// continue; // not a rotation matrix
// auto col = es.eigenvectors().col(bestJ);
// auto q = normalize(cif::quaternion{
// static_cast<float>(col(3).real()),
// static_cast<float>(col(0).real()),
// static_cast<float>(col(1).real()),
// static_cast<float>(col(2).real()) });
// cif::point p1{ 1, 1, 1 };
// cif::point p2 = p1;
// p2.rotate(q);
// cif::point p3 = rot * p1;
// CHECK_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
// CHECK_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
// CHECK_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
// }
// }
TEST_CASE("m2q_0a")
TEST_CASE("m2q_0, *utf::tolerance(0.001f)")
{
for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
for (size_t i = 0; i < cif::kSymopNrTableSize; ++i)
{
auto d = cif::kSymopNrTable[i].symop().data();
Eigen::Matrix3f rot;
rot << static_cast<float>(d[0]), static_cast<float>(d[1]), static_cast<float>(d[2]), static_cast<float>(d[3]), static_cast<float>(d[4]), static_cast<float>(d[5]), static_cast<float>(d[6]), static_cast<float>(d[7]), static_cast<float>(d[8]);
cif::matrix3x3<float> rot;
float Qxx = rot(0, 0) = d[0];
float Qxy = rot(0, 1) = d[1];
float Qxz = rot(0, 2) = d[2];
float Qyx = rot(1, 0) = d[3];
float Qyy = rot(1, 1) = d[4];
float Qyz = rot(1, 2) = d[5];
float Qzx = rot(2, 0) = d[6];
float Qzy = rot(2, 1) = d[7];
float Qzz = rot(2, 2) = d[8];
// check to see if this matrix contains a true rotation
if (rot * rot.transpose() != Eigen::Matrix3f::Identity() or rot.determinant() != 1)
continue;
Eigen::Matrix4f em;
Eigen::Quaternionf qe(rot);
em << Qxx - Qyy - Qzz, Qyx + Qxy, Qzx + Qxz, Qzy - Qyz,
Qyx + Qxy, Qyy - Qxx - Qzz, Qzy + Qyz, Qxz - Qzx,
Qzx + Qxz, Qzy + Qyz, Qzz - Qxx - Qyy, Qyx - Qxy,
Qzy - Qyz, Qxz - Qzx, Qyx - Qxy, Qxx + Qyy + Qzz;
auto q = normalize(cif::quaternion{ qe.w(), qe.x(), qe.y(), qe.z() });
Eigen::EigenSolver<Eigen::Matrix4f> es(em / 3);
auto ev = es.eigenvalues();
size_t bestJ = 0;
float bestEV = -1;
for (size_t j = 0; j < 4; ++j)
{
if (bestEV < ev[j].real())
{
bestEV = ev[j].real();
bestJ = j;
}
}
if (std::abs(bestEV - 1) > 0.01)
continue; // not a rotation matrix
auto col = es.eigenvectors().col(bestJ);
auto q = normalize(cif::quaternion{
static_cast<float>(col(3).real()),
static_cast<float>(col(0).real()),
static_cast<float>(col(1).real()),
static_cast<float>(col(2).real()) });
cif::point p1{ 1, 1, 1 };
cif::point p2 = p1;
p2.rotate(q);
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]),
static_cast<float>(d[4]),
static_cast<float>(d[5]),
static_cast<float>(d[6]),
static_cast<float>(d[7]),
static_cast<float>(d[8])
});
cif::point p3 = rot * p1;
cif::point p3 = rot_c * p1;
CHECK_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
CHECK_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
CHECK_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
REQUIRE_THAT(p2.m_x, Catch::Matchers::WithinRel(p3.m_x, 0.01f));
REQUIRE_THAT(p2.m_y, Catch::Matchers::WithinRel(p3.m_y, 0.01f));
REQUIRE_THAT(p2.m_z, Catch::Matchers::WithinRel(p3.m_z, 0.01f));
}
}
// "TEST_CASE(m2q_1")
// "TEST_CASE(m2q_1, *utf::tolerance(0.001f)")
// {
// for (std::size_t i = 0; i < cif::kSymopNrTableSize; ++i)
// for (size_t i = 0; i < cif::kSymopNrTableSize; ++i)
// {
// auto d = cif::kSymopNrTable[i].symop().data();
@@ -361,10 +319,10 @@ TEST_CASE("m2q_0a")
// auto &&[ev, em] = cif::eigen(m * (1/3.0f), false);
// std::size_t bestJ = 0;
// size_t bestJ = 0;
// float bestEV = -1;
// for (std::size_t j = 0; j < 4; ++j)
// for (size_t j = 0; j < 4; ++j)
// {
// if (bestEV < ev[j])
// {
@@ -381,7 +339,7 @@ TEST_CASE("m2q_0a")
// static_cast<float>(em(bestJ, 0)),
// static_cast<float>(em(bestJ, 1)),
// static_cast<float>(em(bestJ, 2)) });
// cif::point p1{ 1, 1, 1 };
// cif::point p2 = p1;
// p2.rotate(q);
@@ -404,15 +362,15 @@ TEST_CASE("symm_1")
cif::point f = fractional(p, c);
CHECK_THAT(f.m_x, Catch::Matchers::WithinRel(0.1f, 0.01f));
CHECK_THAT(f.m_y, Catch::Matchers::WithinRel(0.1f, 0.01f));
CHECK_THAT(f.m_z, Catch::Matchers::WithinRel(0.1f, 0.01f));
REQUIRE_THAT(f.m_x, Catch::Matchers::WithinRel(0.1f, 0.01f));
REQUIRE_THAT(f.m_y, Catch::Matchers::WithinRel(0.1f, 0.01f));
REQUIRE_THAT(f.m_z, Catch::Matchers::WithinRel(0.1f, 0.01f));
cif::point o = orthogonal(f, c);
CHECK_THAT(o.m_x, Catch::Matchers::WithinRel(1.f, 0.01f));
CHECK_THAT(o.m_y, Catch::Matchers::WithinRel(1.f, 0.01f));
CHECK_THAT(o.m_z, Catch::Matchers::WithinRel(1.f, 0.01f));
REQUIRE_THAT(o.m_x, Catch::Matchers::WithinRel(1.f, 0.01f));
REQUIRE_THAT(o.m_y, Catch::Matchers::WithinRel(1.f, 0.01f));
REQUIRE_THAT(o.m_z, Catch::Matchers::WithinRel(1.f, 0.01f));
}
TEST_CASE("symm_2")
@@ -434,33 +392,33 @@ TEST_CASE("symm_3")
REQUIRE(sg.get_name() == "P 21 21 2");
}
TEST_CASE("symm_4")
TEST_CASE("symm_4, *utf::tolerance(0.1f)")
{
using namespace cif::literals;
// based on 2b8h
auto sg = cif::spacegroup(154); // p 32 2 1
auto c = cif::cell(107.516f, 107.516f, 338.487f, 90.00f, 90.00f, 120.00f);
auto c = cif::cell(107.516, 107.516, 338.487, 90.00, 90.00, 120.00);
cif::point a{ -8.688, 79.351, 10.439 }; // O6 NAG A 500
cif::point b{ -35.356, 33.693, -3.236 }; // CG2 THR D 400
cif::point sb( -6.916, 79.34, 3.236); // 4_565 copy of b
cif::point a{ -8.688f, 79.351f, 10.439f }; // O6 NAG A 500
cif::point b{ -35.356f, 33.693f, -3.236f }; // CG2 THR D 400
cif::point sb(-6.916f, 79.34f, 3.236f); // 4_565 copy of b
CHECK_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f));
CHECK_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f));
CHECK_THAT(distance(a, sg(a, c, "1_554"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_c()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_455"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_a()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_545"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_b()), 0.01f));
REQUIRE_THAT(distance(a, sg(a, c, "1_554"_symop)), Catch::Matchers::WithinRel(static_cast<float>(c.get_c()), 0.01f));
auto sb2 = sg(b, c, "4_565"_symop);
CHECK_THAT(sb.m_x, Catch::Matchers::WithinRel(sb2.m_x, 0.01f));
CHECK_THAT(sb.m_y, Catch::Matchers::WithinRel(sb2.m_y, 0.01f));
CHECK_THAT(sb.m_z, Catch::Matchers::WithinRel(sb2.m_z, 0.01f));
REQUIRE_THAT(sb.m_x, Catch::Matchers::WithinRel(sb2.m_x, 0.01f));
REQUIRE_THAT(sb.m_y, Catch::Matchers::WithinRel(sb2.m_y, 0.01f));
REQUIRE_THAT(sb.m_z, Catch::Matchers::WithinRel(sb2.m_z, 0.01f));
CHECK_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f));
REQUIRE_THAT(distance(a, sb2), Catch::Matchers::WithinRel(7.42f, 0.01f));
}
// --------------------------------------------------------------------
TEST_CASE("symm_4wvp_1")
TEST_CASE("symm_4wvp_1, *utf::tolerance(0.1f)")
{
using namespace cif::literals;
@@ -471,24 +429,25 @@ TEST_CASE("symm_4wvp_1")
cif::crystal c(db);
cif::point p{ -78.722f, 98.528f, 11.994f };
cif::point p{ -78.722, 98.528, 11.994 };
auto a = s.get_residue("A", 10, "").get_atom_by_atom_id("O");
auto sp1 = c.symmetry_copy(a.get_location(), "2_565"_symop);
CHECK_THAT(sp1.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
CHECK_THAT(sp1.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
CHECK_THAT(sp1.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
REQUIRE_THAT(sp1.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
REQUIRE_THAT(sp1.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
REQUIRE_THAT(sp1.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
const auto &[d, sp2, so] = c.closest_symmetry_copy(p, a.get_location());
REQUIRE(d < 1);
CHECK_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
CHECK_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
CHECK_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
REQUIRE_THAT(sp2.m_x, Catch::Matchers::WithinAbs(p.m_x, 0.5f));
REQUIRE_THAT(sp2.m_y, Catch::Matchers::WithinAbs(p.m_y, 0.5f));
REQUIRE_THAT(sp2.m_z, Catch::Matchers::WithinAbs(p.m_z, 0.5f));
}
TEST_CASE("symm_2bi3_1")
TEST_CASE("symm_2bi3_1, *utf::tolerance(0.1f)")
{
cif::file f(gTestDir / "2bi3.cif.gz");
@@ -498,15 +457,18 @@ TEST_CASE("symm_2bi3_1")
cif::crystal c(db);
auto struct_conn = db["struct_conn"];
for (const auto &[asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<std::string, int, std::string, std::string, std::string,
std::string, int, std::string, std::string, std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"))
for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<
std::string,int,std::string,std::string,std::string,
std::string,int,std::string,std::string,std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"
))
{
auto &r1 = s.get_residue(asym1, seqid1, authseqid1);
auto &r2 = s.get_residue(asym2, seqid2, authseqid2);
@@ -517,22 +479,22 @@ TEST_CASE("symm_2bi3_1")
auto sa1 = c.symmetry_copy(a1.get_location(), cif::sym_op(symm1));
auto sa2 = c.symmetry_copy(a2.get_location(), cif::sym_op(symm2));
CHECK_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
auto pa1 = a1.get_location();
const auto &[d, p, so] = c.closest_symmetry_copy(pa1, a2.get_location());
CHECK_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
CHECK_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
CHECK_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
REQUIRE_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
REQUIRE_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
REQUIRE_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
CHECK_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE(so.string() == symm2);
}
}
TEST_CASE("symm_2bi3_1a")
TEST_CASE("symm_2bi3_1a, *utf::tolerance(0.1f)")
{
using namespace cif::literals;
@@ -544,40 +506,43 @@ TEST_CASE("symm_2bi3_1a")
auto struct_conn = db["struct_conn"];
auto atom_site = db["atom_site"];
for (const auto &[asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<std::string, std::optional<int>, std::string, std::string, std::string,
std::string, std::optional<int>, std::string, std::string, std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"))
for (const auto &[
asym1, seqid1, authseqid1, atomid1, symm1,
asym2, seqid2, authseqid2, atomid2, symm2,
dist] : struct_conn.find<
std::string,std::optional<int>,std::string,std::string,std::string,
std::string,std::optional<int>,std::string,std::string,std::string,
float>(
cif::key("ptnr1_symmetry") != "1_555" or cif::key("ptnr2_symmetry") != "1_555",
"ptnr1_label_asym_id", "ptnr1_label_seq_id", "ptnr1_auth_seq_id", "ptnr1_label_atom_id", "ptnr1_symmetry",
"ptnr2_label_asym_id", "ptnr2_label_seq_id", "ptnr2_auth_seq_id", "ptnr2_label_atom_id", "ptnr2_symmetry",
"pdbx_dist_value"
))
{
cif::point p1 = atom_site.find1<float, float, float>(
cif::point p1 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym1 and "label_seq_id"_key == seqid1 and "auth_seq_id"_key == authseqid1 and "label_atom_id"_key == atomid1,
"cartn_x", "cartn_y", "cartn_z");
cif::point p2 = atom_site.find1<float, float, float>(
cif::point p2 = atom_site.find1<float,float,float>(
"label_asym_id"_key == asym2 and "label_seq_id"_key == seqid2 and "auth_seq_id"_key == authseqid2 and "label_atom_id"_key == atomid2,
"cartn_x", "cartn_y", "cartn_z");
auto sa1 = c.symmetry_copy(p1, cif::sym_op(symm1));
auto sa2 = c.symmetry_copy(p2, cif::sym_op(symm2));
CHECK_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(cif::distance(sa1, sa2), Catch::Matchers::WithinAbs(dist, 0.5f));
const auto &[d, p, so] = c.closest_symmetry_copy(p1, p2);
CHECK_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
CHECK_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
CHECK_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
REQUIRE_THAT(p.m_x, Catch::Matchers::WithinAbs(sa2.m_x, 0.5f));
REQUIRE_THAT(p.m_y, Catch::Matchers::WithinAbs(sa2.m_y, 0.5f));
REQUIRE_THAT(p.m_z, Catch::Matchers::WithinAbs(sa2.m_z, 0.5f));
CHECK_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(dist, 0.5f));
REQUIRE(so.string() == symm2);
}
}
TEST_CASE("symm_3bwh_1")
TEST_CASE("symm_3bwh_1, *utf::tolerance(0.1f)")
{
cif::file f(gTestDir / "3bwh.cif.gz");
@@ -592,15 +557,15 @@ TEST_CASE("symm_3bwh_1")
{
if (a1 == a2)
continue;
const auto&[ d, p, so ] = c.closest_symmetry_copy(a1.get_location(), a2.get_location());
const auto &[d, p, so] = c.closest_symmetry_copy(a1.get_location(), a2.get_location());
CHECK_THAT(d, Catch::Matchers::WithinAbs(distance(a1.get_location(), p), 0.5f));
REQUIRE_THAT(d, Catch::Matchers::WithinAbs(distance(a1.get_location(), p), 0.5f));
}
}
}
TEST_CASE("volume_3bwh_1")
TEST_CASE("volume_3bwh_1, *utf::tolerance(0.1f)")
{
cif::file f(gTestDir / "1juh.cif.gz");
@@ -608,5 +573,6 @@ TEST_CASE("volume_3bwh_1")
cif::crystal c(db);
CHECK_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
REQUIRE_THAT(c.get_cell().get_volume(), Catch::Matchers::WithinRel(741009.625f, 0.01f));
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,298 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test-main.hpp"
#include <cif++.hpp>
#include <stdexcept>
// --------------------------------------------------------------------
cif::file operator""_cf(const char *text, std::size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(text), length);
std::istream is(&buffer);
return cif::file(is);
}
// --------------------------------------------------------------------
TEST_CASE("test-1")
{
auto f = R"(data_1CBS
#
_entry.id 1CBS
#
_entity.id 1
_entity.type polymer
#
_entity_poly.entity_id 1
_entity_poly.type 'polypeptide(L)'
_entity_poly.nstd_linkage no
_entity_poly.nstd_monomer no
_entity_poly.pdbx_seq_one_letter_code
;PNFSG
;
_entity_poly.pdbx_seq_one_letter_code_can
;PNFSG
;
_entity_poly.pdbx_strand_id A
_entity_poly.pdbx_target_identifier ?
#
loop_
_entity_poly_seq.entity_id
_entity_poly_seq.num
_entity_poly_seq.mon_id
_entity_poly_seq.hetero
1 1 PRO n
1 2 ASN n
1 3 PHE n
1 4 SER n
1 5 GLY n
#
loop_
_struct_asym.id
_struct_asym.pdbx_blank_PDB_chainid_flag
_struct_asym.pdbx_modified
_struct_asym.entity_id
_struct_asym.details
A N N 1 ?
#
loop_
_atom_type.symbol
C
N
O
S
#
loop_
_atom_site.group_PDB
_atom_site.id
_atom_site.type_symbol
_atom_site.label_atom_id
_atom_site.label_alt_id
_atom_site.label_comp_id
_atom_site.label_asym_id
_atom_site.label_entity_id
_atom_site.label_seq_id
_atom_site.pdbx_PDB_ins_code
_atom_site.Cartn_x
_atom_site.Cartn_y
_atom_site.Cartn_z
_atom_site.occupancy
_atom_site.B_iso_or_equiv
_atom_site.pdbx_formal_charge
_atom_site.auth_seq_id
_atom_site.auth_comp_id
_atom_site.auth_asym_id
_atom_site.auth_atom_id
_atom_site.pdbx_PDB_model_num
ATOM 2 C CA . PRO A 1 1 ? 18.150 13.525 43.680 1.00 28.82 ? 1 PRO A CA 1
ATOM 9 C CA . ASN A 1 2 ? 20.576 16.457 43.578 1.00 20.79 ? 2 ASN A CA 1
ATOM 17 C CA . PHE A 1 3 ? 21.144 17.838 40.087 1.00 12.62 ? 3 PHE A CA 1
ATOM 28 C CA . SER A 1 4 ? 23.170 20.780 41.464 1.00 11.30 ? 4 SER A CA 1
ATOM 34 C CA . GLY A 1 5 ? 26.628 21.486 40.103 1.00 10.86 ? 5 GLY A CA 1
#
loop_
_pdbx_poly_seq_scheme.asym_id
_pdbx_poly_seq_scheme.entity_id
_pdbx_poly_seq_scheme.seq_id
_pdbx_poly_seq_scheme.mon_id
_pdbx_poly_seq_scheme.ndb_seq_num
_pdbx_poly_seq_scheme.pdb_seq_num
_pdbx_poly_seq_scheme.auth_seq_num
_pdbx_poly_seq_scheme.pdb_mon_id
_pdbx_poly_seq_scheme.auth_mon_id
_pdbx_poly_seq_scheme.pdb_strand_id
_pdbx_poly_seq_scheme.pdb_ins_code
_pdbx_poly_seq_scheme.hetero
A 1 1 PRO 1 1 1 PRO PRO A . n
A 1 2 ASN 2 2 2 ASN ASN A . n
A 1 3 PHE 3 3 3 PHE PHE A . n
A 1 4 SER 4 4 4 SER SER A . n
A 1 5 GLY 5 5 5 GLY GLY A . n
#
)"_cf;
SECTION("Plain file")
{
REQUIRE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Delete one atom_site")
{
auto &db = f.front();
auto n = db["atom_site"].erase(cif::key("id") == 2);
REQUIRE(n == 1);
REQUIRE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Delete a pdbx_poly_seq_scheme record")
{
auto &db = f.front();
auto n = db["pdbx_poly_seq_scheme"].erase(cif::key("seq_id") == 2);
REQUIRE(n == 1);
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Delete an entity_poly_seq record")
{
auto &db = f.front();
auto n = db["entity_poly_seq"].erase(cif::key("num") == 2);
REQUIRE(n == 1);
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Delete an entity_poly record")
{
auto &db = f.front();
auto n = db["entity_poly"].erase(cif::key("entity_id") == 1);
REQUIRE(n == 1);
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Mutate an atom_site record")
{
auto &db = f.front();
auto r = db["atom_site"].find1(cif::key("id") == 9);
r.assign({
{ "label_comp_id", "ALA" },
{ "auth_comp_id", "ALA" }
});
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Hetero consistency")
{
auto &db = f.front();
db["entity_poly_seq"].emplace({ //
{ "entity_id", 1 },
{ "num", 1 },
{ "mon_id", "ALA" },
{ "hetero", "n" }
});
db["pdbx_poly_seq_scheme"].emplace({ //
{ "asym_id", "A" },
{ "entity_id", "1" },
{ "seq_id", "1" },
{ "mon_id", "ALA" },
{ "ndb_seq_num", "1" },
{ "pdb_seq_num", "1" },
{ "auth_seq_num", "1" },
{ "pdb_mon_id", "ALA" },
{ "auth_mon_id", "ALA" },
{ "pdb_strand_id", "A" },
{ "pdb_ins_code", "." },
{ "hetero", "n" }
});
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Missing hetero for record in atom_site")
{
auto &db = f.front();
auto r1 = db["atom_site"].front();
cif::row_initializer cr(r1);
cr.set_value("id", "3");
cr.set_value("label_comp_id", "ALA");
db["atom_site"].emplace(std::move(cr));
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Missing letter in entity_poly.pdbx_seq_one_letter_code")
{
auto &db = f.front();
auto &entity_poly = db["entity_poly"];
entity_poly.front().assign({
{ "pdbx_seq_one_letter_code", "PNSG" }
});
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Too many letters in entity_poly.pdbx_seq_one_letter_code")
{
auto &db = f.front();
auto &entity_poly = db["entity_poly"];
entity_poly.front().assign({
{ "pdbx_seq_one_letter_code", "PNFSGX" }
});
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
SECTION("Mismatch in entity_poly.pdbx_seq_one_letter_code")
{
auto &db = f.front();
auto &entity_poly = db["entity_poly"];
entity_poly.front().assign({
{ "pdbx_seq_one_letter_code", "PNASG" }
});
REQUIRE_FALSE(cif::pdb::is_valid_pdbx_file(f));
}
}
TEST_CASE("extended-dictionary-1")
{
cif::add_file_resource("dssp-extension.dic", gTestDir / "dssp-extension.dic");
cif::VERBOSE = 2;
auto f = cif::pdb::read(gTestDir / "1cbs-dssp.cif");
CHECK(f.is_valid());
}
TEST_CASE("brak")
{
auto f = cif::pdb::read(gTestDir / "brak.pdb");
CHECK(f.is_valid());
}

View File

@@ -5,8 +5,6 @@ IF NOT EXIST build_ci\libs (
MKDIR build_ci\libs
)
CD build_ci\libs
@REM Install ZLib
IF NOT EXIST zlib-%ZLIB_VERSION%.zip (
ECHO Downloading https://github.com/libarchive/zlib/archive/v%ZLIB_VERSION%.zip
curl -L -o zlib-%ZLIB_VERSION%.zip https://github.com/libarchive/zlib/archive/v%ZLIB_VERSION%.zip || EXIT /b 1
@@ -16,9 +14,9 @@ IF NOT EXIST zlib-%ZLIB_VERSION% (
C:\windows\system32\tar.exe -x -f zlib-%ZLIB_VERSION%.zip || EXIT /b 1
)
CD zlib-%ZLIB_VERSION%
cmake -B build || EXIT /b 1
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
cmake -G "Visual Studio 17 2022" . || EXIT /b 1
cmake --build . --target ALL_BUILD --config Release || EXIT /b 1
cmake --build . --target RUN_TESTS --config Release || EXIT /b 1
cmake --build . --target INSTALL --config Release || EXIT /b 1
@EXIT /b 0

View File

@@ -10,7 +10,7 @@ euid=${EUID:-$(id -u)}
if [ "${euid}" -ne 0 ]; then
echo "Please run as root"
exit 1
exit
fi
if [ -f "@CIFPP_ETC_DIR@/libcifpp.conf" ]; then
@@ -19,13 +19,12 @@ fi
# check to see if we're supposed to run at all
if [ "$update" != "true" ]; then
exit 0
exit
fi
# if cache directory doesn't exist, exit.
if ! [ -d "@CIFPP_CACHE_DIR@" ]; then
echo "Cache directory '@CIFPP_CACHE_DIR@' does not exist"
exit 1
exit
fi
# Create a temp file in the right directory and
@@ -61,16 +60,12 @@ update_dictionary() {
# Update the dictionaries
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz"
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://ftp.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://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic"
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic"
# notify subscribers, using find instead of run-parts to make it work on FreeBSD as well
# notify subscribers, will fail on FreeBSD
if [ -d "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" ]; then
find "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" \
-exec test -x {} \; -and -not -exec test -d {} \; \
-exec {} "@CIFPP_CACHE_DIR@" \;
if [ -d "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" ] && [ -x /bin/run-parts ]; then
run-parts --arg "@CIFPP_CACHE_DIR@" -- "@CIFPP_ETC_DIR@/libcifpp/cache-update.d"
fi
exit 0