Compare commits

...

439 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
560f6debc6 updating dictionaries and default to downloading components.cif 2022-10-26 15:46:50 +02:00
Maarten L. Hekkelman
ea1ac33de8 Update README.md
No more boost requirement
2022-10-26 15:30:56 +02:00
Maarten L. Hekkelman
7ea30237ae Merge branch 'trunk' into new-develop 2022-10-26 15:23:35 +02:00
Maarten L. Hekkelman
bc668487e2 Fix installation of dictionary files 2022-10-26 14:29:40 +02:00
Maarten L. Hekkelman
1769f9864b Fixed TLS parser, and more 2022-10-26 13:23:23 +02:00
Maarten L. Hekkelman
75ffd97802 use gxrio internally for reading pdb files 2022-10-25 12:41:14 +02:00
Maarten L. Hekkelman
cfd5b7da0f Reintroduce more atom members 2022-10-25 11:51:35 +02:00
Maarten L. Hekkelman
26b7d1df26 Write chem_comp for polymer residues in pdb2cif 2022-10-21 16:01:12 +02:00
Maarten L. Hekkelman
0747929cd6 Allow missing CRYST1 record in PDB files... finally 2022-10-21 15:42:19 +02:00
Maarten L. Hekkelman
5bcfb102f4 change_residue fix 2022-10-20 19:48:26 +02:00
Maarten L. Hekkelman
908fb1ccea gxrio dependency 2022-10-20 13:36:18 +02:00
Maarten L. Hekkelman
af8389baa4 fix dependencies 2022-10-11 15:36:53 +02:00
Maarten L. Hekkelman
24ca1017cd Fix mm::polymer to have its own auth_asym_id 2022-10-11 12:17:42 +02:00
Maarten L. Hekkelman
85c21aeb01 row as vector 2022-10-11 09:05:03 +02:00
Maarten L. Hekkelman
2f249048d9 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2022-10-10 09:03:34 +02:00
Maarten L. Hekkelman
974cb40ab3 for gcc 9.4 2022-10-10 09:01:40 +02:00
Maarten L. Hekkelman
c01c16ea60 more accessors in cif::mm::atom 2022-10-05 17:04:39 +02:00
Maarten L. Hekkelman
bd157c249c Reintroduced PDB conversion code 2022-10-05 14:18:08 +02:00
Maarten L. Hekkelman
b0ac33c1b1 optimisation in query
fix memory problem in cif::item, making it safer
2022-10-05 13:21:57 +02:00
Maarten L. Hekkelman
82e73a9525 All tests pass 2022-10-05 10:13:53 +02:00
Maarten L. Hekkelman
adc316d671 Use index in lookup, when available
start branch code in model::structure (not done yet)
2022-10-04 13:57:38 +02:00
Maarten L. Hekkelman
6a0b6b99ac using index in category 2022-10-03 16:22:32 +02:00
Maarten L. Hekkelman
08dd9dd5b4 missing methods 2022-10-03 10:01:42 +02:00
Maarten L. Hekkelman
557a1c2d00 Reactivated several structure methods 2022-10-03 09:32:20 +02:00
Maarten L. Hekkelman
f77bbfedda updates 2022-10-03 08:41:50 +02:00
Maarten L. Hekkelman
3aa3fe19e2 Better validate diagnostic output 2022-09-30 10:25:55 +02:00
Maarten L. Hekkelman
35fcc0493e Moving cif::Structure back in as model 2022-09-28 17:11:04 +02:00
Maarten L. Hekkelman
9485bec2fa Moved symmetry back into libcifpp 2022-09-27 16:09:24 +02:00
Maarten L. Hekkelman
4b759e731c Moved code back into libcifpp, 3d stuff mainly 2022-09-27 14:34:31 +02:00
Maarten L. Hekkelman
7dd6a8a1aa fixes and updated submodules 2022-08-24 16:20:16 +02:00
Maarten L. Hekkelman
96725ae8b9 Clean up makefile 2022-08-19 13:55:35 +02:00
Maarten L. Hekkelman
b3a0ded9a8 add git submodule 2022-08-19 13:38:59 +02:00
Maarten L. Hekkelman
184c491803 changed find1 a bit more
reverted to returning empty results in case nothing is found
2022-08-18 17:20:41 +02:00
Maarten L. Hekkelman
f944b3ce00 changed find1 logic for only row_handles, now returns empty row_handle instead of throwing when not found
various condition fixes
2022-08-18 11:16:28 +02:00
Maarten L. Hekkelman
2557f41863 some documentation and cleanup of cif::item 2022-08-17 20:40:31 +02:00
Maarten L. Hekkelman
2b92cee3f7 some documentation and cleanup of cif::item 2022-08-17 20:14:48 +02:00
Maarten L. Hekkelman
8071768579 better row_handle::get 2022-08-17 19:39:14 +02:00
Maarten L. Hekkelman
71c8541b68 validate links
fix get_parents_condition
2022-08-17 17:08:38 +02:00
Maarten L. Hekkelman
3d66c77188 writing order 2022-08-17 16:02:57 +02:00
Maarten L. Hekkelman
8701512961 Remove cif++/Cif++.hpp
Implemented reorder by index
2022-08-17 15:29:14 +02:00
Maarten L. Hekkelman
b317c780ba Fixes for pre c++20
file constructor from raw data
2022-08-17 15:02:56 +02:00
Maarten L. Hekkelman
681aa3bf8b clean up 2022-08-17 11:24:29 +02:00
Maarten L. Hekkelman
a68e053471 Remove tests 2022-08-17 11:17:09 +02:00
Maarten L. Hekkelman
25a90e3b32 split out pdbx code
fix dangling memory reference
2022-08-17 11:14:06 +02:00
Maarten L. Hekkelman
2f62759dfe Before split-out of libpdbxpp 2022-08-17 08:50:56 +02:00
Maarten L. Hekkelman
cf9ec46ab8 Removed DSSP code, moved to dssp project 2022-08-16 16:57:25 +02:00
Maarten L. Hekkelman
ecbef51b10 - fix category::clear
- fix dssp TCO value
2022-08-16 16:34:15 +02:00
Maarten L. Hekkelman
dfff8c9587 condition work (children, parents) 2022-08-16 16:18:11 +02:00
Maarten L. Hekkelman
cc5d52bbf9 boost::regex usage 2022-08-16 16:17:56 +02:00
Maarten L. Hekkelman
a9e9f86c93 Renaming internal use of mmcif_pdbx dictionary. It was wrong to use the name mmcif_pdbx_v50 2022-08-16 16:17:30 +02:00
Maarten L. Hekkelman
a2c52713b2 Refactored dssp to be standalone 2022-08-16 11:50:43 +02:00
Maarten L. Hekkelman
545aca88d8 More info 2022-08-15 17:17:56 +02:00
Maarten L. Hekkelman
ac27248784 Started moving DSSP code 2022-08-15 15:37:07 +02:00
Maarten L. Hekkelman
5758bfbaea Required changes for FreeBSD 2022-08-15 10:11:11 +02:00
Maarten L. Hekkelman
8d3a079774 Fix regex for item_validator 2022-08-15 10:09:18 +02:00
Maarten L. Hekkelman
718c138510 Fix constructor for item 2022-08-15 09:49:23 +02:00
Maarten L. Hekkelman
29aac70e67 Changed boost::regex dependency to git submodule, in case it is needed 2022-08-15 08:59:47 +02:00
Maarten L. Hekkelman
700575adfe Merged 2022-08-15 07:42:51 +02:00
Maarten L. Hekkelman
9fe6e5df85 remove dependency on boost::program_options 2022-08-14 20:08:36 +02:00
Maarten L. Hekkelman
ce7434a463 Make boost::regex optional 2022-08-14 19:31:31 +02:00
Maarten L. Hekkelman
ad7d876d07 new dependency 2022-08-13 21:55:19 +02:00
Maarten L. Hekkelman
0dc19e86fa fixed example 2022-08-11 20:53:54 +02:00
Maarten L. Hekkelman
a12acaa5c7 moving from namespace cif::v2 to cif 2022-08-11 20:39:34 +02:00
Maarten L. Hekkelman
ff62efe720 More tests 2022-08-11 16:49:59 +02:00
Maarten L. Hekkelman
2407877184 Reordering all files 2022-08-11 15:44:59 +02:00
Maarten L. Hekkelman
5fde050738 debugging 2022-08-10 17:09:53 +02:00
Maarten L. Hekkelman
a855f88073 Getting rid of boost/algorithm/string 2022-08-10 16:46:48 +02:00
Maarten L. Hekkelman
cfa2acd61d backup 2022-08-08 08:10:22 +02:00
Maarten L. Hekkelman
d9db2fe2e7 insert 2022-08-07 20:48:59 +02:00
Maarten L. Hekkelman
15d62cd3b6 Fix mapping between pdbx_poly_seq_scheme and atom_site while reading an mmcif::Structure 2022-08-07 12:51:16 +02:00
Maarten L. Hekkelman
19a89aeb7e - start row_initializer 2022-08-07 11:49:09 +02:00
Maarten L. Hekkelman
677c61c32f moving insert_impl, index work 2022-08-06 16:08:34 +02:00
Maarten L. Hekkelman
4dd4f66397 backup 2022-08-04 16:29:55 +02:00
Maarten L. Hekkelman
04b7828abc validator work 2022-08-04 13:57:12 +02:00
Maarten L. Hekkelman
9c621ecab8 more condition work 2022-08-03 16:40:48 +02:00
Maarten L. Hekkelman
ab9c4d9416 compiling again 2022-08-03 16:09:27 +02:00
Maarten L. Hekkelman
e5eb62255a started with validator, running into the ground 2022-08-03 12:44:58 +02:00
Maarten L. Hekkelman
98ff79432b backup 2022-08-02 16:56:55 +02:00
Maarten L. Hekkelman
24fa80ba2a parser just started working again, a bit 2022-08-02 16:42:52 +02:00
Maarten L. Hekkelman
3999d792ef const iterator construction from non-const iterator 2022-08-02 11:59:29 +02:00
Maarten L. Hekkelman
4db3732749 move construction and operators = 2022-08-02 11:42:11 +02:00
Maarten L. Hekkelman
07131e8b40 copy constructor for category 2022-08-02 11:24:14 +02:00
Maarten L. Hekkelman
39b91e74c9 - new item storage
- formatting of numbers using to_chars
2022-08-02 10:35:14 +02:00
Maarten L. Hekkelman
d4bb7ec3bc Fixed includes based on feedback from Patricia Herrera. 2022-08-01 19:00:42 +02:00
Maarten L. Hekkelman
6175b7e359 backup 2022-08-01 16:58:52 +02:00
Maarten L. Hekkelman
10442d506a structured binding, start 2022-08-01 15:08:42 +02:00
Maarten L. Hekkelman
573a695c3d small steps 2022-08-01 12:57:00 +02:00
Maarten L. Hekkelman
a76bef0d01 backup 2022-08-01 08:33:49 +02:00
Maarten L. Hekkelman
e20111b566 small optimization 2022-07-31 16:19:24 +02:00
Maarten L. Hekkelman
4a1d9c8f75 New storage layout for item_value 2022-07-31 15:53:18 +02:00
Maarten L. Hekkelman
26c86282e3 before refactoring item_value based on statistics 2022-07-27 16:46:33 +02:00
Maarten L. Hekkelman
0eaeb1650d split out item 2022-07-27 12:41:25 +02:00
Maarten L. Hekkelman
f4a6533f6b eerste fröbelwerk 2022-07-26 17:00:14 +02:00
Maarten L. Hekkelman
df1b6a13e1 updated git ignore 2022-07-25 16:21:55 +02:00
Maarten L. Hekkelman
e8f24f617c - Fixed copy/paste error in Category interface
- message in progress finished
2022-07-25 10:40:03 +02:00
Maarten L. Hekkelman
9454fdc217 Fix for parsing empty quoted strings. 2022-07-21 13:03:23 +02:00
Maarten L. Hekkelman
22543d8fe5 Update changelog 2022-07-12 16:55:55 +02:00
Maarten L. Hekkelman
60d1dc82e6 Improved remark 3 parser for large molecules 2022-07-12 16:54:38 +02:00
Maarten L. Hekkelman
87486f87ef revert loading compressed dictionaries 2022-07-12 14:20:01 +02:00
Maarten L. Hekkelman
80e7da0f13 Locating dictionaries updated 2022-07-12 14:13:59 +02:00
Maarten L. Hekkelman
3745beae66 Fix search order for resources 2022-07-12 13:55:19 +02:00
Maarten L. Hekkelman
3965840bfa New way of locating resources 2022-07-12 13:49:02 +02:00
Maarten L. Hekkelman
a88c6f3d32 Fix for older clang (on MacOS?). 2022-07-05 11:07:12 +02:00
Maarten L. Hekkelman
ed6c6f0026 Move assignment of Structure is not possible due to reference to datablock 2022-07-05 09:46:59 +02:00
Maarten L. Hekkelman
bdda9d72b5 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2022-07-05 09:36:36 +02:00
Maarten L. Hekkelman
fd080e778e Changes for MacOS/MSVC 2022-07-05 09:36:26 +02:00
Maarten L. Hekkelman
9f72df2ecd Update LICENSE
Format changed, now recognized
2022-07-01 08:21:22 +02:00
ojcharles
617db012f0 Update Cif++.cpp (#15)
add mutex library for std::unique_lock, not included in std lib
2022-07-01 08:07:24 +02:00
Maarten L. Hekkelman
9d15541237 include cstring for gnu c++ 12 2022-06-22 08:53:05 +02:00
Maarten L. Hekkelman
35c99564c6 Fix importing sugars from PDB files 2022-06-01 15:17:54 +02:00
Maarten L. Hekkelman
1d8fe334d6 Fix writing sugar branches 2022-06-01 13:22:34 +02:00
Maarten L. Hekkelman
d86bb314ac Better handling of missing residues/mismatch seqres 2022-06-01 11:27:41 +02:00
Maarten L. Hekkelman
0ef8eb59f8 Fix scattering factors error 2022-05-18 13:04:42 +02:00
Maarten L. Hekkelman
b5fe4a9a87 locating resources that might be protected 2022-05-18 11:53:13 +02:00
Maarten L. Hekkelman
11fea31b98 more loading resources 2022-05-18 11:37:26 +02:00
Maarten L. Hekkelman
f629275ed5 locating resources that might be protected 2022-05-18 11:25:47 +02:00
Maarten L. Hekkelman
a5f6166469 locating resources that might be protected 2022-05-18 11:14:14 +02:00
Maarten L. Hekkelman
501050e591 Add move constructor to mmcif::Structure 2022-05-10 17:11:04 +02:00
Maarten L. Hekkelman
e1b240b2b2 sugar work 2022-05-04 16:48:28 +02:00
Maarten L. Hekkelman
3d79278ed7 Merge branch 'trunk' into develop 2022-05-04 09:51:15 +02:00
Maarten L. Hekkelman
5e0b197a43 mmcif::Atom::compound() revision 2022-05-04 09:50:24 +02:00
Maarten L. Hekkelman
9c4170d9e2 - Added more const members
- change PDB writing interface
2022-05-03 11:52:15 +02:00
Maarten L. Hekkelman
af721eb196 Make having no compound less fatal 2022-05-02 14:40:22 +02:00
Maarten L. Hekkelman
788e315f5e Fix entity_branch_link entry 2022-05-02 12:24:35 +02:00
Maarten L. Hekkelman
4a82a8d5a8 Fixed all tests 2022-05-02 11:09:36 +02:00
Maarten L. Hekkelman
11019a26f8 Merge branch 'sugar-tests' into develop 2022-05-02 10:03:44 +02:00
Maarten L. Hekkelman
6f8909dce9 Fixed tests 2022-05-02 10:01:10 +02:00
Maarten L. Hekkelman
5525103aaf backup 2022-05-02 09:26:59 +02:00
Maarten L. Hekkelman
291ef737b1 - Fix removing atoms
- Optimize isUnquotedString
2022-05-01 14:09:06 +02:00
Maarten L. Hekkelman
af125bdd57 backup 2022-04-26 16:04:13 +02:00
Maarten L. Hekkelman
79089bbb8c removed incorrect assert 2022-04-20 16:32:47 +02:00
Maarten L. Hekkelman
1f08498d00 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2022-04-20 11:18:50 +02:00
Maarten L. Hekkelman
49ba714a03 - structure id stuff
- added cif::null
- more tests
2022-04-20 11:17:11 +02:00
Maarten L. Hekkelman
85fd9296b2 Add test for loading 2022-04-19 17:03:09 +02:00
Maarten L. Hekkelman
1cda14867f More interface changes in mmcif::Structure 2022-04-19 13:40:38 +02:00
Maarten L. Hekkelman
2d2b26f7dc Fix regression in bondmap calculation 2022-04-19 09:10:54 +02:00
Maarten L. Hekkelman
93b33af44a oops, wrong field name 2022-04-13 10:57:50 +02:00
Maarten L. Hekkelman
eb80490bcd getPolymerByAsymID 2022-04-13 09:47:18 +02:00
Maarten L. Hekkelman
ba2b06f5af reduce complexity 2022-04-13 09:39:43 +02:00
Maarten L. Hekkelman
fecc762db1 - better link validation
- better output (quote reserved strings)
2022-04-12 17:00:47 +02:00
Maarten L. Hekkelman
1e406253ab loading unknown atoms 2022-04-12 12:41:25 +02:00
Maarten L. Hekkelman
6e3b85f43d getResidue, again 2022-04-11 16:36:40 +02:00
Maarten L. Hekkelman
58f1b626e2 change getResidue 2022-04-06 12:49:03 +02:00
Maarten L. Hekkelman
c104a08e16 fixed Atom::charge to pick more sensible default 2022-03-30 11:14:11 +02:00
Maarten L. Hekkelman
dd0f6ca1e6 accept more invalid characters, sigh 2022-03-29 11:45:33 +02:00
Maarten L. Hekkelman
f02ea91b51 label and auth seq id, some improvements 2022-03-28 09:50:37 +02:00
Maarten L. Hekkelman
6768a501a3 access to atoms 2022-03-21 09:58:16 +01:00
Maarten L. Hekkelman
879e15c759 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2022-03-14 16:28:55 +01:00
Maarten L. Hekkelman
89285b4abc construct quaternion from angle/axis 2022-03-14 16:28:41 +01:00
Maarten L. Hekkelman
c584714f91 ion radii 2022-03-09 15:40:32 +01:00
Maarten L. Hekkelman
f5016403b7 refactored mmcif::File 2022-03-02 15:26:29 +01:00
Maarten L. Hekkelman
c8f66ae6bb start remove residue 2022-02-23 08:24:26 +01:00
Maarten L. Hekkelman
858c967e71 Locate mmcif dictionary in CCP4 space 2022-02-15 08:08:01 +01:00
Maarten L. Hekkelman
f9ca5de5bf Add missing include for gcc 8.2 2022-02-09 16:04:24 +01:00
Maarten L. Hekkelman
252c3476a1 Slightly better handling of hetero residues 2022-02-09 14:53:05 +01:00
Maarten L. Hekkelman
19210df6db Fix parsing mmCIF files with an unquoted string ?? 2022-02-08 11:22:10 +01:00
Maarten L. Hekkelman
15c5730749 Remove redundant FindFilesystem include 2022-02-03 10:35:34 +01:00
Maarten L. Hekkelman
3764adb7ef update changelog 2022-02-02 13:44:32 +01:00
Maarten L. Hekkelman
9160adb1cf Merge branch 'develop' into trunk 2022-02-02 13:40:47 +01:00
Maarten L. Hekkelman
3ebf4338ab Do not crash on uninitialized Atoms 2022-02-02 12:41:32 +01:00
Maarten L. Hekkelman
2eb4b7b39b Fix building in Windows 2022-01-25 15:27:15 +01:00
Maarten L. Hekkelman
c241e49b48 fix makefile 2022-01-25 15:13:15 +01:00
Maarten L. Hekkelman
238c881132 Update dependencies, version string 2022-01-25 13:27:58 +01:00
Maarten L. Hekkelman
49dc733536 Create non poly from described atoms 2022-01-25 13:27:19 +01:00
Maarten L. Hekkelman
755bd78f60 Fix declaration for mmcif::Nudge 2022-01-19 13:21:29 +01:00
Maarten L. Hekkelman
77f80cd51f Fix atomic test (apparently, libatomic is only needed for std::atomic<long long>) 2022-01-19 08:25:25 +01:00
Maarten L. Hekkelman
3df6000635 cleaning up code 2022-01-18 16:06:28 +01:00
Maarten L. Hekkelman
5efee2b40d comment adjusted 2022-01-18 13:28:42 +01:00
Maarten L. Hekkelman
f3c2e59184 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2022-01-18 11:26:23 +01:00
Maarten L. Hekkelman
24ab660e6e Change logic for testing std::filesystem and libatomic 2022-01-18 11:24:31 +01:00
Maarten L. Hekkelman
6c0a418068 Revert "Check atomic"
This reverts commit 07a180991e.
2022-01-18 11:12:56 +01:00
Maarten L. Hekkelman
07a180991e Check atomic 2022-01-17 11:40:46 +01:00
Maarten L. Hekkelman
4732004b67 Merge branch 'develop' into trunk 2022-01-12 16:41:18 +01:00
Maarten L. Hekkelman
faa9cd0431 Added another rotate/translate method to mmcif::Structure 2022-01-12 14:06:32 +01:00
Maarten L. Hekkelman
e0c3c2394d Fix Structure::createNonPoly to add atoms... 2022-01-11 11:21:56 +01:00
Maarten L. Hekkelman
2dec584f54 clean up code 2022-01-05 15:54:23 +01:00
Maarten L. Hekkelman
5ab2ccae40 avoid calling cif::Category::size() too often 2022-01-05 15:45:27 +01:00
Maarten L. Hekkelman
1017d08626 skip updating links when changing atom location 2022-01-05 15:24:22 +01:00
Maarten L. Hekkelman
32b1bbd943 combine translate and rotate in a single call 2022-01-05 14:27:16 +01:00
Maarten L. Hekkelman
1abf31ffa5 no-validate option in cif::Row::assign 2022-01-05 14:04:28 +01:00
Maarten L. Hekkelman
aec60829d2 more quiet code 2022-01-05 11:29:10 +01:00
Maarten L. Hekkelman
888c3c38c2 Add a 'quiet' mode (cif::VERBOSE < 0) 2022-01-05 10:36:39 +01:00
Maarten L. Hekkelman
e2c4648037 clean up 2022-01-05 10:24:37 +01:00
Maarten L. Hekkelman
f7b98c0530 refactored AtomImpl 2022-01-05 10:23:15 +01:00
Maarten L. Hekkelman
d4bd3faa16 Merge branch 'profiling-structure' into trunk 2022-01-04 10:29:23 +01:00
Maarten L. Hekkelman
c4f3b1cd7b delay loading atoms in residues 2022-01-04 09:48:41 +01:00
Maarten L. Hekkelman
74add69a83 Finish removing bzip2 support 2022-01-03 15:51:01 +01:00
Maarten L. Hekkelman
a490b19d24 version bump 2022-01-03 15:45:48 +01:00
Maarten L. Hekkelman
44cfa2c1a2 further optimisation 2022-01-03 15:19:50 +01:00
Maarten L. Hekkelman
6dd9522b3f optimized mmcif::Atom 2022-01-03 14:32:42 +01:00
Maarten L. Hekkelman
5e352cb8e4 Removed erronous dependency in config.cmake.in 2021-12-20 13:33:06 +01:00
Maarten L. Hekkelman
2fad7315b8 make DSSP::iterator bidirectional 2021-12-15 15:12:23 +01:00
Maarten L. Hekkelman
520759dfe8 update changelog 2021-12-14 15:17:50 +01:00
Maarten L. Hekkelman
577b44ae11 Fix in processing CCP4 monomers, proline is a peptide 2021-12-14 15:16:23 +01:00
Maarten L. Hekkelman
66f742d6c0 code to facilitate DSSP 2021-12-14 15:14:45 +01:00
Maarten L. Hekkelman
7ba9f688c7 Merge branch 'develop' into trunk 2021-12-10 10:39:34 +01:00
Maarten L. Hekkelman
883f0307a2 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-12-10 10:38:43 +01:00
Maarten L. Hekkelman
c9719f873f Merge branch 'develop' into trunk 2021-12-10 10:37:04 +01:00
Maarten L. Hekkelman
123d25f853 formatting of floating points in cif files
better verbose info for differences
2021-12-10 10:35:02 +01:00
Maarten L. Hekkelman
56da42db84 formatting of floating points in cif files
better verbose info for differences
2021-12-10 10:32:21 +01:00
Maarten L. Hekkelman
7f820449ca formatting 2021-12-08 09:06:09 +01:00
Maarten L. Hekkelman
ecb2cf5f11 Fix for compiling with gcc 11.2 2021-12-08 09:03:21 +01:00
Maarten L. Hekkelman
7f27da9b3b Fixed rename-compound-test to work when not using resources 2021-11-25 16:27:45 +01:00
Maarten L. Hekkelman
01eb499c69 attempt to fix running tests in different directory 2021-11-25 16:27:42 +01:00
Maarten L. Hekkelman
1ff6f70682 changelog update 2021-11-25 16:25:53 +01:00
Maarten L. Hekkelman
ddde996e10 strip newlines from compound names read from CCD 2021-11-25 16:24:55 +01:00
Maarten L. Hekkelman
1c9212c7e0 Fixed rename-compound-test to work when not using resources 2021-11-25 16:09:15 +01:00
Maarten L. Hekkelman
a568143991 unneeded loading of resource removed from test 2021-11-24 13:52:23 +01:00
Maarten L. Hekkelman
2b6f1bd9ee attempt to fix running tests in different directory 2021-11-23 10:51:22 +01:00
Maarten L. Hekkelman
2527aa5ea6 correct version in cmakefile, fix structure-test to no longer load resource 2021-11-24 13:57:36 +01:00
Maarten L. Hekkelman
4c28091ecd Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-11-18 09:56:11 +01:00
Maarten L. Hekkelman
d49725423e re-added group to Compound. Seems to be important 2021-11-18 09:56:01 +01:00
Maarten L. Hekkelman
fcb4dc61b5 fixed writing PDB files (= remove to_upper for all header lines...) 2021-11-17 08:01:24 +01:00
Maarten L. Hekkelman
b7330c074f Fixed Structure::changeResidue to actually change the residue itself as well. 2021-11-16 08:38:14 +01:00
Maarten L. Hekkelman
e8f4123030 strip newlines from names in Compound 2021-11-15 12:37:45 +01:00
Maarten L. Hekkelman
975057c4c4 Fixed bug in structure::changeresidue when removing atoms 2021-11-15 11:32:50 +01:00
Maarten L. Hekkelman
a0e01668d1 take largest in value for best quaternion 2021-11-15 10:22:56 +01:00
Maarten L. Hekkelman
2c77491416 cleaner implementation of matrices 2021-11-12 10:36:38 +01:00
Maarten L. Hekkelman
be19e4a9cb Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-11-12 09:20:01 +01:00
Maarten L. Hekkelman
61ce91a9d7 using expression templates for matrices 2021-11-12 09:17:21 +01:00
Maarten L. Hekkelman
18f1d07e85 clean up code 2021-11-12 08:28:10 +01:00
Maarten L. Hekkelman
b596976194 correct implementation of alignpoints 2021-11-12 08:15:33 +01:00
Maarten L. Hekkelman
1f6b86d516 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-11-11 21:41:05 +01:00
Maarten L. Hekkelman
31499b977d Fix the 3d alignment code 2021-11-11 21:39:52 +01:00
Maarten L. Hekkelman
f83850e380 git and revisions 2021-11-11 09:42:22 +01:00
Maarten L. Hekkelman
1a4ccd86fe changelog update 2021-11-03 09:28:14 +01:00
Maarten L. Hekkelman
5c3c6fec09 strip newlines from compound names read from CCD 2021-10-29 10:48:09 +02:00
Maarten L. Hekkelman
f97e742daa removed a too strict test in loading structures 2021-10-21 08:42:42 +02:00
Maarten L. Hekkelman
7f39d401e2 Optimised assigning data 2021-10-20 14:55:30 +02:00
Maarten L. Hekkelman
af412c284d clean up 2021-10-20 12:27:27 +02:00
Maarten L. Hekkelman
874cd3bae5 Fix symmetry lookup 2021-10-20 11:36:31 +02:00
Maarten L. Hekkelman
ea28ebdd13 optimized caching of items in mmcif::Atom 2021-10-19 16:10:59 +02:00
Maarten L. Hekkelman
3ba468933f Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-10-18 17:17:20 +02:00
Maarten L. Hekkelman
45f33e4bea Merge branch 'trunk' into develop 2021-10-18 11:12:56 +02:00
Maarten L. Hekkelman
021487ed16 Fix reading mmCIF file where model is defined but model 1 is missing. Version bump. 2021-10-18 11:11:03 +02:00
Maarten L. Hekkelman
cb3443ffb1 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-10-18 09:42:30 +02:00
Maarten L. Hekkelman
6b2c9dc3e3 order of compound info, load CCD first 2021-10-18 09:40:33 +02:00
Maarten L. Hekkelman
7513cc1947 Merge branch 'trunk' into develop 2021-10-14 09:30:19 +02:00
Maarten L. Hekkelman
c98b8ae5c9 fix pkgconfig file
fix example for new interface
2021-10-13 14:25:19 +02:00
Maarten L. Hekkelman
ab2dd4b75f Merge branch 'trunk' into develop 2021-10-13 14:03:29 +02:00
Maarten L. Hekkelman
be77316545 Rename options to make them all start with CIFPP_
Add cmake options for CACHE_DIR and DATA_DIR
2021-10-13 14:01:42 +02:00
Maarten L. Hekkelman
cdfb0d9497 remove unused files 2021-10-13 12:30:44 +02:00
Maarten L. Hekkelman
71f7e7c741 Version bump, changelog updated 2021-10-13 11:51:12 +02:00
Maarten L. Hekkelman
cff099596e Fix installation rules for CCD data 2021-10-13 11:32:10 +02:00
Maarten L. Hekkelman
e182604455 Fix all tests to work with embedded data, no CCD, part 4 2021-10-13 11:23:17 +02:00
Maarten L. Hekkelman
45a7defb7e Fix all tests to work with embedded data, no CCD, part 3 2021-10-13 10:55:25 +02:00
Maarten L. Hekkelman
906f6ac1ea Fix all tests to work with embedded data, no CCD 2021-10-13 10:31:57 +02:00
Maarten L. Hekkelman
8d96e513bd rename update script 2021-10-13 09:55:02 +02:00
Maarten L. Hekkelman
cdefd063e2 Added small ccd subset for testing in absense of full CCD 2021-10-13 09:34:58 +02:00
Maarten L. Hekkelman
8bbcba76cf Performance increase by using std::string_view
Updated Structure::changeResidue
2021-10-12 16:04:27 +02:00
Maarten L. Hekkelman
c767e89a5d Fixed symmetry operator table generator 2021-10-06 12:57:18 +02:00
Maarten L. Hekkelman
b78a603dca run tests before installation 2021-10-05 11:49:54 +02:00
Maarten L. Hekkelman
18088457d3 last updates in develop 2021-09-29 14:21:41 +02:00
Maarten L. Hekkelman
056697d901 imported libname for stdc++fs 2021-09-29 13:24:16 +02:00
Maarten L. Hekkelman
2681cfad50 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-29 13:21:45 +02:00
Maarten L. Hekkelman
8aaa7925a3 fix lib name 2021-09-29 13:21:37 +02:00
Maarten L. Hekkelman
d4f73e471b No need to search zlib/bzip2 in Windows 2021-09-29 11:08:05 +02:00
Maarten L. Hekkelman
750be0c4a4 for 18.04 2021-09-28 15:26:15 +02:00
Maarten L. Hekkelman
0f4a2a26fc dependencies
check compiler version
2021-09-28 14:15:17 +02:00
Maarten L. Hekkelman
6adb56341d remove boost::date_time dependency 2021-09-28 12:48:15 +02:00
Maarten L. Hekkelman
4524357cd3 zlib, again (use the zlib from boost in Windows) 2021-09-28 11:20:02 +02:00
Maarten L. Hekkelman
0b05b6f6e3 run tests in build dir 2021-09-28 10:40:40 +02:00
Maarten L. Hekkelman
6382170157 zlib 2021-09-28 09:39:15 +02:00
Maarten L. Hekkelman
c2eeb69dcc optional static libs boost 2021-09-28 09:38:35 +02:00
Maarten L. Hekkelman
f32261fc59 revert zlib in boost 2021-09-28 09:37:42 +02:00
Maarten L. Hekkelman
90c967c8c6 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-28 09:30:56 +02:00
Maarten L. Hekkelman
4ac90128db zlib in boost 2021-09-28 09:30:43 +02:00
Maarten L. Hekkelman
3d71db1bb7 less stringent test for compiler version 2021-09-28 09:30:22 +02:00
Maarten L. Hekkelman
5e35ea5168 warnings and compiler detection 2021-09-28 09:17:28 +02:00
Maarten L. Hekkelman
2fc88d52eb Updating cmake file to install correct update mechanism 2021-09-28 08:50:28 +02:00
Maarten L. Hekkelman
567b0f3b57 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-28 07:57:05 +02:00
Maarten L. Hekkelman
8f29386998 fix file read test 2021-09-26 14:52:34 +02:00
Maarten L. Hekkelman
d2427d57d9 unit test for reading files 2021-09-27 11:36:24 +02:00
Maarten L. Hekkelman
14a9499962 fix cmake files, pkg-config file 2021-09-27 09:36:33 +02:00
Maarten L. Hekkelman
0fafb80d44 Remove bzip2 support 2021-09-27 08:40:05 +02:00
Maarten L. Hekkelman
b5454f0943 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-26 13:41:21 +02:00
Maarten L. Hekkelman
b698260d73 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into develop 2021-09-26 13:40:39 +02:00
Maarten L. Hekkelman
ccdd1b74a0 required 2021-09-26 13:39:49 +02:00
Maarten L. Hekkelman
298fe20a1b improve cmake files? 2021-09-25 12:05:42 +02:00
Maarten L. Hekkelman
12a7c45452 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-24 16:02:22 +02:00
Maarten L. Hekkelman
02a28c2fd6 clean up makefile 2021-09-24 16:01:48 +02:00
Maarten L. Hekkelman
68e182b0bd Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-24 15:31:31 +02:00
Maarten L. Hekkelman
b0ec88f469 link compression libs 2021-09-24 15:30:34 +02:00
Maarten L. Hekkelman
4feee6ac22 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-24 15:27:48 +02:00
Maarten L. Hekkelman
ee8a85ec2f static boost libs 2021-09-24 15:27:13 +02:00
Maarten L. Hekkelman
0245d4a881 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-24 15:24:42 +02:00
Maarten L. Hekkelman
e8f0058956 add link of stdc++fs 2021-09-24 15:24:30 +02:00
Maarten L. Hekkelman
9c75dbaae0 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-24 15:22:15 +02:00
Maarten L. Hekkelman
5bd39b598e Add filesystem check, with libstdc++fs test 2021-09-24 15:21:12 +02:00
Maarten L. Hekkelman
25a43abffd update version number, fix revision file 2021-09-24 14:06:47 +02:00
Maarten L. Hekkelman
9a7ca022e2 check for existence of run-parts to be more compatible 2021-09-21 08:56:22 +02:00
Maarten L. Hekkelman
46fe0d7caf fixed cmake file
reduced verbosity for missing atoms
update script, more cross platform
2021-09-21 08:52:48 +02:00
Maarten L. Hekkelman
0371cec415 Merge branch 'develop' of github.com:PDB-REDO/libcifpp into develop 2021-09-21 08:40:25 +02:00
Maarten L. Hekkelman
2b1020cbb9 Merge branch 'trunk' into develop 2021-09-21 08:16:57 +02:00
Andrius Merkys
8c3ce2a87d Update OpenStructure compounds.chemlib if OpenStructure is found (#8)
* Update OpenStructure compounds.chemlib if OpenStructure is found.

* Replacing the code to update OpenStructure compounds.chemlib with a call to its internal script.

* Implementing more flexible approach allowing notifying all dependent software.
2021-09-20 12:39:15 +02:00
Maarten L. Hekkelman
d633622e27 Creating new nonpoly compounds 2021-09-20 11:34:07 +02:00
Maarten L. Hekkelman
19b652f615 Merge commit '37f7dd0' into develop 2021-09-15 16:06:43 +02:00
Maarten L. Hekkelman
37f7dd0631 load compound if needed 2021-09-15 16:04:49 +02:00
Maarten L. Hekkelman
4c99710fb3 Refactored iterators (Category, Row)
const versions of find
2021-09-15 11:38:12 +02:00
Maarten L. Hekkelman
59865cdb44 Rotate and Translate of structure data
update unit-test for create_nonpoly
2021-09-14 16:09:08 +02:00
Maarten L. Hekkelman
c3434507da new find1 2021-09-14 11:25:59 +02:00
Maarten L. Hekkelman
79ecf20b85 Sometimes extracting archives does not work in cmake 2021-09-10 09:24:06 +02:00
Maarten L. Hekkelman
1de9681bb7 revert mrc test
extended nonpoly unit-test
2021-09-10 09:22:37 +02:00
Maarten L. Hekkelman
345c4778e6 start structure-test unit test
added compare functions for Datablock and Category
2021-09-08 16:58:50 +02:00
Maarten L. Hekkelman
0ccb2f88ca Added more 3d code (alignpoints) 2021-09-08 11:44:27 +02:00
Maarten L. Hekkelman
f7bef8b0e9 update dictionary
do not throw from Category::isValid()
2021-09-07 14:50:06 +02:00
Maarten L. Hekkelman
9da8608f8f Merge branch 'cmake' into trunk
install update script (optionally, unix only)
2021-09-07 10:12:46 +02:00
Maarten L. Hekkelman
496cb0b909 last minute changes for building inside CCP4 2021-09-07 10:10:38 +02:00
Maarten L. Hekkelman
583cafa91e Merge branch 'cmake' of github.com:PDB-REDO/libcifpp into cmake 2021-09-07 09:23:02 +02:00
Maarten L. Hekkelman
01da665243 test nieuwe CCP4 regels 2021-09-07 09:15:47 +02:00
Maarten L. Hekkelman
e900cd1e3d Windows 2021-09-06 14:58:47 +02:00
Maarten L. Hekkelman
a8a838b33e extract using gzip for cmake < 3.19 (?) 2021-09-02 08:43:12 +02:00
Maarten L. Hekkelman
072be25335 install update script 2021-09-01 16:53:05 +02:00
Maarten L. Hekkelman
5f50429cdb last minute changes to cmake files 2021-08-31 15:53:29 +02:00
Maarten L. Hekkelman
db3fb04172 Merged cmake changes
Updated version to 1.1.1
2021-08-31 15:50:36 +02:00
Maarten L. Hekkelman
839385c3fb update readme 2021-08-31 15:45:01 +02:00
Maarten L. Hekkelman
d508b7b2df updated changelog 2021-08-31 15:43:43 +02:00
Maarten L. Hekkelman
b1456b87c0 Updated readme 2021-08-31 14:27:51 +02:00
Maarten L. Hekkelman
58d2dcaef2 fix weak linking in Linux (when resources are not available) 2021-08-31 13:56:04 +02:00
Maarten L. Hekkelman
a9468d1cbb Fix test in cmake file 2021-08-31 12:49:52 +02:00
Maarten L. Hekkelman
a29e3e8da3 create symop table 2021-08-30 12:06:36 +02:00
Maarten L. Hekkelman
dd5df1bb2a debug code: print atom id 2021-08-25 13:08:15 +02:00
Maarten L. Hekkelman
d5d6f3a7b3 Merge branch 'newer-cmake-files' of github.com:PDB-REDO/libcifpp into newer-cmake-files 2021-08-24 12:50:26 +02:00
Maarten L. Hekkelman
9d2f2b3026 pkgconfig support 2021-08-24 12:50:20 +02:00
Maarten L. Hekkelman
74717a3047 Do not bail out in bondmap creation if compound is unknown 2021-08-24 10:52:18 +02:00
Maarten L. Hekkelman
c13ee92f1e on windows 2021-08-23 17:20:45 +02:00
Maarten L. Hekkelman
0ca04bed4f Updated cmake file, configurable data dir 2021-08-23 17:00:49 +02:00
Maarten L. Hekkelman
89850de660 export symbols in windows 2021-08-18 21:21:14 +02:00
Maarten L. Hekkelman
8bb4ebd897 update cmake rules 2021-08-18 16:31:44 +02:00
Maarten L. Hekkelman
6a67208d24 date in revision file 2021-08-18 15:19:41 +02:00
Maarten L. Hekkelman
139f32c3e8 correct dependency for tests 2021-08-18 14:29:40 +02:00
Maarten L. Hekkelman
81c0d01944 Cleaning up all warnings in MSVC 2021-08-18 14:12:56 +02:00
Maarten L. Hekkelman
1d2f997efb export stuff 2021-08-18 09:53:46 +02:00
Maarten L. Hekkelman
f126b1dac3 include boost libs 2021-08-17 14:35:51 +02:00
Maarten L. Hekkelman
bbc38262ce setting up tests in cmake 2021-08-17 14:11:19 +02:00
Maarten L. Hekkelman
caeafd3189 clean up 2021-08-17 13:43:01 +02:00
Maarten L. Hekkelman
98e3c47cdf remove configure 2021-08-17 13:38:32 +02:00
Maarten L. Hekkelman
390b230cac generate version string with cmake 2021-08-17 13:30:38 +02:00
Maarten L. Hekkelman
c5c3950c91 revision, first attempt 2021-08-17 11:33:22 +02:00
Maarten L. Hekkelman
2aea0b3b1f resources in Windows 2021-08-13 11:38:48 +02:00
Maarten L. Hekkelman
6b16e02b34 clean up warning on Windows, start using resources on Windows 2021-08-12 12:01:48 +02:00
Maarten L. Hekkelman
bb2f81318a Newer CMakeLists.txt file, now creating more correct config files. 2021-08-10 16:21:02 +02:00
Maarten L. Hekkelman
ec91d0fb22 fix test isWater in Residue when mCompound is null 2021-08-10 11:23:56 +02:00
Maarten L. Hekkelman
760e23693e remove suprious error messages,
fix getUniqueID to use correct key field
2021-08-09 15:11:18 +02:00
Maarten L. Hekkelman
cfba00ae46 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2021-08-09 10:01:20 +02:00
Maarten L. Hekkelman
dc1cfb60ff Fix reading AlphaFold files (missing auth_seq_id error) 2021-08-09 10:00:50 +02:00
Maarten L. Hekkelman
92148304ee Safer CompoundFactoryImpl 2021-07-12 11:25:39 +02:00
Maarten L. Hekkelman
9329c0c0f6 optimisation of creating compound 2021-07-12 10:51:52 +02:00
Maarten L. Hekkelman
de3ac001fc added import statement for Windows DLL 2021-07-07 13:06:43 +02:00
Maarten L. Hekkelman
b1047154a4 missing include, export symbols on Windows 2021-06-29 10:39:47 +02:00
Maarten L. Hekkelman
c361af3792 z and bz2 finding, component.cif download 2021-05-26 14:59:59 +02:00
Maarten L. Hekkelman
73487fc24b updated for windows 2021-05-25 15:51:37 +02:00
Maarten L. Hekkelman
73513fd700 cmake file, now working on windows 2021-05-25 15:25:39 +02:00
Maarten L. Hekkelman
6b7723eb2a cmake file can now build unit-test 2021-05-25 09:42:21 +02:00
Maarten L. Hekkelman
7056c15366 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2021-05-21 09:25:34 +02:00
Maarten L. Hekkelman
8539e050ae add code to load default components.cif file 2021-05-21 09:25:20 +02:00
Maarten L. Hekkelman
5d4c2641f6 fix cmake file, now creates unit-test 2021-05-19 15:37:07 +02:00
Maarten L. Hekkelman
bc32071576 fix cmake file 2021-05-18 15:36:46 +02:00
Maarten L. Hekkelman
3b2c57314f initial cmake file 2021-05-18 13:54:03 +02:00
Maarten L. Hekkelman
36351a0608 fix for libc++, not using in_avail anymore 2021-05-17 10:38:06 +02:00
Maarten L. Hekkelman
047b454c1d disable download ccd in configure, for travis 2021-04-28 16:49:14 +02:00
Maarten L. Hekkelman
3f5e620102 loading compound info, finished with error checking and verbose mode 2021-04-28 16:18:08 +02:00
Maarten L. Hekkelman
851a43ba4b Alternative layering of compound factory object implementations 2021-04-26 16:52:18 +02:00
Maarten L. Hekkelman
47ae50f704 only load compound when really needed 2021-04-26 13:16:58 +02:00
Maarten L. Hekkelman
33bdd6ae82 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2021-04-21 16:03:07 +02:00
Maarten L. Hekkelman
433021727e count molecules 2021-04-21 16:02:22 +02:00
Maarten L. Hekkelman
7f23c84287 clean-up 2021-04-21 15:36:46 +02:00
Maarten L. Hekkelman
74bd2585e7 renaming, nonpoly_scheme 2021-04-21 14:41:06 +02:00
Maarten L. Hekkelman
ebb27638ff renaming compound code... 2021-04-21 14:20:36 +02:00
Maarten L. Hekkelman
3eb7e4c5bf Potential fix 2021-04-21 11:38:36 +02:00
Maarten L. Hekkelman
112f859b19 ms windows work 2021-04-21 08:32:10 +02:00
Maarten L. Hekkelman
fd08678ff6 backup 2021-04-20 17:18:15 +02:00
Maarten L. Hekkelman
2e2fc11fe1 renaming, first steps 2021-04-20 13:33:56 +02:00
Maarten L. Hekkelman
d44ed57cf9 load correct dictionary 2021-04-20 13:30:08 +02:00
Maarten L. Hekkelman
aa31acb056 no atomic? 2021-04-15 09:26:45 +02:00
Maarten L. Hekkelman
232003cb2e added sample PDB file 2021-04-14 17:31:30 +02:00
Maarten L. Hekkelman
db21dd1659 fix making, testing 2021-04-14 17:15:02 +02:00
Maarten L. Hekkelman
2f3279a5ff stand alone, write complete update scripts and config 2021-04-14 17:03:00 +02:00
Maarten L. Hekkelman
ab21317156 at least some tests should now work 2021-04-14 16:25:08 +02:00
Maarten L. Hekkelman
43cb312225 addFileResource added 2021-04-14 16:02:22 +02:00
Maarten L. Hekkelman
ce28cb7a48 Moved BondMap from libpdb-redo to libcifpp 2021-04-14 14:55:55 +02:00
Maarten L. Hekkelman
1e47fa557c indexed CCD file 2021-04-14 11:32:03 +02:00
Maarten L. Hekkelman
1ae3cf7b99 moving towards using CCD, parsing single datablock 2021-04-14 10:59:58 +02:00
Maarten L. Hekkelman
915d6504a2 First steps, remove CCP4 info from compound 2021-04-13 16:39:03 +02:00
Maarten L. Hekkelman
5e63ca7a82 Check for zlib/bzip2 linking 2021-04-13 14:13:25 +02:00
Maarten L. Hekkelman
e0777e74c2 minor updates 2021-04-08 09:26:10 +02:00
Maarten L. Hekkelman
cf465134fd fixing linux again 2021-03-10 16:46:10 +01:00
Maarten L. Hekkelman
873ac70d18 Build with MSVC 2021-03-10 14:10:25 +01:00
Maarten L. Hekkelman
9ca3c50c83 Build with MSVC 2021-03-10 14:10:02 +01:00
Maarten L. Hekkelman
cc1c7c39b1 do not build pdb2cif test for now 2021-03-10 10:37:45 +01:00
Maarten L. Hekkelman
2c6222984d link distance calculation 2021-03-10 08:46:14 +01:00
Maarten L. Hekkelman
58d7e17165 Add Anomalous remark 3 fields for phenix 2021-03-09 11:50:46 +01:00
Maarten L. Hekkelman
57618095bf fix LINK record (length position) 2021-03-09 09:09:33 +01:00
Maarten L. Hekkelman
492a1ad8ec LINK distance 2021-03-08 18:55:54 +01:00
Maarten L. Hekkelman
71da0ce345 fix in fixing distance for LINK records 2021-03-08 16:56:11 +01:00
Maarten L. Hekkelman
7dc574b534 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2021-03-05 11:50:53 +01:00
Maarten L. Hekkelman
ff8a7a1bfd for g++-11 2021-03-05 11:50:08 +01:00
Maarten L. Hekkelman
0f963eeab1 @travis 2021-02-22 19:22:45 +01:00
Maarten L. Hekkelman
8f02665e6d symop table generator, now requires output argument 2021-02-22 17:13:50 +01:00
Maarten L. Hekkelman
8604c3ab03 merged 2021-02-22 15:23:51 +01:00
Maarten L. Hekkelman
7a61129388 refactor configure 2021-02-22 15:21:47 +01:00
Maarten L. Hekkelman
516db3d8be for travis 2021-02-20 19:40:07 +01:00
Maarten L. Hekkelman
34bdc5056a fix parsing PDB files containing carriage returns 2021-02-20 19:23:14 +01:00
Maarten L. Hekkelman
9402fd5cf3 boost flags order 2021-02-17 20:54:27 +01:00
Maarten L. Hekkelman
a6faa5ce0c fix libcifpp.pc, ax_pkg_require is not implemented in this configure yet 2021-02-17 19:44:02 +01:00
Maarten L. Hekkelman
5735a70e47 using resources... 2021-02-17 15:27:05 +01:00
Maarten L. Hekkelman
8f997658d7 update configure to configure data directories 2021-02-17 11:28:50 +01:00
Maarten L. Hekkelman
5069316326 When converting pdb to cif, calculate distance for LINKR records 2021-02-16 09:55:54 +01:00
Maarten L. Hekkelman
143f17fb2a reorganize resources 2021-02-10 13:55:40 +01:00
Maarten L. Hekkelman
900d7fa07a moved dictionaries 2021-02-10 13:28:33 +01:00
Maarten L. Hekkelman
a4389542fd pic 2021-02-10 10:42:49 +01:00
Maarten L. Hekkelman
b7f4e40917 ouch... wrong type in verbose output generation 2021-02-09 16:37:21 +01:00
Maarten L. Hekkelman
a0447ba91c implemented missing function cif::Category::find1 2021-02-08 16:24:02 +01:00
Maarten L. Hekkelman
17c9d208ad revert back to boost::regex for validator since gnu std::regex implementation is recursive without proper checks... 2021-01-25 16:51:12 +01:00
Maarten L. Hekkelman
e89311fcff init compound factory in absence of CLIBD_MON 2021-01-18 14:25:27 +01:00
Maarten L. Hekkelman
bab750fa6c USE_RSRC is really needed 2021-01-12 11:33:39 +01:00
Maarten L. Hekkelman
0caaf23767 Thread local compound factory, for servers that manipulate the factory. 2021-01-12 09:37:38 +01:00
Maarten L. Hekkelman
c5676f1dfb accept compressed raw data in mmcif::File constructor 2021-01-12 09:36:43 +01:00
Maarten L. Hekkelman
86854581f8 Add contructor loading raw data to mmcif::File 2021-01-11 16:37:18 +01:00
Maarten L. Hekkelman
ef81944826 fix hang in pdb parsing with single line file 2021-01-11 16:36:30 +01:00
Maarten L. Hekkelman
b3b53ebcc0 travis job with clang 2021-01-11 13:19:52 +01:00
Maarten L. Hekkelman
3bc0384100 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2021-01-11 13:01:28 +01:00
Maarten L. Hekkelman
24b1c2ae58 remove Config.hpp from list of files to install 2021-01-11 13:01:20 +01:00
Maarten L. Hekkelman
c996bfcfaf added pthread flags 2021-01-11 13:00:06 +01:00
Maarten L. Hekkelman
3814635aa0 shared libraries, disable by default. Not my favourite, but hey, we need to build on MacOS as well... 2021-01-11 11:54:40 +01:00
Maarten L. Hekkelman
2b6c09cfad shared libraries, default action again 2021-01-11 11:19:34 +01:00
Maarten L. Hekkelman
9ee474081e search data_dir as well as cache_dir, for MacOS 2021-01-11 10:38:51 +01:00
Maarten L. Hekkelman
5bab298f74 search data_dir as well as cache_dir, for MacOS 2021-01-11 10:31:57 +01:00
Maarten L. Hekkelman
fd42b7f443 search data_dir as well as cache_dir, for MacOS 2021-01-11 10:28:49 +01:00
Maarten L. Hekkelman
edcea220f6 search data_dir as well as cache_dir, for MacOS 2021-01-11 10:12:14 +01:00
Maarten L. Hekkelman
a92e85f8db config.h file 2021-01-11 09:12:23 +01:00
Maarten L. Hekkelman
6fbe8bb192 refactored config file, avoid clashes in defines by autoconf 2021-01-11 08:54:49 +01:00
Maarten L. Hekkelman
5f45fd9b5b revert writing SOURCE_DATE_EPOCH since that is a Debian thing 2021-01-06 14:54:38 +01:00
Maarten L. Hekkelman
46925331c7 Merge branch 'trunk' of github.com:PDB-REDO/libcifpp into trunk 2021-01-06 13:04:06 +01:00
Maarten L. Hekkelman
84123df996 changes from Debian included 2021-01-06 13:03:52 +01:00
drlemmus
641b446195 Update update-dictionary-script
Updated URL for the mmcif dictionary download to HTTPS.
2021-01-06 08:53:54 +01:00
Maarten L. Hekkelman
34bbf06793 update config.hpp.in, fix update script to write correct filename 2020-12-17 08:49:39 +01:00
Maarten L. Hekkelman
53a88e236b check for libatomic in configure 2020-12-16 08:51:03 +01:00
Maarten L. Hekkelman
c2069a12b4 stupid typo 2020-12-15 20:24:31 +01:00
122 changed files with 191886 additions and 75653 deletions

23
.gitignore vendored
View File

@@ -1,16 +1,17 @@
obj.dbg/
obj/
.libs/
build/
.vscode/
.vs/
.pc/
autom4te.cache/
GNUmakefile
include/cif++/Config.hpp
tools/symop-map-generator
test/unit-test
libcifpp.pc
libcifpp.la
config.status
config.log
libtool
test/pdb2cif-test
test/rename-compound-test
tools/update-libcifpp-data
data/components.cif*
CMakeSettings.json
msvc/
Testing/
rsrc/feature-request.txt
test/test-create_sugar_?.cif
test/oprofile_data/
test/perf.data*

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "regex"]
path = regex
url = https://github.com/boostorg/regex
[submodule "gxrio"]
path = gxrio
url = https://github.com/mhekkel/gxrio.git

View File

@@ -1,33 +0,0 @@
language: cpp
os:
- linux
- osx
dist: focal
osx_image:
- xcode12
compiler:
- gcc
# - clang
addons:
apt:
packages:
- libboost-all-dev
before_install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install make; fi
script:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./configure --disable-shared ; else ./configure ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then gmake ; else make ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then gmake test ; else make test ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then sudo gmake install ; else sudo make install; fi
# jobs:
# allow_failures:
# - os: osx

450
CMakeLists.txt Normal file
View File

@@ -0,0 +1,450 @@
# SPDX-License-Identifier: BSD-2-Clause
# Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.16)
# set the project name
project(cifpp VERSION 5.0.0 LANGUAGES CXX)
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(GNUInstallDirs)
include(CheckFunctionExists)
include(CheckIncludeFiles)
include(CheckLibraryExists)
include(CMakePackageConfigHelpers)
include(CheckCXXSourceCompiles)
# include(Dart)
include(AddGitSubmodule)
set(CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
elseif(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
# Building shared libraries?
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
# We do not want to write an export file for all our symbols...
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Optionally build a version to be installed inside CCP4
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4" OFF)
# 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)
option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" OFF)
endif()
# When CCP4 is sourced in the environment, we can recreate the symmetry operations table
if(EXISTS "$ENV{CCP4}")
if(EXISTS "$ENV{CLIBD}/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()
set(CIFPP_RECREATE_SYMOP_DATA OFF)
message("Not trying to recreate symop_table_data.hpp since CCP4 is not defined")
endif()
# Unit tests
option(ENABLE_TESTING "Build test exectuables" OFF)
if(BUILD_FOR_CCP4)
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced")
else()
list(APPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
list(APPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}")
if(WIN32)
set(BUILD_SHARED_LIBS ON)
endif()
endif()
endif()
if(MSVC)
# make msvc standards compliant...
add_compile_options(/permissive-)
macro(get_WIN32_WINNT version)
if(WIN32 AND CMAKE_SYSTEM_VERSION)
set(ver ${CMAKE_SYSTEM_VERSION})
string(REPLACE "." "" ver ${ver})
string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver})
set(${version} "0x${ver}")
endif()
endmacro()
get_WIN32_WINNT(ver)
add_definitions(-D_WIN32_WINNT=${ver})
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 try to use boost::regex instead")
set(BOOST_REGEX_STANDALONE ON)
add_git_submodule(regex EXCLUDE_FROM_ALL)
endif()
endif()
set(CMAKE_THREAD_PREFER_PTHREAD)
set(THREADS_PREFER_PTHREAD_FLAG)
find_package(Threads)
add_git_submodule(gxrio EXCLUDE_FROM_ALL)
find_package(ZLIB REQUIRED)
find_package(LibLZMA REQUIRED)
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("LibCIFPP")
# SymOp data table
if(CIFPP_RECREATE_SYMOP_DATA)
# The tool to create the table
add_executable(symop-map-generator "${PROJECT_SOURCE_DIR}/tools/symop-map-generator.cpp")
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
COMMAND $<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
)
add_custom_target(
OUTPUT ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
)
endif()
# Sources
set(project_sources
${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/pdb2cif_remark_3.cpp
${PROJECT_SOURCE_DIR}/src/pdb/tls.cpp
)
set(project_headers
${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++/list.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/cif2pdb.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/io.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/pdb2cif.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/pdb2cif_remark_3.hpp
${PROJECT_SOURCE_DIR}/include/cif++/pdb/tls.hpp
)
add_library(cifpp ${project_sources} ${project_headers} ${PROJECT_SOURCE_DIR}/src/symop_table_data.hpp)
add_library(cifpp::cifpp ALIAS cifpp)
set_target_properties(cifpp PROPERTIES POSITION_INDEPENDENT_CODE ON)
if(BOOST_REGEX_STANDALONE)
target_compile_definitions(cifpp PUBLIC USE_BOOST_REGEX=1 BOOST_REGEX_STANDALONE=1)
endif()
target_include_directories(cifpp
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
target_link_libraries(cifpp PUBLIC
Threads::Threads
ZLIB::ZLIB
LibLZMA::LibLZMA
${CIFPP_REQUIRED_LIBRARIES})
get_target_property(GXRIO_INCLUDE_DIR gxrio::gxrio INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(cifpp PRIVATE ${GXRIO_INCLUDE_DIR})
if(BOOST_REGEX_STANDALONE)
target_include_directories(cifpp PRIVATE regex/include)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CIFPP_DOWNLOAD_CCD)
# download the components.cif file from CCD
set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/data/components.cif)
if(NOT EXISTS ${COMPONENTS_CIF})
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/data)
file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
endif()
find_program(GUNZIP gunzip)
if(GUNZIP)
file(DOWNLOAD ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz
SHOW_PROGRESS)
add_custom_command(OUTPUT ${COMPONENTS_CIF}
COMMAND ${GUNZIP} ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
else()
file(DOWNLOAD ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF}
SHOW_PROGRESS)
endif()
endif()
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
endif()
if(UNIX)
set(CIFPP_CACHE_DIR "/var/cache/libcifpp" CACHE STRING "The cache directory to use")
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
endif()
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_DATADIR}/libcifpp)
set(CIFPP_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${SHARE_INSTALL_DIR}" CACHE STRING "The directory containing the provided data files")
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
# Install rules
install(TARGETS cifpp
EXPORT cifppTargets
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(
FILES $<TARGET_PDB_FILE:${PROJECT_NAME}>
DESTINATION ${CMAKE_INSTALL_LIBDIR}
OPTIONAL)
endif()
install(EXPORT cifppTargets
FILE "cifppTargets.cmake"
NAMESPACE cifpp::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
)
install(
DIRECTORY include/cif++
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT Devel
)
install(
FILES include/cif++.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT Devel
)
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}
)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/cmake/cifppConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR SHARE_INSTALL_DIR
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
COMPONENT Devel
)
set(cifpp_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR})
set_target_properties(cifpp PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${cifpp_MAJOR_VERSION}
INTERFACE_cifpp_MAJOR_VERSION ${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/cifppConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
# pkgconfig support
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
set(includedir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libcifpp.pc.in
${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc.in @ONLY)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc
INPUT ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc.in)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
if(ENABLE_TESTING)
enable_testing()
find_package(Boost REQUIRED headers)
list(APPEND CIFPP_tests unit-v2 unit-3d format model rename-compound sugar
)
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})
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp Boost::headers)
if(CIFPP_USE_RSRC)
mrc_target_resources(${CIFPP_TEST} ${PROJECT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic)
endif()
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}> -- ${PROJECT_SOURCE_DIR}/test)
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> -- ${PROJECT_SOURCE_DIR}/test)
endforeach()
endif()
message("Will install in ${CMAKE_INSTALL_PREFIX}")
# Optionally install the update scripts for CCD and dictionary files
if(CIFPP_INSTALL_UPDATE_SCRIPT)
set(CIFPP_CRON_DIR "$ENV{DESTDIR}/etc/cron.weekly")
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})
install(DIRECTORY DESTINATION "$ENV{DESTDIR}/etc/libcifpp/cache-update.d")
# a config to, to make it complete
if(NOT EXISTS "$ENV{DESTDIR}/etc/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 "$ENV{DESTDIR}/etc")
install(CODE "message(\"A configuration file has been written to $ENV{DESTDIR}/etc/libcifpp.conf, please edit this file to enable automatic updates\")")
endif()
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
endif()

View File

@@ -1,273 +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.
# makefile for libcifpp
.PHONY: firstTarget
firstTarget: all
CXX = @CXX@
CXXFLAGS = @CPPFLAGS@ @CXXFLAGS@ @BOOST_CPPFLAGS@
LDFLAGS = @LDFLAGS@ @LIBS@ @BOOST_LDFLAGS@
LIBS = @LIBS@ \
-latomic \
@BOOST_IOSTREAMS_LIB@ \
@BOOST_DATE_TIME_LIB@
prefix = $(DESTDIR)@prefix@
exec_prefix = @exec_prefix@
libdir = @libdir@
includedir = @includedir@
datarootdir = @datarootdir@
datadir = @datadir@
docdir = @docdir@
pkgconfigdir = $(libdir)/pkgconfig
CCP4DIR = @CCP4@
CLIBD = $(CCP4DIR:%=%/lib/data)
CACHE_DIR = $(DESTDIR)/var/cache/libcifpp
CRON_DIR = $(DESTDIR)/etc/cron.weekly
DEFINES += CACHE_DIR='"$(CACHE_DIR)"'
GNUmakefile: config.status GNUmakefile.in
$(SHELL) ./config.status
ifeq "$(CHECK_CONFIG)" "1"
config.status: configure
$(SHELL) ./config.status --recheck
configure: configure.ac
autoconf
endif
LIB_NAME = @PACKAGE_NAME@
LIB_VERSION = @LIBCIF_LT_VERSION@
LIB_CURRENT = @LIBCIF_LT_CURRENT@
LIB_TARGET = $(LIB_NAME).la
VERSION = @LIBCIF_SEMANTIC_VERSION@
DIST_NAME = @PACKAGE_NAME@-$(subst :,.,$(VERSION))
# libtool stuff
LIBTOOL_DEPS = @LIBTOOL_DEPS@
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status libtool
LIBTOOL = $(SHELL) @abs_top_builddir@/libtool
CXXCOMPILE = $(LIBTOOL) --silent --tag=CXX --mode=compile $(CXX) $(CXXFLAGS)
CXXLINK = $(LIBTOOL) --silent --tag=CXX --mode=link $(CXX) $(CXXFLAGS) $(LDFLAGS) -version-info $(LIB_VERSION) -o $@
# main build variables
CXXFLAGS += -Wall -Wno-multichar -I include
# Use the DEBUG flag to build debug versions of the code
DEBUG = @DEBUG@
ifeq "$(DEBUG)" "1"
DEFINES += DEBUG
CXXFLAGS += -g -O0
LDFLAGS += -g
else
CXXFLAGS += -O2
DEFINES += NDEBUG
endif
# targets
VPATH += src:test
CXXFLAGS += $(DEFINES:%=-D%)
OBJDIR = obj
ifeq "$(DEBUG)" "1"
OBJDIR := $(OBJDIR).dbg
endif
$(OBJDIR):
mkdir -p $(OBJDIR)
OBJECTS = $(OBJDIR)/AtomType.lo \
$(OBJDIR)/Cif2PDB.lo \
$(OBJDIR)/Cif++.lo \
$(OBJDIR)/CifParser.lo \
$(OBJDIR)/CifUtils.lo \
$(OBJDIR)/CifValidator.lo \
$(OBJDIR)/Compound.lo \
$(OBJDIR)/PDB2Cif.lo \
$(OBJDIR)/PDB2CifRemark3.lo \
$(OBJDIR)/Point.lo \
$(OBJDIR)/Secondary.lo \
$(OBJDIR)/Structure.lo \
$(OBJDIR)/Symmetry.lo \
$(OBJDIR)/TlsParser.lo
ifneq "$(CCP4DIR)" ""
# Special rules to generate symmetry operation number table
tools/symop-map-generator: tools/symop-map-generator.cpp
src/SymOpTable_data.cpp: tools/symop-map-generator $(CLIBD)/symop.lib
tools/symop-map-generator > $@
$(OBJDIR)/Symmetry.lo: src/SymOpTable_data.cpp
endif
# We have development releases and official releases, for each we
# maintain different versioning schemes.
ifneq "x@UPDATE_REVISION@" "x"
REVISION = $(shell git log --pretty=format:%h --max-count=1)
REVISION_FILE = version-info-$(REVISION).txt
$(REVISION_FILE):
rm -f version-info-*.txt
@ echo libcifpp-version: $(VERSION) > $@
@ git describe --match=build --dirty >> $@
@ git log --pretty=medium --date=iso8601 -1 >> $@
src/revision.hpp: $(REVISION_FILE)
@ echo 'const char kRevision[] = R"(' > $@
@ cat $? >> $@
@ echo ')";' >> $@
else
src/revision.hpp:
@ echo 'const char kRevision[] = R"(' > $@
@ echo libcifpp-version: $(VERSION) >> $@
@ echo Date: $$(date --iso-8601) >> $@
@ echo ')";' >> $@
endif
$(OBJDIR)/CifUtils.o: src/revision.hpp
$(OBJDIR)/CifUtils.lo: src/revision.hpp
$(LIB_TARGET): $(OBJECTS)
$(CXXLINK) -rpath $(libdir) $(OBJECTS) $(LIBS)
.PHONY: lib
lib: $(LIB_TARGET)
.PHONY: all
all: $(LIB_TARGET)
-include $(OBJECTS:%.lo=%.d)
$(OBJECTS:.lo=.d):
$(OBJDIR)/%.lo: %.cpp | $(OBJDIR)
@ echo ">>" $<
@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(OBJDIR)/$*.d -c -o $@ $<
$(OBJDIR)/%.o: %.cpp | $(OBJDIR)
@ echo ">>" $<
@ $(CXX) $(CXXFLAGS) -MT $@ -MD -MP -MF $(OBJDIR)/$*.d -c -o $@ $<
.PHONY: clean
clean:
rm -rf .libs $(OBJDIR)/* $(LIB_TARGET)
rm -f $(TESTS:%=test/%-test)
.PHONY: distclean
distclean: clean
rm -f libtool config.lt
rm -f config.status config.cache config.log configure.lineno config.status.lineno
rm -f GNUmakefile
# Test rules
define TEST_template =
-include $$(OBJDIR)/$(1)-test.d
$(1)_OBJECTS = $$(OBJDIR)/$(1)-test.o
test/$(1)-test: $(LIB_TARGET) $$($(1)_OBJECTS)
@ echo ">>> building $(1)-test"
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $$@ $$($(1)_OBJECTS) -L.libs -lcifpp $(LIBS)
.PHONY: $(1)-test
$(1)-test: test/$(1)-test
cd test; LD_LIBRARY_PATH=../.libs ./$(1)-test $$($(1)_PARAMS)
endef
TESTS = unit
$(foreach part,$(TESTS),$(eval $(call TEST_template,$(part))))
.PHONY: test
test: $(TESTS:%=%-test)
HEADERS = \
AtomType.hpp \
CifParser.hpp \
Compound.hpp \
PDB2CifRemark3.hpp \
Structure.hpp \
Cif2PDB.hpp \
CifUtils.hpp \
Config.hpp \
Point.hpp \
Symmetry.hpp \
Cif++.hpp \
CifValidator.hpp \
PDB2Cif.hpp \
Secondary.hpp \
TlsParser.hpp
.PHONY: install
install: lib
install -d $(libdir)
$(LIBTOOL) --mode=install install $(LIB_TARGET) $(libdir)
install -d $(datadir)/libcifpp
for d in isomers.txt dictionaries/mmcif_ddl.dic dictionaries/mmcif_pdbx_v50.dic; do \
install -m644 rsrc/$$d $(datadir)/libcifpp; \
done
gzip -f $(datadir)/libcifpp/*
install -d $(CRON_DIR)
install -m755 tools/update-dictionary-script $(CRON_DIR)/libcifpp
install -d $(includedir)/cif++
for f in $(HEADERS); do install include/cif++/$$f $(includedir)/cif++/$$f; done
install -d $(pkgconfigdir)
install -m 644 $(LIB_NAME).pc $(pkgconfigdir)/$(LIB_NAME).pc
dist-clean: clean
.PHONY: dist
dist:
rm -rf $(DIST_NAME)
mkdir $(DIST_NAME)
git archive trunk | tar -x -C $(DIST_NAME)
tar czf $(DIST_NAME).tgz $(DIST_NAME)
rm -rf $(DIST_NAME)
FORCE:

View File

@@ -1,6 +1,7 @@
SPDX-License-Identifier: BSD-2-Clause
BSD-2-Clause License
Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -20,4 +21,4 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
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.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,32 +1,46 @@
[![Build Status](https://travis-ci.org/PDB-REDO/libcifpp.svg?branch=trunk)](https://travis-ci.org/PDB-REDO/libcifpp)
libcifpp
========
This library contains code to work with mmCIF and PDB files.
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.3 and clang 9.0
have been used.
have been used as well as MSVC version 2019.
Other requirements are:
- GNU make version 4.1 or higher.
- Boost libraries, at least version 1.71
- [mrc](https://github.com/mhekkel/mrc), a resource compiler that
allows including data files into the executable making them easier to
install. Strictly this is optional, but at the expense of a lot of
functionality.
install. Strictly this is optional, but at the expense of functionality.
Building
--------
Simply configure, make and make install.
This library uses [cmake](https://cmake.org). The usual way of building
and installing is to create a `build` directory and run cmake there.
On linux e.g. you would issue the following commands:
```
git clone https://github.com/PDB-REDO/libcifpp.git
cd libcifpp
mkdir build
cd build
cmake ..
cmake --build . --config Release
ctest -C Release
cmake --install .
```
This checks out the source code from github, creates a new directory
where cmake stores its files. Run a configure, build the code and run
tests. And then it installs the library and auxiliary files.
The default is to install everything in `$HOME/.local` on Linux and
`%LOCALAPPDATA%` on Windows (the AppData/Local folder in your home directory).
You can change this by specifying the prefix with the
[CMAKE_INSTALL_PREFIX](https://cmake.org/cmake/help/v3.21/variable/CMAKE_INSTALL_PREFIX.html)
variable.
There's one configure flag that might be of interest: if you specify
DEBUG=1 the make will create a debug version by default. Otherwise,
you can run make with DEBUG=1 to create a debug version.

View File

@@ -1,6 +1,91 @@
Version 5.0.0
- Total rewrite of cif part
- Removed DSSP code, moved into dssp project itself
Version 4.2.1
- Improved REMARK 3 parser (for TLS in large molecules)
Version 4.2.0
- Yet another rewrite of resource loading
Version 4.1.1
- Fall back to zero charge for scattering factors if the atom
was not found in the table.
- Improve code to locate resources, failing less.
Version 4.1.0
- Some interface changes for mmcif::Atom
Version 4.0.1
- Added a bunch of const methods to Datablock and Category.
- Changed PDB writing interface to accept Datablock instead of File.
Version 4.0.0
- getResidue in mmcif::Structure now requires both a
sequence ID and an auth sequence ID. As a result the code was cleaned
up considerably.
Version 3.0.5
- mmcif::Structure redesign. It is now a wrapper around a cif::Datablock.
Version 3.0.4
- Fix in mmCIF parser, now correctly handles the unquoted
string ??
Version 3.0.3
- Better configuration checks, for atomic e.g.
- Fixed a problem introduced in refactoring mmcif::Atom
- Version string creation
Version 3.0.2
- refactored mmcif::Atom for performance reasons
Version 3.0.1
- Fixed processing of proline restraints file from CCP4, proline
is a peptide, really.
- Added code to facilitate DSSP
Version 3.0.0
- Replaced many strings in the API with string_view for
performance reasons.
- Upgraded mmcif::Structure
- various other small fixes
Version 2.0.5
- Backporting updated CMakeLists.txt file
Version 2.0.4
- Reverted a too strict test when reading cif files.
Version 2.0.3
- Fixed reading mmCIF files where model numbers are used and
model number 1 is missing.
Version 2.0.2
- Added configuration flag to disable downloading CCD data during build
Note that there are now two flags for CCD data:
DOWNLOAD_CCD to enable downloading during build
INSTALL_UPDATE_SCRIPT to install an update mechanism for this file
- Updated unit tests to work even if no CCD data is available
Version 2.0.1
- Fixed the generator for the symmetry operator table
Version 2.0.0
- New API interface for accessing query results
- Removed bzip2 support
- improved makefiles
Version 1.1.1
- Now with full support for MS Windows
Version 1.1.0
- Changed from GNU configure to CMake.
- Loading compound information from CCD file
Version 1.0.1
- Changed the way resources are looked up, local dir first,
then /var/cache and finally compiled in resources (with mrc).
Version 1.0.0
- First public release
- First public release

View File

@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.16..3.19)
function(add_git_submodule dir)
# add a Git submodule directory to CMake, assuming the
# Git submodule directory is a CMake project.
#
# Usage: in CMakeLists.txt
#
# include(AddGitSubmodule.cmake)
# add_git_submodule(mysubmod_dir)
find_package(Git REQUIRED)
if(NOT EXISTS ${dir}/CMakeLists.txt)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19)
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- ${dir}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMAND_ERROR_IS_FATAL ANY)
else()
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- ${dir}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
endif()
endif()
set(ENABLE_TESTING OFF)
add_subdirectory(${dir} ${ARGV})
endfunction(add_git_submodule)

62
cmake/FindAtomic.cmake Normal file
View File

@@ -0,0 +1,62 @@
# Simple check to see if we need a library for std::atomic
if(TARGET std::atomic)
return()
endif()
cmake_minimum_required(VERSION 3.10)
include(CMakePushCheckState)
include(CheckIncludeFileCXX)
include(CheckCXXSourceRuns)
cmake_push_check_state()
check_include_file_cxx("atomic" _CXX_ATOMIC_HAVE_HEADER)
mark_as_advanced(_CXX_ATOMIC_HAVE_HEADER)
set(code [[
#include <atomic>
int main(int argc, char** argv) {
std::atomic<long long> s;
++s;
return 0;
}
]])
check_cxx_source_runs("${code}" _CXX_ATOMIC_BUILTIN)
if(_CXX_ATOMIC_BUILTIN)
set(_found 1)
else()
list(APPEND CMAKE_REQUIRED_LIBRARIES atomic)
list(APPEND FOLLY_LINK_LIBRARIES atomic)
check_cxx_source_runs("${code}" _CXX_ATOMIC_LIB_NEEDED)
if (NOT _CXX_ATOMIC_LIB_NEEDED)
message(FATAL_ERROR "unable to link C++ std::atomic code: you may need \
to install GNU libatomic")
else()
set(_found 1)
endif()
endif()
if(_found)
add_library(std::atomic INTERFACE IMPORTED)
set_property(TARGET std::atomic APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14)
if(_CXX_ATOMIC_BUILTIN)
# Nothing to add...
elseif(_CXX_ATOMIC_LIB_NEEDED)
set_target_properties(std::atomic PROPERTIES IMPORTED_LIBNAME atomic)
set(STDCPPATOMIC_LIBRARY atomic)
endif()
endif()
cmake_pop_check_state()
set(Atomic_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::atomic" FORCE)
if(Atomic_FIND_REQUIRED AND NOT Atomic_FOUND)
message(FATAL_ERROR "Cannot run simple program using std::atomic")
endif()

View File

@@ -0,0 +1,74 @@
# 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)
if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
message(FATAL_ERROR "Cannot run simple program using std::filesystem")
endif()

View File

@@ -0,0 +1,284 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the working tree (--dirty option),
# and adjusting the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# git_local_changes(<var>)
#
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
# Uses the return code of "git diff-index --quiet HEAD --".
# Does not regard untracked files.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
# http://academic.cleardefinition.com
#
# Copyright 2009-2013, Iowa State University.
# Copyright 2013-2020, Ryan Pavlik
# Copyright 2013-2020, Contributors
# SPDX-License-Identifier: BSL-1.0
# 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)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
# Function _git_find_closest_git_dir finds the next closest .git directory
# that is part of any directory in the path defined by _start_dir.
# The result is returned in the parent scope variable whose name is passed
# as variable _git_dir_var. If no .git directory can be found, the
# function returns an empty string via _git_dir_var.
#
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
# neither foo nor bar contain a file/directory .git. This wil return
# C:/bla/.git
#
function(_git_find_closest_git_dir _start_dir _git_dir_var)
set(cur_dir "${_start_dir}")
set(git_dir "${_start_dir}/.git")
while(NOT EXISTS "${git_dir}")
# .git dir not found, search parent directories
set(git_previous_parent "${cur_dir}")
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
if(cur_dir STREQUAL git_previous_parent)
# We have reached the root directory, we are not in git
set(${_git_dir_var}
""
PARENT_SCOPE)
return()
endif()
set(git_dir "${cur_dir}/.git")
endwhile()
set(${_git_dir_var}
"${git_dir}"
PARENT_SCOPE)
endfunction()
function(get_git_head_revision _refspecvar _hashvar)
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
else()
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
endif()
if(NOT "${GIT_DIR}" STREQUAL "")
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
"${GIT_DIR}")
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
# We've gone above the CMake root dir.
set(GIT_DIR "")
endif()
endif()
if("${GIT_DIR}" STREQUAL "")
set(${_refspecvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
set(${_hashvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# Check if the current source dir is a git submodule or a worktree.
# In both cases .git is a file instead of a directory.
#
if(NOT IS_DIRECTORY ${GIT_DIR})
# The following git command will return a non empty string that
# points to the super project working tree if the current
# source dir is inside a git submodule.
# Otherwise the command will return an empty string.
#
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse
--show-superproject-working-tree
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${out}" STREQUAL "")
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
${submodule})
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
ABSOLUTE)
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
else()
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
file(READ ${GIT_DIR} worktree_ref)
# The .git directory contains a path to the worktree information directory
# inside the parent git repo of the worktree.
#
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
${worktree_ref})
string(STRIP ${git_worktree_dir} git_worktree_dir)
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
endif()
else()
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake" @ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar}
"${HEAD_REF}"
PARENT_SCOPE)
set(${_hashvar}
"${HEAD_HASH}"
PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_describe_working_tree _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var}
"CLEAN"
PARENT_SCOPE)
else()
set(${_var}
"DIRTY"
PARENT_SCOPE)
endif()
endfunction()

View File

@@ -0,0 +1,43 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright 2009-2012, Iowa State University
# Copyright 2011-2015, Contributors
# 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)
# SPDX-License-Identifier: BSL-1.0
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}")
endif()
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

77
cmake/VersionString.cmake Normal file
View File

@@ -0,0 +1,77 @@
# SPDX-License-Identifier: BSD-2-Clause
# Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.15)
# Create a revision file, containing the current git version info, if any
function(write_version_header)
include(GetGitRevisionDescription)
if(NOT(GIT-NOTFOUND OR HEAD-HASH-NOTFOUND))
git_describe_working_tree(BUILD_VERSION_STRING --match=build --dirty)
if(BUILD_VERSION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?")
set(BUILD_GIT_TAGREF "${CMAKE_MATCH_2}")
if(CMAKE_MATCH_3)
set(BUILD_VERSION_STRING "${CMAKE_MATCH_1}*")
else()
set(BUILD_VERSION_STRING "${CMAKE_MATCH_1}")
endif()
endif()
else()
set(BUILD_VERSION_STRING "no git info available")
endif()
include_directories(${CMAKE_BINARY_DIR} PRIVATE)
string(TIMESTAMP BUILD_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC)
if(ARGC GREATER 0)
set(VAR_PREFIX "${ARGV0}")
endif()
file(WRITE "${CMAKE_BINARY_DIR}/revision.hpp.in" [[// Generated revision file
#pragma once
#include <ostream>
const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@";
const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@";
const char k@VAR_PREFIX@VersionGitTag[] = "@BUILD_GIT_TAGREF@";
const char k@VAR_PREFIX@BuildInfo[] = "@BUILD_VERSION_STRING@";
const char k@VAR_PREFIX@BuildDate[] = "@BUILD_DATE_TIME@";
inline void write_version_string(std::ostream &os, bool verbose)
{
os << k@VAR_PREFIX@ProjectName << " version " << k@VAR_PREFIX@VersionNumber << std::endl;
if (verbose)
{
os << "build: " << k@VAR_PREFIX@BuildInfo << ' ' << k@VAR_PREFIX@BuildDate << std::endl;
if (k@VAR_PREFIX@VersionGitTag[0] != 0)
os << "git tag: " << k@VAR_PREFIX@VersionGitTag << std::endl;
}
}
]])
configure_file("${CMAKE_BINARY_DIR}/revision.hpp.in" "${CMAKE_BINARY_DIR}/revision.hpp" @ONLY)
endfunction()

View File

@@ -0,0 +1,15 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Threads)
find_dependency(ZLIB REQUIRED)
find_dependency(LibLZMA REQUIRED)
INCLUDE("${CMAKE_CURRENT_LIST_DIR}/cifppTargets.cmake")
set_and_check(CIFPP_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(CIFPP_LIBRARY_DIR "@PACKAGE_LIBRARY_INSTALL_DIR@")
set_and_check(CIFPP_SHARE_DIR "@PACKAGE_SHARE_INSTALL_DIR@")
check_required_components(cifpp)

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) << std::endl;
std::cout << m.str(1).substr(0, 10) << std::endl;
return 0;
}

View File

@@ -1,19 +0,0 @@
#include <algorithm>
template<typename COMP>
class foo
{
public:
foo(int a, COMP&& b)
: m_a(a), m_b(std::move(b)) {}
int m_a;
COMP m_b;
};
void bar(const int& b)
{
int c = 1;
auto f = new foo(c, [tag = c, b](const int& x)
{ x < b; });
}

1480
config/config.guess vendored

File diff suppressed because it is too large Load Diff

1801
config/config.sub vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,518 +0,0 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2018-03-11.20; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
# Note that $RANDOM variable is not portable (e.g. dash); Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p' feature.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

File diff suppressed because it is too large Load Diff

8394
config/m4/libtool.m4 vendored

File diff suppressed because it is too large Load Diff

437
config/m4/ltoptions.m4 vendored
View File

@@ -1,437 +0,0 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 8 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option '$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
_LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
[_LT_WITH_AIX_SONAME([aix])])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [1], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the 'shared' and
# 'disable-shared' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the 'static' and
# 'disable-static' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the 'fast-install'
# and 'disable-fast-install' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_AIX_SONAME([DEFAULT])
# ----------------------------------
# implement the --with-aix-soname flag, and support the `aix-soname=aix'
# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
m4_define([_LT_WITH_AIX_SONAME],
[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
shared_archive_member_spec=
case $host,$enable_shared in
power*-*-aix[[5-9]]*,yes)
AC_MSG_CHECKING([which variant of shared library versioning to provide])
AC_ARG_WITH([aix-soname],
[AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
[shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
[case $withval in
aix|svr4|both)
;;
*)
AC_MSG_ERROR([Unknown argument to --with-aix-soname])
;;
esac
lt_cv_with_aix_soname=$with_aix_soname],
[AC_CACHE_VAL([lt_cv_with_aix_soname],
[lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
with_aix_soname=$lt_cv_with_aix_soname])
AC_MSG_RESULT([$with_aix_soname])
if test aix != "$with_aix_soname"; then
# For the AIX way of multilib, we name the shared archive member
# based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
# and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
# Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
# the AIX toolchain works better with OBJECT_MODE set (default 32).
if test 64 = "${OBJECT_MODE-32}"; then
shared_archive_member_spec=shr_64
else
shared_archive_member_spec=shr
fi
fi
;;
*)
with_aix_soname=aix
;;
esac
_LT_DECL([], [shared_archive_member_spec], [0],
[Shared archive member basename, for filename based shared library versioning on AIX])dnl
])# _LT_WITH_AIX_SONAME
LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
# LT_INIT options.
# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[lt_p=${PACKAGE-default}
case $withval in
yes|no) pic_mode=$withval ;;
*)
pic_mode=default
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for lt_pkg in $withval; do
IFS=$lt_save_ifs
if test "X$lt_pkg" = "X$lt_p"; then
pic_mode=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[pic_mode=m4_default([$1], [default])])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

124
config/m4/ltsugar.m4 vendored
View File

@@ -1,124 +0,0 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59, which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

View File

@@ -1,23 +0,0 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# @configure_input@
# serial 4179 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.4.6])
m4_define([LT_PACKAGE_REVISION], [2.4.6])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.4.6'
macro_revision='2.4.6'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

View File

@@ -1,99 +0,0 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 5 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])

19493
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +0,0 @@
AC_PREREQ([2.69])
AC_INIT([libcifpp], 1.0, [m.hekkelman@nki.nl])
dnl Switch to a C++ compiler, and check if it works.
AC_LANG(C++)
AX_CXX_COMPILE_STDCXX_17([noext])
AX_CHECK_COMPILE_FLAG([-fstandalone-debug], [ CXXFLAGS="$CXXFLAGS -fstandalone-debug" ], , [-Werror])
AC_CONFIG_SRCDIR([src/Cif++.cpp])
AC_CONFIG_AUX_DIR(config)
AC_CONFIG_MACRO_DIR([config/m4])
AC_CONFIG_HEADERS([include/cif++/Config.hpp])
AC_PREFIX_DEFAULT(/usr/local)
AC_CHECK_HEADER([filesystem], [], [AC_MSG_ERROR([The file <filesystem> is missing, perhaps you should install a more recent libstdc++ implementation.])])
dnl check if we need stdc++fs as library
AC_MSG_CHECKING([if linking to stdc++fs is required])
AC_TRY_LINK(
[#include <filesystem>],
[(void)std::filesystem::current_path();],
[
AC_MSG_RESULT(no)
],
[
LIBS="$LIBS -lstdc++fs"
AC_TRY_LINK(
[#include <filesystem>],
[(void)std::filesystem::current_path();],
[
AC_MSG_RESULT(yes)
],
[
AC_MSG_ERROR([Could not link filesystem])
]
)
]
)
dnl AC_DEFUN([read_test], [AC_LANG_SOURCE(
dnl esyscmd(tools/m4esc.sh config-tests/$1))])
dnl
dnl AC_MSG_CHECKING([compiler standards compliance])
dnl AC_COMPILE_IFELSE(
dnl [read_test(cpp-17-test.cpp)], [],
dnl [AC_MSG_ERROR([Your c++ compiler is not capable of compiling libcifpp, please upgrade])])
dnl AC_MSG_RESULT(ok)
AC_PROG_INSTALL
dnl Shared libraries are not convenient
dnl LT_INIT([disable-shared])
LT_INIT
AC_SUBST(LIBTOOL_DEPS)
dnl versioning, first for libtool
LIBCIF_CURRENT=1
LIBCIF_REVISION=0
LIBCIF_AGE=0
LIBCIF_LT_CURRENT="${LIBCIF_CURRENT}"
LIBCIF_LT_VERSION="${LIBCIF_CURRENT}:${LIBCIF_REVISION}:${LIBCIF_AGE}"
AC_SUBST(LIBCIF_LT_CURRENT)
AC_SUBST(LIBCIF_LT_VERSION)
dnl and now for the semantic version
LIBCIF_SEMANTIC_VERSION=1.0.0
AC_SUBST(LIBCIF_SEMANTIC_VERSION)
AC_ARG_VAR([DEBUG], [Build a debug version of the library])
dnl revision numbering is something used internally at the NKI
AC_ARG_ENABLE(
revision,
[AS_HELP_STRING([--enable-revision], [Create a build number as revision])])
AS_IF([test "x$enable_revision" != "xyes" ], [
UPDATE_REVISION=1
])
dnl disable resources on macOS
AS_CASE([$host],
[*-apple-*], [USE_RSRC=0],
[USE_RSRC=1]
)
AC_SUBST([USE_RSRC])
AC_SUBST([UPDATE_REVISION], [$UPDATE_REVISION])
AC_ARG_VAR([CCP4], [The location where CCP4 is installed])
AC_PATH_PROG([PKG_CONFIG], [pkg-config])
AC_CHECK_FUNCS([floor pow rint sqrt strchr strerror])
AC_CHECK_HEADERS([sys/ioctl.h])
AC_CHECK_HEADERS([termios.h])
AC_CHECK_HEADER_STDBOOL
AC_CHECK_TYPES([ptrdiff_t])
AC_PROG_MAKE_SET
AX_BOOST_BASE([1.71], [], [
AC_MSG_ERROR([Sorry, your boost is not found or not up-to-date.])
])
AX_BOOST_IOSTREAMS
AX_BOOST_DATE_TIME
AC_OUTPUT([GNUmakefile
libcifpp.pc])

2582
data/ccd-subset.cif Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,24 @@
#include <iostream>
#include <filesystem>
#include <cif++/Cif++.hpp>
#include <cif++.hpp>
namespace fs = std::filesystem;
int main()
{
fs::path in("1cbs.cif.gz");
cif::File file;
file.loadDictionary("mmcif_pdbx_v50");
cif::file file;
file.load("1cbs.cif.gz");
auto& db = file.firstDatablock()["atom_site"];
auto n = db.find(cif::Key("label_atom_id") == "OXT").size();
auto& db = file.front();
auto &atom_site = db["atom_site"];
auto n = atom_site.find(cif::key("label_atom_id") == "OXT").size();
std::cout << "File contains " << db.size() << " atoms of which " << n << (n == 1 ? " is" : " are") << " OXT" << std::endl
std::cout << "File contains " << atom_site.size() << " atoms of which " << n << (n == 1 ? " is" : " are") << " OXT" << std::endl
<< "residues with an OXT are:" << std::endl;
for (const auto& [asym, comp, seqnr]: db.find<std::string,std::string,int>(
cif::Key("label_atom_id") == "OXT",
{ "label_asym_id", "label_comp_id", "label_seq_id" }
))
for (const auto& [asym, comp, seqnr]: atom_site.find<std::string,std::string,int>(
cif::key("label_atom_id") == "OXT", "label_asym_id", "label_comp_id", "label_seq_id"))
{
std::cout << asym << ' ' << comp << ' ' << seqnr << std::endl;
}

1
gxrio Submodule

Submodule gxrio added at a7bb5b5c4b

40
include/cif++.hpp Normal file
View File

@@ -0,0 +1,40 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cif++/utilities.hpp>
#include <cif++/file.hpp>
#include <cif++/parser.hpp>
#include <cif++/format.hpp>
#include <cif++/compound.hpp>
#include <cif++/point.hpp>
#include <cif++/symmetry.hpp>
#include <cif++/model.hpp>
#include <cif++/pdb/io.hpp>

View File

@@ -1,247 +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.
*/
// Lib for working with structures as contained in mmCIF and PDB files
#pragma once
#include <cstdint>
#include <string>
#include <stdexcept>
#include "cif++/Config.hpp"
namespace mmcif
{
enum AtomType : uint8_t
{
Nn = 0, // Unknown
H = 1, // Hydro­gen
He = 2, // He­lium
Li = 3, // Lith­ium
Be = 4, // Beryl­lium
B = 5, // Boron
C = 6, // Carbon
N = 7, // Nitro­gen
O = 8, // Oxy­gen
F = 9, // Fluor­ine
Ne = 10, // Neon
Na = 11, // So­dium
Mg = 12, // Magne­sium
Al = 13, // Alumin­ium
Si = 14, // Sili­con
P = 15, // Phos­phorus
S = 16, // Sulfur
Cl = 17, // Chlor­ine
Ar = 18, // Argon
K = 19, // Potas­sium
Ca = 20, // Cal­cium
Sc = 21, // Scan­dium
Ti = 22, // Tita­nium
V = 23, // Vana­dium
Cr = 24, // Chrom­ium
Mn = 25, // Manga­nese
Fe = 26, // Iron
Co = 27, // Cobalt
Ni = 28, // Nickel
Cu = 29, // Copper
Zn = 30, // Zinc
Ga = 31, // Gallium
Ge = 32, // Germa­nium
As = 33, // Arsenic
Se = 34, // Sele­nium
Br = 35, // Bromine
Kr = 36, // Kryp­ton
Rb = 37, // Rubid­ium
Sr = 38, // Stront­ium
Y = 39, // Yttrium
Zr = 40, // Zirco­nium
Nb = 41, // Nio­bium
Mo = 42, // Molyb­denum
Tc = 43, // Tech­netium
Ru = 44, // Ruthe­nium
Rh = 45, // Rho­dium
Pd = 46, // Pallad­ium
Ag = 47, // Silver
Cd = 48, // Cad­mium
In = 49, // Indium
Sn = 50, // Tin
Sb = 51, // Anti­mony
Te = 52, // Tellurium
I = 53, // Iodine
Xe = 54, // Xenon
Cs = 55, // Cae­sium
Ba = 56, // Ba­rium
La = 57, // Lan­thanum
Hf = 72, // Haf­nium
Ta = 73, // Tanta­lum
W = 74, // Tung­sten
Re = 75, // Rhe­nium
Os = 76, // Os­mium
Ir = 77, // Iridium
Pt = 78, // Plat­inum
Au = 79, // Gold
Hg = 80, // Mer­cury
Tl = 81, // Thallium
Pb = 82, // Lead
Bi = 83, // Bis­muth
Po = 84, // Polo­nium
At = 85, // Asta­tine
Rn = 86, // Radon
Fr = 87, // Fran­cium
Ra = 88, // Ra­dium
Ac = 89, // Actin­ium
Rf = 104, // Ruther­fordium
Db = 105, // Dub­nium
Sg = 106, // Sea­borgium
Bh = 107, // Bohr­ium
Hs = 108, // Has­sium
Mt = 109, // Meit­nerium
Ds = 110, // Darm­stadtium
Rg = 111, // Roent­genium
Cn = 112, // Coper­nicium
Nh = 113, // Nihon­ium
Fl = 114, // Flerov­ium
Mc = 115, // Moscov­ium
Lv = 116, // Liver­morium
Ts = 117, // Tenness­ine
Og = 118, // Oga­nesson
Ce = 58, // Cerium
Pr = 59, // Praseo­dymium
Nd = 60, // Neo­dymium
Pm = 61, // Prome­thium
Sm = 62, // Sama­rium
Eu = 63, // Europ­ium
Gd = 64, // Gadolin­ium
Tb = 65, // Ter­bium
Dy = 66, // Dyspro­sium
Ho = 67, // Hol­mium
Er = 68, // Erbium
Tm = 69, // Thulium
Yb = 70, // Ytter­bium
Lu = 71, // Lute­tium
Th = 90, // Thor­ium
Pa = 91, // Protac­tinium
U = 92, // Ura­nium
Np = 93, // Neptu­nium
Pu = 94, // Pluto­nium
Am = 95, // Ameri­cium
Cm = 96, // Curium
Bk = 97, // Berkel­ium
Cf = 98, // Califor­nium
Es = 99, // Einstei­nium
Fm = 100, // Fer­mium
Md = 101, // Mende­levium
No = 102, // Nobel­ium
Lr = 103, // Lawren­cium
D = 129, // Deuterium
};
// --------------------------------------------------------------------
// AtomTypeInfo
enum RadiusType {
eRadiusCalculated,
eRadiusEmpirical,
eRadiusCovalentEmpirical,
eRadiusSingleBond,
eRadiusDoubleBond,
eRadiusTripleBond,
eRadiusVanderWaals,
eRadiusTypeCount
};
struct AtomTypeInfo
{
AtomType type;
std::string name;
std::string symbol;
float weight;
bool metal;
float radii[eRadiusTypeCount];
};
extern const AtomTypeInfo kKnownAtoms[];
// --------------------------------------------------------------------
// AtomTypeTraits
class AtomTypeTraits
{
public:
AtomTypeTraits(AtomType a);
AtomTypeTraits(const std::string& symbol);
AtomType type() const { return mInfo->type; }
std::string name() const { return mInfo->name; }
std::string symbol() const { return mInfo->symbol; }
float weight() const { return mInfo->weight; }
bool isMetal() const { return mInfo->metal; }
static bool isElement(const std::string& symbol);
static bool isMetal(const std::string& symbol);
float radius(RadiusType type = eRadiusSingleBond) const
{
if (type >= eRadiusTypeCount)
throw std::invalid_argument("invalid radius requested");
return mInfo->radii[type] / 100.f;
}
// data type encapsulating the Waasmaier & Kirfel scattering factors
// in a simplified form (only a and b).
// Added the electrion scattering factors as well
struct SFData
{
double a[6], b[6];
};
// to get the Cval and Siva values, use this constant as charge:
enum { kWKSFVal = -99 };
const SFData& wksf(int charge = 0) const;
const SFData& elsf() const;
private:
const struct AtomTypeInfo* mInfo;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,238 +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.
*/
#pragma once
#include "cif++/Cif++.hpp"
#include <stack>
namespace cif
{
// --------------------------------------------------------------------
class CifParserError : public std::runtime_error
{
public:
CifParserError(uint32_t lineNr, const std::string& message);
};
// --------------------------------------------------------------------
extern const uint32_t kMaxLineLength;
extern const uint8_t kCharTraitsTable[128];
enum CharTraitsMask: uint8_t {
kOrdinaryMask = 1 << 0,
kNonBlankMask = 1 << 1,
kTextLeadMask = 1 << 2,
kAnyPrintMask = 1 << 3
};
inline bool isWhite(int ch)
{
return std::isspace(ch) or ch == '#';
}
inline bool isOrdinary(int ch)
{
return ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kOrdinaryMask) != 0;
}
inline bool isNonBlank(int ch)
{
return ch > 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kNonBlankMask) != 0;
}
inline bool isTextLead(int ch)
{
return ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kTextLeadMask) != 0;
}
inline bool isAnyPrint(int ch)
{
return ch == '\t' or
(ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kAnyPrintMask) != 0);
}
inline bool isUnquotedString(const char* s)
{
bool result = isOrdinary(*s++);
while (result and *s != 0)
{
result = isNonBlank(*s);
++s;
}
return result;
}
// --------------------------------------------------------------------
std::tuple<std::string,std::string> splitTagName(const std::string& tag);
// --------------------------------------------------------------------
// sac Parser, analogous to SAX Parser (simple api for xml)
class SacParser
{
public:
SacParser(std::istream& is);
virtual ~SacParser() {}
enum CIFToken
{
eCIFTokenUnknown,
eCIFTokenEOF,
eCIFTokenDATA,
eCIFTokenLOOP,
eCIFTokenGLOBAL,
eCIFTokenSAVE,
eCIFTokenSTOP,
eCIFTokenTag,
eCIFTokenValue,
};
static const char* kTokenName[];
enum CIFValueType
{
eCIFValueInt,
eCIFValueFloat,
eCIFValueNumeric,
eCIFValueString,
eCIFValueTextField,
eCIFValueInapplicable,
eCIFValueUnknown
};
static const char* kValueName[];
int getNextChar();
void retract();
void restart();
CIFToken getNextToken();
void match(CIFToken token);
void parseFile();
void parseGlobal();
void parseDataBlock();
virtual void parseSaveFrame();
void parseDictionary();
void error(const std::string& msg);
// production methods, these are pure virtual here
virtual void produceDatablock(const std::string& name) = 0;
virtual void produceCategory(const std::string& name) = 0;
virtual void produceRow() = 0;
virtual void produceItem(const std::string& category, const std::string& item, const std::string& value) = 0;
protected:
enum State
{
eStateStart,
eStateWhite,
eStateComment,
eStateQuestionMark,
eStateDot,
eStateQuotedString,
eStateQuotedStringQuote,
eStateUnquotedString,
eStateTag,
eStateTextField,
eStateFloat = 100,
eStateInt = 110,
// eStateNumericSuffix = 200,
eStateValue = 300
};
std::istream& mData;
// Parser state
bool mValidate;
uint32_t mLineNr;
bool mBol;
int mState, mStart;
CIFToken mLookahead;
std::string mTokenValue;
CIFValueType mTokenType;
std::stack<int> mBuffer;
};
// --------------------------------------------------------------------
class Parser : public SacParser
{
public:
Parser(std::istream& is, File& f);
virtual void produceDatablock(const std::string& name);
virtual void produceCategory(const std::string& name);
virtual void produceRow();
virtual void produceItem(const std::string& category, const std::string& item, const std::string& value);
protected:
File& mFile;
Datablock* mDataBlock;
Datablock::iterator mCat;
Row mRow;
};
// --------------------------------------------------------------------
class DictParser : public Parser
{
public:
DictParser(Validator& validator, std::istream& is);
~DictParser();
void loadDictionary();
private:
virtual void parseSaveFrame();
bool collectItemTypes();
void linkItems();
Validator& mValidator;
File mFile;
struct DictParserDataImpl* mImpl;
bool mCollectedItemTypes = false;
};
}

View File

@@ -1,194 +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.
*/
#pragma once
#include "cif++/Cif++.hpp"
#include <regex>
#include <set>
namespace cif
{
struct ValidateCategory;
// --------------------------------------------------------------------
class ValidationError : public std::exception
{
public:
ValidationError(const std::string& msg);
ValidationError(const std::string& cat, const std::string& item,
const std::string& msg);
const char* what() const noexcept { return mMsg.c_str(); }
std::string mMsg;
};
// --------------------------------------------------------------------
enum class DDL_PrimitiveType
{
Char, UChar, Numb
};
DDL_PrimitiveType mapToPrimitiveType(const std::string& s);
struct ValidateType
{
std::string mName;
DDL_PrimitiveType mPrimitiveType;
std::regex mRx;
bool operator<(const ValidateType& rhs) const
{
return icompare(mName, rhs.mName) < 0;
}
// compare values based on type
// int compare(const std::string& a, const std::string& b) const
// {
// return compare(a.c_str(), b.c_str());
// }
int compare(const char* a, const char* b) const;
};
struct ValidateItem
{
std::string mTag;
bool mMandatory;
const ValidateType* mType;
cif::iset mEnums;
std::string mDefault;
bool mDefaultIsNull;
ValidateCategory* mCategory = nullptr;
// ItemLinked is used for non-key links
struct ItemLinked
{
ValidateItem* mParent;
std::string mParentItem;
std::string mChildItem;
};
std::vector<ItemLinked> mLinked;
bool operator<(const ValidateItem& rhs) const
{
return icompare(mTag, rhs.mTag) < 0;
}
bool operator==(const ValidateItem& rhs) const
{
return iequals(mTag, rhs.mTag);
}
void operator()(std::string value) const;
};
struct ValidateCategory
{
std::string mName;
std::vector<std::string> mKeys;
cif::iset mGroups;
cif::iset mMandatoryFields;
std::set<ValidateItem> mItemValidators;
bool operator<(const ValidateCategory& rhs) const
{
return icompare(mName, rhs.mName) < 0;
}
void addItemValidator(ValidateItem&& v);
const ValidateItem* getValidatorForItem(std::string tag) const;
const std::set<ValidateItem>& itemValidators() const
{
return mItemValidators;
}
};
struct ValidateLink
{
int mLinkGroupID;
std::string mParentCategory;
std::vector<std::string> mParentKeys;
std::string mChildCategory;
std::vector<std::string> mChildKeys;
std::string mLinkGroupLabel;
};
// --------------------------------------------------------------------
class Validator
{
public:
friend class DictParser;
Validator();
~Validator();
Validator(const Validator& rhs) = delete;
Validator& operator=(const Validator& rhs) = delete;
Validator(Validator&& rhs);
Validator& operator=(Validator&& rhs);
void addTypeValidator(ValidateType&& v);
const ValidateType* getValidatorForType(std::string typeCode) const;
void addCategoryValidator(ValidateCategory&& v);
const ValidateCategory* getValidatorForCategory(std::string category) const;
void addLinkValidator(ValidateLink&& v);
std::vector<const ValidateLink*> getLinksForParent(const std::string& category) const;
std::vector<const ValidateLink*> getLinksForChild(const std::string& category) const;
void reportError(const std::string& msg, bool fatal);
std::string dictName() const { return mName; }
void dictName(const std::string& name) { mName = name; }
std::string dictVersion() const { return mVersion; }
void dictVersion(const std::string& version) { mVersion = version; }
private:
// name is fully qualified here:
ValidateItem* getValidatorForItem(std::string name) const;
std::string mName;
std::string mVersion;
bool mStrict = false;
// std::set<uint32_t> mSubCategories;
std::set<ValidateType> mTypeValidators;
std::set<ValidateCategory> mCategoryValidators;
std::vector<ValidateLink> mLinkValidators;
};
}

View File

@@ -1,342 +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.
*/
#pragma once
#include <set>
#include <tuple>
#include <vector>
#include <map>
#include "cif++/AtomType.hpp"
#include "cif++/Cif++.hpp"
namespace mmcif
{
// --------------------------------------------------------------------
// The chemical composition of the structure in an mmCIF file is
// defined in the class composition. A compositon consists of
// entities. Each Entity can be either a polymer, a non-polymer
// a macrolide or a water molecule.
// Entities themselves are made up of compounds. And compounds
// contain CompoundAtom records for each atom.
class Compound;
class Link;
struct CompoundAtom;
enum BondType { singleBond, doubleBond, tripleBond, delocalizedBond };
// --------------------------------------------------------------------
// struct containing information about an atom in a chemical compound
// This information comes from the CCP4 monomer library.
struct CompoundAtom
{
std::string id;
AtomType typeSymbol;
std::string typeEnergy;
float partialCharge;
};
// --------------------------------------------------------------------
// struct containing information about the bonds
// This information comes from the CCP4 monomer library.
struct CompoundBond
{
std::string atomID[2];
BondType type;
float distance;
float esd;
};
// --------------------------------------------------------------------
// struct containing information about the bond-angles
// This information comes from the CCP4 monomer library.
struct CompoundAngle
{
std::string atomID[3];
float angle;
float esd;
};
// --------------------------------------------------------------------
// struct containing information about the bond-angles
// This information comes from the CCP4 monomer library.
struct CompoundTorsion
{
std::string atomID[4];
float angle;
float esd;
int period;
};
// --------------------------------------------------------------------
// struct containing information about the bond-angles
// This information comes from the CCP4 monomer library.
struct CompoundPlane
{
std::string id;
std::vector<std::string> atomID;
float esd;
};
// --------------------------------------------------------------------
// struct containing information about a chiral centre
// This information comes from the CCP4 monomer library.
enum ChiralVolumeSign { negativ, positiv, both };
struct CompoundChiralCentre
{
std::string id;
std::string atomIDCentre;
std::string atomID[3];
ChiralVolumeSign volumeSign;
};
// --------------------------------------------------------------------
// a class that contains information about a chemical compound.
// This information is derived from the ccp4 monomer library by default.
// To create compounds, you'd best use the factory method.
class Compound
{
public:
Compound(const std::string& file, const std::string& id, const std::string& name,
const std::string& group);
// factory method, create a Compound based on the three letter code
// (for amino acids) or the one-letter code (for bases) or the
// code as it is known in the CCP4 monomer library.
static const Compound* create(const std::string& id);
// this second factory method can create a Compound even if it is not
// recorded in the library. It will take the values from the CCP4 lib
// unless the value passed to this function is not empty.
static const Compound* create(const std::string& id, const std::string& name,
const std::string& type, const std::string& formula);
// add an additional path to the monomer library.
static void addMonomerLibraryPath(const std::string& dir);
// accessors
std::string id() const { return mID; }
std::string name() const { return mName; }
std::string type() const;
std::string group() const { return mGroup; }
std::vector<CompoundAtom> atoms() const { return mAtoms; }
std::vector<CompoundBond> bonds() const { return mBonds; }
std::vector<CompoundAngle> angles() const { return mAngles; }
std::vector<CompoundChiralCentre> chiralCentres() const
{ return mChiralCentres; }
std::vector<CompoundPlane> planes() const { return mPlanes; }
std::vector<CompoundTorsion> torsions() const { return mTorsions; }
CompoundAtom getAtomByID(const std::string& atomID) const;
bool atomsBonded(const std::string& atomId_1, const std::string& atomId_2) const;
float atomBondValue(const std::string& atomId_1, const std::string& atomId_2) const;
float bondAngle(const std::string& atomId_1, const std::string& atomId_2, const std::string& atomId_3) const;
float chiralVolume(const std::string& centreID) const;
std::string formula() const;
float formulaWeight() const;
int charge() const;
bool isWater() const;
bool isSugar() const;
std::vector<std::string> isomers() const;
bool isIsomerOf(const Compound& c) const;
std::vector<std::tuple<std::string,std::string>> mapToIsomer(const Compound& c) const;
private:
~Compound();
cif::File mCF;
std::string mID;
std::string mName;
std::string mGroup;
std::vector<CompoundAtom> mAtoms;
std::vector<CompoundBond> mBonds;
std::vector<CompoundAngle> mAngles;
std::vector<CompoundTorsion>mTorsions;
std::vector<CompoundChiralCentre>
mChiralCentres;
std::vector<CompoundPlane> mPlanes;
};
// --------------------------------------------------------------------
// struct containing information about the bonds
// This information comes from the CCP4 monomer library.
struct LinkAtom
{
int compID;
std::string atomID;
bool operator==(const LinkAtom& rhs) const { return compID == rhs.compID and atomID == rhs.atomID; }
};
struct LinkBond
{
LinkAtom atom[2];
BondType type;
float distance;
float esd;
};
// --------------------------------------------------------------------
// struct containing information about the bond-angles
// This information comes from the CCP4 monomer library.
struct LinkAngle
{
LinkAtom atom[3];
float angle;
float esd;
};
// --------------------------------------------------------------------
// struct containing information about the bond-torsions
// This information comes from the CCP4 monomer library.
struct LinkTorsion
{
LinkAtom atom[4];
float angle;
float esd;
int period;
};
// --------------------------------------------------------------------
// struct containing information about the bond-angles
// This information comes from the CCP4 monomer library.
struct LinkPlane
{
std::string id;
std::vector<LinkAtom> atoms;
float esd;
};
// --------------------------------------------------------------------
// struct containing information about a chiral centre
// This information comes from the CCP4 monomer library.
struct LinkChiralCentre
{
std::string id;
LinkAtom atomCentre;
LinkAtom atom[3];
ChiralVolumeSign volumeSign;
};
// --------------------------------------------------------------------
// a class that contains information about a chemical link between compounds.
// This information is derived from the ccp4 monomer library by default.
class Link
{
public:
Link(cif::Datablock& db);
// Factory method.
static const Link& create(const std::string& id);
// accessors
std::string id() const { return mID; }
std::vector<LinkBond> bonds() const { return mBonds; }
std::vector<LinkAngle> angles() const { return mAngles; }
std::vector<LinkChiralCentre> chiralCentres() const { return mChiralCentres; }
std::vector<LinkPlane> planes() const { return mPlanes; }
std::vector<LinkTorsion> torsions() const { return mTorsions; }
float atomBondValue(const LinkAtom& atomId_1, const LinkAtom& atomId_2) const;
float bondAngle(const LinkAtom& atomId_1, const LinkAtom& atomId_2, const LinkAtom& atomId_3) const;
float chiralVolume(const std::string& id) const;
private:
~Link();
std::string mID;
std::vector<LinkBond> mBonds;
std::vector<LinkAngle> mAngles;
std::vector<LinkTorsion> mTorsions;
std::vector<LinkChiralCentre> mChiralCentres;
std::vector<LinkPlane> mPlanes;
};
// --------------------------------------------------------------------
// Factory class for Compound and Link objects
extern const std::map<std::string,char> kAAMap, kBaseMap;
class CompoundFactory
{
public:
static CompoundFactory& instance();
void pushDictionary(const std::string& inDictFile);
void popDictionary();
bool isKnownPeptide(const std::string& res_name) const;
bool isKnownBase(const std::string& res_name) const;
std::string unalias(const std::string& res_name) const;
const Compound* get(std::string id);
const Compound* create(std::string id);
const Link* getLink(std::string id);
const Link* createLink(std::string id);
private:
CompoundFactory();
~CompoundFactory();
CompoundFactory(const CompoundFactory&) = delete;
CompoundFactory& operator=(const CompoundFactory&) = delete;
static CompoundFactory* sInstance;
class CompoundFactoryImpl* mImpl;
};
}

View File

@@ -1,97 +0,0 @@
/* include/cif++/Config.hpp.in. Generated from configure.ac by autoheader. */
/* define if the Boost library is available */
#undef HAVE_BOOST
/* define if the Boost::Date_Time library is available */
#undef HAVE_BOOST_DATE_TIME
/* define if the Boost::IOStreams library is available */
#undef HAVE_BOOST_IOSTREAMS
/* define if the compiler supports basic C++17 syntax */
#undef HAVE_CXX17
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the `floor' function. */
#undef HAVE_FLOOR
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `pow' function. */
#undef HAVE_POW
/* Define to 1 if the system has the type `ptrdiff_t'. */
#undef HAVE_PTRDIFF_T
/* Define to 1 if you have the `rint' function. */
#undef HAVE_RINT
/* Define to 1 if you have the `sqrt' function. */
#undef HAVE_SQRT
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strchr' function. */
#undef HAVE_STRCHR
/* Define to 1 if you have the `strerror' function. */
#undef HAVE_STRERROR
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS

View File

@@ -1,420 +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.
*/
#pragma once
#include <functional>
#if HAVE_LIBCLIPPER
#include <clipper/core/coords.h>
#endif
#include "cif++/Config.hpp"
#include <boost/math/quaternion.hpp>
namespace mmcif
{
typedef boost::math::quaternion<float> quaternion;
const long double
kPI = 3.141592653589793238462643383279502884L;
// --------------------------------------------------------------------
// Point, a location with x, y and z coordinates as floating point.
// This one is derived from a tuple<float,float,float> so
// you can do things like:
//
// float x, y, z;
// tie(x, y, z) = atom.loc();
template<typename F>
struct PointF
{
typedef F FType;
FType mX, mY, mZ;
PointF() : mX(0), mY(0), mZ(0) {}
PointF(FType x, FType y, FType z) : mX(x), mY(y), mZ(z) {}
template<typename PF>
PointF(const PointF<PF>& pt)
: mX(static_cast<F>(pt.mX))
, mY(static_cast<F>(pt.mY))
, mZ(static_cast<F>(pt.mZ)) {}
#if HAVE_LIBCLIPPER
PointF(const clipper::Coord_orth& pt): mX(pt[0]), mY(pt[1]), mZ(pt[2]) {}
PointF& operator=(const clipper::Coord_orth& rhs)
{
mX = rhs[0];
mY = rhs[1];
mZ = rhs[2];
return *this;
}
#endif
template<typename PF>
PointF& operator=(const PointF<PF>& rhs)
{
mX = static_cast<F>(rhs.mX);
mY = static_cast<F>(rhs.mY);
mZ = static_cast<F>(rhs.mZ);
return *this;
}
FType& getX() { return mX; }
FType getX() const { return mX; }
void setX(FType x) { mX = x; }
FType& getY() { return mY; }
FType getY() const { return mY; }
void setY(FType y) { mY = y; }
FType& getZ() { return mZ; }
FType getZ() const { return mZ; }
void setZ(FType z) { mZ = z; }
PointF& operator+=(const PointF& rhs)
{
mX += rhs.mX;
mY += rhs.mY;
mZ += rhs.mZ;
return *this;
}
PointF& operator+=(FType d)
{
mX += d;
mY += d;
mZ += d;
return *this;
}
PointF& operator-=(const PointF& rhs)
{
mX -= rhs.mX;
mY -= rhs.mY;
mZ -= rhs.mZ;
return *this;
}
PointF& operator-=(FType d)
{
mX -= d;
mY -= d;
mZ -= d;
return *this;
}
PointF& operator*=(FType rhs)
{
mX *= rhs;
mY *= rhs;
mZ *= rhs;
return *this;
}
PointF& operator/=(FType rhs)
{
mX /= rhs;
mY /= rhs;
mZ /= rhs;
return *this;
}
FType normalize()
{
auto length = mX * mX + mY * mY + mZ * mZ;
if (length > 0)
{
length = std::sqrt(length);
operator/=(length);
}
return length;
}
void rotate(const boost::math::quaternion<FType>& q)
{
boost::math::quaternion<FType> p(0, mX, mY, mZ);
p = q * p * boost::math::conj(q);
mX = p.R_component_2();
mY = p.R_component_3();
mZ = p.R_component_4();
}
#if HAVE_LIBCLIPPER
operator clipper::Coord_orth() const
{
return clipper::Coord_orth(mX, mY, mZ);
}
#endif
operator std::tuple<const FType&, const FType&, const FType&>() const
{
return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
}
operator std::tuple<FType&,FType&,FType&>()
{
return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
}
bool operator==(const PointF& rhs) const
{
return mX == rhs.mX and mY == rhs.mY and mZ == rhs.mZ;
}
// consider point as a vector... perhaps I should rename Point?
FType lengthsq() const
{
return mX * mX + mY * mY + mZ * mZ;
}
FType length() const
{
return sqrt(mX * mX + mY * mY + mZ * mZ);
}
};
typedef PointF<float> Point;
typedef PointF<double> DPoint;
template<typename F>
inline std::ostream& operator<<(std::ostream& os, const PointF<F>& pt)
{
os << '(' << pt.mX << ',' << pt.mY << ',' << pt.mZ << ')';
return os;
}
template<typename F>
inline PointF<F> operator+(const PointF<F>& lhs, const PointF<F>& rhs)
{
return PointF<F>(lhs.mX + rhs.mX, lhs.mY + rhs.mY, lhs.mZ + rhs.mZ);
}
template<typename F>
inline PointF<F> operator-(const PointF<F>& lhs, const PointF<F>& rhs)
{
return PointF<F>(lhs.mX - rhs.mX, lhs.mY - rhs.mY, lhs.mZ - rhs.mZ);
}
template<typename F>
inline PointF<F> operator-(const PointF<F>& pt)
{
return PointF<F>(-pt.mX, -pt.mY, -pt.mZ);
}
template<typename F>
inline PointF<F> operator*(const PointF<F>& pt, F f)
{
return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
}
template<typename F>
inline PointF<F> operator*(F f, const PointF<F>& pt)
{
return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
}
template<typename F>
inline PointF<F> operator/(const PointF<F>& pt, F f)
{
return PointF<F>(pt.mX / f, pt.mY / f, pt.mZ / f);
}
// --------------------------------------------------------------------
// several standard 3d operations
template<typename F>
inline double DistanceSquared(const PointF<F>& a, const PointF<F>& b)
{
return
(a.mX - b.mX) * (a.mX - b.mX) +
(a.mY - b.mY) * (a.mY - b.mY) +
(a.mZ - b.mZ) * (a.mZ - b.mZ);
}
template<typename F>
inline double Distance(const PointF<F>& a, const PointF<F>& b)
{
return sqrt(
(a.mX - b.mX) * (a.mX - b.mX) +
(a.mY - b.mY) * (a.mY - b.mY) +
(a.mZ - b.mZ) * (a.mZ - b.mZ));
}
template<typename F>
inline F DotProduct(const PointF<F>& a, const PointF<F>& b)
{
return a.mX * b.mX + a.mY * b.mY + a.mZ * b.mZ;
}
template<typename F>
inline PointF<F> CrossProduct(const PointF<F>& a, const PointF<F>& b)
{
return PointF<F>(a.mY * b.mZ - b.mY * a.mZ,
a.mZ * b.mX - b.mZ * a.mX,
a.mX * b.mY - b.mX * a.mY);
}
template<typename F>
double Angle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3)
{
PointF<F> v1 = p1 - p2;
PointF<F> v2 = p3 - p2;
return std::acos(DotProduct(v1, v2) / (v1.length() * v2.length())) * 180 / kPI;
}
template<typename F>
double DihedralAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
{
PointF<F> v12 = p1 - p2; // vector from p2 to p1
PointF<F> v43 = p4 - p3; // vector from p3 to p4
PointF<F> z = p2 - p3; // vector from p3 to p2
PointF<F> p = CrossProduct(z, v12);
PointF<F> x = CrossProduct(z, v43);
PointF<F> y = CrossProduct(z, x);
double u = DotProduct(x, x);
double v = DotProduct(y, y);
double result = 360;
if (u > 0 and v > 0)
{
u = DotProduct(p, x) / sqrt(u);
v = DotProduct(p, y) / sqrt(v);
if (u != 0 or v != 0)
result = atan2(v, u) * 180 / kPI;
}
return result;
}
template<typename F>
double CosinusAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
{
PointF<F> v12 = p1 - p2;
PointF<F> v34 = p3 - p4;
double result = 0;
double x = DotProduct(v12, v12) * DotProduct(v34, v34);
if (x > 0)
result = DotProduct(v12, v34) / sqrt(x);
return result;
}
// --------------------------------------------------------------------
// For e.g. simulated annealing, returns a new point that is moved in
// a random direction with a distance randomly chosen from a normal
// distribution with a stddev of offset.
template<typename F>
PointF<F> Nudge(PointF<F> p, F offset);
// --------------------------------------------------------------------
// We use quaternions to do rotations in 3d space
quaternion Normalize(quaternion q);
//std::tuple<double,Point> QuaternionToAngleAxis(quaternion q);
Point Centroid(std::vector<Point>& Points);
Point CenterPoints(std::vector<Point>& Points);
quaternion AlignPoints(const std::vector<Point>& a, const std::vector<Point>& b);
double RMSd(const std::vector<Point>& a, const std::vector<Point>& b);
// --------------------------------------------------------------------
// Helper class to generate evenly divided Points on a sphere
// we use a fibonacci sphere to calculate even distribution of the dots
template<int N>
class SphericalDots
{
public:
enum { P = 2 * N + 1 };
typedef typename std::array<Point,P> array_type;
typedef typename array_type::const_iterator iterator;
static SphericalDots& instance()
{
static SphericalDots sInstance;
return sInstance;
}
size_t size() const { return mPoints.size(); }
const Point operator[](uint32_t inIx) const { return mPoints[inIx]; }
iterator begin() const { return mPoints.begin(); }
iterator end() const { return mPoints.end(); }
double weight() const { return mWeight; }
SphericalDots()
{
const double
kGoldenRatio = (1 + std::sqrt(5.0)) / 2;
mWeight = (4 * kPI) / P;
auto p = mPoints.begin();
for (int32_t i = -N; i <= N; ++i)
{
double lat = std::asin((2.0 * i) / P);
double lon = std::fmod(i, kGoldenRatio) * 2 * kPI / kGoldenRatio;
p->mX = sin(lon) * cos(lat);
p->mY = cos(lon) * cos(lat);
p->mZ = sin(lat);
++p;
}
}
private:
array_type mPoints;
double mWeight;
};
typedef SphericalDots<50> SphericalDots_50;
}

View File

@@ -1,218 +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.
*/
// Calculate DSSP-like secondary structure information
#pragma once
namespace mmcif
{
class Structure;
class Monomer;
struct Res;
extern const float
kCouplingConstant, kMinHBondEnergy, kMaxHBondEnergy;
enum SecondaryStructureType : char
{
ssLoop = ' ',
ssAlphahelix = 'H',
ssBetabridge = 'B',
ssStrand = 'E',
ssHelix_3 = 'G',
ssHelix_5 = 'I',
ssHelix_PPII = 'P',
ssTurn = 'T',
ssBend = 'S'
};
enum class HelixType
{
rh_3_10, rh_alpha, rh_pi, rh_pp
};
enum class Helix
{
None, Start, End, StartAndEnd, Middle
};
//struct HBond
//{
// std::string labelAsymID;
// int labelSeqID;
// double energy;
//};
//
//struct BridgePartner
//{
// std::string labelAsymID;
// int labelSeqID;
// int ladder;
// bool parallel;
//};
struct SecondaryStructure
{
SecondaryStructureType type;
// HBond donor[2], acceptor[2];
// BridgePartner beta[2];
// int sheet;
// bool bend;
};
//void CalculateSecondaryStructure(Structure& s);
const size_t
kHistogramSize = 30;
struct DSSP_Statistics
{
uint32_t nrOfResidues, nrOfChains, nrOfSSBridges, nrOfIntraChainSSBridges, nrOfHBonds;
uint32_t nrOfHBondsInAntiparallelBridges, nrOfHBondsInParallelBridges;
uint32_t nrOfHBondsPerDistance[11] = {};
double accessibleSurface = 0;
uint32_t residuesPerAlphaHelixHistogram[kHistogramSize] = {};
uint32_t parallelBridgesPerLadderHistogram[kHistogramSize] = {};
uint32_t antiparallelBridgesPerLadderHistogram[kHistogramSize] = {};
uint32_t laddersPerSheetHistogram[kHistogramSize] = {};
};
enum class ChainBreak
{
None, NewChain, Gap
};
class DSSP
{
public:
DSSP(const Structure& s, int min_poly_proline_stretch_length, bool calculateSurfaceAccessibility);
~DSSP();
DSSP(const DSSP&) = delete;
DSSP& operator=(const DSSP&) = delete;
SecondaryStructureType operator()(const std::string& inAsymID, int inSeqID) const;
SecondaryStructureType operator()(const Monomer& m) const;
double accessibility(const std::string& inAsymID, int inSeqID) const;
double accessibility(const Monomer& m) const;
bool isAlphaHelixEndBeforeStart(const Monomer& m) const;
bool isAlphaHelixEndBeforeStart(const std::string& inAsymID, int inSeqID) const;
DSSP_Statistics GetStatistics() const;
class iterator;
using res_iter = typename std::vector<Res>::iterator;
class ResidueInfo
{
public:
friend class iterator;
explicit operator bool() const { return not empty(); }
bool empty() const { return mImpl == nullptr; }
const Monomer& residue() const;
std::string alt_id() const;
/// \brief return 0 if not a break, ' ' in case of a new chain and '*' in case of a broken chain
ChainBreak chainBreak() const;
/// \brief the internal number in DSSP
int nr() const;
SecondaryStructureType ss() const;
int ssBridgeNr() const;
Helix helix(HelixType helixType) const;
bool bend() const;
double accessibility() const;
/// \brief returns resinfo, ladder and parallel
std::tuple<ResidueInfo,int,bool> bridgePartner(int i) const;
int sheet() const;
/// \brief return resinfo and the energy of the bond
std::tuple<ResidueInfo,double> acceptor(int i) const;
std::tuple<ResidueInfo,double> donor(int i) const;
private:
ResidueInfo(Res* res);
Res* mImpl;
};
class iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = ResidueInfo;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
iterator(const iterator& i);
iterator(res_iter cur);
iterator& operator=(const iterator& i);
reference operator*() { return mCurrent; }
pointer operator->() { return &mCurrent; }
iterator& operator++();
iterator operator++(int)
{
auto tmp(*this);
this->operator++();
return tmp;
}
bool operator==(const iterator& rhs) const { return mCurrent.mImpl == rhs.mCurrent.mImpl; }
bool operator!=(const iterator& rhs) const { return mCurrent.mImpl != rhs.mCurrent.mImpl; }
private:
ResidueInfo mCurrent;
};
iterator begin() const;
iterator end() const;
bool empty() const { return begin() == end(); }
private:
struct DSSPImpl* mImpl;
};
}

View File

@@ -1,512 +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.
*/
#pragma once
#include <numeric>
#include "cif++/AtomType.hpp"
#include "cif++/Point.hpp"
#include "cif++/Compound.hpp"
#include "cif++/Cif++.hpp"
/*
To modify a structure, you will have to use actions.
The currently supported actions are:
// - Move atom to new location
- Remove atom
// - Add new atom that was formerly missing
// - Add alternate Residue
-
*/
namespace mmcif
{
class Atom;
class Residue;
class Monomer;
class Polymer;
class Structure;
class File;
// --------------------------------------------------------------------
class Atom
{
public:
// Atom(const structure& s, const std::string& id);
Atom();
Atom(struct AtomImpl* impl);
Atom(const Atom& rhs);
// a special constructor to create symmetry copies
Atom(const Atom& rhs, const Point& symmmetry_location, const std::string& symmetry_operation);
~Atom();
explicit operator bool() const { return mImpl_ != nullptr; }
// return a copy of this atom, with data copied instead of referenced
Atom clone() const;
Atom& operator=(const Atom& rhs);
const std::string& id() const;
AtomType type() const;
Point location() const;
void location(Point p);
// for direct access to underlying data, be careful!
const cif::Row getRow() const;
const cif::Row getRowAniso() const;
// Atom symmetryCopy(const Point& d, const clipper::RTop_orth& rt);
bool isSymmetryCopy() const;
std::string symmetry() const;
// const clipper::RTop_orth& symop() const;
const Compound& comp() const;
bool isWater() const;
int charge() const;
float uIso() const;
bool getAnisoU(float anisou[6]) const;
float occupancy() const;
template<typename T>
T property(const std::string& name) const;
void property(const std::string& name, const std::string& value);
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
void property(const std::string& name, const T& value)
{
property(name, std::to_string(value));
}
// specifications
std::string labelAtomID() const;
std::string labelCompID() const;
std::string labelAsymID() const;
int labelSeqID() const;
std::string labelAltID() const;
bool isAlternate() const;
std::string authAtomID() const;
std::string authCompID() const;
std::string authAsymID() const;
std::string authSeqID() const;
std::string pdbxAuthInsCode() const;
std::string pdbxAuthAltID() const;
std::string labelID() const;// label_comp_id + '_' + label_asym_id + '_' + label_seq_id
std::string pdbID() const; // auth_comp_id + '_' + auth_asym_id + '_' + auth_seq_id + pdbx_PDB_ins_code
bool operator==(const Atom& rhs) const;
// // get clipper format Atom
// clipper::Atom toClipper() const;
// Radius calculation based on integrating the density until perc of electrons is found
void calculateRadius(float resHigh, float resLow, float perc);
float radius() const;
// access data in compound for this atom
// the energy-type field
std::string energyType() const;
// convenience routine
bool isBackBone() const
{
auto atomID = labelAtomID();
return atomID == "N" or atomID == "O" or atomID == "C" or atomID == "CA";
}
void swap(Atom& b)
{
std::swap(mImpl_, b.mImpl_);
}
int compare(const Atom& b) const;
bool operator<(const Atom& rhs) const
{
return compare(rhs) < 0;
}
friend std::ostream& operator<<(std::ostream& os, const Atom& atom);
private:
friend class Structure;
void setID(int id);
AtomImpl* impl();
const AtomImpl* impl() const;
struct AtomImpl* mImpl_;
};
inline void swap(mmcif::Atom& a, mmcif::Atom& b)
{
a.swap(b);
}
inline double Distance(const Atom& a, const Atom& b)
{
return Distance(a.location(), b.location());
}
inline double DistanceSquared(const Atom& a, const Atom& b)
{
return DistanceSquared(a.location(), b.location());
}
typedef std::vector<Atom> AtomView;
// --------------------------------------------------------------------
class Residue
{
public:
// constructors should be private, but that's not possible for now (needed in emplace)
// constructor for waters
Residue(const Structure& structure, const std::string& compoundID,
const std::string& asymID, const std::string& authSeqID);
Residue(const Structure& structure, const std::string& compoundID,
const std::string& asymID, int seqID = 0);
Residue(const Residue& rhs) = delete;
Residue& operator=(const Residue& rhs) = delete;
Residue(Residue&& rhs);
Residue& operator=(Residue&& rhs);
virtual ~Residue();
const Compound& compound() const;
const AtomView& atoms() const;
/// \brief Unique atoms returns only the atoms without alternates and the first of each alternate atom id.
AtomView unique_atoms() const;
/// \brief The alt ID used for the unique atoms
std::string unique_alt_id() const;
Atom atomByID(const std::string& atomID) const;
const std::string& compoundID() const { return mCompoundID; }
const std::string& asymID() const { return mAsymID; }
int seqID() const { return mSeqID; }
std::string authAsymID() const;
std::string authSeqID() const;
std::string authInsCode() const;
// return a human readable PDB-like auth id (chain+seqnr+iCode)
std::string authID() const;
// similar for mmCIF space
std::string labelID() const;
// Is this residue a single entity?
bool isEntity() const;
bool isWater() const { return mCompoundID == "HOH"; }
bool isSugar() const;
bool isPyranose() const;
bool isFuranose() const;
const Structure& structure() const { return *mStructure; }
bool empty() const { return mStructure == nullptr; }
bool hasAlternateAtoms() const;
/// \brief Return the list of unique alt ID's present in this residue
std::set<std::string> getAlternateIDs() const;
/// \brief Return the list of unique atom ID's
std::set<std::string> getAtomIDs() const;
/// \brief Return the list of atoms having ID \a atomID
AtomView getAtomsByID(const std::string& atomID) const;
// some routines for 3d work
std::tuple<Point,float> centerAndRadius() const;
friend std::ostream& operator<<(std::ostream& os, const Residue& res);
protected:
Residue() {}
friend class Polymer;
const Structure* mStructure = nullptr;
std::string mCompoundID, mAsymID;
int mSeqID = 0;
std::string mAuthSeqID;
AtomView mAtoms;
};
// --------------------------------------------------------------------
// a monomer models a single Residue in a protein chain
class Monomer : public Residue
{
public:
// Monomer();
Monomer(const Monomer& rhs) = delete;
Monomer& operator=(const Monomer& rhs) = delete;
Monomer(Monomer&& rhs);
Monomer& operator=(Monomer&& rhs);
// Monomer(const Polymer& polymer, uint32_t index);
Monomer(const Polymer& polymer, uint32_t index, int seqID,
const std::string& compoundID);
bool is_first_in_chain() const;
bool is_last_in_chain() const;
// convenience
bool has_alpha() const;
bool has_kappa() const;
// Assuming this is really an amino acid...
float phi() const;
float psi() const;
float alpha() const;
float kappa() const;
float tco() const;
float omega() const;
// torsion angles
size_t nrOfChis() const;
float chi(size_t i) const;
bool isCis() const;
/// \brief Returns true if the four atoms C, CA, N and O are present
bool isComplete() const;
/// \brief Returns true if any of the backbone atoms has an alternate
bool hasAlternateBackboneAtoms() const;
Atom CAlpha() const { return atomByID("CA"); }
Atom C() const { return atomByID("C"); }
Atom N() const { return atomByID("N"); }
Atom O() const { return atomByID("O"); }
Atom H() const { return atomByID("H"); }
bool isBondedTo(const Monomer& rhs) const
{
return this != &rhs and areBonded(*this, rhs);
}
static bool areBonded(const Monomer& a, const Monomer& b, float errorMargin = 0.5f);
static bool isCis(const Monomer& a, const Monomer& b);
static float omega(const Monomer& a, const Monomer& b);
// for LEU and VAL
float chiralVolume() const;
private:
const Polymer* mPolymer;
uint32_t mIndex;
};
// --------------------------------------------------------------------
class Polymer : public std::vector<Monomer>
{
public:
Polymer(const Structure& s, const std::string& entityID, const std::string& asymID);
Polymer(const Polymer&) = delete;
Polymer& operator=(const Polymer&) = delete;
// Polymer(Polymer&& rhs) = delete;
// Polymer& operator=(Polymer&& rhs) = de;
Monomer& getBySeqID(int seqID);
const Monomer& getBySeqID(int seqID) const;
Structure* structure() const { return mStructure; }
std::string asymID() const { return mAsymID; }
std::string entityID() const { return mEntityID; }
std::string chainID() const;
int Distance(const Monomer& a, const Monomer& b) const;
private:
Structure* mStructure;
std::string mEntityID;
std::string mAsymID;
cif::RowSet mPolySeq;
};
// --------------------------------------------------------------------
// file is a reference to the data stored in e.g. the cif file.
// This object is not copyable.
class File : public std::enable_shared_from_this<File>
{
public:
File();
File(const std::string& path);
~File();
File(const File&) = delete;
File& operator=(const File&) = delete;
void load(const std::string& path);
void save(const std::string& path);
Structure* model(size_t nr = 1);
struct FileImpl& impl() const { return *mImpl; }
cif::Datablock& data();
cif::File& file();
private:
struct FileImpl* mImpl;
};
// --------------------------------------------------------------------
enum class StructureOpenOptions
{
SkipHydrogen = 1 << 0
};
inline bool operator&(StructureOpenOptions a, StructureOpenOptions b)
{
return static_cast<int>(a) bitand static_cast<int>(b);
}
// --------------------------------------------------------------------
class Structure
{
public:
Structure(File& p, uint32_t modelNr = 1, StructureOpenOptions options = {});
Structure& operator=(const Structure&) = delete;
~Structure();
// Create a read-only clone of the current structure (for multithreaded calculations that move atoms)
Structure(const Structure&);
File& getFile() const;
const AtomView& atoms() const { return mAtoms; }
AtomView waters() const;
const std::list<Polymer>& polymers() const { return mPolymers; }
std::list<Polymer>& polymers() { return mPolymers; }
const std::vector<Residue>& nonPolymers() const { return mNonPolymers; }
const std::vector<Residue>& branchResidues() const { return mBranchResidues; }
Atom getAtomByID(std::string id) const;
// Atom getAtomByLocation(Point pt, float maxDistance) const;
Atom getAtomByLabel(const std::string& atomID, const std::string& asymID,
const std::string& compID, int seqID, const std::string& altID = "");
const Residue& getResidue(const std::string& asymID, const std::string& compID, int seqID) const;
// map between auth and label locations
std::tuple<std::string,int,std::string> MapAuthToLabel(const std::string& asymID,
const std::string& seqID, const std::string& compID, const std::string& insCode = "");
std::tuple<std::string,std::string,std::string,std::string> MapLabelToAuth(
const std::string& asymID, int seqID, const std::string& compID);
// returns chain, seqnr, icode
std::tuple<char,int,char> MapLabelToAuth(
const std::string& asymID, int seqID) const;
// returns chain,seqnr,comp,iCode
std::tuple<std::string,int,std::string,std::string> MapLabelToPDB(
const std::string& asymID, int seqID, const std::string& compID,
const std::string& authSeqID) const;
std::tuple<std::string,int,std::string> MapPDBToLabel(
const std::string& asymID, int seqID, const std::string& compID, const std::string& iCode) const;
// Actions
void removeAtom(Atom& a);
void swapAtoms(Atom& a1, Atom& a2); // swap the labels for these atoms
void moveAtom(Atom& a, Point p); // move atom to a new location
void changeResidue(const Residue& res, const std::string& newCompound,
const std::vector<std::tuple<std::string,std::string>>& remappedAtoms);
/// To sort the atoms in order of model > asym-id > res-id > atom-id
/// Will asssign new atom_id's to all atoms. Be carefull
void sortAtoms();
const std::vector<Residue>& getNonPolymers() const { return mNonPolymers; }
const std::vector<Residue>& getBranchResidues() const { return mBranchResidues; }
private:
friend Polymer;
friend Residue;
// friend residue_view;
// friend residue_iterator;
cif::Category& category(const char* name) const;
cif::Datablock& datablock() const;
void insertCompound(const std::string& compoundID, bool isEntity);
void loadData();
void updateAtomIndex();
File& mFile;
uint32_t mModelNr;
AtomView mAtoms;
std::vector<size_t> mAtomIndex;
std::list<Polymer> mPolymers;
std::vector<Residue> mNonPolymers, mBranchResidues;
};
}

View File

@@ -1,114 +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.
*/
#pragma once
#include <cstdint>
#include <array>
namespace mmcif
{
// --------------------------------------------------------------------
struct Spacegroup
{
const char* name;
const char* xHM;
const char* Hall;
int nr;
};
extern const Spacegroup kSpaceGroups[];
extern const std::size_t kNrOfSpaceGroups;
// --------------------------------------------------------------------
struct SymopData
{
constexpr SymopData(const std::array<int,15>& data)
: m_packed((data[ 0] & 0x03ULL) << 34 bitor
(data[ 1] & 0x03ULL) << 32 bitor
(data[ 2] & 0x03ULL) << 30 bitor
(data[ 3] & 0x03ULL) << 28 bitor
(data[ 4] & 0x03ULL) << 26 bitor
(data[ 5] & 0x03ULL) << 24 bitor
(data[ 6] & 0x03ULL) << 22 bitor
(data[ 7] & 0x03ULL) << 20 bitor
(data[ 8] & 0x03ULL) << 18 bitor
(data[ 9] & 0x07ULL) << 15 bitor
(data[10] & 0x07ULL) << 12 bitor
(data[11] & 0x07ULL) << 9 bitor
(data[12] & 0x07ULL) << 6 bitor
(data[13] & 0x07ULL) << 3 bitor
(data[14] & 0x07ULL) << 0)
{
}
bool operator==(const SymopData& rhs) const
{
return m_packed == rhs.m_packed;
}
private:
friend struct SymopDataBlock;
const uint64_t kPackMask = (~0ULL >> (64-36));
SymopData(uint64_t v)
: m_packed(v bitand kPackMask) {}
uint64_t m_packed;
};
struct SymopDataBlock
{
constexpr SymopDataBlock(int spacegroup, int rotational_number, const std::array<int,15>& rt_data)
: m_v((spacegroup & 0xffffULL) << 48 bitor
(rotational_number & 0xffULL) << 40 bitor
SymopData(rt_data).m_packed)
{
}
uint16_t spacegroup() const { return m_v >> 48; }
SymopData symop() const { return SymopData(m_v); }
uint8_t rotational_number() const { return (m_v >> 40) bitand 0xff; }
private:
uint64_t m_v;
};
static_assert(sizeof(SymopDataBlock) == sizeof(uint64_t), "Size of SymopData is wrong");
extern const SymopDataBlock kSymopNrTable[];
extern const std::size_t kSymopNrTableSize;
// --------------------------------------------------------------------
int GetSpacegroupNumber(std::string spacegroup); // alternative for clipper's parsing code
}

277
include/cif++/atom_type.hpp Normal file
View File

@@ -0,0 +1,277 @@
/*-
* 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.
*/
// Lib for working with structures as contained in mmCIF and PDB files
#pragma once
#include <cstdint>
#include <stdexcept>
#include <string>
namespace cif
{
enum atom_type : uint8_t
{
Nn = 0, // Unknown
H = 1, // Hydro­gen
He = 2, // He­lium
Li = 3, // Lith­ium
Be = 4, // Beryl­lium
B = 5, // Boron
C = 6, // Carbon
N = 7, // Nitro­gen
O = 8, // Oxy­gen
F = 9, // Fluor­ine
Ne = 10, // Neon
Na = 11, // So­dium
Mg = 12, // Magne­sium
Al = 13, // Alumin­ium
Si = 14, // Sili­con
P = 15, // Phos­phorus
S = 16, // Sulfur
Cl = 17, // Chlor­ine
Ar = 18, // Argon
K = 19, // Potas­sium
Ca = 20, // Cal­cium
Sc = 21, // Scan­dium
Ti = 22, // Tita­nium
V = 23, // Vana­dium
Cr = 24, // Chrom­ium
Mn = 25, // Manga­nese
Fe = 26, // Iron
Co = 27, // Cobalt
Ni = 28, // Nickel
Cu = 29, // Copper
Zn = 30, // Zinc
Ga = 31, // Gallium
Ge = 32, // Germa­nium
As = 33, // Arsenic
Se = 34, // Sele­nium
Br = 35, // Bromine
Kr = 36, // Kryp­ton
Rb = 37, // Rubid­ium
Sr = 38, // Stront­ium
Y = 39, // Yttrium
Zr = 40, // Zirco­nium
Nb = 41, // Nio­bium
Mo = 42, // Molyb­denum
Tc = 43, // Tech­netium
Ru = 44, // Ruthe­nium
Rh = 45, // Rho­dium
Pd = 46, // Pallad­ium
Ag = 47, // Silver
Cd = 48, // Cad­mium
In = 49, // Indium
Sn = 50, // Tin
Sb = 51, // Anti­mony
Te = 52, // Tellurium
I = 53, // Iodine
Xe = 54, // Xenon
Cs = 55, // Cae­sium
Ba = 56, // Ba­rium
La = 57, // Lan­thanum
Hf = 72, // Haf­nium
Ta = 73, // Tanta­lum
W = 74, // Tung­sten
Re = 75, // Rhe­nium
Os = 76, // Os­mium
Ir = 77, // Iridium
Pt = 78, // Plat­inum
Au = 79, // Gold
Hg = 80, // Mer­cury
Tl = 81, // Thallium
Pb = 82, // Lead
Bi = 83, // Bis­muth
Po = 84, // Polo­nium
At = 85, // Asta­tine
Rn = 86, // Radon
Fr = 87, // Fran­cium
Ra = 88, // Ra­dium
Ac = 89, // Actin­ium
Rf = 104, // Ruther­fordium
Db = 105, // Dub­nium
Sg = 106, // Sea­borgium
Bh = 107, // Bohr­ium
Hs = 108, // Has­sium
Mt = 109, // Meit­nerium
Ds = 110, // Darm­stadtium
Rg = 111, // Roent­genium
Cn = 112, // Coper­nicium
Nh = 113, // Nihon­ium
Fl = 114, // Flerov­ium
Mc = 115, // Moscov­ium
Lv = 116, // Liver­morium
Ts = 117, // Tenness­ine
Og = 118, // Oga­nesson
Ce = 58, // Cerium
Pr = 59, // Praseo­dymium
Nd = 60, // Neo­dymium
Pm = 61, // Prome­thium
Sm = 62, // Sama­rium
Eu = 63, // Europ­ium
Gd = 64, // Gadolin­ium
Tb = 65, // Ter­bium
Dy = 66, // Dyspro­sium
Ho = 67, // Hol­mium
Er = 68, // Erbium
Tm = 69, // Thulium
Yb = 70, // Ytter­bium
Lu = 71, // Lute­tium
Th = 90, // Thor­ium
Pa = 91, // Protac­tinium
U = 92, // Ura­nium
Np = 93, // Neptu­nium
Pu = 94, // Pluto­nium
Am = 95, // Ameri­cium
Cm = 96, // Curium
Bk = 97, // Berkel­ium
Cf = 98, // Califor­nium
Es = 99, // Einstei­nium
Fm = 100, // Fer­mium
Md = 101, // Mende­levium
No = 102, // Nobel­ium
Lr = 103, // Lawren­cium
D = 129, // Deuterium
};
// --------------------------------------------------------------------
// atom_type_info
enum class radius_type
{
calculated,
empirical,
covalent_empirical,
single_bond,
double_bond,
triple_bond,
van_der_waals,
type_count
};
constexpr size_t kRadiusTypeCount = static_cast<size_t>(radius_type::type_count);
enum class ionic_radius_type
{
effective, crystal
};
struct atom_type_info
{
atom_type type;
std::string name;
std::string symbol;
float weight;
bool metal;
float radii[kRadiusTypeCount];
};
extern const atom_type_info kKnownAtoms[];
// --------------------------------------------------------------------
// AtomTypeTraits
class atom_type_traits
{
public:
atom_type_traits(atom_type a);
atom_type_traits(const std::string &symbol);
atom_type type() const { return m_info->type; }
std::string name() const { return m_info->name; }
std::string symbol() const { return m_info->symbol; }
float weight() const { return m_info->weight; }
bool is_metal() const { return m_info->metal; }
static bool is_element(const std::string &symbol);
static bool is_metal(const std::string &symbol);
float radius(radius_type type = radius_type::single_bond) const
{
if (type >= radius_type::type_count)
throw std::invalid_argument("invalid radius requested");
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
///
/// \param charge The charge of the ion
/// \return The radius of the ion
float crystal_ionic_radius(int charge) const;
/// \brief Return the radius for a charged version of this atom in a non-solid environment
///
/// \param charge The charge of the ion
/// \return The radius of the ion
float effective_ionic_radius(int charge) const;
/// \brief Return the radius for a charged version of this atom, returns the effective radius by default
///
/// \param charge The charge of the ion
/// \return The radius of the ion
float ionic_radius(int charge, ionic_radius_type type = ionic_radius_type::effective) const
{
return type == ionic_radius_type::effective ? effective_ionic_radius(charge) : crystal_ionic_radius(charge);
}
// data type encapsulating the Waasmaier & Kirfel scattering factors
// in a simplified form (only a and b).
// Added the electrion scattering factors as well
struct SFData
{
double a[6], b[6];
};
// to get the Cval and Siva values, use this constant as charge:
enum
{
kWKSFVal = -99
};
const SFData &wksf(int charge = 0) const;
const SFData &elsf() const;
private:
const struct atom_type_info *m_info;
};
} // namespace pdbx

587
include/cif++/category.hpp Normal file
View File

@@ -0,0 +1,587 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <array>
#include <cif++/forward_decl.hpp>
#include <cif++/condition.hpp>
#include <cif++/iterator.hpp>
#include <cif++/row.hpp>
#include <cif++/validate.hpp>
// 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 category
{
public:
friend class row_handle;
template <typename, typename...>
friend class iterator_impl;
using value_type = row_handle;
using reference = value_type;
using const_reference = const value_type;
using iterator = iterator_impl<category>;
using const_iterator = iterator_impl<const category>;
category() = default;
category(std::string_view name);
category(const category &rhs);
category(category &&rhs);
category &operator=(const category &rhs);
category &operator=(category &&rhs);
~category();
// --------------------------------------------------------------------
const std::string &name() const { return m_name; }
iset fields() const;
std::set<uint16_t> key_field_indices() const;
void set_validator(const validator *v, datablock &db);
void update_links(datablock &db);
const validator *get_validator() const { return m_validator; }
const category_validator *get_cat_validator() const { return m_cat_validator; }
bool is_valid() const;
bool validate_links() const;
bool operator==(const category &rhs) const;
bool operator!=(const category &rhs) const
{
return not operator==(rhs);
}
// --------------------------------------------------------------------
reference front()
{
return {*this, *m_head};
}
const_reference front() const
{
return {const_cast<category &>(*this), const_cast<row &>(*m_head)};
}
reference back()
{
return {*this, *m_tail};
}
const_reference back() const
{
return {const_cast<category &>(*this), const_cast<row &>(*m_tail)};
}
iterator begin()
{
return {*this, m_head};
}
iterator end()
{
return {*this, nullptr};
}
const_iterator begin() const
{
return {*this, m_head};
}
const_iterator end() const
{
return {*this, nullptr};
}
const_iterator cbegin() const
{
return {*this, m_head};
}
const_iterator cend() const
{
return {*this, nullptr};
}
size_t size() const
{
return std::distance(cbegin(), cend());
}
bool empty() const
{
return m_head == nullptr;
}
// --------------------------------------------------------------------
// A category can have a key, as defined by the validator/dictionary
/// @brief The key 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, 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);
const row_handle operator[](const key_type &key) const
{
return const_cast<category *>(this)->operator[](key);
}
// --------------------------------------------------------------------
template <typename... Ts, typename... Ns>
iterator_proxy<const category, Ts...> rows(Ns... names) const
{
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...});
}
template <typename... Ts, typename... Ns>
iterator_proxy<category, Ts...> rows(Ns... names)
{
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...});
}
// --------------------------------------------------------------------
conditional_iterator_proxy<category> find(condition &&cond)
{
return find(begin(), std::forward<condition>(cond));
}
conditional_iterator_proxy<category> find(iterator pos, condition &&cond)
{
return {*this, pos, std::forward<condition>(cond)};
}
conditional_iterator_proxy<const category> find(condition &&cond) const
{
return find(cbegin(), std::forward<condition>(cond));
}
conditional_iterator_proxy<const category> find(const_iterator pos, condition &&cond) const
{
return conditional_iterator_proxy<const category>{*this, pos, std::forward<condition>(cond)};
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(condition &&cond, Ns... names)
{
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::forward<condition>(cond), std::forward<Ns>(names)...);
}
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 column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Ns>(names)...);
}
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 column titles should be equal to the number of types to return");
return {*this, pos, std::forward<condition>(cond), std::forward<Ns>(names)...};
}
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 column titles should be equal to the number of types to return");
return {*this, pos, std::forward<condition>(cond), std::forward<Ns>(names)...};
}
// --------------------------------------------------------------------
// if you only expect a single row
row_handle find1(condition &&cond)
{
return find1(begin(), std::forward<condition>(cond));
}
row_handle find1(iterator pos, condition &&cond)
{
auto h = find(pos, std::forward<condition>(cond));
return h.size() != 1 ? row_handle{} : *h.begin();
}
const row_handle find1(condition &&cond) const
{
return find1(cbegin(), std::forward<condition>(cond));
}
const row_handle find1(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::forward<condition>(cond));
return h.size() != 1 ? row_handle{} : *h.begin();
}
template <typename T>
T find1(condition &&cond, const char *column) const
{
return find1<T>(cbegin(), std::forward<condition>(cond), column);
}
template <typename T>
T find1(const_iterator pos, condition &&cond, const char *column) const
{
auto h = find<T>(pos, std::forward<condition>(cond), column);
return h.size() == 1 ? *h.begin() : T{};
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(condition &&cond, Cs... columns) const
{
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::forward<condition>(cond), std::forward<Cs>(columns)...);
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... columns) const
{
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::forward<condition>(cond), std::forward<Cs>(columns)...);
return h.size() == 1 ? *h.begin() : std::tuple<Ts...>{};
}
bool exists(condition &&cond) const
{
bool result = false;
if (cond)
{
cond.prepare(*this);
auto sh = cond.single();
if (sh.has_value() and *sh)
result = true;
else
{
for (auto r : *this)
{
if (cond(r))
{
result = true;
break;
}
}
}
}
return result;
}
// --------------------------------------------------------------------
bool has_children(row_handle r) const;
bool has_parents(row_handle r) const;
std::vector<row_handle> get_children(row_handle r, const category &childCat) const;
std::vector<row_handle> get_parents(row_handle r, const category &parentCat) const;
std::vector<row_handle> get_linked(row_handle r, const category &cat) const;
// --------------------------------------------------------------------
// void insert(const_iterator pos, const row_initializer &row)
// {
// insert_impl(pos, row);
// }
// void insert(const_iterator pos, row_initializer &&row)
// {
// insert_impl(pos, std::move(row));
// }
iterator erase(iterator pos);
void erase(row_handle rh)
{
erase(iterator(*this, rh.m_row));
}
size_t erase(condition &&cond);
size_t erase(condition &&cond, std::function<void(row_handle)> &&visit);
iterator emplace(row_initializer &&ri)
{
return this->emplace(ri.begin(), ri.end());
}
template <typename ItemIter>
iterator emplace(ItemIter b, ItemIter e)
{
row *r = this->create_row();
try
{
for (auto i = b; i != e; ++i)
{
// item_value *new_item = this->create_item(*i);
r->append(add_column(i->name()), { i->value() });
}
}
catch (...)
{
if (r != nullptr)
this->delete_row(r);
throw;
}
return insert_impl(cend(), r);
}
void clear();
// --------------------------------------------------------------------
/// \brief generate a new, unique ID. Pass it an ID generating function
/// based on a sequence number. This function will be called until the
/// 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);
std::string get_unique_id(const std::string &prefix)
{
return get_unique_id([prefix](int nr)
{ return prefix + std::to_string(nr + 1); });
}
// --------------------------------------------------------------------
/// \brief Rename a single column 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 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, tag, value);
}
void update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view value);
// --------------------------------------------------------------------
/// \brief Return the index number for \a column_name
uint16_t get_column_ix(std::string_view column_name) const
{
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 << std::endl;
}
return result;
}
std::string_view get_column_name(uint16_t ix) const
{
if (ix >= m_columns.size())
throw std::out_of_range("column index is out of range");
return m_columns[ix].m_name;
}
uint16_t add_column(std::string_view column_name)
{
using namespace std::literals;
size_t result = get_column_ix(column_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;
}
bool has_column(std::string_view name) const
{
return get_column_ix(name) < m_columns.size();
}
// --------------------------------------------------------------------
void sort(std::function<int(row_handle,row_handle)> f);
void reorder_by_index();
// --------------------------------------------------------------------
std::vector<std::string> get_tag_order() const;
void write(std::ostream &os) const;
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 includeEmptyColumns) const;
public:
friend std::ostream &operator<<(std::ostream &os, const category &cat)
{
cat.write(os);
return os;
}
private:
void update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate = true);
private:
void erase_orphans(condition &&cond, category &parent);
using allocator_type = std::allocator<void>;
constexpr allocator_type get_allocator() const
{
return {};
}
using char_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<char>;
using char_allocator_traits = std::allocator_traits<char_allocator_type>;
using row_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<row>;
using row_allocator_traits = std::allocator_traits<row_allocator_type>;
row_allocator_traits::pointer get_row()
{
row_allocator_type ra(get_allocator());
return row_allocator_traits::allocate(ra, 1);
}
row *create_row()
{
auto p = this->get_row();
row_allocator_type ra(get_allocator());
row_allocator_traits::construct(ra, p);
return p;
}
row *clone_row(const row &r);
void delete_row(row *r);
row_handle create_copy(row_handle r);
struct item_column
{
std::string m_name;
const item_validator *m_validator;
item_column(std::string_view name, const item_validator *validator)
: m_name(name)
, m_validator(validator)
{
}
};
struct link
{
link(category *linked, const link_validator *v)
: linked(linked)
, v(v)
{
}
category *linked;
const link_validator *v;
};
// proxy methods for every insertion
iterator insert_impl(const_iterator pos, row *n);
iterator erase_impl(const_iterator pos);
// --------------------------------------------------------------------
condition get_parents_condition(row_handle rh, const category &parentCat) const;
condition get_children_condition(row_handle rh, const category &childCat) const;
// --------------------------------------------------------------------
void swap_item(size_t column_ix, row_handle &a, row_handle &b);
// --------------------------------------------------------------------
std::string m_name;
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;
bool m_cascade = true;
uint32_t m_last_unique_num = 0;
class category_index *m_index = nullptr;
row *m_head = nullptr, *m_tail = nullptr;
};
} // namespace cif

191
include/cif++/compound.hpp Normal file
View File

@@ -0,0 +1,191 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020-2022 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
/// \file This file contains the definition for the class compound, encapsulating
/// the information found for compounds in the CCD.
#include <map>
#include <set>
#include <tuple>
#include <vector>
#include <cif++.hpp>
#include <cif++/atom_type.hpp>
namespace cif
{
// --------------------------------------------------------------------
class compound;
struct compound_atom;
class compound_factory_impl;
/// \brief The bond type as defined in the CCD, possible values taken from the mmcif_pdbx file
enum class bond_type
{
sing, // 'single bond'
doub, // 'double bond'
trip, // 'triple bond'
quad, // 'quadruple bond'
arom, // 'aromatic bond'
poly, // 'polymeric bond'
delo, // 'delocalized double bond'
pi, // 'pi bond'
};
std::string to_string(bond_type bondType);
bond_type from_string(const std::string &bondType);
/// --------------------------------------------------------------------
/// \brief struct containing information about an atom in a chemical compound.
/// This is a subset of the available information. Contact the author if you need more fields.
struct compound_atom
{
std::string id;
atom_type type_symbol;
int charge = 0;
bool aromatic = false;
bool leaving_atom = false;
bool stereo_config = false;
float x, y, z;
};
/// --------------------------------------------------------------------
/// \brief struct containing information about the bonds
struct compound_bond
{
std::string atom_id[2];
bond_type type;
bool aromatic = false, stereo_config = false;
};
/// --------------------------------------------------------------------
/// \brief a class that contains information about a chemical compound.
/// 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 addExtraComponents function and
/// pass it a valid CCD formatted file.
class compound
{
public:
// accessors
std::string id() const { return m_id; }
std::string name() const { return m_name; }
std::string type() const { return m_type; }
std::string group() const { return m_group; }
std::string formula() const { return m_formula; }
float formula_weight() const { return m_formula_weight; }
int formal_charge() const { return m_formal_charge; }
const std::vector<compound_atom> &atoms() const { return m_atoms; }
const std::vector<compound_bond> &bonds() const { return m_bonds; }
compound_atom get_atom_by_atom_id(const std::string &atom_id) const;
bool atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const;
bool is_water() const
{
return m_id == "HOH" or m_id == "H2O" or m_id == "WAT";
}
private:
friend class compound_factory_impl;
friend class CCD_compound_factory_impl;
friend class CCP4_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;
float m_formula_weight = 0;
int m_formal_charge = 0;
std::vector<compound_atom> m_atoms;
std::vector<compound_bond> m_bonds;
};
// --------------------------------------------------------------------
// Factory class for compound and Link objects
class compound_factory
{
public:
/// \brief Initialise a singleton instance.
///
/// If you have a multithreaded application and want to have different
/// compounds in each thread (e.g. a web service processing user requests
/// with different sets of compounds) you can set the \a useThreadLocalInstanceOnly
/// flag to true.
static void init(bool useThreadLocalInstanceOnly);
static compound_factory &instance();
static void clear();
void set_default_dictionary(const std::filesystem::path &inDictFile);
void push_dictionary(const std::filesystem::path &inDictFile);
void pop_dictionary();
bool is_known_peptide(const std::string &res_name) const;
bool is_known_base(const std::string &res_name) const;
/// \brief Create the compound object for \a id
///
/// This will create the compound instance for \a id if it doesn't exist already.
/// 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 id);
~compound_factory();
static const std::map<std::string, char> kAAMap, kBaseMap;
private:
compound_factory();
compound_factory(const compound_factory &) = delete;
compound_factory &operator=(const compound_factory &) = delete;
static std::unique_ptr<compound_factory> s_instance;
static thread_local std::unique_ptr<compound_factory> tl_instance;
static bool s_use_thread_local_instance;
std::shared_ptr<compound_factory_impl> m_impl;
};
} // namespace pdbx

745
include/cif++/condition.hpp Normal file
View File

@@ -0,0 +1,745 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <cassert>
#include <functional>
#include <iostream>
#include <regex>
#include <utility>
#include <cif++/row.hpp>
namespace cif
{
// --------------------------------------------------------------------
// let's make life easier
iset get_category_fields(const category &cat);
uint16_t get_column_ix(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
namespace detail
{
struct condition_impl
{
virtual ~condition_impl() {}
virtual condition_impl *prepare(const category &c) { return this; }
virtual bool test(row_handle r) const = 0;
virtual void str(std::ostream &os) const = 0;
virtual std::optional<row_handle> single() const { return {}; };
};
struct all_condition_impl : public condition_impl
{
bool test(row_handle r) const override { return true; }
void str(std::ostream &os) const override { os << "*"; }
};
struct or_condition_impl;
struct and_condition_impl;
struct not_condition_impl;
} // namespace detail
class condition
{
public:
using condition_impl = detail::condition_impl;
condition()
: m_impl(nullptr)
{
}
explicit condition(condition_impl *impl)
: m_impl(impl)
{
}
condition(const condition &) = delete;
condition(condition &&rhs) noexcept
: m_impl(nullptr)
{
std::swap(m_impl, rhs.m_impl);
}
condition &operator=(const condition &) = delete;
condition &operator=(condition &&rhs) noexcept
{
std::swap(m_impl, rhs.m_impl);
return *this;
}
~condition()
{
delete m_impl;
m_impl = nullptr;
}
void prepare(const category &c);
bool operator()(row_handle r) const
{
assert(this->m_impl != nullptr);
assert(this->m_prepared);
return m_impl ? m_impl->test(r) : false;
}
explicit operator bool() { return not empty(); }
bool empty() const { return m_impl == nullptr; }
std::optional<row_handle> single() const
{
return m_impl ? m_impl->single() : std::optional<row_handle>();
}
friend condition operator||(condition &&a, condition &&b);
friend condition operator&&(condition &&a, condition &&b);
friend struct detail::or_condition_impl;
friend struct detail::and_condition_impl;
friend struct detail::not_condition_impl;
void swap(condition &rhs)
{
std::swap(m_impl, rhs.m_impl);
std::swap(m_prepared, rhs.m_prepared);
}
friend std::ostream &operator<<(std::ostream &os, const condition &cond)
{
if (cond.m_impl)
cond.m_impl->str(os);
return os;
}
private:
void optimise(condition_impl *&impl);
condition_impl *m_impl;
bool m_prepared = false;
};
namespace detail
{
struct key_is_empty_condition_impl : public condition_impl
{
key_is_empty_condition_impl(const std::string &item_tag)
: m_item_tag(item_tag)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
return this;
}
bool test(row_handle r) const override
{
return r[m_item_ix].empty();
}
void str(std::ostream &os) const override
{
os << m_item_tag << " IS NULL";
}
std::string m_item_tag;
size_t m_item_ix = 0;
};
struct key_equals_condition_impl : public condition_impl
{
key_equals_condition_impl(item &&i)
: m_item_tag(i.name())
, m_value(i.value())
{
}
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, m_icase) == 0;
}
void str(std::ostream &os) const override
{
os << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value;
}
virtual std::optional<row_handle> single() const override
{
return m_single_hit;
}
std::string m_item_tag;
size_t m_item_ix = 0;
bool m_icase = false;
std::string m_value;
std::optional<row_handle> m_single_hit;
};
struct key_equals_or_empty_condition_impl : public condition_impl
{
key_equals_or_empty_condition_impl(key_equals_condition_impl *equals, key_is_empty_condition_impl *empty)
: m_item_tag(equals->m_item_tag)
, m_value(equals->m_value)
, m_icase(equals->m_icase)
, m_single_hit(equals->m_single_hit)
{
assert(empty->m_item_ix == equals->m_item_ix);
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
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, m_icase) == 0;
return result;
}
void str(std::ostream &os) const override
{
os << m_item_tag << (m_icase ? "^ " : " ") << " == " << m_value << " OR " << m_item_tag << " IS NULL";
}
virtual std::optional<row_handle> single() const override
{
return m_single_hit;
}
std::string m_item_tag;
size_t m_item_ix = 0;
std::string m_value;
bool m_icase = false;
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_tag, COMP &&comp, const std::string &s)
: m_item_tag(item_tag)
, m_compare(std::move(comp))
, m_str(s)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
return this;
}
bool test(row_handle r) const override
{
return m_compare(r, m_icase);
}
void str(std::ostream &os) const override
{
os << m_item_tag << (m_icase ? "^ " : " ") << m_str;
}
std::string m_item_tag;
size_t m_item_ix = 0;
bool m_icase = false;
std::function<bool(row_handle, bool)> m_compare;
std::string m_str;
};
struct key_matches_condition_impl : public condition_impl
{
key_matches_condition_impl(const std::string &item_tag, const std::regex &rx)
: m_item_tag(item_tag)
, m_item_ix(0)
, mRx(rx)
{
}
condition_impl *prepare(const category &c) override
{
m_item_ix = get_column_ix(c, m_item_tag);
return this;
}
bool test(row_handle r) const override
{
std::string_view txt = r[m_item_ix].text();
return std::regex_match(txt.begin(), txt.end(), mRx);
}
void str(std::ostream &os) const override
{
os << m_item_tag << " =~ expression";
}
std::string m_item_tag;
size_t m_item_ix;
std::regex mRx;
};
template <typename T>
struct any_is_condition_impl : public condition_impl
{
typedef T valueType;
any_is_condition_impl(const valueType &value)
: mValue(value)
{
}
bool test(row_handle r) const override
{
auto &c = r.get_category();
bool result = false;
for (auto &f : get_category_fields(c))
{
try
{
if (r[f].compare(mValue) == 0)
{
result = true;
break;
}
}
catch (...)
{
}
}
return result;
}
void str(std::ostream &os) const override
{
os << "<any> == " << mValue;
}
valueType mValue;
};
struct any_matches_condition_impl : public condition_impl
{
any_matches_condition_impl(const std::regex &rx)
: mRx(rx)
{
}
bool test(row_handle r) const override
{
auto &c = r.get_category();
bool result = false;
for (auto &f : get_category_fields(c))
{
try
{
std::string_view txt = r[f].text();
if (std::regex_match(txt.begin(), txt.end(), mRx))
{
result = true;
break;
}
}
catch (...)
{
}
}
return result;
}
void str(std::ostream &os) const override
{
os << "<any> =~ expression";
}
std::regex mRx;
};
// TODO: Optimize and_condition by having a list of sub items.
// That way you can also collapse multiple _is_ conditions in
// case they make up an indexed tuple.
struct and_condition_impl : public condition_impl
{
and_condition_impl(condition &&a, condition &&b)
{
mSub.emplace_back(std::exchange(a.m_impl, nullptr));
mSub.emplace_back(std::exchange(b.m_impl, nullptr));
}
~and_condition_impl()
{
for (auto sub : mSub)
delete sub;
}
condition_impl *prepare(const category &c) override;
bool test(row_handle r) const override
{
bool result = true;
for (auto sub : mSub)
{
if (sub->test(r))
continue;
result = false;
break;
}
return result;
}
void str(std::ostream &os) const override
{
os << '(';
bool first = true;
for (auto sub : mSub)
{
if (first)
first = false;
else
os << " AND ";
sub->str(os);
}
os << ')';
}
virtual std::optional<row_handle> single() const override
{
std::optional<row_handle> result;
for (auto sub : mSub)
{
auto s = sub->single();
if (not result.has_value())
{
result = s;
continue;
}
if (s == result)
continue;
result.reset();
break;
}
return result;
}
std::vector<condition_impl *> mSub;
};
struct or_condition_impl : public condition_impl
{
or_condition_impl(condition &&a, condition &&b)
: mA(nullptr)
, mB(nullptr)
{
std::swap(mA, a.m_impl);
std::swap(mB, b.m_impl);
}
~or_condition_impl()
{
delete mA;
delete mB;
}
condition_impl *prepare(const category &c) override;
bool test(row_handle r) const override
{
return mA->test(r) or mB->test(r);
}
void str(std::ostream &os) const override
{
os << '(';
mA->str(os);
os << ") OR (";
mB->str(os);
os << ')';
}
virtual std::optional<row_handle> single() const override
{
auto sa = mA->single();
auto sb = mB->single();
if (sa.has_value() and sb.has_value() and sa != sb)
sa.reset();
else if (not sa.has_value())
sa = sb;
return sa;
}
condition_impl *mA;
condition_impl *mB;
};
struct not_condition_impl : public condition_impl
{
not_condition_impl(condition &&a)
: mA(nullptr)
{
std::swap(mA, a.m_impl);
}
~not_condition_impl()
{
delete mA;
}
condition_impl *prepare(const category &c) override
{
mA = mA->prepare(c);
return this;
}
bool test(row_handle r) const override
{
return not mA->test(r);
}
void str(std::ostream &os) const override
{
os << "NOT (";
mA->str(os);
os << ')';
}
condition_impl *mA;
};
} // namespace detail
inline condition operator&&(condition &&a, condition &&b)
{
if (a.m_impl and b.m_impl)
return condition(new detail::and_condition_impl(std::move(a), std::move(b)));
if (a.m_impl)
return condition(std::move(a));
return condition(std::move(b));
}
inline condition operator||(condition &&a, condition &&b)
{
if (a.m_impl and b.m_impl)
return condition(new detail::or_condition_impl(std::move(a), std::move(b)));
if (a.m_impl)
return condition(std::move(a));
return condition(std::move(b));
}
struct empty_type
{
};
/// \brief A helper to make it possible to have conditions like ("id"_key == cif::null)
inline constexpr empty_type null = empty_type();
struct key
{
explicit key(const std::string &itemTag)
: m_item_tag(itemTag)
{
}
explicit key(const char *itemTag)
: m_item_tag(itemTag)
{
}
key(const key &) = delete;
key &operator=(const key &) = delete;
std::string m_item_tag;
};
template <typename T>
condition operator==(const key &key, const T &v)
{
return condition(new detail::key_equals_condition_impl({ key.m_item_tag, v }));
}
inline condition operator==(const key &key, const char *value)
{
if (value != nullptr and *value != 0)
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_tag));
}
// inline condition_t operator==(const key& key, const detail::ItemReference& v)
// {
// if (v.empty())
// return condition_t(new detail::key_is_empty_condition_impl(key.m_item_tag));
// else
// return condition_t(new detail::key_compare_condition_impl(key.m_item_tag, [tag = key.m_item_tag, v](const category& c, const row& r, bool icase)
// { return r[tag].template compare<(v, icase) == 0; }));
// }
template <typename T>
condition operator!=(const key &key, const T &v)
{
return condition(new detail::not_condition_impl(operator==(key, v)));
}
inline condition operator!=(const key &key, const char *v)
{
std::string value(v ? v : "");
return condition(new detail::not_condition_impl(operator==(key, value)));
}
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_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; },
s.str()));
}
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_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; },
s.str()));
}
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_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; },
s.str()));
}
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_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; },
s.str()));
}
inline condition operator==(const key &key, const std::regex &rx)
{
return condition(new detail::key_matches_condition_impl(key.m_item_tag, rx));
}
inline condition operator==(const key &key, const empty_type &)
{
return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
}
inline condition operator !(condition &&rhs)
{
return condition(new detail::not_condition_impl(std::move(rhs)));
}
struct any_type
{
};
inline constexpr any_type any = any_type{};
template <typename T>
condition operator==(const any_type &, const T &v)
{
return condition(new detail::any_is_condition_impl<T>(v));
}
inline condition operator==(const any_type &, const std::regex &rx)
{
return condition(new detail::any_matches_condition_impl(rx));
}
inline condition all()
{
return condition(new detail::all_condition_impl());
}
namespace literals
{
inline key operator""_key(const char *text, size_t length)
{
return key(std::string(text, length));
}
} // namespace literals
} // namespace cif

100
include/cif++/datablock.hpp Normal file
View File

@@ -0,0 +1,100 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <cif++/forward_decl.hpp>
#include <cif++/category.hpp>
namespace cif
{
// --------------------------------------------------------------------
class datablock : public std::list<category>
{
public:
datablock() = default;
datablock(std::string_view name)
: m_name(name)
{
}
datablock(const datablock &) = default;
datablock(datablock &&) = default;
datablock &operator=(const datablock &) = default;
datablock &operator=(datablock &&) = default;
// --------------------------------------------------------------------
const std::string &name() const { return m_name; }
void set_name(std::string_view name)
{
m_name = name;
}
void set_validator(const validator *v);
const validator *get_validator() const;
bool is_valid() const;
bool validate_links() const;
// --------------------------------------------------------------------
category &operator[](std::string_view name);
const category &operator[](std::string_view name) const;
category *get(std::string_view name);
const category *get(std::string_view name) const;
std::tuple<iterator, bool> emplace(std::string_view name);
std::vector<std::string> get_tag_order() const;
void write(std::ostream &os) const;
void write(std::ostream &os, const std::vector<std::string> &tag_order);
friend std::ostream &operator<<(std::ostream &os, const datablock &db)
{
db.write(os);
return os;
}
// --------------------------------------------------------------------
bool operator==(const datablock &rhs) const;
private:
std::string m_name;
const validator *m_validator = nullptr;
};
} // namespace cif

View File

@@ -0,0 +1,36 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cif++/validate.hpp>
namespace cif
{
validator parse_dictionary(std::string_view name, std::istream &is);
} // namespace cif

122
include/cif++/file.hpp Normal file
View File

@@ -0,0 +1,122 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <list>
#include <cif++/datablock.hpp>
#include <cif++/parser.hpp>
namespace cif
{
// --------------------------------------------------------------------
class file : public std::list<datablock>
{
public:
file() = default;
explicit file(const std::filesystem::path &p)
{
load(p);
}
explicit file(std::istream &is)
{
load(is);
}
explicit file(const char *data, size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(data), length);
std::istream is(&buffer);
load(is);
}
file(const file &) = default;
file(file &&) = default;
file &operator=(const file &) = default;
file &operator=(file &&) = default;
void set_validator(const validator *v);
const validator *get_validator() const
{
return m_validator;
}
bool is_valid() const;
bool is_valid();
bool validate_links() const;
void load_dictionary();
void load_dictionary(std::string_view name);
bool contains(std::string_view name) const;
datablock &front()
{
assert(not empty());
return std::list<datablock>::front();
}
const datablock &front() const
{
assert(not empty());
return std::list<datablock>::front();
}
datablock &operator[](std::string_view name);
const datablock &operator[](std::string_view name) const;
std::tuple<iterator, bool> emplace(std::string_view name);
void load(const std::filesystem::path &p);
void load(std::istream &is);
void save(const std::filesystem::path &p) const;
void save(std::ostream &os) const;
friend std::ostream &operator<<(std::ostream &os, const file &f)
{
f.save(os);
return os;
}
private:
const validator *m_validator = nullptr;
};
} // namespace cif

221
include/cif++/format.hpp Normal file
View File

@@ -0,0 +1,221 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <string>
/// \file format.hpp
/// 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
{
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<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<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
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;
};
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
class fill_out_streambuf : public std::streambuf
{
public:
using base_type = std::streambuf;
using int_type = base_type::int_type;
using char_type = base_type::char_type;
using traits_type = base_type::traits_type;
fill_out_streambuf(std::ostream &os, int width = 80)
: m_os(os)
, m_upstream(os.rdbuf())
, m_width(width)
{
}
~fill_out_streambuf()
{
m_os.rdbuf(m_upstream);
}
virtual int_type
overflow(int_type ic = traits_type::eof())
{
char ch = traits_type::to_char_type(ic);
int_type result = ic;
if (ch == '\n')
{
for (int i = m_column_count; result != traits_type::eof() and i < m_width; ++i)
result = m_upstream->sputc(' ');
}
if (result != traits_type::eof())
result = m_upstream->sputc(ch);
if (result != traits_type::eof())
{
if (ch == '\n')
{
m_column_count = 0;
++m_line_count;
}
else
++m_column_count;
}
return result;
}
std::streambuf *get_upstream() const { return m_upstream; }
int get_line_count() const { return m_line_count; }
private:
std::ostream &m_os;
std::streambuf *m_upstream;
int m_width;
int m_line_count = 0;
int m_column_count = 0;
};
} // namespace pdbx

View File

@@ -0,0 +1,46 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <string>
#include <vector>
namespace cif
{
class category;
class datablock;
class file;
class parser;
class row;
class row_handle;
class item;
class item_handle;
} // namespace cif

566
include/cif++/item.hpp Normal file
View File

@@ -0,0 +1,566 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <charconv>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <memory>
#include <optional>
#include <cif++/forward_decl.hpp>
#include <cif++/text.hpp>
/// \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 field.
namespace cif
{
extern int VERBOSE;
// --------------------------------------------------------------------
/// \brief item is a transient class that is used to pass data into rows
/// but it also takes care of formatting data.
class item
{
public:
/// \brief Default constructor, empty item
item() = default;
/// \brief constructor for an item with name \a name and as
/// content a single character string with content \a value
item(std::string_view name, char value)
: m_name(name)
, m_value({ value })
{
}
/// \brief constructor for an item with name \a name and as
/// content a the formatted floating point value \a value with
/// precision \a precision
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
item(std::string_view name, const T &value, int precision)
: m_name(name)
{
using namespace std;
using namespace cif;
char buffer[32];
auto r = to_chars(buffer, buffer + sizeof(buffer) - 1, value, chars_format::fixed, precision);
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);
}
/// \brief constructor for an item with name \a name and as
/// content a formatted floating point value \a value with
/// so-called general formatting
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
item(const std::string_view name, const T &value)
: m_name(name)
{
using namespace std;
using namespace cif;
char buffer[32];
auto r = to_chars(buffer, buffer + sizeof(buffer) - 1, value, chars_format::general);
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);
}
/// \brief constructor for an item with name \a name and as
/// content a the formatted integral value \a value
template <typename T, std::enable_if_t<std::is_integral_v<T> and not std::is_same_v<T,bool>, int> = 0>
item(const std::string_view name, const T &value)
: m_name(name)
{
char buffer[32];
auto r = std::to_chars(buffer, buffer + sizeof(buffer) - 1, value);
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);
}
/// \brief constructor for an item with name \a name and as
/// content a the formatted boolean value \a value
template <typename T, std::enable_if_t<std::is_same_v<T,bool>, int> = 0>
item(const std::string_view name, const T &value)
: m_name(name)
{
m_value.assign(value ? "y" : "n");
}
/// \brief constructor for an item with name \a name and as
/// content value \a value
item(const std::string_view name, const std::string_view value)
: m_name(name)
, m_value(value)
{
}
item(const item &rhs) = default;
item(item &&rhs) noexcept = default;
item &operator=(const item &rhs) = default;
item &operator=(item &&rhs) noexcept = default;
std::string_view name() const { return m_name; }
std::string_view value() const { return m_value; }
/// \brief replace the content of the stored value with \a v
void value(std::string_view v) { m_value = v; }
/// \brief empty means either null or unknown
bool empty() const { return m_value.empty(); }
/// \brief returns true if the field contains '.'
bool is_null() const { return m_value == "."; }
/// \brief returns true if the field contains '?'
bool is_unknown() const { return m_value == "?"; }
/// \brief the length of the value string
size_t length() const { return m_value.length(); }
/// \brief support for structured binding
template<size_t N>
decltype(auto) get() const
{
if constexpr (N == 0) return name();
else if constexpr (N == 1) return value();
}
private:
std::string_view m_name;
std::string m_value;
};
// --------------------------------------------------------------------
/// \brief the internal storage for items in a category
///
/// Internal storage, strictly forward linked list with minimal space
/// requirements. Strings of size 7 or shorter are stored internally.
/// Typically, more than 99% of the strings in an mmCIF file are less
/// than 8 bytes in length.
struct item_value
{
item_value() = default;
/// \brief constructor
item_value(std::string_view text)
: m_length(text.length())
{
if (m_length >= kBufferSize)
{
m_data = new char[m_length + 1];
std::copy(text.begin(), text.end(), m_data);
m_data[m_length] = 0;
}
else
{
std::copy(text.begin(), text.end(), m_local_data);
m_local_data[m_length] = 0;
}
}
item_value(item_value &&rhs)
: m_length(std::exchange(rhs.m_length, 0))
, m_data(std::exchange(rhs.m_data, nullptr))
{
}
item_value &operator=(item_value &&rhs)
{
if (this != &rhs)
{
m_length = std::exchange(rhs.m_length, m_length);
m_data = std::exchange(rhs.m_data, m_data);
}
return *this;
}
~item_value()
{
if (m_length >= kBufferSize)
delete[] m_data;
m_data = nullptr;
m_length = 0;
}
item_value(const item_value &) = delete;
item_value &operator=(const item_value &) = delete;
explicit operator bool() const
{
return m_length != 0;
}
size_t m_length = 0;
union
{
char m_local_data[8];
char *m_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.
constexpr inline std::string_view text() const
{
return { m_length >= kBufferSize ? m_data : m_local_data, m_length };
}
};
// static_assert(sizeof(item_value) == 24, "sizeof(item_value) should be 24 bytes");
static_assert(sizeof(item_value) == 16, "sizeof(item_value) should be 16 bytes");
// --------------------------------------------------------------------
// Transient object to access stored data
/// \brief This is item_handle, it is used to access the data stored in item_value.
struct item_handle
{
public:
// conversion helper class
template <typename T, typename = void>
struct item_value_as;
template <typename T>
item_handle &operator=(const T &value)
{
item v{ "", value };
assign_value(v);
return *this;
}
template <typename... Ts>
void os(const Ts &...v)
{
std::ostringstream ss;
((ss << v), ...);
this->operator=(ss.str());
}
void swap(item_handle &b);
template <typename T = std::string>
auto as() const -> T
{
using value_type = std::remove_cv_t<std::remove_reference_t<T>>;
return item_value_as<value_type>::convert(*this);
}
template <typename T>
auto value_or(const T &dv) const
{
return empty() ? dv : this->as<T>();
}
template <typename T>
int compare(const T &value, bool icase = true) const
{
return item_value_as<T>::compare(*this, value, icase);
}
template <typename T>
bool operator==(const T &value) const
{
// TODO: icase or not icase?
return item_value_as<T>::compare(*this, value, true) == 0;
}
// We may not have C++20 yet...
template <typename T>
bool operator!=(const T &value) const
{
return not operator==(value);
}
// empty means either null or unknown
bool empty() const
{
auto txt = text();
return txt.empty() or (txt.length() == 1 and (txt.front() == '.' or txt.front() == '?'));
}
explicit operator bool() const { return not empty(); }
// is_null means the field contains '.'
bool is_null() const
{
auto txt = text();
return txt.length() == 1 and txt.front() == '.';
}
// is_unknown means the field contains '?'
bool is_unknown() const
{
auto txt = text();
return txt.length() == 1 and txt.front() == '?';
}
std::string_view text() const;
item_handle(uint16_t column, row_handle &row)
: m_column(column)
, m_row_handle(row)
{
}
static const item_handle s_null_item;
friend void swap(item_handle a, item_handle b)
{
a.swap(b);
}
private:
item_handle();
uint16_t m_column;
row_handle &m_row_handle;
void assign_value(const item &value);
};
// So sad that older gcc implementations of from_chars did not support floats yet...
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T> and not std::is_same_v<T, bool>>>
{
using value_type = std::remove_reference_t<std::remove_cv_t<T>>;
static value_type convert(const item_handle &ref)
{
value_type result = {};
if (not ref.empty())
{
auto txt = ref.text();
std::from_chars_result r = selected_charconv<value_type>::from_chars(txt.data(), txt.data() + txt.size(), result);
if (r.ec != std::errc())
{
result = {};
if (cif::VERBOSE)
{
if (r.ec == std::errc::invalid_argument)
std::cerr << "Attempt to convert " << std::quoted(txt) << " into a number" << std::endl;
else if (r.ec == std::errc::result_out_of_range)
std::cerr << "Conversion of " << std::quoted(txt) << " into a type that is too small" << std::endl;
}
}
}
return result;
}
static int compare(const item_handle &ref, const T &value, bool icase)
{
int result = 0;
auto txt = ref.text();
if (txt.empty())
result = 1;
else
{
value_type v = {};
std::from_chars_result r = selected_charconv<value_type>::from_chars(txt.data(), txt.data() + txt.size(), v);
if (r.ec != std::errc())
{
if (cif::VERBOSE)
{
if (r.ec == std::errc::invalid_argument)
std::cerr << "Attempt to convert " << std::quoted(txt) << " into a number" << std::endl;
else if (r.ec == std::errc::result_out_of_range)
std::cerr << "Conversion of " << std::quoted(txt) << " into a type that is too small" << std::endl;
}
result = 1;
}
else if (v < value)
result = -1;
else if (v > value)
result = 1;
}
return result;
}
};
template <typename T>
struct item_handle::item_value_as<std::optional<T>>
{
static std::optional<T> convert(const item_handle &ref)
{
std::optional<T> result;
if (ref)
result = ref.as<T>();
return result;
}
static int compare(const item_handle &ref, std::optional<T> value, bool icase)
{
if (ref.empty() and not value)
return 0;
if (ref.empty())
return -1;
else if (not value)
return 1;
else
return ref.compare(*value, icase);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>>
{
static bool convert(const item_handle &ref)
{
bool result = false;
if (not ref.empty())
result = iequals(ref.text(), "y");
return result;
}
static int compare(const item_handle &ref, bool value, bool icase)
{
bool rv = convert(ref);
return value && rv ? 0
: (rv < value ? -1 : 1);
}
};
template <size_t N>
struct item_handle::item_value_as<char[N]>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const char (&value)[N], bool icase)
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const char *value, bool icase)
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string_view>>>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const std::string_view &value, bool icase)
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
template <typename T>
struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>>
{
static std::string convert(const item_handle &ref)
{
if (ref.empty())
return {};
return { ref.text().data(), ref.text().size() };
}
static int compare(const item_handle &ref, const std::string &value, bool icase)
{
return icase ? cif::icompare(ref.text(), value) : ref.text().compare(value);
}
};
} // namespace cif
namespace std
{
template<> struct tuple_size<::cif::item>
: public std::integral_constant<std::size_t, 2> {};
template<> struct tuple_element<0, ::cif::item>
{
using type = decltype(std::declval<::cif::item>().name());
};
template<> struct tuple_element<1, ::cif::item>
{
using type = decltype(std::declval<::cif::item>().value());
};
}

676
include/cif++/iterator.hpp Normal file
View File

@@ -0,0 +1,676 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <cif++/row.hpp>
namespace cif
{
// --------------------------------------------------------------------
template <typename Category, typename... Ts>
class iterator_impl
{
public:
template <typename, typename...>
friend class iterator_impl;
friend class category;
static constexpr size_t N = sizeof...(Ts);
using category_type = std::remove_cv_t<Category>;
using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
using tuple_type = std::tuple<Ts...>;
using iterator_category = std::forward_iterator_tag;
using value_type = tuple_type;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default;
template <typename C2, typename... T2s>
iterator_impl(const iterator_impl<C2, T2s...> &rhs)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix)
{
}
template <typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value)
, 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<size_t, N> &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=(const iterator_impl &i)
{
m_category = i.m_category;
m_current = i.m_current;
m_column_ix = i.m_column_ix;
m_value = i.m_value;
return *this;
}
virtual ~iterator_impl() = default;
reference operator*()
{
return m_value;
}
pointer operator->()
{
return &m_value;
}
operator const row_handle() const
{
return { *m_category, *m_current };
}
operator row_handle()
{
return { *m_category, *m_current };
}
iterator_impl &operator++()
{
if (m_current != nullptr)
m_current = m_current->m_next;
m_value = get(std::make_index_sequence<N>());
return *this;
}
iterator_impl operator++(int)
{
iterator_impl result(*this);
this->operator++();
return result;
}
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current == rhs.m_current;
}
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current != rhs.m_current;
}
private:
template <std::size_t... Is>
tuple_type get(std::index_sequence<Is...>) const
{
if (m_current != nullptr)
{
row_handle rh{*m_category, *m_current};
return tuple_type{rh[m_column_ix[Is]].template as<Ts>()...};
}
return {};
}
category_type *m_category = nullptr;
row_type *m_current = nullptr;
value_type m_value;
std::array<size_t, N> m_column_ix;
};
template<typename Category>
class iterator_impl<Category>
{
public:
template <typename, typename...>
friend class iterator_impl;
friend class category;
using category_type = std::remove_cv_t<Category>;
using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
using iterator_category = std::forward_iterator_tag;
using value_type = row_handle;
using difference_type = std::ptrdiff_t;
using pointer = row_handle;
using reference = row_handle;
iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default;
template <typename C2>
iterator_impl(const iterator_impl<C2> &rhs)
: m_category(rhs.m_category)
, m_current(const_cast<row_type*>(rhs.m_current))
{
}
iterator_impl(Category &cat, row *current)
: m_category(const_cast<category_type *>(&cat))
, m_current(current)
{
}
template <typename IRowType>
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<size_t, 0> &cix)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
{
}
iterator_impl &operator=(const iterator_impl &i)
{
m_category = i.m_category;
m_current = i.m_current;
return *this;
}
virtual ~iterator_impl() = default;
reference operator*()
{
return {*m_category, *m_current};
}
pointer operator->()
{
return &m_current;
}
operator const row_handle() const
{
return { *m_category, *m_current };
}
operator row_handle()
{
return { *m_category, *m_current };
}
iterator_impl &operator++()
{
if (m_current != nullptr)
m_current = m_current->m_next;
return *this;
}
iterator_impl operator++(int)
{
iterator_impl result(*this);
this->operator++();
return result;
}
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current == rhs.m_current;
}
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current != rhs.m_current;
}
private:
category_type *m_category = nullptr;
row_type *m_current = nullptr;
};
template<typename Category, typename T>
class iterator_impl<Category, T>
{
public:
template <typename, typename...>
friend class iterator_impl;
friend class category;
using category_type = std::remove_cv_t<Category>;
using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default;
template <typename C2, typename T2>
iterator_impl(const iterator_impl<C2, T2> &rhs)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix)
{
}
template <typename IRowType>
iterator_impl(iterator_impl<IRowType, T> &rhs)
: m_category(rhs.m_category)
, m_current(const_cast<row_type *>(rhs.m_current))
, m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix)
{
m_value = get(m_current);
}
template <typename IRowType>
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<size_t, 1> &cix)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(cix[0])
{
m_value = get();
}
iterator_impl &operator=(const iterator_impl &i)
{
m_category = i.m_category;
m_current = i.m_current;
m_column_ix = i.m_column_ix;
m_value = i.m_value;
return *this;
}
virtual ~iterator_impl() = default;
reference operator*()
{
return m_value;
}
pointer operator->()
{
return &m_value;
}
operator const row_handle() const
{
return { *m_category, *m_current };
}
operator row_handle()
{
return { *m_category, *m_current };
}
iterator_impl &operator++()
{
if (m_current != nullptr)
m_current = m_current->m_next;
m_value = get();
return *this;
}
iterator_impl operator++(int)
{
iterator_impl result(*this);
this->operator++();
return result;
}
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current == rhs.m_current;
}
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current != rhs.m_current;
}
private:
value_type get() const
{
if (m_current != nullptr)
{
row_handle rh{*m_category, *m_current};
return rh[m_column_ix].template as<T>();
}
return {};
}
category_type *m_category = nullptr;
row_type *m_current = nullptr;
value_type m_value;
size_t m_column_ix;
};
// --------------------------------------------------------------------
// iterator proxy
template <typename Category, typename... Ts>
class iterator_proxy
{
public:
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>;
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 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);
iterator_proxy(const iterator_proxy &) = delete;
iterator_proxy &operator=(const iterator_proxy &) = delete;
iterator begin() const { return iterator(m_begin, m_column_ix); }
iterator end() const { return iterator(m_end, m_column_ix); }
bool empty() const { return m_begin == m_end; }
explicit operator bool() const { return not empty(); }
size_t size() const { return std::distance(begin(), end()); }
// row front() { return *begin(); }
// row back() { return *(std::prev(end())); }
category_type &category() const { return *m_category; }
void swap(iterator_proxy &rhs)
{
std::swap(m_category, rhs.m_category);
std::swap(m_begin, rhs.m_begin);
std::swap(m_end, rhs.m_end);
std::swap(m_column_ix, rhs.m_column_ix);
}
private:
category_type *m_category;
row_iterator m_begin, m_end;
std::array<size_t, N> m_column_ix;
};
// --------------------------------------------------------------------
// conditional iterator proxy
template <typename CategoryType, typename... Ts>
class conditional_iterator_proxy
{
public:
static constexpr const size_t N = sizeof...(Ts);
using category_type = std::remove_cv_t<CategoryType>;
using base_iterator = iterator_impl<CategoryType, Ts...>;
using value_type = typename base_iterator::value_type;
using row_type = typename base_iterator::row_type;
using row_iterator = iterator_impl<CategoryType>;
class conditional_iterator_impl
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = conditional_iterator_proxy::value_type;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type;
conditional_iterator_impl(CategoryType &cat, row_iterator pos, const condition &cond, const std::array<size_t, N> &cix);
conditional_iterator_impl(const conditional_iterator_impl &i) = default;
conditional_iterator_impl &operator=(const conditional_iterator_impl &i) = default;
virtual ~conditional_iterator_impl() = default;
reference operator*()
{
return *mBegin;
}
pointer operator->()
{
return &*mBegin;
}
conditional_iterator_impl &operator++()
{
while (mBegin != mEnd)
{
if (++mBegin == mEnd)
break;
if (m_condition->operator()(mBegin))
break;
}
return *this;
}
conditional_iterator_impl operator++(int)
{
conditional_iterator_impl result(*this);
this->operator++();
return result;
}
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 mBegin == rhs; }
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; }
private:
CategoryType *mCat;
base_iterator mBegin, mEnd;
const condition *m_condition;
};
using iterator = conditional_iterator_impl;
using reference = typename iterator::reference;
template <typename... Ns>
conditional_iterator_proxy(CategoryType &cat, row_iterator pos, condition &&cond, Ns... names);
conditional_iterator_proxy(conditional_iterator_proxy &&p);
conditional_iterator_proxy &operator=(conditional_iterator_proxy &&p);
conditional_iterator_proxy(const conditional_iterator_proxy &) = delete;
conditional_iterator_proxy &operator=(const conditional_iterator_proxy &) = delete;
iterator begin() const;
iterator end() const;
bool empty() const;
explicit operator bool() const { return not empty(); }
size_t size() const { return std::distance(begin(), end()); }
row_handle front() { return *begin(); }
// row_handle back() { return *begin(); }
CategoryType &category() const { return *m_cat; }
void swap(conditional_iterator_proxy &rhs);
private:
CategoryType *m_cat;
condition m_condition;
row_iterator mCBegin, mCEnd;
std::array<size_t, N> mCix;
};
// --------------------------------------------------------------------
template <typename Category, typename... Ts>
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 (size_t i = 0; i < N; ++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 *> columns)
: m_category(&cat)
, m_begin(pos)
, m_end(cat.end())
{
// static_assert(columns.size() == N, "The list of column names should be exactly the same as the list of requested columns");
std::size_t i = 0;
for (auto column : columns)
m_column_ix[i++] = m_category->get_column_ix(column);
}
// --------------------------------------------------------------------
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<size_t, N> &cix)
: mCat(&cat)
, mBegin(pos, cix)
, mEnd(cat.end(), cix)
, m_condition(&cond)
{
}
template <typename Category, typename... Ts>
conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy &&p)
: m_cat(nullptr)
, mCBegin(p.mCBegin)
, mCEnd(p.mCEnd)
, mCix(p.mCix)
{
std::swap(m_cat, p.m_cat);
std::swap(mCix, p.mCix);
m_condition.swap(p.m_condition);
}
template <typename Category, typename... Ts>
template <typename... Ns>
conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, condition &&cond, Ns... names)
: m_cat(&cat)
, m_condition(std::move(cond))
, mCBegin(pos)
, mCEnd(cat.end())
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of column names should be equal to number of requested value types");
m_condition.prepare(cat);
while (mCBegin != mCEnd and not m_condition(*mCBegin))
++mCBegin;
size_t i = 0;
((mCix[i++] = m_cat->get_column_ix(names)), ...);
}
template <typename Category, typename... Ts>
conditional_iterator_proxy<Category, Ts...> &conditional_iterator_proxy<Category, Ts...>::operator=(conditional_iterator_proxy &&p)
{
swap(p);
return *this;
}
template <typename Category, typename... Ts>
typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::begin() const
{
return iterator(*m_cat, mCBegin, m_condition, mCix);
}
template <typename Category, typename... Ts>
typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::end() const
{
return iterator(*m_cat, mCEnd, m_condition, mCix);
}
template <typename Category, typename... Ts>
bool conditional_iterator_proxy<Category, Ts...>::empty() const
{
return mCBegin == mCEnd;
}
template <typename Category, typename... Ts>
void conditional_iterator_proxy<Category, Ts...>::swap(conditional_iterator_proxy &rhs)
{
std::swap(m_cat, rhs.m_cat);
m_condition.swap(rhs.m_condition);
std::swap(mCBegin, rhs.mCBegin);
std::swap(mCEnd, rhs.mCEnd);
std::swap(mCix, rhs.mCix);
}
} // namespace cif

79
include/cif++/list.hpp Normal file
View File

@@ -0,0 +1,79 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <memory>
namespace cif
{
// --------------------------------------------------------------------
template<typename Allocator = std::allocator<void>>
class list
{
public:
protected:
struct list_item
{
list_item *m_next = nullptr;
};
using list_item_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<list_item>;
using list_item_allocator_traits = std::allocator_traits<item_allocator_type>;
list_item_allocator_traits::pointer get_item()
{
list_item_allocator_type ia(get_allocator());
return list_item_allocator_traits::allocate(ia, 1);
}
template<typename ...Arguments>
list_item *create_list_item(uint16_t column_ix, Arguments... args)
{
auto p = this->get_item();
list_item_allocator_type ia(get_allocator());
list_item_allocator_traits::construct(ia, p, std::forward<Arguments>(args)...);
return p;
}
void delete_list_item(list_item *iv)
{
list_item_allocator_type ia(get_allocator());
list_item_allocator_traits::destroy(ia, iv);
list_item_allocator_traits::deallocate(ia, iv, 1);
}
list_item *m_head = nullptr, *m_tail = nullptr;
};
} // namespace cif

922
include/cif++/model.hpp Normal file
View File

@@ -0,0 +1,922 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <numeric>
#include <cif++/atom_type.hpp>
#if __cpp_lib_format
#include <format>
#endif
namespace cif::mm
{
class atom;
class residue;
class monomer;
class polymer;
class structure;
// --------------------------------------------------------------------
class atom
{
private:
struct atom_impl : public std::enable_shared_from_this<atom_impl>
{
atom_impl(datablock &db, std::string_view id)
: m_db(db)
, m_cat(db["atom_site"])
, m_id(id)
{
auto r = row();
if (r)
tie(m_location.m_x, m_location.m_y, m_location.m_z) = r.get("Cartn_x", "Cartn_y", "Cartn_z");
}
// constructor for a symmetry copy of an atom
atom_impl(const atom_impl &impl, const point &loc, const std::string &sym_op)
: atom_impl(impl)
{
m_location = loc;
m_symop = sym_op;
}
atom_impl(const atom_impl &i) = default;
void prefetch();
int compare(const atom_impl &b) const;
// bool getAnisoU(float anisou[6]) const;
int get_charge() const;
void moveTo(const point &p);
// const compound *compound() const;
std::string get_property(std::string_view name) const;
int get_property_int(std::string_view name) const;
float get_property_float(std::string_view name) const;
void set_property(const std::string_view name, const std::string &value);
row_handle row()
{
return m_cat[{{"id", m_id}}];
}
const row_handle row() const
{
return m_cat[{{"id", m_id}}];
}
row_handle row_aniso()
{
auto cat = m_db.get("atom_site_anisotrop");
return cat ? cat->find1(key("id") == m_id) : row_handle{};
}
const row_handle row_aniso() const
{
auto cat = m_db.get("atom_site_anisotrop");
return cat ? cat->find1(key("id") == m_id) : row_handle{};
}
const datablock &m_db;
category &m_cat;
std::string m_id;
point m_location;
std::string m_symop = "1_555";
};
public:
atom() {}
atom(std::shared_ptr<atom_impl> impl)
: m_impl(impl)
{
}
atom(const atom &rhs)
: m_impl(rhs.m_impl)
{
}
atom(datablock &db, row_handle &row)
: atom(std::make_shared<atom_impl>(db, row["id"].as<std::string>()))
{
}
// a special constructor to create symmetry copies
atom(const atom &rhs, const point &symmmetry_location, const std::string &symmetry_operation)
: atom(std::make_shared<atom_impl>(*rhs.m_impl, symmmetry_location, symmetry_operation))
{
}
explicit operator bool() const { return (bool)m_impl; }
// // return a copy of this atom, with data copied instead of referenced
// atom clone() const
// {
// auto copy = std::make_shared<atom_impl>(*m_impl);
// copy->mClone = true;
// return atom(copy);
// }
atom &operator=(const atom &rhs) = default;
// template <typename T>
// T get_property(const std::string_view name) const;
std::string get_property(std::string_view name) const
{
if (not m_impl)
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
return m_impl->get_property(name);
}
int get_property_int(std::string_view name) const
{
if (not m_impl)
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
return m_impl->get_property_int(name);
}
float get_property_float(std::string_view name) const
{
if (not m_impl)
throw std::logic_error("Error trying to fetch a property from an uninitialized atom");
return m_impl->get_property_float(name);
}
void set_property(const std::string_view name, const std::string &value)
{
if (not m_impl)
throw std::logic_error("Error trying to modify an uninitialized atom");
m_impl->set_property(name, 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)
{
set_property(name, std::to_string(value));
}
const std::string &id() const { return impl().m_id; }
cif::atom_type get_type() const { return atom_type_traits(get_property("type_symbol")).type(); }
point get_location() const { return impl().m_location; }
void set_location(point p)
{
if (not m_impl)
throw std::logic_error("Error trying to modify an uninitialized atom");
m_impl->moveTo(p);
}
/// \brief Translate the position of this atom by \a t
void translate(point t)
{
set_location(get_location() + t);
}
/// \brief Rotate the position of this atom by \a q
void rotate(quaternion q)
{
auto loc = get_location();
loc.rotate(q);
set_location(loc);
}
/// \brief Translate and rotate the position of this atom by \a t and \a q
void translate_and_rotate(point t, quaternion q)
{
auto loc = get_location();
loc += t;
loc.rotate(q);
set_location(loc);
}
/// \brief Translate, rotate and translate again the coordinates this atom by \a t1 , \a q and \a t2
void translate_rotate_and_translate(point t1, quaternion q, point t2)
{
auto loc = get_location();
loc += t1;
loc.rotate(q);
loc += t2;
set_location(loc);
}
// for direct access to underlying data, be careful!
const row_handle get_row() const { return impl().row(); }
const row_handle get_row_aniso() const { return impl().row_aniso(); }
bool is_symmetry_copy() const { return impl().m_symop != "1_555"; }
std::string symmetry() const { return impl().m_symop; }
// const compound &compound() const;
bool is_water() const
{
auto comp_id = get_label_comp_id();
return comp_id == "HOH" or comp_id == "H2O" or comp_id == "WAT";
}
int get_charge() const { return impl().get_charge(); }
// float uIso() const;
// bool getAnisoU(float anisou[6]) const { return impl().getAnisoU(anisou); }
float get_occupancy() const { return get_property_float("occupancy"); }
// specifications
std::string get_label_asym_id() const { return get_property("label_asym_id"); }
int get_label_seq_id() const { return get_property_int("label_seq_id"); }
std::string get_label_atom_id() const { return get_property("label_atom_id"); }
std::string get_label_alt_id() const { return get_property("label_alt_id"); }
std::string get_label_comp_id() const { return get_property("label_comp_id"); }
std::string get_label_entity_id() const { return get_property("label_entity_id"); }
std::string get_auth_asym_id() const { return get_property("auth_asym_id"); }
std::string get_auth_seq_id() const { return get_property("auth_seq_id"); }
std::string get_auth_atom_id() const { return get_property("auth_atom_id"); }
std::string get_auth_alt_id() const { return get_property("auth_alt_id"); }
std::string get_auth_comp_id() const { return get_property("auth_comp_id"); }
std::string get_pdb_ins_code() const { return get_property("pdbx_PDB_ins_code"); }
bool is_alternate() const { return not get_label_alt_id().empty(); }
// std::string labelID() const; // label_comp_id + '_' + label_asym_id + '_' + label_seq_id
std::string pdb_id() const
{
return get_label_comp_id() + '_' + get_auth_asym_id() + '_' + get_auth_seq_id() + get_pdb_ins_code();
}
bool operator==(const atom &rhs) const
{
if (m_impl == rhs.m_impl)
return true;
if (not(m_impl and rhs.m_impl))
return false;
return &m_impl->m_db == &rhs.m_impl->m_db and m_impl->m_id == rhs.m_impl->m_id;
}
bool operator!=(const atom &rhs) const
{
return not operator==(rhs);
}
// // access data in compound for this atom
// convenience routine
bool is_back_bone() const
{
auto atomID = get_label_atom_id();
return atomID == "N" or atomID == "O" or atomID == "C" or atomID == "CA";
}
void swap(atom &b)
{
std::swap(m_impl, b.m_impl);
}
int compare(const atom &b) const { return impl().compare(*b.m_impl); }
bool operator<(const atom &rhs) const
{
return compare(rhs) < 0;
}
friend std::ostream &operator<<(std::ostream &os, const atom &atom);
// /// \brief Synchronize data with underlying cif data
// void sync()
// {
// if (m_impl)
// m_impl->prefetch();
// }
private:
friend class structure;
const atom_impl &impl() const
{
if (not m_impl)
throw std::runtime_error("Uninitialized atom, not found?");
return *m_impl;
}
std::shared_ptr<atom_impl> m_impl;
};
// template <>
// inline std::string atom::get_property<std::string>(const std::string_view name) const
// {
// return get_property(name);
// }
// template <>
// inline int atom::get_property<int>(const std::string_view name) const
// {
// auto v = impl().get_property(name);
// return v.empty() ? 0 : stoi(v);
// }
// template <>
// inline float atom::get_property<float>(const std::string_view name) const
// {
// return stof(impl().get_property(name));
// }
inline void swap(atom &a, atom &b)
{
a.swap(b);
}
inline float distance(const atom &a, const atom &b)
{
return distance(a.get_location(), b.get_location());
}
inline float distance_squared(const atom &a, const atom &b)
{
return distance_squared(a.get_location(), b.get_location());
}
// --------------------------------------------------------------------
enum class EntityType
{
polymer,
NonPolymer,
Macrolide,
Water,
Branched
};
// --------------------------------------------------------------------
class residue
{
public:
friend class structure;
// constructor
residue(const structure &structure, const std::string &compoundID,
const std::string &asymID, int seqID,
const std::string &authAsymID, const std::string &authSeqID,
const std::string &pdbInsCode)
: m_structure(&structure)
, m_compound_id(compoundID)
, m_asym_id(asymID)
, m_seq_id(seqID)
, m_auth_asym_id(authAsymID)
, m_auth_seq_id(authSeqID)
, m_pdb_ins_code(pdbInsCode)
{
}
residue(const residue &rhs) = delete;
residue &operator=(const residue &rhs) = delete;
residue(residue &&rhs) = default;
residue &operator=(residue &&rhs) = default;
virtual ~residue() = default;
std::string get_entity_id() const;
EntityType entity_type() const;
const std::string &get_asym_id() const { return m_asym_id; }
int get_seq_id() const { return m_seq_id; }
const std::string get_auth_asym_id() const { return m_auth_asym_id; }
const std::string get_auth_seq_id() const { return m_auth_seq_id; }
std::string get_pdb_ins_code() const { return m_pdb_ins_code; }
const std::string &get_compound_id() const { return m_compound_id; }
void set_compound_id(const std::string &id) { m_compound_id = id; }
const structure *get_structure() const { return m_structure; }
// const compound &compound() const;
std::vector<atom> &atoms()
{
return m_atoms;
}
const std::vector<atom> &atoms() const
{
return m_atoms;
}
void add_atom(atom &atom);
/// \brief Unique atoms returns only the atoms without alternates and the first of each alternate atom id.
std::vector<atom> unique_atoms() const;
/// \brief The alt ID used for the unique atoms
std::string unique_alt_id() const;
atom get_atom_by_atom_id(const std::string &atomID) const;
// Is this residue a single entity?
bool is_entity() const;
bool is_water() const { return m_compound_id == "HOH"; }
// bool empty() const { return m_structure == nullptr; }
bool has_alternate_atoms() const;
/// \brief Return the list of unique alt ID's present in this residue
std::set<std::string> get_alternate_ids() const;
/// \brief Return the list of unique atom ID's
std::set<std::string> get_atom_ids() const;
/// \brief Return the list of atoms having ID \a atomID
std::vector<atom> get_atoms_by_id(const std::string &atomID) const;
// some routines for 3d work
std::tuple<point, float> center_and_radius() const;
friend std::ostream &operator<<(std::ostream &os, const residue &res);
bool operator==(const residue &rhs) const
{
return this == &rhs or (m_structure == rhs.m_structure and
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_auth_seq_id == rhs.m_auth_seq_id);
}
protected:
residue() {}
const structure *m_structure = nullptr;
std::string m_compound_id, m_asym_id;
int m_seq_id = 0;
std::string m_auth_asym_id, m_auth_seq_id, m_pdb_ins_code;
std::vector<atom> m_atoms;
};
// --------------------------------------------------------------------
// a monomer models a single residue in a protein chain
class monomer : public residue
{
public:
// monomer();
monomer(const monomer &rhs) = delete;
monomer &operator=(const monomer &rhs) = delete;
monomer(monomer &&rhs);
monomer &operator=(monomer &&rhs);
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;
bool is_last_in_chain() const;
// convenience
bool has_alpha() const;
bool has_kappa() const;
// Assuming this is really an amino acid...
float phi() const;
float psi() const;
float alpha() const;
float kappa() const;
float tco() const;
float omega() const;
// torsion angles
size_t nr_of_chis() const;
float chi(size_t i) const;
bool is_cis() const;
/// \brief Returns true if the four atoms C, CA, N and O are present
bool is_complete() const;
/// \brief Returns true if any of the backbone atoms has an alternate
bool has_alternate_backbone_atoms() const;
atom CAlpha() const { return get_atom_by_atom_id("CA"); }
atom C() const { return get_atom_by_atom_id("C"); }
atom N() const { return get_atom_by_atom_id("N"); }
atom O() const { return get_atom_by_atom_id("O"); }
atom H() const { return get_atom_by_atom_id("H"); }
bool is_bonded_to(const monomer &rhs) const
{
return this != &rhs and are_bonded(*this, rhs);
}
static bool are_bonded(const monomer &a, const monomer &b, float errorMargin = 0.5f);
static bool is_cis(const monomer &a, const monomer &b);
static float omega(const monomer &a, const monomer &b);
// for LEU and VAL
float chiral_volume() const;
bool operator==(const monomer &rhs) const
{
return m_polymer == rhs.m_polymer and m_index == rhs.m_index;
}
private:
const polymer *m_polymer;
size_t m_index;
};
// --------------------------------------------------------------------
class polymer : public std::vector<monomer>
{
public:
polymer(const structure &s, const std::string &entityID, const std::string &asymID, const std::string &auth_asym_id);
polymer(const polymer &) = delete;
polymer &operator=(const polymer &) = delete;
// monomer &getBySeqID(int seqID);
// const monomer &getBySeqID(int seqID) const;
const structure *get_structure() const { return m_structure; }
std::string get_asym_id() const { return m_asym_id; }
std::string get_auth_asym_id() const { return m_auth_asym_id; } // The PDB chain ID, actually
std::string get_entity_id() const { return m_entity_id; }
// int Distance(const monomer &a, const monomer &b) const;
private:
const structure *m_structure;
std::string m_entity_id;
std::string m_asym_id;
std::string m_auth_asym_id;
};
// --------------------------------------------------------------------
// sugar and branch, to describe glycosylation sites
class branch;
class sugar : public residue
{
public:
sugar(const branch &branch, const std::string &compoundID,
const std::string &asymID, int authSeqID);
sugar(sugar &&rhs);
sugar &operator=(sugar &&rhs);
int num() const { return std::stoi(m_auth_seq_id); }
std::string name() const;
/// \brief Return the atom the C1 is linked to
atom get_link() const { return m_link; }
void set_link(atom link) { m_link = link; }
size_t get_link_nr() const
{
size_t result = 0;
if (m_link)
result = m_link.get_property_int("auth_seq_id");
return result;
}
private:
const branch *m_branch;
atom m_link;
};
class branch : public std::vector<sugar>
{
public:
branch(structure &structure, const std::string &asymID);
void link_atoms();
std::string name() const;
float weight() const;
std::string get_asym_id() const { return m_asym_id; }
structure &get_structure() { return *m_structure; }
const structure &get_structure() const { return *m_structure; }
sugar &getSugarByNum(int nr);
const sugar &getSugarByNum(int nr) const;
private:
friend sugar;
std::string name(const sugar &s) const;
structure *m_structure;
std::string m_asym_id;
};
// // --------------------------------------------------------------------
// // file is a reference to the data stored in e.g. the cif file.
// // This object is not copyable.
// class File : public file
// {
// public:
// File() {}
// // File(const std::filesystem::path &path)
// // {
// // load(path);
// // }
// // File(const char *data, size_t length)
// // {
// // load(data, length);
// // }
// File(const File &) = delete;
// File &operator=(const File &) = delete;
// // void load(const std::filesystem::path &p) override;
// // void save(const std::filesystem::path &p) override;
// // using file::load;
// // using file::save;
// datablock &data() { return front(); }
// };
// --------------------------------------------------------------------
enum class StructureOpenOptions
{
SkipHydrogen = 1 << 0
};
inline bool operator&(StructureOpenOptions a, StructureOpenOptions b)
{
return static_cast<int>(a) bitand static_cast<int>(b);
}
// --------------------------------------------------------------------
class structure
{
public:
structure(file &p, size_t modelNr = 1, StructureOpenOptions options = {});
structure(datablock &db, size_t modelNr = 1, StructureOpenOptions options = {});
structure(structure &&s) = default;
// Create a read-only clone of the current structure (for multithreaded calculations that move atoms)
// NOTE: removed, simply create a new structure for each thread
structure(const structure &) = delete;
structure &operator=(const structure &) = delete;
// Structure &operator=(Structure &&s) = default;
~structure() = default;
size_t get_model_nr() const { return m_model_nr; }
const std::vector<atom> &atoms() const { return m_atoms; }
// std::vector<atom> &atoms() { return m_atoms; }
EntityType get_entity_type_for_entity_id(const std::string entityID) const;
EntityType get_entity_type_for_asym_id(const std::string asymID) const;
// std::vector<atom> waters() const;
const std::list<polymer> &polymers() const { return m_polymers; }
std::list<polymer> &polymers() { return m_polymers; }
polymer &get_polymer_by_asym_id(const std::string &asymID);
const polymer &get_polymer_by_asym_id(const std::string &asymID) const
{
return const_cast<structure *>(this)->get_polymer_by_asym_id(asymID);
}
const std::list<branch> &branches() const { return m_branches; }
std::list<branch> &branches() { return m_branches; }
branch &get_branch_by_asym_id(const std::string &asymID);
const branch &get_branch_by_asym_id(const std::string &asymID) const;
const std::vector<residue> &non_polymers() const { return m_non_polymers; }
atom get_atom_by_id(const std::string &id) const;
// atom getAtomByLocation(point pt, float maxDistance) const;
atom get_atom_by_label(const std::string &atomID, const std::string &asymID,
const std::string &compID, int seqID, const std::string &altID = "");
// /// \brief Return the atom closest to point \a p
atom get_atom_by_position(point p) const;
/// \brief Return the atom closest to point \a p with atom type \a type in a residue of type \a res_type
atom get_atom_by_position_and_type(point p, std::string_view type, std::string_view res_type) const;
/// \brief Get a non-poly residue for an asym with id \a asymID
residue &get_residue(const std::string &asymID)
{
return get_residue(asymID, 0, "");
}
/// \brief Get a non-poly residue for an asym with id \a asymID
const residue &get_residue(const std::string &asymID) const
{
return get_residue(asymID, 0, "");
}
/// \brief Get a residue for an asym with id \a asymID seq id \a seqID and authSeqID \a authSeqID
residue &get_residue(const std::string &asymID, int seqID, const std::string &authSeqID);
/// \brief Get a the single residue for an asym with id \a asymID seq id \a seqID and authSeqID \a authSeqID
const residue &get_residue(const std::string &asymID, int seqID, const std::string &authSeqID) const
{
return const_cast<structure *>(this)->get_residue(asymID, seqID, authSeqID);
}
/// \brief Get a residue for an asym with id \a asymID, compound id \a compID, seq id \a seqID and authSeqID \a authSeqID
residue &get_residue(const std::string &asymID, const std::string &compID, int seqID, const std::string &authSeqID);
/// \brief Get a residue for an asym with id \a asymID, compound id \a compID, seq id \a seqID and authSeqID \a authSeqID
const residue &get_residue(const std::string &asymID, const std::string &compID, int seqID, const std::string &authSeqID) const
{
return const_cast<structure *>(this)->get_residue(asymID, compID, seqID, authSeqID);
}
/// \brief Get a the residue for atom \a atom
residue &get_residue(const atom &atom)
{
return get_residue(atom.get_label_asym_id(), atom.get_label_comp_id(), atom.get_label_seq_id(), atom.get_auth_seq_id());
}
/// \brief Get a the residue for atom \a atom
const residue &get_residue(const atom &atom) const
{
return get_residue(atom.get_label_asym_id(), atom.get_label_comp_id(), atom.get_label_seq_id(), atom.get_auth_seq_id());
}
// Actions
void remove_atom(atom &a)
{
remove_atom(a, true);
}
void swap_atoms(atom a1, atom a2); // swap the labels for these atoms
void move_atom(atom a, point p); // move atom to a new location
void change_residue(residue &res, const std::string &newcompound,
const std::vector<std::tuple<std::string, std::string>> &remappedAtoms);
/// \brief Remove a residue, can be monomer or nonpoly
///
/// \param asym_id The asym ID
/// \param seq_id The sequence ID
void remove_residue(const std::string &asym_id, int seq_id, const std::string &auth_seq_id)
{
remove_residue(get_residue(asym_id, seq_id, auth_seq_id));
}
/// \brief Create a new non-polymer entity, returns new ID
/// \param mon_id The mon_id for the new nonpoly, must be an existing and known compound from CCD
/// \return The ID of the created entity
std::string create_non_poly_entity(const std::string &mon_id);
/// \brief Create a new NonPolymer struct_asym with atoms constructed from \a atoms, returns asym_id.
/// This method assumes you are copying data from one cif file to another.
///
/// \param entity_id The entity ID of the new nonpoly
/// \param atoms The array of atom_site rows containing the data.
/// \return The newly create asym ID
std::string create_non_poly(const std::string &entity_id, const std::vector<atom> &atoms);
/// \brief Create a new NonPolymer struct_asym with atoms constructed from info in \a atom_info, returns asym_id.
/// This method creates new atom records filled with info from the info.
///
/// \param entity_id The entity ID of the new nonpoly
/// \param atoms The array of sets of item data containing the data for the atoms.
/// \return The newly create asym ID
std::string create_non_poly(const std::string &entity_id, std::vector<row_initializer> atoms);
/// \brief Create a new (sugar) branch with one first NAG containing atoms constructed from \a atoms
branch &create_branch(std::vector<row_initializer> atoms);
/// \brief Extend an existing (sugar) branch identified by \a asymID with one sugar containing atoms constructed from \a atom_info
///
/// \param asym_id The asym id of the branch to extend
/// \param atom_info Array containing the info for the atoms to construct for the new sugar
/// \param link_sugar The sugar to link to, note: this is the sugar number (1 based)
/// \param link_atom The atom id of the atom linked in the sugar
branch &extend_branch(const std::string &asym_id, std::vector<row_initializer> atom_info,
int link_sugar, const std::string &link_atom);
/// \brief Remove \a branch
void remove_branch(branch &branch);
/// \brief Remove residue \a res
///
/// \param res The residue to remove
void remove_residue(residue &res);
/// \brief Translate the coordinates of all atoms in the structure by \a t
void translate(point t);
/// \brief Rotate the coordinates of all atoms in the structure by \a q
void rotate(quaternion t);
/// \brief Translate and rotate the coordinates of all atoms in the structure by \a t and \a q
void translate_and_rotate(point t, quaternion q);
/// \brief Translate, rotate and translate again the coordinates of all atoms in the structure by \a t1 , \a q and \a t2
void translate_rotate_and_translate(point t1, quaternion q, point t2);
void cleanup_empty_categories();
/// \brief Direct access to underlying data
category &get_category(std::string_view name) const
{
return m_db[name];
}
datablock &get_datablock() const
{
return m_db;
}
void validate_atoms() const;
private:
friend polymer;
friend residue;
std::string insert_compound(const std::string &compoundID, bool is_entity);
std::string create_entity_for_branch(branch &branch);
void load_data();
void load_atoms_for_model(StructureOpenOptions options);
template <typename... Args>
atom &emplace_atom(Args... args)
{
return emplace_atom(atom{ std::forward<Args>(args)... });
}
atom &emplace_atom(atom &&atom);
void remove_atom(atom &a, bool removeFromResidue);
void remove_sugar(sugar &sugar);
datablock &m_db;
size_t m_model_nr;
std::vector<atom> m_atoms;
std::vector<size_t> m_atom_index;
std::list<polymer> m_polymers;
std::list<branch> m_branches;
std::vector<residue> m_non_polymers;
};
} // namespace cif::mm

289
include/cif++/parser.hpp Normal file
View File

@@ -0,0 +1,289 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <map>
#include <cif++/row.hpp>
namespace cif
{
// --------------------------------------------------------------------
class parse_error : public std::runtime_error
{
public:
parse_error(uint32_t line_nr, const std::string &message)
: std::runtime_error("parse error at line " + std::to_string(line_nr) + ": " + message)
{
}
};
// --------------------------------------------------------------------
// TODO: Need to implement support for transformed long lines
class sac_parser
{
public:
using datablock_index = std::map<std::string, std::size_t>;
sac_parser(std::istream &is, bool init = true);
virtual ~sac_parser() = default;
enum CharTraitsMask : uint8_t
{
kOrdinaryMask = 1 << 0,
kNonBlankMask = 1 << 1,
kTextLeadMask = 1 << 2,
kAnyPrintMask = 1 << 3
};
static bool is_white(int ch)
{
return std::isspace(ch) or ch == '#';
}
static constexpr bool is_ordinary(int ch)
{
return ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kOrdinaryMask) != 0;
}
static constexpr bool is_non_blank(int ch)
{
return ch > 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kNonBlankMask) != 0;
}
static constexpr bool is_text_lead(int ch)
{
return ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kTextLeadMask) != 0;
}
static constexpr bool is_any_print(int ch)
{
return ch == '\t' or
(ch >= 0x20 and ch <= 0x7f and (kCharTraitsTable[ch - 0x20] & kAnyPrintMask) != 0);
}
static bool is_unquoted_string(std::string_view text)
{
auto s = text.begin();
bool result = is_ordinary(*s++);
while (result and s != text.end())
{
result = is_non_blank(*s);
++s;
}
// but be careful it does not contain e.g. stop_
if (result)
{
static const std::regex reservedRx(R"((^(?:data|save)|.*(?:loop|stop|global))_.+)", std::regex_constants::icase);
result = not std::regex_match(text.begin(), text.end(), reservedRx);
}
return result;
}
protected:
static constexpr uint8_t kCharTraitsTable[128] = {
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
14, 15, 14, 14, 14, 15, 15, 14, 15, 15, 15, 15, 15, 15, 15, 15, // 2
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 10, 15, 15, 15, 15, // 3
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // 4
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 14, 15, 14, // 5
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // 6
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, // 7
};
enum class CIFToken
{
Unknown,
Eof,
DATA,
LOOP,
GLOBAL,
SAVE,
STOP,
Tag,
Value
};
static constexpr const char *get_token_name(CIFToken token)
{
switch (token)
{
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::STOP: return "STOP";
case CIFToken::Tag: return "Tag";
case CIFToken::Value: return "Value";
default: return "Invalid token parameter";
}
}
enum class CIFValue
{
Int,
Float,
Numeric,
String,
TextField,
Inapplicable,
Unknown
};
static constexpr const char *get_value_name(CIFValue type)
{
switch (type)
{
case CIFValue::Int: return "Int";
case CIFValue::Float: return "Float";
case CIFValue::Numeric: return "Numeric";
case CIFValue::String: return "String";
case CIFValue::TextField: return "TextField";
case CIFValue::Inapplicable: return "Inapplicable";
case CIFValue::Unknown: return "Unknown";
default: return "Invalid type parameter";
}
}
// get_next_char takes a char from the buffer, or if it is empty
// from the istream. This function also does carriage/linefeed
// translation.
int get_next_char();
void retract();
int restart(int start);
CIFToken get_next_token();
void match(CIFToken token);
public:
bool parse_single_datablock(const std::string &datablock);
datablock_index index_datablocks();
bool parse_single_datablock(const std::string &datablock, const datablock_index &index);
void parse_file();
protected:
void parse_global();
void parse_datablock();
virtual void parse_save_frame();
void error(const std::string &msg)
{
throw parse_error(m_line_nr, msg);
}
void warning(const std::string &msg)
{
std::cerr << "parser warning at line" << m_line_nr << ": " << msg << std::endl;
}
// production methods, these are pure virtual here
virtual void produce_datablock(const std::string &name) = 0;
virtual void produce_category(const std::string &name) = 0;
virtual void produce_row() = 0;
virtual void produce_item(const std::string &category, const std::string &item, const std::string &value) = 0;
protected:
enum State
{
Start,
White,
Esc,
Comment,
QuestionMark,
Dot,
QuotedString,
QuotedStringQuote,
UnquotedString,
Tag,
TextField,
Float = 100,
Int = 110,
Value = 300,
DATA,
SAVE
};
std::istream &m_source;
// Parser state
bool m_validate;
uint32_t m_line_nr;
bool m_bol;
CIFToken m_lookahead;
std::string m_token_value;
CIFValue mTokenType;
std::stack<int> m_buffer;
};
// --------------------------------------------------------------------
class parser : public sac_parser
{
public:
parser(std::istream &is, file &file)
: sac_parser(is)
, m_file(file)
{
}
void produce_datablock(const std::string &name) override;
void produce_category(const std::string &name) override;
void produce_row() override;
void produce_item(const std::string &category, const std::string &item, const std::string &value) override;
protected:
file &m_file;
datablock *m_datablock = nullptr;
category *m_category = nullptr;
row_handle m_row;
};
} // namespace cif

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -26,14 +26,17 @@
#pragma once
#include "cif++/Cif++.hpp"
#include <cif++.hpp>
void WritePDBFile(std::ostream& pdbFile, cif::File& cifFile);
namespace cif::pdb
{
/// \brief Just the HEADER, COMPND, SOURCE and AUTHOR lines
void WritePDBHeaderLines(std::ostream& os, cif::File& cifFile);
void write_header_lines(std::ostream &os, const datablock &data);
std::string GetPDBHEADERLine(cif::File& cifFile, std::string::size_type truncate_at = 127);
std::string GetPDBCOMPNDLine(cif::File& cifFile, std::string::size_type truncate_at = 127);
std::string GetPDBSOURCELine(cif::File& cifFile, std::string::size_type truncate_at = 127);
std::string GetPDBAUTHORLine(cif::File& cifFile, std::string::size_type truncate_at = 127);
std::string get_HEADER_line(const datablock &data, std::string::size_type truncate_at = 127);
std::string get_COMPND_line(const datablock &data, std::string::size_type truncate_at = 127);
std::string get_SOURCE_line(const datablock &data, std::string::size_type truncate_at = 127);
std::string get_AUTHOR_line(const datablock &data, std::string::size_type truncate_at = 127);
} // namespace pdbx

44
include/cif++/pdb/io.hpp Normal file
View File

@@ -0,0 +1,44 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <cif++.hpp>
namespace cif::pdb
{
file read(std::istream &is);
file read(const std::filesystem::path &file);
void write(std::ostream &os, const datablock &db);
inline void write(std::ostream &os, const file &f)
{
write(os, f.front());
}
}

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -26,28 +26,32 @@
#pragma once
#include "cif++/Cif++.hpp"
#include <cif++.hpp>
namespace cif::pdb
{
// --------------------------------------------------------------------
struct PDBRecord
{
PDBRecord* mNext;
uint32_t mLineNr;
char mName[11];
size_t mVlen;
char mValue[0];
PDBRecord *mNext;
uint32_t mLineNr;
char mName[11];
size_t mVlen;
char mValue[1];
PDBRecord(uint32_t lineNr, const std::string& name, const std::string& value);
PDBRecord(uint32_t lineNr, const std::string &name, const std::string &value);
~PDBRecord();
void* operator new(size_t);
void* operator new(size_t size, size_t vLen);
void operator delete(void* p);
bool is(const char* name) const;
void *operator new(size_t);
void *operator new(size_t size, size_t vLen);
void operator delete(void *p);
void operator delete(void *p, size_t vLen);
bool is(const char *name) const;
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);
@@ -56,4 +60,6 @@ struct PDBRecord
// --------------------------------------------------------------------
void ReadPDBFile(std::istream& pdbFile, cif::File& cifFile);
void ReadPDBFile(std::istream &pdbFile, file &cifFile);
} // namespace pdbx

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -26,10 +26,13 @@
#pragma once
#include "cif++/PDB2Cif.hpp"
#include <cif++/pdb/pdb2cif.hpp>
// --------------------------------------------------------------------
namespace cif::pdb
{
struct TemplateLine;
class Remark3Parser
@@ -37,37 +40,36 @@ class Remark3Parser
public:
virtual ~Remark3Parser() {}
static bool parse(const std::string& expMethod, PDBRecord* r, cif::Datablock& db);
static bool parse(const std::string &expMethod, PDBRecord *r, cif::datablock &db);
virtual std::string program();
virtual std::string version();
protected:
Remark3Parser(const std::string& name, const std::string& expMethod, PDBRecord* r, cif::Datablock& db,
const TemplateLine templatelines[], uint32_t templateLineCount, std::regex programVersion);
Remark3Parser(const std::string &name, const std::string &expMethod, PDBRecord *r, cif::datablock &db,
const TemplateLine templatelines[], uint32_t templateLineCount, std::regex programVersion);
virtual float parse();
std::string nextLine();
bool match(const char* expr, int nextState);
void storeCapture(const char* category, std::initializer_list<const char*> items, bool createNew = false);
void storeRefineLsRestr(const char* type, std::initializer_list<const char*> values);
void updateRefineLsRestr(const char* type, std::initializer_list<const char*> values);
bool match(const char *expr, int nextState);
void storeCapture(const char *category, std::initializer_list<const char *> items, bool createNew = false);
void storeRefineLsRestr(const char *type, std::initializer_list<const char *> values);
void updateRefineLsRestr(const char *type, std::initializer_list<const char *> values);
virtual void fixup() {}
std::string mName;
std::string mExpMethod;
PDBRecord* mRec;
cif::Datablock mDb;
std::string mLine;
std::smatch mM;
uint32_t mState;
std::string mName;
std::string mExpMethod;
PDBRecord *mRec;
cif::datablock mDb;
std::string mLine;
std::smatch mM;
uint32_t mState;
const TemplateLine* mTemplate;
uint32_t mTemplateCount;
std::regex mProgramVersion;
const TemplateLine *mTemplate;
uint32_t mTemplateCount;
std::regex mProgramVersion;
};
} // namespace pdbx

View File

@@ -1,17 +1,17 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -26,32 +26,30 @@
#pragma once
#include <vector>
#include <string>
#include <tuple>
#include <vector>
#include "cif++/Cif++.hpp"
#include <cif++.hpp>
namespace cif
{
extern const int
kResidueNrWildcard,
kNoSeqNum;
struct TLSSelection;
typedef std::unique_ptr<TLSSelection> TLSSelectionPtr;
struct tls_selection;
struct tls_residue;
struct TLSResidue;
struct TLSSelection
struct tls_selection
{
virtual ~TLSSelection() {}
virtual void CollectResidues(cif::Datablock& db, std::vector<TLSResidue>& residues, int indentLevel = 0) const = 0;
std::vector<std::tuple<std::string,int,int>> GetRanges(cif::Datablock& db, bool pdbNamespace) const;
virtual ~tls_selection() {}
virtual void collect_residues(cif::datablock &db, std::vector<tls_residue> &residues, std::size_t indentLevel = 0) const = 0;
std::vector<std::tuple<std::string, int, int>> get_ranges(cif::datablock &db, bool pdbNamespace) const;
};
// Low level: get the selections
TLSSelectionPtr ParseSelectionDetails(const std::string& program, const std::string& selection);
std::unique_ptr<tls_selection> parse_tls_selection_details(const std::string &program, const std::string &selection);
}
} // namespace cif

736
include/cif++/point.hpp Normal file
View File

@@ -0,0 +1,736 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cmath>
#include <complex>
#include <functional>
#include <valarray>
#if __has_include(<clipper/core/coords.h>)
#define HAVE_LIBCLIPPER 1
#include <clipper/core/coords.h>
#endif
namespace cif
{
// --------------------------------------------------------------------
const double
kPI = 3.141592653589793238462643383279502884;
// --------------------------------------------------------------------
// A stripped down quaternion implementation, based on boost::math::quaternion
// We use quaternions to do rotations in 3d space
template <typename T>
class quaternion_type
{
public:
using value_type = T;
constexpr explicit quaternion_type(value_type const &value_a = value_type(), value_type const &value_b = value_type(), value_type const &value_c = value_type(), value_type const &value_d = value_type())
: a(value_a)
, b(value_b)
, c(value_c)
, d(value_d)
{
}
constexpr explicit quaternion_type(std::complex<value_type> const &z0, std::complex<value_type> const &z1 = std::complex<value_type>())
: a(z0.real())
, b(z0.imag())
, c(z1.real())
, d(z1.imag())
{
}
constexpr quaternion_type(quaternion_type const &) = default;
constexpr quaternion_type(quaternion_type &&) = default;
template <typename X>
constexpr explicit quaternion_type(quaternion_type<X> const &rhs)
: a(static_cast<value_type>(rhs.a))
, b(static_cast<value_type>(rhs.b))
, c(static_cast<value_type>(rhs.c))
, d(static_cast<value_type>(rhs.d))
{
}
// accessors
//
// Note: Like complex number, quaternions do have a meaningful notion of "real part",
// but unlike them there is no meaningful notion of "imaginary part".
// Instead there is an "unreal part" which itself is a quaternion, and usually
// nothing simpler (as opposed to the complex number case).
// However, for practicality, there are accessors for the other components
// (these are necessary for the templated copy constructor, for instance).
constexpr value_type real() const
{
return a;
}
constexpr quaternion_type unreal() const
{
return { 0, b, c, d };
}
constexpr void swap(quaternion_type &o)
{
std::swap(a, o.a);
std::swap(b, o.b);
std::swap(c, o.c);
std::swap(d, o.d);
}
// assignment operators
template <typename X>
constexpr quaternion_type &operator=(quaternion_type<X> const &rhs)
{
a = static_cast<value_type>(rhs.a);
b = static_cast<value_type>(rhs.b);
c = static_cast<value_type>(rhs.c);
d = static_cast<value_type>(rhs.d);
return *this;
}
constexpr quaternion_type &operator=(quaternion_type const &rhs)
{
a = rhs.a;
b = rhs.b;
c = rhs.c;
d = rhs.d;
return *this;
}
constexpr quaternion_type &operator=(value_type const &rhs)
{
a = rhs;
b = c = d = static_cast<value_type>(0);
return *this;
}
constexpr quaternion_type &operator=(std::complex<value_type> const &rhs)
{
a = rhs.real();
b = rhs.imag();
c = d = static_cast<value_type>(0);
return *this;
}
// other assignment-related operators
//
// NOTE: Quaternion multiplication is *NOT* commutative;
// symbolically, "q *= rhs;" means "q = q * rhs;"
// and "q /= rhs;" means "q = q * inverse_of(rhs);"
constexpr quaternion_type &operator+=(value_type const &rhs)
{
a += rhs;
return *this;
}
constexpr quaternion_type &operator+=(std::complex<value_type> const &rhs)
{
a += std::real(rhs);
b += std::imag(rhs);
return *this;
}
template <class X>
constexpr quaternion_type &operator+=(quaternion_type<X> const &rhs)
{
a += rhs.a;
b += rhs.b;
c += rhs.c;
d += rhs.d;
return *this;
}
constexpr quaternion_type &operator-=(value_type const &rhs)
{
a -= rhs;
return *this;
}
constexpr quaternion_type &operator-=(std::complex<value_type> const &rhs)
{
a -= std::real(rhs);
b -= std::imag(rhs);
return *this;
}
template <class X>
constexpr quaternion_type &operator-=(quaternion_type<X> const &rhs)
{
a -= rhs.a;
b -= rhs.b;
c -= rhs.c;
d -= rhs.d;
return *this;
}
constexpr quaternion_type &operator*=(value_type const &rhs)
{
a *= rhs;
b *= rhs;
c *= rhs;
d *= rhs;
return *this;
}
constexpr quaternion_type &operator*=(std::complex<value_type> const &rhs)
{
value_type ar = rhs.real();
value_type br = rhs.imag();
quaternion_type result(a * ar - b * br, a * br + b * ar, c * ar + d * br, -c * br + d * ar);
swap(result);
return *this;
}
constexpr friend quaternion_type operator*(const quaternion_type &a, const quaternion_type &b)
{
auto result = a;
result *= b;
return result;
}
template <typename X>
constexpr quaternion_type &operator*=(quaternion_type<X> const &rhs)
{
value_type ar = static_cast<value_type>(rhs.a);
value_type br = static_cast<value_type>(rhs.b);
value_type cr = static_cast<value_type>(rhs.c);
value_type dr = static_cast<value_type>(rhs.d);
quaternion_type result(a * ar - b * br - c * cr - d * dr, a * br + b * ar + c * dr - d * cr, a * cr - b * dr + c * ar + d * br, a * dr + b * cr - c * br + d * ar);
swap(result);
return *this;
}
constexpr quaternion_type &operator/=(value_type const &rhs)
{
a /= rhs;
b /= rhs;
c /= rhs;
d /= rhs;
return *this;
}
constexpr quaternion_type &operator/=(std::complex<value_type> const &rhs)
{
value_type ar = rhs.real();
value_type br = rhs.imag();
value_type denominator = ar * ar + br * br;
quaternion_type result((+a * ar + b * br) / denominator, (-a * br + b * ar) / denominator, (+c * ar - d * br) / denominator, (+c * br + d * ar) / denominator);
swap(result);
return *this;
}
template <typename X>
constexpr quaternion_type &operator/=(quaternion_type<X> const &rhs)
{
value_type ar = static_cast<value_type>(rhs.a);
value_type br = static_cast<value_type>(rhs.b);
value_type cr = static_cast<value_type>(rhs.c);
value_type dr = static_cast<value_type>(rhs.d);
value_type denominator = ar * ar + br * br + cr * cr + dr * dr;
quaternion_type result((+a * ar + b * br + c * cr + d * dr) / denominator, (-a * br + b * ar - c * dr + d * cr) / denominator, (-a * cr + b * dr + c * ar - d * br) / denominator, (-a * dr - b * cr + c * br + d * ar) / denominator);
swap(result);
return *this;
}
constexpr friend quaternion_type normalize(quaternion_type q)
{
std::valarray<value_type> t(4);
t[0] = q.a;
t[1] = q.b;
t[2] = q.c;
t[3] = q.d;
t *= t;
value_type length = std::sqrt(t.sum());
if (length > 0.001)
q /= static_cast<value_type>(length);
else
q = quaternion_type(1, 0, 0, 0);
return q;
}
constexpr friend quaternion_type conj(quaternion_type q)
{
return quaternion_type{ +q.a, -q.b, -q.c, -q.d };
}
constexpr value_type get_a() const { return a; }
constexpr value_type get_b() const { return b; }
constexpr value_type get_c() const { return c; }
constexpr value_type get_d() const { return d; }
private:
value_type a, b, c, d;
};
template <typename T>
inline quaternion_type<T> spherical(T const &rho, T const &theta, T const &phi1, T const &phi2)
{
T cos_phi1 = std::cos(phi1);
T cos_phi2 = std::cos(phi2);
T a = std::cos(theta) * cos_phi1 * cos_phi2;
T b = std::sin(theta) * cos_phi1 * cos_phi2;
T c = std::sin(phi1) * cos_phi2;
T d = std::sin(phi2);
quaternion_type result(a, b, c, d);
result *= rho;
return result;
}
using quaternion = quaternion_type<float>;
// --------------------------------------------------------------------
// point, a location with x, y and z coordinates as floating point.
// This one is derived from a tuple<float,float,float> so
// you can do things like:
//
// float x, y, z;
// tie(x, y, z) = atom.loc();
template <typename F>
struct point_type
{
using value_type = F;
value_type m_x, m_y, m_z;
constexpr point_type()
: m_x(0)
, m_y(0)
, m_z(0)
{
}
constexpr point_type(value_type x, value_type y, value_type z)
: m_x(x)
, m_y(y)
, m_z(z)
{
}
template <typename PF>
constexpr point_type(const point_type<PF> &pt)
: m_x(static_cast<F>(pt.m_x))
, m_y(static_cast<F>(pt.m_y))
, m_z(static_cast<F>(pt.m_z))
{
}
constexpr point_type(const std::tuple<value_type, value_type, value_type> &pt)
: point_type(std::get<0>(pt), std::get<1>(pt), std::get<2>(pt))
{
}
#if HAVE_LIBCLIPPER
constexpr point_type(const clipper::Coord_orth &pt)
: m_x(pt[0])
, m_y(pt[1])
, m_z(pt[2])
{
}
constexpr point_type &operator=(const clipper::Coord_orth &rhs)
{
m_x = rhs[0];
m_y = rhs[1];
m_z = rhs[2];
return *this;
}
#endif
template <typename PF>
constexpr point_type &operator=(const point_type<PF> &rhs)
{
m_x = static_cast<F>(rhs.m_x);
m_y = static_cast<F>(rhs.m_y);
m_z = static_cast<F>(rhs.m_z);
return *this;
}
constexpr value_type &get_x() { return m_x; }
constexpr value_type get_x() const { return m_x; }
constexpr void set_x(value_type x) { m_x = x; }
constexpr value_type &get_y() { return m_y; }
constexpr value_type get_y() const { return m_y; }
constexpr void set_y(value_type y) { m_y = y; }
constexpr value_type &get_z() { return m_z; }
constexpr value_type get_z() const { return m_z; }
constexpr void set_z(value_type z) { m_z = z; }
constexpr point_type &operator+=(const point_type &rhs)
{
m_x += rhs.m_x;
m_y += rhs.m_y;
m_z += rhs.m_z;
return *this;
}
constexpr point_type &operator+=(value_type d)
{
m_x += d;
m_y += d;
m_z += d;
return *this;
}
constexpr point_type &operator-=(const point_type &rhs)
{
m_x -= rhs.m_x;
m_y -= rhs.m_y;
m_z -= rhs.m_z;
return *this;
}
constexpr point_type &operator-=(value_type d)
{
m_x -= d;
m_y -= d;
m_z -= d;
return *this;
}
constexpr point_type &operator*=(value_type rhs)
{
m_x *= rhs;
m_y *= rhs;
m_z *= rhs;
return *this;
}
constexpr point_type &operator/=(value_type rhs)
{
m_x /= rhs;
m_y /= rhs;
m_z /= rhs;
return *this;
}
constexpr value_type normalize()
{
auto length = m_x * m_x + m_y * m_y + m_z * m_z;
if (length > 0)
{
length = std::sqrt(length);
operator/=(length);
}
return length;
}
constexpr void rotate(const quaternion &q)
{
quaternion_type<value_type> p(0, m_x, m_y, m_z);
p = q * p * conj(q);
m_x = p.get_b();
m_y = p.get_c();
m_z = p.get_d();
}
#if HAVE_LIBCLIPPER
operator clipper::Coord_orth() const
{
return clipper::Coord_orth(m_x, m_y, m_z);
}
#endif
constexpr operator std::tuple<const value_type &, const value_type &, const value_type &>() const
{
return std::make_tuple(std::ref(m_x), std::ref(m_y), std::ref(m_z));
}
constexpr operator std::tuple<value_type &, value_type &, value_type &>()
{
return std::make_tuple(std::ref(m_x), std::ref(m_y), std::ref(m_z));
}
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;
}
// consider point as a vector... perhaps I should rename point?
constexpr value_type length_sq() const
{
return m_x * m_x + m_y * m_y + m_z * m_z;
}
constexpr value_type length() const
{
return std::sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
}
};
using point = point_type<float>;
template <typename F>
inline constexpr std::ostream &operator<<(std::ostream &os, const point_type<F> &pt)
{
os << '(' << pt.m_x << ',' << pt.m_y << ',' << pt.m_z << ')';
return os;
}
template <typename F>
inline constexpr point_type<F> operator+(const point_type<F> &lhs, const point_type<F> &rhs)
{
return point_type<F>(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z);
}
template <typename F>
inline constexpr point_type<F> operator-(const point_type<F> &lhs, const point_type<F> &rhs)
{
return point_type<F>(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z);
}
template <typename F>
inline constexpr point_type<F> operator-(const point_type<F> &pt)
{
return point_type<F>(-pt.m_x, -pt.m_y, -pt.m_z);
}
template <typename F>
inline constexpr point_type<F> operator*(const point_type<F> &pt, F f)
{
return point_type<F>(pt.m_x * f, pt.m_y * f, pt.m_z * f);
}
template <typename F>
inline constexpr point_type<F> operator*(F f, const point_type<F> &pt)
{
return point_type<F>(pt.m_x * f, pt.m_y * f, pt.m_z * f);
}
template <typename F>
inline constexpr point_type<F> operator/(const point_type<F> &pt, F f)
{
return point_type<F>(pt.m_x / f, pt.m_y / f, pt.m_z / f);
}
// --------------------------------------------------------------------
// several standard 3d operations
template <typename F>
inline constexpr auto distance_squared(const point_type<F> &a, const point_type<F> &b)
{
return (a.m_x - b.m_x) * (a.m_x - b.m_x) +
(a.m_y - b.m_y) * (a.m_y - b.m_y) +
(a.m_z - b.m_z) * (a.m_z - b.m_z);
}
template <typename F>
inline constexpr auto distance(const point_type<F> &a, const point_type<F> &b)
{
return std::sqrt(
(a.m_x - b.m_x) * (a.m_x - b.m_x) +
(a.m_y - b.m_y) * (a.m_y - b.m_y) +
(a.m_z - b.m_z) * (a.m_z - b.m_z));
}
template <typename F>
inline constexpr auto dot_product(const point_type<F> &a, const point_type<F> &b)
{
return a.m_x * b.m_x + a.m_y * b.m_y + a.m_z * b.m_z;
}
template <typename F>
inline constexpr point_type<F> cross_product(const point_type<F> &a, const point_type<F> &b)
{
return point_type<F>(a.m_y * b.m_z - b.m_y * a.m_z,
a.m_z * b.m_x - b.m_z * a.m_x,
a.m_x * b.m_y - b.m_x * a.m_y);
}
template <typename F>
constexpr auto angle(const point_type<F> &p1, const point_type<F> &p2, const point_type<F> &p3)
{
point_type<F> v1 = p1 - p2;
point_type<F> v2 = p3 - p2;
return std::acos(dot_product(v1, v2) / (v1.length() * v2.length())) * 180 / kPI;
}
template <typename F>
constexpr auto dihedral_angle(const point_type<F> &p1, const point_type<F> &p2, const point_type<F> &p3, const point_type<F> &p4)
{
point_type<F> v12 = p1 - p2; // vector from p2 to p1
point_type<F> v43 = p4 - p3; // vector from p3 to p4
point_type<F> z = p2 - p3; // vector from p3 to p2
point_type<F> p = cross_product(z, v12);
point_type<F> x = cross_product(z, v43);
point_type<F> y = cross_product(z, x);
auto u = dot_product(x, x);
auto v = dot_product(y, y);
F result = 360;
if (u > 0 and v > 0)
{
u = dot_product(p, x) / std::sqrt(u);
v = dot_product(p, y) / std::sqrt(v);
if (u != 0 or v != 0)
result = std::atan2(v, u) * static_cast<F>(180 / kPI);
}
return result;
}
template <typename F>
constexpr auto cosinus_angle(const point_type<F> &p1, const point_type<F> &p2, const point_type<F> &p3, const point_type<F> &p4)
{
point_type<F> v12 = p1 - p2;
point_type<F> v34 = p3 - p4;
auto x = dot_product(v12, v12) * dot_product(v34, v34);
return x > 0 ? dot_product(v12, v34) / std::sqrt(x) : 0;
}
template <typename F>
constexpr auto distance_point_to_line(const point_type<F> &l1, const point_type<F> &l2, const point_type<F> &p)
{
auto line = l2 - l1;
auto p_to_l1 = p - l1;
auto p_to_l2 = p - l2;
auto cross = cross_product(p_to_l1, p_to_l2);
return cross.length() / line.length();
}
// --------------------------------------------------------------------
// For e.g. simulated annealing, returns a new point that is moved in
// a random direction with a distance randomly chosen from a normal
// distribution with a stddev of offset.
point nudge(point p, float offset);
// --------------------------------------------------------------------
quaternion construct_from_angle_axis(float angle, point axis);
std::tuple<double, point> quaternion_to_angle_axis(quaternion q);
point centroid(const std::vector<point> &Points);
point center_points(std::vector<point> &Points);
/// \brief Returns how the two sets of points \a a and \b b can be aligned
///
/// \param a The first set of points
/// \param b The second set of points
/// \result The quaternion which should be applied to the points in \a a to
/// obtain the best superposition.
quaternion align_points(const std::vector<point> &a, const std::vector<point> &b);
/// \brief The RMSd for the points in \a a and \a b
double RMSd(const std::vector<point> &a, const std::vector<point> &b);
// --------------------------------------------------------------------
// Helper class to generate evenly divided points on a sphere
// we use a fibonacci sphere to calculate even distribution of the dots
template <int N>
class spherical_dots
{
public:
constexpr static int P = 2 * N * 1;
using array_type = typename std::array<point, P>;
using iterator = typename array_type::const_iterator;
static spherical_dots &instance()
{
static spherical_dots sInstance;
return sInstance;
}
size_t size() const { return m_points.size(); }
const point operator[](uint32_t inIx) const { return m_points[inIx]; }
iterator begin() const { return m_points.begin(); }
iterator end() const { return m_points.end(); }
double weight() const { return m_weight; }
spherical_dots()
{
const double
kGoldenRatio = (1 + std::sqrt(5.0)) / 2;
m_weight = (4 * kPI) / P;
auto p = m_points.begin();
for (int32_t i = -N; i <= N; ++i)
{
double lat = std::asin((2.0 * i) / P);
double lon = std::fmod(i, kGoldenRatio) * 2 * kPI / kGoldenRatio;
p->m_x = std::sin(lon) * std::cos(lat);
p->m_y = std::cos(lon) * std::cos(lat);
p->m_z = std::sin(lat);
++p;
}
}
private:
array_type m_points;
double m_weight;
};
} // namespace cif

305
include/cif++/row.hpp Normal file
View File

@@ -0,0 +1,305 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <cif++/item.hpp>
namespace cif
{
namespace detail
{
// some helper classes to help create tuple result types
template <typename... C>
struct get_row_result
{
static constexpr size_t N = sizeof...(C);
get_row_result(const row_handle &r, std::array<size_t, N> &&columns)
: m_row(r)
, m_columns(std::move(columns))
{
}
const item_handle operator[](size_t ix) const
{
return m_row[m_columns[ix]];
}
template <typename... Ts, std::enable_if_t<N == sizeof...(Ts), int> = 0>
operator std::tuple<Ts...>() const
{
return get<Ts...>(std::index_sequence_for<Ts...>{});
}
template <typename... Ts, std::size_t... Is>
std::tuple<Ts...> get(std::index_sequence<Is...>) const
{
return std::tuple<Ts...>{ m_row[m_columns[Is]].template as<Ts>()... };
}
const row_handle &m_row;
std::array<size_t, N> m_columns;
};
// we want to be able to tie some variables to a get_row_result, for this we use tiewraps
template <typename... Ts>
struct tie_wrap
{
tie_wrap(Ts... args)
: m_value(args...)
{
}
template <typename RR>
void operator=(const RR &&rr)
{
// get_row_result will do the conversion, but only if the types
// are compatible. That means the number of parameters to the get()
// of the row should be equal to the number of items in the tuple
// you are trying to tie.
using RType = std::tuple<typename std::remove_reference<Ts>::type...>;
m_value = static_cast<RType>(rr);
}
std::tuple<Ts...> m_value;
};
} // namespace detail
template <typename... Ts>
auto tie(Ts &...v)
{
return detail::tie_wrap<Ts &...>(std::forward<Ts &>(v)...);
}
// --------------------------------------------------------------------
/// \brief the row class, this one is not directly accessible from the outside
class row : public std::vector<item_value>
{
public:
row() = default;
item_value* get(size_t ix)
{
return ix < size() ? &at(ix) : nullptr;
}
const item_value* get(size_t ix) const
{
return ix < size() ? &at(ix) : nullptr;
}
private:
friend class category;
friend class category_index;
template <typename, typename...>
friend class iterator_impl;
void append(size_t ix, item_value &&iv)
{
if (ix >= size())
resize(ix + 1);
at(ix) = std::move(iv);
}
void remove(size_t ix)
{
if (ix < size())
at(ix) = item_value{};
}
row *m_next = nullptr;
};
// --------------------------------------------------------------------
/// \brief row_handle is the way to access data stored in rows
class row_handle
{
public:
friend class item_handle;
friend class category;
friend class category_index;
friend class row_initializer;
row_handle() = default;
row_handle(const row_handle &) = default;
row_handle(row_handle &&) = default;
row_handle &operator=(const row_handle &) = default;
row_handle &operator=(row_handle &&) = default;
row_handle(const category &cat, const row &r)
: m_category(const_cast<category *>(&cat))
, m_row(const_cast<row *>(&r))
{
}
const category &get_category() const
{
return *m_category;
}
bool empty() const
{
return m_category == nullptr or m_row == nullptr;
}
explicit operator bool() const
{
return not empty();
}
item_handle operator[](uint32_t column_ix)
{
return empty() ? item_handle::s_null_item : item_handle(column_ix, *this);
}
const item_handle operator[](uint32_t column_ix) const
{
return empty() ? item_handle::s_null_item : item_handle(column_ix, const_cast<row_handle &>(*this));
}
item_handle operator[](std::string_view column_name)
{
return empty() ? item_handle::s_null_item : item_handle(add_column(column_name), *this);
}
const item_handle operator[](std::string_view column_name) const
{
return empty() ? item_handle::s_null_item : item_handle(get_column_ix(column_name), const_cast<row_handle &>(*this));
}
template <typename... C>
auto get(C... columns) const
{
return detail::get_row_result<C...>(*this, { get_column_ix(columns)... });
}
template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C), int> = 0>
std::tuple<Ts...> get(C... columns) const
{
return detail::get_row_result<Ts...>(*this, { get_column_ix(columns)... });
}
template <typename T>
T get(const char *column)
{
return operator[](get_column_ix(column)).template as<T>();
}
void assign(const std::vector<item> &values)
{
for (auto &value : values)
assign(value, true);
}
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
{
assign(add_column(name), value, updateLinked, validate);
}
void assign(size_t column, std::string_view value, bool updateLinked, bool validate = true);
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
private:
uint16_t get_column_ix(std::string_view name) const;
std::string_view get_column_name(uint16_t ix) const;
uint16_t add_column(std::string_view name);
row *get_row()
{
return m_row;
}
const row *get_row() const
{
return m_row;
}
void assign(const item &i, bool updateLinked)
{
assign(i.name(), i.value(), updateLinked);
}
void swap(size_t column, row_handle &r);
category *m_category = nullptr;
row *m_row = nullptr;
};
// --------------------------------------------------------------------
class row_initializer : public std::vector<item>
{
public:
friend class category;
row_initializer() = default;
row_initializer(const row_initializer &) = default;
row_initializer(row_initializer &&) = default;
row_initializer &operator=(const row_initializer &) = default;
row_initializer &operator=(row_initializer &&) = default;
row_initializer(std::initializer_list<item> items)
: std::vector<item>(items)
{
}
template <typename ItemIter, std::enable_if_t<std::is_same_v<typename ItemIter::value_type, item>, int> = 0>
row_initializer(ItemIter b, ItemIter e)
: std::vector<item>(b, e)
{
}
row_initializer(row_handle rh);
void set_value(std::string_view name, std::string_view value);
void set_value(const item &i)
{
set_value(i.name(), i.value());
}
void set_value_if_empty(std::string_view name, std::string_view value);
void set_value_if_empty(const item &i)
{
set_value_if_empty(i.name(), i.value());
}
};
} // namespace cif

145
include/cif++/symmetry.hpp Normal file
View File

@@ -0,0 +1,145 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <array>
#include <cstdint>
#include <string>
namespace cif
{
// --------------------------------------------------------------------
enum class space_group_name
{
full,
xHM,
Hall
};
struct space_group
{
const char *name;
const char *xHM;
const char *Hall;
int nr;
};
extern const space_group kSpaceGroups[];
extern const std::size_t kNrOfSpaceGroups;
// --------------------------------------------------------------------
struct symop_data
{
constexpr symop_data(const std::array<int, 15> &data)
: m_packed((data[0] & 0x03ULL) << 34 bitor
(data[1] & 0x03ULL) << 32 bitor
(data[2] & 0x03ULL) << 30 bitor
(data[3] & 0x03ULL) << 28 bitor
(data[4] & 0x03ULL) << 26 bitor
(data[5] & 0x03ULL) << 24 bitor
(data[6] & 0x03ULL) << 22 bitor
(data[7] & 0x03ULL) << 20 bitor
(data[8] & 0x03ULL) << 18 bitor
(data[9] & 0x07ULL) << 15 bitor
(data[10] & 0x07ULL) << 12 bitor
(data[11] & 0x07ULL) << 9 bitor
(data[12] & 0x07ULL) << 6 bitor
(data[13] & 0x07ULL) << 3 bitor
(data[14] & 0x07ULL) << 0)
{
}
bool operator==(const symop_data &rhs) const
{
return m_packed == rhs.m_packed;
}
std::array<int, 15> data() const
{
return {
static_cast<int>(m_packed >> 34) bitand 0x03,
static_cast<int>(m_packed >> 32) bitand 0x03,
static_cast<int>(m_packed >> 30) bitand 0x03,
static_cast<int>(m_packed >> 28) bitand 0x03,
static_cast<int>(m_packed >> 26) bitand 0x03,
static_cast<int>(m_packed >> 24) bitand 0x03,
static_cast<int>(m_packed >> 22) bitand 0x03,
static_cast<int>(m_packed >> 20) bitand 0x03,
static_cast<int>(m_packed >> 18) bitand 0x03,
static_cast<int>(m_packed >> 15) bitand 0x07,
static_cast<int>(m_packed >> 12) bitand 0x07,
static_cast<int>(m_packed >> 9) bitand 0x07,
static_cast<int>(m_packed >> 6) bitand 0x07,
static_cast<int>(m_packed >> 3) bitand 0x07,
static_cast<int>(m_packed >> 0) bitand 0x07,
};
}
private:
friend struct symop_datablock;
const uint64_t kPackMask = (~0ULL >> (64 - 36));
symop_data(uint64_t v)
: m_packed(v bitand kPackMask)
{
}
uint64_t m_packed;
};
struct symop_datablock
{
constexpr symop_datablock(int spacegroup, int rotational_number, const std::array<int, 15> &rt_data)
: m_v((spacegroup & 0xffffULL) << 48 bitor
(rotational_number & 0xffULL) << 40 bitor
symop_data(rt_data).m_packed)
{
}
uint16_t spacegroup() const { return m_v >> 48; }
symop_data symop() const { return symop_data(m_v); }
uint8_t rotational_number() const { return (m_v >> 40) bitand 0xff; }
private:
uint64_t m_v;
};
static_assert(sizeof(symop_datablock) == sizeof(uint64_t), "Size of symop_data is wrong");
extern const symop_datablock kSymopNrTable[];
extern const std::size_t kSymopNrTableSize;
// --------------------------------------------------------------------
int get_space_group_number(std::string spacegroup); // alternative for clipper's parsing code, using space_group_name::full
int get_space_group_number(std::string spacegroup, space_group_name type); // alternative for clipper's parsing code
} // namespace cif

458
include/cif++/text.hpp Normal file
View File

@@ -0,0 +1,458 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <charconv>
#include <cmath>
#include <set>
#include <sstream>
#include <tuple>
#include <vector>
#if __has_include(<experimental/type_traits>)
#include <experimental/type_traits>
#endif
namespace cif
{
// --------------------------------------------------------------------
// some basic utilities: Since we're using ASCII input only, we define for optimisation
// our own case conversion routines.
bool iequals(std::string_view a, std::string_view b);
int icompare(std::string_view a, std::string_view b);
bool iequals(const char *a, const char *b);
int icompare(const char *a, const char *b);
void to_lower(std::string &s);
std::string to_lower_copy(std::string_view s);
void to_upper(std::string &s);
// std::string toUpperCopy(const std::string &s);
template <typename IterType>
std::string join(IterType b, IterType e, std::string_view sep)
{
std::ostringstream s;
if (b != e)
{
auto ai = b;
auto ni = std::next(ai);
for (;;)
{
s << *ai;
if (ni == e)
break;
ai = ni;
ni = std::next(ai);
s << sep;
}
}
return s.str();
}
template <typename V>
std::string join(const V &arr, std::string_view sep)
{
return join(arr.begin(), arr.end(), sep);
}
template <typename StringType = std::string_view>
std::vector<StringType> split(std::string_view s, std::string_view separators, bool suppress_empty = false)
{
std::vector<StringType> result;
auto b = s.begin();
auto e = b;
while (e != s.end())
{
if (separators.find(*e) != std::string_view::npos)
{
if (e > b or not suppress_empty)
result.emplace_back(b, e - b);
b = e = e + 1;
continue;
}
++e;
}
if (e > b or not suppress_empty)
result.emplace_back(b, e - b);
return result;
}
void replace_all(std::string &s, std::string_view what, std::string_view with = {});
#if defined(__cpp_lib_starts_ends_with)
inline bool starts_with(std::string s, std::string_view with)
{
return s.starts_with(with);
}
inline bool ends_with(std::string_view s, std::string_view with)
{
return s.ends_with(with);
}
#else
inline bool starts_with(std::string s, std::string_view with)
{
return s.compare(0, with.length(), with) == 0;
}
inline bool ends_with(std::string_view s, std::string_view with)
{
return s.length() >= with.length() and s.compare(s.length() - with.length(), with.length(), with) == 0;
}
#endif
#if defined(__cpp_lib_string_contains)
inline bool contains(std::string_view s, std::string_view q)
{
return s.contains(q);
}
#else
inline bool contains(std::string_view s, std::string_view q)
{
return s.find(q) != std::string_view::npos;
}
#endif
bool icontains(std::string_view s, std::string_view q);
void trim_left(std::string &s);
void trim_right(std::string &s);
void trim(std::string &s);
std::string trim_left_copy(std::string_view s);
std::string trim_right_copy(std::string_view s);
std::string trim_copy(std::string_view s);
// To make life easier, we also define iless and iset using iequals
struct iless
{
bool operator()(const std::string &a, const std::string &b) const
{
return icompare(a, b) < 0;
}
};
typedef std::set<std::string, iless> iset;
// --------------------------------------------------------------------
// This really makes a difference, having our own tolower routines
extern const uint8_t kCharToLowerMap[256];
inline char tolower(int ch)
{
return static_cast<char>(kCharToLowerMap[static_cast<uint8_t>(ch)]);
}
// --------------------------------------------------------------------
std::tuple<std::string, std::string> split_tag_name(std::string_view tag);
// --------------------------------------------------------------------
// generate a cif name, mainly used to generate asym_id's
std::string cif_id_for_number(int number);
// --------------------------------------------------------------------
// custom wordwrapping routine
std::vector<std::string> word_wrap(const std::string &text, size_t width);
// --------------------------------------------------------------------
/// std::from_chars for floating point types.
/// These are optional, there's a selected_charconv class below that selects
/// the best option to used 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;
}
enum class chars_format
{
scientific = 1,
fixed = 2,
// hex,
general = fixed | scientific
};
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 = last - first;
int r;
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;
}
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 = last - first;
int r;
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;
}
template <typename T>
struct my_charconv
{
static std::from_chars_result from_chars(const char *a, const char *b, T &d)
{
return cif::from_chars(a, b, d);
}
static std::to_chars_result to_chars(char *first, char *last, T &value, chars_format fmt)
{
return cif::to_chars(first, last, value, fmt);
}
};
template <typename T>
struct std_charconv
{
static std::from_chars_result from_chars(const char *a, const char *b, T &d)
{
return std::from_chars(a, b, d);
}
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>
using from_chars_function = decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(), std::declval<T &>()));
template <typename T>
using selected_charconv = typename std::conditional_t<std::experimental::is_detected_v<from_chars_function, T>, std_charconv<T>, my_charconv<T>>;
} // namespace cif

View File

@@ -26,70 +26,36 @@
#pragma once
#include "cif++/Config.hpp"
#include <vector>
#include <set>
#include <cassert>
#include <memory>
#include <list>
#include <iostream>
#include <filesystem>
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#if _MSC_VER
#include <io.h>
#define isatty _isatty
#else
#include <unistd.h>
#endif
#if _MSC_VER
#pragma warning(disable : 4996) // unsafe function or variable (strcpy e.g.)
#pragma warning(disable : 4068) // unknown pragma
#pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4101) // unreferenced local variable
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1
#endif
namespace cif
{
extern int VERBOSE;
// the git 'build' number
std::string get_version_nr();
// std::string get_version_date();
// --------------------------------------------------------------------
// some basic utilities: Since we're using ASCII input only, we define for optimisation
// our own case conversion routines.
bool iequals(const std::string& a, const std::string& b);
int icompare(const std::string& a, const std::string& b);
bool iequals(const char* a, const char* b);
int icompare(const char* a, const char* b);
void toLower(std::string& s);
std::string toLowerCopy(const std::string& s);
// To make life easier, we also define iless and iset using iequals
struct iless
{
bool operator()(const std::string& a, const std::string& b) const
{
return icompare(a, b) < 0;
}
};
typedef std::set<std::string, iless> iset;
// --------------------------------------------------------------------
// This really makes a difference, having our own tolower routines
extern const uint8_t kCharToLowerMap[256];
inline char tolower(char ch)
{
return static_cast<char>(kCharToLowerMap[static_cast<uint8_t>(ch)]);
}
// --------------------------------------------------------------------
std::tuple<std::string,std::string> splitTagName(const std::string& tag);
// --------------------------------------------------------------------
// custom wordwrapping routine
std::vector<std::string> wordWrap(const std::string& text, unsigned int width);
// --------------------------------------------------------------------
// Code helping with terminal i/o
@@ -103,26 +69,41 @@ std::string get_executable_path();
// --------------------------------------------------------------------
// some manipulators to write coloured text to terminals
enum StringColour {
scBLACK = 0, scRED, scGREEN, scYELLOW, scBLUE, scMAGENTA, scCYAN, scWHITE, scNONE = 9 };
enum StringColour
{
scBLACK = 0,
scRED,
scGREEN,
scYELLOW,
scBLUE,
scMAGENTA,
scCYAN,
scWHITE,
scNONE = 9
};
template<typename String, typename CharT>
template <typename String, typename CharT>
struct ColouredString
{
static_assert(std::is_reference<String>::value or std::is_pointer<String>::value, "String type must be pointer or reference");
ColouredString(String s, StringColour fore, StringColour back, bool bold = true)
: m_s(s), m_fore(fore), m_back(back), m_bold(bold) {}
ColouredString& operator=(const ColouredString&) = delete;
: m_s(s)
, m_fore(fore)
, m_back(back)
, m_bold(bold)
{
}
ColouredString &operator=(const ColouredString &) = delete;
String m_s;
StringColour m_fore, m_back;
bool m_bold;
};
template<typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const ColouredString<const CharT*,CharT>& s)
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os, const ColouredString<const CharT *, CharT> &s)
{
if (isatty(STDOUT_FILENO))
{
@@ -130,15 +111,15 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
ostr << "\033[" << (30 + s.m_fore) << ';' << (s.m_bold ? "1" : "22") << ';' << (40 + s.m_back) << 'm'
<< s.m_s
<< "\033[0m";
return os << ostr.str();
}
else
return os << s.m_s;
}
template<typename CharT, typename Traits, typename String>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const ColouredString<String,CharT>& s)
template <typename CharT, typename Traits, typename String>
std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os, const ColouredString<String, CharT> &s)
{
if (isatty(STDOUT_FILENO))
{
@@ -146,27 +127,27 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
ostr << "\033[" << (30 + s.m_fore) << ';' << (s.m_bold ? "1" : "22") << ';' << (40 + s.m_back) << 'm'
<< s.m_s
<< "\033[0m";
return os << ostr.str();
}
else
return os << s.m_s;
}
template<typename CharT>
inline auto coloured(const CharT* s, StringColour fore = scWHITE, StringColour back = scRED, bool bold = true)
template <typename CharT>
inline auto coloured(const CharT *s, StringColour fore = scWHITE, StringColour back = scRED, bool bold = true)
{
return ColouredString<const CharT*, CharT>(s, fore, back, bold);
return ColouredString<const CharT *, CharT>(s, fore, back, bold);
}
template<typename CharT, typename Traits, typename Alloc>
inline auto coloured(const std::basic_string<CharT, Traits, Alloc>& s, StringColour fore = scWHITE, StringColour back = scRED, bool bold = true)
template <typename CharT, typename Traits, typename Alloc>
inline auto coloured(const std::basic_string<CharT, Traits, Alloc> &s, StringColour fore = scWHITE, StringColour back = scRED, bool bold = true)
{
return ColouredString<const std::basic_string<CharT, Traits, Alloc>, CharT>(s, fore, back, bold);
}
template<typename CharT, typename Traits, typename Alloc>
inline auto coloured(std::basic_string<CharT, Traits, Alloc>& s, StringColour fore = scWHITE, StringColour back = scRED, bool bold = true)
template <typename CharT, typename Traits, typename Alloc>
inline auto coloured(std::basic_string<CharT, Traits, Alloc> &s, StringColour fore = scWHITE, StringColour back = scRED, bool bold = true)
{
return ColouredString<std::basic_string<CharT, Traits, Alloc>, CharT>(s, fore, back, bold);
}
@@ -177,26 +158,26 @@ inline auto coloured(std::basic_string<CharT, Traits, Alloc>& s, StringColour fo
class Progress
{
public:
Progress(int64_t inMax, const std::string& inAction);
virtual ~Progress();
void consumed(int64_t inConsumed); // consumed is relative
void progress(int64_t inProgress); // progress is absolute
Progress(int64_t inMax, const std::string &inAction);
virtual ~Progress();
void message(const std::string& inMessage);
void consumed(int64_t inConsumed); // consumed is relative
void progress(int64_t inProgress); // progress is absolute
void message(const std::string &inMessage);
private:
Progress(const Progress&);
Progress& operator=(const Progress&);
Progress(const Progress &) = delete;
Progress &operator=(const Progress &) = delete;
struct ProgressImpl* mImpl;
struct ProgressImpl *m_impl;
};
// --------------------------------------------------------------------
// Resources
std::unique_ptr<std::istream> loadResource(std::filesystem::path name);
}
std::unique_ptr<std::istream> load_resource(std::filesystem::path name);
void add_file_resource(const std::string &name, std::filesystem::path dataFile);
void add_data_directory(std::filesystem::path dataDir);
} // namespace cif

242
include/cif++/validate.hpp Normal file
View File

@@ -0,0 +1,242 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <filesystem>
#include <list>
#include <mutex>
#include <utility>
#include <cif++/text.hpp>
namespace cif
{
struct category_validator;
// --------------------------------------------------------------------
class validation_error : public std::exception
{
public:
validation_error(const std::string &msg);
validation_error(const std::string &cat, const std::string &item,
const std::string &msg);
const char *what() const noexcept { return m_msg.c_str(); }
std::string m_msg;
};
// --------------------------------------------------------------------
enum class DDL_PrimitiveType
{
Char,
UChar,
Numb
};
DDL_PrimitiveType map_to_primitive_type(std::string_view s);
struct regex_impl;
struct type_validator
{
std::string m_name;
DDL_PrimitiveType m_primitive_type;
regex_impl *m_rx;
type_validator() = delete;
type_validator(std::string_view name, DDL_PrimitiveType type, std::string_view rx);
type_validator(const type_validator &) = delete;
type_validator(type_validator &&rhs)
: m_name(std::move(rhs.m_name))
, m_primitive_type(rhs.m_primitive_type)
{
m_rx = std::exchange(rhs.m_rx, nullptr);
}
type_validator &operator=(const type_validator &) = delete;
type_validator &operator=(type_validator &&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;
}
~type_validator();
bool operator<(const type_validator &rhs) const
{
return icompare(m_name, rhs.m_name) < 0;
}
int compare(std::string_view a, std::string_view b) const;
};
struct item_validator
{
std::string m_tag;
bool m_mandatory;
const type_validator *m_type;
cif::iset m_enums;
std::string m_default;
bool m_default_is_null;
category_validator *m_category = nullptr;
// ItemLinked is used for non-key links
struct item_link
{
item_validator *m_parent;
std::string m_parent_item;
std::string m_child_item;
};
std::vector<item_link> mLinked;
bool operator<(const item_validator &rhs) const
{
return icompare(m_tag, rhs.m_tag) < 0;
}
bool operator==(const item_validator &rhs) const
{
return iequals(m_tag, rhs.m_tag);
}
void operator()(std::string_view value) const;
};
struct category_validator
{
std::string m_name;
std::vector<std::string> m_keys;
cif::iset m_groups;
cif::iset m_mandatory_fields;
std::set<item_validator> m_item_validators;
bool operator<(const category_validator &rhs) const
{
return icompare(m_name, rhs.m_name) < 0;
}
void addItemValidator(item_validator &&v);
const item_validator *get_validator_for_item(std::string_view tag) const;
const std::set<item_validator> &item_validators() const
{
return m_item_validators;
}
};
struct link_validator
{
int m_link_group_id;
std::string m_parent_category;
std::vector<std::string> m_parent_keys;
std::string m_child_category;
std::vector<std::string> m_child_keys;
std::string m_link_group_label;
};
// --------------------------------------------------------------------
class validator
{
public:
validator(std::string_view name)
: m_name(name)
{
}
~validator() = default;
validator(const validator &rhs) = delete;
validator &operator=(const validator &rhs) = delete;
validator(validator &&rhs) = default;
validator &operator=(validator &&rhs) = default;
friend class dictionary_parser;
void add_type_validator(type_validator &&v);
const type_validator *get_validator_for_type(std::string_view type_code) const;
void add_category_validator(category_validator &&v);
const category_validator *get_validator_for_category(std::string_view category) const;
void add_link_validator(link_validator &&v);
std::vector<const link_validator *> get_links_for_parent(std::string_view category) const;
std::vector<const link_validator *> get_links_for_child(std::string_view category) const;
void report_error(const std::string &msg, bool fatal) const;
const std::string &name() const { return m_name; }
void set_name(const std::string &name) { m_name = name; }
const std::string &version() const { return m_version; }
void version(const std::string &version) { m_version = version; }
private:
// name is fully qualified here:
item_validator *get_validator_for_item(std::string_view name) const;
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;
std::vector<link_validator> m_link_validators;
};
// --------------------------------------------------------------------
class validator_factory
{
public:
static validator_factory &instance()
{
static validator_factory s_instance;
return s_instance;
}
const validator &operator[](std::string_view dictionary_name);
private:
void construct_validator(std::string_view name, std::istream &is);
// --------------------------------------------------------------------
validator_factory() = default;
std::mutex m_mutex;
std::list<validator> m_validators;
};
} // namespace cif

View File

@@ -2,9 +2,12 @@ prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
datalibdir=@datarootdir@/libcifpp
Name: libcifpp
Description: C++ library for the manipulation of mmCIF files.
Version: @PACKAGE_VERSION@
Requires.private: zlib, liblzma
Libs: -L${libdir} -lcifpp
Cflags: -I${includedir}
Cflags: -I${includedir} -pthread

1
regex Submodule

Submodule regex added at e5979ae1af

File diff suppressed because it is too large Load Diff

150867
rsrc/mmcif_ma.dic Normal file

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,351 +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 <boost/algorithm/string.hpp>
#include "cif++/Cif++.hpp"
#include "cif++/CifParser.hpp"
#include "cif++/CifValidator.hpp"
namespace ba = boost::algorithm;
extern int VERBOSE;
namespace cif
{
ValidationError::ValidationError(const std::string& msg)
: mMsg(msg)
{
}
ValidationError::ValidationError(const std::string& cat, const std::string& item, const std::string& msg)
: mMsg("When validating _" + cat + '.' + item + ": " + msg)
{
}
// --------------------------------------------------------------------
DDL_PrimitiveType mapToPrimitiveType(const std::string& s)
{
DDL_PrimitiveType result;
if (iequals(s, "char"))
result = DDL_PrimitiveType::Char;
else if (iequals(s, "uchar"))
result = DDL_PrimitiveType::UChar;
else if (iequals(s, "numb"))
result = DDL_PrimitiveType::Numb;
else
throw ValidationError("Not a known primitive type");
return result;
}
// --------------------------------------------------------------------
int ValidateType::compare(const char* a, const char* b) const
{
int result = 0;
if (*a == 0)
result = *b == 0 ? 0 : -1;
else if (*b == 0)
result = *a == 0 ? 0 : +1;
else
{
try
{
switch (mPrimitiveType)
{
case DDL_PrimitiveType::Numb:
{
double da = strtod(a, nullptr);
double db = strtod(b, nullptr);
auto d = da - db;
if (std::abs(d) > std::numeric_limits<double>::epsilon())
{
if (d > 0)
result = 1;
else if (d < 0)
result = -1;
}
break;
}
case DDL_PrimitiveType::UChar:
case DDL_PrimitiveType::Char:
{
// CIF is guaranteed to have ascii only, therefore this primitive code will do
// also, we're collapsing spaces
auto ai = a, bi = b;
for (;;)
{
if (*ai == 0)
{
if (*bi != 0)
result = -1;
break;
}
else if (*bi == 0)
{
result = 1;
break;
}
char ca = *ai;
char cb = *bi;
if (mPrimitiveType == DDL_PrimitiveType::UChar)
{
ca = toupper(ca);
cb = toupper(cb);
}
result = ca - cb;
if (result != 0)
break;
if (ca == ' ')
{
while (ai[1] == ' ')
++ai;
while (bi[1] == ' ')
++bi;
}
++ai;
++bi;
}
break;
}
}
}
catch (const std::invalid_argument& ex)
{
result = 1;
}
}
return result;
}
// --------------------------------------------------------------------
//void ValidateItem::addLinked(ValidateItem* parent, const std::string& parentItem, const std::string& childItem)
//{
//// if (mParent != nullptr and VERBOSE)
//// cerr << "replacing parent in " << mCategory->mName << " from " << mParent->mCategory->mName << " to " << parent->mCategory->mName << endl;
//// mParent = parent;
//
// if (mType == nullptr and parent != nullptr)
// mType = parent->mType;
//
// if (parent != nullptr)
// {
// mLinked.push_back({parent, parentItem, childItem});
//
// parent->mChildren.insert(this);
////
//// if (mCategory->mKeys == std::vector<std::string>{mTag})
//// parent->mForeignKeys.insert(this);
// }
//}
void ValidateItem::operator()(std::string value) const
{
if (not value.empty() and value != "?" and value != ".")
{
if (mType != nullptr and not std::regex_match(value, mType->mRx))
throw ValidationError(mCategory->mName, mTag, "Value '" + value + "' does not match type expression for type " + mType->mName);
if (not mEnums.empty())
{
if (mEnums.count(value) == 0)
throw ValidationError(mCategory->mName, mTag, "Value '" + value + "' is not in the list of allowed values");
}
}
}
// --------------------------------------------------------------------
void ValidateCategory::addItemValidator(ValidateItem&& v)
{
if (v.mMandatory)
mMandatoryFields.insert(v.mTag);
v.mCategory = this;
auto r = mItemValidators.insert(std::move(v));
if (not r.second and VERBOSE >= 4)
std::cout << "Could not add validator for item " << v.mTag << " to category " << mName << std::endl;
}
const ValidateItem* ValidateCategory::getValidatorForItem(std::string tag) const
{
const ValidateItem* result = nullptr;
auto i = mItemValidators.find(ValidateItem{tag});
if (i != mItemValidators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for tag " << tag << std::endl;
return result;
}
// --------------------------------------------------------------------
Validator::Validator()
{
}
Validator::~Validator()
{
}
void Validator::addTypeValidator(ValidateType&& v)
{
auto r = mTypeValidators.insert(std::move(v));
if (not r.second and VERBOSE > 4)
std::cout << "Could not add validator for type " << v.mName << std::endl;
}
const ValidateType* Validator::getValidatorForType(std::string typeCode) const
{
const ValidateType* result = nullptr;
auto i = mTypeValidators.find(ValidateType{ typeCode, DDL_PrimitiveType::Char, std::regex() });
if (i != mTypeValidators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for type " << typeCode << std::endl;
return result;
}
void Validator::addCategoryValidator(ValidateCategory&& v)
{
auto r = mCategoryValidators.insert(std::move(v));
if (not r.second and VERBOSE > 4)
std::cout << "Could not add validator for category " << v.mName << std::endl;
}
const ValidateCategory* Validator::getValidatorForCategory(std::string category) const
{
const ValidateCategory* result = nullptr;
auto i = mCategoryValidators.find(ValidateCategory{category});
if (i != mCategoryValidators.end())
result = &*i;
else if (VERBOSE > 4)
std::cout << "No validator for category " << category << std::endl;
return result;
}
ValidateItem* Validator::getValidatorForItem(std::string tag) const
{
ValidateItem* result = nullptr;
std::string cat, item;
std::tie(cat, item) = splitTagName(tag);
auto* cv = getValidatorForCategory(cat);
if (cv != nullptr)
result = const_cast<ValidateItem*>(cv->getValidatorForItem(item));
if (result == nullptr and VERBOSE > 4)
std::cout << "No validator for item " << tag << std::endl;
return result;
}
void Validator::addLinkValidator(ValidateLink&& v)
{
assert(v.mParentKeys.size() == v.mChildKeys.size());
if (v.mParentKeys.size() != v.mChildKeys.size())
throw std::runtime_error("unequal number of keys for parent and child in link");
auto pcv = getValidatorForCategory(v.mParentCategory);
auto ccv = getValidatorForCategory(v.mChildCategory);
if (pcv == nullptr)
throw std::runtime_error("unknown parent category " + v.mParentCategory);
if (ccv == nullptr)
throw std::runtime_error("unknown child category " + v.mChildCategory);
for (size_t i = 0; i < v.mParentKeys.size(); ++i)
{
auto piv = pcv->getValidatorForItem(v.mParentKeys[i]);
if (piv == nullptr)
throw std::runtime_error("unknown parent tag _" + v.mParentCategory + '.' + v.mParentKeys[i]);
auto civ = ccv->getValidatorForItem(v.mChildKeys[i]);
if (civ == nullptr)
throw std::runtime_error("unknown child tag _" + v.mChildCategory + '.' + v.mChildKeys[i]);
if (civ->mType == nullptr and piv->mType != nullptr)
const_cast<ValidateItem*>(civ)->mType = piv->mType;
}
mLinkValidators.emplace_back(std::move(v));
}
std::vector<const ValidateLink*> Validator::getLinksForParent(const std::string& category) const
{
std::vector<const ValidateLink*> result;
for (auto& l: mLinkValidators)
{
if (l.mParentCategory == category)
result.push_back(&l);
}
return result;
}
std::vector<const ValidateLink*> Validator::getLinksForChild(const std::string& category) const
{
std::vector<const ValidateLink*> result;
for (auto& l: mLinkValidators)
{
if (l.mChildCategory == category)
result.push_back(&l);
}
return result;
}
void Validator::reportError(const std::string& msg, bool fatal)
{
if (mStrict or fatal)
throw ValidationError(msg);
else if (VERBOSE)
std::cerr << msg << std::endl;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,305 +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 <random>
#include <valarray>
#include "cif++/Point.hpp"
namespace mmcif
{
// --------------------------------------------------------------------
quaternion Normalize(quaternion q)
{
std::valarray<double> t(4);
t[0] = q.R_component_1();
t[1] = q.R_component_2();
t[2] = q.R_component_3();
t[3] = q.R_component_4();
t *= t;
double length = sqrt(t.sum());
if (length > 0.001)
q /= length;
else
q = quaternion(1, 0, 0, 0);
return q;
}
// --------------------------------------------------------------------
std::tuple<double,Point> QuaternionToAngleAxis(quaternion q)
{
if (q.R_component_1() > 1)
q = Normalize(q);
// angle:
double angle = 2 * acos(q.R_component_1());
angle = angle * 180 / kPI;
// axis:
double s = sqrt(1 - q.R_component_1() * q.R_component_1());
if (s < 0.001)
s = 1;
Point axis(q.R_component_2() / s, q.R_component_3() / s, q.R_component_4() / s);
return std::make_tuple(angle, axis);
}
Point CenterPoints(std::vector<Point>& Points)
{
Point t;
for (Point& pt : Points)
{
t.mX += pt.mX;
t.mY += pt.mY;
t.mZ += pt.mZ;
}
t.mX /= Points.size();
t.mY /= Points.size();
t.mZ /= Points.size();
for (Point& pt : Points)
{
pt.mX -= t.mX;
pt.mY -= t.mY;
pt.mZ -= t.mZ;
}
return t;
}
Point Centroid(std::vector<Point>& Points)
{
Point result;
for (Point& pt : Points)
result += pt;
result /= Points.size();
return result;
}
double RMSd(const std::vector<Point>& a, const std::vector<Point>& b)
{
double sum = 0;
for (uint32_t i = 0; i < a.size(); ++i)
{
std::valarray<double> d(3);
d[0] = b[i].mX - a[i].mX;
d[1] = b[i].mY - a[i].mY;
d[2] = b[i].mZ - a[i].mZ;
d *= d;
sum += d.sum();
}
return std::sqrt(sum / a.size());
}
// The next function returns the largest solution for a quartic equation
// based on Ferrari's algorithm.
// A depressed quartic is of the form:
//
// x^4 + ax^2 + bx + c = 0
//
// (since I'm too lazy to find out a better way, I've implemented the
// routine using complex values to avoid nan's as a result of taking
// sqrt of a negative number)
double LargestDepressedQuarticSolution(double a, double b, double c)
{
std::complex<double> P = - (a * a) / 12 - c;
std::complex<double> Q = - (a * a * a) / 108 + (a * c) / 3 - (b * b) / 8;
std::complex<double> R = - Q / 2.0 + std::sqrt((Q * Q) / 4.0 + (P * P * P) / 27.0);
std::complex<double> U = std::pow(R, 1 / 3.0);
std::complex<double> y;
if (U == 0.0)
y = -5.0 * a / 6.0 + U - std::pow(Q, 1.0 / 3.0);
else
y = -5.0 * a / 6.0 + U - P / (3.0 * U);
std::complex<double> W = std::sqrt(a + 2.0 * y);
// And to get the final result:
// result = (±W + sqrt(-(3 * alpha + 2 * y ± 2 * beta / W))) / 2;
// We want the largest result, so:
std::valarray<double> t(4);
t[0] = (( W + std::sqrt(-(3.0 * a + 2.0 * y + 2.0 * b / W))) / 2.0).real();
t[1] = (( W + std::sqrt(-(3.0 * a + 2.0 * y - 2.0 * b / W))) / 2.0).real();
t[2] = ((-W + std::sqrt(-(3.0 * a + 2.0 * y + 2.0 * b / W))) / 2.0).real();
t[3] = ((-W + std::sqrt(-(3.0 * a + 2.0 * y - 2.0 * b / W))) / 2.0).real();
return t.max();
}
//quaternion AlignPoints(const vector<Point>& pa, const vector<Point>& pb)
//{
// // First calculate M, a 3x3 matrix containing the sums of products of the coordinates of A and B
// matrix<double> M(3, 3, 0);
//
// for (uint32_t i = 0; i < pa.size(); ++i)
// {
// const Point& a = pa[i];
// const Point& b = pb[i];
//
// M(0, 0) += a.mX * b.mX; M(0, 1) += a.mX * b.mY; M(0, 2) += a.mX * b.mZ;
// M(1, 0) += a.mY * b.mX; M(1, 1) += a.mY * b.mY; M(1, 2) += a.mY * b.mZ;
// M(2, 0) += a.mZ * b.mX; M(2, 1) += a.mZ * b.mY; M(2, 2) += a.mZ * b.mZ;
// }
//
// // Now calculate N, a symmetric 4x4 matrix
// symmetric_matrix<double> N(4);
//
// N(0, 0) = M(0, 0) + M(1, 1) + M(2, 2);
// N(0, 1) = M(1, 2) - M(2, 1);
// N(0, 2) = M(2, 0) - M(0, 2);
// N(0, 3) = M(0, 1) - M(1, 0);
//
// N(1, 1) = M(0, 0) - M(1, 1) - M(2, 2);
// N(1, 2) = M(0, 1) + M(1, 0);
// N(1, 3) = M(0, 2) + M(2, 0);
//
// N(2, 2) = -M(0, 0) + M(1, 1) - M(2, 2);
// N(2, 3) = M(1, 2) + M(2, 1);
//
// N(3, 3) = -M(0, 0) - M(1, 1) + M(2, 2);
//
// // det(N - λI) = 0
// // find the largest λ (λm)
// //
// // Aλ4 + Bλ3 + Cλ2 + Dλ + E = 0
// // A = 1
// // B = 0
// // and so this is a so-called depressed quartic
// // solve it using Ferrari's algorithm
//
// double C = -2 * (
// M(0, 0) * M(0, 0) + M(0, 1) * M(0, 1) + M(0, 2) * M(0, 2) +
// M(1, 0) * M(1, 0) + M(1, 1) * M(1, 1) + M(1, 2) * M(1, 2) +
// M(2, 0) * M(2, 0) + M(2, 1) * M(2, 1) + M(2, 2) * M(2, 2));
//
// double D = 8 * (M(0, 0) * M(1, 2) * M(2, 1) +
// M(1, 1) * M(2, 0) * M(0, 2) +
// M(2, 2) * M(0, 1) * M(1, 0)) -
// 8 * (M(0, 0) * M(1, 1) * M(2, 2) +
// M(1, 2) * M(2, 0) * M(0, 1) +
// M(2, 1) * M(1, 0) * M(0, 2));
//
// double E =
// (N(0,0) * N(1,1) - N(0,1) * N(0,1)) * (N(2,2) * N(3,3) - N(2,3) * N(2,3)) +
// (N(0,1) * N(0,2) - N(0,0) * N(2,1)) * (N(2,1) * N(3,3) - N(2,3) * N(1,3)) +
// (N(0,0) * N(1,3) - N(0,1) * N(0,3)) * (N(2,1) * N(2,3) - N(2,2) * N(1,3)) +
// (N(0,1) * N(2,1) - N(1,1) * N(0,2)) * (N(0,2) * N(3,3) - N(2,3) * N(0,3)) +
// (N(1,1) * N(0,3) - N(0,1) * N(1,3)) * (N(0,2) * N(2,3) - N(2,2) * N(0,3)) +
// (N(0,2) * N(1,3) - N(2,1) * N(0,3)) * (N(0,2) * N(1,3) - N(2,1) * N(0,3));
//
// // solve quartic
// double lm = LargestDepressedQuarticSolution(C, D, E);
//
// // calculate t = (N - λI)
// matrix<double> li = identity_matrix<double>(4) * lm;
// matrix<double> t = N - li;
//
// // calculate a matrix of cofactors for t
// matrix<double> cf(4, 4);
//
// const uint32_t ixs[4][3] =
// {
// { 1, 2, 3 },
// { 0, 2, 3 },
// { 0, 1, 3 },
// { 0, 1, 2 }
// };
//
// uint32_t maxR = 0;
// for (uint32_t r = 0; r < 4; ++r)
// {
// const uint32_t* ir = ixs[r];
//
// for (uint32_t c = 0; c < 4; ++c)
// {
// const uint32_t* ic = ixs[c];
//
// cf(r, c) =
// t(ir[0], ic[0]) * t(ir[1], ic[1]) * t(ir[2], ic[2]) +
// t(ir[0], ic[1]) * t(ir[1], ic[2]) * t(ir[2], ic[0]) +
// t(ir[0], ic[2]) * t(ir[1], ic[0]) * t(ir[2], ic[1]) -
// t(ir[0], ic[2]) * t(ir[1], ic[1]) * t(ir[2], ic[0]) -
// t(ir[0], ic[1]) * t(ir[1], ic[0]) * t(ir[2], ic[2]) -
// t(ir[0], ic[0]) * t(ir[1], ic[2]) * t(ir[2], ic[1]);
// }
//
// if (r > maxR and cf(r, 0) > cf(maxR, 0))
// maxR = r;
// }
//
// // NOTE the negation of the y here, why? Maybe I swapped r/c above?
// quaternion q(cf(maxR, 0), cf(maxR, 1), -cf(maxR, 2), cf(maxR, 3));
// q = Normalize(q);
//
// return q;
//}
// --------------------------------------------------------------------
Point Nudge(Point p, float offset)
{
static std::random_device rd;
static std::mt19937_64 rng(rd());
std::uniform_real_distribution<> randomAngle(0, 2 * kPI);
std::normal_distribution<> randomOffset(0, offset);
float theta = randomAngle(rng);
float phi1 = randomAngle(rng) - kPI;
float phi2 = randomAngle(rng) - kPI;
quaternion q = boost::math::spherical(1.0f, theta, phi1, phi2);
Point r{ 0, 0, 1 };
r.rotate(q);
r *= randomOffset(rng);
return p + r;
}
}

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

1154
src/atom_type.cpp Normal file

File diff suppressed because it is too large Load Diff

2064
src/category.cpp Normal file

File diff suppressed because it is too large Load Diff

748
src/compound.cpp Normal file
View File

@@ -0,0 +1,748 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020-2022 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 <map>
#include <mutex>
#include <numeric>
#include <shared_mutex>
#include <filesystem>
#include <fstream>
#include <cif++/compound.hpp>
namespace fs = std::filesystem;
namespace cif
{
// --------------------------------------------------------------------
std::string to_string(bond_type bondType)
{
switch (bondType)
{
case bond_type::sing: return "sing";
case bond_type::doub: return "doub";
case bond_type::trip: return "trip";
case bond_type::quad: return "quad";
case bond_type::arom: return "arom";
case bond_type::poly: return "poly";
case bond_type::delo: return "delo";
case bond_type::pi: return "pi";
}
throw std::invalid_argument("Invalid bondType");
}
bond_type from_string(const std::string &bondType)
{
if (cif::iequals(bondType, "sing"))
return bond_type::sing;
if (cif::iequals(bondType, "doub"))
return bond_type::doub;
if (cif::iequals(bondType, "trip"))
return bond_type::trip;
if (cif::iequals(bondType, "quad"))
return bond_type::quad;
if (cif::iequals(bondType, "arom"))
return bond_type::arom;
if (cif::iequals(bondType, "poly"))
return bond_type::poly;
if (cif::iequals(bondType, "delo"))
return bond_type::delo;
if (cif::iequals(bondType, "pi"))
return bond_type::pi;
throw std::invalid_argument("Invalid bondType: " + bondType);
}
// --------------------------------------------------------------------
// compound helper classes
struct compound_atom_less
{
bool operator()(const compound_atom &a, const compound_atom &b) const
{
int d = a.id.compare(b.id);
if (d == 0)
d = a.type_symbol - b.type_symbol;
return d < 0;
}
};
struct compound_bond_less
{
bool operator()(const compound_bond &a, const compound_bond &b) const
{
int d = a.atom_id[0].compare(b.atom_id[0]);
if (d == 0)
d = a.atom_id[1].compare(b.atom_id[1]);
if (d == 0)
d = static_cast<int>(a.type) - static_cast<int>(b.type);
return d < 0;
}
};
// --------------------------------------------------------------------
// compound
compound::compound(cif::datablock &db)
{
auto &chemComp = db["chem_comp"];
if (chemComp.size() != 1)
throw std::runtime_error("Invalid compound file, chem_comp should contain a single row");
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)
{
compound_atom atom;
std::string type_symbol;
cif::tie(atom.id, type_symbol, atom.charge, atom.aromatic, atom.leaving_atom, atom.stereo_config, atom.x, atom.y, atom.z) =
row.get("atom_id", "type_symbol", "charge", "pdbx_aromatic_flag", "pdbx_leaving_atom_flag", "pdbx_stereo_config",
"model_Cartn_x", "model_Cartn_y", "model_Cartn_z");
atom.type_symbol = atom_type_traits(type_symbol).type();
m_atoms.push_back(std::move(atom));
}
auto &chemCompBond = db["chem_comp_bond"];
for (auto row : chemCompBond)
{
compound_bond bond;
std::string valueOrder;
cif::tie(bond.atom_id[0], bond.atom_id[1], valueOrder, bond.aromatic, bond.stereo_config) = row.get("atom_id_1", "atom_id_2", "value_order", "pdbx_aromatic_flag", "pdbx_stereo_config");
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 << std::endl;
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 = {};
for (auto &a : m_atoms)
{
if (a.id == atom_id)
{
result = a;
break;
}
}
if (result.id != atom_id)
throw std::out_of_range("No atom " + atom_id + " in compound " + m_id);
return result;
}
bool compound::atoms_bonded(const std::string &atomId_1, const std::string &atomId_2) const
{
auto i = find_if(m_bonds.begin(), m_bonds.end(),
[&](const compound_bond &b) {
return (b.atom_id[0] == atomId_1 and b.atom_id[1] == atomId_2) or (b.atom_id[0] == atomId_2 and b.atom_id[1] == atomId_1);
});
return i != m_bonds.end();
}
// --------------------------------------------------------------------
// known amino acids and bases
const std::map<std::string, char> compound_factory::kAAMap{
{"ALA", 'A'},
{"ARG", 'R'},
{"ASN", 'N'},
{"ASP", 'D'},
{"CYS", 'C'},
{"GLN", 'Q'},
{"GLU", 'E'},
{"GLY", 'G'},
{"HIS", 'H'},
{"ILE", 'I'},
{"LEU", 'L'},
{"LYS", 'K'},
{"MET", 'M'},
{"PHE", 'F'},
{"PRO", 'P'},
{"SER", 'S'},
{"THR", 'T'},
{"TRP", 'W'},
{"TYR", 'Y'},
{"VAL", 'V'},
{"GLX", 'Z'},
{"ASX", 'B'}};
const std::map<std::string, char> compound_factory::kBaseMap{
{"A", 'A'},
{"C", 'C'},
{"G", 'G'},
{"T", 'T'},
{"U", 'U'},
{"DA", 'A'},
{"DC", 'C'},
{"DG", 'G'},
{"DT", 'T'}};
// --------------------------------------------------------------------
// a factory class to generate compounds
class compound_factory_impl : public std::enable_shared_from_this<compound_factory_impl>
{
public:
compound_factory_impl(std::shared_ptr<compound_factory_impl> next);
compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next);
virtual ~compound_factory_impl()
{
for (auto c: m_compounds)
delete c;
}
compound *get(std::string id)
{
std::shared_lock lock(mMutex);
cif::to_upper(id);
compound *result = nullptr;
// walk the list, see if any of us has the compound already
for (auto impl = shared_from_this(); impl; impl = impl->m_next)
{
for (auto cmp : impl->m_compounds)
{
if (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;
}
std::shared_ptr<compound_factory_impl> next() const
{
return m_next;
}
bool is_known_peptide(const std::string &resName)
{
return m_known_peptides.count(resName) or
(m_next and m_next->is_known_peptide(resName));
}
bool is_known_base(const std::string &resName)
{
return m_known_bases.count(resName) or
(m_next and m_next->is_known_base(resName));
}
protected:
virtual compound *create(const std::string &id)
{
// For the base class we assume every compound is preloaded
return nullptr;
}
std::shared_timed_mutex mMutex;
std::vector<compound *> m_compounds;
std::set<std::string> m_known_peptides;
std::set<std::string> m_known_bases;
std::set<std::string> m_missing;
std::shared_ptr<compound_factory_impl> m_next;
};
// --------------------------------------------------------------------
compound_factory_impl::compound_factory_impl(std::shared_ptr<compound_factory_impl> next)
: m_next(next)
{
for (const auto &[key, value] : compound_factory::kAAMap)
m_known_peptides.insert(key);
for (const auto &[key, value] : compound_factory::kBaseMap)
m_known_bases.insert(key);
}
compound_factory_impl::compound_factory_impl(const fs::path &file, std::shared_ptr<compound_factory_impl> next)
: m_next(next)
{
cif::file cifFile(file);
if (cifFile.contains("comp_list")) // So this is a CCP4 restraints file, special handling
{
auto &compList = cifFile["comp_list"];
auto &chemComp = compList["chem_comp"];
for (const auto &[id, name, group] : chemComp.rows<std::string, std::string, std::string>("id", "name", "group"))
{
std::string type;
// known groups are (counted from ccp4 monomer dictionary)
// D-pyranose
// DNA
// L-PEPTIDE LINKING
// L-SACCHARIDE
// L-peptide
// L-pyranose
// M-peptide
// NON-POLYMER
// P-peptide
// RNA
// furanose
// non-polymer
// non_polymer
// peptide
// pyranose
// saccharide
if (cif::iequals(id, "gly"))
type = "peptide linking";
else if (cif::iequals(group, "l-peptide") or cif::iequals(group, "L-peptide linking") or cif::iequals(group, "peptide") or cif::iequals(group, "p-peptide"))
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";
auto &db = cifFile["comp_" + id];
m_compounds.push_back(new compound(db, id, name, type, group));
}
}
else
{
// A CCD components file, validate it first
cifFile.load_dictionary("mmcif_pdbx");
if (not cifFile.is_valid())
throw std::runtime_error("Invalid compound file");
for (auto &db : cifFile)
m_compounds.push_back(new compound(db));
}
}
// --------------------------------------------------------------------
// Version for the default compounds, based on the cached components.cif file from CCD
class CCD_compound_factory_impl : public compound_factory_impl
{
public:
CCD_compound_factory_impl(std::shared_ptr<compound_factory_impl> next, const fs::path& file)
: compound_factory_impl(next)
, mCompoundsFile(file)
{
}
CCD_compound_factory_impl(std::shared_ptr<compound_factory_impl> next)
: compound_factory_impl(next)
{
}
compound *create(const std::string &id) override;
cif::parser::datablock_index mIndex;
fs::path mCompoundsFile;
};
compound *CCD_compound_factory_impl::create(const std::string &id)
{
compound *result = nullptr;
std::unique_ptr<std::istream> ccd;
if (mCompoundsFile.empty())
{
ccd = cif::load_resource("components.cif");
if (not ccd)
throw std::runtime_error("Could not locate the CCD components.cif file, please make sure the software is installed properly and/or use the update-libcifpp-data to fetch the data.");
}
else
ccd.reset(new std::ifstream(mCompoundsFile));
cif::file file;
if (mIndex.empty())
{
if (cif::VERBOSE > 1)
{
std::cout << "Creating component index "
<< "...";
std::cout.flush();
}
cif::parser parser(*ccd, file);
mIndex = parser.index_datablocks();
if (cif::VERBOSE > 1)
std::cout << " done" << std::endl;
// reload the resource, perhaps this should be improved...
if (mCompoundsFile.empty())
{
ccd = cif::load_resource("components.cif");
if (not ccd)
throw std::runtime_error("Could not locate the CCD components.cif file, please make sure the software is installed properly and/or use the update-libcifpp-data to fetch the data.");
}
else
ccd.reset(new std::ifstream(mCompoundsFile));
}
if (cif::VERBOSE > 1)
{
std::cout << "Loading component " << id << "...";
std::cout.flush();
}
cif::parser parser(*ccd, file);
parser.parse_single_datablock(id, mIndex);
if (cif::VERBOSE > 1)
std::cout << " done" << std::endl;
if (not file.empty())
{
auto &db = file.front();
if (db.name() == id)
{
result = new compound(db);
std::shared_lock lock(mMutex);
m_compounds.push_back(result);
}
}
if (result == nullptr and cif::VERBOSE > 0)
std::cerr << "Could not locate compound " << id << " in the CCD components file" << std::endl;
return result;
}
// --------------------------------------------------------------------
// Version for the default compounds, based on the data found in CCP4's monomers lib
class CCP4_compound_factory_impl : public compound_factory_impl
{
public:
CCP4_compound_factory_impl(const fs::path &clibd_mon, std::shared_ptr<compound_factory_impl> next = nullptr);
compound *create(const std::string &id) override;
private:
cif::file m_file;
fs::path m_CLIBD_MON;
};
CCP4_compound_factory_impl::CCP4_compound_factory_impl(const fs::path &clibd_mon, std::shared_ptr<compound_factory_impl> next)
: compound_factory_impl(next)
, m_file((clibd_mon / "list" / "mon_lib_list.cif").string())
, m_CLIBD_MON(clibd_mon)
{
const std::regex peptideRx("(?:[lmp]-)?peptide", std::regex::icase);
auto &chemComps = m_file["comp_list"]["chem_comp"];
for (const auto &[group, threeLetterCode] : chemComps.rows<std::string, std::string>("group", "three_letter_code"))
{
if (std::regex_match(group, peptideRx))
m_known_peptides.insert(threeLetterCode);
else if (cif::iequals(group, "DNA") or cif::iequals(group, "RNA"))
m_known_bases.insert(threeLetterCode);
}
}
compound *CCP4_compound_factory_impl::create(const std::string &id)
{
compound *result = nullptr;
auto &cat = m_file["comp_list"]["chem_comp"];
auto rs = cat.find(cif::key("three_letter_code") == id);
if (rs.size() == 1)
{
auto row = rs.front();
std::string name, group;
uint32_t numberAtomsAll, numberAtomsNh;
cif::tie(name, group, numberAtomsAll, numberAtomsNh) =
row.get("name", "group", "number_atoms_all", "number_atoms_nh");
fs::path resFile = m_CLIBD_MON / cif::to_lower_copy(id.substr(0, 1)) / (id + ".cif");
if (not fs::exists(resFile) and (id == "COM" or id == "CON" or "PRN")) // seriously...
resFile = m_CLIBD_MON / cif::to_lower_copy(id.substr(0, 1)) / (id + '_' + id + ".cif");
if (fs::exists(resFile))
{
cif::file cf(resFile.string());
// locate the datablock
auto &db = cf["comp_" + id];
std::string type;
// known groups are (counted from ccp4 monomer dictionary)
// D-pyranose
// DNA
// L-PEPTIDE LINKING
// L-SACCHARIDE
// L-peptide
// L-pyranose
// M-peptide
// NON-POLYMER
// P-peptide
// RNA
// furanose
// non-polymer
// non_polymer
// peptide
// pyranose
// saccharide
if (cif::iequals(id, "gly"))
type = "peptide linking";
else if (cif::iequals(group, "l-peptide") or cif::iequals(group, "L-peptide linking") or cif::iequals(group, "peptide") or cif::iequals(group, "p-peptide"))
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";
m_compounds.push_back(new compound(db, id, name, type, group));
result = m_compounds.back();
}
}
return result;
}
// --------------------------------------------------------------------
std::unique_ptr<compound_factory> compound_factory::s_instance;
thread_local std::unique_ptr<compound_factory> compound_factory::tl_instance;
bool compound_factory::s_use_thread_local_instance;
void compound_factory::init(bool useThreadLocalInstanceOnly)
{
s_use_thread_local_instance = useThreadLocalInstanceOnly;
}
compound_factory::compound_factory()
: m_impl(nullptr)
{
auto ccd = cif::load_resource("components.cif");
if (ccd)
m_impl.reset(new CCD_compound_factory_impl(m_impl));
else if (cif::VERBOSE > 0)
std::cerr << "CCD components.cif file was not found" << std::endl;
const char *clibd_mon = getenv("CLIBD_MON");
if (clibd_mon != nullptr and fs::is_directory(clibd_mon))
m_impl.reset(new CCP4_compound_factory_impl(clibd_mon));
else if (cif::VERBOSE > 0)
std::cerr << "CCP4 monomers library not found, CLIBD_MON is not defined" << std::endl;
}
compound_factory::~compound_factory()
{
}
compound_factory &compound_factory::instance()
{
if (s_use_thread_local_instance)
{
if (not tl_instance)
tl_instance.reset(new compound_factory());
return *tl_instance;
}
else
{
if (not s_instance)
s_instance.reset(new compound_factory());
return *s_instance;
}
}
void compound_factory::clear()
{
if (s_use_thread_local_instance)
tl_instance.reset(nullptr);
else
s_instance.reset();
}
void compound_factory::set_default_dictionary(const fs::path &inDictFile)
{
if (not fs::exists(inDictFile))
throw std::runtime_error("file not found: " + inDictFile.string());
try
{
m_impl.reset(new CCD_compound_factory_impl(m_impl, inDictFile));
}
catch (const std::exception &)
{
if (cif::VERBOSE >= 0)
std::cerr << "Error loading dictionary " << inDictFile << std::endl;
throw;
}
}
void compound_factory::push_dictionary(const fs::path &inDictFile)
{
if (not fs::exists(inDictFile))
throw std::runtime_error("file not found: " + inDictFile.string());
// ifstream file(inDictFile);
// if (not file.is_open())
// throw std::runtime_error("Could not open peptide list " + inDictFile);
try
{
m_impl.reset(new compound_factory_impl(inDictFile, m_impl));
}
catch (const std::exception &)
{
if (cif::VERBOSE >= 0)
std::cerr << "Error loading dictionary " << inDictFile << std::endl;
throw;
}
}
void compound_factory::pop_dictionary()
{
if (m_impl)
m_impl = m_impl->next();
}
const compound *compound_factory::create(std::string id)
{
// static bool warned = false;
// if (m_impl and warned == false)
// {
// std::cerr << "Warning: no compound information library was found, resulting data may be incorrect or incomplete" << std::endl;
// warned = true;
// }
return m_impl ? m_impl->get(id) : nullptr;
}
bool compound_factory::is_known_peptide(const std::string &resName) const
{
return m_impl ? m_impl->is_known_peptide(resName) : kAAMap.count(resName) > 0;
}
bool compound_factory::is_known_base(const std::string &resName) const
{
return m_impl ? m_impl->is_known_base(resName) : kBaseMap.count(resName) > 0;
}
} // namespace pdbx

138
src/condition.cpp Normal file
View File

@@ -0,0 +1,138 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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++/category.hpp>
#include <cif++/condition.hpp>
namespace cif
{
iset get_category_fields(const category &cat)
{
return cat.fields();
}
uint16_t get_column_ix(const category &cat, std::string_view col)
{
return cat.get_column_ix(col);
}
bool is_column_type_uchar(const category &cat, std::string_view col)
{
bool result = false;
auto cv = cat.get_cat_validator();
if (cv)
{
auto iv = cv->get_validator_for_item(col);
if (iv != nullptr and iv->m_type != nullptr)
{
auto type = iv->m_type;
result = type->m_primitive_type == DDL_PrimitiveType::UChar;
}
}
return result;
}
namespace detail
{
condition_impl *key_equals_condition_impl::prepare(const category &c)
{
m_item_ix = get_column_ix(c, m_item_tag);
m_icase = is_column_type_uchar(c, m_item_tag);
if (c.get_cat_validator() != nullptr and
c.key_field_indices().contains(m_item_ix) and
c.key_field_indices().size() == 1)
{
m_single_hit = c[{ { m_item_tag, m_value } }];
}
return this;
}
condition_impl *and_condition_impl::prepare(const category &c)
{
for (auto &sub : mSub)
sub = sub->prepare(c);
for (;;)
{
auto si = find_if(mSub.begin(), mSub.end(), [](condition_impl *sub) { return dynamic_cast<and_condition_impl *>(sub) != nullptr; });
if (si == mSub.end())
break;
and_condition_impl *sub_and = static_cast<and_condition_impl *>(*si);
mSub.erase(si);
mSub.insert(mSub.end(), sub_and->mSub.begin(), sub_and->mSub.end());
sub_and->mSub.clear();
delete sub_and;
}
return this;
}
condition_impl *or_condition_impl::prepare(const category &c)
{
condition_impl *result = this;
mA = mA->prepare(c);
mB = mB->prepare(c);
key_equals_condition_impl *equals = dynamic_cast<key_equals_condition_impl*>(mA);
key_is_empty_condition_impl *empty = dynamic_cast<key_is_empty_condition_impl*>(mB);
if (equals == nullptr and empty == nullptr)
{
equals = dynamic_cast<key_equals_condition_impl*>(mB);
empty = dynamic_cast<key_is_empty_condition_impl*>(mA);
}
if (equals != nullptr and empty != nullptr)
{
result = new detail::key_equals_or_empty_condition_impl(equals, empty);
result = result->prepare(c);
delete this;
}
return result;
}
} // namespace detail
void condition::prepare(const category &c)
{
if (m_impl)
m_impl = m_impl->prepare(c);
m_prepared = true;
}
} // namespace cif

319
src/datablock.cpp Normal file
View File

@@ -0,0 +1,319 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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++/datablock.hpp>
namespace cif
{
void datablock::set_validator(const validator *v)
{
m_validator = v;
for (auto &cat : *this)
cat.set_validator(v, *this);
}
const validator *datablock::get_validator() const
{
return m_validator;
}
bool datablock::is_valid() const
{
if (m_validator == nullptr)
throw std::runtime_error("Validator not specified");
bool result = true;
for (auto &cat : *this)
result = cat.is_valid() and result;
return result;
}
bool datablock::validate_links() const
{
bool result = true;
for (auto &cat : *this)
result = cat.validate_links() and result;
return result;
}
// --------------------------------------------------------------------
category &datablock::operator[](std::string_view name)
{
auto i = std::find_if(begin(), end(), [name](const category &c)
{ return iequals(c.name(), name); });
if (i != end())
return *i;
auto &cat = emplace_back(name);
if (m_validator)
cat.set_validator(m_validator, *this);
return back();
}
const category &datablock::operator[](std::string_view name) const
{
static const category s_empty;
auto i = std::find_if(begin(), end(), [name](const category &c)
{ return iequals(c.name(), name); });
return i == end() ? s_empty : *i;
}
category *datablock::get(std::string_view name)
{
auto i = std::find_if(begin(), end(), [name](const category &c)
{ return iequals(c.name(), name); });
return i == end() ? nullptr : &*i;
}
const category *datablock::get(std::string_view name) const
{
return const_cast<datablock *>(this)->get(name);
}
std::tuple<datablock::iterator, bool> datablock::emplace(std::string_view name)
{
bool is_new = true;
auto i = begin();
while (i != end())
{
if (iequals(name, i->name()))
{
is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break;
}
++i;
}
if (is_new)
{
auto &c = emplace_front(name);
c.set_validator(m_validator, *this);
}
return std::make_tuple(begin(), is_new);
}
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"; });
if (ci != end())
{
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"; });
if (ci != end())
{
auto cto = ci->get_tag_order();
result.insert(result.end(), cto.begin(), cto.end());
}
for (auto &cat : *this)
{
if (cat.name() == "entry" or cat.name() == "audit_conform")
continue;
auto cto = cat.get_tag_order();
result.insert(result.end(), cto.begin(), cto.end());
}
return result;
}
void datablock::write(std::ostream &os) const
{
os << "data_" << m_name << std::endl
<< "# " << std::endl;
// mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the
// audit_conform record.
for (auto &cat : *this)
{
if (cat.name() != "entry")
continue;
cat.write(os);
break;
}
// 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)
{
category auditConform("audit_conform");
auditConform.emplace({
{"dict_name", m_validator->name()},
{"dict_version", m_validator->version()}});
auditConform.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> &tag_order)
{
os << "data_" << m_name << std::endl
<< "# " << std::endl;
std::vector<std::string> cat_order;
for (auto &o : tag_order)
{
std::string cat_name, item_name;
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);
}
for (auto &c : cat_order)
{
auto cat = get(c);
if (cat == nullptr)
continue;
std::vector<std::string> items;
for (auto &o : tag_order)
{
std::string cat_name, item_name;
std::tie(cat_name, item_name) = split_tag_name(o);
if (cat_name == c)
items.push_back(item_name);
}
cat->write(os, items);
}
// for any Category we missed in the catOrder
for (auto &cat : *this)
{
if (find_if(cat_order.begin(), cat_order.end(), [&](const std::string &s) -> bool
{ return iequals(cat.name(), s); }) != cat_order.end())
continue;
cat.write(os);
}
}
bool datablock::operator==(const datablock &rhs) const
{
auto &dbA = *this;
auto &dbB = rhs;
std::vector<std::string> catA, catB;
for (auto &cat : dbA)
{
if (not cat.empty())
catA.push_back(cat.name());
}
std::sort(catA.begin(), catA.end());
for (auto &cat : dbB)
{
if (not cat.empty())
catB.push_back(cat.name());
}
std::sort(catB.begin(), catB.end());
// loop over categories twice, to group output
// First iteration is to list missing categories.
std::vector<std::string> missingA, missingB;
auto catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
if (not iequals(*catA_i, *catB_i))
return false;
++catA_i, ++catB_i;
}
if (catA_i != catA.end() or catB_i != catB.end())
return false;
// Second loop, now compare category values
catA_i = catA.begin(), catB_i = catB.begin();
while (catA_i != catA.end() and catB_i != catB.end())
{
std::string nA = *catA_i;
to_lower(nA);
std::string nB = *catB_i;
to_lower(nB);
int d = nA.compare(nB);
if (d > 0)
++catB_i;
else if (d < 0)
++catA_i;
else
{
if (not (*dbA.get(*catA_i) == *dbB.get(*catB_i)))
return false;
++catA_i;
++catB_i;
}
}
return true;
}
} // namespace cif::cif

490
src/dictionary_parser.cpp Normal file
View File

@@ -0,0 +1,490 @@
/*-
* 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 <cif++/condition.hpp>
#include <cif++/dictionary_parser.hpp>
#include <cif++/file.hpp>
#include <cif++/parser.hpp>
namespace cif
{
using namespace literals;
inline void replace_all(std::string &s, std::string_view pat, std::string_view rep)
{
for (std::string::size_type i = s.find(pat); i != std::string::npos; i = s.find(pat, i))
s.replace(i, pat.size(), rep.data(), rep.size());
}
class dictionary_parser : public parser
{
public:
dictionary_parser(validator &validator, std::istream &is, file &f)
: parser(is, f)
, m_validator(validator)
{
}
void load_dictionary()
{
std::unique_ptr<datablock> dict;
auto savedDatablock = m_datablock;
try
{
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
case CIFToken::GLOBAL:
parse_global();
break;
default:
{
dict.reset(new datablock(m_token_value)); // dummy datablock, for constructing the validator only
m_datablock = dict.get();
match(CIFToken::DATA);
parse_datablock();
break;
}
}
}
}
catch (const std::exception &ex)
{
error(ex.what());
}
// store all validators
for (auto &ic : mCategoryValidators)
m_validator.add_category_validator(std::move(ic));
mCategoryValidators.clear();
for (auto &iv : mItemValidators)
{
auto cv = m_validator.get_validator_for_category(iv.first);
if (cv == nullptr)
error("Undefined category '" + iv.first);
for (auto &v : iv.second)
const_cast<category_validator *>(cv)->addItemValidator(std::move(v));
}
// check all item validators for having a typeValidator
if (dict)
link_items();
// store meta information
datablock::iterator info;
bool is_new;
std::tie(info, is_new) = m_datablock->emplace("dictionary");
if (not is_new and not info->empty())
{
auto r = info->front();
m_validator.set_name(r["title"].as<std::string>());
m_validator.version(r["version"].as<std::string>());
}
m_datablock = savedDatablock;
mItemValidators.clear();
}
private:
void parse_save_frame() override
{
if (not m_collected_item_types)
m_collected_item_types = collect_item_types();
std::string saveFrameName = m_token_value;
if (saveFrameName.empty())
error("Invalid save frame, should contain more than just 'save_' here");
bool isCategorySaveFrame = m_token_value[0] != '_';
datablock dict(m_token_value);
datablock::iterator cat = dict.end();
match(CIFToken::SAVE);
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag)
{
if (m_lookahead == CIFToken::LOOP)
{
cat = dict.end(); // should start a new category
match(CIFToken::LOOP);
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
{
std::string catName, item_name;
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_");
tags.push_back(item_name);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::Value)
{
cat->emplace({});
auto row = cat->back();
for (auto tag : tags)
{
row[tag] = m_token_value;
match(CIFToken::Value);
}
}
cat = dict.end();
}
else
{
std::string catName, item_name;
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::Tag);
if (cat->empty())
cat->emplace({});
cat->back()[item_name] = m_token_value;
match(CIFToken::Value);
}
}
match(CIFToken::SAVE);
if (isCategorySaveFrame)
{
std::string category = dict["category"].front().get<std::string>("id");
std::vector<std::string> keys;
for (auto k : dict["category_key"])
keys.push_back(std::get<1>(split_tag_name(k["name"].as<std::string>())));
iset groups;
for (auto g : dict["category_group"])
groups.insert(g["id"].as<std::string>());
mCategoryValidators.push_back(category_validator{ category, keys, groups });
}
else
{
// if the type code is missing, this must be a pointer, just skip it
std::string typeCode = dict["item_type"].front().get<std::string>("code");
const type_validator *tv = nullptr;
if (not(typeCode.empty() or typeCode == "?"))
tv = m_validator.get_validator_for_type(typeCode);
iset ess;
for (auto e : dict["item_enumeration"])
ess.insert(e["value"].as<std::string>());
std::string defaultValue = dict["item_default"].front().get<std::string>("value");
bool defaultIsNull = false;
if (defaultValue.empty())
{
// TODO: Is this correct???
for (auto r : dict["_item_default"])
{
defaultIsNull = r["value"].is_null();
break;
}
}
// collect the dict from our dataBlock and construct validators
for (auto i : dict["item"])
{
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_tag_name(tagName);
if (cat_name.empty() or item_name.empty())
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 tag '" + tagName + '\'');
else
category = cat_name;
auto &ivs = mItemValidators[category];
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, defaultIsNull });
else
{
// need to update the itemValidator?
if (vi->m_mandatory != (iequals(mandatory, "yes")))
{
if (VERBOSE > 2)
{
std::cerr << "inconsistent mandatory value for " << tagName << " in dictionary" << std::endl;
if (iequals(tagName, saveFrameName))
std::cerr << "choosing " << mandatory << std::endl;
else
std::cerr << "choosing " << (vi->m_mandatory ? "Y" : "N") << std::endl;
}
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 " << tagName << " in dictionary" << std::endl;
}
// vi->mMandatory = (iequals(mandatory, "yes"));
if (vi->m_type == nullptr)
vi->m_type = tv;
vi->m_enums.insert(ess.begin(), ess.end());
// anything else yet?
// ...
}
}
// collect the dict from our dataBlock and construct validators
for (auto i : dict["item_linked"])
{
mLinkedItems.emplace(i.get<std::string,std::string>("child_name", "parent_name"));
}
}
}
void link_items()
{
if (not m_datablock)
error("no datablock");
auto &dict = *m_datablock;
// links are identified by a parent category, a child category and a group ID
using key_type = std::tuple<std::string, std::string, int>;
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 = [&](size_t ix, const std::string &pk, const std::string &ck)
{
auto &&[pkeys, ckeys] = linkKeys.at(ix);
bool found = false;
for (size_t i = 0; i < pkeys.size(); ++i)
{
if (pkeys[i] == pk and ckeys[i] == ck)
{
found = true;
break;
}
}
if (not found)
{
pkeys.push_back(pk);
ckeys.push_back(ck);
}
};
auto &linkedGroupList = dict["pdbx_item_linked_group_list"];
for (auto gl : linkedGroupList)
{
std::string child, parent;
int link_group_id;
cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
auto civ = m_validator.get_validator_for_item(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = m_validator.get_validator_for_item(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
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({});
}
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
if (linkedGroupList.empty())
{
// for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li : mLinkedItems)
{
std::string child, parent;
std::tie(child, parent) = li;
auto civ = m_validator.get_validator_for_item(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = m_validator.get_validator_for_item(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
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({});
}
size_t ix = linkIndex.at(key);
addLink(ix, piv->m_tag, civ->m_tag);
}
}
auto &linkedGroup = dict["pdbx_item_linked_group"];
// now store the links in the validator
for (auto &kv : linkIndex)
{
link_validator link = {};
std::tie(link.m_parent_category, link.m_child_category, link.m_link_group_id) = kv.first;
std::tie(link.m_parent_keys, link.m_child_keys) = linkKeys[kv.second];
// look up the label
for (auto r : linkedGroup.find("category_id"_key == link.m_child_category and "link_group_id"_key == link.m_link_group_id))
{
link.m_link_group_label = r["label"].as<std::string>();
break;
}
m_validator.add_link_validator(std::move(link));
}
// now make sure the itemType is specified for all itemValidators
for (auto &cv : m_validator.m_category_validators)
{
for (auto &iv : cv.m_item_validators)
{
if (iv.m_type == nullptr and cif::VERBOSE >= 0)
std::cerr << "Missing item_type for " << iv.m_tag << std::endl;
}
}
}
bool collect_item_types()
{
bool result = false;
if (not m_datablock)
error("no datablock");
auto &dict = *m_datablock;
for (auto t : dict["item_type_list"])
{
std::string code, primitiveCode, construct;
cif::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct");
replace_all(construct, "\\n", "\n");
replace_all(construct, "\\t", "\t");
replace_all(construct, "\\\n", "");
try
{
type_validator v = {
code, map_to_primitive_type(primitiveCode), construct
};
m_validator.add_type_validator(std::move(v));
}
catch (const std::exception &)
{
std::throw_with_nested(parse_error(/*t.lineNr()*/ 0, "error in regular expression"));
}
// Do not replace an already defined type validator, this won't work with pdbx_v40
// as it has a name that is too strict for its own names :-)
// if (mFileImpl.mTypeValidators.count(v))
// mFileImpl.mTypeValidators.erase(v);
if (VERBOSE >= 5)
std::cerr << "Added type " << code << " (" << primitiveCode << ") => " << construct << std::endl;
result = true;
}
return result;
}
validator &m_validator;
bool m_collected_item_types = false;
std::vector<category_validator> mCategoryValidators;
std::map<std::string, std::vector<item_validator>> mItemValidators;
std::set<std::tuple<std::string, std::string>> mLinkedItems;
};
// --------------------------------------------------------------------
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;
}
} // namespace cif

216
src/file.cpp Normal file
View File

@@ -0,0 +1,216 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <gxrio.hpp>
#include <cif++/file.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;
if (result)
result = validate_links();
return result;
}
bool file::is_valid()
{
if (m_validator == nullptr)
{
if (VERBOSE > 0)
std::cerr << "No dictionary loaded explicitly, loading default" << std::endl;
load_dictionary();
}
bool result = not empty();
for (auto &d : *this)
result = d.is_valid() and result;
if (result)
result = validate_links();
return result;
}
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 (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() << std::endl;
}
}
}
}
if (not m_validator)
load_dictionary("mmcif_ddl");
}
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 db.name() == name; }) != end();
}
datablock &file::operator[](std::string_view name)
{
auto i = std::find_if(begin(), end(), [name](const datablock &c)
{ return iequals(c.name(), name); });
if (i != end())
return *i;
emplace_back(name);
return back();
}
const datablock &file::operator[](std::string_view name) const
{
static const datablock s_empty;
auto i = std::find_if(begin(), end(), [name](const datablock &c)
{ return iequals(c.name(), name); });
return i == end() ? s_empty : *i;
}
std::tuple<file::iterator, bool> file::emplace(std::string_view name)
{
bool is_new = true;
auto i = begin();
while (i != end())
{
if (iequals(name, i->name()))
{
is_new = false;
if (i != begin())
{
auto n = std::next(i);
splice(begin(), *this, i, n);
}
break;
}
++i;
}
if (is_new)
{
auto &db = emplace_front(name);
db.set_validator(m_validator);
}
return std::make_tuple(begin(), is_new);
}
void file::load(const std::filesystem::path &p)
{
gxrio::ifstream in(p);
load(in);
}
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
{
gxrio::ofstream outFile(p);
save(outFile);
}
void file::save(std::ostream &os) const
{
// if (not is_valid())
// std::cout << "File is not valid!" << std::endl;
for (auto &db : *this)
db.write(os);
}
} // namespace cif

68
src/item.cpp Normal file
View File

@@ -0,0 +1,68 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 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 <cassert>
#include <cif++/row.hpp>
namespace cif
{
const item_handle item_handle::s_null_item;
row_handle s_null_row_handle;
item_handle::item_handle()
: m_column(std::numeric_limits<uint16_t>::max())
, m_row_handle(s_null_row_handle)
{
}
std::string_view item_handle::text() const
{
if (not m_row_handle.empty())
{
auto iv = m_row_handle.m_row->get(m_column);
if (iv != nullptr)
return iv->text();
}
return {};
}
void item_handle::assign_value(const item &v)
{
assert(not m_row_handle.empty());
m_row_handle.assign(m_column, v.value(), true);
}
void item_handle::swap(item_handle &b)
{
assert(m_column == b.m_column);
// assert(&m_row_handle.m_category == &b.m_row_handle.m_category);
m_row_handle.swap(m_column, b.m_row_handle);
}
}

2609
src/model.cpp Normal file

File diff suppressed because it is too large Load Diff

820
src/parser.cpp Normal file
View File

@@ -0,0 +1,820 @@
/*-
* 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 <cassert>
#include <iostream>
#include <map>
#include <regex>
#include <stack>
#include <cif++/utilities.hpp>
#include <cif++/forward_decl.hpp>
#include <cif++/parser.hpp>
#include <cif++/file.hpp>
namespace cif
{
extern int VERBOSE;
}
namespace cif
{
// --------------------------------------------------------------------
sac_parser::sac_parser(std::istream &is, bool init)
: m_source(is)
{
m_validate = true;
m_line_nr = 1;
m_bol = true;
if (init)
m_lookahead = get_next_token();
}
// get_next_char takes a char from the buffer, or if it is empty
// from the istream. This function also does carriage/linefeed
// translation.
int sac_parser::get_next_char()
{
int result;
if (m_buffer.empty())
result = m_source.get();
else
{
result = m_buffer.top();
m_buffer.pop();
}
// very simple CR/LF translation into LF
if (result == '\r')
{
int lookahead = m_source.get();
if (lookahead != '\n')
m_buffer.push(lookahead);
result = '\n';
}
m_token_value += static_cast<char>(result);
if (result == '\n')
++m_line_nr;
if (VERBOSE >= 6)
{
std::cerr << "get_next_char => ";
if (iscntrl(result) or not isprint(result))
std::cerr << int(result) << std::endl;
else
std::cerr << char(result) << std::endl;
}
return result;
}
void sac_parser::retract()
{
assert(not m_token_value.empty());
char ch = m_token_value.back();
if (ch == '\n')
--m_line_nr;
m_buffer.push(ch);
m_token_value.pop_back();
}
int sac_parser::restart(int start)
{
int result = 0;
while (not m_token_value.empty())
retract();
switch (start)
{
case State::Start:
result = State::Float;
break;
case State::Float:
result = State::Int;
break;
case State::Int:
result = State::Value;
break;
default:
error("Invalid state in SacParser");
}
m_bol = false;
return result;
}
sac_parser::CIFToken sac_parser::get_next_token()
{
const auto kEOF = std::char_traits<char>::eof();
CIFToken result = CIFToken::Unknown;
int quoteChar = 0;
int state = State::Start, start = State::Start;
m_bol = false;
m_token_value.clear();
mTokenType = CIFValue::Unknown;
while (result == CIFToken::Unknown)
{
auto ch = get_next_char();
switch (state)
{
case State::Start:
if (ch == kEOF)
result = CIFToken::Eof;
else if (ch == '\n')
{
m_bol = true;
state = State::White;
}
else if (ch == ' ' or ch == '\t')
state = State::White;
else if (ch == '#')
state = State::Comment;
else if (ch == '_')
state = State::Tag;
else if (ch == ';' and m_bol)
state = State::TextField;
else if (ch == '\'' or ch == '"')
{
quoteChar = ch;
state = State::QuotedString;
}
else
state = start = restart(start);
break;
case State::White:
if (ch == kEOF)
result = CIFToken::Eof;
else if (not isspace(ch))
{
state = State::Start;
retract();
m_token_value.clear();
}
else
m_bol = (ch == '\n');
break;
case State::Comment:
if (ch == '\n')
{
state = State::Start;
m_bol = true;
m_token_value.clear();
}
else if (ch == kEOF)
result = CIFToken::Eof;
else if (not is_any_print(ch))
error("invalid character in comment");
break;
case State::TextField:
if (ch == '\n')
state = State::TextField + 1;
else if (ch == kEOF)
error("unterminated textfield");
// else if (ch == '\\')
// state = State::Esc;
else if (not is_any_print(ch))
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
break;
// case State::Esc:
// if (ch == '\n')
// break;
case State::TextField + 1:
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextField;
else if (ch == ';')
{
assert(m_token_value.length() >= 2);
m_token_value = m_token_value.substr(1, m_token_value.length() - 3);
mTokenType = CIFValue::TextField;
result = CIFToken::Value;
}
else if (ch == kEOF)
error("unterminated textfield");
else if (ch != '\n')
error("invalid character in text field");
break;
case State::QuotedString:
if (ch == kEOF)
error("unterminated quoted string");
else if (ch == quoteChar)
state = State::QuotedStringQuote;
else if (not is_any_print(ch))
warning("invalid character in quoted string: '" + std::string({static_cast<char>(ch)}) + '\'');
break;
case State::QuotedStringQuote:
if (is_white(ch))
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::String;
if (m_token_value.length() < 2)
error("Invalid quoted string token");
m_token_value = m_token_value.substr(1, m_token_value.length() - 2);
}
else if (ch == quoteChar)
;
else if (is_any_print(ch))
state = State::QuotedString;
else if (ch == kEOF)
error("unterminated quoted string");
else
error("invalid character in quoted string");
break;
case State::Tag:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Tag;
}
break;
case State::Float:
if (ch == '+' or ch == '-')
{
state = State::Float + 1;
}
else if (isdigit(ch))
state = State::Float + 1;
else
state = start = restart(start);
break;
case State::Float + 1:
// if (ch == '(') // numeric???
// mState = State::NumericSuffix;
// else
if (ch == '.')
state = State::Float + 2;
else if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
// parsed '.'
case State::Float + 2:
if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
// parsed 'e'
case State::Float + 3:
if (ch == '-' or ch == '+')
state = State::Float + 4;
else if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 4:
if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 5:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
case State::Int:
if (isdigit(ch) or ch == '+' or ch == '-')
state = State::Int + 1;
else
state = start = restart(start);
break;
case State::Int + 1:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
case State::Value:
if (ch == '_')
{
std::string s = to_lower_copy(m_token_value);
if (s == "global_")
result = CIFToken::GLOBAL;
else if (s == "stop_")
result = CIFToken::STOP;
else if (s == "loop_")
result = CIFToken::LOOP;
else if (s == "data_")
{
state = State::DATA;
continue;
}
else if (s == "save_")
{
state = State::SAVE;
continue;
}
}
if (result == CIFToken::Unknown and not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
if (m_token_value == ".")
mTokenType = CIFValue::Inapplicable;
else if (m_token_value == "?")
{
mTokenType = CIFValue::Unknown;
m_token_value.clear();
}
}
break;
case State::DATA:
case State::SAVE:
if (not is_non_blank(ch))
{
retract();
if (state == State::DATA)
result = CIFToken::DATA;
else
result = CIFToken::SAVE;
m_token_value.erase(m_token_value.begin(), m_token_value.begin() + 5);
}
break;
default:
assert(false);
error("Invalid state in get_next_token");
break;
}
}
if (VERBOSE >= 5)
{
std::cerr << get_token_name(result);
if (mTokenType != CIFValue::Unknown)
std::cerr << ' ' << get_value_name(mTokenType);
if (result != CIFToken::Eof)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << std::endl;
}
return result;
}
void sac_parser::match(CIFToken token)
{
if (m_lookahead != token)
error(std::string("Unexpected token, expected ") + get_token_name(token) + " but found " + get_token_name(m_lookahead));
m_lookahead = get_next_token();
}
bool sac_parser::parse_single_datablock(const std::string &datablock)
{
// first locate the start, as fast as we can
auto &sb = *m_source.rdbuf();
enum
{
start,
comment,
string,
string_quote,
qstring,
data
} state = start;
int quote = 0;
bool bol = true;
std::string dblk = "data_" + datablock;
std::string::size_type si = 0;
bool found = false;
for (auto ch = sb.sbumpc(); not found and ch != std::streambuf::traits_type::eof(); ch = sb.sbumpc())
{
switch (state)
{
case start:
switch (ch)
{
case '#': state = comment; break;
case 'd':
case 'D':
state = data;
si = 1;
break;
case '\'':
case '"':
state = string;
quote = ch;
break;
case ';':
if (bol)
state = qstring;
break;
}
break;
case comment:
if (ch == '\n')
state = start;
break;
case string:
if (ch == quote)
state = string_quote;
break;
case string_quote:
if (std::isspace(ch))
state = start;
else
state = string;
break;
case qstring:
if (ch == ';' and bol)
state = start;
break;
case data:
if (isspace(ch) and dblk[si] == 0)
found = true;
else if (dblk[si++] != ch)
state = start;
break;
}
bol = (ch == '\n');
}
if (found)
{
produce_datablock(datablock);
m_lookahead = get_next_token();
parse_datablock();
}
return found;
}
sac_parser::datablock_index sac_parser::index_datablocks()
{
datablock_index index;
// first locate the start, as fast as we can
auto &sb = *m_source.rdbuf();
enum
{
start,
comment,
string,
string_quote,
qstring,
data,
data_name
} state = start;
int quote = 0;
bool bol = true;
const char dblk[] = "data_";
std::string::size_type si = 0;
std::string datablock;
for (auto ch = sb.sbumpc(); ch != std::streambuf::traits_type::eof(); ch = sb.sbumpc())
{
switch (state)
{
case start:
switch (ch)
{
case '#': state = comment; break;
case 'd':
case 'D':
state = data;
si = 1;
break;
case '\'':
case '"':
state = string;
quote = ch;
break;
case ';':
if (bol)
state = qstring;
break;
}
break;
case comment:
if (ch == '\n')
state = start;
break;
case string:
if (ch == quote)
state = string_quote;
break;
case string_quote:
if (std::isspace(ch))
state = start;
else
state = string;
break;
case qstring:
if (ch == ';' and bol)
state = start;
break;
case data:
if (dblk[si] == 0 and is_non_blank(ch))
{
datablock = {static_cast<char>(ch)};
state = data_name;
}
else if (dblk[si++] != ch)
state = start;
break;
case data_name:
if (is_non_blank(ch))
datablock.insert(datablock.end(), char(ch));
else if (isspace(ch))
{
if (not datablock.empty())
index[datablock] = m_source.tellg();
state = start;
}
else
state = start;
break;
}
bol = (ch == '\n');
}
return index;
}
bool sac_parser::parse_single_datablock(const std::string &datablock, const datablock_index &index)
{
bool result = false;
auto i = index.find(datablock);
if (i != index.end())
{
m_source.seekg(i->second);
produce_datablock(datablock);
m_lookahead = get_next_token();
parse_datablock();
result = true;
}
return result;
}
void sac_parser::parse_file()
{
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
case CIFToken::GLOBAL:
parse_global();
break;
case CIFToken::DATA:
produce_datablock(m_token_value);
match(CIFToken::DATA);
parse_datablock();
break;
default:
error("This file does not seem to be an mmCIF file");
break;
}
}
}
void sac_parser::parse_global()
{
match(CIFToken::GLOBAL);
while (m_lookahead == CIFToken::Tag)
{
match(CIFToken::Tag);
match(CIFToken::Value);
}
}
void sac_parser::parse_datablock()
{
std::string cat;
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE)
{
switch (m_lookahead)
{
case CIFToken::LOOP:
{
cat.clear(); // should start a new category
match(CIFToken::LOOP);
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
{
std::string catName, itemName;
std::tie(catName, itemName) = split_tag_name(m_token_value);
if (cat.empty())
{
produce_category(catName);
cat = catName;
}
else if (not iequals(cat, catName))
error("inconsistent categories in loop_");
tags.push_back(itemName);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::Value)
{
produce_row();
for (auto tag : tags)
{
produce_item(cat, tag, m_token_value);
match(CIFToken::Value);
}
}
cat.clear();
break;
}
case CIFToken::Tag:
{
std::string catName, itemName;
std::tie(catName, itemName) = split_tag_name(m_token_value);
if (not iequals(cat, catName))
{
produce_category(catName);
cat = catName;
produce_row();
}
match(CIFToken::Tag);
produce_item(cat, itemName, m_token_value);
match(CIFToken::Value);
break;
}
case CIFToken::SAVE:
parse_save_frame();
break;
default:
assert(false);
break;
}
}
}
void sac_parser::parse_save_frame()
{
error("A regular CIF file should not contain a save frame");
}
// --------------------------------------------------------------------
void parser::produce_datablock(const std::string &name)
{
const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
}
void parser::produce_category(const std::string &name)
{
if (VERBOSE >= 4)
std::cerr << "producing category " << name << std::endl;
const auto &[cat, ignore] = m_datablock->emplace(name);
m_category = &*cat;
}
void parser::produce_row()
{
if (VERBOSE >= 4)
std::cerr << "producing row for category " << m_category->name() << std::endl;
m_category->emplace({});
m_row = m_category->back();
// m_row.lineNr(m_line_nr);
}
void parser::produce_item(const std::string &category, const std::string &item, const std::string &value)
{
if (VERBOSE >= 4)
std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
if (not iequals(category, m_category->name()))
error("inconsistent categories in loop_");
m_row[item] = m_token_value;
}
} // namespace cif

3726
src/pdb/cif2pdb.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1500
src/pdb/pdb2cif_remark_3.cpp Normal file

File diff suppressed because it is too large Load Diff

2016
src/pdb/tls.cpp Normal file

File diff suppressed because it is too large Load Diff

544
src/point.cpp Normal file
View File

@@ -0,0 +1,544 @@
/*-
* 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 <cassert>
#include <random>
#include <cif++/point.hpp>
namespace cif
{
// --------------------------------------------------------------------
// We're using expression templates here
template <typename M>
class MatrixExpression
{
public:
uint32_t dim_m() const { return static_cast<const M &>(*this).dim_m(); }
uint32_t dim_n() const { return static_cast<const M &>(*this).dim_n(); }
double &operator()(uint32_t i, uint32_t j)
{
return static_cast<M &>(*this).operator()(i, j);
}
double operator()(uint32_t i, uint32_t j) const
{
return static_cast<const M &>(*this).operator()(i, j);
}
};
// --------------------------------------------------------------------
// matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
// element m i,j is mapped to [i * n + j] and thus storage is row major
class Matrix : public MatrixExpression<Matrix>
{
public:
template <typename M2>
Matrix(const MatrixExpression<M2> &m)
: m_m(m.dim_m())
, m_n(m.dim_n())
, m_data(m_m * m_n)
{
for (uint32_t i = 0; i < m_m; ++i)
{
for (uint32_t j = 0; j < m_n; ++j)
operator()(i, j) = m(i, j);
}
}
Matrix(size_t m, size_t n, double v = 0)
: m_m(m)
, m_n(n)
, m_data(m_m * m_n)
{
std::fill(m_data.begin(), m_data.end(), v);
}
Matrix() = default;
Matrix(Matrix &&m) = default;
Matrix(const Matrix &m) = default;
Matrix &operator=(Matrix &&m) = default;
Matrix &operator=(const Matrix &m) = default;
uint32_t dim_m() const { return m_m; }
uint32_t dim_n() const { return m_n; }
double operator()(uint32_t i, uint32_t j) const
{
assert(i < m_m);
assert(j < m_n);
return m_data[i * m_n + j];
}
double &operator()(uint32_t i, uint32_t j)
{
assert(i < m_m);
assert(j < m_n);
return m_data[i * m_n + j];
}
private:
uint32_t m_m = 0, m_n = 0;
std::vector<double> m_data;
};
// --------------------------------------------------------------------
class SymmetricMatrix : public MatrixExpression<SymmetricMatrix>
{
public:
SymmetricMatrix(uint32_t n, double v = 0)
: m_n(n)
, m_data((m_n * (m_n + 1)) / 2)
{
std::fill(m_data.begin(), m_data.end(), v);
}
SymmetricMatrix() = default;
SymmetricMatrix(SymmetricMatrix &&m) = default;
SymmetricMatrix(const SymmetricMatrix &m) = default;
SymmetricMatrix &operator=(SymmetricMatrix &&m) = default;
SymmetricMatrix &operator=(const SymmetricMatrix &m) = default;
uint32_t dim_m() const { return m_n; }
uint32_t dim_n() const { return m_n; }
double operator()(uint32_t i, uint32_t j) const
{
return i < j
? m_data[(j * (j + 1)) / 2 + i]
: m_data[(i * (i + 1)) / 2 + j];
}
double &operator()(uint32_t i, uint32_t j)
{
if (i > j)
std::swap(i, j);
assert(j < m_n);
return m_data[(j * (j + 1)) / 2 + i];
}
private:
uint32_t m_n;
std::vector<double> m_data;
};
class IdentityMatrix : public MatrixExpression<IdentityMatrix>
{
public:
IdentityMatrix(uint32_t n)
: m_n(n)
{
}
uint32_t dim_m() const { return m_n; }
uint32_t dim_n() const { return m_n; }
double operator()(uint32_t i, uint32_t j) const
{
return i == j ? 1 : 0;
}
private:
uint32_t m_n;
};
// --------------------------------------------------------------------
// matrix functions, implemented as expression templates
template <typename M1, typename M2>
class MatrixSubtraction : public MatrixExpression<MatrixSubtraction<M1, M2>>
{
public:
MatrixSubtraction(const M1 &m1, const M2 &m2)
: m_m1(m1)
, m_m2(m2)
{
assert(m_m1.dim_m() == m_m2.dim_m());
assert(m_m1.dim_n() == m_m2.dim_n());
}
uint32_t dim_m() const { return m_m1.dim_m(); }
uint32_t dim_n() const { return m_m1.dim_n(); }
double operator()(uint32_t i, uint32_t j) const
{
return m_m1(i, j) - m_m2(i, j);
}
private:
const M1 &m_m1;
const M2 &m_m2;
};
template <typename M1, typename M2>
MatrixSubtraction<M1, M2> operator-(const MatrixExpression<M1> &m1, const MatrixExpression<M2> &m2)
{
return MatrixSubtraction(*static_cast<const M1 *>(&m1), *static_cast<const M2 *>(&m2));
}
template <typename M>
class MatrixMultiplication : public MatrixExpression<MatrixMultiplication<M>>
{
public:
MatrixMultiplication(const M &m, double v)
: m_m(m)
, m_v(v)
{
}
uint32_t dim_m() const { return m_m.dim_m(); }
uint32_t dim_n() const { return m_m.dim_n(); }
double operator()(uint32_t i, uint32_t j) const
{
return m_m(i, j) * m_v;
}
private:
const M &m_m;
double m_v;
};
template <typename M>
MatrixMultiplication<M> operator*(const MatrixExpression<M> &m, double v)
{
return MatrixMultiplication(*static_cast<const M *>(&m), v);
}
// --------------------------------------------------------------------
template <class M1>
Matrix Cofactors(const M1 &m)
{
Matrix cf(m.dim_m(), m.dim_m());
const size_t ixs[4][3] = {
{ 1, 2, 3 },
{ 0, 2, 3 },
{ 0, 1, 3 },
{ 0, 1, 2 }
};
for (size_t x = 0; x < 4; ++x)
{
const size_t *ix = ixs[x];
for (size_t y = 0; y < 4; ++y)
{
const size_t *iy = ixs[y];
cf(x, y) =
m(ix[0], iy[0]) * m(ix[1], iy[1]) * m(ix[2], iy[2]) +
m(ix[0], iy[1]) * m(ix[1], iy[2]) * m(ix[2], iy[0]) +
m(ix[0], iy[2]) * m(ix[1], iy[0]) * m(ix[2], iy[1]) -
m(ix[0], iy[2]) * m(ix[1], iy[1]) * m(ix[2], iy[0]) -
m(ix[0], iy[1]) * m(ix[1], iy[0]) * m(ix[2], iy[2]) -
m(ix[0], iy[0]) * m(ix[1], iy[2]) * m(ix[2], iy[1]);
if ((x + y) % 2 == 1)
cf(x, y) *= -1;
}
}
return cf;
}
// --------------------------------------------------------------------
template<typename T>
quaternion_type<T> normalize(quaternion_type<T> q)
{
std::valarray<double> t(4);
t[0] = q.get_a();
t[1] = q.get_b();
t[2] = q.get_c();
t[3] = q.get_d();
t *= t;
double length = std::sqrt(t.sum());
if (length > 0.001)
q /= static_cast<quaternion::value_type>(length);
else
q = quaternion(1, 0, 0, 0);
return q;
}
// --------------------------------------------------------------------
quaternion construct_from_angle_axis(float angle, point axis)
{
auto q = std::cos((angle * kPI / 180) / 2);
auto s = std::sqrt(1 - q * q);
axis.normalize();
return normalize(quaternion{
static_cast<float>(q),
static_cast<float>(s * axis.m_x),
static_cast<float>(s * axis.m_y),
static_cast<float>(s * axis.m_z) });
}
std::tuple<double, point> quaternion_to_angle_axis(quaternion q)
{
if (q.get_a() > 1)
q = normalize(q);
// angle:
double angle = 2 * std::acos(q.get_a());
angle = angle * 180 / kPI;
// axis:
float s = std::sqrt(1 - q.get_a() * q.get_a());
if (s < 0.001)
s = 1;
point axis(q.get_b() / s, q.get_c() / s, q.get_d() / s);
return { angle, axis };
}
point center_points(std::vector<point> &Points)
{
point t;
for (point &pt : Points)
{
t.m_x += pt.m_x;
t.m_y += pt.m_y;
t.m_z += pt.m_z;
}
t.m_x /= Points.size();
t.m_y /= Points.size();
t.m_z /= Points.size();
for (point &pt : Points)
{
pt.m_x -= t.m_x;
pt.m_y -= t.m_y;
pt.m_z -= t.m_z;
}
return t;
}
point centroid(const std::vector<point> &pts)
{
point result;
for (auto &pt : pts)
result += pt;
result /= static_cast<float>(pts.size());
return result;
}
double RMSd(const std::vector<point> &a, const std::vector<point> &b)
{
double sum = 0;
for (uint32_t i = 0; i < a.size(); ++i)
{
std::valarray<double> d(3);
d[0] = b[i].m_x - a[i].m_x;
d[1] = b[i].m_y - a[i].m_y;
d[2] = b[i].m_z - a[i].m_z;
d *= d;
sum += d.sum();
}
return std::sqrt(sum / a.size());
}
// The next function returns the largest solution for a quartic equation
// based on Ferrari's algorithm.
// A depressed quartic is of the form:
//
// x^4 + ax^2 + bx + c = 0
//
// (since I'm too lazy to find out a better way, I've implemented the
// routine using complex values to avoid nan's as a result of taking
// sqrt of a negative number)
double LargestDepressedQuarticSolution(double a, double b, double c)
{
std::complex<double> P = -(a * a) / 12 - c;
std::complex<double> Q = -(a * a * a) / 108 + (a * c) / 3 - (b * b) / 8;
std::complex<double> R = -Q / 2.0 + std::sqrt((Q * Q) / 4.0 + (P * P * P) / 27.0);
std::complex<double> U = std::pow(R, 1 / 3.0);
std::complex<double> y;
if (U == 0.0)
y = -5.0 * a / 6.0 + U - std::pow(Q, 1.0 / 3.0);
else
y = -5.0 * a / 6.0 + U - P / (3.0 * U);
std::complex<double> W = std::sqrt(a + 2.0 * y);
// And to get the final result:
// result = (±W + std::sqrt(-(3 * alpha + 2 * y ± 2 * beta / W))) / 2;
// We want the largest result, so:
std::valarray<double> t(4);
t[0] = ((W + std::sqrt(-(3.0 * a + 2.0 * y + 2.0 * b / W))) / 2.0).real();
t[1] = ((W + std::sqrt(-(3.0 * a + 2.0 * y - 2.0 * b / W))) / 2.0).real();
t[2] = ((-W + std::sqrt(-(3.0 * a + 2.0 * y + 2.0 * b / W))) / 2.0).real();
t[3] = ((-W + std::sqrt(-(3.0 * a + 2.0 * y - 2.0 * b / W))) / 2.0).real();
return t.max();
}
quaternion align_points(const std::vector<point> &pa, const std::vector<point> &pb)
{
// First calculate M, a 3x3 Matrix containing the sums of products of the coordinates of A and B
Matrix M(3, 3, 0);
for (uint32_t i = 0; i < pa.size(); ++i)
{
const point &a = pa[i];
const point &b = pb[i];
M(0, 0) += a.m_x * b.m_x;
M(0, 1) += a.m_x * b.m_y;
M(0, 2) += a.m_x * b.m_z;
M(1, 0) += a.m_y * b.m_x;
M(1, 1) += a.m_y * b.m_y;
M(1, 2) += a.m_y * b.m_z;
M(2, 0) += a.m_z * b.m_x;
M(2, 1) += a.m_z * b.m_y;
M(2, 2) += a.m_z * b.m_z;
}
// Now calculate N, a symmetric 4x4 Matrix
SymmetricMatrix N(4);
N(0, 0) = M(0, 0) + M(1, 1) + M(2, 2);
N(0, 1) = M(1, 2) - M(2, 1);
N(0, 2) = M(2, 0) - M(0, 2);
N(0, 3) = M(0, 1) - M(1, 0);
N(1, 1) = M(0, 0) - M(1, 1) - M(2, 2);
N(1, 2) = M(0, 1) + M(1, 0);
N(1, 3) = M(0, 2) + M(2, 0);
N(2, 2) = -M(0, 0) + M(1, 1) - M(2, 2);
N(2, 3) = M(1, 2) + M(2, 1);
N(3, 3) = -M(0, 0) - M(1, 1) + M(2, 2);
// det(N - λI) = 0
// find the largest λ (λm)
//
// Aλ4 + Bλ3 + Cλ2 + Dλ + E = 0
// A = 1
// B = 0
// and so this is a so-called depressed quartic
// solve it using Ferrari's algorithm
double C = -2 * (M(0, 0) * M(0, 0) + M(0, 1) * M(0, 1) + M(0, 2) * M(0, 2) +
M(1, 0) * M(1, 0) + M(1, 1) * M(1, 1) + M(1, 2) * M(1, 2) +
M(2, 0) * M(2, 0) + M(2, 1) * M(2, 1) + M(2, 2) * M(2, 2));
double D = 8 * (M(0, 0) * M(1, 2) * M(2, 1) +
M(1, 1) * M(2, 0) * M(0, 2) +
M(2, 2) * M(0, 1) * M(1, 0)) -
8 * (M(0, 0) * M(1, 1) * M(2, 2) +
M(1, 2) * M(2, 0) * M(0, 1) +
M(2, 1) * M(1, 0) * M(0, 2));
// E is the determinant of N:
double E =
(N(0, 0) * N(1, 1) - N(0, 1) * N(0, 1)) * (N(2, 2) * N(3, 3) - N(2, 3) * N(2, 3)) +
(N(0, 1) * N(0, 2) - N(0, 0) * N(2, 1)) * (N(2, 1) * N(3, 3) - N(2, 3) * N(1, 3)) +
(N(0, 0) * N(1, 3) - N(0, 1) * N(0, 3)) * (N(2, 1) * N(2, 3) - N(2, 2) * N(1, 3)) +
(N(0, 1) * N(2, 1) - N(1, 1) * N(0, 2)) * (N(0, 2) * N(3, 3) - N(2, 3) * N(0, 3)) +
(N(1, 1) * N(0, 3) - N(0, 1) * N(1, 3)) * (N(0, 2) * N(2, 3) - N(2, 2) * N(0, 3)) +
(N(0, 2) * N(1, 3) - N(2, 1) * N(0, 3)) * (N(0, 2) * N(1, 3) - N(2, 1) * N(0, 3));
// solve quartic
double lambda = LargestDepressedQuarticSolution(C, D, E);
// calculate t = (N - λI)
Matrix t = N - IdentityMatrix(4) * lambda;
// calculate a Matrix of cofactors for t
Matrix cf = Cofactors(t);
int maxR = 0;
for (int r = 1; r < 4; ++r)
{
if (std::abs(cf(r, 0)) > std::abs(cf(maxR, 0)))
maxR = r;
}
quaternion q(
static_cast<float>(cf(maxR, 0)),
static_cast<float>(cf(maxR, 1)),
static_cast<float>(cf(maxR, 2)),
static_cast<float>(cf(maxR, 3)));
q = normalize(q);
return q;
}
// --------------------------------------------------------------------
point nudge(point p, float offset)
{
static std::random_device rd;
static std::mt19937_64 rng(rd());
std::uniform_real_distribution<float> randomAngle(0, 2 * kPI);
std::normal_distribution<> randomOffset(0, offset);
float theta = randomAngle(rng);
float phi1 = randomAngle(rng) - kPI;
float phi2 = randomAngle(rng) - kPI;
quaternion q = spherical(1.0f, theta, phi1, phi2);
point r{ 0, 0, 1 };
r.rotate(q);
r *= randomOffset(rng);
return p + r;
}
} // namespace cif

Some files were not shown because too many files have changed in this diff Show More